# Implementation Plan: CCP Metrics View Restructure ## Overview Restructure the CCP Metrics page from a vertical-first drill-down model to a metric-first model. Two new backend endpoints aggregate metrics across verticals. The frontend replaces VerticalTable/VerticalDetailView with MetricTable/MetricDetailView, removes the "By Vertical" table from AggregatedBurndownChart, and inverts the drill-down state hierarchy. ## Tasks - [x] 1. Add backend endpoints for metric-centric aggregation - [x] 1.1 Implement GET /metrics endpoint in backend/routes/vclMultiVertical.js - Add route handler that queries `vcl_multi_vertical_summary` using only `ALL:` rollup rows from the latest upload per vertical - Aggregate by `metric_id`: SUM non_compliant, compliant, total; MAX metric_desc, category; AVG target - Compute `compliance_pct` as `compliant / total` (0 when total is 0) - Sort by `non_compliant` DESC - Return `{ metrics: [...] }` with empty array when no data exists - Require `requireAuth()` middleware - Return HTTP 500 with `{ "error": "Database error" }` on query failure - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9_ - [x] 1.2 Implement GET /metric/:id/verticals endpoint in backend/routes/vclMultiVertical.js - Validate metric ID parameter (reject if > 50 chars with HTTP 400) - Query all rows for the metric from latest uploads - Separate `ALL:` rollup rows from sub-team rows - Build per-vertical entries with nested `sub_teams` arrays - Sort verticals by `non_compliant` DESC - Return `{ metric_id, metric_desc, category, verticals: [...] }` with empty verticals array when metric not found - Require `requireAuth()` middleware - Return HTTP 500 with `{ "error": "Database error" }` on query failure - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9_ - [ ]* 1.3 Write property test: Metrics aggregation uses only rollup rows from latest uploads - **Property 1: Metrics aggregation uses only rollup rows from latest uploads** - Generate random `vcl_multi_vertical_summary` rows with multiple verticals, metrics, uploads, and team configurations - Assert the endpoint returns exactly one entry per distinct `metric_id` with totals matching only `ALL:`-prefixed rows from the latest upload per vertical - Test file: `backend/__tests__/ccp-metrics-view-restructure.property.test.js` - **Validates: Requirements 3.1, 3.3** - [ ]* 1.4 Write property test: Metrics computed fields are mathematically correct - **Property 2: Metrics computed fields are mathematically correct** - Assert `compliance_pct` equals `compliant / total` (or 0 when total is 0) for each metric - Assert `target` equals the arithmetic mean of target values across verticals for each metric - Test file: `backend/__tests__/ccp-metrics-view-restructure.property.test.js` - **Validates: Requirements 3.4, 3.5** - [ ]* 1.5 Write property test: Metrics response is sorted by non-compliant descending - **Property 3: Metrics response is sorted by non-compliant descending** - Assert each entry's `non_compliant` is >= the next entry's `non_compliant` - Test file: `backend/__tests__/ccp-metrics-view-restructure.property.test.js` - **Validates: Requirements 3.6** - [ ]* 1.6 Write property test: Metric-verticals breakdown is complete and correct - **Property 4: Metric-verticals breakdown is complete and correct** - Assert one vertical entry per vertical that has the metric in its latest upload - Assert each entry's `sub_teams` contains exactly the non-rollup team rows for that metric/vertical - Test file: `backend/__tests__/ccp-metrics-view-restructure.property.test.js` - **Validates: Requirements 4.1, 4.3** - [ ]* 1.7 Write property test: Metric-verticals response is sorted by non-compliant descending - **Property 5: Metric-verticals response is sorted by non-compliant descending** - Assert each vertical entry's `non_compliant` is >= the next entry's `non_compliant` - Test file: `backend/__tests__/ccp-metrics-view-restructure.property.test.js` - **Validates: Requirements 4.4** - [x] 2. Checkpoint - Backend endpoints verified - Ensure all tests pass, ask the user if questions arise. - [x] 3. Remove "By Vertical" table from AggregatedBurndownChart - [x] 3.1 Remove the "By Vertical" contribution table JSX from AggregatedBurndownChart in frontend/src/components/pages/CCPMetricsPage.js - Remove the entire `{data.by_vertical && data.by_vertical.length > 0 && (...)}` block that renders the per-vertical table below the bar chart - Preserve the summary header (total non-compliant, blockers, in-progress, projected clear date) and bar chart - Preserve all loading, error, empty-state, and all-blockers display behaviors - _Requirements: 1.1, 1.2, 1.3_ - [x] 4. Implement MetricTable component and replace VerticalTable - [x] 4.1 Create MetricTable component in frontend/src/components/pages/CCPMetricsPage.js - Render one row per metric from the `/metrics` endpoint response - Columns: Metric ID, Description, Category, Compliant, Non-Compliant, Total, Compliance %, Target % - Sort rows by non-compliant descending (server-side, already sorted) - Rows are clickable — clicking triggers `onSelectMetric(metricId)` - Handle empty state (no metrics data) - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5_ - [x] 4.2 Replace VerticalTable usage with MetricTable in the overview render logic - Add fetch call to `GET /api/compliance/vcl-multi/metrics` in the overview data loading - Pass fetched metrics data to MetricTable - Wire `onSelectMetric` to set `selectedMetric` state - _Requirements: 2.4, 2.5_ - [x] 5. Implement MetricDetailView and update drill-down state - [x] 5.1 Create MetricDetailView component in frontend/src/components/pages/CCPMetricsPage.js - Fetch data from `GET /metric/:id/verticals` on mount - Display header with metric ID, description, category - Display aggregated stats cards (total, compliant, non-compliant, compliance %) - Display table of verticals with columns: vertical name, compliant, non-compliant, total, compliance % - Clicking a vertical row calls `onSelectVertical(vertical, verticalData)` - Include "Back to Overview" button that calls `onBack` - Handle loading, error, and empty states - _Requirements: 5.1, 5.2, 5.3, 5.4, 6.2_ - [x] 5.2 Restructure drill-down state in CCPMetricsPage main component - Change state model from `selectedVertical → selectedMetric → selectedTeam` to `selectedMetric → selectedVertical → selectedTeam` - Add state variables: `selectedMetric`, `selectedMetricData`, `selectedVertical`, `selectedVerticalData`, `selectedTeam` - Update render logic: no metric = Overview (MetricTable), metric only = MetricDetailView, metric+vertical = MetricSubTeamView, all three = MetricDeviceList - Wire back-navigation handlers at each level (clear appropriate state to go up one level) - Pass `selectedVerticalData.sub_teams` to MetricSubTeamView as `metricData` - _Requirements: 5.4, 5.5, 5.6, 5.7, 5.8, 6.1, 6.2, 6.3, 6.4, 6.5_ - [ ]* 5.3 Write property test: Drill-down state determines rendered view - **Property 6: Drill-down state determines rendered view** - Generate random combinations of `(selectedMetric, selectedVertical, selectedTeam)` state values - Assert exactly one view is rendered for each combination following the state hierarchy rules - Test file: `backend/__tests__/ccp-metrics-view-restructure.property.test.js` - **Validates: Requirements 6.1, 6.5** - [ ] 6. Verify backward compatibility of existing endpoints - [ ]* 6.1 Write integration tests verifying existing endpoints are unchanged - Verify `GET /vertical/:code/metrics` returns same response shape - Verify `GET /vertical/:code/metric/:metricId/devices` returns same response shape - Verify `GET /vertical/:code/burndown` returns same response shape - Verify `GET /stats` still returns `vertical_breakdown` and `metric_breakdown` fields - Test file: `backend/__tests__/ccp-metrics-view-restructure.property.test.js` - _Requirements: 7.1, 7.2, 7.3, 7.4_ - [x] 7. 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 existing `MetricSubTeamView` and `MetricDeviceList` components are reused with minor prop adjustments — no new component creation needed for those - All backend endpoints are added to the existing `backend/routes/vclMultiVertical.js` file - All frontend changes are within `frontend/src/components/pages/CCPMetricsPage.js` - Property-based tests use `fast-check` (already in project dependencies) ## Task Dependency Graph ```json { "waves": [ { "id": 0, "tasks": ["1.1", "1.2"] }, { "id": 1, "tasks": ["1.3", "1.4", "1.5", "1.6", "1.7", "3.1"] }, { "id": 2, "tasks": ["4.1"] }, { "id": 3, "tasks": ["4.2", "5.1"] }, { "id": 4, "tasks": ["5.2"] }, { "id": 5, "tasks": ["5.3", "6.1"] } ] } ```