DeduplicationQueueScreen scaffold and paginated list
epic-duplicate-activity-detection-ui-task-007 — Create the DeduplicationQueueScreen for coordinator users, implementing a paginated list of unresolved duplicate-flagged activity pairs. Each list item must show the two conflicting activity summaries, flagged date, and a link to the detail view. Include pull-to-refresh and infinite scroll pagination using DeduplicationQueueService.
Acceptance Criteria
Technical Requirements
Implementation Notes
Use a DeduplicationQueueCubit (or BLoC) with states: QueueInitial, QueueLoading, QueueLoaded(List
For the queue count badge, expose a stream from DeduplicationQueueService that emits the unresolved count — update via Supabase Realtime subscription on the duplicates table for live updates.
Testing Requirements
Write BLoC unit tests: (1) QueueLoading emitted on load; (2) QueueLoaded with items emitted on service success; (3) QueueEmpty emitted when service returns empty list; (4) QueueError emitted on service failure; (5) QueueLoadingMore emitted on pagination trigger; (6) new page appended to existing items on success. Write widget tests: (7) list items render correct activity pair data; (8) tapping a list item triggers navigation; (9) pull-to-refresh calls reload; (10) empty state visible when list is empty; (11) retry button calls reload on error. Test pagination: verify .range(0,19) on first load and .range(20,39) on second page. Mock DeduplicationQueueService with fake returning 20, then 5 items.
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.
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.
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.