Integrate ExportPreviewPanel into export screen Preview phase
epic-bufdir-report-export-user-interface-task-009 — Mount the ExportPreviewPanel inside BufdirExportScreen's Preview phase. When the user navigates from Configuration to Preview, trigger ExportPreviewCubit.fetchPreview() with the committed scope and period. Show ExportProgressIndicator during the preview fetch. Provide a 'Back to Configuration' button that clears preview state and returns to phase one. Show the 'Confirm Export' button only when the preview loaded successfully with no blocking errors.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Model the preview fetch lifecycle as ExportPreviewState variants: PreviewIdle, PreviewLoading, PreviewLoaded(data, hasBlockingErrors), PreviewError(message). Use BlocBuilder with buildWhen to avoid rebuilding the entire phase widget on unrelated cubit emissions. Cache the last successful ExportPreviewResult inside the cubit (not the repository) and return it immediately on re-entry if scope and period match the cached query key. The 'Confirm Export' button visibility should use an Offstage or conditional rendering — never just opacity: 0 — to prevent screen readers from discovering a hidden interactive element.
Testing Requirements
Widget tests: (1) entering Preview phase triggers fetchPreview() on the cubit — verify via bloc_test emitted states, (2) loading state shows ExportProgressIndicator and hides 'Confirm Export', (3) loaded state with no blocking errors shows 'Confirm Export', (4) loaded state with blocking errors hides 'Confirm Export', (5) error state shows error message and no 'Confirm Export', (6) 'Back to Configuration' always visible and calls resetPreview() on tap, (7) second entry to Preview phase without scope/period change does not call fetchPreview() again. Use bloc_test and mocktail.
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.