import type { Appointment } from "fhir/r4"; /** * How long a visit is, from what the appointment itself records: `minutesDuration`, else end minus start. * Undefined when it records neither, and never inferred from the service, which may have changed since. * A zero `minutesDuration` counts as unrecorded because one write path stores 0 for "not given". */ export const appointmentLengthMinutes = ( appointment: Pick, ): number | undefined => { const { minutesDuration, start, end } = appointment; if (typeof minutesDuration === "number" && minutesDuration > 0) return minutesDuration; if (!start || !end) return undefined; const minutes = Math.round((Date.parse(end) - Date.parse(start)) / 60_000); return Number.isFinite(minutes) && minutes > 0 ? minutes : undefined; }; const plural = (count: number, unit: string) => `${count} ${unit}${count === 1 ? "" : "s"}`; /** Spelled out as the deck writes it: "20 minutes", "1 hour", "1 hour 30 minutes". */ export const formatAppointmentLength = (minutes: number): string => { const hours = Math.floor(minutes / 60); const rest = minutes % 60; if (hours === 0) return plural(rest, "minute"); return rest === 0 ? plural(hours, "hour") : `${plural(hours, "hour")} ${plural(rest, "minute")}`; }; /** A time line with the visit's length after it, or the time alone when the length is unknown. */ export const withAppointmentLength = ( timeLabel: string, appointment: Pick, ): string => { const minutes = appointmentLengthMinutes(appointment); return minutes === undefined ? timeLabel : `${timeLabel} ยท ${formatAppointmentLength(minutes)}`; };