high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

A RenewalHistoryTimeline widget renders a vertical list of RenewalEvent items, each showing: renewal date (locale-formatted), certification type tag, and optionally a 'recorded by' label if present in the data
Timeline visual uses a left-side vertical line with circular dots at each event node — implemented with CustomPaint or a Column+Divider pattern using design token colors (no third-party timeline packages unless already in pubspec)
Events are ordered chronologically descending (most recent first) by default
Empty state: when renewal_history is null or empty, render a centered message 'No renewal history yet' using design token secondary text style — no blank space or invisible section
The widget is scrollable independently if the history list is long (use ListView.builder for lists with more than 3 items)
Each timeline item has a semanticsLabel combining date and cert type for screen reader output (e.g., 'Renewal on March 26, 2025: HLF Peer Mentor Level 1')
Widget test covers: list with 3 items renders all 3; empty list renders empty state message; items appear in descending date order
Tapping a timeline item does nothing in this task (interaction reserved for a future task) — no GestureDetector required
Section title 'Renewal History' is displayed above the timeline using design token heading style

Technical Requirements

frameworks
Flutter
BLoC (renewal_history from CertificationLoadedState)
data models
RenewalEvent (date, cert_type, recorded_by?)
renewal_history: List<RenewalEvent> from peer_mentor_certifications JSONB column
performance requirements
Use ListView.builder for the timeline list to avoid building all items at once — important for peer mentors with long histories (HLF had a user with 380 registrations in one year)
Avoid calling DateTime.parse inside build — parse dates in the BLoC or a mapper before passing to the widget
security requirements
renewal_history data comes from Supabase — ensure the BLoC validates the JSONB parsing before passing to the widget to avoid runtime cast errors crashing the UI
ui components
ListView.builder
CustomPaint or Container-based timeline connector
Chip or Container tag for cert type label
Text with Semantics wrapper for each item

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Keep the timeline rendering logic in a separate RenewalHistoryTimeline stateless widget with a constructor parameter List events. This makes it testable in isolation. For the timeline connector line, a simple approach is a Column where each item has a Row with a left Column containing a small circle Container and a vertical Divider (use SizedBox for the connector between items). Avoid complex CustomPaint unless the design requires curved paths — a CSS-style approach with nested containers is sufficient and easier to maintain.

The cert type tag can reuse the badge/chip widget used in other parts of the app for consistency. Parse the renewal_history JSONB in the BLoC's state mapper (not in the widget) — the widget should receive a typed List. If the BLoC does not yet map the JSONB, add a RenewalEvent.fromJson factory and parse in the BLoC's _mapEventToState.

Testing Requirements

Widget tests: (1) render with a list of 3 RenewalEvent objects — assert all 3 dates and cert type tags are visible; (2) render with empty list — assert 'No renewal history yet' text is visible; (3) render with list of 10 items — assert ListView.builder is used (check widget type) and no overflow errors; (4) verify descending date order by checking the first visible date text matches the most recent event. Use flutter_test's find.text and find.byType. Accessibility test: set textScaler to 1.5 and assert no overflow.

Component
Certification Status Screen
ui medium
Epic Risks (3)
high impact medium prob technical

Flutter date pickers have historically poor screen reader support (VoiceOver/TalkBack), which is especially critical for this feature given that HLF peer mentors may have hearing impairment and the broader user base includes people with visual impairments. An inaccessible date picker on RecordRenewalScreen could block coordinator workflows entirely.

Mitigation & Contingency

Mitigation: Evaluate and adopt a third-party accessible date picker widget with verified WCAG 2.2 AA support, or build a custom picker using Flutter Semantics wrappers following the pattern established by the accessibility epic. Test all date pickers against VoiceOver on iOS and TalkBack on Android before UI sign-off.

Contingency: If no accessible date picker is available in time, provide a manual text field fallback for date entry (ISO format with clear labelling) alongside the picker, ensuring keyboard and screen reader users are never blocked.

medium impact high prob scope

Course enrolment initiation may redirect the user to the external HLF course portal (deep link or browser), which breaks the in-app flow and may confuse users expecting a seamless experience. The course data structure from Dynamics may also not be available in a machine-readable format in time for the initial release.

Mitigation & Contingency

Mitigation: Agree with HLF on whether enrolment is in-app or via deep link before UI design begins. If course data is not available from Dynamics at launch, design the enrolment prompt as a placeholder CTA that links to the HLF course portal homepage with a clear label indicating the user is leaving the app.

Contingency: Ship the course enrolment prompt as a configurable deep link per org. If Dynamics integration is delayed, the feature flag for the enrolment section can be disabled without affecting the rest of the certification status screen.

medium impact medium prob scope

Coordinators may attempt to record a renewal with an expiry date earlier than the previous certification's expiry (e.g., data entry error), or attempt to back-date a renewal. Without strict validation, the renewal history timeline could become chronologically inconsistent and mislead peer mentors about their coverage.

Mitigation & Contingency

Mitigation: CertificationManagementService validates that new expiry_date > current date and new issue_date >= previous renewal's issue_date before persisting. Surface validation errors as plain-language messages on RecordRenewalScreen using the error_message_registry pattern.

Contingency: If invalid renewal entries are discovered in production (from pre-validation data), provide a coordinator-only correction flow (edit renewal entry) behind an admin feature flag to fix historical records without requiring a full reset.