critical priority low complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

TimeWindow enum defines exactly 5 values: week, month, quarter, year, custom — no additional variants
StatsTimeRange freezed class has non-nullable DateTime start and end fields plus a factory constructor fromTimeWindow(TimeWindow, {DateTime? referenceDate}) that computes correct date boundaries
TimeWindowSelection freezed class holds a TimeWindow and an optional StatsTimeRange (required only when custom is selected)
All freezed classes generate without errors via 'dart run build_runner build'
fromTimeWindow factory for 'week' returns Monday 00:00:00 to Sunday 23:59:59 of the current ISO week
fromTimeWindow factory for 'month' returns first to last day of the current calendar month
fromTimeWindow factory for 'quarter' returns correct Q1–Q4 boundaries based on reference date
fromTimeWindow factory for 'year' returns January 1 to December 31 of the reference year
Custom TimeWindow requires an explicit StatsTimeRange; a TimeWindowSelection with custom and null range throws an AssertionError
All classes implement equality via freezed (== and hashCode correct)
All classes are exported from a single barrel file (e.g., stats_types.dart) — no scattered imports required

Technical Requirements

frameworks
Flutter
freezed
build_runner
data models
StatsTimeRange
TimeWindowSelection
TimeWindow
performance requirements
Enum and freezed class instantiation must complete in under 1ms
No heap allocations beyond the freezed object itself during fromTimeWindow computation
security requirements
No PII stored in these value types — they carry only date boundaries
Custom date ranges must not allow end < start (assert in factory)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use freezed 2.x with the @freezed annotation. Place the enum and data classes in lib/features/stats/domain/stats_types.dart. The fromTimeWindow factory should use the Dart DateTime API only — do not pull in any date-utility packages. For ISO week calculation, implement a small private helper _isoWeekStart(DateTime d) that finds the preceding Monday.

Keep the barrel export in lib/features/stats/domain/index.dart. This file has no UI dependencies — it must be importable by both the Riverpod notifier and any repository layer without triggering Flutter framework imports.

Testing Requirements

Unit tests only. Test file: test/stats/stats_types_test.dart. Required test cases: (1) fromTimeWindow returns correct Monday–Sunday boundaries for 'week' across a DST transition date; (2) fromTimeWindow for 'month' on the last day of February in a leap year; (3) fromTimeWindow for each quarter boundary month; (4) fromTimeWindow for 'year' returns Jan 1 – Dec 31; (5) custom with null range throws AssertionError; (6) equality check — two StatsTimeRange with identical fields are equal; (7) copyWith produces a new instance with updated fields. Target: 100% branch coverage on fromTimeWindow.

Component
Time Window Selector
ui low
Epic Risks (4)
high impact high prob technical

fl_chart renders chart elements on a Canvas, making individual bars and data points invisible to the Flutter Semantics tree by default. Without explicit Semantics wrappers, VoiceOver and TalkBack users receive no chart information, violating the WCAG 2.2 AA requirement mandated by all three partner organizations.

Mitigation & Contingency

Mitigation: Wrap the fl_chart widget in a Semantics node with a dynamically generated textual description of the chart data (e.g., 'Bar chart: January 12, February 8, March 15 sessions'). Implement a collapsible data table alternative beneath the chart that screen readers can navigate row by row. Validate with VoiceOver on iOS and TalkBack on Android before the epic is marked complete.

Contingency: If fl_chart's Canvas rendering cannot be made accessible within the epic timeline, ship the chart hidden from the Semantics tree with ExcludeSemantics and promote the data table alternative to first-class UI so screen reader users have full access to the information. Log a tech-debt item to revisit native chart accessibility in a future sprint.

medium impact medium prob scope

Coordinators managing up to 5 chapters (NHF requirement) require the PeerMentorStatsList to display chapter affiliation labels for each row. With large chapter lists and many peer mentors, the list could become overwhelming and cause layout overflow or scrolling performance issues on lower-end Android devices.

Mitigation & Contingency

Mitigation: Implement chapter filtering as a segmented control above the list so coordinators can scope the list to one chapter at a time. Use ListView.builder (lazy rendering) rather than a Column of all rows. Profile scroll performance on a low-end Android device (Pixel 4a equivalent) with 50 peer mentors in scope during development.

Contingency: If multi-chapter display causes unacceptable performance, ship with single-chapter scope as the default view and a chapter switcher dropdown, deferring the combined cross-chapter list to a follow-up sprint.

low impact low prob technical

Summary cards and the chart widget rebuilding simultaneously on provider state change could cause a visible jank frame on slower devices, degrading perceived quality especially since this screen is intended to feel motivating and polished for gamification purposes.

Mitigation & Contingency

Mitigation: Use AnimatedSwitcher with a short fade transition (150ms) on the stats cards and chart so that data replacement feels intentional rather than jarring. Profile with Flutter DevTools on a mid-range device and ensure no frame exceeds 16ms during a time-window switch.

Contingency: If animation introduces complexity that delays delivery, ship without animation and use a loading skeleton (shimmer effect) during re-fetch instead, which is simpler and equally effective at masking the data swap.

high impact low prob security

If the role-based screen dispatch is misconfigured, a peer mentor could navigate to the coordinator stats screen and see aggregated chapter data for all peer mentors, which is a data privacy violation and a compliance risk for all three organizations.

Mitigation & Contingency

Mitigation: Implement role guard at the router level using an existing role-route-guard component so the coordinator screen route is unreachable for peer mentor roles. Add a widget test that mounts the coordinator screen with a peer mentor session token and asserts that the guard redirects to the no-access screen.

Contingency: If a bypass is found in QA, add a secondary in-screen role assertion in the coordinator screen's initState that throws an AuthorizationException and navigates to the error screen, ensuring defence in depth regardless of router configuration.