critical priority high complexity frontend pending fullstack developer Tier 6

Acceptance Criteria

Screen renders paginated list of pending claims; page size is 20; loading next page triggers automatically on scroll-to-bottom with a bottom progress indicator
Each claim card displays: submitter full name, total amount (NOK formatted), comma-separated expense types, submission date (dd.MM.yyyy), and ClaimStatusBadge with correct status color
Tapping a claim card opens ApprovalActionBottomSheet for single-claim review; after approve/reject the card is removed from the list with a slide-out animation
Checkbox on each card enables multi-select; selecting any claim reveals BulkApprovalBar at bottom of screen showing selected count and Approve All / Reject All actions
Cross-page multi-select: selecting all on page 1, scrolling to page 2, and selecting items there retains page 1 selections in state without re-fetching
BulkApprovalBar Approve All triggers batch approval for all selected claim IDs; progress is shown per-claim; partial failures surface individual error indicators without rolling back successes
Filter controls allow filtering by: date range, minimum/maximum amount, expense type (multi-select). Active filters are indicated visually. Clearing filters resets to full queue.
Sort controls allow sorting by: submission date (asc/desc), total amount (asc/desc). Default sort is submission date descending.
RealtimeApprovalSubscription injects new claims into the top of the list in real time; a 'New claims available' banner appears when updates arrive while the user is scrolled down
Empty state renders a meaningful illustration and message when no pending claims exist
Screen is accessible: list items have semantic labels readable by VoiceOver; checkboxes announce checked/unchecked state; BulkApprovalBar announces selection count
E2E test: peer mentor submits a claim above threshold → claim appears in coordinator queue within 2 seconds via realtime → coordinator approves → claim disappears from queue → audit timeline on claim shows coordinator action → peer mentor receives push notification

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
ApprovalWorkflowService.approveClaim()
ApprovalWorkflowService.rejectClaim()
ApprovalWorkflowService.bulkApprove()
ApprovalWorkflowService.bulkReject()
RealtimeApprovalSubscription (Supabase Realtime)
ExpenseClaimRepository.getPendingClaims(page, filters, sort)
NotificationService
data models
ExpenseClaim
ExpenseLine
ClaimApprovalDecision
AuditEvent
PaginatedResult
ApprovalFilter
ApprovalSort
performance requirements
Initial page load must render within 500ms on a standard device with a warm Supabase connection
Pagination must not re-render already-loaded cards; use SliverList with keys
Realtime updates must be debounced to prevent UI thrashing when multiple claims arrive simultaneously (debounce 500ms)
Bulk approval of up to 50 claims must complete or fail within 10 seconds with per-claim progress feedback
security requirements
Only users with coordinator role may access this screen; enforce route guard at navigation level
Bulk action API calls must include coordinator identity; server validates role before processing each claim
Realtime subscription must be scoped to the coordinator's assigned organizational unit — claims from other orgs must never appear
All claim amounts and submitter names must be treated as PII; screen must not persist these values to device storage or logs
ui components
ClaimSummaryCard
ClaimStatusBadge
BulkApprovalBar
ApprovalActionBottomSheet
FilterSortBottomSheet
RealtimeUpdateBanner
EmptyQueueIllustration
PaginationLoadingIndicator

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Model the screen's BLoC as CoordinatorReviewQueueBloc with a CoordinatorReviewQueueState containing: List claims, Set selectedClaimIds, bool isLoadingNextPage, bool hasMorePages, ApprovalFilter activeFilter, ApprovalSort activeSort, Optional realtimeError. Use a SliverList inside a CustomScrollView for performant pagination — avoid ListView.builder for this use case as it doesn't compose well with slivers for the sticky BulkApprovalBar. Implement the BulkApprovalBar as a Positioned widget inside a Stack overlaying the list, animated in/out with AnimatedSlide when selection count transitions between 0 and >0. For cross-page selection, store selected IDs in the BLoC state as a Set — never derive selection from the visible list.

For realtime, subscribe to Supabase channel in the BLoC's constructor and add RealtimeClaimAdded events; cancel subscription in close(). For the E2E tests, use flutter_test's pumpAndSettle with a custom timeout and poll the Supabase DB directly to assert audit records. The filter/sort sheet should be a separate stateless widget receiving current filter/sort values and emitting new ones via callback — keep it decoupled from the BLoC.

Testing Requirements

Unit tests (flutter_test): CoordinatorReviewQueueBloc — test LoadQueue, LoadNextPage, SelectClaim, DeselectClaim, SelectAll, ClearSelection, ApplyFilters, ApplySortOrder, RealtimeClaimAdded events and their resulting states. Mock all repository and service dependencies. Widget tests: render queue with 3 fixture claims and assert card content; tap a card and assert bottom sheet is shown; select 2 claims and assert BulkApprovalBar appears with count=2; apply a date filter and assert filtered list. Integration tests (flutter_test with Supabase test environment): full E2E pipeline as specified in acceptance criteria #12 — use a dedicated test Supabase project with seeded data; verify realtime latency <2s; verify notification delivery.

TestFlight distribution: include this screen in the first TestFlight build for coordinator beta testers across HLF and NHF.

Dependencies (7)
Build the ClaimStatusBadge Flutter widget that visually represents claim status (pending, approved, rejected, auto_approved, under_review) using the design token color system. Use accessible contrast-safe colors, include semantic labels for screen readers, and support both compact (list) and expanded (detail) display modes. epic-expense-approval-workflow-coordinator-ui-task-007 Build the BulkApprovalBar Flutter widget that appears when one or more claims are selected in the review queue. Display selected count, Approve All and Reject All action buttons, and a clear-selection control. Integrate with BulkApprovalProcessor and disable buttons during processing with a loading indicator. Meets WCAG 2.2 AA touch target requirements. epic-expense-approval-workflow-coordinator-ui-task-012 Build the RealtimeApprovalSubscription infrastructure component that subscribes to Supabase Realtime channels for the expense_claims table. Emit stream events when claims are submitted, updated, or actioned by other coordinators. Handle reconnection, channel cleanup, and coordinator-scoped filtering via RLS. epic-expense-approval-workflow-coordinator-ui-task-004 Build the ApprovalWorkflowService orchestrating the full coordinator approval flow: processing single-claim approve/reject decisions, writing approval decision records, transitioning claim status, emitting claim events, and triggering notifications. Depends on ThresholdEvaluationService, ClaimEventsRepository, ExpenseClaimStatusRepository, and ApprovalNotificationService. epic-expense-approval-workflow-coordinator-ui-task-009 Build the ClaimApprovalDecisionRepository to store and retrieve coordinator approval decisions including approve, reject, and request-more-info actions. Include audit fields (decision timestamp, coordinator ID, justification text) and Supabase RLS policies scoped to coordinator role. epic-expense-approval-workflow-coordinator-ui-task-002 Create the ApprovalActionBottomSheet Flutter widget used for single-claim review. Display claim summary metadata (submitter, amount, expense types, receipt thumbnails), provide Approve and Reject buttons with optional justification text input, and integrate with ApprovalWorkflowService. Include claim status audit timeline inline and support accessible modal navigation patterns. epic-expense-approval-workflow-coordinator-ui-task-013 Deploy a Supabase Edge Function that executes server-side threshold validation when a claim is submitted. The function reads org-specific threshold config, evaluates claim against rules (amount, distance, receipt presence), updates claim status to auto_approved or pending, and emits a claim event record. Ensures validation logic runs securely server-side. epic-expense-approval-workflow-coordinator-ui-task-008
Epic Risks (3)
medium impact medium prob technical

Maintaining multi-select state across paginated list pages is architecturally complex in Flutter with Riverpod/BLoC. If the selection state is stored in the widget tree rather than the state layer, page transitions and list redraws can silently clear selections, causing coordinators to lose their multi-select and re-enter it.

Mitigation & Contingency

Mitigation: Store the selected claim ID set in a dedicated Riverpod StateNotifier outside the paginated list widget tree. The paginated list reads selection state from this provider and does not own it. Selection persists independently of list scroll position or page loads.

Contingency: If cross-page selection proves prohibitively complex, limit bulk selection to the currently visible page (add a clear warning in the UI) and prioritise single-page bulk approval for the initial release.

medium impact medium prob integration

If a coordinator has the queue open while another coordinator approves claims from the same queue (possible in large organisations with shared chapter coverage), the Realtime update may arrive out of order or be missed during a reconnect, leaving the first coordinator's view stale and allowing them to attempt to approve an already-actioned claim.

Mitigation & Contingency

Mitigation: The ApprovalWorkflowService's optimistic locking (from the foundation epic) will catch the concurrent edit at the database level. The CoordinatorReviewQueueScreen should handle the resulting ConcurrencyException by removing the claim from the local list and showing a brief snackbar: 'This claim was already actioned by another coordinator.'

Contingency: Add a queue staleness indicator (a subtle 'last updated X seconds ago' label) and a manual refresh button as a fallback for coordinators who notice inconsistencies.

low impact high prob dependency

The end-to-end test requirement that a peer mentor receives a push notification within 30 seconds of coordinator approval depends on FCM delivery latency, which is outside the application's control and can vary significantly in CI/CD environments.

Mitigation & Contingency

Mitigation: Structure end-to-end tests to verify notification intent (correct FCM payload dispatched, correct Realtime event emitted) rather than actual device delivery timing. Use test doubles for FCM delivery in automated tests and reserve real-device delivery tests for manual pre-release validation.

Contingency: If notification timing requirements must be validated in automation, instrument the ApprovalNotificationService with a test hook that records dispatch timestamps and assert against those rather than actual FCM callbacks.