feat(giellie): a Stock update shortcut in the composer
Opens a paste panel for the SKU and quantity lines and sends them to the chat. It carries no set/add intent — the operator is asked that every time — so it is only a shortcut into the conversation.
This commit is contained in:
parent
c943118300
commit
e08d28f365
2 changed files with 78 additions and 1 deletions
|
|
@ -26,3 +26,29 @@ describe("Composer draft", () => {
|
|||
expect(onSend).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("the stock update shortcut", () => {
|
||||
it("sends the pasted lines to Giellie", () => {
|
||||
const onSend = vi.fn();
|
||||
render(<Composer disabled={false} onSend={onSend} />);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /stock update/i }));
|
||||
fireEvent.change(screen.getByPlaceholderText(/ABC-123 50/i), {
|
||||
target: { value: "ABC-123 50\nBOLT-6MM 120" },
|
||||
});
|
||||
fireEvent.click(screen.getByRole("button", { name: /send to giellie/i }));
|
||||
|
||||
expect(onSend).toHaveBeenCalledTimes(1);
|
||||
const sent = String(onSend.mock.calls[0][0]);
|
||||
expect(sent).toContain("ABC-123 50");
|
||||
expect(sent).toContain("BOLT-6MM 120");
|
||||
});
|
||||
|
||||
it("does not send an empty list", () => {
|
||||
const onSend = vi.fn();
|
||||
render(<Composer disabled={false} onSend={onSend} />);
|
||||
fireEvent.click(screen.getByRole("button", { name: /stock update/i }));
|
||||
fireEvent.click(screen.getByRole("button", { name: /send to giellie/i }));
|
||||
expect(onSend).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useRef, useState } from "react";
|
||||
import { Camera, Paperclip, Send, X } from "lucide-react";
|
||||
import { Camera, Package, Paperclip, Send, X } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { downscaleImage } from "@/lib/giellie-image";
|
||||
|
|
@ -15,6 +15,8 @@ interface Props {
|
|||
export function Composer({ disabled, onSend }: Props) {
|
||||
const [text, setText] = useState("");
|
||||
const [files, setFiles] = useState<File[]>([]);
|
||||
const [stockOpen, setStockOpen] = useState(false);
|
||||
const [stockText, setStockText] = useState("");
|
||||
const fileRef = useRef<HTMLInputElement>(null);
|
||||
const cameraRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
|
|
@ -32,6 +34,19 @@ export function Composer({ disabled, onSend }: Props) {
|
|||
});
|
||||
}
|
||||
|
||||
function sendStockList() {
|
||||
const list = stockText.trim();
|
||||
if (!list || disabled) return;
|
||||
onSend(
|
||||
`Update the stock quantities on these lines.\n\n${list}`,
|
||||
[],
|
||||
() => {
|
||||
setStockText("");
|
||||
setStockOpen(false);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="border-t bg-background p-3 space-y-2">
|
||||
{files.length > 0 && (
|
||||
|
|
@ -75,6 +90,17 @@ export function Composer({ disabled, onSend }: Props) {
|
|||
<Button type="button" variant="ghost" size="icon" aria-label="Take a photo" onClick={() => cameraRef.current?.click()}>
|
||||
<Camera className="h-5 w-5" />
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label="Stock update"
|
||||
title="Stock update"
|
||||
disabled={disabled}
|
||||
onClick={() => setStockOpen((v) => !v)}
|
||||
>
|
||||
<Package className="h-5 w-5" />
|
||||
</Button>
|
||||
<Textarea
|
||||
value={text}
|
||||
onChange={(e) => setText(e.target.value)}
|
||||
|
|
@ -92,6 +118,31 @@ export function Composer({ disabled, onSend }: Props) {
|
|||
<Send className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
{stockOpen && (
|
||||
<div className="space-y-2 rounded-md border bg-muted/30 p-2">
|
||||
<Textarea
|
||||
value={stockText}
|
||||
onChange={(e) => setStockText(e.target.value)}
|
||||
placeholder={"ABC-123 50\nBOLT-6MM 120"}
|
||||
rows={4}
|
||||
className="min-h-[80px] font-mono text-xs"
|
||||
/>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
One SKU and quantity per line. Giellie asks whether this is the new quantity on
|
||||
hand or an addition.
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
disabled={disabled || !stockText.trim()}
|
||||
onClick={sendStockList}
|
||||
>
|
||||
Send to Giellie
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue