Resolve encrypted field display rules from organisation config
epic-contact-detail-and-edit-service-layer-task-003 — Implement the logic inside ContactDetailService that reads the organisation configuration to determine which contact fields require encrypted display. Expose a typed list of encrypted field keys alongside the loaded contact state so that downstream UI components can render the correct widget variant without containing this business logic.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Fetch OrganisationConfig in parallel with the contact/activity/assignment fetches using Future.wait to avoid adding latency. If the config is already provided by a top-level Riverpod provider (likely, since org config is global), read it synchronously from the provider rather than making another network call. Define a type alias or small value class (e.g., EncryptedFieldKeys = List
Document the expected format of the org config field (e.g., encrypted_fields: ['ssn', 'phone_number']) in a comment near the parsing logic.
Testing Requirements
Unit tests using flutter_test: (1) Verify that when org config specifies ['ssn', 'diagnosis'], ContactDetailLoaded.encryptedFieldKeys equals ['ssn', 'diagnosis']. (2) Verify that when org config has no encrypted field rules, encryptedFieldKeys is an empty list. (3) Verify that a null or missing org config results in an empty encryptedFieldKeys list, not a thrown exception. (4) Verify the config is resolved without blocking the contact profile fetch (use fake_async to check timing).
(5) Verify the service correctly uses the org config from the injected config repository, not a hardcoded source.
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.
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.