feat: AI chatbot preauth intent, UnitedDH pre-auth improvements

- Add preauth intent to AI chatbot (classifier, workflow, frontend UI card)
- Auto-prefill preauth form with CDT codes, service date, and mapped prices
- Auto-trigger preauth Selenium handler by insurance siteKey (MH/Tufts/CCA/UnitedDH)
- Default tentative service date to today+3 for preauth (user didn't pick a date)
- Fix #number always means tooth number, distributes to all procedures in comma list
- Fix bare "post"/"pos" → D2954 (was matching D2955 via keyword scorer)
- UnitedDH pre-auth: fill procedure date with Ctrl+A to overwrite prefilled value
- UnitedDH pre-auth: select Location "Summit Dental Care" in step1 (same as billing entity)
- UnitedDH pre-auth: remove page refresh in step9 to preserve pre-auth number
- UnitedDH pre-auth: wait for table rows before extracting pre-auth number
- UnitedDH pre-auth/claim: explicit wait for Submit button after file upload (no sleep)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ff
2026-06-17 01:21:51 -04:00
parent 43340ab39d
commit 8e011c5a29
8 changed files with 311 additions and 19 deletions

View File

@@ -30,7 +30,8 @@ type Step =
| "need-insurance-clarification"
| "need-appointment-selection"
| "need-cdt-clarification"
| "claim-ready";
| "claim-ready"
| "preauth-ready";
interface Message {
id: number;
@@ -158,6 +159,13 @@ export function ChatbotButton() {
appointmentId: number | null;
renderingProvider: string | null;
} | null>(null);
const [preauthReadyData, setPreauthReadyData] = useState<{
patient: PatientResult | null;
matchedCodes: { code: string; description: string; toothNumber?: string }[];
siteKey: string;
serviceDate: string;
renderingProvider: string | null;
} | null>(null);
const [pendingFiles, setPendingFiles] = useState<File[]>([]);
const [, setLocation] = useLocation();
const messagesEndRef = useRef<HTMLDivElement>(null);
@@ -211,6 +219,7 @@ export function ChatbotButton() {
setApptSelectionData(null);
setCdtClarificationData(null);
setClaimReadyData(null);
setPreauthReadyData(null);
setPendingFiles([]);
};
@@ -471,6 +480,19 @@ export function ChatbotButton() {
return;
}
if (data.action === "preauth_ready" && data.actionData) {
const { patient, matchedCodes, siteKey, serviceDate, renderingProvider } = data.actionData;
setPreauthReadyData({
patient: patient ?? null,
matchedCodes: matchedCodes ?? [],
siteKey,
serviceDate,
renderingProvider: renderingProvider ?? null,
});
setStep("preauth-ready");
return;
}
setStep("menu");
} catch {
replaceLastMsg("Sorry, something went wrong. Please try again.");
@@ -917,6 +939,56 @@ export function ChatbotButton() {
);
})()}
{/* PreAuth confirmation card */}
{step === "preauth-ready" && preauthReadyData && (() => {
const [sy, sm, sd] = (preauthReadyData.serviceDate ?? "").split("-");
const dateLabel = sy ? `${sm}/${sd}/${sy}` : preauthReadyData.serviceDate;
return (
<div className="bg-blue-50 border border-blue-200 rounded-xl p-3 space-y-2">
<p className="text-xs font-semibold text-blue-800">Confirm Pre-Authorization</p>
{preauthReadyData.patient && (
<p className="text-xs text-blue-700 font-medium">
{preauthReadyData.patient.firstName} {preauthReadyData.patient.lastName}
</p>
)}
<p className="text-xs text-gray-500">Tentative date: {dateLabel}</p>
{preauthReadyData.matchedCodes.length > 0 && (
<div className="space-y-0.5 pt-0.5">
{preauthReadyData.matchedCodes.map((c) => (
<p key={c.code} className="text-xs text-gray-700 pl-1">
<span className="font-medium">{c.code}</span>{c.toothNumber ? ` #${c.toothNumber}` : ""} {c.description}
</p>
))}
</div>
)}
<div className="flex gap-2 pt-1">
<Button
size="sm"
className="flex-1 h-8 text-xs bg-blue-600 hover:bg-blue-700 text-white"
onClick={() => {
const { patient, matchedCodes, siteKey, serviceDate, renderingProvider } = preauthReadyData;
addMsg("user", "Confirm & open pre-auth");
addMsg("bot", "Opening pre-auth form...");
if (patient?.id && matchedCodes.length > 0) {
sessionStorage.setItem(
"chatbot_preauth_prefill",
JSON.stringify({ codes: matchedCodes, siteKey, serviceDate, renderingProvider: renderingProvider ?? null })
);
}
setChatbotPendingFiles(pendingFiles);
const url = `/claims?newPatient=${patient?.id}&tab=preauth`;
setTimeout(() => { setLocation(url); setOpen(false); resetStep(); }, 600);
}}
>
<FileText className="h-3 w-3 mr-1" />
Confirm &amp; Open PreAuth
</Button>
<Button size="sm" variant="ghost" className="h-8 text-xs" onClick={resetStep}>Cancel</Button>
</div>
</div>
);
})()}
{/* CDT clarification — unknown procedure terms */}
{step === "need-cdt-clarification" && cdtClarificationData && (
<div className="bg-amber-50 border border-amber-200 rounded-xl p-3 space-y-2">