import { useCallback, useEffect, useState } from "react"; import { PersonBadge } from "../../../../components/ui/person-badge.tsx"; import { usePractitionerServiceContext, type PractitionerWithRoles } from "../../../../services/PractitionerService.tsx"; import { useFhirServiceContext } from "../../../../services/FhirService.tsx"; import { formatFhirHumanName } from "../../../../utils/fhirHumanName.tsx"; import { getFhirPhotoUrl } from "../../../../utils/fhirPhoto.ts"; import { OrgSection } from "./OrgSection.tsx"; import { AppSpinner } from "../../../../components"; import { isPatientVisibleProvider } from "../../../../utils/patientVisibleProviders.ts"; import { sectionReadFailure, type SectionReadFailure } from "../../../../utils/sectionReadRefusal"; import { SectionReadNotice } from "./SectionReadNotice.tsx"; const roleText = (p: PractitionerWithRoles): string => p.roles?.[0]?.specialty?.[0]?.text || p.roles?.[0]?.code?.[0]?.text || "Provider"; export const OrgStaffSection = ({ orgId, organizationName }: { orgId: string; organizationName?: string }) => { const { fetchPractitioners } = usePractitionerServiceContext(); const { unpackBundle } = useFhirServiceContext(); const [staff, setStaff] = useState([]); // THE READ CAN END THREE WAYS AND ONLY ONE OF THEM IS "THERE IS NOBODY". This used to catch the // error, discard it and fall through to the empty sentence below, so a practice with four // practitioners that the server refused to describe read as a practice with no providers. Measured on // a patient's page for a practice they cannot be shown: the Practitioner read answered 403. const [failure, setFailure] = useState(null); const [loading, setLoading] = useState(true); const practice = organizationName || "This practice"; const load = useCallback(() => { let cancelled = false; setLoading(true); setFailure(null); fetchPractitioners(orgId) // Filtered HERE rather than in the render, so `staff.length === 0` means "nobody to show this // patient" and the empty state below tells the truth. Filtering at the map would leave a card that // says "Providers" over nothing at all whenever a practice is all back-office. .then(b => { if (!cancelled) { setStaff(unpackBundle(b).filter(p => isPatientVisibleProvider(p.roles))); } }) .catch(readError => { if (!cancelled) setFailure(sectionReadFailure(readError)); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [orgId, fetchPractitioners, unpackBundle]); useEffect(() => load(), [load]); return ( {loading ? (
) : failure ? ( { load(); }} /> ) : staff.length === 0 ? (

No providers listed.

) : staff.map(p => { // The deck's person row: a 26px face or initials (never the stock silhouette), name over role. const name = formatFhirHumanName(p.practitioner.name) || "Provider"; return (
{p.practitioner.photo?.[0] ? ( ) : } {name} {roleText(p)}
); })}
); };