high priority low complexity infrastructure pending backend specialist Tier 0

Acceptance Criteria

ContactFormValidator class (or equivalent top-level functions) is implemented in a dedicated file under the infrastructure layer
validateName returns ValidationResult.invalid with org-label key 'validation.name.required' when input is null or empty/whitespace
validateName returns ValidationResult.valid when input has at least 1 non-whitespace character
validateEmail returns ValidationResult.invalid with key 'validation.email.invalid' for malformed emails (missing @, missing domain, spaces)
validateEmail returns ValidationResult.valid for well-formed addresses (user@example.com, user+tag@sub.domain.no)
validatePhoneNumber returns ValidationResult.invalid with key 'validation.phone.invalid' for non-Norwegian formats; accepts +47XXXXXXXX, 8-digit local, and 47XXXXXXXX
validateBirthDate returns ValidationResult.invalid with key 'validation.birthdate.tooYoung' or 'validation.birthdate.tooOld' for dates outside allowed range (e.g. 0–120 years ago)
validateChapterAffiliation returns ValidationResult.invalid with key 'validation.chapter.required' when the provided list is null or empty
ValidationResult is a sealed class or equivalent with valid and invalid variants; invalid carries a String errorLabelKey
All validators are pure functions with no side effects and no Flutter widget dependencies — usable in unit tests without widget binding

Technical Requirements

frameworks
Dart
Flutter
data models
ContactEditPayload
ChapterAffiliation
performance requirements
All validators execute synchronously — no async operations
Regex patterns are compiled once (const or static) to avoid recompilation on each keystroke
security requirements
Phone and email validators must not log or store input values
Validation logic must not make network calls

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Define a sealed class ValidationResult with two subclasses: ValidationSuccess and ValidationFailure(String errorLabelKey). Place the file at `lib/infrastructure/validators/contact_form_validator.dart`. Norwegian phone regex: `^(\+47|47)?[2-9]\d{7}$` — validate this against real Norwegian mobile and landline patterns. For email, use a standard RFC 5322 simplified regex rather than a package dependency to keep the module lightweight.

Birth date range should be configurable via constants (e.g. `kMinAge = 0`, `kMaxAge = 120`) to allow future adjustment. Error label keys must match exactly the keys defined in the org-labels system — coordinate with the labels file before finalising key names to avoid a mismatch at integration time.

Testing Requirements

Unit tests only (flutter_test, no widget binding needed). Cover: each validator's happy path (valid input returns ValidationResult.valid), each validator's failure path (invalid input returns ValidationResult.invalid with the correct errorLabelKey), boundary values for birth date (exactly at min/max age), phone number edge cases (8-digit, +47 prefix, no prefix, non-numeric characters). Aim for 100% branch coverage on the validator module. Tests must not depend on any Flutter widget infrastructure.

Component
Edit Contact Screen
ui medium
Epic Risks (3)
medium impact medium prob scope

The encrypted-field-display confirmation dialog adds interaction steps that may frustrate coordinators who access sensitive fields frequently, leading to requests to bypass the flow or skip read-receipt logging, which would violate Blindeforbundet's compliance requirements.

Mitigation & Contingency

Mitigation: Design the confirmation dialog to be as minimal as possible (one clear sentence, single confirm action) and ensure it does not reappear for the same field within a single screen session. Validate the UX with Blindeforbundet coordinators during the TestFlight pilot before finalising.

Contingency: If coordinators raise strong objections, escalate to Blindeforbundet's data protection officer to determine whether a lighter confirmation pattern (e.g., biometric confirmation instead of dialog) satisfies their compliance obligation.

medium impact medium prob technical

The activity-history-list infinite scroll requires paginated Supabase queries. Contacts with hundreds of activities (e.g., an HLF peer mentor with 380 annual registrations) could cause slow page loads or memory pressure on older devices if pagination boundaries are set too large.

Mitigation & Contingency

Mitigation: Use a page size of 20 records with cursor-based pagination. Implement list item recycling using Flutter's ListView.builder. Benchmark memory usage with 400+ item simulation on a low-end test device before TestFlight release.

Contingency: If performance degrades on older devices, reduce page size to 10 and add a time-window filter (last 30 days, last 6 months, all) so the default view loads a manageable record count for most coordinators.

low impact high prob scope

The cognitive load rule engine (from the Cognitive Accessibility feature) mandates no more than 7 fields per screen section. If a contact model has more than 7 editable fields, the edit-contact-screen layout must be split into sections, adding complexity not accounted for in the initial scope.

Mitigation & Contingency

Mitigation: Audit the full contact field list from all four organisations before implementation. Group fields into logical sections (personal info, contact details, affiliation) so no single section exceeds 7 fields. Use the cognitive-load-rule-engine component if it is already delivered by the Cognitive Accessibility feature.

Contingency: If the rule-engine component is not yet available, implement a simple manual section layout with accordion-style expansion for less-frequently edited fields to stay within the 7-field guideline without blocking delivery.