high priority low complexity backend pending backend specialist Tier 2

Acceptance Criteria

All known Supabase PostgREST error codes (23505 duplicate, 23503 foreign key, 42501 RLS violation, PGRST116 not found) are mapped to Norwegian user-readable strings
Validation error codes emitted by the repository layer (required_field_missing, invalid_phone_format, invalid_email_format) each map to a distinct Norwegian message
ContactEditError state carries a non-null, non-empty userMessage string in Norwegian (Bokmål)
No raw error codes, stack traces, or English technical strings are ever included in ContactEditError.userMessage
Unmapped/unexpected error codes fall through to a generic Norwegian fallback message ('Noe gikk galt. Prøv igjen.')
The error mapping logic lives in a dedicated private method or class inside the service layer — zero error-message strings in any widget
ContactEditError is emitted rather than thrown so the BLoC/Riverpod stream does not close on error
Unit tests cover each mapped error code and the generic fallback path

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase PostgREST error response (code, message, details, hint)
data models
ContactEditState
ContactEditError
ContactEditSuccess
performance requirements
Error mapping must be synchronous (no async I/O) — sub-millisecond latency
No memory allocation beyond the message string itself
security requirements
Never expose raw database error details, table names, or column names in userMessage
Log full technical error details only to a secure, verbose-only internal logger — never to the UI

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Create a private _mapErrorToUserMessage(Object error) method inside ContactEditService (or a co-located ContactEditErrorMapper class if reuse is anticipated). Use a switch/if-else chain on the error type and code property. For Supabase errors, cast to PostgrestException and switch on .code. For domain validation errors, switch on a custom ErrorCode enum defined in the repository layer.

Store all Norwegian strings as constants at the top of the mapper — never inline in switch branches — to make future translation or copy-edit easy. Ensure the BLoC/Riverpod notifier catches the error in a try/catch block, calls _mapErrorToUserMessage, and emits ContactEditError(userMessage: msg) instead of rethrowing. The state stream must remain open after emitting an error state so the user can retry.

Testing Requirements

Write pure unit tests in flutter_test covering: (1) each explicitly mapped Supabase error code produces the correct Norwegian string, (2) each validation error code maps correctly, (3) an unknown error code produces the generic fallback string, (4) ContactEditError.userMessage is never null or empty for any input. No widget or integration tests required for this task. Aim for 100% branch coverage of the mapping method.

Component
Contact Edit Service
service medium
Epic Risks (2)
medium impact medium prob technical

Parallel fetching of profile, activity history, and assignment status from contact-detail-service may produce race conditions where partial state is emitted to the UI before all fetches complete, resulting in flickering or incorrect loading indicators.

Mitigation & Contingency

Mitigation: Use Future.wait or a single composed BLoC event that only emits a loaded state once all three futures resolve. Define a strict state machine: initial → loading → loaded/error with no intermediate partial-loaded states emitted to the UI.

Contingency: If parallelism proves unreliable in testing, fall back to sequential fetching with a combined loading indicator. The 500ms target may need to be renegotiated with stakeholders if sequential fetching exceeds it on slow connections.

high impact low prob integration

The partial-field update pattern in contact-edit-service assumes the contact record has not changed between when the edit screen was loaded and when the save is submitted. Concurrent edits by another coordinator could cause the earlier editor's save to silently overwrite the later one.

Mitigation & Contingency

Mitigation: Include an updated_at timestamp in the PATCH request and configure Supabase to reject updates where the server-side timestamp differs from the client's version. Return a 409-equivalent error that the service maps to a user-readable conflict message.

Contingency: If optimistic locking is too complex for initial delivery, implement a simple 'reload and retry' flow: on save error, reload the contact detail and prompt the coordinator to re-apply their changes manually.