Peer Mentor Single Selector search and single-select enforcement
epic-bulk-and-proxy-registration-mentor-selection-task-006 — Add a search bar to the Peer Mentor Single Selector that filters the contact list in real time using debounced local search. Implement single-select radio semantics so exactly one mentor can be selected at a time. Enforce a validation gate — the 'Continue' action button remains disabled until a selection is made — and display a clear inline error message if the user attempts to proceed without selecting.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
Manage search and selection state in a single Riverpod StateNotifier (SingleSelectorNotifier) so both pieces of state are co-located and can be tested in isolation. Debounce by holding a Timer in the notifier and cancelling it on each new search input event — avoid using StreamTransformer unless it is already established in the project. The filter logic should derive the visible list via a Provider.select() or a derived AutoDisposeProvider watching both the full list and the search query; this avoids re-rendering the full list widget when only the query changes. For the Continue button, pass an onPressed: null value when no mentor is selected — Flutter's ElevatedButton/TextButton automatically renders in a disabled style and is correctly announced as disabled by screen readers.
Store hasAttemptedContinue as a boolean flag in the notifier to control inline error visibility, flipping it true on a Continue tap and false when a valid selection exists.
Testing Requirements
Write widget tests for: (1) search filtering — type 'anna', assert only rows containing 'anna' (case-insensitive) are visible; (2) debounce — use fake async to verify list does not re-render before 300 ms elapses; (3) selection — tap a row, assert it is visually selected and others are not; (4) single-select enforcement — tap row A, then row B, assert row A is deselected and row B is selected; (5) Continue disabled — assert button is disabled with no selection; (6) Continue enabled — select a mentor, assert button is enabled; (7) validation error — call continue handler with no selection, assert error message is visible; (8) error clears — assert error disappears after selection. Write unit tests for the debounce and filter logic in isolation.
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.