critical priority medium complexity backend pending backend specialist Tier 3

Acceptance Criteria

When the user selects Proceed, a new activity record is inserted into the database with duplicate_reviewed = true
The inserted activity record contains all fields from the draft exactly as entered in the wizard — no data mutation occurs during insertion
DuplicateReviewedFlagMiddleware sets duplicate_reviewed = true on the activity payload before the repository insert call
Bottom sheet closes immediately after successful insert — no additional confirmation dialog required
A success snackbar or confirmation UI is shown to the user after the sheet closes
If the insert fails (network error, RLS rejection), the bottom sheet remains open and an error message is shown — the user can retry
The inserted activity's organization_id is taken from the authenticated session claims — never from client-supplied input
Audit: the inserted activity record includes peer_mentor_id from the authenticated user's profile, not from wizard form input
BLoC emits ActivityInsertSuccess state, which navigates the wizard to the success screen
Duplicate resolution action is idempotent: re-triggering Proceed with the same draft does not create a second record

Technical Requirements

frameworks
Flutter
BLoC
apis
Supabase PostgreSQL 15
Supabase Auth
data models
activity
performance requirements
Insert operation must complete within 3 seconds on a standard 4G connection
UI must show a loading state during insert — button disabled to prevent double-submission
security requirements
organization_id and peer_mentor_id must be derived from the JWT claims, never trusted from client wizard form data
RLS policy on the activities table must enforce that the inserting user can only write to their own organization
duplicate_reviewed flag must only be settable by the DuplicateReviewedFlagMiddleware — not directly from the UI payload
ui components
Proceed button loading state
Success snackbar / confirmation widget
Error inline message in bottom sheet

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Implement DuplicateResolutionHandler as a use case class (not inside the BLoC) with a single proceed(ActivityDraft draft, String sessionOrgId) method. DuplicateReviewedFlagMiddleware should be a simple transformer that takes an ActivityDraft and returns a new ActivityDraft with duplicate_reviewed = true — keep it stateless. The repository insert should use Supabase's .insert() method and return the inserted row for confirmation. Use a try/catch around the Supabase call and map exceptions to domain-level errors (NetworkError, PermissionError) before surfacing them to the BLoC.

Do not use upsert — a proceed action always creates a new record.

Testing Requirements

Write unit tests for DuplicateResolutionHandler.proceed() that verify: (1) repository.insertActivity() is called with duplicate_reviewed = true; (2) organization_id is sourced from session, not draft; (3) on success, ActivityInsertSuccess is emitted; (4) on repository failure, ActivityInsertError is emitted and sheet stays open. Write integration tests against a Supabase test project verifying: (5) the inserted row has duplicate_reviewed = true; (6) RLS prevents inserting into another organization. Mock DuplicateReviewedFlagMiddleware in unit tests; use the real implementation in integration tests.

Component
Duplicate Resolution Handler
service medium
Epic Risks (3)
high impact medium prob technical

The merge resolution path requires identifying which fields from the draft differ from the existing record, applying those differences to the existing record, and cancelling the draft — all as an atomic operation. Partial failures (e.g., update succeeds but draft cancellation fails) could leave the system in an inconsistent state.

Mitigation & Contingency

Mitigation: Implement the merge path as a Supabase RPC transaction that updates the existing record and soft-deletes the draft in a single atomic call. The DuplicateResolutionHandler should never attempt field-level merge at the application layer.

Contingency: If the atomic RPC approach proves too complex for the initial release, simplify the merge path to: mark existing record as the canonical record and cancel the new submission without field merging, displaying a message to the user to manually verify the existing record's fields. Log a follow-up ticket for full field-merge in a later sprint.

medium impact medium prob integration

The DuplicateWarningBottomSheet must intercept the activity wizard's save action without disrupting the wizard's existing navigation stack. If the bottom sheet is implemented as a separate route rather than an overlay, back navigation could break the wizard's step state.

Mitigation & Contingency

Mitigation: Use a `showModalBottomSheet` overlay pattern so the bottom sheet sits above the wizard route without pushing a new route onto the Navigator stack. The wizard's CuBit/BLoC retains all draft state while the sheet is visible. Test this integration with the existing activity-registration-cubit before merging.

Contingency: If the overlay approach causes Z-order or focus issues with the wizard's keyboard-aware layout, route the duplicate check result back to the wizard as a state event (DuplicateDetected), and let the wizard render a local inline warning banner instead of a bottom sheet.

medium impact medium prob technical

The bottom sheet and comparison panel involve complex layouts with multiple interactive elements. Screen reader users (particularly relevant for Blindeforbundet) may struggle with the side-by-side comparison layout if semantics are not carefully ordered.

Mitigation & Contingency

Mitigation: Design the comparison panel with a single-column semantic order (read record A fully, then record B fully) regardless of visual layout. Use Flutter's `Semantics` widget with `sortKey` to enforce correct traversal order. Test with TalkBack and VoiceOver against the WCAG 2.2 AA reading order criteria.

Contingency: If side-by-side layout cannot achieve acceptable screen reader ordering, switch to a stacked tab layout (Tab A / Tab B) for the comparison panel that is semantically simple even if less visually immediate for sighted users.