Add CertificationExpiryWarningBanner to roster screen
epic-peer-mentor-pause-management-core-workflows-task-010 — Integrate the CertificationExpiryWarningBanner widget into the CoordinatorPauseRosterScreen. For each mentor with an expiring or expired certification, render a prominently styled warning banner below the mentor card showing days remaining or an expiry notice. Ensure the banner is accessible with live region announcements for screen readers. Banner should link to the mentor detail page for coordinator action.
Acceptance Criteria
Technical Requirements
Execution Context
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.
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.
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.
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.