import type { RecordsAthenaReceipt, RecordsCategory, RecordsManifestSummary } from "../../../services/RecordsReleaseService.tsx"; // The server's coverage registry decides what a packet holds; these are its labels, and the categories a release offers. export const CATEGORY_LABEL: Record = { Condition: "Problems and diagnoses", AllergyIntolerance: "Allergies and intolerances", MedicationStatement: "Medications taken", MedicationRequest: "Prescriptions", Immunization: "Immunizations", Observation: "Vitals and results", DiagnosticReport: "Diagnostic reports", Procedure: "Procedures", Encounter: "Visits", ClinicalImpression: "Visit notes", DocumentReference: "Documents", CarePlan: "Care plans", ServiceRequest: "Orders", Communication: "Clinical messages", Consent: "Consent documents", }; export const OFFERED: RecordsCategory[] = [ "Condition", "AllergyIntolerance", "MedicationStatement", "MedicationRequest", "Immunization", "Observation", "DiagnosticReport", "Procedure", "Encounter", "ClinicalImpression", "DocumentReference", "CarePlan", ]; export const NOT_OFFERED_NOTE = "Orders, clinical messages and consent documents are not part of a release yet. A document's file goes in only once it has passed its malware check; any file left out is named with the reason."; export interface CategoryLine { category: RecordsCategory; label: string; included: number; withheld: number; unavailable: number; status: RecordsManifestSummary["categories"][number]["status"]; reason?: string; } /** One line per category the packet was built from, counting what it holds and what it leaves out. */ export const categoryLines = (manifest: RecordsManifestSummary): CategoryLine[] => manifest.categories.map((c) => { const items = manifest.items.filter((item) => item.resourceType === c.category); return { category: c.category, label: CATEGORY_LABEL[c.category] ?? c.category, included: items.filter((item) => item.disposition === "include").length, withheld: items.filter((item) => item.disposition === "withheld").length, unavailable: items.filter((item) => item.disposition === "unavailable").length, status: c.status, reason: c.reason, }; }); /** Why items were left out, each reason once with how many it covers. Shown to staff only, never in the packet. */ export const leftOutReasons = (manifest: RecordsManifestSummary): Array<{ reason: string; count: number }> => { const counts = new Map(); for (const item of manifest.items) { if (item.disposition === "include") continue; const reason = item.dispositionReason ?? "not decided"; counts.set(reason, (counts.get(reason) ?? 0) + 1); } return [...counts].map(([reason, count]) => ({ reason, count })).sort((a, b) => b.count - a.count); }; /** * What an athena attempt's own record says, never more: athena having it is not delivery, delivered needs its proof, * and an unconfirmed, failed or mismatched attempt is said in the server's words. Owed is not settled. */ export const athenaWords = (receipt: RecordsAthenaReceipt, owed?: string, said?: string): string => { if (owed) return `Not settled yet: ${owed}`; if (receipt.state === "intent") return "Sending to athena: not known yet. It is never sent twice by itself."; if (receipt.state === "submitted") { return receipt.proof === "content-unverified" ? "athena has a document from this send, but its content could not be checked. That is not delivery; attest it once the recipient confirms." : "athena has it; that is not delivery. Read it back to check what athena holds."; } if (receipt.state === "delivered") { if (receipt.proof === "exact") return "Delivered: athena holds exactly the approved packet."; if (receipt.proof === "staff-attested") return "Delivered: confirmed by staff with the recipient."; return "athena's record of this send does not say how it was delivered."; } return said ?? receipt.vendorSaid ?? receipt.refusal ?? `athena's answer for this send is ${receipt.state}. It is never sent twice by itself.`; };