import { Extension } from "fhir/r4"; // Patient availability preference for a time-window appointment request (request-only + // `time-window`): preferred days of week + times of day, with no practitioner or slot. The server // passes this through untouched - only the plugin reads/writes it - so it is NOT mirrored server-side. export const APPOINTMENT_PREFERRED_AVAILABILITY_EXTENSION_URL = "https://akashichealthrecords.com/fhir/StructureDefinition/appointment-preferred-availability"; const DAY_SUB_URL = "day"; const TIME_OF_DAY_SUB_URL = "timeOfDay"; export const PREFERRED_DAY_OPTIONS: { code: string; label: string }[] = [ { code: "mon", label: "Mon" }, { code: "tue", label: "Tue" }, { code: "wed", label: "Wed" }, { code: "thu", label: "Thu" }, { code: "fri", label: "Fri" }, { code: "sat", label: "Sat" }, { code: "sun", label: "Sun" }, ]; export const PREFERRED_TIME_OF_DAY_OPTIONS: { code: string; label: string }[] = [ { code: "morning", label: "Morning" }, { code: "afternoon", label: "Afternoon" }, { code: "evening", label: "Evening" }, ]; // Builds the complex extension from the patient's selected day + time-of-day codes. export const buildPreferredAvailabilityExtension = ( dayCodes: string[], timeOfDayCodes: string[], ): Extension => ({ url: APPOINTMENT_PREFERRED_AVAILABILITY_EXTENSION_URL, extension: [ ...dayCodes.map((code) => ({ url: DAY_SUB_URL, valueCode: code })), ...timeOfDayCodes.map((code) => ({ url: TIME_OF_DAY_SUB_URL, valueCode: code })), ], }); const labelFor = ( options: { code: string; label: string }[], code?: string, ): string => options.find((o) => o.code === code)?.label ?? code ?? ""; const DAY_PLURALS: Record = { mon: "Mondays", tue: "Tuesdays", wed: "Wednesdays", thu: "Thursdays", fri: "Fridays", sat: "Saturdays", sun: "Sundays", }; const TIME_OF_DAY_PLURALS: Record = { morning: "mornings", afternoon: "afternoons", evening: "evenings", }; const sayList = (items: string[]): string => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`; // The patient's own choice as a sentence, e.g. "Mondays, Wednesdays and Fridays, mornings." export const describePreferredAvailability = (dayCodes: string[], timeOfDayCodes: string[]): string => { const inOrder = (codes: string[], options: { code: string }[]) => [...codes].sort((a, b) => options.findIndex((o) => o.code === a) - options.findIndex((o) => o.code === b)); const days = sayList(inOrder(dayCodes, PREFERRED_DAY_OPTIONS).map((code) => DAY_PLURALS[code] ?? code)); const times = sayList(inOrder(timeOfDayCodes, PREFERRED_TIME_OF_DAY_OPTIONS).map((code) => TIME_OF_DAY_PLURALS[code] ?? code)); return `${[days, times].filter(Boolean).join(", ")}.`; }; // Returns a readable summary like "Mon, Wed - Morning, Afternoon", or "" when the preference is absent. export const formatPreferredAvailability = (appointment?: { extension?: Extension[]; }): string => { const root = appointment?.extension?.find( (e) => e.url === APPOINTMENT_PREFERRED_AVAILABILITY_EXTENSION_URL, ); if (!root?.extension?.length) return ""; const days = root.extension .filter((e) => e.url === DAY_SUB_URL) .map((e) => labelFor(PREFERRED_DAY_OPTIONS, e.valueCode)); const times = root.extension .filter((e) => e.url === TIME_OF_DAY_SUB_URL) .map((e) => labelFor(PREFERRED_TIME_OF_DAY_OPTIONS, e.valueCode)); const parts: string[] = []; if (days.length) parts.push(days.join(", ")); if (times.length) parts.push(times.join(", ")); return parts.join(" ยท "); };