8.1 KiB
Implementation Plan: Batch Finding Disposition
Overview
Add multi-select capability to the Vulnerability Triage findings table with a batch-add-to-queue API endpoint. The backend gets a new POST /api/ivanti/todo-queue/batch route in ivantiTodoQueue.js. The frontend gets selection state, checkbox dual-mode logic, a SelectionToolbar component, shift-click range select, select-all, and Escape-to-clear — all within ReportingPage.js.
Tasks
-
1. Add
POST /api/ivanti/todo-queue/batchendpoint- 1.1 Add batch route handler to
backend/routes/ivantiTodoQueue.js- Add
POST /batchroute insidecreateIvantiTodoQueueRouter, before thePOST /route - Apply
requireAuth(db)andrequireGroup('Admin', 'Standard_User')middleware - Validate request body:
findingsarray (1–200 items), each with non-emptyfinding_idstring - Validate
workflow_typeis one ofFP,Archer,CARD - Validate
vendor: required non-empty string ≤200 chars for FP/Archer; ignored for CARD - If any validation fails, return 400 with descriptive error message and reject entire batch
- Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.8, 3.10
- Add
- 1.2 Implement transactional batch insert with SQLite
- Use
db.serialize()withBEGIN TRANSACTION/COMMITto insert all findings atomically - For each finding: insert row into
ivanti_todo_queuewithuser_id,finding_id,finding_title,cves_json,ip_address,vendor,workflow_type - On success: fetch all inserted rows, parse
cves_jsonback to arrays, return 201 with{ items: [...] } - On any DB error:
ROLLBACKthe transaction and return 500 - Requirements: 3.7, 3.8, 3.9, 3.11
- Use
- 1.3 Add audit logging for batch additions
- After successful commit, call
logAudit(db, { ... })with action'batch_add_to_queue', entityType'ivanti_todo_queue', and details including the count and workflow_type - Import
logAuditfrom../helpers/auditLog - Requirements: 3.7
- After successful commit, call
- 1.1 Add batch route handler to
-
2. Checkpoint — Verify backend endpoint
- Ensure the batch endpoint is syntactically correct and the route file has no errors. Ask the user if questions arise.
-
3. Add multi-select state and checkbox dual-mode logic to
ReportingPage.js- 3.1 Add selection state variables to
VulnerabilityTriagePage- Add
selectedIds(new Set()),lastClickedId(null),batchSubmitting(false),batchError(null),batchWorkflowType('FP'),batchVendor('') as newuseStatehooks - Requirements: 1.1, 2.1
- Add
- 3.2 Implement checkbox dual-mode click handler
- Replace the existing
<td>onClick in the checkbox cell with new logic:- If finding is already queued → no-op (existing behavior)
- If
selectedIds.size === 0AND not shift-click → openAddToQueuePopover(preserves single-select flow) - If shift-click AND
lastClickedIdexists → range-select all visible non-queued findings betweenlastClickedIdand current finding in thesortedarray - Otherwise → toggle finding.id in
selectedIds
- Always update
lastClickedIdwhen toggling selection - Requirements: 1.1, 1.2, 5.1, 5.2, 6.1
- Replace the existing
- 3.3 Add visual highlighting for selected rows
- When a finding's ID is in
selectedIds, apply a highlighted background (e.g.rgba(14,165,233,0.12)) to the row - Override the existing alternating row background and hover for selected rows
- Requirements: 1.3
- When a finding's ID is in
- 3.4 Disable checkbox for already-queued findings
- Keep existing behavior: queued findings show checked + disabled checkbox, preventing re-selection
- Ensure queued findings are excluded from shift-click range select and select-all
- Requirements: 1.5
- 3.1 Add selection state variables to
-
4. Implement Select All / Deselect All in column header
- Modify the checkbox column
<th>to render a clickable "Select All" checkbox whenselectedIds.size > 0or when the user interacts with it - Click behavior: if not all visible non-queued findings are selected → select all visible non-queued; if all are selected → deselect all
- Requirements: 1.6, 1.7
- Modify the checkbox column
-
5. Add selection pruning on filter changes
- Add a
useEffectthat watchesfiltered(the filtered findings array) and prunesselectedIdsto only include IDs still present in the filtered set - This ensures selection stays consistent when
columnFilters,actionFilter, orexcFilterchange - Requirements: 1.4
- Add a
-
6. Implement SelectionToolbar component
- 6.1 Create the
SelectionToolbarinline component inReportingPage.js- Render between the panel header controls and the
<table>element, only whenselectedIds.size > 0 - Use
position: stickywith appropriatetopvalue to stay visible during scroll - Follow the dark theme design system: monospace fonts, dark gradient background, accent-colored borders
- Requirements: 2.1, 2.8
- Render between the panel header controls and the
- 6.2 Add toolbar controls: count badge, Clear Selection, workflow toggles, vendor input, submit button
- Display selected count badge (e.g. "12 selected")
- "Clear Selection" button that empties
selectedIdsand hides toolbar - Workflow type toggle buttons (FP / Archer / CARD) using existing color scheme: FP = amber (
#F59E0B), Archer = blue (#0EA5E9), CARD = green (#10B981) - Vendor text input (hidden when CARD is selected, show "No vendor required" indicator for CARD)
- "Add to Queue" submit button — enabled only when workflow_type is CARD, or vendor is non-empty
- Requirements: 2.2, 2.3, 2.4, 2.5, 2.6, 2.7
- 6.1 Create the
-
7. Implement batch submission flow
- 7.1 Add
submitBatchasync function toVulnerabilityTriagePage- Build request payload from
selectedIds(map each ID to its finding object fromsorted/filteredforfinding_id,finding_title,cves,ip_address), plusbatchWorkflowTypeandbatchVendor - POST to
${API_BASE}/ivanti/todo-queue/batchwithcredentials: 'include' - Set
batchSubmitting = truebefore request,falseafter - Requirements: 4.1, 4.2
- Build request payload from
- 7.2 Handle batch success response
- On 201: merge returned items into
queueItemsstate (sorted by vendor then id, matching existing pattern) - Clear
selectedIds, resetbatchWorkflowTypeto 'FP', resetbatchVendorto '', clearbatchError - The newly queued findings will automatically show as checked+disabled via the existing
isQueued()helper - Requirements: 4.3, 4.4, 4.6
- On 201: merge returned items into
- 7.3 Handle batch error response
- On 4xx/5xx: parse error message from response JSON, set
batchErrorto display in toolbar - On network failure: set
batchErrorto "Network error — please try again" - Keep selection intact on error so user can retry
- Requirements: 4.5
- On 4xx/5xx: parse error message from response JSON, set
- 7.1 Add
-
8. Add Escape key handler to clear selection
- Add a
useEffectwith akeydownlistener for Escape that clearsselectedIdswhen the SelectionToolbar is visible (i.e.selectedIds.size > 0) - Ensure it doesn't conflict with the existing Escape handler on
AddToQueuePopover - Requirements: 6.3
- Add a
-
9. Ensure keyboard Tab accessibility for SelectionToolbar
- Verify all interactive elements in the toolbar (workflow buttons, vendor input, submit button, clear button) are focusable via Tab key
- Use native
<button>and<input>elements (which are inherently tabbable) rather than<div>with onClick - Requirements: 6.2
-
10. Final checkpoint — Full integration verification
- Ensure all files have no syntax errors or diagnostic issues
- Verify the checkbox dual-mode logic: no selection → popover, existing selection → toggle
- Verify the SelectionToolbar renders/hides correctly based on selection state
- Verify batch submit wires through to the backend endpoint and updates queue state
- Ensure all tests pass, ask the user if questions arise.
Notes
- No new database migration needed — batch insert reuses the existing
ivanti_todo_queueschema - The batch endpoint must be registered before
POST /in the router to avoid Express route conflicts - All testing is done on the dev server after push — no local test tasks included
- Each task references specific acceptance criteria from the requirements document for traceability