critical priority medium complexity frontend pending frontend specialist Tier 13

Acceptance Criteria

Two-column layout displays 'New Activity' (incoming) and 'Existing Activity' (duplicate) side by side using Row + Expanded(flex:1) widgets
Each column renders: date, activity type, duration, and peer mentor name in a visually distinct card or section
Column headers are clearly labelled ('New Activity' / 'Existing Activity') with design token typography (AppTextStyle.labelMedium or equivalent)
Fields that differ between the two activities are visually highlighted (e.g., accent color text or background) to draw coordinator attention to the conflict
Layout remains readable and non-overflowing at Flutter textScaleFactor 1.0, 1.3, and 1.5 (large font accessibility)
At narrow screen widths (<360px), layout falls back to a stacked Column layout rather than overflowing
All date values use the locale-appropriate format (Norwegian: dd.MM.yyyy) via intl package
Peer mentor name is truncated with ellipsis if too long, not causing overflow
Conflict panel is populated from the ActivityConflict data model passed from the BLoC state
WCAG 2.2 AA contrast ratio ≥4.5:1 for all text in both columns, including highlighted fields

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
intl (date formatting)
data models
ActivityConflict { incomingActivity: ActivityDraft, existingActivity: ActivityRecord }
ActivityDraft { date, activityType, durationMinutes, mentorId, mentorName }
ActivityRecord { id, date, activityType, durationMinutes, mentorId, mentorName }
performance requirements
Conflict panel renders synchronously from in-memory BLoC state — no additional async calls
No layout thrash: use const constructors wherever possible
security requirements
Display only necessary PII (mentor name) — do not expose mentor's personal ID numbers or contact details in the conflict panel
ui components
Row + Expanded(flex:1) two-column container
ConflictColumn sub-widget (reusable for both sides)
ConflictFieldRow sub-widget (label + value with optional highlight)
Design token typography and color styles
Responsive fallback: LayoutBuilder for narrow screens

Execution Context

Execution Tier
Tier 13

Tier 13 - 6 tasks

Can start after Tier 12 completes

Dependencies (21)
epic-proxy-activity-registration-orchestration-task-001 component Cross-Epic Component duplicate-warning-dialog depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-002 component Cross-Epic Component duplicate-warning-dialog depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-003 component Cross-Epic Component duplicate-warning-dialog depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-004 component Cross-Epic Component duplicate-warning-dialog depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-005 component Cross-Epic Component duplicate-warning-dialog depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-010 component Cross-Epic Component duplicate-warning-dialog depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-006 component Cross-Epic Component duplicate-warning-dialog depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-007 component Cross-Epic Component duplicate-warning-dialog depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-008 component Cross-Epic Component duplicate-warning-dialog depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-009 component Cross-Epic Component duplicate-warning-dialog depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-011 component Cross-Epic Component duplicate-warning-dialog depends on bulk-registration-service
epic-proxy-activity-registration-core-services-task-001 component Cross-Epic Component duplicate-warning-dialog depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-002 component Cross-Epic Component duplicate-warning-dialog depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-003 component Cross-Epic Component duplicate-warning-dialog depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-008 component Cross-Epic Component duplicate-warning-dialog depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-004 component Cross-Epic Component duplicate-warning-dialog depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-005 component Cross-Epic Component duplicate-warning-dialog depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-006 component Cross-Epic Component duplicate-warning-dialog depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-007 component Cross-Epic Component duplicate-warning-dialog depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-009 component Cross-Epic Component duplicate-warning-dialog depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-010 component Cross-Epic Component duplicate-warning-dialog depends on activity-attribution-service

Implementation Notes

Extract a ConflictColumn widget and a ConflictFieldRow widget to keep DuplicateWarningDialog clean. Use LayoutBuilder to detect available width and switch between Row and Column layout at a breakpoint of ~300px per column (total ~600px). For field highlighting, compare incomingActivity and existingActivity field values at render time — wrap differing values in a Container with AppColors.warningBackground. Use design tokens exclusively: AppTextStyle, AppColors, AppSpacing.

Norwegian locale date formatting: DateFormat('dd.MM.yyyy', 'nb_NO') from intl — ensure 'nb' locale is initialized in main.dart. Duration should be displayed as 'X min' or 'X t Y min' using a helper function consistent with the rest of the app.

Testing Requirements

Widget tests: (1) render ConflictDetailPanel with mock ActivityConflict and assert both columns display correct date, type, duration, mentor name; (2) assert highlighted fields are visually distinct when values differ (check widget tree for highlight color widget); (3) golden tests at textScaleFactor 1.0 and 1.5; (4) test narrow screen fallback at 320px width using LayoutBuilder condition. Accessibility tests: verify each field row has a Semantics label combining label + value for screen reader announcement.

Epic Risks (4)
medium impact high prob scope

The bulk registration screen combines pre-filled defaults, a dynamic multi-select participant list, per-participant conflict badges, and a batch submission confirmation — making it one of the most complex screens in the application. Scope creep or underestimated interaction complexity could cause the epic to exceed its estimate significantly.

Mitigation & Contingency

Mitigation: Implement the bulk screen in two vertical slices: (1) participant selection and form submission without conflict handling, (2) conflict badge rendering and override flow. Validate slice 1 with coordinators before building slice 2.

Contingency: If the full conflict review UI cannot be completed within the epic, ship the bulk screen with a simplified 'skip all duplicates' fallback mode and defer per-participant override toggles to a follow-up sprint.

medium impact medium prob technical

The proxy-registration-bloc must manage state across two distinct flows (single proxy and bulk) with branching conflict paths, intermediate buffering of bulk participant selections, and reliable state reset. Incorrect state transitions could leave the UI in a loading or stale-conflict state after submission.

Mitigation & Contingency

Mitigation: Model the BLoC state as a sealed class hierarchy with exhaustive pattern matching in the UI. Write state-machine unit tests that exercise every valid transition and assert that invalid transitions are no-ops. Use flutter_bloc's BlocObserver in debug builds to log all transitions.

Contingency: If BLoC state bugs surface in QA, introduce an explicit ResetToIdle event triggered on screen disposal to guarantee clean state, and add a 'Start over' affordance visible to the coordinator at any conflict step.

high impact medium prob technical

The typeahead peer mentor selector with multi-select mode may be difficult to operate with VoiceOver/TalkBack, particularly the dynamic search results list and the selected-chip removal controls, risking WCAG 2.2 AA non-compliance for screen reader users.

Mitigation & Contingency

Mitigation: Wrap all search result items and selected chips with explicit Semantics widgets providing role, label, and selected-state announcements. Test the selector with VoiceOver on iOS and TalkBack on Android during development, not only at QA time.

Contingency: If the multi-select typeahead cannot be made fully accessible within the sprint, provide an alternative flat scrollable list with checkboxes as a fallback mode, toggled by an accessibility-settings flag.

high impact medium prob security

The peer mentor selector must apply RLS chapter-scope filtering to show only mentors the coordinator is responsible for. If the Supabase query for the selector does not correctly join against the coordinator's chapter assignments, coordinators may see mentors from other chapters, violating data isolation.

Mitigation & Contingency

Mitigation: Implement the selector's data query using the same RLS-aware Supabase client used by the contact list feature, which already handles chapter-scope filtering. Write an integration test with a multi-chapter coordinator fixture asserting cross-chapter mentors are not returned.

Contingency: If a data isolation breach is discovered, immediately add a client-side chapter_id filter as a defence-in-depth measure and audit selector query logs for any unauthorised cross-chapter results.