medium priority low complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

Tapping a user row navigates to the user profile detail screen with the correct user ID passed as a route argument
The user profile screen loads and displays the correct user's data
Pressing the system back button or the explicit back button returns to the user list screen
On return, the list is scrolled to the same position as when the user left (scroll offset preserved)
All active filters (search query, role filter, status filter) are preserved on return — no filter state reset
Deep-linking to `/admin/users/:userId` opens the correct profile screen directly without requiring list pre-load
Deep-link navigation shows a loading state if the user data has not yet been fetched, then renders the profile
The profile screen route is registered in the app router and accessible without the list screen being on the stack
Row tap has a visible ripple/ink splash to provide immediate feedback
Navigation works on both iOS and Android without visual glitches

Technical Requirements

frameworks
Flutter
GoRouter (or existing app router)
BLoC
apis
UserManagementService.getUserById(userId)
data models
UserProfile
UserRole
performance requirements
Navigation transition animation completes in ≤300ms
If user data is already in BLoC cache, profile screen renders immediately without a loading spinner
security requirements
Route guard must verify the requesting admin has permission to view the target user's profile (same org or global admin)
User ID in deep-link must be validated as a UUID before any DB call
ui components
Existing user list row widget (add GestureDetector/InkWell if not already tappable)
UserProfileDetailScreen (existing or to be created in a sibling task)
Back button in AppBar using leading: BackButton()

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Preserve scroll position by storing the ScrollController's offset in the UserManagementBloc state (or a dedicated ListScrollState) before pushing the route, and restoring it in a post-frame callback on return. With GoRouter, use the `extra` parameter to pass the pre-fetched user summary object alongside the userId, so the profile screen can render immediately from the already-loaded data while optionally refreshing in the background. For deep links, the profile screen must be able to fetch user data independently via UserManagementService.getUserById() when navigated to without `extra` data. Register the route as `/admin/users/:userId` in the router configuration.

Use `onPopInvoked` or `PopScope` (Flutter 3.16+) for back navigation handling if custom logic is needed beyond simple pop. Avoid storing scroll position in a global singleton — keep it in the BLoC to allow proper disposal.

Testing Requirements

Widget tests: (1) tapping a user row triggers navigation with the correct userId argument; (2) back navigation pops the profile screen. Integration tests: (1) navigate from list → profile → back; verify scroll position and filter state are restored using a ScrollController and BLoC state inspection. Deep-link test: construct a GoRouter link `/admin/users/test-uuid`, navigate programmatically, verify profile screen renders with correct userId. Test on both iOS simulator and Android emulator to catch platform-specific back gesture differences.

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.