` caller. Pinned by a test.
*/
error?: string | null
/** Whether the list is empty. Only consulted when the load actually succeeded. */
isEmpty: boolean
/**
* - WAS THE SERVER ACTUALLY ASKED?
*
* The header above says the empty state is *"reachable ONLY on a successful load, which is what makes
* it a true statement."* **That was assumed, not enforced**, and the callers do not hold it up.
*
* Every `useFhirX` hook opens `useState(false)` for `loading` and early-returns when `patientState`
* is unresolved - so `setLoading(true)` never runs and the effect never fires. The component is then
* handed `loading: false` - `error: undefined` - `isEmpty: true`, which is **byte-identical to a load
* that succeeded and returned nothing** - and a patient is told *"No procedures found."* about a
* question nobody asked.
*
* **This is own family arriving through the CALLER.** 150 made "a failure and an empty
* result must never render together" impossible from inside the component; it could not - from in
* here - make "no load at all" distinguishable from "loaded and empty". This prop is what supplies
* that fact, because only the caller knows it.
*
* **NOT a way to suppress the empty message.** `false` produces its OWN sentence; it does not
* silence the third state. A blank region is indistinguishable from a broken page, which is the
* defect this card's own lane forbids trading for.
*
* Optional, defaulting to `true`, so the nineteen call sites not yet wired keep exactly today's
* behaviour rather than silently flipping to the new state. The gap that leaves is NOT invisible:
* `__tests__/resourceListStatusNeverAsked.test.tsx` pins the outstanding call sites BY NAME and fails
* if the list grows, so a new surface cannot quietly join them.
*/
attempted?: boolean
/** Lowercase plural, as a clinician would say it: "conditions", "allergies", "procedures". */
resourceLabel: string
/** The load was answered with a 403, so the failure is named as a denial, not a lost connection. */
refused?: boolean
/**
* Shown ONLY on a successful load that genuinely returned nothing.
* OMIT when the caller renders its own empty state because that state carries actions rather
* than just a message (AllergyIntolerances offers "Add Allergy" / "No Known Allergies"). This
* component then owns the FAILURE only, and the caller must gate its own block on `!error`.
*/
emptyMessage?: string
/**
* The sentence under the headline, for a surface whose reader is NOT a clinician looking at
* somebody else's chart.
*
* MEASURED 2026-09-14 in a real two-party visit: a patient who joined before anything had been
* said was shown the default - "You may not have access to this patient's record... This is not
* a statement that the patient has none on file" - about themselves, in the third person. The
* words are right for the chart surfaces they were written for and wrong in a room the reader
* is standing in.
*
* OPTIONAL, and the default is the shared sentence, so nothing already using this moves. The
* point is that the words still live in ONE place per audience rather than being hand-copied at
* a call site, which is the drift this file's own header exists to prevent.
*/
failureDetail?: string
/** Omit to render a non-dismissible failure banner. */
onDismissError?: () => void
className?: string
}
/**
* What a list says when nobody asked the server about it.
*
* Exported for the same reason the failure sentence is: the words live here ONCE. A second surface
* hand-copying them is how duplicated rule drifted, and this file's own header says so.
*
* It states a fact about the SCREEN, never about the record. *"No procedures found"* is a claim about
* the patient; *"hasn't loaded"* is a claim about us, and only one of those can be made without asking.
*/
export const resourceNotAskedMessage = (resourceLabel: string) =>
`${resourceLabel.charAt(0).toUpperCase()}${resourceLabel.slice(1)} haven't loaded yet.`
export function ResourceListStatus({
loading,
error,
isEmpty,
resourceLabel,
failureDetail,
emptyMessage,
onDismissError,
attempted = true,
refused = false,
className,
}: ResourceListStatusProps) {
// A dismissed failure is still a failure: until the next load the list is unknown, never empty.
const [failedSinceLoad, setFailedSinceLoad] = React.useState(false)
React.useEffect(() => {
if (error) setFailedSinceLoad(true)
else if (loading) setFailedSinceLoad(false)
}, [error, loading])
// FAILED takes precedence over everything except loading, and deliberately renders even
// when rows are on screen: a refresh that failed over stale data must not pass as current.
if (error) {
return (
{refused ? resourceRefusedHeadline(resourceLabel) : resourceLoadFailureHeadline(resourceLabel)}
{" "}
{refused ? RESOURCE_REFUSED_DETAIL : failureDetail ?? RESOURCE_LOAD_FAILURE_DETAIL}
)
}
if (loading) {
return null
}
// NEVER ASKED and it sits HERE, immediately before the empty state, because that
// adjacency IS the fix: these two were one branch, and the whole defect was that "we did not ask" fell
// through into a sentence about the patient's record.
//
// A QUIET LINE, NOT AN ALERT, and that is a judgement rather than styling. The common cause is a
// patient id that has not resolved yet, so on most routes this is transient - nineteen alert banners
// flashing across a chart on every load would teach a clinician to ignore the one that is real. But it
// is NOT nothing: if the id never resolves the line persists and is diagnosable, where a blank region
// is indistinguishable from a broken page.
if (!attempted) {
return (
{resourceNotAskedMessage(resourceLabel)}
)
}
if (failedSinceLoad) {
return (
{resourceLoadFailureHeadline(resourceLabel)}
)
}
// EMPTY reachable ONLY on a successful load, which is what makes it a true statement.
// And that is now ENFORCED rather than assumed: `attempted` gates the branch above it.
if (isEmpty && emptyMessage) {
return (
{emptyMessage}
)
}
return null
}
export default ResourceListStatus