feat: add multi_claim intent for different procedures per patient + fix batch claim PDF race

- Add multi_claim intent so AI correctly handles "claim X for patient A and Y for patient B"
  instead of applying all procedures to all patients (batch_claim)
- Each patient carries their own matchedCodes in the queue
- Fix batch claim PDF race condition: chatbot queue no longer advances in closeClaim(),
  instead advances after selenium PDF is downloaded (matching column claim behavior)
- Align United SCO eligibility worker with claim worker: only fill subscriber ID + DOB,
  use treatmentLocation by ID instead of arrow-wrapper click

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 01:08:41 -04:00
co-authored by Claude Sonnet 4.6
parent cb49298b66
commit cdda91f2b4
5 changed files with 324 additions and 96 deletions
@@ -33,6 +33,7 @@ type Step =
| "need-cdt-clarification"
| "claim-ready"
| "batch-claim-ready"
| "multi-claim-ready"
| "batch-check-and-claim-ready"
| "preauth-ready";
@@ -589,6 +590,16 @@ export function ChatbotButton() {
return;
}
if (data.action === "multi_claim_ready" && data.actionData) {
setBatchClaimData({
queue: data.actionData.queue ?? [],
matchedCodes: [],
renderingProvider: data.actionData.renderingProvider ?? null,
});
setStep("multi-claim-ready");
return;
}
if (data.action === "claim_only_ready" && data.actionData) {
const { patient, matchedCodes, siteKey, serviceDate, appointmentId, renderingProvider } = data.actionData;
setClaimReadyData({
@@ -638,6 +649,7 @@ export function ChatbotButton() {
step === "batch-eligibility-ready" ||
step === "check-and-claim-ready" ||
step === "batch-claim-ready" ||
step === "multi-claim-ready" ||
step === "batch-check-and-claim-ready" ||
step === "need-insurance-clarification" ||
step === "need-appointment-selection";
@@ -1193,6 +1205,72 @@ export function ChatbotButton() {
</div>
)}
{/* Multi claim ready — different procedures per patient */}
{step === "multi-claim-ready" && batchClaimData && (
<div className="bg-orange-50 border border-orange-200 rounded-xl p-3 space-y-2">
<p className="text-xs font-semibold text-orange-800">
Claim for {batchClaimData.queue.length} patients:
</p>
{batchClaimData.queue.map((item: any, i: number) => (
<div key={i} className="pl-2 space-y-0.5">
<p className="text-xs text-orange-600 font-medium">
{i + 1}. {item.patient.firstName} {item.patient.lastName}
</p>
{item.matchedCodes?.length > 0 && (
<div className="pl-3">
{item.matchedCodes.map((c: any) => (
<p key={c.code} className="text-xs text-gray-700">
<span className="font-medium">{c.code}</span> — {c.description}
</p>
))}
</div>
)}
</div>
))}
<div className="flex flex-col gap-2 pt-1">
<Button
size="sm"
className="w-full h-8 text-xs bg-orange-600 hover:bg-orange-700 text-white"
onClick={() => {
const { queue, renderingProvider } = batchClaimData;
addMsg("user", `Claim all ${queue.length} patients`);
addMsg("bot", `Claiming ${queue.length} patients one by one...`);
const [first, ...rest] = queue as any[];
if (rest.length > 0) {
sessionStorage.setItem("chatbot_claim_queue", JSON.stringify({
remaining: rest,
matchedCodes: null,
renderingProvider,
}));
}
if (first!.patient.id && first!.matchedCodes?.length > 0) {
sessionStorage.setItem("chatbot_claim_prefill", JSON.stringify({
codes: first!.matchedCodes,
siteKey: first!.siteKey,
serviceDate: first!.serviceDate,
autoSubmit: true,
renderingProvider: renderingProvider ?? null,
dob: first!.patient.dateOfBirth ?? null,
}));
}
setChatbotPendingFiles(pendingFiles);
markJobStarted();
const url = first!.appointmentId
? `/claims?appointmentId=${first!.appointmentId}`
: `/claims?newPatient=${first!.patient.id}`;
setTimeout(() => { setLocation(url); setOpen(false); resetStep(); }, 600);
}}
>
<FileText className="h-3 w-3 mr-1" />
Claim All ({batchClaimData.queue.length} patients)
</Button>
<Button size="sm" variant="ghost" className="w-full h-8 text-xs" onClick={resetStep}>
Cancel
</Button>
</div>
</div>
)}
{/* Claim ready — confirm before submitting */}
{step === "claim-ready" && claimReadyData && (() => {
const [sy, sm, sd] = (claimReadyData.serviceDate ?? "").split("-");