import { Address, CareTeam, ContactPoint, Organization, Patient, Practitioner } from 'fhir/r4'; export const OUTSIDE_PROVIDERS_CATEGORY_SYSTEM = 'https://akashichealthrecords.com/fhir/CodeSystem/care-team-category'; export const OUTSIDE_PROVIDERS_CATEGORY_CODE = 'patient-reported-outside-providers'; export const OUTSIDE_PROVIDERS_TEAM_NAME = 'Outside Providers'; export type OutsideProviderKind = 'practitioner' | 'organization'; export interface OutsideProviderEntry { localId: string; kind: OutsideProviderKind; name: string; specialty?: string; phone?: string; city?: string; } export const isOutsideProvidersTeam = (careTeam: CareTeam): boolean => (careTeam.category ?? []).some(concept => (concept.coding ?? []).some(coding => coding.system === OUTSIDE_PROVIDERS_CATEGORY_SYSTEM && coding.code === OUTSIDE_PROVIDERS_CATEGORY_CODE ) ); export const findOutsideProvidersTeam = (careTeams: CareTeam[]): CareTeam | undefined => careTeams.find(isOutsideProvidersTeam); export const extractOutsideProviders = (careTeam: CareTeam | undefined): OutsideProviderEntry[] => { if (!careTeam) { return []; } return (careTeam.participant ?? []).flatMap((participant): OutsideProviderEntry[] => { const reference = participant.member?.reference; if (!reference || !reference.startsWith('#')) { return []; } const localId = reference.substring(1); const contained = (careTeam.contained ?? []).find(resource => resource.id === localId); if (!contained) { return []; } const specialty = participant.role?.[0]?.text; if (contained.resourceType === 'Practitioner') { const practitioner = contained as Practitioner; return [{ localId, kind: 'practitioner', name: practitioner.name?.[0]?.text ?? '', specialty, phone: practitioner.telecom?.find(t => t.system === 'phone')?.value, city: practitioner.address?.[0]?.city }]; } if (contained.resourceType === 'Organization') { const organization = contained as Organization; return [{ localId, kind: 'organization', name: organization.name ?? '', specialty, phone: organization.telecom?.find(t => t.system === 'phone')?.value, city: organization.address?.[0]?.city }]; } return []; }); }; const buildContained = (entry: OutsideProviderEntry): Practitioner | Organization => { const telecom: ContactPoint[] | undefined = entry.phone ? [{ system: 'phone', value: entry.phone }] : undefined; const address: Address[] | undefined = entry.city ? [{ city: entry.city }] : undefined; if (entry.kind === 'practitioner') { return { resourceType: 'Practitioner', id: entry.localId, name: [{ text: entry.name }], ...(telecom && { telecom }), ...(address && { address }) }; } return { resourceType: 'Organization', id: entry.localId, name: entry.name, ...(telecom && { telecom }), ...(address && { address }) }; }; export const buildOutsideProvidersTeam = ( patient: Patient, organization: Organization, entries: OutsideProviderEntry[], existingTeam?: CareTeam ): CareTeam => ({ ...(existingTeam ?? {}), resourceType: 'CareTeam', status: 'active', name: OUTSIDE_PROVIDERS_TEAM_NAME, subject: { reference: `Patient/${patient.id}`, type: 'Patient' }, // The API requires managingOrganization on create and scopes list searches // by it; the server preserves the existing value on update. managingOrganization: existingTeam?.managingOrganization ?? [{ reference: `Organization/${organization.id}`, type: 'Organization', ...(organization.name ? { display: organization.name } : {}) }], category: [{ coding: [{ system: OUTSIDE_PROVIDERS_CATEGORY_SYSTEM, code: OUTSIDE_PROVIDERS_CATEGORY_CODE }], text: 'Patient-reported outside providers' }], contained: entries.map(buildContained), participant: entries.map(entry => ({ ...(entry.specialty && { role: [{ text: entry.specialty }] }), member: { reference: `#${entry.localId}`, type: entry.kind === 'practitioner' ? 'Practitioner' : 'Organization', display: entry.name } })) }); export const nextLocalId = (entries: OutsideProviderEntry[]): string => { const max = entries.reduce((acc, entry) => { const n = Number(entry.localId.replace(/^op/, '')); return Number.isFinite(n) && n > acc ? n : acc; }, 0); return `op${max + 1}`; };