high priority low complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

Tapping the export button dispatches a ConfirmExportRequested event to ExportTriggerBloc
BufdirExportConfirmationDialog opens as a modal with live values: scope, period label, record count, and selected format
Record count displayed in dialog matches the value from ExportTriggerBloc state at the moment the dialog opens
Tapping 'Confirm' in the dialog dispatches ExportInitiated and dismisses the dialog
Tapping 'Cancel' or pressing the back button dismisses the dialog and returns ExportTriggerBloc to idle state without any side effects
On cancel, Flutter focus is programmatically returned to the export trigger button (FocusNode.requestFocus)
Dialog does not open if ExportTriggerBloc is already in an exporting or loading state
All dialog fields populate correctly from BlocBuilder — no hardcoded or stale values
Widget test verifies the full open → cancel and open → confirm event sequences
Dialog is fully dismissible via Android back gesture and iOS swipe-down with the same cancel behaviour

Technical Requirements

frameworks
Flutter
BLoC
data models
bufdir_export_audit_log
performance requirements
Dialog must open within one frame after button tap — no async gap before showDialog
No unnecessary BLoC state rebuilds on dialog open/close
security requirements
Export action must only be dispatchable by users with coordinator or org-admin role — validate role in BLoC before emitting ExportInitiated
Organisation scope shown in dialog must be derived from authenticated JWT claims, not passed as user-editable input
ui components
BufdirExportConfirmationDialog
ExportTriggerScreen
FocusNode for trigger button
BlocBuilder<ExportTriggerBloc, ExportTriggerState>

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Use showDialog with barrierDismissible: false to prevent accidental dismissal during confirmation. Pass a FocusNode to the trigger button widget and call node.requestFocus() inside the onCancel callback before popping the dialog. Use BlocBuilder inside the dialog to read live state — avoid capturing state in a local variable before showDialog as it may become stale. Keep ExportTriggerBloc state transitions deterministic: ConfirmExportRequested → AwaitingConfirmation, ExportInitiated → Exporting, cancel → Idle.

Do not use Navigator.pop inside the BLoC — the UI layer owns navigation; the BLoC only emits state.

Testing Requirements

Write widget tests using flutter_test and bloc_test covering: (1) button tap emits ConfirmExportRequested and dialog appears, (2) dialog displays correct scope/period/count/format from mocked BLoC state, (3) confirm tap emits ExportInitiated and dialog dismisses, (4) cancel tap returns to idle state and dialog dismisses, (5) focus node is restored after cancel. Use MockExportTriggerBloc to control state. Aim for 100% branch coverage of the dialog integration logic.

Component
Bufdir Export Trigger Screen
ui medium
Epic Risks (2)
medium impact medium prob technical

For large exports that run for 10–30 seconds, a static loading spinner will feel broken to users on slow mobile connections. If the UI cannot display meaningful progress during the export pipeline, coordinators may abandon the flow or trigger duplicate exports by pressing the button multiple times.

Mitigation & Contingency

Mitigation: Implement streaming progress events from the orchestrator BLoC through named pipeline stages (querying, mapping, generating, uploading). Display each stage label with a progress indicator on the trigger screen. Disable the generate button immediately on first tap to prevent duplicates.

Contingency: If streaming pipeline progress is not feasible in the first release, implement a deterministic stage-based progress animation (10% querying, 50% generating, 90% uploading) that gives users feedback without requiring real server events.

high impact medium prob technical

Custom date range pickers are among the most common accessibility failures in mobile apps. Blindeforbundet users rely on VoiceOver, and NHF users include people with cognitive impairments. A non-accessible period picker could make the entire export workflow unusable for a significant portion of the intended user base.

Mitigation & Contingency

Mitigation: Build the period picker using Flutter's native date picker semantics as the foundation, with preset shortcuts as primary navigation (reducing the need to interact with the custom range picker at all). Test with VoiceOver on iOS and TalkBack on Android before UI epic sign-off. Engage Blindeforbundet's test contact for accessibility validation.

Contingency: If the custom date range picker cannot be made fully accessible before release, ship only the preset period shortcuts (covering the majority of use cases) and add the custom range picker in a follow-up sprint after dedicated accessibility remediation.