high priority high complexity frontend pending frontend specialist Tier 16

Acceptance Criteria

Each participant row in BulkParticipantList shows a conflict badge when the BLoC state for that participant contains a duplicate conflict, and no badge when there is no conflict
Conflict badge uses a visually distinct colour (e.g. amber/warning token) with aria-label 'Scheduling conflict' readable by screen readers
Tapping Submit dispatches SubmitBulkRegistration event; a non-dismissible progress overlay appears within one frame and blocks all interaction
Progress overlay includes a loading indicator and accessible label 'Submitting registrations…' for screen readers
If BLoC returns conflicting participants, DuplicateWarningDialog opens listing each conflicting mentor by name with options to skip or override
After the coordinator chooses skip/override for all conflicts, submission resumes and the progress overlay reappears until completion
On successful batch completion, progress overlay dismisses and a snackbar shows 'X registrations saved' plus 'Y skipped' if any were skipped
On partial or full failure, snackbar shows an error summary; the form is not cleared so the coordinator can retry
After successful submission, BLoC emits reset state, participant list clears, and count badge resets to 0
Submit button is disabled when the participant list is empty; tooltip explains why when tapped in disabled state

Technical Requirements

frameworks
Flutter
flutter_bloc
BLoC
apis
BulkRegistrationService (internal)
ProxyRegistrationService (internal)
data models
BulkParticipant
BulkRegistrationResult
ConflictDetail
performance requirements
Progress overlay must appear within one frame of Submit tap — dispatch event first, then let BLoC drive overlay visibility
DuplicateWarningDialog renders conflict list for up to 50 participants without jank
Snackbar appears within 200 ms of BLoC emitting completion state
security requirements
SubmitBulkRegistration event includes coordinator's Supabase user ID from session — never trust client-provided coordinator ID
Conflict resolution choices (skip/override) are recorded server-side for audit trail
Progress overlay prevents double-submission by blocking UI — additionally guard in BLoC by ignoring SubmitBulkRegistration if already in submitting state
ui components
Conflict badge widget (per participant row)
Non-dismissible progress overlay
DuplicateWarningDialog
Submit button with disabled state
Summary result snackbar

Execution Context

Execution Tier
Tier 16

Tier 16 - 2 tasks

Can start after Tier 15 completes

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

Implementation Notes

Drive all overlay and dialog visibility exclusively from BLoC state — avoid local setState for submission phases to keep the UI fully testable. Use BlocListener for side-effect-only reactions (opening dialog, showing snackbar) and BlocBuilder only for state-driven widget tree changes (overlay visibility, conflict badges). The DuplicateWarningDialog should receive its conflict list from the BLoC state rather than storing its own copy. For the progress overlay, prefer a Stack with an AbsorbPointer + IgnorePointer wrapping the form content combined with a centered CircularProgressIndicator overlay so the layout does not shift.

Model the resolution choices as a map of participantId → ConflictResolution in BLoC state so the dialog can resume mid-list if interrupted. The summary snackbar should use the design token colour for success/warning/error consistently with the rest of the app. Per workshop notes, NHF specifically requires duplikatvarsling (duplicate detection) — this flow directly implements that requirement.

Testing Requirements

Widget tests: (1) conflict badge renders when BLoC state contains conflict for a participant and is absent otherwise; (2) progress overlay appears on SubmitBulkRegistration dispatch and disappears on completion state; (3) DuplicateWarningDialog opens when BLoC emits conflicts and closes after resolution; (4) snackbar shows correct success count and skipped count from BLoC result state; (5) Submit button is disabled when participant list is empty. Integration test: full bulk submit flow with a stubbed BulkRegistrationService returning mixed success/conflict/error results. Use flutter_test + mocktail. Golden test for DuplicateWarningDialog with three conflicting participants.

Target ≥ 85% branch coverage for submission state transitions.

Component
Bulk Registration Screen
ui high
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.