# Tasks ## Task 1: Database Migration - [x] 1.1 Create migration file `backend/migrations/add_vcl_reporting_columns.js` that adds `resolution_date DATE DEFAULT NULL` and `remediation_plan TEXT DEFAULT NULL` columns to the `compliance_items` table using `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` - [x] 1.2 In the same migration, create the `compliance_snapshots` table with columns: id (SERIAL PK), snapshot_month (TEXT NOT NULL), vertical (TEXT NOT NULL), total_devices (INTEGER), compliant (INTEGER), non_compliant (INTEGER), compliance_pct (NUMERIC(5,2)), created_at (TIMESTAMPTZ), with a UNIQUE constraint on (snapshot_month, vertical) - [x] 1.3 Add index `idx_compliance_snapshots_month` on `compliance_snapshots(snapshot_month)` - [x] 1.4 Run the migration and verify columns and table exist in the database ## Task 2: Backend — Pure Helper Functions - [x] 2.1 Implement and export `truncateText(text, maxLen)` — returns original if length <= maxLen, otherwise first maxLen chars + "…" - [x] 2.2 Implement and export `validateRemediationPlan(text)` — returns `{ valid: true }` if length <= 2000, otherwise `{ valid: false, error: "..." }` - [x] 2.3 Implement and export `isValidDateString(str)` — returns true only for strings parseable as real calendar dates (rejects "2026-02-30", null, empty) - [x] 2.4 Implement and export `formatPct(decimal)` — returns `Math.round(decimal * 100) + '%'` - [x] 2.5 Implement and export `computeVCLStats(items, targetPct)` — computes total, in_scope, compliant, non_compliant, remediations_required, compliance_pct from an array of device objects - [x] 2.6 Implement and export `categorizeNonCompliant(items)` — partitions items into "blocked" (no resolution_date set) and "in_progress" (resolution_date set) with counts and percentages - [x] 2.7 Implement and export `rankHeavyHitters(verticalData)` — sorts verticals by non_compliant count descending - [x] 2.8 Implement and export `computeForecastBurndown(items)` — buckets items by resolution_date month, returns monthly forecast counts - [x] 2.9 Implement and export `matchByHostname(uploadedRows, existingHostnames)` — returns { matched, unmatched } arrays - [x] 2.10 Implement and export `computeBulkDiff(matchedRows, currentData)` — compares uploaded vs current values, returns change details - [x] 2.11 Implement and export `mapColumnHeaders(headers)` — maps column header strings to known field names (case-insensitive matching) ## Task 3: Backend — Device Metadata Endpoint - [x] 3.1 Add `PATCH /items/:hostname/metadata` endpoint to `backend/routes/compliance.js` with `requireAuth()` and `requireGroup('Admin', 'Standard_User')` middleware - [x] 3.2 Implement request body validation: resolution_date must be a valid ISO date or null, remediation_plan must be <= 2000 chars or null - [x] 3.3 Update all active `compliance_items` rows matching the hostname with the provided fields - [x] 3.4 Return 404 if no rows match the hostname, 200 with `{ updated: count }` on success - [x] 3.5 Log audit entry with action `compliance_metadata_update` ## Task 4: Backend — VCL Stats Endpoint - [x] 4.1 Add `GET /vcl/stats` endpoint to `backend/routes/compliance.js` with `requireAuth()` middleware - [x] 4.2 Query compliance_items to compute summary statistics (total devices, compliant, non-compliant counts) using `computeVCLStats` - [x] 4.3 Compute donut chart data using `categorizeNonCompliant` on active non-compliant items - [x] 4.4 Compute heavy hitters using `rankHeavyHitters` grouped by team/vertical - [x] 4.5 Compute vertical breakdown with actual burndown (from upload history) and forecast burndown (from resolution_dates) using `computeForecastBurndown` - [x] 4.6 Return the combined JSON response with stats, donut, heavy_hitters, and vertical_breakdown ## Task 5: Backend — VCL Trend Endpoint - [x] 5.1 Add `GET /vcl/trend` endpoint to `backend/routes/compliance.js` with `requireAuth()` middleware - [x] 5.2 Query `compliance_snapshots` table for monthly compliance data points - [x] 5.3 Compute forecast line using linear regression on the last 3+ months of actual data - [x] 5.4 Return monthly data array with compliant_count, compliance_pct, forecast_pct, and target_pct ## Task 6: Backend — Bulk Upload Endpoints - [x] 6.1 Add `POST /vcl/bulk-preview` endpoint with `requireAuth()` and `requireGroup('Admin', 'Standard_User')` middleware - [x] 6.2 Validate request body: require rows array, enforce 2000 row limit, require hostname field on each row - [x] 6.3 Use `mapColumnHeaders` to identify updatable fields; return 400 if no updatable fields found - [x] 6.4 Use `matchByHostname` to separate matched/unmatched rows - [x] 6.5 Validate each row's fields using `isValidDateString` and `validateRemediationPlan`; flag invalid rows - [x] 6.6 Use `computeBulkDiff` to identify changed rows and return the full diff preview response - [x] 6.7 Add `POST /vcl/bulk-commit` endpoint with `requireAuth()` and `requireGroup('Admin', 'Standard_User')` middleware - [x] 6.8 Accept validated changes array, execute all updates in a single PostgreSQL transaction - [x] 6.9 Log audit entry with action `compliance_bulk_update` including count of rows updated ## Task 7: Backend — Snapshot Creation on Upload - [x] 7.1 Modify the existing `persistUpload` function in `compliance.js` to insert/update a `compliance_snapshots` row for the current month after each upload commit - [x] 7.2 Compute per-vertical compliance percentages at snapshot time from the current state of compliance_items ## Task 8: Frontend — Device Detail Panel Fields - [x] 8.1 Add a "Resolution Date" section to `ComplianceDetailPanel.js` with an `` field - [x] 8.2 Add a "Remediation Plan" section with a `