critical priority medium complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

DuplicateComparisonPanel accepts exactly two named parameters: activityA (ActivityRecord) and activityB (ActivityRecord) with no optional service or repository dependencies
Layout renders two equally-sized columns side by side on screens >= 360dp wide
On screens < 360dp, columns stack vertically (fallback for small accessibility viewport sizes)
Each column displays: contact name, date (formatted locale-aware), activity type label, duration (minutes), notes excerpt (max 3 lines with ellipsis overflow), and registration source
A visible column header labels each side (e.g., 'Activity 1' / 'Activity 2') to orient users
All text uses design token typography (AppTypography) — no hardcoded font sizes or weights
All spacing uses design token spacing constants — no hardcoded pixel values
Widget is stateless (StatelessWidget or const-constructible)
Widget can be embedded in a BottomSheet and in a full-screen detail view without layout overflow
Golden test matches approved screenshot for two sample ActivityRecord objects

Technical Requirements

frameworks
Flutter
Design token system (AppTypography, AppSpacing, AppColors)
data models
ActivityRecord
performance requirements
Widget must build in < 16ms (single frame) — no async operations inside build()
Use const constructors wherever possible to enable Flutter widget tree caching
security requirements
Display contact names using initials or masked format if the ActivityRecord is flagged as sensitive — check ActivityRecord.isSensitive flag
Notes excerpt must not leak PII beyond the 3-line visible truncation
ui components
DuplicateComparisonPanel (StatelessWidget)
ActivityColumnCard (inner widget per column)
FieldRow (label + value row used inside each column)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Structure the widget as `DuplicateComparisonPanel` containing a `Row` of two `Expanded` `ActivityColumnCard` widgets with a thin `VerticalDivider` between them. `ActivityColumnCard` is a private `StatelessWidget` taking a single `ActivityRecord` and rendering `FieldRow` entries. `FieldRow` is a `Row` with a `Text` label (AppTypography.labelSmall, muted color) and a `Text` value (AppTypography.bodyMedium). Use `LayoutBuilder` to detect narrow viewports and switch to a `Column` layout.

Format dates with `intl` package's `DateFormat.yMMMd()` — use the device locale. Duration: display as 'X min' string. Notes excerpt: use `Text(..., maxLines: 3, overflow: TextOverflow.ellipsis)`. Pass `semanticsLabel` to `FieldRow` values — this is required for task-010's accessibility work.

Keep the widget free of BLoC/Riverpod `ref` usage — all data comes from the two `ActivityRecord` parameters.

Testing Requirements

Widget tests (flutter_test): render the panel with two ActivityRecord fixtures and verify all 6 fields appear in each column. Test vertical stacking below 360dp width using `tester.binding.window.physicalSizeTestValue`. Test overflow safety — notes longer than 3 lines must not cause RenderFlex overflow. Test with null/empty notes field (graceful empty state).

Golden test: generate and commit a golden image for the canonical two-column layout. Accessibility test: verify all text nodes are reachable by Semantics finder. Test const-constructibility.

Component
Duplicate Comparison Panel
ui medium
Epic Risks (2)
medium impact medium prob technical

If the duplicate check RPC fails due to a network error or Supabase outage, the service must decide whether to block submission entirely (safe but disruptive) or allow submission to proceed silently (functional but risks data duplication). An incorrect choice leads to either user frustration or data quality issues.

Mitigation & Contingency

Mitigation: Define an explicit error policy in the service: RPC failures result in a DuplicateCheckResult with status: 'check_failed' and no candidates. The caller treats this as 'allow submission, flag for async review'. Document this as the intended graceful degradation behaviour in the service interface contract.

Contingency: If stakeholders require blocking on RPC failure, expose a configurable `failMode` parameter in the service that can be toggled per organisation via the feature flag system without a code deployment.

medium impact medium prob scope

The DuplicateComparisonPanel must handle varying activity schemas across organisations (NHF, HLF, Blindeforbundet each have different activity fields). A rigid layout may not accommodate all field variations, causing truncation or missing data in the comparison view.

Mitigation & Contingency

Mitigation: Design the panel to render a dynamic list of key-value pairs rather than a fixed-column layout. Define a `ComparisonField` model that each service populates with only the fields relevant to the activity type and organisation, allowing the panel to adapt without schema knowledge.

Contingency: If dynamic rendering proves too complex within the timeline, ship a simplified panel showing only the five most critical fields (peer mentor, activity type, date, chapter, submitter) and log a follow-up ticket for full field rendering in a later sprint.