From 7a2c56a11fb3453e3a2e65d762ea77d962ebd219 Mon Sep 17 00:00:00 2001 From: jramos Date: Thu, 26 Mar 2026 15:43:43 -0600 Subject: [PATCH] fix(reporting): visible queue checkbox + multi-select delete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Table: removed disabled={queued} from the row checkbox so accentColor renders properly — checked rows now show a solid blue tick instead of the greyed-out browser default. Queue panel: each item now has a small red selection checkbox (opacity 0.35 when idle, full when selected). Selecting any items reveals a red 'Delete (N)' button in the footer alongside 'Clear Completed'. Bulk deletes run in parallel; selection state is automatically pruned when items are removed via the individual trash button. Co-Authored-By: Claude Sonnet 4.6 --- .../src/components/pages/ReportingPage.js | 72 ++++++++++++++++++- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/pages/ReportingPage.js b/frontend/src/components/pages/ReportingPage.js index 1d3b95c..bfd12c7 100644 --- a/frontend/src/components/pages/ReportingPage.js +++ b/frontend/src/components/pages/ReportingPage.js @@ -1241,10 +1241,35 @@ function AddToQueuePopover({ finding, anchorRect, queueForm, setQueueForm, onAdd // --------------------------------------------------------------------------- // QueuePanel — fixed slide-out panel showing the user's Ivanti queue // --------------------------------------------------------------------------- -function QueuePanel({ open, items, onClose, onUpdate, onDelete, onClearCompleted }) { +function QueuePanel({ open, items, onClose, onUpdate, onDelete, onDeleteMany, onClearCompleted }) { const pendingCount = items.filter((i) => i.status === 'pending').length; const completedCount = items.filter((i) => i.status === 'complete').length; + const [selectedIds, setSelectedIds] = useState(new Set()); + + // Drop any selected IDs that no longer exist in items + useEffect(() => { + setSelectedIds((prev) => { + if (prev.size === 0) return prev; + const valid = new Set(items.map((i) => i.id)); + const next = new Set([...prev].filter((id) => valid.has(id))); + return next.size === prev.size ? prev : next; + }); + }, [items]); + + const toggleSelect = (id) => { + setSelectedIds((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); else next.add(id); + return next; + }); + }; + + const handleDeleteSelected = () => { + onDeleteMany([...selectedIds]); + setSelectedIds(new Set()); + }; + // CARD items are their own top section; everything else groups by vendor const grouped = useMemo(() => { const cardItems = items.filter((i) => i.workflow_type === 'CARD'); @@ -1376,6 +1401,15 @@ function QueuePanel({ open, items, onClose, onUpdate, onDelete, onClearCompleted transition: 'opacity 0.15s', }} > + {/* Selection checkbox — for bulk delete */} + toggleSelect(item.id)} + style={{ accentColor: '#EF4444', width: '12px', height: '12px', flexShrink: 0, marginTop: '3px', cursor: 'pointer', opacity: selectedIds.has(item.id) ? 1 : 0.35 }} + title="Select for deletion" + /> + {/* Complete checkbox */} + {/* Delete selected — only shown when items are selected */} + {selectedIds.size > 0 && ( + + )}