# Implementation Plan: FP Submission Editing ## Overview Extends the existing FP workflow system with lifecycle status tracking, edit/resubmit capabilities, and a submission history audit trail. Implementation proceeds bottom-up: database migration → pure helpers → backend endpoints → frontend components → wiring and integration. ## Tasks - [x] 1. Database migration and schema changes - [x] 1.1 Create migration script `backend/migrations/add_fp_submission_editing.js` - Add `lifecycle_status` column to `ivanti_fp_submissions` with CHECK constraint and default `'submitted'` - Add `ivanti_workflow_batch_uuid` TEXT column to `ivanti_fp_submissions` - Add `updated_at` DATETIME column to `ivanti_fp_submissions` with default CURRENT_TIMESTAMP - Create `ivanti_fp_submission_history` table with columns: id, submission_id (FK), user_id, username, change_type (CHECK constraint), change_details_json, created_at - Create index `idx_fp_history_submission` on submission_id - Wrap ALTER TABLE statements in try/catch for idempotency; use CREATE TABLE IF NOT EXISTS / CREATE INDEX IF NOT EXISTS - _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5_ - [x] 2. Implement pure helper functions in `backend/routes/ivantiFpWorkflow.js` - [x] 2.1 Implement `validateLifecycleTransition(currentStatus, newStatus)` - Accept two status strings from the set {submitted, approved, rejected, rework, resubmitted} - Return `{ valid: false, error }` when currentStatus is `'approved'` (finalized, no transitions allowed) - Return `{ valid: false, error }` when newStatus is not in the allowed set - Return `{ valid: true }` for all other valid transitions - Export from module for testing - _Requirements: 5.4, 5.5_ - [x] 2.2 Implement `mergeFindings(existingJson, newIds)` - Parse existingJson (JSON array of strings), concatenate with newIds array - Deduplicate by converting to Set, return JSON.stringify of the merged array - Handle edge cases: empty existing array, empty newIds, overlapping IDs - Export from module for testing - _Requirements: 3.3_ - [x] 2.3 Implement `buildSubmissionHistoryEntry(changeType, details, userId, username)` - Construct and return an object with: submission_id (to be set by caller), user_id, username, change_type, change_details_json (JSON.stringify of details), created_at (ISO string) - Export from module for testing - _Requirements: 6.1, 6.2_ - [ ]* 2.4 Write property test for `mergeFindings` — Property 1: Finding Merge Preserves All IDs and Deduplicates - **Property 1: Finding Merge Preserves All IDs and Deduplicates** - **Validates: Requirements 3.3** - Use fast-check to generate arbitrary arrays of string IDs for existing and new - Assert: parsed result contains every ID from both inputs, no duplicates, length ≤ sum of input lengths - Test file: `backend/__tests__/fpSubmissionEditing.property.test.js` - [ ]* 2.5 Write property test for `validateLifecycleTransition` — Property 2: Lifecycle Transition Validation - **Property 2: Lifecycle Transition Validation** - **Validates: Requirements 5.4, 5.5** - Use fast-check to generate pairs from {submitted, approved, rejected, rework, resubmitted} - Assert: always invalid when currentStatus is 'approved'; always valid for other currentStatus values with valid newStatus; rejected/rework → resubmitted is always valid - Test file: `backend/__tests__/fpSubmissionEditing.property.test.js` - [ ] 3. Checkpoint — Ensure all tests pass - Ensure all tests pass, ask the user if questions arise. - [x] 4. Implement backend API endpoints in `backend/routes/ivantiFpWorkflow.js` - [x] 4.1 Implement `GET /api/ivanti/fp-submissions` - Add route with `requireAuth(db)` — any authenticated user - Query `ivanti_fp_submissions` filtered by `req.user.id` - Return JSON array of submission records including lifecycle_status and updated_at - _Requirements: 7.1, 1.1_ - [x] 4.2 Implement `PUT /api/ivanti/fp-submissions/:id` - Add route with `requireAuth(db)`, `requireGroup('Admin', 'Standard_User')` - Verify ownership (user_id match → 403 if not) - Lifecycle guard: reject if lifecycle_status is 'approved' → 400 - Validate body with existing `validateFpWorkflowForm` - Proxy to Ivanti update endpoint via `ivantiPost()` - On success: UPDATE local record fields + updated_at, INSERT history row (change_type: 'fields_updated'), log audit - If previous status was 'rejected' or 'rework', set lifecycle_status to 'resubmitted' - _Requirements: 7.2, 2.1, 2.2, 2.3, 2.4, 2.5, 5.4, 5.5, 7.6, 7.7_ - [x] 4.3 Implement `POST /api/ivanti/fp-submissions/:id/findings` - Add route with `requireAuth(db)`, `requireGroup('Admin', 'Standard_User')` - Verify ownership, lifecycle guard (reject if approved) - Validate findingIds and queueItemIds from body; verify queue items belong to user, are FP type, and pending - Proxy to Ivanti map endpoint via `ivantiFormPost()` using `buildSubjectFilterRequest` - On success: merge finding IDs with `mergeFindings()`, mark queue items complete, INSERT history + audit - _Requirements: 7.3, 3.1, 3.2, 3.3, 3.4, 3.5_ - [x] 4.4 Implement `POST /api/ivanti/fp-submissions/:id/attachments` - Add route with `requireAuth(db)`, `requireGroup('Admin', 'Standard_User')`, Multer middleware - Verify ownership, lifecycle guard (reject if approved) - Validate file constraints (10 MB, allowed extensions) - Loop each file: call `ivantiMultipartPost()` to Ivanti attach endpoint - Collect per-file success/failure results - Update attachment_count and attachment_results_json, INSERT history + audit - _Requirements: 7.4, 4.1, 4.2, 4.3, 4.4, 4.5_ - [x] 4.5 Implement `PATCH /api/ivanti/fp-submissions/:id/status` - Add route with `requireAuth(db)`, `requireGroup('Admin', 'Standard_User')` - Verify ownership - Validate new status is in allowed set - Use `validateLifecycleTransition()` to check transition validity - UPDATE lifecycle_status and updated_at, INSERT history row (change_type: 'status_changed'), log audit - _Requirements: 7.5, 5.1, 5.2, 5.3, 7.6, 7.7_ - [ ]* 4.6 Write unit tests for backend endpoints - Test ownership verification returns 403 for non-owner - Test lifecycle guard returns 400 for approved submissions - Test role guard rejects non-Admin/Standard_User - Test Ivanti error status mapping (401, 419, 429, 5xx) - Test history recording produces correct change_type and change_details_json - Test migration idempotency (can run multiple times without error) - Test file: `backend/__tests__/fpSubmissionEditing.test.js` - _Requirements: 7.6, 7.7, 5.5_ - [ ]* 4.7 Write integration tests for backend endpoints - Test GET returns correct records for authenticated user - Test PUT proxies to Ivanti and updates local record - Test POST findings maps to Ivanti and merges finding IDs - Test POST attachments uploads to Ivanti and updates attachment records - Test PATCH status updates lifecycle and creates history entry - Test queue items marked complete after successful finding addition - Test file: `backend/__tests__/fpSubmissionEditing.integration.test.js` - _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5_ - [ ] 5. Checkpoint — Ensure all tests pass - Ensure all tests pass, ask the user if questions arise. - [x] 6. Register new endpoints in `backend/server.js` - Wire the updated `ivantiFpWorkflow` router so the new GET/PUT/POST/PATCH routes are accessible under `/api/ivanti/fp-submissions` - Verify the existing POST `/api/ivanti/fp-workflow` route continues to work - _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5_ - [x] 7. Implement frontend components in `frontend/src/components/pages/ReportingPage.js` - [x] 7.1 Implement `lifecycleStatusBadge(status)` helper function - Return inline style object with border, background, and text color per status - Color mapping: submitted/resubmitted (sky blue), approved (emerald), rejected (red), rework (amber) - _Requirements: 1.3_ - [x] 7.2 Implement `FpEditModal` component - Props: open, onClose, submission, queueItems, onSuccess - State: form fields initialized from submission, activeTab, saving, errors, result - Tab bar with 4 tabs: Details, Findings, Attachments, History - Details tab: editable form fields (name, reason, description, expirationDate, scopeOverride) with Save button; calls PUT endpoint - Findings tab: read-only current finding IDs list + mechanism to select and add FP queue items; calls POST findings endpoint - Attachments tab: existing attachments list + file upload area; calls POST attachments endpoint - History tab: chronological list fetched from submission history (included in GET response or separate query) - Approved submissions: all fields read-only with finalized message - Dark tactical theme matching existing FpWorkflowModal - _Requirements: 1.2, 2.1, 2.2, 2.3, 2.4, 2.5, 3.1, 3.2, 3.4, 3.5, 4.1, 4.2, 4.3, 4.4, 4.5, 5.5, 6.1_ - [x] 7.3 Modify workflow badge renderer for clickable badges - In the workflow column renderer (~lines 1044–1070), for badges with state reworked/rejected/expired: - Add `cursor: 'pointer'` and `onClick` handler - Append pencil icon (lucide `Edit3`, 10px) after state text - On hover: increase border opacity and brighten background - On click: look up matching FP_Submission by workflow batch ID, open FpEditModal - For badges with state requested/approved: no changes (remain non-interactive) - _Requirements: 1.5, 1.6, 1.7, 1.8_ - [x] 7.4 Add submissions list section to QueuePanel - Fetch submissions via GET /api/ivanti/fp-submissions on panel open - Display each submission: workflow name, batch ID, lifecycle status badge, finding count, created date - Clicking a submission row opens FpEditModal with that submission's data - Viewers see the list but cannot click to edit - _Requirements: 1.1, 1.2, 1.3, 1.4_ - [x] 8. Wire frontend state and data flow - [x] 8.1 Add submissions state and fetch logic to ReportingPage - Add state for submissions array and selected submission - Fetch submissions on page load and after successful edits (onSuccess callback) - Pass submissions and queueItems to FpEditModal and QueuePanel - _Requirements: 1.1, 1.2_ - [x] 8.2 Connect FpEditModal callbacks to refresh data - On successful edit/findings/attachments/status change, call onSuccess to refresh submissions list, queue items, and reporting table data - _Requirements: 2.5, 3.4, 4.4, 5.3_ - [ ] 9. Final checkpoint — Ensure all tests pass - Ensure all tests pass, ask the user if questions arise. ## Notes - Tasks marked with `*` are optional and can be skipped for faster MVP - Each task references specific requirements for traceability - Checkpoints ensure incremental validation - Property tests validate universal correctness properties from the design document - The project uses plain JavaScript (no TypeScript) — all code should follow existing conventions - All new endpoints follow the existing factory-pattern router in `ivantiFpWorkflow.js`