critical priority high complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

AdminPortalBloc is registered as a Riverpod provider and accessible from any widget in the admin portal subtree
AdminPortalState is a sealed/union class (using freezed or equivalent) covering: initial, loading, loaded, error substates
State includes fields for: selectedOrgScope (nullable OrgNode), dashboardStats (nullable DashboardStats), exportJobStatus (enum), activityListFilter (ActivityFilterParams), userListFilter (UserFilterParams), currentPage (int), and isLoadingMore (bool)
All AdminPortalEvent subtypes are defined: LoadDashboardStats, SelectOrgScope, ClearOrgScope, StartExportJob, UpdateExportJobStatus, UpdateActivityFilter, UpdateUserFilter, LoadNextPage, ResetFilters
Initial state has null org scope, empty filters, page 0, and status=initial
Event handlers are wired and emit correct state transitions (e.g., LoadDashboardStats → loading → loaded/error)
BLoC is fully unit testable via bloc_test without any UI or network dependencies
Error events produce an error substate with a localised error message string, not a thrown exception
All state classes are equatable (value equality) for reliable BlocBuilder rebuilds
State transitions are logged in debug mode via a BlocObserver

Technical Requirements

frameworks
flutter_bloc
riverpod
freezed
equatable
apis
AdminStatsRepository (abstracted interface)
OrgHierarchyRepository (abstracted interface)
data models
AdminPortalState
AdminPortalEvent
OrgNode
DashboardStats
ActivityFilterParams
UserFilterParams
ExportJobStatus
performance requirements
State emissions must not cause unnecessary rebuilds — use BlocSelector or select() to scope subscriptions
BLoC instantiation completes in under 50ms
State objects are immutable — copyWith must be used for all transitions
security requirements
Selected org scope must be validated against the current admin user's permitted hierarchy nodes before emission
Export job status must not leak sensitive job IDs to state unless the current user owns the job

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use flutter_bloc's Bloc as the base class. Expose the BLoC instance via a Riverpod StateNotifierProvider or a plain Provider — avoid mixing BlocProvider and Riverpod for the same bloc to prevent double-subscription bugs. Define all state subclasses using freezed for immutability and copyWith generation. Keep repository interfaces abstract (AdminStatsRepository, OrgHierarchyRepository) so the BLoC has no direct Supabase dependency — this is critical for testability.

The export job status should be a separate enum (ExportJobStatus: idle, queued, processing, completed, failed) rather than a generic string. Register a custom BlocObserver in main.dart (debug builds only) that logs state transitions to console. This BLoC is the single source of truth for all admin portal state — downstream providers should derive from it rather than maintaining parallel state.

Testing Requirements

Unit tests using bloc_test package. Cover: (1) initial state is correct, (2) LoadDashboardStats emits loading then loaded, (3) LoadDashboardStats emits loading then error on repository failure, (4) SelectOrgScope updates selectedOrgScope, (5) ClearOrgScope resets to null, (6) StartExportJob transitions exportJobStatus, (7) UpdateActivityFilter updates filter without resetting page, (8) LoadNextPage increments currentPage and sets isLoadingMore, (9) ResetFilters clears all filters and resets page to 0. Use MockAdminStatsRepository and MockOrgHierarchyRepository injected via constructor. Assert exact state sequences using bloc_test's expect().

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.