high priority medium complexity frontend pending frontend specialist Tier 14

Acceptance Criteria

SelectorMode.single: selecting a mentor from the overlay replaces any previous selection, closes the overlay, and displays the selected mentor's name in the TextField (read-only display)
SelectorMode.multi: selecting a mentor from the overlay adds them to a chip list below the TextField without closing the overlay (coordinator can keep selecting)
Multi-select chip displays mentor full name with a remove icon (×); tapping × removes that mentor from selection and calls onChanged with updated list
Chip list wraps to multiple lines using Wrap widget when more than ~3 mentors are selected
In single mode, the result tile shows a radio-style indicator (leading Radio or check icon) for the currently selected mentor
In multi mode, the result tile shows a checkbox-style indicator; already-selected mentors are checkmarked and tapping them deselects (removes from list)
onChanged is called with the updated List<PeerMentor> (multi) or PeerMentor? (single) after every selection or deselection
In multi mode, clearing the TextField search input does not clear the chip selection — chips persist independently of the search field
WCAG 2.2 AA: chips have Semantics(label: '${mentor.name}, selected. Tap to remove') for screen reader users
In multi mode, after removing all chips, the selector returns to its empty/idle state and onChanged is called with an empty list
Keyboard navigation: Tab moves between chips; Delete/Backspace on a focused chip removes it

Technical Requirements

frameworks
Flutter
Riverpod
data models
SelectorMode enum { single, multi }
PeerMentor { id, name, chapterId, chapterName }
ProxyPeerMentorSelectorState { selectedMentors: List<PeerMentor>, searchResults: List<PeerMentor>, query: String, mode: SelectorMode }
performance requirements
Chip list rerender on add/remove completes within one frame
Wrap layout for chips handles up to 20 selections without overflow
security requirements
Maximum selection limit should be enforced (e.g., max 50 for bulk registration) to prevent unbounded list growth and accidental mass submission
ui components
Wrap widget for chip layout
InputChip (Flutter material) for each selected mentor with deleteIcon
Radio or CheckboxListTile leading indicator in result overlay tiles
Updated PeerMentorResultTile supporting selected state prop
FocusNode per chip for keyboard navigation (Delete key handler)

Execution Context

Execution Tier
Tier 14

Tier 14 - 3 tasks

Can start after Tier 13 completes

Dependencies (21)
epic-proxy-activity-registration-orchestration-task-001 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-002 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-003 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-004 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-005 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-010 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-006 component Cross-Epic Component proxy-peer-mentor-selector depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-007 component Cross-Epic Component proxy-peer-mentor-selector depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-008 component Cross-Epic Component proxy-peer-mentor-selector depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-009 component Cross-Epic Component proxy-peer-mentor-selector depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-011 component Cross-Epic Component proxy-peer-mentor-selector depends on bulk-registration-service
epic-proxy-activity-registration-core-services-task-001 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-002 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-003 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-008 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-004 component Cross-Epic Component proxy-peer-mentor-selector depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-005 component Cross-Epic Component proxy-peer-mentor-selector depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-006 component Cross-Epic Component proxy-peer-mentor-selector depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-007 component Cross-Epic Component proxy-peer-mentor-selector depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-009 component Cross-Epic Component proxy-peer-mentor-selector depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-010 component Cross-Epic Component proxy-peer-mentor-selector depends on activity-attribution-service

Implementation Notes

Store the selected mentor(s) list in the Riverpod notifier alongside search state. For single mode, after selection update selectedMentors = [selected] and set TextField text to mentor.name (make it read-only or use a display controller). For multi mode, keep TextField editable for continued searching while chips display below. Use InputChip with onDeleted callback for chip removal.

In multi mode, pass selectedMentors to PeerMentorResultTile so it can show a checked state and toggle on tap (if already in list, remove; if not, add). Implement a FocusNode list for chips with RawKeyboardListener detecting LogicalKeyboardKey.delete/backspace for chip keyboard removal. Apply a maximum selection guard (e.g., maxSelections: 50) configurable via constructor. Chip text should use AppTextStyle.labelSmall with design token colors.

Coordinate with the parent screens (proxy-registration-screen and bulk-registration-screen) to confirm which mode each uses and ensure the widget's public API matches their expectations before finalizing constructor signatures.

Testing Requirements

Widget tests: (1) single mode — select mentor → TextField shows name, overlay closes; (2) single mode — select different mentor → previous selection replaced; (3) multi mode — select mentor → chip appears, overlay stays open; (4) multi mode — select same mentor again → deselects (chip removed); (5) tap chip remove icon → mentor removed from selection, onChanged called; (6) Wrap layout with 5+ chips renders without overflow at 375px screen width; (7) verify onChanged called with correct data type (single: PeerMentor?, multi: List) per mode. Accessibility test: verify chip Semantics label includes 'Tap to remove'. Use golden tests for chip list at 1 and 5 selections.

Component
Proxy Peer Mentor Selector
ui medium
Epic Risks (4)
medium impact high prob scope

The bulk registration screen combines pre-filled defaults, a dynamic multi-select participant list, per-participant conflict badges, and a batch submission confirmation — making it one of the most complex screens in the application. Scope creep or underestimated interaction complexity could cause the epic to exceed its estimate significantly.

Mitigation & Contingency

Mitigation: Implement the bulk screen in two vertical slices: (1) participant selection and form submission without conflict handling, (2) conflict badge rendering and override flow. Validate slice 1 with coordinators before building slice 2.

Contingency: If the full conflict review UI cannot be completed within the epic, ship the bulk screen with a simplified 'skip all duplicates' fallback mode and defer per-participant override toggles to a follow-up sprint.

medium impact medium prob technical

The proxy-registration-bloc must manage state across two distinct flows (single proxy and bulk) with branching conflict paths, intermediate buffering of bulk participant selections, and reliable state reset. Incorrect state transitions could leave the UI in a loading or stale-conflict state after submission.

Mitigation & Contingency

Mitigation: Model the BLoC state as a sealed class hierarchy with exhaustive pattern matching in the UI. Write state-machine unit tests that exercise every valid transition and assert that invalid transitions are no-ops. Use flutter_bloc's BlocObserver in debug builds to log all transitions.

Contingency: If BLoC state bugs surface in QA, introduce an explicit ResetToIdle event triggered on screen disposal to guarantee clean state, and add a 'Start over' affordance visible to the coordinator at any conflict step.

high impact medium prob technical

The typeahead peer mentor selector with multi-select mode may be difficult to operate with VoiceOver/TalkBack, particularly the dynamic search results list and the selected-chip removal controls, risking WCAG 2.2 AA non-compliance for screen reader users.

Mitigation & Contingency

Mitigation: Wrap all search result items and selected chips with explicit Semantics widgets providing role, label, and selected-state announcements. Test the selector with VoiceOver on iOS and TalkBack on Android during development, not only at QA time.

Contingency: If the multi-select typeahead cannot be made fully accessible within the sprint, provide an alternative flat scrollable list with checkboxes as a fallback mode, toggled by an accessibility-settings flag.

high impact medium prob security

The peer mentor selector must apply RLS chapter-scope filtering to show only mentors the coordinator is responsible for. If the Supabase query for the selector does not correctly join against the coordinator's chapter assignments, coordinators may see mentors from other chapters, violating data isolation.

Mitigation & Contingency

Mitigation: Implement the selector's data query using the same RLS-aware Supabase client used by the contact list feature, which already handles chapter-scope filtering. Write an integration test with a multi-chapter coordinator fixture asserting cross-chapter mentors are not returned.

Contingency: If a data isolation breach is discovered, immediately add a client-side chapter_id filter as a defence-in-depth measure and audit selector query logs for any unauthorised cross-chapter results.