Multi-Select search, minimum-one validation gate, and navigation
epic-bulk-and-proxy-registration-mentor-selection-task-010 — Add a real-time search bar to the Peer Mentor Multi-Select List with debounced filtering that preserves already-selected items when the query changes. Implement a minimum-one-selection validation gate that disables the 'Continue' button and shows a plain-language inline error when no mentors are selected. Wire the 'Continue' action to pass the selected mentor ID list forward to the bulk registration wizard.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 6 - 158 tasks
Can start after Tier 5 completes
Implementation Notes
Add a searchQuery string field to MentorSelectionState. Dispatch a MentorSearchQueryChangedEvent from the search TextField's onChanged using a RxDart debounceTime or a manual Timer in the BLoC. The BLoC computes filteredMentors as a derived list from allMentors filtered by searchQuery; selectedMentorIds remains a Set
Inline error: use a Visibility or AnimatedSwitcher widget below the list that shows the error Text only when showValidationError is true in BLoC state; set showValidationError to true when user attempts navigation with empty selection. Route argument: define a BulkRegistrationWizardArgs class with final List
Testing Requirements
Widget tests (flutter_test): (1) Render list with 5 mock mentors, type a query that matches 2 mentors, assert only 2 items visible. (2) Select 1 visible item, change query so that item is hidden, assert count badge still shows '1 selected' and the item remains in state. (3) Clear query, assert all 5 items visible and selected item shows checked. (4) With 0 selections, assert 'Continue' button has semantics label indicating disabled.
(5) With 0 selections, trigger continue action, assert inline error message appears in widget tree. (6) Select 1 item, assert inline error disappears. (7) Tap 'Continue' with 2 selected, assert navigator receives route push with correct selectedMentorIds argument. Debounce test: dispatch rapid text-change events, assert BLoC emits filter events only after 300 ms silence.
NHF coordinators may manage dozens of peer mentors across multiple chapters. If the multi-select list renders all contacts in a single unsorted ListView, performance degrades with 50+ items, and coordinators cannot efficiently locate a specific mentor, increasing the probability of selection errors and wrong-person proxy registrations.
Mitigation & Contingency
Mitigation: Use a SliverList with itemExtent for fixed-height rows to enable O(1) scroll position calculation. Implement the search filter using a debounce utility operating on an in-memory list (no extra API calls). Sort the contact list alphabetically by default. Add chapter-filter chips above the list for NHF's multi-chapter coordinators.
Contingency: If performance issues arise in testing with real data sets, introduce pagination with a 'load more' trigger at the bottom of the list and cache rendered rows using Flutter's AutomaticKeepAliveClientMixin.
NHF has a complex 12-national-association / 9-region / 1,400-chapter hierarchy. It is ambiguous whether a coordinator can proxy-register for peer mentors outside their immediately assigned chapter. If the contact list is not correctly scoped by RLS, coordinators might see — and register on behalf of — peer mentors they do not manage, creating fraudulent activity records that skew Bufdir statistics.
Mitigation & Contingency
Mitigation: The Proxy Contact List Provider must query only peer mentors linked to the coordinator's own chapter scope via RLS. Add an explicit Supabase query test asserting that a coordinator from chapter A cannot retrieve peer mentors from chapter B. Display each mentor's chapter affiliation in the list row so coordinators can visually verify scope.
Contingency: If RLS scope is found to be too permissive in testing, apply a server-side coordinator_id filter as a secondary guard on the query. Block the feature release until the scope test passes consistently.