high priority low complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

Dialog displays: formatted date range (e.g., '1 Jan 2025 – 31 Jan 2025'), target system name ('Xledger' or 'Microsoft Dynamics 365'), record count (e.g., '14 claims'), and estimated total formatted as Norwegian locale currency (e.g., 'NOK 4 250,00')
Cancel button dismisses the dialog and invokes onCancelled callback; Confirm Export button invokes onConfirmed callback
When isLoading: true is passed, both buttons are disabled, the Confirm button shows a CircularProgressIndicator, and the Cancel button is hidden
Dialog background uses design token surface color; text colors meet WCAG 2.2 AA 4.5:1 contrast ratio against the surface
All text elements and buttons have Semantics labels; the dialog itself is wrapped in a Semantics(scopesRoute: true) for screen reader navigation
Widget accepts only plain value parameters (no BLoC/provider); caller manages state and passes isLoading
Unit and widget tests cover: normal render, loading state, cancel tap, confirm tap, and accessibility tree

Technical Requirements

frameworks
Flutter
flutter_test
performance requirements
Dialog animates open/close within 200ms using default Flutter dialog transition
security requirements
Widget displays only aggregated summary data (count, total) — never individual claim PII
No data persisted by this widget
ui components
ExportSummaryRow — labelled key-value row for date range, system, count, total
TargetSystemBadge — small colored badge indicating Xledger vs Dynamics
DestructiveConfirmButton — primary action button with loading state support

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Build as a StatelessWidget shown via showDialog(). Accept an ExportSummaryData value object containing: DateRange dateRange, ExportTargetSystem targetSystem (enum: xledger, dynamics), int claimCount, double estimatedTotal. Accept bool isLoading, VoidCallback onConfirmed, VoidCallback onCancelled. Format currency using the intl package with 'nb_NO' locale (already available if intl is in pubspec).

Use AlertDialog as base to get built-in accessibility scaffolding. Target system badge: use a Row with a colored Container dot + Text for the system name — colors from design tokens (e.g., tokenXledgerColor, tokenDynamicsColor). When isLoading, replace the actions row with a centered CircularProgressIndicator at the same height to avoid layout shift. Do not use Navigator.pop inside the widget — always call the callbacks and let the caller dismiss.

Testing Requirements

Widget tests (flutter_test): render dialog with sample data and verify all four summary fields appear; tap Cancel and verify onCancelled called; tap Confirm and verify onConfirmed called; pass isLoading: true and verify buttons disabled and spinner visible. Accessibility test: verify Semantics tree, contrast ratios via flutter_test accessibilityGuidelines. Golden test: normal state and loading state snapshots. No unit tests for pure UI widget beyond widget tests.

Component
Export Confirmation Dialog
ui low
Epic Risks (3)
high impact medium prob technical

Adding exported_at and export_run_id columns to expense_claims requires a live migration on a table shared with the approval workflow. A poorly timed migration could lock the table and block claim submissions or approvals.

Mitigation & Contingency

Mitigation: Use non-blocking ADD COLUMN with a DEFAULT of NULL (no backfill needed) executed during a low-traffic window. Test migration rollback on a staging replica before production deployment.

Contingency: If migration causes table lock contention, roll back and reschedule for a maintenance window. Use a feature flag to gate the export UI until the migration completes successfully.

medium impact high prob scope

Chart of accounts mapping configurations for Xledger and Dynamics may not be fully specified by stakeholders at development time, leaving the mapper with incomplete data and causing validation failures for unmapped expense categories.

Mitigation & Contingency

Mitigation: Implement the mapper to return a structured validation error (not a crash) for any unmapped field, and surface these errors clearly in the export confirmation dialog. Request full mapping tables from Blindeforbundet and HLF stakeholders as a pre-condition for this epic.

Contingency: If mappings arrive incomplete, ship the mapper with the available subset and mark unmapped categories as excluded (skipped with reason). Coordinators see which categories are skipped and can manually submit those records.

medium impact medium prob dependency

Supabase Vault configuration for storing per-org accounting credentials may require infra permissions or environment secrets not yet provisioned in staging or production, blocking development and testing of credential retrieval.

Mitigation & Contingency

Mitigation: Provision Vault secrets and environment configuration in staging as the first task of this epic. Document the exact secret naming convention and rotation procedure before implementation begins.

Contingency: If Vault is unavailable, use environment variables scoped to the Edge Function as a temporary fallback for development. Block production deployment until Vault-based storage is confirmed operational.