high priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

Filter panel is accessible from DeduplicationQueueScreen via a clearly labeled filter icon button in the app bar
Date range filter accepts start and end dates using a Flutter date picker; selecting only one bound is valid (open-ended range)
Status filter presents exactly three mutually exclusive options: 'All', 'Unresolved', 'Resolved'; default is 'Unresolved'
Activity type filter renders a multi-select chip list populated dynamically from available activity types in the queue dataset
Sort control exposes exactly three options: 'Newest first' (default), 'Oldest first', 'By activity date'
Active filter/sort state is held in a BLoC/Riverpod state object and survives tab switches and scroll position changes within the same app session
Applying any filter or sort change triggers a new query to DeduplicationQueueService with updated parameters; the list refreshes within 300 ms on a standard device
An active-filter indicator (e.g., badge on the filter button or a chip strip above the list) shows when non-default filters are applied
A 'Clear all filters' action resets all controls to their defaults and immediately refreshes the list
Empty-state widget is shown when no pairs match the active filters, with a prompt to clear filters
Filter and sort controls meet WCAG 2.2 AA: all controls have minimum 44×44 dp touch targets and descriptive semantic labels
Unit tests cover filter state transitions; widget tests verify list re-renders on filter change

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
DeduplicationQueueService.fetchPairs(filters, sortOrder)
ActivityTypeRepository.getDistinctTypes()
data models
DuplicatePair
DeduplicationQueueFilter
DeduplicationQueueSortOrder
ActivityType
performance requirements
Filter application must refresh list within 300 ms on a mid-range device
Date range picker must not block the UI thread; use isolate or lazy loading if needed
Filter state updates must not cause full widget tree rebuilds outside the list widget subtree
security requirements
Filter parameters must be validated before being passed to Supabase queries to prevent injection
Coordinator role check must gate access to the filter UI; non-coordinator roles must not see this screen
ui components
FilterBottomSheet or FilterSidePanel widget
DateRangePickerField (wrapping Flutter showDateRangePicker)
StatusFilterChips (radio-style chip group)
ActivityTypeMultiSelectChips
SortOrderDropdown or SegmentedControl
ActiveFilterIndicatorStrip
EmptyStateWidget

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Model filter state as an immutable value class (DeduplicationQueueFilter) containing optional DateTimeRange, nullable ActivityTypeFilter list, and a DeduplicationSortOrder enum. Hold this in a Cubit or BLoC alongside the paginated pair list. Avoid embedding filter logic inside the widget — delegate entirely to the state layer. Use Riverpod's select() or BLoC's buildWhen to prevent unnecessary rebuilds of the list when unrelated state changes.

For the date range picker, call showDateRangePicker from the material library; wrap in a helper to enforce that start <= end. Populate activity type chips lazily from a cached repository call made once when the filter panel opens. Persist filter state within the provider/BLoC scope tied to the coordinator's session; do not persist to shared preferences (session-only requirement). Ensure the filter bottom sheet is implemented as a modal bottom sheet so the screen reader treats it as a dialog context.

Testing Requirements

Unit tests (flutter_test) must cover DeduplicationQueueFilterBloc/Cubit: initial state defaults to status=unresolved and sort=newest-first; each filter field update emits a new state; clearing filters resets to defaults. Widget tests must verify that applying a date range filter updates the list title or indicator, that the empty-state widget renders when no pairs match, and that the 'Clear all' button is visible only when non-default filters are active. Integration test must call the real DeduplicationQueueService mock and confirm the correct Supabase query parameters are sent. Accessibility test using flutter_test semantics API must assert all filter controls have a non-empty semanticsLabel.

Component
Deduplication Queue Screen
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.