import { useEffect, useRef, useState } from "react"; import { useMutation, useQuery } from "@tanstack/react-query"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { CheckCircle, LoaderCircleIcon, Bot, PhoneCall, ChevronDown, ChevronUp } from "lucide-react"; import { useAuth } from "@/hooks/use-auth"; import { useToast } from "@/hooks/use-toast"; import { PatientTable } from "@/components/patients/patient-table"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { useAppDispatch, useAppSelector } from "@/redux/hooks"; import { setTaskStatus, clearTaskStatus, } from "@/redux/slices/seleniumTaskSlice"; import { SeleniumTaskBanner } from "@/components/ui/selenium-task-banner"; import { formatLocalDate, parseLocalDate } from "@/utils/dateUtils"; import { socket } from "@/lib/socket"; import { InsertPatient, Patient } from "@repo/db/types"; import { DateInput } from "@/components/ui/dateInput"; import { QK_PATIENTS_BASE } from "@/components/patients/patient-table"; import { PdfPreviewModal } from "@/components/insurance-status/pdf-preview-modal"; import { DualPdfPreviewModal } from "@/components/insurance-status/dual-pdf-preview-modal"; import { useLocation } from "wouter"; import { DdmaEligibilityButton } from "@/components/insurance-status/ddma-buton-modal"; import { DeltaInsEligibilityButton } from "@/components/insurance-status/deltains-button-modal"; import { TuftsSCOEligibilityButton } from "@/components/insurance-status/tufts-sco-button-modal"; import { UnitedSCOEligibilityButton } from "@/components/insurance-status/united-sco-button-modal"; import { CCAEligibilityButton } from "@/components/insurance-status/cca-button-modal"; import { BcbsMaEligibilityButton } from "@/components/insurance-status/bcbs-ma-button-modal"; import { useLicense } from "@/hooks/use-license"; /** * Waits for a Selenium job to complete by racing socket.io events against * HTTP polling every 3 seconds. This ensures the result is received even * when the socket reconnects through Cloudflare (changing socket.id). */ function waitForSeleniumJob( jobId: string, onActive?: (message: string) => void, ): Promise { return new Promise((resolve, reject) => { let settled = false; const settle = (fn: () => void) => { if (settled) return; settled = true; clearInterval(pollInterval); socket.off("job:update", socketHandler); fn(); }; const socketHandler = (payload: any) => { if (String(payload.jobId) !== String(jobId)) return; if (payload.status === "active") { onActive?.(payload.message ?? "Selenium running..."); } else if (payload.status === "completed") { settle(() => resolve(payload.result ?? {})); } else if (payload.status === "failed") { settle(() => reject(new Error(payload.error ?? "Selenium job failed"))); } }; socket.on("job:update", socketHandler); const pollInterval = setInterval(async () => { if (settled) return; try { const res = await apiRequest("GET", `/api/insurance-status/job-status/${jobId}`); if (!res.ok) return; const data = await res.json(); if (data.status === "completed") { settle(() => resolve(data.result ?? {})); } else if (data.status === "failed") { settle(() => reject(new Error(data.error ?? "Selenium job failed"))); } } catch { // ignore transient poll errors } }, 3000); }); } export default function InsuranceStatusPage() { const { user } = useAuth(); const { toast } = useToast(); const { isLicensed } = useLicense(); const dispatch = useAppDispatch(); const { status, message, show } = useAppSelector( (state) => state.seleniumTasks.eligibilityCheck, ); const [selectedPatient, setSelectedPatient] = useState(null); const [location, setLocation] = useLocation(); // Insurance eligibility and claim check form fields const [memberId, setMemberId] = useState(""); const [dateOfBirth, setDateOfBirth] = useState(null); const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const isFormIncomplete = !memberId || !dateOfBirth; const [isCheckingEligibilityStatus, setIsCheckingEligibilityStatus] = useState(false); const [isCheckingEligibilityAppointment, setIsCheckingEligibilityAppointment] = useState(false); const [isCheckingEligibilityClaimsPreAuth, setIsCheckingEligibilityClaimsPreAuth] = useState(false); const [isCheckingEligibilityHistory, setIsCheckingEligibilityHistory] = useState(false); const [isCheckingCMSP, setIsCheckingCMSP] = useState(false); // AI Call Insurance section const [aiCallOpen, setAiCallOpen] = useState(false); const [aiSelectedContactId, setAiSelectedContactId] = useState(""); const [aiCallNotes, setAiCallNotes] = useState(""); type InsuranceContact = { id: number; name: string; phoneNumber?: string | null }; const { data: insuranceContacts = [] } = useQuery({ queryKey: ["/api/insurance-contacts"], queryFn: async () => { const res = await apiRequest("GET", "/api/insurance-contacts"); if (!res.ok) return []; return res.json(); }, }); const selectedInsuranceContact = insuranceContacts.find( (c) => String(c.id) === aiSelectedContactId ) ?? null; // PDF preview modal state const [previewOpen, setPreviewOpen] = useState(false); const [previewPdfId, setPreviewPdfId] = useState(null); const [previewFallbackFilename, setPreviewFallbackFilename] = useState< string | null >(null); // Dual PDF modal state (used by MH Eligibility & History) const [dualPreviewOpen, setDualPreviewOpen] = useState(false); const [dualEligibilityPdfId, setDualEligibilityPdfId] = useState(null); const [dualEligibilityFilename, setDualEligibilityFilename] = useState(null); const [dualMemberDetailsPdfId, setDualMemberDetailsPdfId] = useState(null); const [dualMemberDetailsFilename, setDualMemberDetailsFilename] = useState(null); const [dualHistoryPdfId, setDualHistoryPdfId] = useState(null); const [dualHistoryFilename, setDualHistoryFilename] = useState(null); // CMSP PDF modal state (used by CMSP Eligibility & History & Remaining) const [cmspPreviewOpen, setCmspPreviewOpen] = useState(false); const [cmspEligibilityPdfId, setCmspEligibilityPdfId] = useState(null); const [cmspEligibilityFilename, setCmspEligibilityFilename] = useState(null); const [cmspMemberDetailsPdfId, setCmspMemberDetailsPdfId] = useState(null); const [cmspMemberDetailsFilename, setCmspMemberDetailsFilename] = useState(null); const [cmspHistoryPdfId, setCmspHistoryPdfId] = useState(null); const [cmspHistoryFilename, setCmspHistoryFilename] = useState(null); const [cmspAccumulatorPdfId, setCmspAccumulatorPdfId] = useState(null); const [cmspAccumulatorFilename, setCmspAccumulatorFilename] = useState(null); const pendingAutoCheck = useRef<"mh" | "mh-history" | "cmsp" | "ddma" | "delta-ins" | "united-sco" | "tufts-sco" | "cca" | null>(null); const [triggerTarget, setTriggerTarget] = useState(null); const pendingScrollTo = useRef(null); const [prefillTick, setPrefillTick] = useState(0); // Prefill from chatbot useEffect(() => { const apply = () => { const raw = sessionStorage.getItem("chatbot_eligibility"); if (!raw) return; try { const { memberId: id, dob, autoCheck: ac } = JSON.parse(raw); if (id) setMemberId(id); if (dob) { // dob may arrive as MM/DD/YYYY or YYYY-MM-DD — normalize to YYYY-MM-DD const normalized = /^\d{1,2}\/\d{1,2}\/\d{4}$/.test(dob) ? (() => { const [m, d, y] = dob.split("/"); return `${y}-${m!.padStart(2,"0")}-${d!.padStart(2,"0")}`; })() : dob; setDateOfBirth(parseLocalDate(normalized)); } if (ac) pendingAutoCheck.current = ac; sessionStorage.removeItem("chatbot_eligibility"); // Increment tick so the auto-trigger effect re-fires even when // memberId/dob are unchanged (same patient submitted a second time). setPrefillTick((n) => n + 1); } catch {} }; apply(); window.addEventListener("chatbot:eligibility-prefill", apply); return () => window.removeEventListener("chatbot:eligibility-prefill", apply); }, []); // Populate fields from selected patient useEffect(() => { if (selectedPatient) { setMemberId(selectedPatient.insuranceId ?? ""); setFirstName(selectedPatient.firstName ?? ""); setLastName(selectedPatient.lastName ?? ""); const dob = typeof selectedPatient.dateOfBirth === "string" ? parseLocalDate(selectedPatient.dateOfBirth) : selectedPatient.dateOfBirth; setDateOfBirth(dob ?? null); } else { setMemberId(""); setFirstName(""); setLastName(""); setDateOfBirth(null); } }, [selectedPatient]); // Auto-lookup patient by member ID when typed manually useEffect(() => { if (selectedPatient && memberId === (selectedPatient.insuranceId ?? "")) return; if (!memberId) return; const timer = setTimeout(async () => { try { const res = await apiRequest("GET", `/api/patients/by-insurance-id?insuranceId=${encodeURIComponent(memberId)}`); if (!res.ok) return; const patient: Patient | null = await res.json(); if (patient) { setFirstName(patient.firstName ?? ""); setLastName(patient.lastName ?? ""); const dob = typeof patient.dateOfBirth === "string" ? parseLocalDate(patient.dateOfBirth) : patient.dateOfBirth ?? null; setDateOfBirth(dob); } } catch { // silently ignore lookup errors } }, 500); return () => clearTimeout(timer); }, [memberId, selectedPatient]); // Add patient mutation const addPatientMutation = useMutation({ mutationFn: async (patient: InsertPatient) => { const res = await apiRequest("POST", "/api/patients/", patient); return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE }); toast({ title: "Success", description: "Patient added successfully!", variant: "default", }); }, onError: (error: any) => { const msg = error.message; if (msg === "A patient with this insurance ID already exists.") { toast({ title: "Patient already exists", description: msg, variant: "destructive", }); } else { toast({ title: "Error", description: `Failed to add patient: ${msg}`, variant: "destructive", }); } }, }); // Shared: run MH eligibility selenium job, return jobResult or throw const runMHEligibilitySelenium = async (): Promise => { const formattedDob = dateOfBirth ? formatLocalDate(dateOfBirth) : ""; const data = { memberId, dateOfBirth: formattedDob, insuranceSiteKey: "MH", firstName: firstName || undefined, lastName: lastName || undefined, }; dispatch( setTaskStatus({ key: "eligibilityCheck", status: "pending", message: "Sending Data to Selenium...", }), ); const response = await apiRequest( "POST", "/api/insurance-status/eligibility-check", { data: data, socketId: socket.id }, ); const enqueueResult = await response.json(); if (enqueueResult.error) throw new Error(enqueueResult.error); const jobId = enqueueResult.jobId; if (!jobId) throw new Error("No jobId returned from server"); dispatch( setTaskStatus({ key: "eligibilityCheck", status: "pending", message: "Selenium browser starting...", }), ); return waitForSeleniumJob(jobId, (msg) => dispatch(setTaskStatus({ key: "eligibilityCheck", status: "pending", message: msg })) ); }; const handleAddPatient = async () => { const newPatient: InsertPatient = { firstName, lastName, dateOfBirth: dateOfBirth, gender: "", phone: "", userId: user?.id ?? 1, insuranceId: memberId, }; await addPatientMutation.mutateAsync(newPatient); }; // MH Eligibility — check eligibility, save to DB, open PDF preview const handleMHEligibilityButton = async () => { if (!memberId || !dateOfBirth) { toast({ title: "Missing Fields", description: "Please fill in all the required fields: Member ID, Date of Birth.", variant: "destructive", }); return; } setIsCheckingEligibilityStatus(true); try { const jobResult = await runMHEligibilitySelenium(); dispatch( setTaskStatus({ key: "eligibilityCheck", status: "success", message: "Patient status is updated, and its eligibility pdf is uploaded at Document Page.", }), ); toast({ title: "Selenium service done.", description: "Your Patient Eligibility is fetched and updated, Kindly search through the patient.", variant: "default", }); setSelectedPatient(null); await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE }); void tryAppointmentFromChatbot(); if (jobResult.pdfFileId) { setPreviewPdfId(Number(jobResult.pdfFileId)); setPreviewFallbackFilename(jobResult.pdfFilename ?? `eligibility_${memberId}.pdf`); setPreviewOpen(true); } } catch (error: any) { dispatch(setTaskStatus({ key: "eligibilityCheck", status: "error", message: error.message || "Selenium submission failed" })); toast({ title: "Selenium service error", description: error.message || "An error occurred.", variant: "destructive" }); } finally { setIsCheckingEligibilityStatus(false); } }; // MH Eligibility & Appointment — check eligibility, save to DB, create appointment for today const handleMHEligibilityAppointmentButton = async () => { if (!memberId || !dateOfBirth) { toast({ title: "Missing Fields", description: "Please fill in all the required fields: Member ID, Date of Birth.", variant: "destructive", }); return; } setIsCheckingEligibilityAppointment(true); try { const jobResult = await runMHEligibilitySelenium(); const isInactive = jobResult?.patientUpdateStatus?.includes("INACTIVE"); if (isInactive) { dispatch( setTaskStatus({ key: "eligibilityCheck", status: "error", message: "Insurance is inactive. No appointment created.", }), ); toast({ title: "Insurance Inactive", description: "Patient insurance is inactive. No appointment was created.", variant: "destructive", }); await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE }); return; } await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE }); // Look up the patient (created/updated by the Selenium worker) then create today's appointment let apptTime: string | null = null; try { const lookupRes = await apiRequest("GET", `/api/patients/by-insurance-id?insuranceId=${encodeURIComponent(memberId)}`); if (lookupRes.ok) { const patient = await lookupRes.json(); if (patient?.id) { const apptRes = await apiRequest("POST", "/api/ai/create-appointment-today", { patientId: patient.id }); if (apptRes.ok) { const apptData = await apptRes.json(); apptTime = apptData.startTime ? `${apptData.startTime} (${apptData.column ?? "Column A"})` : null; } } } } catch {} dispatch( setTaskStatus({ key: "eligibilityCheck", status: "success", message: apptTime ? `Eligibility checked. Appointment created at ${apptTime}.` : "Eligibility checked. Could not create appointment — please add manually.", }), ); toast({ title: "Eligibility checked.", description: apptTime ? `Patient added to today's schedule at ${apptTime}.` : "Eligibility saved. Could not create appointment — Column A may be full.", variant: "default", }); setSelectedPatient(null); if (jobResult.pdfFileId) { setPreviewPdfId(Number(jobResult.pdfFileId)); setPreviewFallbackFilename(jobResult.pdfFilename ?? `eligibility_${memberId}.pdf`); setPreviewOpen(true); } } catch (error: any) { dispatch(setTaskStatus({ key: "eligibilityCheck", status: "error", message: error.message || "Selenium submission failed" })); toast({ title: "Selenium service error", description: error.message || "An error occurred.", variant: "destructive" }); } finally { setIsCheckingEligibilityAppointment(false); } }; // MH Eligibility & Claims/PreAuth — check eligibility, save to DB, navigate to claims const handleMHEligibilityClaimsPreAuthButton = async () => { if (!memberId || !dateOfBirth) { toast({ title: "Missing Fields", description: "Please fill in all the required fields: Member ID, Date of Birth.", variant: "destructive", }); return; } setIsCheckingEligibilityClaimsPreAuth(true); try { await runMHEligibilitySelenium(); dispatch( setTaskStatus({ key: "eligibilityCheck", status: "success", message: "Eligibility checked and saved. Redirecting to Claims/PreAuth...", }), ); toast({ title: "Eligibility checked.", description: "Patient eligibility saved. Redirecting to Claims/PreAuth.", variant: "default", }); setSelectedPatient(null); await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE }); setLocation("/claims"); } catch (error: any) { dispatch(setTaskStatus({ key: "eligibilityCheck", status: "error", message: error.message || "Selenium submission failed" })); toast({ title: "Selenium service error", description: error.message || "An error occurred.", variant: "destructive" }); } finally { setIsCheckingEligibilityClaimsPreAuth(false); } }; // MH Eligibility & History — eligibility check + service history, saves both PDFs, auto-downloads eligibility PDF only const handleMHEligibilityHistoryButton = async () => { if (!memberId || !dateOfBirth) { toast({ title: "Missing Fields", description: "Please fill in all the required fields: Member ID, Date of Birth.", variant: "destructive", }); return; } setIsCheckingEligibilityHistory(true); try { const formattedDob = dateOfBirth ? formatLocalDate(dateOfBirth) : ""; const data = { memberId, dateOfBirth: formattedDob, insuranceSiteKey: "MH", firstName: firstName || undefined, lastName: lastName || undefined, }; dispatch(setTaskStatus({ key: "eligibilityCheck", status: "pending", message: "Sending Data to Selenium..." })); const response = await apiRequest( "POST", "/api/insurance-status/eligibility-history-check", { data, socketId: socket.id }, ); const enqueueResult = await response.json(); if (enqueueResult.error) throw new Error(enqueueResult.error); const jobId = enqueueResult.jobId; if (!jobId) throw new Error("No jobId returned from server"); dispatch(setTaskStatus({ key: "eligibilityCheck", status: "pending", message: "Selenium browser starting..." })); const jobResult = await waitForSeleniumJob(jobId, (msg) => dispatch(setTaskStatus({ key: "eligibilityCheck", status: "pending", message: msg })) ); dispatch(setTaskStatus({ key: "eligibilityCheck", status: "success", message: "Eligibility and service history PDFs saved to Documents." })); toast({ title: "Done", description: "Eligibility and service history PDFs saved. Eligibility PDF downloading now.", variant: "default", }); const claimed = await tryClaimFromChatbot(selectedPatient?.id); void tryAppointmentFromChatbot(); if (claimed) return; setSelectedPatient(null); await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE }); // Open all PDFs side by side in the modal if (jobResult.pdfFileId || jobResult.memberDetailsPdfFileId || jobResult.historyPdfFileId) { setDualEligibilityPdfId(jobResult.pdfFileId ? Number(jobResult.pdfFileId) : null); setDualEligibilityFilename(jobResult.pdfFilename ?? `eligibility_${memberId}.pdf`); setDualMemberDetailsPdfId(jobResult.memberDetailsPdfFileId ? Number(jobResult.memberDetailsPdfFileId) : null); setDualMemberDetailsFilename(jobResult.memberDetailsPdfFilename ?? `eligibility_member_details_${memberId}.pdf`); setDualHistoryPdfId(jobResult.historyPdfFileId ? Number(jobResult.historyPdfFileId) : null); setDualHistoryFilename(jobResult.historyPdfFilename ?? `eligibility_history_${memberId}.pdf`); setDualPreviewOpen(true); } } catch (error: any) { dispatch(setTaskStatus({ key: "eligibilityCheck", status: "error", message: error.message || "Selenium submission failed" })); toast({ title: "Selenium service error", description: error.message || "An error occurred.", variant: "destructive" }); } finally { setIsCheckingEligibilityHistory(false); } }; // CMSP Eligibility & History & Remaining — eligibility + service history + accumulator PDFs const handleCMSPButton = async () => { if (!memberId || !dateOfBirth) { toast({ title: "Missing Fields", description: "Please fill in Member ID and Date of Birth.", variant: "destructive", }); return; } setIsCheckingCMSP(true); try { const formattedDob = dateOfBirth ? formatLocalDate(dateOfBirth) : ""; const data = { memberId, dateOfBirth: formattedDob, insuranceSiteKey: "MH", firstName: firstName || undefined, lastName: lastName || undefined, }; dispatch(setTaskStatus({ key: "eligibilityCheck", status: "pending", message: "Sending Data to Selenium..." })); const response = await apiRequest( "POST", "/api/insurance-status/cmsp-eligibility-history-remaining-check", { data, socketId: socket.id }, ); const enqueueResult = await response.json(); if (enqueueResult.error) throw new Error(enqueueResult.error); const jobId = enqueueResult.jobId; if (!jobId) throw new Error("No jobId returned from server"); dispatch(setTaskStatus({ key: "eligibilityCheck", status: "pending", message: "Selenium browser starting..." })); const jobResult = await waitForSeleniumJob(jobId, (msg) => dispatch(setTaskStatus({ key: "eligibilityCheck", status: "pending", message: msg })) ); dispatch(setTaskStatus({ key: "eligibilityCheck", status: "success", message: "CMSP PDFs saved to Documents." })); toast({ title: "Done", description: "Eligibility, history, and accumulator PDFs saved.", variant: "default", }); setSelectedPatient(null); await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE }); // Open 4-panel modal if (jobResult.pdfFileId || jobResult.memberDetailsPdfFileId || jobResult.historyPdfFileId || jobResult.accumulatorPdfFileId) { setCmspEligibilityPdfId(jobResult.pdfFileId ? Number(jobResult.pdfFileId) : null); setCmspEligibilityFilename(jobResult.pdfFilename ?? `cmsp_eligibility_${memberId}.pdf`); setCmspMemberDetailsPdfId(jobResult.memberDetailsPdfFileId ? Number(jobResult.memberDetailsPdfFileId) : null); setCmspMemberDetailsFilename(jobResult.memberDetailsPdfFilename ?? `cmsp_member_details_${memberId}.pdf`); setCmspHistoryPdfId(jobResult.historyPdfFileId ? Number(jobResult.historyPdfFileId) : null); setCmspHistoryFilename(jobResult.historyPdfFilename ?? `cmsp_history_${memberId}.pdf`); setCmspAccumulatorPdfId(jobResult.accumulatorPdfFileId ? Number(jobResult.accumulatorPdfFileId) : null); setCmspAccumulatorFilename(jobResult.accumulatorPdfFilename ?? `cmsp_accumulator_${memberId}.pdf`); setCmspPreviewOpen(true); } } catch (error: any) { dispatch(setTaskStatus({ key: "eligibilityCheck", status: "error", message: error.message || "Selenium submission failed" })); toast({ title: "Selenium service error", description: error.message || "An error occurred.", variant: "destructive" }); } finally { setIsCheckingCMSP(false); } }; // Auto-trigger after prefill (from chatbot or schedule page "Check Eligibility") useEffect(() => { if (!pendingAutoCheck.current || !memberId || !dateOfBirth) return; const check = pendingAutoCheck.current; pendingAutoCheck.current = null; if (check === "mh" || check === "mh-history") { handleMHEligibilityHistoryButton(); } else if (check === "cmsp") { handleCMSPButton(); } else { // Button-component-handled providers: pass target to their autoTrigger prop setTriggerTarget(check); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [memberId, dateOfBirth, prefillTick]); // small helper: remove given query params from the current URL (silent, no reload) const clearUrlParams = (params: string[]) => { try { const url = new URL(window.location.href); let changed = false; for (const p of params) { if (url.searchParams.has(p)) { url.searchParams.delete(p); changed = true; } } if (changed) { window.history.replaceState({}, document.title, url.toString()); } } catch (e) { // ignore } }; // After any eligibility check succeeds, check if chatbot wanted auto-claim. // PDF is already saved to Documents by the Selenium worker before this is called. // Returns true if a chatbot claim was pending (caller should skip opening the preview). const tryClaimFromChatbot = async (resolvedPatientId?: number | null): Promise => { try { const raw = sessionStorage.getItem("chatbot_claim_codes"); if (!raw) return false; const { codes, siteKey, patientId, memberId: storedMemberId, serviceDate, renderingProvider } = JSON.parse(raw); sessionStorage.removeItem("chatbot_claim_codes"); let pid: number | null = resolvedPatientId ?? patientId ?? null; // Fallback: look up patient by memberId if patientId wasn't stored if (!pid && storedMemberId) { try { const res = await apiRequest("GET", `/api/patients/by-insurance-id?insuranceId=${encodeURIComponent(storedMemberId)}`); if (res.ok) { const p = await res.json(); pid = p?.id ?? null; } } catch {} } if (!codes?.length || !pid) return false; sessionStorage.setItem( "chatbot_claim_prefill", JSON.stringify({ codes, siteKey, serviceDate: serviceDate ?? null, autoSubmit: true, renderingProvider: renderingProvider ?? null }) ); setLocation(`/claims?newPatient=${pid}`); return true; } catch {} return false; }; // After any eligibility check succeeds, check if chatbot wanted an appointment created. // Patient was just created/updated in DB by the Selenium worker before this is called. const tryAppointmentFromChatbot = async (): Promise => { try { const raw = sessionStorage.getItem("chatbot_appt_after_eligibility"); if (!raw) return; const { memberId: storedMemberId, date: storedDate } = JSON.parse(raw); sessionStorage.removeItem("chatbot_appt_after_eligibility"); if (!storedMemberId) return; const lookupRes = await apiRequest("GET", `/api/patients/by-insurance-id?insuranceId=${encodeURIComponent(storedMemberId)}`); if (!lookupRes.ok) return; const patient = await lookupRes.json(); if (!patient?.id) return; const apptRes = await apiRequest("POST", "/api/ai/create-appointment-today", { patientId: patient.id, date: storedDate ?? undefined }); const apptData = await apptRes.json(); if (apptRes.ok) { const scheduledOn = storedDate ? new Date(storedDate + "T00:00:00").toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "today"; toast({ title: "Appointment created", description: `${patient.firstName ?? ""} ${patient.lastName ?? ""} added to schedule (${scheduledOn}) at ${apptData.startTime} (${apptData.column ?? "Column A"}).`.trim(), }); } else { toast({ title: "Could not create appointment", description: apptData.message ?? "Please add manually.", variant: "destructive", }); } } catch {} }; // Redirect from schedule page "Check Eligibility": prefill patient + optionally auto-trigger or scroll useEffect(() => { const params = new URLSearchParams(window.location.search); const appointmentId = params.get("appointmentId"); if (!appointmentId) return; const id = Number(appointmentId); if (Number.isNaN(id) || id <= 0) return; const autoCheck = params.get("autoCheck") as typeof pendingAutoCheck.current; const scrollTo = params.get("scrollTo"); const knownAutoChecks = ["mh", "mh-history", "cmsp", "ddma", "delta-ins", "united-sco", "tufts-sco", "cca"] as const; if (autoCheck && (knownAutoChecks as readonly string[]).includes(autoCheck)) { pendingAutoCheck.current = autoCheck; } else if (scrollTo) { pendingScrollTo.current = scrollTo; } let cancelled = false; (async () => { try { const res = await apiRequest("GET", `/api/appointments/${id}/patient`); if (!res.ok) return; const data = await res.json(); const patient = data?.patient ?? data; if (!cancelled && patient) { setSelectedPatient(patient as Patient); clearUrlParams(["appointmentId", "action", "autoCheck", "scrollTo"]); } } catch (err) { console.error("Failed to fetch patient from appointment", err); } })(); return () => { cancelled = true; }; }, [location]); // Scroll to highlighted button after patient is loaded from schedule page (other insurance) useEffect(() => { if (!selectedPatient || !pendingScrollTo.current) return; const target = pendingScrollTo.current; pendingScrollTo.current = null; setTimeout(() => { const el = document.getElementById(`eligibility-${target}`); if (!el) return; el.scrollIntoView({ behavior: "smooth", block: "center" }); el.classList.add("ring-2", "ring-blue-500", "ring-offset-2", "transition-all"); setTimeout(() => { el.classList.remove("ring-2", "ring-blue-500", "ring-offset-2", "transition-all"); }, 2500); }, 300); }, [selectedPatient]); return (
dispatch(clearTaskStatus("eligibilityCheck"))} />

Insurance Eligibility

Check insurance eligibility.

{/* Insurance Eligibility Check Form */}
setMemberId(e.target.value)} />
setFirstName(e.target.value)} />
setLastName(e.target.value)} />
{/* TEMP PROVIDER BUTTONS */}

Other provider checks

{/* Row 1 */}
setTriggerTarget(null)} onPdfReady={async (pdfId, fallbackFilename) => { const claimed = await tryClaimFromChatbot(selectedPatient?.id); void tryAppointmentFromChatbot(); if (!claimed) { setPreviewPdfId(pdfId); setPreviewFallbackFilename(fallbackFilename ?? `eligibility_ddma_${memberId}.pdf`); setPreviewOpen(true); } }} />
setTriggerTarget(null)} onPdfReady={async (pdfId, fallbackFilename) => { const claimed = await tryClaimFromChatbot(selectedPatient?.id); void tryAppointmentFromChatbot(); if (!claimed) { setPreviewPdfId(pdfId); setPreviewFallbackFilename(fallbackFilename ?? `eligibility_deltains_${memberId}.pdf`); setPreviewOpen(true); } }} />
{ void tryAppointmentFromChatbot(); setPreviewPdfId(pdfId); setPreviewFallbackFilename( fallbackFilename ?? `eligibility_bcbs_ma_${memberId}.pdf`, ); setPreviewOpen(true); }} />
{/* Row 2 */}
setTriggerTarget(null)} onPdfReady={async (pdfId, fallbackFilename) => { const claimed = await tryClaimFromChatbot(selectedPatient?.id); void tryAppointmentFromChatbot(); if (!claimed) { setPreviewPdfId(pdfId); setPreviewFallbackFilename(fallbackFilename ?? `eligibility_tuftssco_${memberId}.pdf`); setPreviewOpen(true); } }} />
setTriggerTarget(null)} onPdfReady={async (pdfId, fallbackFilename) => { const claimed = await tryClaimFromChatbot(selectedPatient?.id); void tryAppointmentFromChatbot(); if (!claimed) { setPreviewPdfId(pdfId); setPreviewFallbackFilename(fallbackFilename ?? `eligibility_unitedsco_${memberId}.pdf`); setPreviewOpen(true); } }} />
setTriggerTarget(null)} onPdfReady={async (pdfId, fallbackFilename) => { const claimed = await tryClaimFromChatbot(selectedPatient?.id); void tryAppointmentFromChatbot(); if (!claimed) { setPreviewPdfId(pdfId); setPreviewFallbackFilename(fallbackFilename ?? `eligibility_cca_${memberId}.pdf`); setPreviewOpen(true); } }} />
{/* Row 3 */}
{/* Row 5 */}
{/* AI Call Insurance */} setAiCallOpen((o) => !o)} >
AI Call Insurance/Transportation Use AI to call the insurance company and check eligibility automatically
{aiCallOpen ? : }
{aiCallOpen && ( {/* Patient context banner */} {selectedPatient && (
Selected patient:{" "} {selectedPatient.firstName} {selectedPatient.lastName} {selectedPatient.insuranceId && ( <> — Member ID: {selectedPatient.insuranceId} )}
)}
{insuranceContacts.length === 0 ? (

No insurance contacts saved.{" "} Add one in Settings → Insurance Contact

) : ( )}
setAiCallNotes(e.target.value)} />

{selectedInsuranceContact ? `Will call ${selectedInsuranceContact.name}${selectedInsuranceContact.phoneNumber ? ` at ${selectedInsuranceContact.phoneNumber}` : ""} — AI calling logic coming soon` : "Select an insurance company to begin — AI calling logic coming soon"}

)}
{/* Patients Table */} Patient Records Select Patients and Check Their Eligibility
{/* Pdf preview modal */} { setPreviewOpen(false); setPreviewPdfId(null); setPreviewFallbackFilename(null); }} pdfId={previewPdfId ?? undefined} fallbackFilename={previewFallbackFilename ?? undefined} autoDownload /> {/* 4-panel modal for CMSP — eligibility, member details, history, accumulator */} { setCmspPreviewOpen(false); setCmspEligibilityPdfId(null); setCmspEligibilityFilename(null); setCmspMemberDetailsPdfId(null); setCmspMemberDetailsFilename(null); setCmspHistoryPdfId(null); setCmspHistoryFilename(null); setCmspAccumulatorPdfId(null); setCmspAccumulatorFilename(null); }} title="CMSP Eligibility, History & Remaining" panels={[ { pdfId: cmspEligibilityPdfId, fallbackFilename: cmspEligibilityFilename, label: "Eligibility", autoDownload: true, }, { pdfId: cmspMemberDetailsPdfId, fallbackFilename: cmspMemberDetailsFilename, label: "Member Details", }, { pdfId: cmspHistoryPdfId, fallbackFilename: cmspHistoryFilename, label: "Service History", }, { pdfId: cmspAccumulatorPdfId, fallbackFilename: cmspAccumulatorFilename, label: "Accumulator (Remaining)", }, ]} /> {/* 3-panel modal for MH Eligibility & History */} { setDualPreviewOpen(false); setDualEligibilityPdfId(null); setDualEligibilityFilename(null); setDualMemberDetailsPdfId(null); setDualMemberDetailsFilename(null); setDualHistoryPdfId(null); setDualHistoryFilename(null); }} title="MH Eligibility & Service History" panels={[ { pdfId: dualEligibilityPdfId, fallbackFilename: dualEligibilityFilename, label: "Eligibility", autoDownload: true, }, { pdfId: dualMemberDetailsPdfId, fallbackFilename: dualMemberDetailsFilename, label: "Member Details", }, { pdfId: dualHistoryPdfId, fallbackFilename: dualHistoryFilename, label: "Service History", }, ]} />
); }