high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

Screen is reachable via GoRouter route `contact/:id/edit` and pre-populates all fields with the current contact data on mount
Screen is accessible only to users with `coordinator` or `admin` roles; `permission-checker` redirects read-only roles to the detail view with a `SnackBar` notification explaining lack of access
Multi-chapter-affiliation-chip is rendered for NHF contacts (up to 5 chapters); chapter selections are reflected in the form payload
All form fields are validated through `contact-form-validator` on submit; field-level inline errors appear beneath the offending input without dismissing the keyboard
The save button is disabled while a submission is in progress to prevent double-submit
On successful save, the screen pops back to `contact/:id` and displays a `SnackBar` confirmation ('Contact updated successfully')
On field-level error from `contact-edit-service`, the specific field is highlighted and focused automatically, and an inline error message is shown
On network/conflict error, a dismissible error banner is shown at the top of the screen without losing form state
The screen is fully navigable via keyboard (tab order matches visual top-to-bottom order) and meets WCAG 2.2 AA contrast and focus indicator requirements
Pressing the device back button or the header back arrow prompts an unsaved-changes confirmation dialog if the form is dirty

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
GoRouter
apis
contact-edit-service (updateContact)
permission-checker
contact-form-validator
data models
Contact
ContactUpdatePayload
ContactEditResult
ChapterAffiliation
performance requirements
Screen must render and populate all fields within 300ms of navigation (data is pre-loaded from detail screen state)
Form submission feedback (spinner on save button) must appear within one frame of tap
Screen must not jank on keyboard appearance — use `resizeToAvoidBottomInset: true` and a `SingleChildScrollView`
security requirements
Route guard via `permission-checker` must run before the screen widget tree is built — not inside `initState`
Form data must be sanitised (trimmed) client-side before passing to contact-edit-service
Coordinator cannot elevate a contact's role beyond their own permission level
ui components
AppTextField (reusable, with inline error prop)
AppButton (save, with loading state)
MultiChapterAffiliationChip
UnsavedChangesDialog
ErrorBanner (dismissible)
SnackBar confirmation

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use a `BLoC` or `AsyncNotifier` (Riverpod) to manage form submission state: `idle | submitting | success | error`. Drive the save button enabled/disabled state and loading indicator from this state. Implement dirty-state tracking by comparing the current form field values against the original contact snapshot captured on mount. For the unsaved-changes dialog, use a `PopScope` widget (Flutter 3.16+) with `onPopInvokedWithResult` to intercept back navigation.

GoRouter redirect guard should be implemented in the route's `redirect` callback, not inside the screen widget. Pass `autofocus: true` to the first field with an error after a failed submit to improve keyboard navigation flow.

Testing Requirements

Widget tests (flutter_test): (1) verify screen pre-populates fields from mock contact data; (2) submit with invalid data — verify inline field errors appear and form is not submitted; (3) simulate successful submit — verify `SnackBar` shown and navigator pops; (4) simulate network error — verify error banner appears and form state is preserved; (5) verify save button is disabled during in-progress state; (6) verify unsaved-changes dialog appears on back press when form is dirty; (7) verify read-only role is redirected. Accessibility: use `SemanticsHandle` to confirm all inputs have labels and error messages are announced. Target ≥85% widget test coverage.

Component
Contact Detail Screen
ui medium
Epic Risks (2)
low impact medium prob dependency

The Peer Mentor Profile tab on the contact detail screen depends on the peer-mentor-detail-screen-widget being delivered by the separate Peer Mentor Detail feature. If that feature is delayed, the navigation affordance will be present but lead to a stub screen, which may confuse coordinators in the TestFlight pilot.

Mitigation & Contingency

Mitigation: Implement the peer mentor tab with a feature flag guard. When the Peer Mentor Detail feature is incomplete, the flag disables the tab. Coordinate delivery timelines with the team responsible for Peer Mentor Detail to align TestFlight releases.

Contingency: If the Peer Mentor Detail feature is significantly delayed, ship the contact detail screen without the peer mentor tab in the first TestFlight build and add it as an incremental update once the dependent screen is ready.

medium impact medium prob technical

The contact detail screen must adapt its layout significantly based on organisation context: NHF shows affiliation chips, Blindeforbundet shows encrypted fields and assignment status, standard contacts show neither. Managing this conditional rendering without introducing bugs in each variant is complex and increases the risk of organisation-specific regressions.

Mitigation & Contingency

Mitigation: Define a ContactDetailViewModel that resolves all org-specific flags (showEncryptedFields, showAssignmentStatus, showMultiChapterChips) from the organisation config before the widget tree renders. Widget tests must cover all three organisation variants as separate test cases to catch regressions.

Contingency: If conditional rendering logic grows unwieldy, refactor into separate composable section widgets (ProfileHeaderSection, AffiliationSection, EncryptedFieldsSection) that are conditionally included by the parent screen, isolating org-specific logic to individual components.