import type { Location, Schedule } from 'fhir/r4'; import { todayIn } from './initialScheduleHorizon'; import { locationTimeZoneOf } from './practiceTimezone'; /** * The last day a practitioner's hours run to, when that falls within `days` of today. Each schedule is * measured against today where it runs, so `todayFor` answers per schedule. Undefined while any * schedule is open-ended, runs beyond the window, or has no time zone. */ const plusDays = (date: string, days: number): string => { const next = new Date(`${date}T00:00:00Z`); next.setUTCDate(next.getUTCDate() + days); return next.toISOString().slice(0, 10); }; export const hoursEndingSoon = ( schedules: Schedule[], practitionerId: string, todayFor: (schedule: Schedule) => string | null, days = 30, ): string | undefined => { const mine = schedules.filter((schedule) => schedule.active !== false && (schedule.actor ?? []).some((actor) => actor.reference === `Practitioner/${practitionerId}`)); const spans = mine.map((schedule) => ({ end: schedule.planningHorizon?.end?.slice(0, 10), today: todayFor(schedule) })); if (!spans.length || spans.some(({ end, today }) => !end || !today || end > plusDays(today as string, days))) return undefined; const running = spans.filter(({ end, today }) => (end as string) >= (today as string)).map(({ end }) => end as string); return running.sort()[running.length - 1]; }; /** Today where a schedule runs: its site's zone first, then the practice's, null when neither is set. */ export const todayWhereScheduleRuns = ( schedule: Schedule, locations: Location[], practiceZone: string | null, now = new Date(), ): string | null => { const siteIds = (schedule.actor ?? []).map((actor) => actor.reference ?? '').filter((ref) => ref.startsWith('Location/')).map((ref) => ref.slice(9)); const site = locations.find((location) => location.id && siteIds.includes(location.id) && locationTimeZoneOf(location)); const zone = locationTimeZoneOf(site) ?? practiceZone; return zone ? todayIn(zone, now) : null; };