import { FormEvent, useState } from 'react';
import { Building2, ChevronLeft, Search, UserRound } from 'lucide-react';
import { Button } from '../ui/button.tsx';
import { Input } from '../ui/input.tsx';
import { Badge } from '../ui/badge.tsx';
import { DismissibleAlert } from '../ui/dismissible-alert';
import { Skeleton } from '../ui/skeleton.tsx';
import {
useReferralDirectory,
type DirectoryLocation,
type DirectoryOrganization,
type DirectoryPractitioner,
type DirectoryResult,
} from '../../hooks/useReferralDirectory.tsx';
// THE REFERRAL DIRECTORY SEARCH -, design ruling Addendum 4.
//
// ORG FIRST, THEN THE PEOPLE AT THE LOCATION YOU PICKED. Not a stylistic ordering: measured against
// the live registry, the org-then-address direction returned 77 named cardiologists at one institution
// while the practitioner-first direction resolved 3 of 6 searches and was ambiguous when it did. And it
// is how a referring practitioner actually thinks - *"send them to cardiology at University of Iowa"*,
// not *"find me Dr Someone."*
//
// THE REFERRER MAY STOP AFTER STEP ONE. Addendum 5: a referral addressed to a department rather than a
// person is normal and plausibly the more common case - the receiving org triages internally, and the
// referrer usually cannot know who is available. So "Refer to this organization" is a first-class ending,
// never a fallback for a failed practitioner search.
export interface ReferralTarget {
organization: DirectoryOrganization;
location?: DirectoryLocation;
/** Absent = the referral is addressed to the organization. That is a complete answer. */
practitioner?: DirectoryPractitioner;
}
interface ReferralDirectorySearchProps {
onSelect: (target: ReferralTarget) => void;
/** Offered when the registry cannot answer, so a referral is never blocked on a third party. */
onEnterManually?: () => void;
/** Also offer manual entry when the registry answers with nothing, as a send-out laboratory often is not listed by its common name. */
manualOnNoMatch?: boolean;
/** What choosing an organization is called where it is not a referral. */
chooseLabel?: string;
}
const addressLine = (location: DirectoryLocation | undefined): string => {
if (!location) return 'No practice address listed';
return [
location.street,
location.city && `${location.city}, ${location.state} ${location.postalCode}`.trim(),
].filter(Boolean).join(' ยท ');
};
/**
* THE HONEST LIMIT, SAID ON THE SCREEN RATHER THAN IN A COMMENT. The registry is an IDENTIFIER
* registry, not a licensure one - an NPI is not revoked when a licence lapses. Somebody choosing where to
* send a patient must not read a listing as verification.
*/
const REGISTRY_CAVEAT =
'Listings come from the national provider registry. It confirms a provider is registered โ it does not confirm a licence is current.';
const Unavailable = ({ onEnterManually }: { onEnterManually?: () => void }) => (
{/* On a phone the name takes its own line: beside State and Search it had about 125 px. */}
{loading && !organizations && }
{organizations && !organizations.available && (
organizations.reason === 'unavailable'
?
: organizations.reason === 'refused'
?
: (
Enter at least two characters of the organization's name.
)
)}
{organizations?.available && organizations.results.length === 0 && (
No organizations matched that name. The registry lists the LEGAL name, which is often
not the common one โ try a shorter piece of it.
{atThisAddress.length > 0 && (
)}
{/* โ NEVER MERGED INTO THE SET ABOVE. These are practitioners in the same postal code โ often
the institution's other buildings, sometimes an unrelated practice. Offering them is
useful; presenting them as being at the address the referrer picked is a different claim. */}
{nearby.length > 0 && (
)}
);
/**
* "THERE IS MORE" AND "THERE IS MORE AND YOU CANNOT HAVE IT" ARE DIFFERENT SENTENCES. The registry
* stops paging at a fixed offset, so a wide search cannot be walked to its end - and a list that simply
* stops reads as a complete answer. When that happens the reader is told to narrow instead of being
* offered a button that would return nothing.
*/
const MoreResults = ({
result,
loading,
onShowMore,
}: {
result: { hasMore: boolean; pagingExhausted: boolean };
loading: boolean;
onShowMore: () => void;
}) => {
if (!result.hasMore) return null;
if (result.pagingExhausted) {
return (
There are more matches than the registry will list. Narrow the search โ add a state, a
city, or more of the name.
);
}
return (
);
};
/** Paging appends; a fresh search replaces. A failure never wipes results the reader is still reading. */
function merge(
previous: DirectoryResult | undefined,
next: DirectoryResult,
page: number,
): DirectoryResult {
if (!next.available) {
return page > 0 && previous ? previous : next;
}
if (page === 0 || !previous?.available) {
return next;
}
return { ...next, results: [...previous.results, ...next.results] };
}
export default ReferralDirectorySearch;