critical priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

ExportScopeSelector and ExportPeriodPicker render within the Configuration phase container without overflow
Selecting a scope value calls ExportScreenCubit.updateScope() and updates the cubit state synchronously
Selecting a period value calls ExportScreenCubit.updatePeriod() and updates the cubit state synchronously
The 'Preview' action button is disabled (non-interactive, visually muted) when scope or period is null/invalid
The 'Preview' button becomes enabled immediately after both scope and period hold valid values — no manual refresh required
Changing scope or period after the preview was previously loaded invalidates the cached preview (ExportPreviewCubit resets to idle)
Scope and period values are exposed via Riverpod providers (exportScopeProvider, exportPeriodProvider) so ExportPreviewPanel can read them without prop drilling
Providers are scoped to BufdirExportScreen — they are disposed when the screen is popped
Screen reader labels on ExportScopeSelector and ExportPeriodPicker are descriptive and announced on value change
Design tokens are used for all spacing, typography, and button states

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Riverpod
data models
ExportScope
ExportPeriod
ExportScreenState
performance requirements
Scope/period state update and button re-render must complete within a single frame
Riverpod providers must not cause unnecessary widget rebuilds outside the Configuration phase
security requirements
Scope values must be validated against the user's permitted reporting units before being stored in state — reject out-of-bounds scope IDs
ui components
ExportScopeSelector widget
ExportPeriodPicker widget
Primary action button ('Preview') with disabled state
Riverpod ProviderScope override at screen level

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Prefer StateNotifierProvider (Riverpod) for exportScopeProvider and exportPeriodProvider rather than reading directly from the BLoC, so the preview panel remains decoupled from the cubit. The cubit still owns the canonical state; providers simply mirror the relevant slice. Guard against race conditions: if the user rapidly changes scope/period, debounce the preview-invalidation event by 300ms to avoid firing multiple resets. Keep ExportScopeSelector and ExportPeriodPicker as purely presentational widgets — they call a callback; callers write to state.

Testing Requirements

Widget tests: (1) 'Preview' button is disabled when scope is null, (2) 'Preview' button is disabled when period is null, (3) 'Preview' button enables after both are set, (4) changing scope after preview was loaded resets ExportPreviewCubit to idle state (mock the cubit), (5) Riverpod exportScopeProvider and exportPeriodProvider return correct values after selection, (6) Semantics test that selector change announcements are present. Use mockito or mocktail for cubit mocks.

Component
Bufdir Export Screen
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.