From e08d28f36569731593b260b0df3352366f4a555a Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 6 Nov 2026 01:39:32 +0200 Subject: [PATCH] feat(giellie): a Stock update shortcut in the composer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/components/giellie/composer.test.tsx | 26 ++++++++++++ src/components/giellie/composer.tsx | 53 +++++++++++++++++++++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/components/giellie/composer.test.tsx b/src/components/giellie/composer.test.tsx index e45f598..d67a39b 100644 --- a/src/components/giellie/composer.test.tsx +++ b/src/components/giellie/composer.test.tsx @@ -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(); + + 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(); + fireEvent.click(screen.getByRole("button", { name: /stock update/i })); + fireEvent.click(screen.getByRole("button", { name: /send to giellie/i })); + expect(onSend).not.toHaveBeenCalled(); + }); +}); diff --git a/src/components/giellie/composer.tsx b/src/components/giellie/composer.tsx index 0c1ccd2..c2ba272 100644 --- a/src/components/giellie/composer.tsx +++ b/src/components/giellie/composer.tsx @@ -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([]); + const [stockOpen, setStockOpen] = useState(false); + const [stockText, setStockText] = useState(""); const fileRef = useRef(null); const cameraRef = useRef(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 (
{files.length > 0 && ( @@ -75,6 +90,17 @@ export function Composer({ disabled, onSend }: Props) { +