Implement ReportSummaryMetricsWidget
epic-bufdir-report-history-ui-task-001 — Build the ReportSummaryMetricsWidget Flutter widget that displays snapshot key figures captured at report generation time: total activities, total hours, and participants reached. The widget must render these three metrics in a compact, at-a-glance layout suitable for embedding inside list items, use design token system for typography and spacing, and meet WCAG 2.2 AA contrast and accessibility semantics requirements.
Acceptance Criteria
Technical Requirements
Implementation Notes
Model this as a purely stateless StatelessWidget accepting a single `ReportSummarySnapshot` data class (or equivalent). Use a Row of equally-spaced MetricChip widgets where each chip shows a label token string and a value. Source all text styles from `AppTextStyles` tokens and all spacing from `AppSpacing` tokens — never use raw pixel values. The WCAG contrast requirement means metric values should use the primary text token colour against the list item background token; verify in both light and dark theme if the app supports them.
Keep the widget narrow-first: on very small screens (320 dp), allow the Row to wrap using a Wrap widget rather than overflowing. Expose a named constructor `ReportSummaryMetricsWidget.loading()` that shows shimmer placeholders so the parent list item can render before data loads.
Testing Requirements
Widget tests using flutter_test: render widget with typical values and assert all three metric texts are present in the widget tree; render with zero values and assert fallback display; render with null model and assert no exception is thrown. Use `tester.pump()` to settle the frame. Add a golden test for the compact layout at 375 dp width. Verify Semantics tree contains the correct labels using `tester.getSemantics()`.
The ReportSummaryMetricsWidget must display metrics as they were at time of submission, not recalculated from current data. If the metrics are not stored as a JSON snapshot in the history record at export time, the widget will either show wrong data or require a full re-aggregation on every list load.
Mitigation & Contingency
Mitigation: Ensure the Bufdir export pipeline (bufdir-report-export feature) writes a summary_metrics JSONB column in the history record at export time, containing total_activities, total_hours, and participants_reached. The UI widget reads only from this snapshot field — never from live aggregation queries.
Contingency: If snapshot data is missing for historical records (e.g., older exports before the column existed), display a 'Metrics not available for this report' placeholder in the widget rather than showing zeros or triggering a live aggregation that could return different figures.
Re-export can take several seconds (it runs the full generation pipeline). Without adequate progress feedback, coordinators may tap the button multiple times, triggering duplicate exports and duplicate history records.
Mitigation & Contingency
Mitigation: Disable the re-export button immediately on first tap and show an inline progress indicator on the list item. Guard against duplicate invocations at the service layer using an in-progress flag keyed by report ID. Display a loading state on the list item throughout the operation.
Contingency: If duplicate re-export records are created (e.g., due to a race condition), the history table will show multiple entries for the same original report — which is harmless but confusing. Add a deduplication UI hint ('Re-exported N times') and a backend guard that prevents more than one in-flight re-export for the same source record ID simultaneously.