high priority high complexity frontend pending frontend specialist Tier 6

Acceptance Criteria

Initial page load (first 20 users) completes within 2 seconds on a mid-range Android device on a 4G connection
Scrolling through a list of 1000+ users maintains ≥55 fps (no janky frames) on a mid-range device
Filter operations (role, status, search) apply within 300ms of user input settling (after debounce)
Debounce on search input is set to 400ms to prevent excessive Supabase calls
Pagination loads the next page within 1 second when the user scrolls within 3 rows of the bottom
Repeated identical filter queries (within the same session) are served from cache without a new network call
Cache is invalidated when a user's status or role is changed (activation toggle, role assignment)
Cache is invalidated when a new invitation is sent (potential new pending user appears)
Memory usage does not grow unboundedly: cached query results are capped (e.g., LRU with max 10 pages or 200 users)
Flutter DevTools performance profiling shows no rebuilds of off-screen list items during scroll
The list uses a virtualised builder (ListView.builder or equivalent) — no Column/ListView with all items loaded at once

Technical Requirements

frameworks
Flutter
BLoC
Riverpod (if used for caching layer)
apis
Supabase paginated query: .from('users').select().range(offset, offset+pageSize).order('name')
Supabase count query for total user count (for pagination display)
data models
UserProfile
UserRole
UserStatus
PaginatedUserResult
performance requirements
Initial load ≤2s (p95) on mid-range device / 4G
Scroll at ≥55 fps with 1000 items in list
Filter response ≤300ms after debounce settles
Memory: max 200 cached user records in memory at once (LRU eviction)
security requirements
Paginated queries must be executed through Supabase RLS — never bypass row-level security for performance
Cache must be scoped per admin session (not shared across accounts)
ui components
ListView.builder with itemExtent or prototypeItem set for O(1) scroll position calculation
Pagination footer widget with loading indicator
Empty state widget for zero results after filtering

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Implement cursor-based pagination (using the last fetched user's ID or offset) rather than page-number-based to avoid duplicate records when users are inserted between pages. Store paginated results in the BLoC state as a Map> where FilterKey is a value object combining search query + role filter + status filter. Use Equatable on FilterKey so BLoC can detect state changes efficiently. For debouncing, use an RxDart debounceTime stream transformer or a Timer in the BLoC's event handler — do not debounce in the UI layer.

Set ListView.builder's itemExtent to the known row height (e.g., 72dp) to enable O(1) scroll position calculations and avoid layout passes. For cache invalidation, emit a CacheInvalidationEvent from the BLoC whenever a mutation (toggle, role change, invite) completes successfully. Run Flutter DevTools' 'Rebuild Stats' overlay during development to identify any inadvertent full-list rebuilds triggered by parent state changes.

Testing Requirements

Performance tests: use Flutter's `WidgetTester.pump` benchmarks and integration_test package to measure frame rendering times with a list of 500+ mocked users. Profile with Flutter DevTools (CPU + widget rebuild timeline) and document the before/after frame rate. Unit tests: (1) debounce correctly delays emission by 400ms; (2) cache returns mocked data on second identical call without triggering a new Supabase fetch; (3) cache invalidation clears the affected query on role/status change. Widget tests: (1) ListView.builder is used (verify itemCount and itemBuilder are present); (2) pagination triggers next-page fetch when last visible item index reaches threshold.

Regression test: ensure filter + pagination combination returns correct results (not duplicates or missing items).

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.