Wire preview panel to BufdirExportService
epic-bufdir-report-export-user-interface-task-002 — Connect ExportPreviewCubit to BufdirExportService.fetchPreview(), passing the selected organisational scope and reporting period. Handle HTTP loading, success, and error states. Ensure the service call explicitly targets the preview endpoint on the edge function (no record is finalised). Map the service response DTO to ExportPreviewState using a dedicated mapper class.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Define ExportPreviewDto as a plain Dart class with a fromJson factory — keep it separate from ExportPreviewLoaded so the domain state remains decoupled from the wire format. The mapper class (ExportPreviewMapper) is the only place that knows about both the DTO and the domain state; keep it a static utility class with no dependencies for easy testing. Use the supabase_flutter client's built-in auth header injection rather than manually setting headers. For the Edge Function, ensure the request body includes a 'preview: true' flag that the server checks before any write.
Log error status codes at the warning level using the app's logger; never log response bodies as they may contain PII.
Testing Requirements
Unit tests for ExportPreviewMapper covering: full happy-path DTO with categories, scope, period, warnings; empty categories list; null unitId; malformed DTO triggering MappingException. Integration test for BufdirExportService using a mock HTTP client (http_mock_adapter or similar) simulating 200, 403, 500, and timeout scenarios. Verify no audit log rows are created by checking the mock Supabase client's recorded write calls. Use flutter_test and mocktail for all mocks.
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.