New specs: archer-template-library, ccp-metrics-view-restructure, compliance-list-stale-after-sidebar-edit, compliance-metric-estimated-resolution-date, compliance-remediation-display-fix, flexible-jira-ticket-creation, forecast-burndown-chart, granite-loader-export, ivanti-queue-clear-completed-fix, multi-item-jira-ticket, queue-collapsible-sections, vendor-issue-type-dropdown New steering: archer-template-gen.md Updated: migration-registration-check hook, remediation-plan-history spec, gitlab-workflow, tech, versioning steering files
7.0 KiB
Implementation Plan: Multi-Item Jira Ticket Creation
Overview
This plan implements multi-select on the Ivanti Queue page and a consolidation modal that creates a single Jira ticket from multiple selected queue items. The implementation proceeds from database migration → backend junction endpoints → frontend aggregation logic → frontend selection UI → consolidation modal → ticket link badges.
Tasks
-
1. Database migration for junction table
- 1.1 Create migration file
backend/migrations/add_multi_item_jira_ticket.js- Verify
jira_ticketsandivanti_todo_queuetables exist; exit with error if missing - Create
jira_ticket_queue_itemstable with columns: id (SERIAL PRIMARY KEY), jira_ticket_id (INTEGER NOT NULL REFERENCES jira_tickets(id)), queue_item_id (INTEGER NOT NULL REFERENCES ivanti_todo_queue(id)), created_at (TIMESTAMPTZ DEFAULT NOW()) - Add UNIQUE constraint on (jira_ticket_id, queue_item_id)
- Add index on queue_item_id
- Add index on jira_ticket_id
- Ensure full idempotency — safe to run multiple times
- Requirements: 7.1, 7.2, 7.3, 7.4, 7.5
- Verify
- 1.1 Create migration file
-
2. Backend junction table endpoints
-
2.1 Add
POST /api/jira-tickets/:id/queue-itemsendpoint inbackend/routes/jiraTickets.js- Validate
queue_item_idsis a non-empty array of integers - Verify the jira_ticket exists
- Verify all referenced queue items exist
- Insert rows into
jira_ticket_queue_itemswith ON CONFLICT DO NOTHING - Return 201 with linked_count
- Require Admin or Standard_User group
- Requirements: 5.3, 6.1, 6.2
- Validate
-
2.2 Add
GET /api/ivanti/todo-queue/ticket-linksendpoint inbackend/routes/ivantiTodoQueue.js- Join
jira_ticket_queue_itemswithjira_ticketsto get ticket_key and url - Filter by queue items belonging to the authenticated user
- Return a map of queue_item_id → { ticket_key, jira_url }
- Requirements: 6.3, 6.4
- Join
-
-
3. Frontend aggregation utility functions
-
3.1 Create
frontend/src/utils/jiraConsolidation.jswith pure functionsgenerateConsolidatedSummary(items)— format:[N findings] vendor - title, truncated to 255 charsgenerateConsolidatedDescription(items)— structured description grouped by vendorextractFirstCve(items)— first CVE from first item with non-empty cves_jsonextractCommonVendor(items)— common vendor if all same, empty string otherwise- Requirements: 3.1, 3.2, 4.1, 4.2, 4.3, 4.4, 4.6, 4.7
-
* 3.2 Write property tests for aggregation functions (Properties 1–6)
- Property 1: Summary format and truncation — starts with
[N findings], at most 255 chars, contains correct vendor label - Property 2: Description includes all items — every item's finding_title appears in output
- Property 3: Description groups by vendor — items with same vendor are contiguous
- Property 4: Description header contains count — output contains the item count
- Property 5: First CVE extraction — returns first CVE from first item with CVEs, or empty string
- Property 6: Common vendor extraction — returns vendor when all same, empty when different
- Validates: Requirements 3.1, 3.2, 4.1, 4.2, 4.3, 4.4, 4.6, 4.7
- File:
backend/__tests__/jira-consolidation-aggregation.property.test.js
- Property 1: Summary format and truncation — starts with
-
-
4. Frontend multi-select mode on Ivanti Queue page
-
4.1 Add selection mode toggle and checkbox UI to
frontend/src/components/pages/IvantiQueuePage.js- Add "Select" toggle button to page toolbar
- Show checkboxes on each queue item row when selection mode active
- Add "Select All" checkbox in table header
- Display selection count indicator
- Clear selections when selection mode deactivated
- Preserve selections on scroll/re-render within same session
- Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6
-
4.2 Add floating action bar with "Create Jira Ticket" button
- Render floating bar when selection mode active and at least 1 item selected
- Disable "Create Jira Ticket" button when no items selected
- Route to existing single-item modal when exactly 1 item selected
- Route to new Consolidation Modal when 2+ items selected
- Requirements: 2.1, 2.2, 2.3, 2.4
-
-
5. Frontend Consolidation Modal
- 5.1 Create
frontend/src/components/ConsolidationModal.js- Accept selected queue items as props
- Call aggregation functions to pre-populate summary, description, cve_id, vendor
- Lock source_context to
ivanti_queue(read-only display) - Display item count in modal header/subtitle
- Render scrollable preview list of selected items (finding_title + hostname)
- Allow removing individual items from selection (minimum 2 required)
- Disable submit and show message when fewer than 2 items remain
- Editable summary field with required validation (max 255 chars)
- Editable description textarea
- Editable CVE ID and Vendor fields (optional)
- On submit: POST to create-in-jira, then POST to junction endpoint
- On success: close modal, show success toast, trigger queue page refresh
- On error: display error message, preserve form values
- Requirements: 3.1, 3.2, 3.3, 3.4, 4.1, 4.5, 5.1, 5.2, 5.4, 5.5, 8.1, 8.2, 8.3, 8.4, 8.5
- 5.1 Create
-
6. Frontend ticket link badges on queue items
- 6.1 Fetch and display ticket link badges on Ivanti Queue page
- On page load, fetch
GET /api/ivanti/todo-queue/ticket-links - For each queue item with an association, render a ticket key badge (e.g., "VULN-789")
- Clicking badge opens Jira URL in new tab
- Refresh links after successful consolidated ticket creation
- Requirements: 6.3, 6.4, 6.5
- On page load, fetch
- 6.1 Fetch and display ticket link badges on Ivanti Queue page
-
7. Backend property test for junction insert invariant
- * 7.1 Write property test for junction row count (Property 7)
- Property 7: Junction table row count equals selected item count — for N items, exactly N rows inserted
- Validates: Requirements 5.3, 6.2
- File:
backend/__tests__/jira-consolidation-junction.property.test.js
- * 7.1 Write property test for junction row count (Property 7)
-
8. Final checkpoint
- Ensure all tests pass, ask the user if questions arise.
Notes
- Tasks marked with
*are optional property-based tests that can be skipped for faster MVP - The existing
POST /api/jira-tickets/create-in-jiraendpoint is reused without modification - The aggregation functions are pure and extracted into a utility module for easy testing
- The junction table insert happens as a second request after ticket creation — if it fails, the ticket still exists in Jira (partial success scenario handled with warning)
- The Consolidation Modal is a separate component from the existing Creation Modal to avoid overcomplicating the single-item flow
Task Dependency Graph
{
"waves": [
{ "id": 0, "tasks": ["1.1"] },
{ "id": 1, "tasks": ["2.1", "2.2", "3.1"] },
{ "id": 2, "tasks": ["3.2", "4.1"] },
{ "id": 3, "tasks": ["4.2", "5.1"] },
{ "id": 4, "tasks": ["6.1", "7.1"] }
]
}