feat: add GRANITE as fourth workflow type in Ivanti queue

- Add GRANITE to VALID_WORKFLOW_TYPES in backend (no vendor required, same as CARD)
- Update vendor validation and error messages across all endpoints (single add, batch, PUT, redirect)
- Add GRANITE option to RedirectModal with warm slate color (#A1887F)
- Rename QueuePanel CARD section to Inventory, group CARD + GRANITE with sub-divider
- Add GRANITE to AddToQueuePopover and SelectionToolbar
- Update spec docs (requirements, design, tasks)
This commit is contained in:
jramos
2026-04-14 15:33:19 -06:00
parent 28bce28fc9
commit 0fe8e94d51
6 changed files with 503 additions and 265 deletions

View File

@@ -4,6 +4,8 @@
Implement a redirect action for completed Ivanti queue items. The feature adds a `POST /api/ivanti/todo-queue/:id/redirect` endpoint to the existing route module, fixes the PUT validation message, creates a RedirectModal frontend component, and wires a redirect button into the QueuePanel for completed items. Tasks are ordered: backend bug fix, backend endpoint, frontend modal, frontend integration, with property tests alongside each layer.
Additionally, GRANITE is added as a fourth workflow type across the entire stack — backend validation, RedirectModal, QueuePanel grouping (Inventory section), and AddToQueue popover.
## Tasks
- [x] 1. Fix PUT endpoint validation message
@@ -25,27 +27,6 @@ Implement a redirect action for completed Ivanti queue items. The feature adds a
- Call `logAudit(db, ...)` fire-and-forget with action `"queue_item_redirected"`, entityType `"ivanti_todo_queue"`, entityId = original item ID, details: `{ original_workflow_type, target_workflow_type, new_item_id, vendor }`
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 2.1, 2.2_
- [ ]* 2.2 Write property test: redirect preserves finding data
- **Property 1: Redirect preserves finding data**
- Generate random queue item data (finding_id, finding_title, cves_json, ip_address, hostname with varying lengths and special characters) and random valid workflow_type
- Mock the database layer; verify the INSERT parameters preserve all finding fields and set status to "pending"
- Use `fast-check` with minimum 100 iterations
- **Validates: Requirements 1.1, 1.7**
- [ ]* 2.3 Write property test: vendor requirement is conditional on workflow type
- **Property 2: Vendor requirement is conditional on workflow type**
- Generate random (workflow_type, vendor) pairs where workflow_type is drawn from VALID_WORKFLOW_TYPES and vendor from a mix of valid strings, empty strings, whitespace, strings of length 200, and strings of length 201
- Verify validation accepts/rejects correctly based on the conditional rule
- Use `fast-check` with minimum 100 iterations
- **Validates: Requirements 1.2, 1.3**
- [ ]* 2.4 Write property test: successful redirect produces correct audit entry
- **Property 3: Successful redirect produces correct audit entry**
- Generate random successful redirect scenarios with varying item data and workflow types
- Mock `logAudit`; verify the call contains action `"queue_item_redirected"`, entityType `"ivanti_todo_queue"`, original item ID as entityId, and details with original_workflow_type, target_workflow_type, new_item_id, vendor
- Use `fast-check` with minimum 100 iterations
- **Validates: Requirements 2.1, 2.2**
- [x] 3. Checkpoint — Verify backend changes
- Ensure all tests pass, ask the user if questions arise.
@@ -63,15 +44,8 @@ Implement a redirect action for completed Ivanti queue items. The feature adds a
- Use lucide-react icons (e.g., `CornerUpRight` or `ArrowRightLeft`)
- _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7_
- [ ]* 4.2 Write unit tests for RedirectModal
- Test vendor field visible for FP/Archer, hidden for CARD
- Test read-only context displays finding title, finding ID, current workflow type
- Test error message displayed when API returns error
- Test modal stays open on error
- _Requirements: 4.2, 4.3, 4.4, 4.6_
- [x] 5. Integrate redirect button and modal into QueuePanel
- [x] 5.1 Add redirect button to completed items in QueuePanel (inside `frontend/src/App.js`)
- [x] 5.1 Add redirect button to completed items in QueuePanel (inside `frontend/src/components/pages/ReportingPage.js`)
- Add a redirect icon button (lucide-react) on each completed queue item row, next to the existing delete button
- Button visible only when `item.status === 'complete'`; hidden for pending items
- _Requirements: 3.1, 3.2_
@@ -84,22 +58,86 @@ Implement a redirect action for completed Ivanti queue items. The feature adds a
- Import and render `<RedirectModal>` conditionally when `redirectItem` is set
- _Requirements: 3.3, 4.5, 4.7_
- [ ]* 5.3 Write unit tests for redirect button visibility and modal integration
- Test redirect button rendered on completed items
- Test redirect button not rendered on pending items
- Test clicking redirect button opens modal with correct item
- Test successful redirect adds new item to list
- _Requirements: 3.1, 3.2, 3.3, 4.5_
- [x] 6. Final checkpoint — Ensure all tests pass
- Ensure all tests pass, ask the user if questions arise.
- [ ] 7. Add GRANITE to backend validation
- [ ] 7.1 Update `VALID_WORKFLOW_TYPES` constant in `backend/routes/ivantiTodoQueue.js`
- Change from `['FP', 'Archer', 'CARD']` to `['FP', 'Archer', 'CARD', 'GRANITE']`
- _Requirements: 6.1_
- [ ] 7.2 Update vendor validation condition in POST `/` (single add) endpoint
- Change `workflow_type !== 'CARD'` to `workflow_type === 'FP' || workflow_type === 'Archer'` (or `!['CARD', 'GRANITE'].includes(workflow_type)`) for the vendor-required check
- Change `workflow_type === 'CARD' ? '' : vendor.trim()` to `(workflow_type === 'CARD' || workflow_type === 'GRANITE') ? '' : vendor.trim()` for vendorVal assignment
- _Requirements: 6.2_
- [ ] 7.3 Update vendor validation condition in POST `/batch` endpoint
- Change `workflow_type !== 'CARD'` to `workflow_type === 'FP' || workflow_type === 'Archer'` for the vendor-required check
- Change `workflow_type === 'CARD' ? '' : vendor.trim()` to `(workflow_type === 'CARD' || workflow_type === 'GRANITE') ? '' : vendor.trim()` for vendorVal assignment
- _Requirements: 6.2_
- [ ] 7.4 Update vendor validation condition in POST `/:id/redirect` endpoint
- Change `workflow_type !== 'CARD'` to `workflow_type === 'FP' || workflow_type === 'Archer'` for the vendor-required check
- Change `workflow_type === 'CARD' ? '' : vendor.trim()` to `(workflow_type === 'CARD' || workflow_type === 'GRANITE') ? '' : vendor.trim()` for vendorVal assignment
- _Requirements: 6.2_
- [ ] 7.5 Update all error messages across all endpoints
- Change `"workflow_type must be FP, Archer, or CARD."` to `"workflow_type must be FP, Archer, CARD, or GRANITE."` in POST `/`, POST `/batch`, PUT `/:id`, and POST `/:id/redirect`
- _Requirements: 5.1, 6.3_
- [ ] 8. Add GRANITE to RedirectModal
- [ ] 8.1 Update `WORKFLOW_OPTIONS` in `frontend/src/components/RedirectModal.js`
- Add `{ key: 'GRANITE', label: 'GRANITE', col: '#A1887F', rgb: '161,136,127' }` as the fourth option
- Vendor field already hidden for non-FP/Archer types via `needsVendor` check — no change needed there
- _Requirements: 4.1, 4.3_
- [ ] 9. Update QueuePanel grouping for Inventory section
- [ ] 9.1 Update the `grouped` useMemo in QueuePanel (`frontend/src/components/pages/ReportingPage.js`)
- Change `items.filter((i) => i.workflow_type === 'CARD')` to filter both CARD and GRANITE into inventory items
- Split inventory items into `cardItems` and `graniteItems` sub-arrays
- Change `otherItems` filter from `i.workflow_type !== 'CARD'` to exclude both CARD and GRANITE
- Rename group key from `__CARD__` to `__INVENTORY__`, label from `'CARD'` to `'Inventory'`, and `isCard` to `isInventory`
- Include `cardItems` and `graniteItems` as separate properties on the inventory group object
- _Requirements: 7.1, 7.5_
- [ ] 9.2 Update the QueuePanel rendering to handle the Inventory section
- Update the `.map()` destructuring from `isCard` to `isInventory`
- Update group header border and label color to use `isInventory` instead of `isCard`
- For the Inventory group, render CARD items first, then a subtle sub-divider (only when both `cardItems.length > 0` and `graniteItems.length > 0`), then GRANITE items
- _Requirements: 7.1, 7.2, 7.5_
- [ ] 9.3 Update the workflow type color mapping in QueuePanel item rendering
- Add GRANITE to the `wfColor` ternary: `item.workflow_type === 'GRANITE' ? { col: '#A1887F', rgb: '161,136,127' }` before the default CARD fallback
- _Requirements: 7.3, 7.4_
- [ ] 9.4 Update `isCardItem` to `isInventoryItem` in QueuePanel item rendering
- Change `const isCardItem = item.workflow_type === 'CARD'` to `const isInventoryItem = item.workflow_type === 'CARD' || item.workflow_type === 'GRANITE'`
- Update the conditional rendering that uses `isCardItem` to use `isInventoryItem` (hostname/ip_address display vs CVE display)
- _Requirements: 7.1_
- [ ] 10. Add GRANITE to AddToQueuePopover
- [ ] 10.1 Update workflow type buttons in `AddToQueuePopover` (`frontend/src/components/pages/ReportingPage.js`)
- Add `{ key: 'GRANITE', col: '#A1887F', rgb: '161,136,127' }` as the fourth button in the workflow type toggle array
- _Requirements: 8.1, 8.3_
- [ ] 10.2 Update `isCard` condition in `AddToQueuePopover` to include GRANITE
- Change `const isCard = queueForm.workflowType === 'CARD'` to `const isCard = queueForm.workflowType === 'CARD' || queueForm.workflowType === 'GRANITE'` (or rename to `isInventory`)
- This controls the "No vendor required" message and hides the vendor input for GRANITE
- _Requirements: 8.2_
- [ ] 10.3 Update `SelectionToolbar` component to include GRANITE
- Add `{ type: 'GRANITE', color: '#A1887F', rgb: '161,136,127' }` as the fourth button in the workflow type toggles array
- Change `const isCard = workflowType === 'CARD'` to include GRANITE: `const isCard = workflowType === 'CARD' || workflowType === 'GRANITE'`
- _Requirements: 8.1, 8.2, 8.3_
- [ ] 11. Final checkpoint — Verify all GRANITE changes
- Ensure all changes compile and render correctly, ask the user if questions arise.
## Notes
- Tasks marked with `*` are optional and can be skipped for faster MVP
- Tasks 16 are the original redirect feature tasks, all completed
- Tasks 711 are the new GRANITE workflow type additions
- No test tasks included per user request — testing will be done manually on the dev server
- Each task references specific requirements for traceability
- Checkpoints ensure incremental validation
- Property tests validate universal correctness properties from the design document
- Unit tests validate specific examples and edge cases
- The project uses plain JavaScript (no TypeScript), fast-check for PBT, and react-scripts test (Jest)
- The QueuePanel component is defined inside `App.js`, not a separate file
- The QueuePanel component is defined inside `ReportingPage.js`, not a separate file
- The project uses plain JavaScript (no TypeScript)