diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx index 1259cd6..4201fa4 100644 --- a/web/src/app/page.tsx +++ b/web/src/app/page.tsx @@ -185,6 +185,8 @@ function TicketWorkbenchContent() { const [error, setError] = useState(null); const [selectedId, setSelectedId] = useState(null); const [selectedTxs, setSelectedTxs] = useState([]); + const [batchIds, setBatchIds] = useState>(new Set()); + const [batchSaving, setBatchSaving] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const [filters, setFilters] = useState([]); @@ -543,6 +545,36 @@ function TicketWorkbenchContent() { setTickets((prev) => prev.map((t) => (t.id === ticketId ? data.ticket : t))); } }; + + const handleBatchStatus = async (newStatus: string) => { + setBatchSaving(true); + for (const id of batchIds) { + await updateTicket(id, { status: newStatus }); + } + setBatchSaving(false); + setBatchIds(new Set()); + await fetchData(); + }; + + const handleBatchAssign = async () => { + const me = users[0]?.id; + if (!me) return; + setBatchSaving(true); + for (const id of batchIds) { + await updateTicket(id, { owner_id: me }); + } + setBatchSaving(false); + setBatchIds(new Set()); + await fetchData(); + }; + + const toggleBatchId = (id: number) => { + setBatchIds((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); else next.add(id); + return next; + }); + }; const handleColumnResize = (colKey: string, e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); @@ -931,8 +963,17 @@ function TicketWorkbenchContent() { ? "bg-accent/80 shadow-[inset_3px_0_0_var(--primary)]" : "hover:bg-accent/45" )} - style={{ width: availableColumns.filter((c) => c.visible).reduce((sum, c) => sum + c.width, 96), minWidth: "100%" }} + style={{ width: availableColumns.filter((c) => c.visible).reduce((sum, c) => sum + c.width, 96) + 36, minWidth: "100%" }} > + {/* Checkbox */} + e.stopPropagation()}> + toggleBatchId(ticket.id)} + className="h-3.5 w-3.5 rounded border-border accent-primary" + /> + {availableColumns.filter((c) => c.visible).map((col) => { if (col.key.startsWith("cf.")) { const cfKey = col.key.slice(3); @@ -1010,6 +1051,41 @@ function TicketWorkbenchContent() { + {/* Floating batch action bar */} + {batchIds.size > 0 && ( +
+ + {batchIds.size} selected + + +
+ Status: + {statusOptions.filter((s) => s.key !== "all").slice(0, 5).map((s) => ( + + ))} +
+ +
+
+ )} +