Implement confirmation screen BLoC and service dispatch
epic-bulk-and-proxy-registration-bulk-ui-task-013 — Build the BulkConfirmationCubit that manages the confirmed mentor list state (tracking removals), calls the BulkRegistrationService on coordinator confirmation tap, surfaces a loading state during batch processing, and transitions to a results screen showing per-mentor outcomes. Errors should be displayed inline without navigating away so coordinators can retry.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Use a sealed class for BulkConfirmationState with Equatable for reliable BlocBuilder rebuilds. The loading overlay should use AbsorbPointer(absorbing: true) wrapping the entire scaffold body — this prevents swipe-to-dismiss and scroll events while the request is in flight without disabling widget focus (which would break screen readers). For the service call, wrap in a try/catch with a Future.timeout(Duration(seconds: 30)) and map DioException / SocketException to user-friendly messages via a thin error mapping utility shared with other cubits. Navigate to BulkResultsScreen using go_router with the BulkRegistrationResult serialised as extra — avoid passing complex objects through the route path.
Keep the cubit focused on orchestration; do not place business logic (e.g., duplicate filtering) inside the cubit — delegate to the service layer.
Testing Requirements
Unit tests (flutter_test with mocktail): mock BulkRegistrationService; test confirmTapped() transitions idle → loading → success with correct BulkRegistrationResult; test idle → loading → failure with error message preserved in state; test that removeMentor() is a no-op when state is loading; test timeout scenario emits failure with timeout message. Widget tests: in loading state assert AbsorbPointer blocks list interaction and CircularProgressIndicator is visible; in failure state assert InlineErrorBanner is visible with correct message and Retry button re-triggers confirmTapped(); in success state assert navigation to BulkResultsScreen occurred. Integration test: stub BulkRegistrationService at the repository layer and run the full confirm flow end-to-end within the widget tree.
If the batch insert RPC returns a mix of successes and failures (e.g., 3 of 10 mentors fail due to constraint violations that slipped through application-level duplicate detection), the confirmation screen result state becomes ambiguous. A coordinator who sees '7 of 10 succeeded' may not know whether to manually register the 3 failures, retry, or escalate — leading to either duplicate registrations or silent underreporting.
Mitigation & Contingency
Mitigation: Design the Bulk Registration Service to return a strongly typed BulkRegistrationResult with per-mentor RegistrationOutcome (success | duplicate_detected | constraint_violation | permission_denied). Design the result screen to list each failed mentor with a specific, plain-language reason and a one-tap 'Retry for this mentor' action that pre-fills the activity wizard with the batch template for that individual.
Contingency: If per-mentor retry UI is too complex to deliver within the epic scope, fall back to displaying failed mentors with their error codes and instructing coordinators to use single-proxy mode for the failures. Document this as a known limitation in release notes and create a follow-up ticket for per-mentor retry in the next sprint.
The Proxy Activity Wizard must reuse the existing activity wizard step widgets (type, date, duration, notes) while injecting a proxy attribution banner and a different submission payload builder. If the existing wizard is not designed for composability, the proxy variant may require forking the widget tree, creating two maintenance-diverging codebases that will drift out of sync when the base wizard is updated (e.g., new activity types added, new mandatory fields).
Mitigation & Contingency
Mitigation: Before implementing the Proxy Activity Wizard, audit the existing activity wizard's architecture. If steps are already extracted as independent StatelessWidget/ConsumerWidget classes, compose them directly with a wrapping Column that injects the attribution banner. If they are tightly coupled inside a parent widget, refactor the existing wizard to accept a nullable ProxyContext parameter before starting the proxy variant — this refactor should be a prerequisite task in this epic.
Contingency: If refactoring the base wizard is blocked by unrelated in-flight work on that component, implement the proxy wizard as a full fork but create a shared StepWidgets library file that both the base wizard and proxy wizard import. Schedule a deduplication refactor as a tech-debt ticket in the next planning cycle.
The bulk registration flow spans three sequential screens (multi-select → activity form → confirmation → result) with shared mutable state: the selected mentor list, the activity template, the per-mentor duplicate warnings, and the final submission result. Managing this state across screens without a well-designed Bloc risks state leaks, stale duplicate warning data after mentor removal, and confirmation screen inconsistencies if the user navigates back and changes the mentor selection.
Mitigation & Contingency
Mitigation: Define a single BulkRegistrationBloc (or Cubit) with explicit state transitions covering: MentorsSelected → ActivityTemplateCompleted → DuplicatesChecked → ConfirmationReady → Submitting → SubmissionResult. Each backward navigation event (e.g., 'Back' from confirmation to mentor selection) dispatches a ResetToMentorSelection event that clears downstream state. Unit test every state transition with edge cases including empty mentor list, all mentors having duplicates, and network failure during submission.
Contingency: If state management complexity causes persistent bugs in testing, simplify by passing state explicitly through Navigator arguments (immutable snapshots per screen) rather than a shared Bloc. This reduces flexibility but eliminates cross-screen state mutation bugs.