high priority low complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

The panel renders a chronological list (most recent first) of completed export runs for the current organisation
Each row displays: export date range (start–end), run timestamp (formatted to locale), number of claims included, exporter type label ('Xledger' or 'Dynamics 365'), and run status badge
All text in the panel uses design token typography β€” no hardcoded font sizes or colours
Status badge uses design token colour system: success green for completed, amber for partial, red for failed
The panel supports pull-to-refresh (RefreshIndicator) that re-fetches from the Export Run Repository
Data is fetched asynchronously β€” the panel shows a loading indicator while the first fetch is in progress
The widget is stateless regarding business logic β€” all state managed via BLoC or Riverpod provider
The panel is organisation-scoped: only export runs for the authenticated user's current organisation are shown
Row tap is a no-op in this task (detail navigation handled in a later task) β€” but rows must be visually non-interactive (no ink splash unless tappable)
Widget renders correctly on screen widths from 320px to 428px (standard Flutter phone range)
All visible strings are externalised for localisation (no hardcoded Norwegian or English strings inline)

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase PostgreSQL 15 (Export Run Repository read queries)
data models
annual_summary
activity
performance requirements
Initial list render must complete within 300ms for up to 50 export run records
Pull-to-refresh must not cause visible jank β€” use ListView.builder for lazy rendering
Widget must not trigger unnecessary rebuilds β€” use const constructors for static child widgets
security requirements
Export run data fetched with RLS-enforced queries β€” user cannot see other organisations' runs
No sensitive claim content displayed in the list β€” only metadata (count, date range, type, status)
ui components
ExportHistoryPanel (root widget)
ExportRunListTile (individual row widget)
ExportStatusBadge (status indicator chip)
ExportTypeLabel (Xledger / Dynamics label)
RefreshIndicator (pull-to-refresh wrapper)

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Keep ExportHistoryPanel a pure presentation widget β€” accept a list of ExportRunViewModel objects as its data input, not a repository reference. The BLoC/Riverpod layer handles fetching and exposes a stream/state that the panel listens to. Define ExportRunViewModel as an immutable Dart class with only display-ready fields (pre-formatted date strings, localised status label, count as int). Use ListView.builder with itemExtent set to a design-token-derived value for smooth scrolling performance.

For the status badge, use a small Container with BorderRadius and a background colour derived from design tokens β€” avoid third-party badge packages. The exporter type label ('Xledger' / 'Dynamics 365') should be derived from an enum with a display name getter, not a raw string stored in the database, so the UI is resilient to schema changes.

Testing Requirements

Widget tests using flutter_test: render panel with mock list of 0, 1, and 20 export runs; verify each row displays all required fields; verify pull-to-refresh triggers repository re-fetch; verify design token classes are applied (spot-check key Text and Container styles); verify no overflow on 320px width. Golden file tests for the ExportRunListTile to catch visual regressions. No integration tests required for this task.

Component
Export History Panel
ui low
Epic Risks (3)
high impact medium prob dependency

The Xledger CSV/JSON import specification may not be available in full detail at implementation time. If the field format, column ordering, encoding requirements, or required fields differ from assumptions, the generated file will be rejected by Xledger on first production use.

Mitigation & Contingency

Mitigation: Obtain the official Xledger import specification document from Blindeforbundet before starting XledgerExporter implementation. Build a dedicated acceptance test that validates a sample export file against all documented constraints.

Contingency: If the spec arrives late, implement a configurable column-mapping layer so that field order and names can be adjusted via configuration without code changes. Ship a file-based export that coordinators can manually verify before connecting to Xledger import.

high impact low prob technical

The atomic claim-marking transaction in Double-Export Guard could fail under high concurrency if two coordinators trigger an export for overlapping date ranges simultaneously, potentially allowing duplicate exports to proceed past the guard.

Mitigation & Contingency

Mitigation: Use a database-level advisory lock or a SELECT FOR UPDATE on the relevant claim rows within the export transaction to serialize concurrent exports per organization. Add an integration test that simulates concurrent export triggers.

Contingency: If locking proves problematic at the database level, implement an application-level distributed lock using a Supabase row in a dedicated export_locks table with an expiry timestamp and automatic cleanup on failure.

medium impact high prob integration

HLF's Dynamics portal API endpoint may not be available or documented in time for Phase 1, leaving DynamicsExporter unable to be validated against a real system and potentially shipping with an incorrect field schema.

Mitigation & Contingency

Mitigation: Design DynamicsExporter for file-based export first (CSV/JSON download), with the API push implemented behind a feature flag. Request a Dynamics test environment or sandbox from HLF as early as possible.

Contingency: Ship DynamicsExporter as a file export only for Phase 1. Phase the API push integration into a follow-on task once the Dynamics sandbox is available, using the same AccountingExporter interface with no breaking changes.