high priority medium complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

DuplicateActivityWarningDialog accepts a DuplicateActivityWarning object and renders its conflict description text
Both conflicting activity entries are displayed side by side in a clearly labeled comparison layout
Proceed button and Cancel button are both rendered with accessible labels and tap targets of at least 48x48dp
Focus is trapped inside the dialog — tabbing/swiping does not reach elements behind the modal overlay
VoiceOver and TalkBack announce the dialog as an alert dialog immediately upon presentation without requiring user interaction
Semantics(container: true, label: 'Duplicate activity warning', liveRegion: true) is applied to the root dialog node
Cancel button dismisses the dialog and returns a DialogResult.cancelled value to the caller
Proceed button dismisses the dialog and returns a DialogResult.proceed value to the caller
Dialog is not dismissible by tapping the scrim (barrierDismissible: false) to prevent accidental dismissal
Dialog renders correctly on screen widths from 320dp to 428dp without overflow
Design System v3 modal sheet component and tokens are used exclusively

Technical Requirements

frameworks
Flutter
BLoC
data models
DuplicateActivityWarning
ActivityEntry
DialogResult (enum: proceed, cancelled)
performance requirements
Dialog must animate open within one frame cycle using standard Flutter modal transition
No async operations inside the dialog widget itself — data is passed in via constructor
security requirements
Do not log or persist activity content displayed in the warning dialog beyond what is already in the audit trail
Dialog must not be dismissible programmatically by untrusted code paths — only via explicit user button taps
ui components
DuplicateActivityWarningDialog (AlertDialog or custom modal sheet)
ActivityConflictComparisonRow (side-by-side entry display widget)
AppButton (Proceed variant — warning/destructive style)
AppButton (Cancel variant — secondary style)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Implement as a stateless widget that is shown via showDialog() with barrierDismissible: false. Return DialogResult via Navigator.pop(context, result) from each button's onPressed callback so the caller receives the typed result. Use Semantics(container: true, label: 'Duplicate activity warning', liveRegion: true) as the root node of the dialog content to ensure screen readers announce it immediately as an alert. Flutter's AlertDialog does not automatically set the alert dialog role — the Semantics wrapper is mandatory.

For the side-by-side conflict comparison, use a Row with two Expanded children, each showing the relevant activity's date, time, chapter, and type. Use the design system's modal bottom sheet or AlertDialog variant consistently — avoid raw Dialog widget. The DialogResult enum (proceed, cancelled) should be defined in a shared types file so the calling widget can pattern-match on it cleanly.

Testing Requirements

Write widget tests using flutter_test: (1) mount DuplicateActivityWarningDialog with a mock DuplicateActivityWarning and assert conflict description text appears, (2) assert both activity entries are rendered, (3) tap Proceed and assert the future resolves with DialogResult.proceed, (4) tap Cancel and assert the future resolves with DialogResult.cancelled, (5) assert tapping outside the dialog does not dismiss it (barrierDismissible: false). Write semantics tests asserting the root node has liveRegion: true and a non-empty label. Perform manual VoiceOver and TalkBack tests on device to confirm immediate announcement on modal presentation. Write a golden test for the dialog in its default state.

Component
Duplicate Activity Warning Dialog
ui medium
Epic Risks (3)
high impact medium prob technical

The Duplicate Activity Warning Dialog must announce itself to VoiceOver and TalkBack immediately on appearance. Flutter's default modal semantics do not guarantee focus shift to the dialog on all platform versions, risking silent appearance for screen reader users — a WCAG 2.2 failure.

Mitigation & Contingency

Mitigation: Wrap the dialog content in a Semantics node with liveRegion: true and explicitly request focus via FocusScope.of(context).requestFocus() on the dialog's primary action button in the post-frame callback. Test on physical iOS (VoiceOver) and Android (TalkBack) devices, not only simulators.

Contingency: If automatic focus fails on a specific platform version, add a platform-specific fallback using SemanticsService.announce() to force a live region announcement of the dialog's headline text.

medium impact medium prob technical

The Chapter Membership Cubit tracks pending changes before commit to support the two-step add/confirm flow. If the user navigates away mid-edit or the app is backgrounded, uncommitted pending state could be replayed incorrectly on return, causing phantom affiliation additions or removals.

Mitigation & Contingency

Mitigation: Design the Cubit to hold pending changes only in transient in-memory state with no persistence. On any navigation-away event, emit a reset state that discards pending changes. Prevent accidental navigation during an active edit by showing a discard-changes confirmation dialog.

Contingency: If state desync is reported in production, add an explicit state reconciliation step in the Cubit's onResume handler that re-fetches the authoritative affiliation list from the repository and resets all pending state before re-rendering.

medium impact high prob technical

The Chapter Assignment Editor's searchable chapter list must load quickly. If the organisation has hundreds of chapters (NHF has 1,400 local chapters) and the full list is fetched on dialog open, the editor will be slow to display and the search will be sluggish.

Mitigation & Contingency

Mitigation: Scope the chapter list to only chapters within the coordinator's administrative scope (not all 1,400), leveraging the existing hierarchy access scope service. Implement server-side search with a minimum 2-character threshold and debounce to avoid excessive Supabase queries.

Contingency: If the scoped list is still too large, add local caching of the chapter list with a 15-minute TTL and an explicit refresh button, ensuring the editor is always responsive even on poor network conditions.