import { useState } from "react"; import { AlertCircle, Loader2 } from "lucide-react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../../../components/ui/dialog"; import { Alert, AlertDescription } from "../../../components/ui/alert"; import { Button } from "../../../components/ui/button"; import { Input } from "../../../components/ui/input"; import { Label } from "../../../components/ui/label"; import { Textarea } from "../../../components/ui/textarea"; import { useReferralWorkbenchService } from "../../../services/ReferralWorkbenchService"; import { stillOpen, type ReferralRequestScope } from "../../../utils/referralRequestScope"; import { describeWriteFailure } from "../../../utils/fetchFailureMessage"; import "./referralDialog.css"; /** * - TYPE IN A REFERRAL THAT ARRIVED BY FAX OR PHONE. * * THE GAP THIS CLOSES IS THE WRONG WAY ROUND WITHOUT IT. Every arrival path built so far starts with * another Akashic practice minting a claim code, so **the referrals that most need tracking - the ones on * paper, from a practice that is not on the system - had no way into the queue at all.** * * Spencer, 2026-08-23: *"a way for referral coordinators to manually add a new referral into their * queue before it's been handled."* * * THERE IS NO PATIENT FIELD, AND THAT IS THE WHOLE DESIGN. Spencer's ruling puts the patient at the * point of **contact** - *"they would either try to find the patient in our system or if they don't exist * yet register the new patient"* - which happens when somebody rings them, not while a coordinator is * holding a fax. **A required field nobody can fill at the moment of typing is how a queue stays empty * and the paper tray survives.** * * ONE REQUIRED FIELD: WHO SENT IT. It is on the letterhead of the thing in their hand, and it is what * tells two rows apart in a queue where every row is a referral. */ export type LogInboundReferralDialogProps = { open: boolean; onOpenChange: (open: boolean) => void; /** Re-read the queue rather than patch a row in: the server minted the id and composed the notice. */ onLogged: () => void; /** The opening this dialog belongs to: its requests are admitted by it, and nothing is shown once it retires. */ scope?: ReferralRequestScope; }; export const LogInboundReferralDialog = ({ open, onOpenChange, onLogged, scope, }: LogInboundReferralDialogProps) => { const { logInboundReferral } = useReferralWorkbenchService(); const [from, setFrom] = useState(""); const [reason, setReason] = useState(""); const [note, setNote] = useState(""); const [saving, setSaving] = useState(false); const [error, setError] = useState(undefined); const reset = () => { setFrom(""); setReason(""); setNote(""); setError(undefined); }; const close = (next: boolean) => { if (!next) reset(); onOpenChange(next); }; const submit = async () => { setSaving(true); setError(undefined); try { await logInboundReferral({ from: from.trim(), ...(reason.trim() ? { reason: reason.trim() } : {}), ...(note.trim() ? { note: note.trim() } : {}), }, scope); if (!stillOpen(scope)) return; // Reported before closing: closing retires this opening, and its completion belongs to it. onLogged(); reset(); onOpenChange(false); } catch (failure: unknown) { // Never closes on failure. A coordinator who watched the dialog vanish would believe the // referral is tracked, and the piece of paper is usually already in the shredder pile. if (!stillOpen(scope)) return; setError(describeWriteFailure(failure, "That referral could not be added")); } finally { setSaving(false); } }; return ( Add a referral that came in Record referrals received by fax or phone. They join the referral queue for staff to work.
setFrom(event.target.value)} placeholder="The practice or clinic on the fax" maxLength={200} />
setReason(event.target.value)} placeholder="Optional" maxLength={2000} />