high priority low complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

Widget renders as a tappable row/field showing either 'No return date (indefinite)' or a formatted date string (e.g. '15 April 2026') when a date is selected
Tapping the field opens a date picker dialog — use Material DatePickerDialog on Android, CupertinoDatePicker inside a modal on iOS
Minimum selectable date is tomorrow (DateTime.now().add(Duration(days: 1))) — past and today dates are disabled
Widget exposes an onChanged(DateTime? date) callback that emits the selected date or null
A visible 'Clear date' action (icon button or link) appears when a date is selected, emitting null via onChanged when tapped
Semantics label on the tappable field reads: 'Expected return date, [current value or not set], tap to change'
Clear button Semantics label reads: 'Clear expected return date'
Widget uses design token colors for field background, border, and text — no hardcoded hex values
Widget functions correctly in both light and dark theme contexts
Widget is stateless — current value managed by parent; widget receives DateTime? value and onChanged callback

Technical Requirements

frameworks
Flutter
flutter_test
data models
DateTime (nullable)
performance requirements
Date picker dialog must open within one frame of tap (no async work before showing dialog)
security requirements
Selected date must be validated at widget level before emitting via onChanged — reject any date <= today regardless of picker enforcement
ui components
ExpectedReturnDatePicker (this widget)
Material DatePickerDialog (Android path)
CupertinoDatePicker in modal bottom sheet (iOS path)
Design token color and typography constants
Semantics (Flutter built-in)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Decouple the platform-specific picker invocation into a private _showPicker(BuildContext) method that calls showDatePicker (Material) or shows a CupertinoDatePicker in a showModalBottomSheet (iOS). Use Platform.isIOS or Theme.of(context).platform to choose the path. Since the widget is stateless, the parent (the pause activation form BLoC/Cubit or form widget) holds the selected date in its state. The widget's onChanged is the only communication channel — do not use a TextEditingController or FormField here.

Format dates using intl package's DateFormat.yMMMMd() for locale-aware display. The 'indefinite pause' path is the default and must be the most prominent option — the date picker is optional, matching the product spec that 60–70% of pauses will be indefinite.

Testing Requirements

Write widget tests using flutter_test. Test: (1) Widget renders 'No return date' label when value is null. (2) Widget renders formatted date when value is set. (3) Clear button is hidden when value is null, visible when value is set.

(4) Clear button tap emits null via onChanged. (5) Semantics labels are correct for both null and set states. (6) Validation rejects a today's date if somehow passed programmatically. Use Platform.isIOS check in tests or abstract the picker selection for testability.

Golden tests for null state and set state. Do not test the native date picker UI itself — test the host widget's behaviour before and after dialog interaction.

Component
Expected Return Date Picker
ui low
Epic Risks (3)
medium impact medium prob technical

Concurrent status transitions (e.g., coordinator and automated scheduler both attempting to update the same mentor's status simultaneously) may produce race conditions or inconsistent state in the database, leading to audit log gaps or incorrect notifications.

Mitigation & Contingency

Mitigation: Implement all status transitions as atomic Postgres RPC functions with optimistic locking (version column or updated_at check). Use database-level constraints rather than application-level guards as the final enforcement point.

Contingency: Add a compensation job that reconciles status and log table consistency on each nightly scheduler run, surfacing any discrepancies to coordinator dashboards.

medium impact medium prob integration

The coordinator-to-mentor assignment relationship may not always be 1:1 or may be stale (coordinator reassigned after a pause was set), causing notifications to be sent to the wrong coordinator or not sent at all.

Mitigation & Contingency

Mitigation: Query the assignment relationship at notification dispatch time rather than caching it at pause creation time. Add a fallback to notify the chapter administrator if no active coordinator assignment exists.

Contingency: Log all undeliverable notification attempts with the originating mentor ID so administrators can manually follow up, and surface undelivered notification counts on the coordinator dashboard.

medium impact low prob technical

The CoordinatorPauseRosterScreen may load slowly for coordinators managing large rosters with many concurrent certification expiry queries, degrading usability on low-bandwidth mobile connections.

Mitigation & Contingency

Mitigation: Use a single Supabase RPC that joins mentor status, certification expiry, and assignment data in one query rather than N+1 individual calls. Implement pagination with a configurable page size and skeleton loading states.

Contingency: Add an offline cache of the last-fetched roster state using Riverpod with SharedPreferences, ensuring coordinators can at minimum view stale data when connectivity is poor.