import { useState } from "react"; import { useSearchParams } from "react-router-dom"; import { useMyOrganizations } from "../../../hooks/useMyOrganizations.tsx"; import { useAuthServiceContext } from "../../../services/AuthService.tsx"; import { OrgListCard } from "./OrgListCard.tsx"; import { AppSpinner } from "../../../components"; import { EmptyState } from "../../../components/ui/empty-state.tsx"; import { Alert, AlertDescription, AlertTitle } from "../../../components/ui/alert.tsx"; import { Hospital } from "lucide-react"; /** * The query parameter that opens one practice. * * IT EXISTS SO THE ID SURVIVES A REDIRECT. `/dashboard/providers/{id}` used to be a page; it is now a * redirect to this list. Without a way to open a specific card from the URL, that redirect would * quietly degrade to "go to the list" - the same broken promise wearing a 200 - for anybody who * bookmarked their practice. */ export const OPEN_PRACTICE_PARAM = "open"; export const MyProvidersPage = () => { const { organizations, loading, error, retry, retryOrganization, retryingIds } = useMyOrganizations(); const { currentOrganization } = useAuthServiceContext(); const [searchParams] = useSearchParams(); /** * ONE OPEN AT A TIME, AND THE LIST OWNS IT rather than each card owning its own. * * The whole reason a practice opens in place is so it can be read against the others; several open * at once pushes the rest off the screen and gives back the problem the page shape was chosen to * avoid. * * An id that is not in this list simply opens nothing. That is the honest render for somebody * following an old bookmark to a practice that is no longer theirs: nothing is being DENIED - the * relationship ended - so a refusal naming what is denied would be answering a question nobody * asked. The list showing their practices IS the answer. */ const [openOrgId, setOpenOrgId] = useState( () => searchParams.get(OPEN_PRACTICE_PARAM), ); // The practice whose portal this is. Absent on akashic.health, where there is no entry organization at // all - so the empty state degrades to the same sentence without a name rather than to a blank. const entryOrgName = currentOrganization?.name; return (
{/* Card 248 / finding B18 — the subtitle was narrower than the feature. 🗣️ Spencer, 2026-08-12: *"'Providers' can be used for orgs and practitioners, so orgs and practitioners are a subset of a 'provider' umbrella."* The umbrella title was never the defect. The SUBTITLE was: "Organizations connected to your care" describes this page as organizations-only, and each row opens onto the people at that practice. That mismatch is what led a careful reviewer to record the patient's practitioner as MISSING from the feature when it is one click away — decent evidence a patient would read it the same way. */}

My practices

The practices involved in your care, and the people you can reach at each one

{loading ? (
) : error ? ( // A failed read is never shown as "No practices yet"; it says so and can be tried again. // The shared error notice; the retry sits in the description column with the sentence. We couldn’t load your practices.

Your practices are temporarily unavailable. Try again.

) : organizations.length === 0 ? ( /* THE OLD SENTENCE WAS TRUE AND READ AS THE PRODUCT CONTRADICTING ITSELF. "You aren't associated with any organizations yet" sits under a frame header naming the practice whose portal the patient is standing in - so a new patient is told they have no practices by the same screen that is displaying one, at the exact moment they are deciding whether this works. The defect was never the accuracy. It was that nothing said WHY, so the only available reading was that something is broken. The reason, traced rather than assumed: this list is `Patient.generalPractitioner` (the my-organizations route derives it from the caller's own record). The server writes that array only when an appointment is BOOKED **and** the practice holds an active authorization to see the record - both, not either. Signing in writes nothing; that policy moved off login deliberately. Both halves belong in the sentence, or a patient who has booked but not yet authorized reads it as still broken. Do NOT soften this to "no practices yet - book an appointment". Booking alone does not put a practice here, and a promise the server will not keep is worse than the bare sentence was. */ ) : (
{organizations.map((org) => ( { void retryOrganization(org.id as string); } : undefined} retrying={!!org.id && retryingIds.includes(org.id)} isOpen={!!org.id && org.id === openOrgId} onToggle={() => setOpenOrgId((current) => (current === org.id ? null : org.id ?? null))} /> ))}
)}
); }; export default MyProvidersPage;