high priority low complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

The CertificationExpiryWarningBanner widget renders below the mentor card for any mentor whose certification expiry date is within 30 days or already expired
For expiring certifications (1–30 days remaining), the banner displays 'Certification expires in X days' with a warning colour (amber/yellow per design tokens)
For expired certifications (0 or past), the banner displays 'Certification expired' with an error colour (red per design tokens)
No banner is rendered for mentors with a valid certification more than 30 days away
The banner is wrapped in a Semantics widget with liveRegion: true so screen readers announce the warning when the list item gains focus
The banner is tappable and navigates to the mentor detail page (same destination as tapping the mentor card)
Banner text meets WCAG 2.2 AA contrast ratio on both warning and error background colours
The banner renders correctly inside the existing ListView.builder without causing layout overflow
The 30-day threshold is defined as a named constant (not a magic number) so it can be adjusted centrally

Technical Requirements

frameworks
Flutter
data models
certification
performance requirements
Banner visibility computation (date diff) must be done during list item build, not on every scroll frame — precompute during BLoC state mapping
security requirements
No additional data fetch required — certification expiry date is already part of the roster payload loaded in task-009
ui components
CertificationExpiryWarningBanner widget (new)
Semantics widget with liveRegion annotation
GestureDetector or InkWell for tap-to-navigate
Design token colour references for warning and error states

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Implement CertificationExpiryWarningBanner as a separate stateless widget that accepts a DateTime? certExpiryDate and a VoidCallback onTap. Compute the days remaining inside the widget's build using DateTime.now() — but note that the BLoC state mapping should pre-sort or pre-flag expiring mentors so the filter chip can work correctly. Use design tokens for colours rather than hardcoded hex values.

The 30-day threshold should live in a shared constants file (e.g., kCertExpiryWarningThresholdDays = 30) so the same value can be used in notification logic. Keep the banner compact — a single-line ColourStrip with an icon, text, and chevron is sufficient; avoid making it taller than the mentor card itself.

Testing Requirements

Write widget tests using flutter_test covering: (1) banner renders with warning styling for a mentor with 15 days remaining, (2) banner renders with error styling for an expired certification, (3) no banner renders for a mentor with 60 days remaining, (4) tapping the banner triggers the expected navigation action, (5) Semantics liveRegion is set to true on the banner widget. Test boundary conditions: exactly 30 days remaining (banner shown), exactly 31 days (no banner), exactly 0 days (expired banner). Test that the threshold constant is used — not a hardcoded 30.

Component
Coordinator Pause Roster Screen
ui medium
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.