Composable filter bar UI
epic-admin-portal-user-management-task-006 — Implement the filter bar UI allowing admins to compose filters by role, chapter, status, and certification state. Filters must be independently selectable and combinable. Active filters should be visually indicated and individually clearable. Filter state persists in the BLoC and triggers server-side refetch.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
The filter bar must be a BlocBuilder consumer of UserManagementBloc — it reads activeFilters from state and dispatches FilterChanged events. Do not use local StatefulWidget state for filter values; if the BLoC is rebuilt (e.g. after navigation back), filter selections must be restored from BLoC state. For the Chapter multi-select, consider a ModalBottomSheet with checkboxes for each chapter rather than a nested dropdown, as multi-select dropdowns are complex to make accessible.
Use toggle chips (Flutter ChoiceChip or FilterChip) for Status and CertificationState since these have a small fixed set of options. Debouncing is handled in the BLoC (task-004), not in the UI — the UI can dispatch FilterChanged on every change. Use design token colours for active/inactive chip states (e.g. `AppColors.primaryContainer` for active, `AppColors.surfaceVariant` for inactive).
Testing Requirements
Widget tests: render filter bar with no active filters and assert all controls show inactive state; select a role filter and assert FilterChanged is dispatched with correct FilterParams; clear a single filter and assert other filters unchanged; clear all filters and assert FilterParams is fully null; assert 'Clear all' button absent when no filters active, present when any filter active. BLoC integration test: apply role + chapter filter, assert list refreshes with both constraints. Accessibility test: verify all dropdown options have semantic labels. Snapshot/golden test for filter bar in active and inactive states.
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.
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.
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.