high priority medium complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

A 'Select All' toggle button is visible at the top of the mentor list, above the first list item
Tapping 'Select All' marks all visible mentor items as selected and the button label changes to 'Deselect All'
Tapping 'Deselect All' clears all selections and the button label reverts to 'Select All'
The selected-count badge (e.g., '12 selected') updates synchronously with every checkbox tap — no perceptible delay
The count badge accurately reflects the number of selected items at all times, including after select-all and deselect-all operations
The badge is wrapped in a Flutter Semantics widget with liveRegion: true so VoiceOver/TalkBack announces count changes without moving focus
Selecting all 1 400+ NHF chapters completes without dropped frames (target: 60 fps, measured via Flutter DevTools)
The select-all state is tri-state: 'none selected' shows unchecked, 'some selected' shows indeterminate (dash), 'all selected' shows checked
When search is active (task-010), 'Select All' applies only to filtered results, not the full list
The select-all control has a minimum touch target of 44×44 dp to meet WCAG 2.2 AA

Technical Requirements

frameworks
Flutter
BLoC
data models
PeerMentor
MentorSelectionState
performance requirements
Select-all over 1400 items must complete in under 16 ms per frame to maintain 60 fps
Use a Set<String> (mentor IDs) in BLoC state rather than a per-item boolean flag to keep state mutations O(1) for toggle and O(n) for select-all but with minimal widget rebuilds
ListView.builder with const item widgets to avoid rebuilding unchanged rows
Limit Semantics live region announcements to debounced updates (min 300 ms apart) to avoid flooding screen reader queues
security requirements
No PII (full names, contact details) stored in the selection Set beyond mentor IDs
Selection state lives only in BLoC memory — never persisted to disk or logs
ui components
SelectAllToggleWidget (custom stateless widget wrapping Checkbox + label)
SelectedCountBadge (Semantics liveRegion wrapper around a styled Container/Text)
MentorListItem (checkbox row — reused from task-008)
AppButton (existing design-token button for select-all row)

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Store the selection as a Set selectedMentorIds inside MentorSelectionState (BLoC). Dispatch a SelectAllMentorsEvent that copies all visible mentor IDs into the set and a DeselectAllMentorsEvent that clears it. The BlocBuilder rebuilds only the SelectAllToggleWidget and SelectedCountBadge via a buildWhen predicate that compares set length, preventing per-row rebuilds on select-all. For the live region, wrap the badge Text inside Semantics(liveRegion: true, label: '$count selected').

Flutter's Semantics system will announce label changes automatically on iOS (VoiceOver) and Android (TalkBack). For the tri-state indeterminate visual, use a custom painter or an Icon(Icons.remove) overlay on the checkbox rather than Flutter's built-in Checkbox which lacks indeterminate state natively. Apply design tokens for spacing (e.g., AppSpacing.sm) and typography to the badge to stay consistent with the design system.

Testing Requirements

Widget tests (flutter_test): (1) Render the list with 5 mock mentors, tap 'Select All', assert all checkboxes are checked and badge shows '5 selected'. (2) Tap 'Deselect All', assert all unchecked and badge shows '0 selected'. (3) Tap one item, assert tri-state indeterminate indicator appears. (4) Assert Semantics tree contains a live region node whose label matches the count badge text.

Performance test: pump a 1400-item list, invoke select-all event, verify frame budget is not exceeded using Flutter's test binding timeDilation. Accessibility test: use flutter_test SemanticsController to verify the live region label updates after selection events.

Component
Peer Mentor Multi-Select List
ui medium
Epic Risks (2)
medium impact medium prob technical

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.

high impact medium prob security

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.