import type { CodeableConcept, Resource } from 'fhir/r4'; import type { ProposalKind } from './recordProposals'; /** One item of one kind, however it was written: the key a decision and a filed resource share. */ export const proposalKey = (kind: string, title: string): string => kind + ':' + title.trim().toLowerCase(); type Filed = Resource & { encounter?: { reference?: string } }; // The concept each kind is named by. Diet and vision have no single name to match, so none is claimed. const CONCEPT: Partial) => unknown>> = { condition: (r) => r.code, allergy: (r) => r.code, medication: (r) => r.medicationCodeableConcept, immunization: (r) => r.vaccineCode, observation: (r) => r.code, procedure: (r) => r.code, risk: (r) => r.code, }; const namesOf = (concept: CodeableConcept | undefined): string[] => [concept?.text, ...(concept?.coding ?? []).map((coding) => coding.display)] .filter((name): name is string => Boolean(name && name.trim())); /** * Whether a proposal is among the filed keys. The coded name a clinician picks usually extends what the note * said ("loratadine 10 MG Oral Tablet" for "Loratadine 10 mg"), so a filed name that begins with the * proposal's whole title, then a word, counts too. */ export const isFiledAs = (filed: Set, key: string): boolean => filed.has(key) || [...filed].some((name) => name.startsWith(key + ' ')); /** The keys of what this kind's resources show as filed to this encounter. */ export const filedKeysFor = (kind: ProposalKind, resources: Filed[], encounterId: string): string[] => { const conceptOf = CONCEPT[kind]; if (!conceptOf) return []; return resources .filter((resource) => resource.encounter?.reference === 'Encounter/' + encounterId) .flatMap((resource) => namesOf(conceptOf(resource as unknown as Record) as CodeableConcept | undefined)) .map((name) => proposalKey(kind, name)); };