critical priority high complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

Screen renders OrgHierarchyNavigator at the top, followed by the KPI stat grid
KPI grid shows at minimum: total activities, total peer mentors, active certifications, pending export jobs — all sourced from AdminPortalBloc
Grid layout uses 2 columns on mobile (<600px), 3 columns on tablet (600–1024px), and 4 columns on web (>1024px)
When AdminPortalBloc state is loading, all KPI tiles show shimmer loading state
When AdminPortalBloc state is error, all tiles show error state with a retry button visible at the top of the screen
Selecting an org scope in OrgHierarchyNavigator triggers a SelectOrgScope event to AdminPortalBloc and updates all KPI tiles with scoped data
Tapping a KPI tile navigates to the correct drill-down route (e.g., tapping 'Total Activities' navigates to the activity log screen with the current scope applied)
Drill-down navigation preserves the current org scope in the destination screen's BLoC state
Screen passes WCAG 2.2 AA contrast ratio checks for all text elements against their background
Screen renders without overflow or layout exceptions on: iPhone SE (375px), iPhone 14 Pro (393px), iPad (820px), and 1440px web
Screen is wrapped in a BlocConsumer (or BlocBuilder) and only rebuilds the stat grid when relevant state fields change — not on every BLoC emission
Page header displays the currently selected org scope name (or 'All Organisations' when unscoped)

Technical Requirements

frameworks
flutter
flutter_bloc
riverpod
apis
AdminPortalBloc (for stats data)
go_router (or project's router) for drill-down navigation
data models
AdminPortalState
DashboardStats
OrgNode
KpiStatData
performance requirements
Initial screen render completes first frame within 100ms (before data load)
Grid rebuilds use BlocSelector to scope subscription to only DashboardStats fields, preventing full tree rebuilds on unrelated state changes
LayoutBuilder is called once per breakpoint change, not on every frame
security requirements
Screen must only be accessible to users with the 'admin' or 'coordinator' role — enforce route guard at navigation layer
Stat values displayed must respect the admin's org access scope — server-side RLS enforces this; client must not show data outside permitted scope
ui components
AdminDashboardScreen
AdminKpiStatWidget (grid tiles)
OrgHierarchyNavigator (scope selector at top)
ScopeBadge (header indicator of current scope)
RetryButton (error state CTA)
ResponsiveGrid (LayoutBuilder-based wrapper)

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Implement AdminDashboardScreen as a StatelessWidget consuming AdminPortalBloc via BlocSelector for efficient rebuilds. Map DashboardStats fields to a List in a pure helper function (mapStatsToKpiTiles) outside the build method to keep build() clean. Use LayoutBuilder at the grid level only — not the full screen — to determine column count. For drill-down navigation, define typed route parameters that carry the current org scope ID so the destination screen can restore scope context.

The OrgHierarchyNavigator should be placed in a collapsible top panel (e.g., ExpansionTile or AnimatedContainer) on mobile to avoid consuming too much vertical space. The page header 'All Organisations' / '{Org Name}' text should be a dedicated ScopeBadge widget that reads scope from BLoC state — keep it decoupled from the grid. For the NHF use case, the dashboard is the first screen admins see after login — ensure time-to-first-meaningful-paint is fast by showing skeleton tiles immediately while data loads asynchronously. Avoid placing BLoC event dispatch calls inside build() — use initState (in StatefulWidget) or a post-frame callback for initial data load.

Testing Requirements

Widget tests using flutter_test with ProviderScope and BlocProvider overrides. Cover: (1) initial render shows shimmer tiles in loading state, (2) loaded state renders correct number of KPI tiles with correct values, (3) error state shows retry button, (4) tapping retry dispatches LoadDashboardStats event, (5) scope selection updates page header text, (6) tapping a KPI tile triggers correct navigation route, (7) grid shows 2 columns at 375px width, (8) grid shows 4 columns at 1440px width, (9) WCAG semantics check (all tiles have readable semantic labels). Integration test: mount full screen, simulate scope change, assert all tiles update with new values. Golden tests for: loaded state (desktop), loaded state (mobile), error state.

Component
Admin Dashboard Screen
ui high
Epic Risks (3)
high impact medium prob technical

If org node selection in AdminStateBLoC does not correctly propagate to all dependent data streams (statistics, activity log, user list, certification panel), some panels may show data from the previously selected org scope, creating a confusing and potentially dangerous mixed-scope view.

Mitigation & Contingency

Mitigation: Model org node selection as a single source of truth in AdminStateBLoC. All downstream providers derive their query parameters from this single stream via Riverpod's watch pattern. Write integration tests that verify every data stream emits a reload event when the selected node changes.

Contingency: If scope propagation bugs are detected in QA, add an explicit full-state reset on org node change (clear all cached data and refetch from scratch) as a safe but less efficient fallback until the targeted propagation is fixed.

medium impact medium prob technical

The Admin Dashboard Screen must adapt its layout for Flutter Web (wider viewports, mouse interaction, larger grid) and mobile embedding. Flutter Web responsive layout support has historically required non-trivial workarounds, and the adaptive grid may introduce significant additional development time.

Mitigation & Contingency

Mitigation: Define breakpoints and grid behaviour in the design system before implementation. Use LayoutBuilder with explicit breakpoint constants rather than MediaQuery scattered across widgets. Prototype the web layout with a skeleton screen before implementing live data binding.

Contingency: If web layout proves intractable within sprint, deliver a mobile-first layout for all platforms initially and track a dedicated web-optimisation task for the next sprint.

high impact low prob security

A bug in the Role Assignment Panel's permission scope validation could allow an org_admin to assign roles beyond their authority (e.g., assigning super_admin to a user), representing a serious privilege escalation vulnerability.

Mitigation & Contingency

Mitigation: Enforce role assignment scope on both the client (disable unavailable roles in the panel UI) and the server (UserManagementService validates the target role is within the admin's permitted scope before persisting). Write security-focused tests that attempt out-of-scope role assignments and assert rejection.

Contingency: If an escalation vulnerability is discovered, immediately disable the role assignment panel via feature flag, revoke any incorrectly assigned roles, and deploy a server-side fix before re-enabling.