import { useEffect, useMemo, useState } from "react"; import type { Appointment, Location } from "fhir/r4"; import { useHttpService } from "../services/HttpService.tsx"; import { environment } from "../environments/environment.ts"; import { appointmentLocationId } from "../utils/appointmentDirections"; /** * The practice's loaded Locations plus each appointment's own site from another practice, read under * that practice's organization so a patient's booking elsewhere keeps its address and Directions. */ export const useAppointmentSites = ( appointments: readonly Appointment[], locations: readonly Location[], practiceIdOf: (appointment: Appointment) => string | undefined, ): Location[] => { const { get } = useHttpService(); const [fetched, setFetched] = useState>({}); const missing = useMemo(() => { const known = new Set(locations.map((location) => location.id)); const wanted = new Map(); for (const appointment of appointments) { const id = appointmentLocationId(appointment); const practiceId = practiceIdOf(appointment); if (id && practiceId && !known.has(id)) wanted.set(id, practiceId); } return [...wanted].sort(([a], [b]) => a.localeCompare(b)); }, [appointments, locations, practiceIdOf]); const missingKey = missing.map(([id, practiceId]) => `${id}@${practiceId}`).join(","); useEffect(() => { const toRead = missing.filter(([id]) => !fetched[id]); if (toRead.length === 0) return; let cancelled = false; void Promise.all(toRead.map(async ([id, practiceId]) => { try { return await get(`${environment.fhirBaseApi}/Location/${id}`, { organizationId: practiceId }); } catch { // Without the site the panel keeps the booking's own place name; nothing is claimed about it. return undefined; } })).then((results) => { if (cancelled) return; const found = results.filter((location): location is Location => Boolean(location?.id)); if (found.length) setFetched((previous) => ({ ...previous, ...Object.fromEntries(found.map((l) => [l.id!, l])) })); }); return () => { cancelled = true; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [missingKey, get]); return useMemo(() => [...locations, ...Object.values(fetched)], [locations, fetched]); }; export default useAppointmentSites;