critical priority medium complexity integration pending integration specialist Tier 2

Acceptance Criteria

When the user taps Submit in the activity wizard and DuplicateDetectionService returns ≥1 candidate, the bottom sheet is presented before any database write occurs
When DuplicateDetectionService returns 0 candidates, the wizard proceeds to normal submission without showing the bottom sheet
Wizard submission state is paused (submit button disabled or loading indicator shown) while duplicate check is in progress
All wizard form data is preserved in state when the bottom sheet is displayed — no data loss on sheet open
Bottom sheet receives the first (highest-confidence) candidate activity as its comparison data
If DuplicateDetectionService call fails with a network error, the wizard surfaces an error snackbar and allows the user to retry or proceed without duplicate check
The bottom sheet close callback (Proceed / Merge / Cancel) correctly resumes or discards the wizard flow
Haptic feedback triggers on bottom sheet open (respects AccessibilityPreferences.haptic_feedback_enabled)
BLoC/Cubit emits a DuplicateCheckInProgress state during service call and DuplicateCandidatesFound state on result
Screen reader announces 'Possible duplicate activity detected' when bottom sheet appears

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
DuplicateDetectionService (internal)
Supabase PostgreSQL 15
data models
activity
accessibility_preferences
performance requirements
Duplicate detection service call must complete within 2 seconds on a 4G connection
Wizard UI must remain responsive during duplicate check — run check asynchronously, never on the main thread's build cycle
security requirements
Duplicate check query must be scoped to the current organization_id via RLS — never returns candidates from other organizations
Draft activity data must not be persisted to Supabase before the user resolves the duplicate warning
ui components
Activity wizard submit button with loading state
DuplicateWarningBottomSheet
Error snackbar for failed duplicate check

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

In the wizard's BLoC, intercept the submit event and dispatch a CheckForDuplicates event before the CreateActivity event. Use a separate DuplicateCheckCubit or extend the wizard BLoC with duplicate-specific states. Pass the wizard's current form data as a frozen object to the duplicate check; the same object is later passed to DuplicateResolutionHandler. Use a Completer pattern: show the bottom sheet, await the user's choice, then branch in the BLoC based on the resolution type (proceed / merge / cancel).

This keeps wizard logic centralized rather than split across UI callbacks. Respect AccessibilityPreferences.haptic_feedback_enabled before calling HapticFeedback.mediumImpact().

Testing Requirements

Write BLoC unit tests covering: (1) DuplicateCheckInProgress emitted on submit tap; (2) DuplicateCandidatesFound emitted when service returns candidates; (3) NoDuplicatesFound emitted when service returns empty list; (4) DuplicateCheckError emitted on service failure. Write widget integration tests using flutter_test that simulate: (5) submit tap → service returns candidates → bottom sheet appears; (6) submit tap → service returns empty → normal submission path. Mock DuplicateDetectionService with a fake that can return success, empty, or error responses. Test haptic feedback call is made when sheet opens.

Component
Duplicate Warning Bottom Sheet
ui 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.