import { useEffect, useState } from 'react'; import { ContactPoint, HumanName } from 'fhir/r4'; import { useRelatedPersonServiceContext } from '../services'; import type { ProofingStatusValue } from '../services/ProofingService.tsx'; export interface UseRelatedPersonIdentityResult { name?: HumanName[]; contact?: ContactPoint[]; // Phase 2 (full-proxy identity-proofing hard-gate), client Task 8 - additive: this related // person's own identity-proofing status, read from the same projection. `undefined` while // loading or on a degraded/failed fetch, same as name/contact. proofingStatus?: ProofingStatusValue; /** False until the caregiver first signs in as themselves; `undefined` when not known. */ joined?: boolean; loading: boolean; /** The read for this id failed; nothing here is known about them. */ failed: boolean; } // RelatedPerson re-foundation (Slice A, Phase 1, client Task 10): the RelatedPerson resource is // now a thin link with no demographics - name/contact must be read from the scoped server-side // projection (`GET /RelatedPerson/{id}/identity`), resolved from the helper's own home Patient. // Degrades gracefully on any failure (network error, 403, legacy RelatedPerson with no // home-patient link) rather than throwing - callers should treat `{ name: undefined, contact: // undefined }` as "unavailable" and fall back to something else they already have (e.g. the // relationship label), never render `NaN`/blank demographics. export const useRelatedPersonIdentity = (id: string | undefined | null): UseRelatedPersonIdentityResult => { const { fetchRelatedPersonIdentity } = useRelatedPersonServiceContext(); // Every answer is stamped with the id it was read for. Until the answer for THIS id arrives the // hook says it is loading, so switching from one person to another can never show the first // person's name or verification under the second, nor let a late first answer land on the second. const [answer, setAnswer] = useState(null); useEffect(() => { if (!id) return; let cancelled = false; fetchRelatedPersonIdentity(id) .then((identity) => { if (cancelled) return; setAnswer({ id, name: identity?.name, contact: identity?.contact, proofingStatus: identity?.proofingStatus, joined: identity?.joined, failed: false }); }) .catch((error: unknown) => { if (cancelled) return; console.error('Failed to load related person identity:', error); setAnswer({ id, failed: true }); }); return () => { cancelled = true; }; }, [id, fetchRelatedPersonIdentity]); if (!id) return { loading: false, failed: false }; if (!answer || answer.id !== id) return { loading: true, failed: false }; return { name: answer.name, contact: answer.contact, proofingStatus: answer.proofingStatus, joined: answer.joined, loading: false, failed: answer.failed }; }; interface IdentityAnswer { id: string; name?: HumanName[]; contact?: ContactPoint[]; proofingStatus?: ProofingStatusValue; joined?: boolean; failed: boolean; } export default useRelatedPersonIdentity;