critical priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

AdminPortalBloc exposes LoadDashboardStats, RefreshStats, and ScopeChanged events with correct type signatures
Dispatching LoadDashboardStats transitions state to DashboardStatsLoading, then to DashboardStatsLoaded on success with populated KPI data
Dispatching ScopeChanged cancels any in-flight stats request and reloads stats for the new scope (org or chapter level)
Dispatching RefreshStats forces a fresh fetch bypassing any cached state and updates the last-refreshed timestamp in state
Error responses from AdminStatisticsService transition state to DashboardStatsError with a user-facing message; previous data is preserved in state for graceful degradation
BLoC state model includes: status enum (initial/loading/success/error), statsData nullable, scopeId, lastRefreshedAt, and errorMessage fields
Concurrent scope changes are handled safely — only the most recent scope's response is applied (use cancelToken or switchMap equivalent)
Unit tests cover all three events with mock AdminStatisticsService responses including success, empty data, and network error scenarios
BLoC is closed without pending subscriptions when the widget tree disposes it (no stream leaks)

Technical Requirements

frameworks
Flutter
flutter_bloc
BLoC
apis
AdminStatisticsService.getDashboardStats(scopeId)
AdminStatisticsService.getKPIs(scopeId, dateRange)
data models
DashboardStats
KPIMetric
OrgScope
AdminPortalState
performance requirements
Stats fetch must complete within 3 seconds on standard mobile network
ScopeChanged must debounce rapid successive changes by at least 300ms to avoid redundant API calls
State transitions must not block the UI thread — all service calls must be async
security requirements
Scope validation: the scopeId passed to AdminStatisticsService must match the admin's privilege scope stored in auth state — never trust client-supplied scope unchecked
Stats data must not be persisted to local storage without encryption
Service layer must include the authenticated Supabase session token in all requests

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use flutter_bloc ≥ 8.x with sealed state classes (or freezed-generated) for exhaustive pattern matching in the UI. Implement ScopeChanged with a transformer that uses `droppable()` or `restartable()` to cancel in-flight requests when scope changes rapidly. The AdminPortalBloc should hold a reference to an internal CancelableOperation or use StreamSubscription cancellation. Inject AdminStatisticsService via BlocProvider/constructor injection for testability.

Keep the BLoC thin: no business logic beyond event routing and state mapping — all data transformation belongs in AdminStatisticsService. Follow the pattern: event → emit(Loading) → await service → emit(Loaded/Error). Avoid storing raw Supabase responses in state; map to domain models before emitting.

Testing Requirements

Unit tests using flutter_test and bloc_test package. Test all event → state transitions: (1) LoadDashboardStats → loading → loaded with mock data, (2) LoadDashboardStats → loading → error with mock service throwing, (3) ScopeChanged mid-load → cancels previous → loads new scope, (4) RefreshStats → reloads even when already in loaded state. Mock AdminStatisticsService with Mockito. Assert emitted state sequences exactly.

Achieve 100% branch coverage on the BLoC event handlers. Integration test: mount AdminDashboardPage, verify BLoC emits loaded state and stat widgets render correct values.

Component
Admin Portal BLoC
data 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.