high priority medium complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

AdminPortalBloc accepts a RoleAssignmentRequested event carrying userId, targetRole, and adminScopeId
On RoleAssignmentRequested, BLoC validates that targetRole is within adminScopeId before dispatching to UserManagementService; if invalid, emits RoleAssignmentError with a descriptive message and does NOT call the service
AdminPortalBloc accepts a RoleAssignmentConfirmed event and only then calls UserManagementService.assignRole(); the two-event flow mirrors the UI's two-step confirmation
On successful role assignment, BLoC emits a RoleAssignmentSuccess state and performs an in-place update of the affected user entry in the userList portion of the state (no full list reload)
On service failure, BLoC emits RoleAssignmentError with the error message; userList state remains unchanged
The user list widget reacts to RoleAssignmentSuccess by updating the specific list tile's role label without triggering a scroll-position reset
BLoC privilege scope validation logic is unit-tested independently of the UI
After role assignment completes (success or error), BLoC returns to a neutral role-assignment-idle sub-state, ready for the next assignment

Technical Requirements

frameworks
Flutter
flutter_bloc
BLoC
apis
UserManagementService.assignRole(userId, newRole)
AdminPrivilegeScopeService.isRoleWithinScope(role, scopeId)
data models
UserProfile
UserRole
AdminPrivilegeScope
AdminPortalState
performance requirements
In-place user list update must not rebuild the entire list widget — use targeted BlocBuilder with buildWhen condition
Privilege scope validation must be synchronous (in-memory check against loaded scope data) — no extra async call
security requirements
Client-side privilege scope check is a UX guard only; server-side Supabase RLS must enforce the same rule authoritatively
RoleAssignmentRequested event must carry the adminScopeId from authenticated session state, never from UI input
ui components
UserListTile (must support in-place role label update via BlocBuilder)
RoleAssignmentPanel (dispatches events to BLoC from task-006)

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Model the role assignment flow as a sub-state within AdminPortalState (e.g., a roleAssignmentStatus field with idle/processing/success/error and a nullable roleAssignmentError). Use BlocBuilder with buildWhen: (prev, curr) => prev.roleAssignmentStatus != curr.roleAssignmentStatus || prev.userList != curr.userList on the UserListTile to avoid unnecessary rebuilds of unrelated UI. The in-place list update should produce a new list with only the affected user's role updated — use immutable list copy with map(). Keep the two-event pattern (Requested → Confirmed) aligned with the two-step UI in task-006; do not short-circuit to a single event as this bypasses the confirmation UX.

Scope validation logic should be a pure function (List allowedRoles, UserRole candidate) → bool, easily unit-testable.

Testing Requirements

Unit tests with bloc_test: (1) RoleAssignmentRequested with in-scope role → emits processing → emits success with updated userList entry, (2) RoleAssignmentRequested with out-of-scope role → emits error, service NOT called, (3) RoleAssignmentConfirmed after Requested → service called once, (4) service throws → emits error, userList unchanged. Widget test: verify UserListTile updates role label on RoleAssignmentSuccess without full list rebuild (assert rebuild count via test instrumentation). Integration test: open RoleAssignmentPanel, select role, confirm, verify parent list reflects change.

Component
Role Assignment Panel
ui medium
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.