high priority low complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

BulkApprovalBar is invisible (not rendered) when selectedCount == 0
BulkApprovalBar slides in from the bottom with an animation when selectedCount transitions from 0 to > 0
Displays '{N} selected' label that updates in real time as selection changes
Approve All button triggers BulkApprovalProcessor.process(selectedIds, approve, coordinatorId)
Reject All button triggers BulkApprovalProcessor.process(selectedIds, reject, coordinatorId)
Both action buttons are disabled and show a CircularProgressIndicator during processing
Clear selection button (X icon) calls the provided onClearSelection callback and hides the bar
All tap targets meet WCAG 2.2 AA minimum: 44x44dp touch target
Buttons are accessible with descriptive labels: 'Approve all {N} selected claims', 'Reject all {N} selected claims'
On processing completion, bar shows a success snackbar ('N claims approved') and clears selection automatically
On partial failure, bar shows a warning snackbar listing failure count ('N approved, M failed') without auto-dismissing
Widget does not manage BLoC state internally — it receives selectedIds, isProcessing, onApprove, onReject, onClearSelection as parameters

Technical Requirements

frameworks
Flutter
BLoC
flutter_test
apis
BulkApprovalProcessor (via BLoC, injected externally)
data models
BulkApprovalResult (consumed to show snackbar content)
performance requirements
Slide-in animation runs at 60fps — use AnimatedSlide or SlideTransition, not manual setState animation
Widget rebuilds only when selectedCount or isProcessing changes — use const constructors and minimal rebuild scope
security requirements
Confirm dialog required before bulk reject: 'Reject {N} claims?' with Cancel/Confirm buttons
No confirm dialog required for bulk approve (lower risk action)
ui components
BulkApprovalBar (main widget)
AppButton (reuse existing design system button)
CircularProgressIndicator (loading state)
AnimatedSlide / AnimatedContainer (show/hide animation)
ConfirmationDialog (for reject action)

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Keep BulkApprovalBar a pure stateless widget — all state (selectedIds, isProcessing) lives in the parent BLoC. Pass callbacks (onApprove, onReject, onClearSelection) as VoidCallback or typed function parameters. Use AnimatedSlide with a Tween(begin: Offset(0, 1), end: Offset.zero) for the slide-up entrance — drive it with an AnimationController in the parent screen or use AnimatedSwitcher. For the confirmation dialog on Reject All, use showDialog() with a standard AlertDialog — do not build a custom dialog.

For the snackbar, dispatch a ShowSnackbarEvent to the BLoC after BulkApprovalResult is received rather than calling ScaffoldMessenger directly inside the widget — this keeps the widget testable without a Scaffold ancestor. The '44x44dp touch target' requirement means wrapping small icon buttons in a SizedBox(width: 44, height: 44) with GestureDetector if using custom controls, or relying on IconButton's default padding which already meets this.

Testing Requirements

Widget tests: render with selectedCount=0 and assert widget is not visible. Render with selectedCount=3 and assert '3 selected' label, both action buttons enabled. Simulate tap on Approve All and assert onApprove callback is called. Simulate tap on Reject All and assert confirmation dialog appears; confirm and assert onReject is called.

Render with isProcessing=true and assert both buttons are disabled and progress indicator is visible. Assert touch target sizes >= 44dp using tester.getSize(). Verify Semantics labels contain the claim count. Golden test: snapshot in default, processing, and zero-selection states.

Target 90% branch coverage.

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.