critical priority high complexity frontend pending fullstack developer Tier 3

Acceptance Criteria

ActivityChartWidget on CoordinatorStatsScreen shows chapter-aggregated activity data (not a single peer mentor's data)
PeerMentorStatsList renders all peer mentors whose primary or affiliated chapter matches the active chapter from ChapterScopeResolver
TimeWindowSelector change triggers exactly one async provider refresh — summary cards, chart, and list all enter loading state simultaneously and all resolve simultaneously from one data fetch
Chapter scope change (via ChapterSwitcherWidget from task-011) also triggers a single coordinated refresh of all three widgets
No widget shows stale data from the previous time window while the new fetch is in progress — all show skeleton/loading
PeerMentorStatsList within CoordinatorStatsScreen passes chapterId as a filter parameter so only in-scope peer mentors appear
Tapping a PeerMentorStatsListRow navigates to the peer mentor drill-down with the correct userId
The screen correctly handles a chapter with zero peer mentors — PeerMentorStatsList shows empty state, chart shows zero-value bars (not an error)
Scroll performance: the full screen (header + cards + chart + list) is embedded in a single CustomScrollView with SliverList/SliverToBoxAdapter to avoid nested scroll conflicts
All visible text and interactive elements pass WCAG 2.2 AA contrast ratio (4.5:1 for normal text, 3:1 for large text and UI components)
Screen renders correctly on a 375px-wide viewport (iPhone SE) without horizontal overflow

Technical Requirements

frameworks
Flutter
Riverpod
apis
Supabase — coordinator stats query: aggregate sessions/hours by chapterId + dateRange
Supabase — peer mentor list query filtered by chapterId
data models
CoordinatorStatsData
ChapterScope (chapterId + TimeWindow)
ActivityChartData (chapter-aggregated)
PeerMentorStatsSummary (list items)
performance requirements
Single Supabase query or batched parallel queries — not N+1 per peer mentor
Chart and list data fetched in one provider call, split client-side, not two separate network round-trips
CustomScrollView with slivers eliminates nested scroll jank
security requirements
Supabase RLS enforces that coordinators can only query chapters they are affiliated with — client filter is defence-in-depth
chapterId validated against the coordinator's affiliated chapters list before being used in any query
No peer mentor PII exposed beyond name and aggregate session counts in this view
ui components
CustomScrollView with SliverAppBar / SliverToBoxAdapter / SliverList
TimeWindowSelector (task-002)
StatsSummaryCards (task-003) — chapter-aggregated
ActivityChartWidget (task-006) — chapter-aggregated variant
PeerMentorStatsList (task-008) — with chapterId filter prop
ChapterSwitcherWidget (task-011)
Skeleton overlays for all data sections

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

The key architectural challenge is ensuring a single coordinated refresh. Model the screen's data source as coordinatorStatsProvider(ChapterScope(chapterId, timeWindow)) where ChapterScope is an immutable value object (implement == and hashCode or use freezed). The provider returns a CoordinatorStatsData object containing BOTH the chart data AND the peer mentor summary list. This guarantees one fetch, one loading state, one data update for all child widgets.

Do NOT use separate providers for chart and list — this creates independent loading states that update at different times, causing visual jank and potential data inconsistency. For the scroll layout: wrap the entire screen body in a CustomScrollView. Use SliverToBoxAdapter for the TimeWindowSelector, StatsSummaryCards, and ActivityChartWidget. Use SliverList for the PeerMentorStatsList items (pass itemCount and itemBuilder directly to SliverList.builder).

This eliminates the ListView-inside-Column nested scroll conflict. For the chapter scope change, update the activeChapterProvider StateProvider — because coordinatorStatsProvider watches both activeChapterProvider and selectedTimeWindowProvider, any change to either automatically invalidates and rebuilds the provider.

Testing Requirements

Widget and integration tests. Widget tests: (1) all three data widgets (cards, chart, list) show loading simultaneously on time window change — verify all three finders show skeleton during AsyncLoading; (2) all three resolve simultaneously on AsyncData — verify no widget still shows skeleton; (3) empty chapter renders empty state in list and zero-bar chart (not error widget); (4) chapter switch re-fetches with new chapterId — verify provider called with updated ChapterScope; (5) nested scroll test — fling gesture scrolls the full CustomScrollView without error; (6) 375px viewport test — no RenderFlex overflow. Integration test (optional): mock Supabase responses and verify end-to-end data flow from time window tap to chart render.

Component
Coordinator Stats Screen
ui high
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.