high priority medium complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

Email input field validates format on blur and shows inline error for invalid formats (e.g. missing @, invalid TLD)
Email input rejects addresses already registered in the organisation and shows a descriptive inline error
Role selector dropdown is pre-populated exclusively with roles within the admin's current org scope (not global roles beyond their permission level)
A default role is pre-selected based on the most common invitation role for the org (configurable via BLoC state)
Submit button is disabled until both a valid email and a role are selected
On successful invitation, an inline success banner appears with the invited email address and a dismiss action; the form resets
On API failure, an inline error message renders with the server-provided reason (or a fallback generic message) without leaving the form
The entire form is fully keyboard-navigable: Tab moves email → role → submit, Enter submits, Escape dismisses error states
All form elements have Semantics labels compatible with TalkBack and VoiceOver screen readers
Touch targets for the role selector and submit button are at minimum 48×48 dp
Loading state during submission disables the form and shows a progress indicator on the submit button

Technical Requirements

frameworks
Flutter
BLoC
apis
UserManagementService.inviteUser(email, roleId, orgId)
UserManagementService.getRolesForOrg(orgId)
data models
UserInvitation
UserRole
Organisation
performance requirements
Role list must load within 500ms of form open (fetched as part of screen init, not on dropdown tap)
Email validation debounce of 400ms to avoid excessive re-renders
security requirements
Admin must only see roles at or below their own permission level — enforced both in UI (filtered role list) and validated server-side
Email field must be sanitised before submission to prevent injection
Invitation token generation and email dispatch must occur server-side only (task-010)
ui components
AppTextField (existing reusable widget) with email keyboard type and input formatters
DropdownButtonFormField or custom AppDropdown seeded from BLoC state
AppButton (existing) with loading state variant
InlineFeedbackBanner (success/error) — create if not already in shared widgets
Semantics wrappers on all interactive elements

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Use a dedicated InviteUserCubit (or extend existing UserManagementBloc with an InviteUserEvent) holding InviteFormState with fields: email, selectedRoleId, availableRoles, submissionStatus (idle/loading/success/failure), errorMessage. Fetch available roles in the cubit's constructor or on a LoadRolesEvent triggered from initState — not lazily. The role list should already be cached from the user list screen's BLoC if the same bloc instance is shared; check for reuse before adding a new Supabase call. For the inline feedback banner, prefer an AnimatedSwitcher over a SnackBar so it stays visible while the user reviews the email they typed.

Keep the form in a bottom sheet or a dedicated section of the screen — do not navigate away for the invite flow. Ensure focus returns to the email field after a failed submission so keyboard users can immediately correct the input.

Testing Requirements

Widget tests: (1) form renders with email field and pre-populated role dropdown; (2) submit is disabled with empty/invalid email; (3) selecting a role and entering a valid email enables submit; (4) loading state disables form during async call; (5) success state renders banner and resets form; (6) error state renders inline message without form reset. Accessibility test: verify Semantics tree includes labels for email field, role selector, and submit button. Integration test: end-to-end invitation flow with mocked UserManagementService returning 200 and 4xx responses. No golden tests required for this task.

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.