high priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

DriverAssignmentList fetches and displays paginated driver assignments from the backend with a default page size of 20
Each list item renders: formatted assignment date (localized, e.g., 'Mon 14 Apr 2025'), assignment type label, DeclarationStatusBadge, and contact name
Contact name is masked as '••••• •••••' when the assignment's declaration status is 'not_sent' or 'pending' (i.e., declaration has not been acknowledged)
Contact name is shown in full when declaration status is 'acknowledged' or 'expired'
DeclarationStatusBadge reflects one of four states: not sent (gray), sent (blue), acknowledged (green-equivalent using design tokens), expired (amber) — using design token colors, not hardcoded hex
Tapping a list item navigates to DeclarationSendScreen with the assignment ID as a route parameter
Pull-to-refresh (RefreshIndicator) triggers a full list reload from page 1
Infinite scroll pagination loads the next page when the user scrolls within 200px of the list bottom
While loading the next page, a centered loading indicator is shown below the last item
An empty state widget is shown when no assignments exist, with the message: 'No driver assignments found'
The entire list widget is wrapped inside DriverFeatureFlagGuard
Widget tests cover: items render correctly, masked name shown for pending status, full name shown for acknowledged status, empty state renders when list is empty, loading indicator shown during pagination

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase PostgreSQL 15 (driver assignments query with pagination)
Supabase Realtime (optional: live badge updates)
data models
assignment
contact
confidentiality_declaration
declaration_acknowledgement
performance requirements
First page must render within 300ms of screen mount on a stable network
List scroll must maintain 60fps — use ListView.builder (not ListView with children array)
Contact name masking must be computed in the list item builder, not as a separate async operation
security requirements
Contact name masking is a UI-layer privacy measure — the API must also enforce that full contact details are not returned for unacknowledged declarations at the server level (RLS / Edge Function)
Assignment list query must be scoped to the authenticated coordinator's organization via Supabase RLS — never return cross-organization assignments
Declaration status must be sourced from the server on each page load — do not cache status in local widget state beyond the current session
ui components
DriverAssignmentList (ListView.builder)
DriverAssignmentListItem
DeclarationStatusBadge
RefreshIndicator
CircularProgressIndicator (pagination loading)
EmptyStateWidget
DriverFeatureFlagGuard

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use a Riverpod AsyncNotifierProvider (or StateNotifierProvider) to manage the paginated list state: List items, bool isLoadingNextPage, bool hasMorePages, String? errorMessage. For pagination, track the current offset and increment by page size (20) on each load. The ScrollController listener for infinite scroll should check: controller.position.pixels >= controller.position.maxScrollExtent - 200 before triggering loadNextPage().

For contact name masking, create a helper: String maskedName(DriverAssignment assignment) => assignment.declarationStatus == DeclarationStatus.acknowledged || assignment.declarationStatus == DeclarationStatus.expired ? assignment.contactName : '••••• •••••'. For the DeclarationStatusBadge, define a sealed enum DeclarationStatus with four values and map each to a design token color — avoid hardcoded colors. Use design token semantic names (e.g., AppColors.statusSent) rather than role-specific names to keep the badge reusable.

Ensure ListView.builder is used (not CustomScrollView with SliverList unless pagination complexity requires it) to keep the implementation simple and performant.

Testing Requirements

Widget tests using flutter_test with mocked assignment repository/provider. Test cases: (1) list renders 3 items given mock data with 3 assignments, (2) pending declaration → contact name masked, (3) acknowledged declaration → contact name visible, (4) empty list → EmptyStateWidget shown, (5) pagination loading indicator shown when fetching next page, (6) pull-to-refresh triggers reload from page 1, (7) tapping a list item triggers navigation to DeclarationSendScreen route with correct assignment ID. Use Mockito/Mocktail to mock the Supabase data layer. Aim for 85% branch coverage on DriverAssignmentList and DriverAssignmentListItem.

Verify DriverFeatureFlagGuard wrapping by asserting the list is not rendered when the driver feature flag is disabled.

Component
Driver Assignment History List
ui medium
Epic Risks (3)
high impact medium prob technical

The declaration acknowledgement screen has the most complex accessibility requirements of any screen in this feature: scrollable long-form legal text, a conditional checkbox that is only enabled after reading, and a timestamp capture. Incorrect focus management or missing semantics annotations could fail VoiceOver navigation or cause the screen reader to announce the checkbox as available before the driver has scrolled, undermining the legal validity of the acknowledgement.

Mitigation & Contingency

Mitigation: Build the acknowledgement screen against the WCAG 2.2 AA checklist from the start, not as a post-hoc audit. Use semantics-wrapper-widget and live-region-announcer from the platform's accessibility toolkit. Include a VoiceOver test session in the acceptance criteria with a tester using the screen reader.

Contingency: If WCAG compliance cannot be fully achieved within the sprint, ship the screen with a documented list of accessibility gaps and a follow-up sprint commitment. Do not block the declaration workflow launch if the core interaction works but a non-critical semantics annotation is missing.

medium impact medium prob integration

Drivers receive a push notification with a deep link to the declaration acknowledgement screen for a specific assignment. If the deep link handler does not correctly route to the right screen and assignment context — particularly when the app is launched cold from the notification — the driver may see a blank screen or the wrong declaration.

Mitigation & Contingency

Mitigation: Implement and test all three notification scenarios: app foregrounded, app backgrounded, and cold start. Use the platform's existing deep-link-handler infrastructure. Add integration tests that simulate notification tap events and assert correct screen and data loading.

Contingency: If cold-start deep link routing proves unreliable, implement a notification-centre fallback where the driver can find the pending declaration from the notification centre screen, ensuring the workflow can always complete even if the direct deep link fails.

medium impact low prob technical

If the driver-feature-flag-guard has any rendering edge case — such as a brief flash of driver UI before the flag value is loaded, or a guard that fails open on a flag service error — driver-specific UI elements could be momentarily visible to coordinators in organizations that have not opted in, causing confusion and potentially a support escalation.

Mitigation & Contingency

Mitigation: Default the guard to rendering nothing (not a loading indicator) until the flag value is definitively resolved. Treat flag service errors as flag-disabled to fail closed. Write widget tests covering the loading, disabled, and enabled states including the error case.

Contingency: If fail-closed cannot be guaranteed within the sprint, add a server-side RLS check on the driver assignment endpoints so that even if the UI guard leaks, the data layer refuses to return driver data for organizations without the flag enabled.