import { useRef, useState } from "react"; import type { Location, Organization } from "fhir/r4"; import { useAuthServiceContext, useOrganizationServiceContext } from "../../services"; import { StaleOrganizationError } from "../../services/OrganizationService"; import { useFhirLocations } from "../../hooks"; import { useRefreshSessionOrganization } from "../../hooks/useRefreshSessionOrganization"; import { onlySites } from "../../utils/roomLocation"; import { mayMissRecentWrite } from "../../utils/recentWritesUnresolved"; import { validLocationTimeZoneOf } from "../../utils/practiceTimezone"; import { findPrimaryLocation } from "../../utils/practiceSetupSteps"; import { formatSingleAddress, normalizeFhirAddress } from "../../utils/fhirAddress"; import { describeWriteFailure } from "../../utils/fetchFailureMessage"; import { isSaveRetired, outcomeUnknown, useSaveScope, type SaveOperation } from "../../utils/saveOperation"; import { isEligibleOffice } from "../../utils/receivingOfficeHold"; import { RadioControl } from "../ui/radio-control"; import { DismissibleAlert } from "../ui/dismissible-alert"; import { OverlayActions } from "../ui/overlay-actions"; import AddLocationForm from "../../pages/DashboardPage/OrganizationPage/LocationsPage/AddLocationForm"; import LocationTimeZoneRepair from "../../pages/DashboardPage/OrganizationPage/LocationsPage/LocationTimeZoneRepair"; const CHANGED_ELSEWHERE = "Someone else changed the practice's primary location after you chose this office, so it was not made primary. Look at the offices, then choose again."; const NEEDS_ADMINISTRATOR = "An administrator needs to add your practice's office before staff can be invited."; type ConfirmPracticeOfficeProps = { // Organization management may add the office and make it primary; anyone else only confirms an existing one. canManage: boolean; timezoneHelp: string; onConfirmed: (office: Location) => void; onDirtyChange?: (dirty: boolean) => void; }; const officeLine = (office: Location) => [office.name || "Unnamed office", office.address && formatSingleAddress(office.address), validLocationTimeZoneOf(office)] .filter(Boolean) .join(" · "); // Which office a receiving practice works from, confirmed by a person: one complete office is shown to confirm, // several are chosen between, none is added once. Nothing is chosen or created from a list that may be missing a // write; that list is only read again. const ConfirmPracticeOffice = ({ canManage, timezoneHelp, onConfirmed, onDirtyChange }: ConfirmPracticeOfficeProps) => { const { wpOrganization, setWpOrganization } = useAuthServiceContext(); const { updateOrganization } = useOrganizationServiceContext(); const { loading, error: readError, loadLocations, locationsBundle, locationsList } = useFhirLocations(); const refreshOrganization = useRefreshSessionOrganization(); const practiceId = wpOrganization.current?.id; const [picked, setPicked] = useState(); const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(); const [primaryUnconfirmed, setPrimaryUnconfirmed] = useState(false); // The practice version the "make primary" decision was taken against, captured once per chosen office. A check // after an unanswered write finishes that decision only while the practice is still at that version. const basis = useRef<{ officeId: string; orgVersionId: string } | undefined>(undefined); const saveScope = useSaveScope(practiceId ?? "no-practice", () => { setSaving(false); setPicked(undefined); setSaveError(undefined); setPrimaryUnconfirmed(false); basis.current = undefined; }); const complete = Boolean(locationsBundle) && !mayMissRecentWrite(locationsBundle) && !readError && !loading; const ownSites = onlySites(locationsList).filter((site) => isEligibleOffice(site, practiceId)); const ready = ownSites.filter((site) => normalizeFhirAddress(site.address) && validLocationTimeZoneOf(site)); const chosen = ready.length === 1 ? ready[0] : ready.find((site) => site.id === picked); const hasPrimary = Boolean(findPrimaryLocation(wpOrganization.current, ownSites)); // Made primary only against the practice version first read for this office: already primary is done, the same // version takes the write conditioned on it, and anything newer changed elsewhere and is left alone. const promote = async (office: Location, operation: SaveOperation) => { const current: Organization | undefined = await operation.step(() => refreshOrganization(operation.transport)); if (!current) throw new Error("The practice could not be reloaded. Reload the page and try again."); if (normalizeFhirAddress(current.address?.[0]) === normalizeFhirAddress(office.address)) { setWpOrganization(current); return; } const taken = basis.current && basis.current.officeId === office.id ? basis.current : { officeId: office.id ?? "", orgVersionId: current.meta?.versionId ?? "" }; basis.current = taken; if (current.meta?.versionId !== taken.orgVersionId) throw new Error(CHANGED_ELSEWHERE); const saved = await operation.step(() => updateOrganization({ ...current, address: [office.address!, ...(current.address ?? []).slice(1)], }, operation.transport), true); setWpOrganization(saved); }; const confirm = async () => { const office = chosen; if (!office || saving) return; const operation = saveScope.start(); setSaving(true); setSaveError(undefined); try { if (canManage && !hasPrimary) await promote(office, operation); operation.admit(); basis.current = undefined; setPrimaryUnconfirmed(false); onConfirmed(office); } catch (err) { if (isSaveRetired(err)) return; if (outcomeUnknown(err)) { setPrimaryUnconfirmed(true); setSaveError(`Whether ${office.name || "this office"} became your primary location could not be confirmed. Check again.`); return; } setPrimaryUnconfirmed(false); const changed = err instanceof StaleOrganizationError || (err instanceof Error && err.message === CHANGED_ELSEWHERE); if (changed) basis.current = undefined; setSaveError(changed ? CHANGED_ELSEWHERE : describeWriteFailure(err, `${office.name || "This office"} could not be made your primary location`)); } finally { if (operation.belongs()) setSaving(false); } }; if (!complete) { const checking = loading || (!locationsBundle && !readError); return (

{checking ? "Checking your practice's offices…" : readError ?? "Your practice's offices may not all be showing yet. Check again before choosing one."}

{!checking && ( )}
); } if (ownSites.length === 0) { if (!canManage) return

{NEEDS_ADMINISTRATOR}

; return ( ); } if (ready.length === 0) { // An office missing its address or time zone is finished here, never filled in for the person. const unzoned = ownSites.find((site) => normalizeFhirAddress(site.address) && !validLocationTimeZoneOf(site)); if (canManage && unzoned) return undefined} />; return (

{canManage ? "Your practice's office needs its address. Add it in Locations, then come back." : "An administrator needs to finish your practice's office before staff can be invited."}

); } const checkAgain = primaryUnconfirmed; return (
{ready.length === 1 ? (

{wpOrganization.current?.name || "Your practice"} {" works from "} {officeLine(ready[0])}

) : ( ({ label: officeLine(site), value: site.id ?? "" }))} onChange={(value) => { setPicked(value); setSaveError(undefined); }} /> )} {saveError && setSaveError(undefined)}>{saveError}}
); }; export default ConfirmPracticeOffice;