critical priority high complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

BLoC is implemented using the bloc package with explicit Event and State classes — no Cubit shortcut given complexity
Initial state is UserManagementInitial; after first load it transitions to UserManagementLoaded or UserManagementError
UserManagementLoaded state contains: List<UserSummary> users, FilterParams activeFilters, int currentPage, bool hasMore, bool isLoadingNextPage, UserManagementOperationStatus operationStatus
FilterChanged event replaces activeFilters and resets pagination to page 0, then triggers a fresh server-side fetch
LoadNextPage event appends next page to existing list; if isLoadingNextPage is already true the event is ignored (no duplicate fetch)
RoleAssigned event triggers optimistic update of the affected user row, calls UserManagementService, then confirms or reverts on service response
ActivationToggled event triggers optimistic status flip, calls UserManagementService, then confirms or reverts with error state surfaced via operationStatus
operationStatus carries enough context to display per-row feedback (which user, which operation, success/failure message)
InvitationSent event triggers invitation flow; on success a success banner is shown; on failure error is surfaced without disrupting the list
Error state from data layer surfaces as UserManagementError with a typed error code — not a raw exception string
PullToRefresh event resets pagination and re-fetches page 0 with current filters
BLoC is closed and disposed correctly when screen is popped — no stream leaks
All BLoC events and state transitions are unit-tested with bloc_test

Technical Requirements

frameworks
Flutter
BLoC (bloc, flutter_bloc packages)
supabase_flutter
apis
UserManagementService (data layer from task-001/002)
Composable filter query builder (task-003)
data models
UserSummary
FilterParams
UserManagementOperationStatus
UserManagementEvent (sealed)
UserManagementState (sealed)
performance requirements
BLoC must debounce FilterChanged events by 300ms to avoid firing a query per keystroke if text search is later added
Optimistic updates must apply synchronously to state before the async service call resolves
Pagination append must not rebuild unchanged rows — use list equality or keyed widgets
security requirements
BLoC must not store raw user PII beyond what is needed for display — delegate storage to the data layer
Role assignment and activation toggle operations must validate that the acting admin has permission before dispatching to the service (client-side guard; server enforces via RLS)
ui components
UserManagementScreen (consumer)
UserListView
FilterBarWidget
RoleAssignmentPanel
ActivationToggleRow

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Use sealed classes for both Event and State in Dart 3 — this gives exhaustive switch coverage and compile-time safety. Keep the BLoC thin: it orchestrates events and delegates all async work to the service layer. Do not put Supabase queries inside the BLoC. Optimistic updates work well here because role and status changes are low-risk reversals — apply the change to the in-memory UserSummary immediately, then await the service call and revert if it fails.

For operationStatus, model it as a discriminated union: Idle | InProgress(userId) | Success(userId, message) | Failure(userId, message) — this allows the UI to show per-row spinners and feedback without a separate state tree. The invitation flow state can be a simple sub-state field (InvitationIdle | InvitationInProgress | InvitationSuccess | InvitationFailure) on UserManagementLoaded.

Testing Requirements

Use bloc_test for all event-to-state transitions. Required test cases: initial load success, initial load failure, filter change resets pagination, load next page appends, load next page while already loading is a no-op, role assignment optimistic update then success confirm, role assignment optimistic update then server error reverts, activation toggle optimistic update then revert on error, pull-to-refresh resets list, invitation sent success, invitation sent failure. Mock UserManagementService with mockito or manual fakes. Do not use real Supabase in BLoC tests.

Minimum 85% branch coverage.

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.