critical priority medium complexity frontend pending frontend specialist Tier 7

Acceptance Criteria

A non-coordinator user (peer mentor, org admin, global admin) who navigates to CoordinatorStatsScreen is immediately redirected to an appropriate screen (e.g., home or access-denied) with a user-readable explanation
The coordinator's stats provider constructs its Supabase query using only the chapter IDs returned by the chapter scope resolver for the authenticated user; hardcoded or wildcard chapter IDs are rejected at code review
The aggregated stats view contains data only from peer mentors in the coordinator's assigned chapters — verified by mocking a Supabase response that includes out-of-scope peer mentors and asserting they are absent from rendered output
If the coordinator is assigned zero chapters, the screen renders a specific empty state ('No chapters assigned') rather than a generic error or blank screen
Chapter scope resolver is called once per session (or on explicit refresh); it does not re-fetch on every widget rebuild
Role guard evaluation completes before any stats data fetch is initiated — no data leakage window during async guard evaluation
Changing role mid-session (e.g., via admin impersonation) triggers re-evaluation of the role guard and chapter scope
All Supabase RLS policies are respected; the client-side scoping is a defence-in-depth layer, not the sole access control mechanism

Technical Requirements

frameworks
Flutter
Riverpod
Supabase Flutter SDK
apis
Supabase RLS-enforced stats endpoint
chapter scope resolver (Supabase function or filtered query)
data models
UserRole
Chapter
CoordinatorChapterAssignment
PeerMentorStats
performance requirements
Chapter scope resolver result is cached in a Riverpod provider for the session duration to avoid redundant queries
Role guard adds no more than 50ms to screen load time on first visit
security requirements
Role check must read from the authenticated JWT claims or a server-side role provider — never from client-controlled state that a user could manipulate
Chapter IDs used in Supabase queries must be validated server-side via RLS; client-side filtering alone is insufficient
Log and alert on any attempt to access CoordinatorStatsScreen by a non-coordinator role — treat as a potential privilege escalation attempt
Ensure no chapter IDs from other coordinators can be injected via deep-link route parameters
ui components
CoordinatorRoleGuard widget (redirect wrapper)
EmptyChapterState widget
AccessDeniedScreen (reuse existing if available)

Execution Context

Execution Tier
Tier 7

Tier 7 - 84 tasks

Can start after Tier 6 completes

Implementation Notes

Implement the role guard as a Riverpod-aware widget that reads from an `authRoleProvider` (derived from the Supabase session JWT) and uses a `ref.watch` redirect pattern — prefer a declarative guard over imperative `Navigator.push` in `initState`. The chapter scope resolver should be a FutureProvider that fetches the coordinator's chapter assignments from Supabase once and caches them; downstream providers that fetch peer mentor stats should use `ref.watch(chapterScopeProvider)` to get the chapter ID list and include it as a `.in()` filter in the Supabase query. Never embed chapter IDs in the client bundle or route definitions. If the chapter scope provider returns an error, surface it explicitly on the screen rather than falling back to an unfiltered query.

Testing Requirements

Unit tests: test chapter scope resolver returns correct chapter IDs for a coordinator and empty list for a non-coordinator. Unit tests: test role guard logic for each role type (peer_mentor, coordinator, org_admin, global_admin). Widget tests: mock auth provider as non-coordinator role and assert CoordinatorStatsScreen triggers redirect navigation. Widget tests: mock coordinator with two chapters and assert only peer mentors from those chapters appear in PeerMentorStatsList.

Integration test: verify Supabase query parameters include only authorised chapter IDs. Security test: attempt to render CoordinatorStatsScreen with a mocked peer mentor session and verify redirect fires before any data fetch.

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.