feat: rename DDMA to ClaimMD across selenium service, remove MH/CMSP eligibility feature
Rename ddma_browser_manager/helpers/worker files to claimmd_* naming in SeleniumService. Remove the MassHealth (MH) and CMSP eligibility-check feature end to end: buttons and dead placeholder buttons on the insurance eligibility page, their handlers and auto-trigger wiring, the chatbot's MassHealth-only manual paste flow, backend routes/queue job types/processors, and the corresponding Python Selenium worker endpoints. Multi-provider eligibility checking (DDMA, Delta, Tufts SCO, United SCO, CCA, BCBS MA) and the Twilio SMS eligibility flow are untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
00fd213291
commit
a3d2960c98
@@ -7,7 +7,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<title>My Dental Office Management</title>
|
||||
<title>Wellspring Acupuncture Office Management</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -419,7 +419,7 @@ export function DdmaEligibilityButton({
|
||||
) : (
|
||||
<>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
Delta MA
|
||||
Claim MD
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useState, useRef, useEffect } from "react";
|
||||
import {
|
||||
Bot,
|
||||
X,
|
||||
ChevronRight,
|
||||
Stethoscope,
|
||||
Calendar,
|
||||
FileText,
|
||||
@@ -13,7 +12,6 @@ import {
|
||||
Paperclip,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useLocation } from "wouter";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
@@ -21,8 +19,6 @@ import { setChatbotPendingFiles } from "@/lib/chatbotFileStore";
|
||||
|
||||
type Step =
|
||||
| "menu"
|
||||
| "eligibility-input"
|
||||
| "eligibility-confirm"
|
||||
| "ai-loading"
|
||||
| "patient-found"
|
||||
| "eligibility-id-ready"
|
||||
@@ -53,12 +49,6 @@ interface PatientResult {
|
||||
dateOfBirth: string | null;
|
||||
}
|
||||
|
||||
interface EligibilityData {
|
||||
memberId: string;
|
||||
dob: string;
|
||||
dobISO: string;
|
||||
}
|
||||
|
||||
interface CheckAndClaimData {
|
||||
patient: PatientResult | null;
|
||||
memberId: string;
|
||||
@@ -75,40 +65,6 @@ function makeMsg(role: "bot" | "user", text: string, isLoading = false): Message
|
||||
return { id: ++msgCounter, role, text, isLoading };
|
||||
}
|
||||
|
||||
function getAutoCheck(dobISO: string): "mh" | "cmsp" {
|
||||
const [y, m, d] = dobISO.split("-").map(Number);
|
||||
const today = new Date();
|
||||
let age = today.getFullYear() - (y ?? 0);
|
||||
const monthDiff = today.getMonth() + 1 - (m ?? 0);
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < (d ?? 0))) age--;
|
||||
return age >= 21 ? "mh" : "cmsp";
|
||||
}
|
||||
|
||||
function parseEligibilityInput(
|
||||
raw: string
|
||||
): { memberId: string; display: string; iso: string } | null {
|
||||
const dateMatch = raw.match(/\b(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})\b/);
|
||||
if (!dateMatch) return null;
|
||||
const m = dateMatch[1];
|
||||
const d = dateMatch[2];
|
||||
const y = dateMatch[3];
|
||||
if (!m || !d || !y) return null;
|
||||
const month = parseInt(m, 10);
|
||||
const day = parseInt(d, 10);
|
||||
const year = parseInt(y, 10);
|
||||
if (month < 1 || month > 12 || day < 1 || day > 31 || year < 1900) return null;
|
||||
|
||||
const withoutDate = raw.replace(dateMatch[0], "");
|
||||
const memberId = (withoutDate.match(/[a-zA-Z0-9]/g) ?? []).join("");
|
||||
if (!memberId) return null;
|
||||
|
||||
return {
|
||||
memberId,
|
||||
display: `${m.padStart(2, "0")}/${d.padStart(2, "0")}/${y}`,
|
||||
iso: `${y}-${m.padStart(2, "0")}-${d.padStart(2, "0")}`,
|
||||
};
|
||||
}
|
||||
|
||||
const CHAT_STORAGE_KEY = "chatbot_messages";
|
||||
const CHATBOT_JOB_TS_KEY = "chatbot_job_started_at";
|
||||
|
||||
@@ -158,9 +114,6 @@ export function ChatbotButton() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [step, setStep] = useState<Step>("menu");
|
||||
const [messages, setMessages] = useState<Message[]>(loadSavedMessages);
|
||||
const [pasteInput, setPasteInput] = useState("");
|
||||
const [parseError, setParseError] = useState("");
|
||||
const [eligibilityData, setEligibilityData] = useState<EligibilityData | null>(null);
|
||||
const [freeTextInput, setFreeTextInput] = useState("");
|
||||
const [patientResult, setPatientResult] = useState<PatientResult | null>(null);
|
||||
const [eligibilityIdData, setEligibilityIdData] = useState<{ memberId: string; dob: string; siteKey: string; autoCheck: string; patient: PatientResult | null; appointmentDate?: string | null } | null>(null);
|
||||
@@ -206,7 +159,6 @@ export function ChatbotButton() {
|
||||
const [pendingFiles, setPendingFiles] = useState<File[]>([]);
|
||||
const [, setLocation] = useLocation();
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const pasteRef = useRef<HTMLTextAreaElement>(null);
|
||||
const freeTextRef = useRef<HTMLTextAreaElement>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -244,9 +196,6 @@ export function ChatbotButton() {
|
||||
}, [messages]);
|
||||
|
||||
useEffect(() => {
|
||||
if (step === "eligibility-input") {
|
||||
setTimeout(() => pasteRef.current?.focus(), 50);
|
||||
}
|
||||
if (step === "menu") {
|
||||
setTimeout(() => freeTextRef.current?.focus(), 50);
|
||||
}
|
||||
@@ -266,9 +215,6 @@ export function ChatbotButton() {
|
||||
// Resets step/data only — keeps message history
|
||||
const resetStep = () => {
|
||||
setStep("menu");
|
||||
setPasteInput("");
|
||||
setParseError("");
|
||||
setEligibilityData(null);
|
||||
setFreeTextInput("");
|
||||
setPatientResult(null);
|
||||
setEligibilityIdData(null);
|
||||
@@ -300,7 +246,7 @@ export function ChatbotButton() {
|
||||
resetStep();
|
||||
};
|
||||
|
||||
const handleOptionSelect = (option: "eligibility" | "schedule" | "claims") => {
|
||||
const handleOptionSelect = (option: "schedule" | "claims") => {
|
||||
if (option === "schedule") {
|
||||
addMsg("user", "Schedule an appointment");
|
||||
addMsg("bot", "Opening the appointments page...");
|
||||
@@ -309,67 +255,9 @@ export function ChatbotButton() {
|
||||
addMsg("user", "View claims");
|
||||
addMsg("bot", "Opening the claims page...");
|
||||
setTimeout(() => { setLocation("/claims"); setOpen(false); resetStep(); }, 600);
|
||||
} else if (option === "eligibility") {
|
||||
addMsg("user", "Check Eligibility");
|
||||
addMsg("bot", "Please enter the patient's Member ID and Date of Birth:");
|
||||
setStep("eligibility-input");
|
||||
}
|
||||
};
|
||||
|
||||
const handleEligibilitySubmit = () => {
|
||||
const parsed = parseEligibilityInput(pasteInput);
|
||||
if (!parsed) {
|
||||
setParseError("Couldn't find both a Member ID and a date. Try: 123456789 01/15/1985");
|
||||
return;
|
||||
}
|
||||
setParseError("");
|
||||
const data: EligibilityData = { memberId: parsed.memberId, dob: parsed.display, dobISO: parsed.iso };
|
||||
setEligibilityData(data);
|
||||
addMsg("user", pasteInput.trim());
|
||||
addMsg("bot", `Ready to check MassHealth eligibility for:\n• Member ID: ${parsed.memberId}\n• Date of Birth: ${parsed.display}\n\nShall I proceed?`);
|
||||
setStep("eligibility-confirm");
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!eligibilityData) return;
|
||||
addMsg("user", "Yes, check now");
|
||||
addMsg("bot", "Opening the eligibility check page with this patient...");
|
||||
sessionStorage.setItem("chatbot_eligibility", JSON.stringify({
|
||||
memberId: eligibilityData.memberId,
|
||||
dob: eligibilityData.dobISO,
|
||||
autoCheck: getAutoCheck(eligibilityData.dobISO),
|
||||
}));
|
||||
window.dispatchEvent(new CustomEvent("chatbot:eligibility-prefill"));
|
||||
setTimeout(() => { setLocation("/insurance-status"); setOpen(false); resetStep(); }, 600);
|
||||
};
|
||||
|
||||
const handleEligibilityFromPatient = () => {
|
||||
if (!patientResult?.insuranceId || !patientResult?.dateOfBirth) return;
|
||||
addMsg("user", "Check eligibility now");
|
||||
addMsg("bot", "Opening the eligibility check page...");
|
||||
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...");
|
||||
}
|
||||
prefillAndNavigate(patientResult.insuranceId, patientResult.dateOfBirth, getAutoCheck(patientResult.dateOfBirth));
|
||||
};
|
||||
|
||||
const prefillAndNavigate = (memberId: string, dobISO: string, autoCheck: string) => {
|
||||
sessionStorage.setItem("chatbot_eligibility", JSON.stringify({ memberId, dob: dobISO, autoCheck }));
|
||||
window.dispatchEvent(new CustomEvent("chatbot:eligibility-prefill"));
|
||||
@@ -728,14 +616,7 @@ export function ChatbotButton() {
|
||||
|
||||
{/* Quick-action buttons (menu step) */}
|
||||
{step === "menu" && (
|
||||
<div className="grid grid-cols-2 gap-2 pt-1">
|
||||
<button
|
||||
onClick={() => handleOptionSelect("eligibility")}
|
||||
className="flex items-center gap-2 p-3 rounded-xl border border-primary/30 hover:bg-primary/5 text-sm text-left transition-colors"
|
||||
>
|
||||
<Stethoscope className="h-4 w-4 text-primary shrink-0" />
|
||||
<span>Check Eligibility</span>
|
||||
</button>
|
||||
<div className="grid grid-cols-1 gap-2 pt-1">
|
||||
<button
|
||||
onClick={() => handleOptionSelect("schedule")}
|
||||
className="flex items-center gap-2 p-3 rounded-xl border border-blue-300 hover:bg-blue-50 text-sm text-left transition-colors"
|
||||
@@ -745,7 +626,7 @@ export function ChatbotButton() {
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleOptionSelect("claims")}
|
||||
className="flex items-center gap-2 p-3 rounded-xl border border-orange-300 hover:bg-orange-50 text-sm text-left transition-colors col-span-2"
|
||||
className="flex items-center gap-2 p-3 rounded-xl border border-orange-300 hover:bg-orange-50 text-sm text-left transition-colors"
|
||||
>
|
||||
<FileText className="h-4 w-4 text-orange-500 shrink-0" />
|
||||
<span>Claims</span>
|
||||
@@ -753,58 +634,6 @@ export function ChatbotButton() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Eligibility manual input */}
|
||||
{step === "eligibility-input" && (
|
||||
<div className="space-y-2 bg-gray-50 rounded-xl p-3 border">
|
||||
<Label className="text-xs text-gray-600">Paste Member ID and Date of Birth</Label>
|
||||
<textarea
|
||||
ref={pasteRef}
|
||||
rows={3}
|
||||
placeholder={"e.g. 123456789 01/15/1985\nor 01/15/1985 123456789"}
|
||||
value={pasteInput}
|
||||
onChange={(e) => { setPasteInput(e.target.value); setParseError(""); }}
|
||||
className={cn(
|
||||
"w-full rounded-md border bg-white px-3 py-2 text-sm resize-none focus:outline-none focus:ring-2 focus:ring-ring",
|
||||
parseError && "border-red-400"
|
||||
)}
|
||||
/>
|
||||
{parseError && <p className="text-xs text-red-500">{parseError}</p>}
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
className="flex-1 h-8 text-xs"
|
||||
onClick={handleEligibilitySubmit}
|
||||
disabled={!pasteInput.trim()}
|
||||
>
|
||||
Continue <ChevronRight className="h-3 w-3 ml-1" />
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" className="h-8 text-xs" onClick={resetStep}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Eligibility confirm */}
|
||||
{step === "eligibility-confirm" && (
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" className="flex-1 h-8 text-xs" onClick={handleConfirm}>
|
||||
Yes, Check Now
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-8 text-xs"
|
||||
onClick={() => {
|
||||
setStep("eligibility-input");
|
||||
setMessages((prev) => prev.slice(0, -2));
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* AI-found patient card */}
|
||||
{step === "patient-found" && patientResult && (
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-xl p-3 space-y-2">
|
||||
@@ -821,26 +650,6 @@ export function ChatbotButton() {
|
||||
<p className="text-xs text-gray-500">DOB: {patientResult.dateOfBirth}</p>
|
||||
)}
|
||||
<div className="flex flex-col gap-2 pt-1">
|
||||
{patientResult.insuranceId && patientResult.dateOfBirth && (
|
||||
<>
|
||||
<Button
|
||||
size="sm"
|
||||
className="w-full h-8 text-xs bg-primary hover:bg-primary/90"
|
||||
onClick={handleEligibilityFromPatient}
|
||||
>
|
||||
<Stethoscope className="h-3 w-3 mr-1" />
|
||||
Check Eligibility
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="w-full h-8 text-xs bg-emerald-600 hover:bg-emerald-700 text-white"
|
||||
onClick={handleEligibilityAndAppointmentFromPatient}
|
||||
>
|
||||
<Calendar className="h-3 w-3 mr-1" />
|
||||
Eligibility & Appointment Today
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button size="sm" variant="ghost" className="w-full h-8 text-xs" onClick={resetStep}>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
@@ -46,7 +46,7 @@ export function TopAppBar() {
|
||||
</svg>
|
||||
|
||||
<h1 className="text-lg font-medium text-primary">
|
||||
My Dental Office Management
|
||||
Wellspring Acupuncture Office Management
|
||||
</h1>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
@@ -40,6 +40,27 @@ export function DateInput({
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
const handleBlur = () => {
|
||||
// If the user finished typing a 2-digit year (e.g. "02/12/03") the live-typing
|
||||
// logic below never commits it, since it waits for a 4-digit year. On blur,
|
||||
// expand a trailing 2-digit year so the date isn't silently dropped.
|
||||
const match = inputValue.match(/^(\d{1,2})\/(\d{1,2})\/(\d{2})$/);
|
||||
if (!match) return;
|
||||
|
||||
const [, mmStr, ddStr, yyStr] = match;
|
||||
const mm = mmStr.padStart(2, "0");
|
||||
const dd = ddStr.padStart(2, "0");
|
||||
const currentYearLastTwo = new Date().getFullYear() % 100;
|
||||
const yy = parseInt(yyStr, 10);
|
||||
const yyyy = yy <= currentYearLastTwo ? 2000 + yy : 1900 + yy;
|
||||
|
||||
const parsed = new Date(yyyy, +mm - 1, +dd);
|
||||
if (!isNaN(parsed.getTime())) {
|
||||
setInputValue(format(parsed, "MM/dd/yyyy"));
|
||||
onChange(parsed);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const raw = e.target.value;
|
||||
|
||||
@@ -148,6 +169,7 @@ export function DateInput({
|
||||
placeholder="MM/DD/YYYY"
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
|
||||
@@ -1120,22 +1120,10 @@ export default function AppointmentsPage() {
|
||||
const patient = apt ? patientsFromDay.find((p) => p.id === apt.patientId) : null;
|
||||
const insuranceProvider = (patient as any)?.insuranceProvider as string | null ?? null;
|
||||
const p = insuranceProvider?.toLowerCase() ?? "";
|
||||
const isMassHealth = p.includes("masshealth");
|
||||
|
||||
let autoCheck = "other-providers";
|
||||
|
||||
if (isMassHealth) {
|
||||
const dob = (patient as any)?.dateOfBirth;
|
||||
let age: number | null = null;
|
||||
if (dob) {
|
||||
const dobDate = typeof dob === "string" ? new Date(dob) : dob as Date;
|
||||
const today = new Date();
|
||||
age = today.getFullYear() - dobDate.getFullYear();
|
||||
const m = today.getMonth() - dobDate.getMonth();
|
||||
if (m < 0 || (m === 0 && today.getDate() < dobDate.getDate())) age--;
|
||||
}
|
||||
autoCheck = age !== null && age >= 21 ? "mh-history" : "cmsp";
|
||||
} else if (p.includes("delta dental ma")) {
|
||||
if (p.includes("delta dental ma")) {
|
||||
autoCheck = "ddma";
|
||||
} else if (p.includes("delta dental ins")) {
|
||||
autoCheck = "delta-ins";
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function AuthPage() {
|
||||
<Card className="p-8 bg-white">
|
||||
<div className="mb-10 text-center">
|
||||
<h1 className="text-3xl font-bold text-primary mb-3 tracking-tight">
|
||||
My Dental Office Management
|
||||
Wellspring Acupuncture Office Management
|
||||
</h1>
|
||||
<p className="text-sm font-semibold tracking-[0.15em] bg-clip-text text-transparent animate-gradient-flash"
|
||||
style={{ backgroundImage: "linear-gradient(90deg, #7c3aed, #d946ef, #06b6d4, #7c3aed)", backgroundSize: "200% 100%" }}>
|
||||
|
||||
@@ -28,13 +28,12 @@ import {
|
||||
clearTaskStatus,
|
||||
} from "@/redux/slices/seleniumTaskSlice";
|
||||
import { SeleniumTaskBanner } from "@/components/ui/selenium-task-banner";
|
||||
import { formatLocalDate, parseLocalDate } from "@/utils/dateUtils";
|
||||
import { 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";
|
||||
@@ -111,15 +110,6 @@ export default function InsuranceStatusPage() {
|
||||
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);
|
||||
@@ -147,27 +137,7 @@ export default function InsuranceStatusPage() {
|
||||
string | null
|
||||
>(null);
|
||||
|
||||
// Dual PDF modal state (used by MH Eligibility & History)
|
||||
const [dualPreviewOpen, setDualPreviewOpen] = useState(false);
|
||||
const [dualEligibilityPdfId, setDualEligibilityPdfId] = useState<number | null>(null);
|
||||
const [dualEligibilityFilename, setDualEligibilityFilename] = useState<string | null>(null);
|
||||
const [dualMemberDetailsPdfId, setDualMemberDetailsPdfId] = useState<number | null>(null);
|
||||
const [dualMemberDetailsFilename, setDualMemberDetailsFilename] = useState<string | null>(null);
|
||||
const [dualHistoryPdfId, setDualHistoryPdfId] = useState<number | null>(null);
|
||||
const [dualHistoryFilename, setDualHistoryFilename] = useState<string | null>(null);
|
||||
|
||||
// CMSP PDF modal state (used by CMSP Eligibility & History & Remaining)
|
||||
const [cmspPreviewOpen, setCmspPreviewOpen] = useState(false);
|
||||
const [cmspEligibilityPdfId, setCmspEligibilityPdfId] = useState<number | null>(null);
|
||||
const [cmspEligibilityFilename, setCmspEligibilityFilename] = useState<string | null>(null);
|
||||
const [cmspMemberDetailsPdfId, setCmspMemberDetailsPdfId] = useState<number | null>(null);
|
||||
const [cmspMemberDetailsFilename, setCmspMemberDetailsFilename] = useState<string | null>(null);
|
||||
const [cmspHistoryPdfId, setCmspHistoryPdfId] = useState<number | null>(null);
|
||||
const [cmspHistoryFilename, setCmspHistoryFilename] = useState<string | null>(null);
|
||||
const [cmspAccumulatorPdfId, setCmspAccumulatorPdfId] = useState<number | null>(null);
|
||||
const [cmspAccumulatorFilename, setCmspAccumulatorFilename] = useState<string | null>(null);
|
||||
|
||||
const pendingAutoCheck = useRef<"mh" | "mh-history" | "cmsp" | "ddma" | "delta-ins" | "united-sco" | "tufts-sco" | "cca" | null>(null);
|
||||
const pendingAutoCheck = useRef<"ddma" | "delta-ins" | "united-sco" | "tufts-sco" | "cca" | null>(null);
|
||||
const [triggerTarget, setTriggerTarget] = useState<string | null>(null);
|
||||
const pendingScrollTo = useRef<string | null>(null);
|
||||
const [prefillTick, setPrefillTick] = useState(0);
|
||||
@@ -280,56 +250,6 @@ export default function InsuranceStatusPage() {
|
||||
},
|
||||
});
|
||||
|
||||
const isUnder14 = (dob: Date): boolean => {
|
||||
const cutoff = new Date(dob);
|
||||
cutoff.setFullYear(cutoff.getFullYear() + 14);
|
||||
return new Date() < cutoff;
|
||||
};
|
||||
|
||||
// Shared: run MH eligibility selenium job, return jobResult or throw
|
||||
const runMHEligibilitySelenium = async (): Promise<any> => {
|
||||
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,
|
||||
@@ -343,347 +263,13 @@ export default function InsuranceStatusPage() {
|
||||
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;
|
||||
}
|
||||
|
||||
if (isUnder14(dateOfBirth)) {
|
||||
toast({ title: "CMSP Patient", description: "Patient is under 14 — running CMSP Eligibility & History & Remaining instead.", variant: "default" });
|
||||
return handleCMSPButton();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
processNextInQueue();
|
||||
} 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;
|
||||
}
|
||||
|
||||
if (isUnder14(dateOfBirth)) {
|
||||
toast({ title: "CMSP Patient", description: "Patient is under 14 — running CMSP Eligibility & History & Remaining instead.", variant: "default" });
|
||||
return handleCMSPButton();
|
||||
}
|
||||
|
||||
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 });
|
||||
processNextInQueue();
|
||||
|
||||
// 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 });
|
||||
processNextInQueue();
|
||||
|
||||
// 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") {
|
||||
if (isUnder14(dateOfBirth)) {
|
||||
handleCMSPButton();
|
||||
} else {
|
||||
handleMHEligibilityHistoryButton();
|
||||
}
|
||||
} else if (check === "cmsp") {
|
||||
handleCMSPButton();
|
||||
} else {
|
||||
// Button-component-handled providers: pass target to their autoTrigger prop
|
||||
setTriggerTarget(check);
|
||||
}
|
||||
// Button-component-handled providers: pass target to their autoTrigger prop
|
||||
setTriggerTarget(check);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [memberId, dateOfBirth, prefillTick]);
|
||||
|
||||
@@ -820,7 +406,7 @@ export default function InsuranceStatusPage() {
|
||||
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;
|
||||
const knownAutoChecks = ["ddma", "delta-ins", "united-sco", "tufts-sco", "cca"] as const;
|
||||
if (autoCheck && (knownAutoChecks as readonly string[]).includes(autoCheck)) {
|
||||
pendingAutoCheck.current = autoCheck;
|
||||
} else if (scrollTo) {
|
||||
@@ -932,102 +518,6 @@ export default function InsuranceStatusPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col-2 gap-4">
|
||||
<Button
|
||||
onClick={() => handleMHEligibilityButton()}
|
||||
className="w-full"
|
||||
disabled={isCheckingEligibilityStatus}
|
||||
>
|
||||
{isCheckingEligibilityStatus ? (
|
||||
<>
|
||||
<LoaderCircleIcon className="h-4 w-4 mr-2 animate-spin" />
|
||||
Processing...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
MH Eligibility
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => handleMHEligibilityAppointmentButton()}
|
||||
className="w-full"
|
||||
disabled={isCheckingEligibilityAppointment}
|
||||
>
|
||||
{isCheckingEligibilityAppointment ? (
|
||||
<>
|
||||
<LoaderCircleIcon className="h-4 w-4 mr-2 animate-spin" />
|
||||
Processing...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
MH Eligibility&Appointment
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => handleMHEligibilityClaimsPreAuthButton()}
|
||||
className="w-full"
|
||||
disabled={isCheckingEligibilityClaimsPreAuth}
|
||||
>
|
||||
{isCheckingEligibilityClaimsPreAuth ? (
|
||||
<>
|
||||
<LoaderCircleIcon className="h-4 w-4 mr-2 animate-spin" />
|
||||
Processing...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
MH Eligibility&Claims/PreAuth
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col-2 gap-4 mt-4">
|
||||
<Button
|
||||
id="eligibility-mh-history"
|
||||
className="w-full"
|
||||
disabled={isCheckingEligibilityHistory}
|
||||
onClick={() => handleMHEligibilityHistoryButton()}
|
||||
>
|
||||
{isCheckingEligibilityHistory ? (
|
||||
<>
|
||||
<LoaderCircleIcon className="h-4 w-4 mr-2 animate-spin" />
|
||||
Processing...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
MH Eligibility & History
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
id="eligibility-cmsp"
|
||||
className="w-full"
|
||||
disabled={isCheckingCMSP}
|
||||
onClick={() => handleCMSPButton()}
|
||||
>
|
||||
{isCheckingCMSP ? (
|
||||
<>
|
||||
<LoaderCircleIcon className="h-4 w-4 mr-2 animate-spin" />
|
||||
Processing...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
CMSP Eligibility & History & Remaining
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* TEMP PROVIDER BUTTONS */}
|
||||
<div id="eligibility-other-providers" className="space-y-4 mt-6">
|
||||
<h3 className="text-sm font-medium text-muted-foreground">
|
||||
@@ -1169,81 +659,6 @@ export default function InsuranceStatusPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 3 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<Button
|
||||
className="w-full"
|
||||
variant="outline"
|
||||
disabled={isFormIncomplete}
|
||||
>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
United AAPR
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
className="w-full"
|
||||
variant="outline"
|
||||
disabled={isFormIncomplete}
|
||||
>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
Aetna
|
||||
</Button>
|
||||
<Button
|
||||
className="w-full"
|
||||
variant="outline"
|
||||
disabled={isFormIncomplete}
|
||||
>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
Altus
|
||||
</Button>
|
||||
<Button
|
||||
className="w-full"
|
||||
variant="outline"
|
||||
disabled={isFormIncomplete}
|
||||
>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
Metlife Dental
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
className="w-full"
|
||||
variant="outline"
|
||||
disabled={isFormIncomplete}
|
||||
>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
Cigna
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
className="w-full"
|
||||
variant="outline"
|
||||
disabled={isFormIncomplete}
|
||||
>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
Delta WA
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Row 5 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<Button
|
||||
className="w-full"
|
||||
variant="outline"
|
||||
disabled={isFormIncomplete}
|
||||
>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
Delta IL
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
className="w-full"
|
||||
variant="outline"
|
||||
disabled={isFormIncomplete}
|
||||
>
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
Others
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -1394,78 +809,6 @@ export default function InsuranceStatusPage() {
|
||||
autoDownload
|
||||
/>
|
||||
|
||||
{/* 4-panel modal for CMSP — eligibility, member details, history, accumulator */}
|
||||
<DualPdfPreviewModal
|
||||
open={cmspPreviewOpen}
|
||||
onClose={() => {
|
||||
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 */}
|
||||
<DualPdfPreviewModal
|
||||
open={dualPreviewOpen}
|
||||
onClose={() => {
|
||||
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",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user