Implement unmapped activity and missing data warning section
epic-bufdir-report-export-user-interface-task-005 — Within ExportPreviewPanel, render a collapsible warning section that lists unmapped activity types (types present in the dataset but not mapped to any Bufdir category) and missing required data fields. Use an amber warning colour from the contrast-safe colour palette. Each warning item must include a plain-language explanation and a suggested remediation hint. The section must announce itself as a live region to assistive technology.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Use a SliverToBoxAdapter wrapping an AnimatedSize or custom expansion widget — avoid ExpansionTile if its styling conflicts with design tokens, as it can be difficult to override comprehensively. The liveRegion announcement is tied to the widget appearing in the tree; wrap the section in a Semantics with liveRegion: true and ensure the parent rebuilds on state change (BlocBuilder). For the amber colour, use the design token AppColors.warningAmber (or equivalent) — never hardcode hex values. Remediation hints come from the server DTO (UnmappedTypeWarning.remediationHint); if the hint is empty, show a generic fallback: 'Contact your coordinator to update the activity type mapping.' Keep the warning item layout simple: Column with CrossAxisAlignment.start, no nested BLoC providers.
Testing Requirements
Widget tests: render with 0 warnings and verify section is absent from widget tree; render with 2 warnings (collapsed default) and verify header text and collapsed state; render with 5 warnings (expanded default) and verify all warning items visible; tap toggle and verify expanded/collapsed state change; verify Semantics liveRegion is true on the section wrapper; verify amber colour contrast with a helper function asserting ratio >= 4.5:1. Accessibility test verifying touch target size of toggle button >= 44 dp using SemanticsController. Animation test: pump and settle after state change and verify final layout matches expected.
The preview panel requires a round-trip to the edge function to compute aggregated counts. If this call takes 5–15 seconds for large scopes, users may assume the app has frozen and navigate away, potentially triggering duplicate preview requests or leaving the export in an undefined state.
Mitigation & Contingency
Mitigation: Show an immediate skeleton loading state on the preview panel as soon as the period and scope are confirmed, with named stage labels (e.g. 'Querying activities…', 'Computing totals…') streamed from the edge function's progress events. Set a clear user-visible timeout with a retry option. Pre-warm the edge function with a lightweight ping when the scope selector is opened.
Contingency: If preview latency consistently exceeds 10 seconds for large scopes, cache the preview payload in local BLoC state and allow the coordinator to proceed to confirmation without re-fetching if scope and period have not changed since the last preview.
The export preview panel contains dynamic content (warning badges, loading skeletons, aggregated counts) that must be announced correctly by VoiceOver and TalkBack. Incorrect semantics annotations could make the preview unreadable for blind coordinators, violating the project's WCAG 2.2 AA accessibility mandate.
Mitigation & Contingency
Mitigation: Implement semantic annotations using Flutter's Semantics widget with explicit labels for all dynamic content. Use live region announcements for loading state transitions. Schedule a dedicated accessibility review session with a screen reader user (Blindeforbundet has relevant expertise) before marking the epic complete.
Contingency: If the preview panel cannot be made fully screen-reader-accessible in time for launch, ship a simplified text-only summary mode activated by a toggle at the top of the preview panel, which renders all data as a single readable paragraph with no interactive badges.