critical priority medium complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

ApprovalActionSheet renders as a modal bottom sheet using Flutter's showModalBottomSheet with isDismissible: false and enableDrag: false
Claim summary header displays claim identifier, claimant name, amount, and submission date using data from ExpenseClaim entity
Two action buttons (Approve and Reject) are displayed with distinct visual treatment — approve uses primary/success color token, reject uses destructive/error color token
Comment text field is present below the action buttons with appropriate label and placeholder text
Sheet cannot be dismissed by tapping the scrim or dragging — only explicit Cancel button or post-confirmation action closes it
Cancel button is present and immediately dismisses the sheet without any state change
Sheet is scrollable if content overflows (e.g., long claim descriptions) without breaking layout
All interactive elements meet WCAG 2.2 AA touch target minimum of 44x44dp
Sheet uses design token system for all colors, typography, and spacing — no hardcoded values
Widget is exported as a reusable component and accepts ExpenseClaim model plus onApprove/onReject callbacks as constructor parameters

Technical Requirements

frameworks
Flutter
BLoC
data models
claim_event
activity
performance requirements
Sheet must animate open in under 300ms using default Flutter bottom sheet animation curve
No jank during scroll — claim summary content must be pre-loaded before sheet opens
security requirements
Claim data displayed in sheet must be fetched via authenticated Supabase client with RLS enforcement
No claim PII (claimant name, amount) stored in widget state beyond sheet lifecycle
ui components
ModalBottomSheet wrapper with WillPopScope or PopScope for dismiss control
ClaimSummaryHeader widget (reusable sub-widget)
AppButton (approve variant, reject variant)
AppTextField (comment input)
Cancel TextButton

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use showModalBottomSheet with barrierDismissible: false. Wrap the sheet body in a PopScope widget (Flutter 3.16+) with canPop: false to block back-gesture dismissal on Android. Use a DraggableScrollableSheet only if content may overflow — otherwise a simple SingleChildScrollView Column suffices. Keep widget stateless at this scaffold stage; local state (selected action, comment text, confirmation step) will be added in subsequent tasks.

Model the sheet header after the existing AppPageHeader pattern for consistency. Ensure the sheet respects the device's bottom safe area (MediaQuery.of(context).padding.bottom) so the Cancel button is never obscured by the home indicator on iPhone.

Testing Requirements

Write widget tests verifying: sheet renders with correct claim summary data passed via constructor; scrim tap does not dismiss sheet; drag gesture does not dismiss sheet; Cancel button calls Navigator.pop; Approve and Reject buttons are present and tappable. Use flutter_test with WidgetTester.tap and pumpAndSettle. Verify no overflow RenderFlex errors at standard iPhone SE viewport (375x667).

Component
Approval Action Bottom Sheet
ui medium
Epic Risks (2)
medium impact medium prob scope

If a bulk approval batch partially fails (some claims approved, some failed), the UI must communicate which specific claims failed without overwhelming the coordinator. A poorly designed error display could cause coordinators to re-approve already-approved claims or miss claims that still need attention.

Mitigation & Contingency

Mitigation: Design the BulkApprovalResult display to show a clear summary (e.g., '14 approved, 2 failed') with a collapsible list of failed claims including their IDs and submitter names. Failed claims should remain selected in the queue so the coordinator can retry them individually.

Contingency: If the summary UI proves insufficient, add a dedicated 'bulk action history' sheet showing the last bulk operation result, accessible from the queue screen header.

medium impact low prob technical

If the app is backgrounded or the network drops while the coordinator has the ApprovalActionSheet open mid-decision, the typed comment could be lost and the transition state could be ambiguous, potentially causing a coordinator to believe they approved a claim that was never submitted.

Mitigation & Contingency

Mitigation: Persist the in-progress action sheet state (selected action + comment text) to a local draft store keyed on claim ID. On sheet re-open for the same claim, restore the draft. After confirmed submission, verify the resulting claim status from the server before dismissing the sheet.

Contingency: On network error during submission, display a persistent retry banner within the sheet rather than dismissing it, so the coordinator can resubmit without re-entering their comment.