high priority medium complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

ExportPreviewWarningSection is rendered as a sliver below the category table when ExportPreviewLoaded contains at least one warning
The section is collapsible: collapsed by default if warning count <= 3, expanded by default if warning count > 3
A header row shows: amber warning icon, 'X warnings found' label, and a chevron toggle button
When expanded, each UnmappedTypeWarning renders: activity type name, plain-language explanation, and remediation hint on separate lines
When expanded, each MissingDataWarning renders: field name, plain-language explanation, and remediation hint on separate lines
Amber colour used for icon and header background passes WCAG 2.2 AA contrast ratio of 4.5:1 against text colour (verified with contrast-ratio-validator)
The entire warning section is wrapped in a Semantics widget with liveRegion: true so assistive technology announces it when it appears
When the section appears after a state transition from loading to loaded, the live region announcement includes: 'X warnings found. Expand for details.'
When no warnings exist, the section is completely absent from the widget tree (not just hidden)
The collapse/expand toggle has a minimum touch target of 44Ă—44 dp
Collapse/expand animation completes in 250 ms using an AnimatedCrossFade or SizeTransition

Technical Requirements

frameworks
Flutter
flutter_bloc
BLoC
data models
activity_type
bufdir_column_schema
performance requirements
Section with up to 20 warnings must render without jank during expand/collapse animation on mid-range devices
security requirements
Remediation hint text is display-only and sourced from the server DTO — sanitise for display as plain text, never render as HTML
ui components
ExportPreviewWarningSection (collapsible container)
ExportPreviewWarningHeader (icon + count + toggle)
ExportPreviewWarningItem (individual warning row)

Execution Context

Execution Tier
Tier 3

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.

Component
Export Preview Panel
ui medium
Epic Risks (2)
medium impact medium prob technical

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.

high impact medium prob scope

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.