import { useCallback } from 'react'; import { environment } from '../environments/environment.ts'; import { useHttpService } from './HttpService.tsx'; import { buildAuthorityChange, buildAuthorityRequest } from './relatedPersonAuthority'; import type { AuthorityTier, AuthorityGrant, AuthorityGrantsResponse, AuthorityIntent } from './relatedPersonAuthority'; import type { DesignationAttestationRequest } from './relatedPersonDesignation'; export type { AuthorityTier, AuthorityGrant, AuthorityGrantsResponse, AuthorityIntent } from './relatedPersonAuthority'; export { TIER_LABELS, TIER_DESCRIPTIONS, buildAuthorityRequest } from './relatedPersonAuthority'; // Thin client for the related-person authority-grant sub-route (F1 plan, Task 8). A patient grants a // related person (spouse/child/parent, etc. they've linked) one of three authority tiers, plus an // independent messaging toggle. Self-scoped server-side to the caller-patient (via the trusted token), so // no patient id is sent - only the target related person and the grant itself. // Mirrors CommunicationPreferenceService.tsx. export const useRelatedPersonAuthorityService = () => { const { put, get } = useHttpService(); // Returns void: the server PUT responds with the grant (no relatedPersonId), and callers read current // grants via getAuthorities()/getAuthorityForRelatedPerson() rather than the write response. //: the two billing flags are optional and default to FALSE, so the invite flow keeps granting no // financial access. The intent comes first and is required: a write without one is a decision the server // cannot apply once or refuse when stale. const putAuthority = useCallback( ( intent: AuthorityIntent, relatedPersonId: string, tier: AuthorityTier, allowMessaging: boolean, allowBillingView = false, allowBillingPay = false, attestation?: DesignationAttestationRequest, // The opening this request belongs to, when a caller has one. transport?: { admit: () => void; signal: AbortSignal; organizationId?: string }, ): Promise => put( `${environment.fhirIdentityApi}/related-person-authority`, buildAuthorityChange( buildAuthorityRequest( relatedPersonId, tier, allowMessaging, allowBillingView, allowBillingPay, attestation, ), intent, ), { ...transport }, ), [put], ); const getAuthorities = useCallback( (signal?: AbortSignal): Promise => get(`${environment.fhirIdentityApi}/related-person-authority`, { signal }), [get], ); // One known link, read by id on the server so a new link the searches have not caught up with still has its // stored decision; a server that predates that read answers with the whole list, read the same way. A link missing // from the answer is never read as untouched, nor as revoked: it comes back unavailable (no rights, nothing to // change from). Only a server that predates the list's `complete` returns nothing, as before. const getAuthorityForRelatedPerson = useCallback( async (relatedPersonId: string, signal?: AbortSignal): Promise => { const { grants, complete } = await get( `${environment.fhirIdentityApi}/related-person-authority?relatedPerson=${encodeURIComponent(relatedPersonId)}`, { signal }, ); const row = grants.find((g) => g.relatedPersonId === relatedPersonId); if (row || complete === undefined) return row; return { relatedPersonId, state: 'unavailable' } as AuthorityGrant; }, [get], ); return { putAuthority, getAuthorities, getAuthorityForRelatedPerson }; };