Files
cve-dashboard/.kiro/specs/ccp-metrics-view-restructure/tasks.md
Jordan Ramos a61d254ff9 Sync .kiro/ from master — v2.2.0 release batch
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
2026-06-04 11:27:31 -06:00

9.2 KiB

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

  • 1. Add backend endpoints for metric-centric aggregation

    • 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
    • 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
  • 2. Checkpoint - Backend endpoints verified

    • Ensure all tests pass, ask the user if questions arise.
  • 3. Remove "By Vertical" table from AggregatedBurndownChart

    • 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
  • 4. Implement MetricTable component and replace VerticalTable

    • 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
    • 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
  • 5. Implement MetricDetailView and update drill-down state

    • 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
    • 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
  • 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

{
  "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"] }
  ]
}