import type { Extension, PractitionerRole } from "fhir/r4"; // Appointment setup saved without publishing. It rides on the role so it survives a reload; it is not // availability - nothing books from it, and role extensions reach patients only through an allow-list. export const APPOINTMENT_SETUP_DRAFT_EXTENSION_URL = "https://akashichealthrecords.com/fhir/StructureDefinition/practitioner-role-appointment-setup-draft"; export type AppointmentSetupDraft = { serviceIds: string[]; locationId: string; days: string[]; start: string; end: string; }; export const EMPTY_APPOINTMENT_SETUP: AppointmentSetupDraft = { serviceIds: [], locationId: "", days: [], start: "", end: "" }; const idOf = (reference: string | undefined, type: string): string => reference?.startsWith(`${type}/`) ? reference.slice(type.length + 1) : ""; export const appointmentSetupDraftOf = (role?: PractitionerRole | null): AppointmentSetupDraft | null => { const draft = role?.extension?.find((extension) => extension.url === APPOINTMENT_SETUP_DRAFT_EXTENSION_URL); if (!draft) return null; const parts = (url: string) => (draft.extension ?? []).filter((part) => part.url === url); return { serviceIds: parts("service").map((part) => idOf(part.valueReference?.reference, "HealthcareService")).filter(Boolean), locationId: idOf(parts("location")[0]?.valueReference?.reference, "Location"), days: parts("day").map((part) => part.valueCode ?? "").filter(Boolean), start: parts("start")[0]?.valueString ?? "", end: parts("end")[0]?.valueString ?? "", }; }; // Replaces any earlier draft; null removes it once the schedule is published. export const withAppointmentSetupDraft = (role: PractitionerRole, draft: AppointmentSetupDraft | null): PractitionerRole => { const others = (role.extension ?? []).filter((extension) => extension.url !== APPOINTMENT_SETUP_DRAFT_EXTENSION_URL); if (!draft) return { ...role, extension: others }; const parts: Extension[] = [ ...draft.serviceIds.map((id) => ({ url: "service", valueReference: { reference: `HealthcareService/${id}` } })), ...(draft.locationId ? [{ url: "location", valueReference: { reference: `Location/${draft.locationId}` } }] : []), ...draft.days.map((day) => ({ url: "day", valueCode: day })), ...(draft.start ? [{ url: "start", valueString: draft.start }] : []), ...(draft.end ? [{ url: "end", valueString: draft.end }] : []), ]; return { ...role, extension: [...others, { url: APPOINTMENT_SETUP_DRAFT_EXTENSION_URL, extension: parts }] }; }; const keyOf = (setup: AppointmentSetupDraft): string => JSON.stringify([[...setup.serviceIds].sort(), setup.locationId, [...setup.days].sort(), setup.start, setup.end]); export const sameAppointmentSetup = (a: AppointmentSetupDraft, b: AppointmentSetupDraft): boolean => keyOf(a) === keyOf(b);