# 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 `