critical priority high complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

BulkProxyRegistrationScreen renders RecurringTemplateSelectorWidget and MentorMultiSelectWidget in a vertically stacked (mobile) or side-by-side (tablet/landscape) layout using responsive breakpoints from the design token system
A participant count badge updates in real time as mentors are selected/deselected, showing '0 selected', '1 selected', '5 selected' etc. with singular/plural grammar
The Submit button is disabled when 0 mentors are selected or when no template is chosen; it becomes active only when both conditions are satisfied
Tapping Submit opens a confirmation bottom sheet summarising the template name, date, activity type, and list of selected mentor names (truncated at 5 with '+N more' overflow)
After confirmation, the screen transitions to a loading state: Submit button shows a CircularProgressIndicator, mentor and template widgets become non-interactive
Partial failure state is rendered as an inline result list below the form: green checkmark rows for successes and red error rows for failures, each row showing mentor name and error reason
Full success state shows a success banner with total count and a 'Register More' action that resets the form without navigating away
All interactive elements meet WCAG 2.2 AA touch target minimum of 44×44 dp
Participant count badge changes are announced via Semantics(liveRegion: true) so screen readers read the updated count without user focus change
The screen handles BLoC state transitions: BulkRegistrationIdle → BulkRegistrationLoading → BulkRegistrationComplete (success/partial/failure) without visual glitches
Widget tests cover: initial disabled state, mentor selection enabling submit, confirmation bottom sheet content, loading state non-interactivity, partial failure rendering, success reset flow

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Riverpod (for service injection below BLoC layer)
apis
ProxyRegistrationBLoC event stream (BulkSubmissionRequested event)
RecurringTemplateSelectorWidget callback API
MentorMultiSelectWidget selection callback API
data models
BulkProxyRegistrationState
BulkSubmissionResult
SelectedMentorEntry
RecurringTemplateSelection
performance requirements
Screen initial render must complete within one frame (≤16 ms) with no jank; use const constructors for all static widgets
Participant count badge must update within one frame of mentor selection change — no debouncing on count display
Partial failure result list must render up to 50 mentor rows without frame drops; use ListView.builder for the result list
security requirements
Confirmation bottom sheet must display mentor names sourced from the local BLoC state (not re-fetched), preventing TOCTOU issues between selection and confirmation
Submit action must be idempotent at the UI layer: double-tap protection via disabling the button immediately on first tap
ui components
RecurringTemplateSelectorWidget
MentorMultiSelectWidget
ParticipantCountBadge (new widget)
BulkConfirmationBottomSheet
BulkResultListItem (success variant)
BulkResultListItem (failure variant)
SuccessBannerWidget
AppButton (disabled/loading/active states)
Semantics (liveRegion wrapper for count badge)

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Use BlocBuilder to rebuild only the affected sub-trees when BLoC state changes — wrap the participant count badge and submit button separately to avoid full-screen rebuilds on each mentor selection. The confirmation bottom sheet should be shown via showModalBottomSheet inside a BlocListener reaction to a BulkConfirmationRequested state, not directly from button onPressed, so it is testable via BLoC state injection. For the responsive layout, use LayoutBuilder to switch between Column (width < 600 dp) and Row (width ≥ 600 dp) arrangements. Partial failure list must use the organisation label system to display mentor relationship terminology consistent with the current organisation context.

Ensure all colour choices for success/failure rows come from the design token system (e.g. tokens.colorSuccess, tokens.colorError) — no hardcoded hex values.

Testing Requirements

Write widget tests using flutter_test and bloc_test. Test initial state (submit disabled), mentor selection flow (count badge updates, submit enables), confirmation bottom sheet content accuracy, loading state (widgets non-interactive), partial failure list rendering with mixed success/failure rows, full success banner with reset. Use a MockProxyRegistrationBLoC to drive state transitions. Accessibility tests: verify Semantics tree contains liveRegion node for count badge; verify all tap targets are ≥44dp via tester.getSize().

Integration test: full happy-path submission flow against the real BLoC wired to mocked services. Aim for 85% widget branch coverage.

Component
Bulk Proxy Registration Screen
ui high
Dependencies (3)
Epic Risks (3)
high impact high prob scope

Partial failures in bulk registration — where some mentors succeed and others fail — create a complex UX state that is easy to mishandle. If the UI does not clearly communicate which records succeeded and which failed, coordinators may re-submit already-saved records (creating duplicates) or miss failed records entirely (creating underreporting).

Mitigation & Contingency

Mitigation: Design the per-mentor result screen as a primary deliverable of this epic, not an afterthought. Use a clear list view with success/failure indicators per mentor name, and offer a 'Retry failed' action that pre-selects only the failed mentors for resubmission.

Contingency: If partial failure UX proves too complex to deliver within scope, implement a simpler all-or-nothing submission mode for the initial release with a clear error message listing which mentors failed, and defer the partial-retry UI to a follow-up sprint.

medium impact medium prob technical

Submitting proxy records for a large group (e.g., 30+ mentors) as individual Supabase inserts may cause latency issues or hit rate limits, degrading the coordinator experience and potentially causing timeout failures that leave data in an inconsistent state.

Mitigation & Contingency

Mitigation: Implement the BulkRegistrationOrchestrator to batch inserts using a Supabase RPC call that accepts an array of proxy records, reducing round-trips to a single network call. Add progress indication using a stream of per-record results if the RPC supports it.

Contingency: If the RPC approach is blocked by Supabase limitations, fall back to chunked parallel inserts (5 records per batch) with retry logic, capping total submission time and surface a progress bar to manage coordinator expectations.

medium impact medium prob technical

Unifying state management for both single and bulk proxy flows in a single BLoC risks state leakage between flows — for example, a previously selected mentor list persisting when a coordinator switches from bulk to single mode — causing confusing UI states or incorrect submissions.

Mitigation & Contingency

Mitigation: Define separate, named state subtrees within the BLoC for single-proxy state and bulk-proxy state, with explicit reset events triggered on flow entry. Write unit tests for state isolation scenarios using the bloc_test package.

Contingency: If unified BLoC state becomes unmanageable, split into two separate BLoCs (ProxySingleRegistrationBLoC and ProxyBulkRegistrationBLoC) sharing only common events via a parent coordinator Cubit.