high priority medium complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

Tapping the role badge or a dedicated 'Change role' action on a user row opens the Role Assignment Panel
Panel opens as a DraggableScrollableSheet (bottom sheet) on mobile; on larger screens may open as a side panel
Panel displays the user's name, current role, and a list of assignable roles scoped to what the acting admin is permitted to assign
Selecting a new role and confirming dispatches RoleAssigned event to UserManagementBloc
The user row in the list shows an optimistic update immediately after confirmation — role badge changes before server response
If the server call fails, the role badge reverts to the previous value and an error snackbar/toast appears with the failure reason
If the server call succeeds, the role badge remains updated and a brief success indicator is shown
Panel is dismissed after successful submission — admin returns to the list view without full refresh
Panel cannot be submitted while a previous submission for the same user is in progress (button disabled, loading indicator shown)
Assigning the same role the user already has is either disabled or triggers a no-op with informative message
Panel is accessible: focus moves into the panel when opened, returns to the triggering row when closed
All role names use the organisation's label system (dynamic terminology per org) — not hardcoded strings

Technical Requirements

frameworks
Flutter
flutter_bloc
Design token system
apis
UserManagementBloc (RoleAssigned event, operationStatus state)
UserManagementService (role assignment endpoint)
Organisation labels system
data models
UserSummary
UserRole enum
UserManagementOperationStatus
RoleAssignmentRequest
performance requirements
Bottom sheet open animation must complete in under 300ms
Optimistic update must apply within one frame of confirmation tap
security requirements
Assignable roles shown in the panel must be server-validated — do not allow client to submit a role the admin is not permitted to assign
Role assignment must be gated by admin's own role and org scope in the service layer and RLS policy
ui components
RoleAssignmentBottomSheet
RoleOptionTile
ConfirmRoleButton
RoleBadge (updated state)
OperationFeedbackSnackbar

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

The Role Assignment Panel should be a separate widget file, not inlined in the user row. Pass the userId and currentRole as constructor parameters; the panel reads permitted roles from the BLoC or a separate roles provider. For the bottom sheet, use `showModalBottomSheet` with `isScrollControlled: true` and a DraggableScrollableSheet child to support variable role list lengths. Optimistic update logic lives in the BLoC (task-004); the panel simply dispatches the event and observes operationStatus.

Use BlocListener in the panel to react to operationStatus changes: close the sheet on success, show inline error on failure. The organisation labels system must be consulted for role display names — use the existing `OrganizationLabels` mechanism already in the app architecture. Avoid hardcoding 'admin', 'coordinator', 'peer mentor' strings in the widget layer.

Testing Requirements

Widget tests: open panel and assert current role is pre-selected; select new role and tap confirm — assert RoleAssigned event dispatched with correct userId and new role; assert panel dismissed on success state from BLoC; assert panel stays open and shows error on failure state; assert confirm button disabled during in-progress operation. Integration test: full flow from row tap → panel open → role change → optimistic update in list → server success confirm. Accessibility test: assert focus management (panel receives focus on open, returns on close). Test with mocked organisation labels to confirm dynamic terminology is applied.

Component
User Account Management Screen
ui high
Epic Risks (3)
medium impact medium prob technical

Displaying NHF users with membership in up to 5 local chapters in a flat list view without duplicating entries requires a non-trivial aggregation query. Incorrect query design could result in duplicated user rows or missing chapter affiliations, confusing admins and causing incorrect role assignments.

Mitigation & Contingency

Mitigation: Design the user list query to GROUP BY user_id and aggregate chapter affiliations as an array field. Use AdminRepository's typed models to surface this aggregated structure to the UI. Validate with a test dataset containing users in 5 chapters.

Contingency: If aggregation query complexity proves too high for real-time filtering, implement a separate multi-chapter affiliation fetch triggered only when a specific user row is expanded, reducing query complexity for the base list.

medium impact medium prob technical

Composable multi-dimensional filters (role + chapter + status + certification state) applied server-side against an org with 2,000+ users may produce slow queries, particularly when filtering by certification state requires joining an additional table.

Mitigation & Contingency

Mitigation: Ensure the relevant filter columns (role, status, chapter_id, certification_expiry) are indexed in Supabase. Use cursor-based pagination rather than OFFSET to maintain consistent performance at high page numbers. Profile filter query combinations against a large dataset during development.

Contingency: If multi-filter performance degrades in production, introduce a denormalised search index table updated on user status changes, allowing the list query to filter from a single table.

medium impact medium prob integration

Deactivating a user account that has ongoing activity assignments, open expense claims, or active chapter affiliations may leave orphaned records or break downstream workflows if the deactivation does not trigger correct cascade handling.

Mitigation & Contingency

Mitigation: Define and document the expected state of each dependent record type on user deactivation before implementing the toggle. Implement deactivation as a UserManagementService operation that checks for and warns about open dependencies before persisting. Write integration tests covering each dependency type.

Contingency: If orphaned record issues are discovered post-launch, provide an admin-accessible reconciliation view that surfaces users with inconsistent dependency states and allows manual resolution without requiring a code deploy.