high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

MultiChapterAffiliationChip is a StatelessWidget accepting a required List<ChapterAffiliation> chapters and an optional bool isEditable (defaults to false for this task's scope)
In read-only mode (isEditable=false), each chapter is rendered as a non-tappable Chip widget displaying the chapter name
Chips are laid out using a Wrap widget with horizontal runSpacing and spacing matching design token gap values
Each Chip uses design token colours for background (surface) and label text (on-surface) — no hardcoded colours
When chapters is empty, a neutral placeholder Text widget is shown (e.g. 'No chapter affiliations') styled with the tertiary/muted text token
The entire chip group is wrapped in a Semantics widget with a label that lists all chapter names in a single readable string (e.g. 'Affiliated chapters: Oslo, Bergen, Trondheim')
The Semantics label for the empty state is 'No chapter affiliations'
Individual Chip widgets inside the group do not expose separate Semantics nodes — the group Semantics is the single announcement unit (use excludeSemantics: true on each Chip)
Widget renders without overflow at any List<ChapterAffiliation> length from 0 to 12
Widget renders correctly at text scale 1.0 and 2.0 without layout overflow
NHF maximum of 5 chapters per contact is a validation rule (contact-form-validator) — this widget must display up to 12 gracefully without enforcing any limit
Widget does not fetch data — it is a pure presentation component; data is passed via constructor

Technical Requirements

frameworks
Flutter StatelessWidget
Flutter Wrap layout widget
Flutter design token system
data models
ChapterAffiliation (id: String, name: String)
assignment-repository chapter affiliation list (from task-002)
performance requirements
Widget is const-constructible when chapters list is const
Wrap layout is efficient for up to 12 chips with no lazy loading needed
security requirements
Chapter names displayed in chips must be HTML/text escaped if rendered in any WebView context — for native Flutter Text widgets this is handled automatically
ui components
Wrap (layout container)
Chip (individual chapter badge, non-interactive)
Text (empty state placeholder)
Semantics (group accessibility wrapper)

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use Chip with onDeleted: null and onPressed: null to render a non-interactive display chip — Flutter's Chip widget is interactive by default, so null callbacks are required to suppress tap behaviour and the delete icon. Do not use InputChip or ActionChip as they imply interactivity. Construct the Semantics label by joining chapter names with a comma separator: 'Affiliated chapters: ${chapters.map((c) => c.name).join(', ')}'. Use MergeSemantics or a top-level Semantics with a computed label string and excludeSemantics: true on the Wrap children.

Design token alignment: use AppSpacing.chipHorizontalGap and AppSpacing.chipRunSpacing constants for the Wrap spacing values — do not hardcode 8.0. The empty state placeholder should use the same text style as other 'not set' placeholders in the contact detail screen for visual consistency. This task covers read-only mode only; the editable chip picker (add/remove chapters) is a separate task scoped out of this epic.

Testing Requirements

Widget tests: (1) pump with a list of 3 ChapterAffiliation items and verify 3 Chip widgets are rendered with correct names, (2) pump with an empty list and verify the placeholder text is shown and no Chip widgets are present, (3) verify the Semantics label contains all chapter names when 3 chapters are provided (e.g. 'Affiliated chapters: Oslo, Bergen, Trondheim'), (4) verify Semantics label for empty state is 'No chapter affiliations', (5) verify no individual Chip exposes its own Semantics node (use tester.getSemantics() and assert only one leaf Semantics node), (6) verify no RenderFlex overflow at 12 chips and textScaleFactor=2.0, (7) verify Chip onPressed/onDeleted are null (non-interactive). Golden test recommended for the 3-chip layout if project uses golden testing.

Epic Risks (3)
high impact medium prob security

Blindeforbundet's encryption key retrieval mechanism may not be finalised at implementation time, or session key availability via Supabase RLS may be inconsistent, causing decryption failures that expose masked placeholders to users and degrade the experience.

Mitigation & Contingency

Mitigation: Agree with Blindeforbundet on key storage and retrieval contract before implementation starts. Prototype key retrieval in a spike against the staging Supabase instance and validate the full decrypt/verify cycle with real test data before committing to the implementation.

Contingency: Implement a fallback that shows a 'field temporarily unavailable' state with a retry affordance. Log decryption failures server-side for audit. Escalate to Blindeforbundet stakeholders to unblock key management before the service tier epic begins.

medium impact medium prob technical

NHF contacts may belong to up to 5 chapters, each governed by separate RLS policies. A coordinator's chapter scope may not cover all affiliations, causing partial profile reads or silent data omissions that are difficult to detect in tests.

Mitigation & Contingency

Mitigation: Map all RLS policy combinations for multi-chapter contacts early. Write integration tests that create contacts with 5 affiliations and query them from coordinators with varying chapter scopes. Use Supabase's RLS test utilities to verify row visibility per role.

Contingency: Add an explicit 'affiliation partially visible' state in the repository response model so the UI can communicate scope limitations to the coordinator rather than silently showing incomplete data.

low impact medium prob scope

Organisation-specific validation rules (e.g., NHF chapter limit, Blindeforbundet encrypted field edit flow) may expand in scope during implementation as edge cases are discovered, causing the validator to grow beyond the planned complexity.

Mitigation & Contingency

Mitigation: Define the complete validation rule set with product and org stakeholders before coding begins. Document each rule with its source organisation and acceptance test. Use a rule registry pattern so new rules can be added without modifying core validator logic.

Contingency: Timebox validator enhancements to 2 hours per additional rule. Defer non-blocking rules to a follow-on maintenance task rather than blocking the epic delivery.