critical priority medium complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

Tapping 'Confirm Export' in the Preview phase opens ExportConfirmationDialog as a modal (showDialog) before any export action is taken
The dialog clearly states what will be exported (scope and period labels) and requires an explicit 'Confirm' tap to proceed
Cancelling the dialog returns to the Preview phase without side effects
Confirming the dialog calls BufdirExportService.startExport() and transitions the screen to the Execution phase showing ExportProgressIndicator
ExportProgressIndicator shows deterministic progress when the service emits progress events; shows indeterminate spinner otherwise
On export success, ExportScreenCubit transitions to an ExportComplete state and FileDownloadHandler.promptDownload() is called with the export file URL or blob
The completion state shows a success message and a 'Download' action; the user can dismiss back to the main reporting screen
On export failure, a plain-language error message is shown within the Execution phase (not a dialog) with a 'Retry' button
Tapping 'Retry' re-invokes BufdirExportService.startExport() with the same scope and period — no re-entry into Configuration or Preview required
All interactive elements in the Execution phase are accessible: progress indicator has a live Semantics region, dialog buttons have descriptive labels

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Riverpod
apis
Bufdir Export REST API (POST /bufdir/export)
FileDownloadHandler (platform file-save API)
data models
ExportJob
ExportJobStatus
ExportScreenState
performance requirements
Dialog open/close animation must complete within 200ms
Export job polling must not block the UI thread — use Stream-based progress from BufdirExportService
security requirements
Export POST request must carry the Supabase JWT and the user's organisation ID
Download URL returned by the API must be validated (same host, HTTPS) before passing to FileDownloadHandler
Downloaded file must be saved to the platform's sandboxed documents directory, never to a world-readable path
ui components
ExportConfirmationDialog (AlertDialog wrapper)
ExportProgressIndicator (linear or circular with semantic label)
ExportComplete success card with 'Download' button
ExportError inline message with 'Retry' button

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

BufdirExportService.startExport() should return a Stream so the cubit can forward progress events to the UI without polling. Map ExportJobStatus to ExportExecutionState variants: ExecutionLoading(progress), ExecutionSuccess(downloadUrl), ExecutionError(message). Use flutter_bloc's BlocListener (not BlocBuilder) to trigger FileDownloadHandler.promptDownload() as a one-shot side effect on ExecutionSuccess — never trigger side effects inside build(). The ExportConfirmationDialog should be shown via Navigator.push (dialog route) rather than manipulating cubit state; this avoids a phase-state/dialog-state entanglement.

Ensure the 'Retry' path does not re-open the confirmation dialog — the user already confirmed once.

Testing Requirements

Widget tests: (1) tapping 'Confirm Export' shows dialog, (2) cancelling dialog stays in Preview, (3) confirming dialog calls startExport() and enters ExportExecutionLoading state, (4) success state renders completion card and calls FileDownloadHandler, (5) failure state renders inline error with 'Retry' button, (6) 'Retry' re-calls startExport(), (7) progress indicator Semantics live region is present. Integration test (task-012 scope): full flow from Preview → Confirmation → Success → Download prompt using a mocked BufdirExportService.

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.