high priority medium complexity frontend pending fullstack developer Tier 5

Acceptance Criteria

Screen fetches only claims where the coordinator's organisation_id matches and status is pending on initial load
Claims are sorted by submitted_at ascending (oldest first) so the coordinator processes in FIFO order
Each list item displays: submitter name, activity date, total amount, and a status badge using the design token color for the claim status
Supabase realtime subscription is established on screen mount and torn down on dispose
A new pending claim submitted by any peer mentor in the coordinator's org appears in the list within 3 seconds without a manual refresh
A claim that transitions to approved or rejected via realtime update is removed from the pending list or updates its badge immediately
An empty-state illustration and message 'No pending claims' is shown when the list is empty
Pull-to-refresh triggers a full re-fetch as a fallback to realtime
Screen is accessible: list items have Semantics labels combining submitter name, amount, and status; status badge color changes are also communicated via label text
Screen renders correctly at font scale 2.0x without overflow

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Dart
Supabase Flutter SDK
apis
ExpenseAttestationService.fetchPendingClaims(coordinatorOrgId)
Supabase Realtime (postgres_changes on expense_claims table, filter: organisation_id=eq.{id})
data models
ExpenseClaim
AttestationQueueState (Loading, Loaded, Error variants)
ClaimStatusBadge design token mapping
performance requirements
Initial list load must complete within 1.5 seconds on a 4G connection
ListView must use itemExtent or SliverFixedExtentList to avoid excessive layout passes for large queues (100+ items)
Realtime events must be diffed against current list state; do not rebuild the full list on each event
security requirements
Supabase RLS must ensure a coordinator can only subscribe to and read claims for their own organisation
Realtime channel must be authenticated; anonymous subscriptions must be rejected
Personal data (submitter name, amounts) must not be logged
ui components
ClaimListItemWidget
ClaimStatusBadge
EmptyQueueIllustration
PullToRefresh wrapper

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Use StreamSubscription from Supabase Realtime and pipe events into the BLoC via a dedicated RealtimeClaimEvent. Keep the subscription lifecycle inside the BLoC's constructor/close rather than in the widget to avoid re-subscription on rebuilds. For diffing, maintain a Map in the BLoC state indexed by claim ID so realtime updates are O(1) lookups. Status badge colors must come from the design token system (AppColors.statusPending, statusApproved, statusRejected) — never hardcoded hex values.

Use the organisation labels system to localise any status strings displayed to the user. Lazy-load submitter names from a local cache (contacts already in the Contacts feature) to avoid N+1 fetches.

Testing Requirements

Widget tests using flutter_test: verify list renders correct number of items given a mocked service response; verify empty-state is shown when service returns empty list; verify status badge color maps correctly to each status value. BLoC unit tests: verify LoadQueue event triggers fetch and emits Loaded state; verify realtime INSERT event adds item to list; verify realtime UPDATE event updates existing item badge. Integration test: mount the screen against a Supabase test instance, insert a claim, assert it appears in the list within 3 seconds. Accessibility test: run flutter_test Semantics tree assertions on each list item to confirm label content.

Component
Attestation Queue Screen
ui medium
Epic Risks (3)
medium impact medium prob dependency

The image_picker Flutter plugin requires platform-specific permissions (NSPhotoLibraryUsageDescription, camera permission) and behaves differently across iOS and Android versions. Permission denial or plugin misconfiguration can silently prevent receipt attachment.

Mitigation & Contingency

Mitigation: Configure all required permission strings in Info.plist and AndroidManifest.xml during initial plugin setup. Use the permission_handler package to check and request permissions before launching the picker, with clear user-facing explanations. Test on both platforms across at least two OS versions.

Contingency: If image_picker proves unreliable on a specific platform version, fall back to file_picker as an alternative that uses the OS document picker interface, which requires fewer permissions on some Android versions.

high impact medium prob technical

The expense form BLoC manages interconnected state across expense type selection, field visibility, receipt requirement, threshold evaluation, and submission flow. Incorrect state transitions can cause UI inconsistencies such as required receipt indicator not updating after amount change, or form appearing valid when mutual exclusion is violated.

Mitigation & Contingency

Mitigation: Model BLoC states as sealed classes with exhaustive pattern matching. Write state transition unit tests covering every combination of: type selection change, amount field change above/below threshold, receipt attachment/removal, and offline mode toggle. Use bloc_test for comprehensive state sequence assertions.

Contingency: If BLoC complexity becomes unmanageable, split into two BLoCs — one for type selection/exclusion state and one for field values/submission — coordinating via a parent provider, accepting the small overhead of inter-BLoC communication.

high impact medium prob technical

The expense type selector must enforce mutual exclusion visually by disabling options and showing conflict tooltips, while remaining fully accessible to screen reader users who cannot perceive visual disable states. Incorrect semantics labelling will fail WCAG 2.2 AA requirements critical for Blindeforbundet and HLF users.

Mitigation & Contingency

Mitigation: Use Flutter Semantics widgets to explicitly set disabled state and provide conflict explanations as semanticLabel strings on disabled options. Run accessibility audits with TalkBack and VoiceOver during widget development, not post-completion. Reference the project's accessibility test harness for required test coverage.

Contingency: If custom widget accessibility is difficult to certify, implement the selector as a standard Flutter Radio/Checkbox group with built-in accessibility semantics and an explanatory Text widget below each conflicting option, sacrificing visual elegance for guaranteed WCAG compliance.