diff --git a/apps/Frontend/src/components/layout/chatbot.tsx b/apps/Frontend/src/components/layout/chatbot.tsx index a2c44c84..412086ab 100644 --- a/apps/Frontend/src/components/layout/chatbot.tsx +++ b/apps/Frontend/src/components/layout/chatbot.tsx @@ -197,6 +197,9 @@ export function ChatbotButton() { const [eligibilityData, setEligibilityData] = useState(null); const [freeTextInput, setFreeTextInput] = useState(""); const [patientResult, setPatientResult] = useState(null); + // Snapshot of insuranceId/dateOfBirth as originally fetched, so we can tell whether the + // user hand-edited them in the patient-found card and only PUT to the DB when they did. + const originalPatientResultRef = useRef<{ insuranceId: string | null; dateOfBirth: string | null } | null>(null); const [eligibilityIdData, setEligibilityIdData] = useState<{ memberId: string; dob: string; siteKey: string; autoCheck: string; patient: PatientResult | null; appointmentDate?: string | null } | null>(null); const [batchEligibilityData, setBatchEligibilityData] = useState<{ memberId: string; dob: string; siteKey: string; autoCheck: string; patient: PatientResult | null }[] | null>(null); const [checkAndClaimData, setCheckAndClaimData] = useState(null); @@ -435,30 +438,41 @@ export function ChatbotButton() { setTimeout(() => { setLocation("/insurance-status"); setOpen(false); resetStep(); }, 600); }; - const handleEligibilityFromPatient = () => { + // If the user hand-edited Member ID / DOB on the patient-found card, save the correction + // back to the patient record so it doesn't revert to the stale value next time. + const persistPatientEditsIfChanged = async (patient: PatientResult) => { + const original = originalPatientResultRef.current; + if (!original) return; + const changed = + patient.insuranceId !== original.insuranceId || patient.dateOfBirth !== original.dateOfBirth; + if (!changed) return; + try { + await apiRequest("PUT", `/api/patients/${patient.id}`, { + insuranceId: patient.insuranceId, + dateOfBirth: patient.dateOfBirth, + }); + originalPatientResultRef.current = { insuranceId: patient.insuranceId, dateOfBirth: patient.dateOfBirth }; + } catch { + // Non-fatal — the edited values are still used for this eligibility check below. + } + }; + + const handleEligibilityFromPatient = async () => { if (!patientResult?.insuranceId || !patientResult?.dateOfBirth) return; addMsg("user", "Check eligibility now"); addMsg("bot", "Opening the eligibility check page..."); + await persistPatientEditsIfChanged(patientResult); prefillAndNavigate(patientResult.insuranceId, patientResult.dateOfBirth, getAutoCheck(patientResult.dateOfBirth)); }; const handleEligibilityAndAppointmentFromPatient = async () => { if (!patientResult?.insuranceId || !patientResult?.dateOfBirth) return; addMsg("user", "Check eligibility & add to schedule (today)"); - addMsg("bot", "Creating appointment for today...", true); - try { - const res = await apiRequest("POST", "/api/ai/create-appointment-today", { - patientId: patientResult.id, - }); - const data = await res.json(); - if (!res.ok) { - replaceLastMsg(data.message ?? "Could not create appointment."); - return; - } - replaceLastMsg(`Appointment added at ${data.startTime} (${data.column ?? "Column A"}) — opening eligibility check page...`); - } catch { - replaceLastMsg("Could not create appointment — opening eligibility check page..."); - } + addMsg("bot", "Running eligibility check — will add to today's schedule after..."); + await persistPatientEditsIfChanged(patientResult); + // Create the appointment only after eligibility is confirmed, not before — the eligibility + // page's tryAppointmentFromChatbot picks this up once the check running below completes. + sessionStorage.setItem("chatbot_appt_after_eligibility", JSON.stringify({ memberId: patientResult.insuranceId, date: null })); prefillAndNavigate(patientResult.insuranceId, patientResult.dateOfBirth, getAutoCheck(patientResult.dateOfBirth)); }; @@ -532,30 +546,11 @@ export function ChatbotButton() { : "today"; addMsg("user", `Check eligibility & add to schedule (${dateLabel})`); await syncEditedPatientFields(eligibilityIdData.memberId, eligibilityIdData.dob, eligibilityIdData.patient?.id); - - if (!eligibilityIdData.patient) { - addMsg("bot", `Running eligibility check — will add patient and create appointment for ${dateLabel} after...`); - sessionStorage.setItem("chatbot_appt_after_eligibility", JSON.stringify({ memberId: eligibilityIdData.memberId, date: targetDate ?? null })); - prefillAndNavigate(eligibilityIdData.memberId, eligibilityIdData.dob, eligibilityIdData.autoCheck); - return; - } - - addMsg("bot", `Creating appointment for ${dateLabel}...`, true); - try { - const res = await apiRequest("POST", "/api/ai/create-appointment-today", { - patientId: eligibilityIdData.patient.id, - date: targetDate ?? undefined, - }); - const data = await res.json(); - if (!res.ok) { - replaceLastMsg(data.message ?? "Could not create appointment."); - return; - } - replaceLastMsg(`Appointment added at ${data.startTime} (${data.column ?? "Column A"}) for ${data.dateLabel} — opening eligibility check page...`); - prefillAndNavigate(eligibilityIdData.memberId, eligibilityIdData.dob, eligibilityIdData.autoCheck); - } catch { - replaceLastMsg("Could not create appointment. Please try again."); - } + addMsg("bot", `Running eligibility check — will add${eligibilityIdData.patient ? "" : " patient and"} to schedule for ${dateLabel} after...`); + // Create the appointment only after eligibility is confirmed, not before — the eligibility + // page's tryAppointmentFromChatbot picks this up once the check running below completes. + sessionStorage.setItem("chatbot_appt_after_eligibility", JSON.stringify({ memberId: eligibilityIdData.memberId, date: targetDate ?? null })); + prefillAndNavigate(eligibilityIdData.memberId, eligibilityIdData.dob, eligibilityIdData.autoCheck); }; const handleCheckAndClaimRun = () => { @@ -614,6 +609,10 @@ export function ChatbotButton() { data.actionData?.patient ) { setPatientResult(data.actionData.patient); + originalPatientResultRef.current = { + insuranceId: data.actionData.patient.insuranceId, + dateOfBirth: data.actionData.patient.dateOfBirth, + }; setStep("patient-found"); return; } @@ -933,12 +932,29 @@ export function ChatbotButton() { {patientResult.insuranceProvider && (

{patientResult.insuranceProvider}

)} - {patientResult.insuranceId && ( -

ID: {patientResult.insuranceId}

- )} - {patientResult.dateOfBirth && ( -

DOB: {patientResult.dateOfBirth}

- )} +
+ + + setPatientResult((prev) => (prev ? { ...prev, insuranceId: e.target.value } : prev)) + } + className="h-7 text-xs bg-white" + /> +
+
+ + + setPatientResult((prev) => + prev ? { ...prev, dateOfBirth: mdyToISO(e.target.value) } : prev + ) + } + placeholder="MM/DD/YYYY" + className="h-7 text-xs bg-white" + /> +
{patientResult.insuranceId && patientResult.dateOfBirth && ( <> diff --git a/apps/Frontend/src/pages/insurance-status-page.tsx b/apps/Frontend/src/pages/insurance-status-page.tsx index 74095db5..86b5a32c 100755 --- a/apps/Frontend/src/pages/insurance-status-page.tsx +++ b/apps/Frontend/src/pages/insurance-status-page.tsx @@ -173,6 +173,9 @@ export default function InsuranceStatusPage() { const [triggerTarget, setTriggerTarget] = useState(null); const pendingScrollTo = useRef(null); const [prefillTick, setPrefillTick] = useState(0); + // Set when the chatbot prefill supplies a DOB, so the auto-lookup-by-member-ID effect below + // doesn't clobber a deliberately hand-edited DOB with the (possibly stale/wrong) one on file. + const suppressAutoLookupDobRef = useRef(false); // Prefill from chatbot useEffect(() => { @@ -192,6 +195,7 @@ export default function InsuranceStatusPage() { ? (() => { const [m, d, y] = dob.split("/"); return `${y}-${m!.padStart(2,"0")}-${d!.padStart(2,"0")}`; })() : dob; setDateOfBirth(parseLocalDate(normalized)); + suppressAutoLookupDobRef.current = true; } catch (dobErr) { console.error("[insurance-status] failed to parse prefilled DOB:", dob, dobErr); toast({ @@ -254,11 +258,15 @@ export default function InsuranceStatusPage() { if (patient) { setFirstName(patient.firstName ?? ""); setLastName(patient.lastName ?? ""); - const dob = - typeof patient.dateOfBirth === "string" - ? parseLocalDate(patient.dateOfBirth) - : patient.dateOfBirth ?? null; - setDateOfBirth(dob); + if (suppressAutoLookupDobRef.current) { + suppressAutoLookupDobRef.current = false; + } else { + const dob = + typeof patient.dateOfBirth === "string" + ? parseLocalDate(patient.dateOfBirth) + : patient.dateOfBirth ?? null; + setDateOfBirth(dob); + } } } catch { // silently ignore lookup errors @@ -709,6 +717,9 @@ export default function InsuranceStatusPage() { variant: "default", }); + const claimed = await tryClaimFromChatbot(selectedPatient?.id); + void tryAppointmentFromChatbot(); + if (claimed) return; setSelectedPatient(null); await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE }); processNextInQueue(); @@ -831,6 +842,17 @@ export default function InsuranceStatusPage() { const patient = await lookupRes.json(); if (!patient?.id) return; + // The eligibility check that just ran saved the result to patient.status — only book + // the appointment when insurance came back ACTIVE. + if (patient.status !== "ACTIVE") { + toast({ + title: "No appointment created", + description: `${patient.firstName ?? ""} ${patient.lastName ?? ""} insurance is ${patient.status === "INACTIVE" ? "inactive" : "not active"} — please add manually if needed.`.trim(), + variant: "destructive", + }); + return; + } + const apptRes = await apiRequest("POST", "/api/ai/create-appointment-today", { patientId: patient.id, date: useDate ?? undefined }); const apptData = await apptRes.json(); if (apptRes.ok) {