Files
DentalManagementMH05/apps/Frontend/src/pages/insurance-status-page.tsx
Gitead 0628f9f7fc feat: add member details PDF step to MH history and CMSP flows
After clicking the member ID link, print the member details page via CDP
before navigating to service history. Adds member details as a panel in
the side-by-side PDF viewer: MH History shows 3 panels (eligibility,
member details, service history); CMSP shows 4 panels (eligibility,
member details, service history, accumulator).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:33:02 -04:00

1245 lines
45 KiB
TypeScript
Executable File

import { useEffect, 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";
export default function InsuranceStatusPage() {
const { user } = useAuth();
const { toast } = useToast();
const dispatch = useAppDispatch();
const { status, message, show } = useAppSelector(
(state) => state.seleniumTasks.eligibilityCheck,
);
const [selectedPatient, setSelectedPatient] = useState<Patient | null>(null);
const [location, setLocation] = useLocation();
// Insurance eligibility and claim check form fields
const [memberId, setMemberId] = useState("");
const [dateOfBirth, setDateOfBirth] = useState<Date | null>(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<string>("");
const [aiCallNotes, setAiCallNotes] = useState("");
type InsuranceContact = { id: number; name: string; phoneNumber?: string | null };
const { data: insuranceContacts = [] } = useQuery<InsuranceContact[]>({
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<number | null>(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<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);
// 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);
} 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<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 new Promise<any>((resolve, reject) => {
const handler = (payload: any) => {
if (String(payload.jobId) !== String(jobId)) return;
if (payload.status === "active") {
dispatch(
setTaskStatus({
key: "eligibilityCheck",
status: "pending",
message: payload.message ?? "Selenium running...",
}),
);
} else if (payload.status === "completed") {
socket.off("job:update", handler);
resolve(payload.result ?? {});
} else if (payload.status === "failed") {
socket.off("job:update", handler);
reject(new Error(payload.error ?? "Selenium job failed"));
}
};
socket.on("job:update", handler);
});
};
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 });
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, navigate to appointments
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. Staying on Eligibility page.",
}),
);
toast({
title: "Insurance Inactive",
description: "Patient insurance is inactive. Staying on Eligibility page.",
variant: "destructive",
});
await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE });
return;
}
dispatch(
setTaskStatus({
key: "eligibilityCheck",
status: "success",
message: "Eligibility checked and saved. Redirecting to Appointments...",
}),
);
toast({
title: "Eligibility checked.",
description: "Patient eligibility saved. Redirecting to Appointments.",
variant: "default",
});
setSelectedPatient(null);
await queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE });
setLocation("/appointments");
} 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 new Promise<any>((resolve, reject) => {
const handler = (payload: any) => {
if (String(payload.jobId) !== String(jobId)) return;
if (payload.status === "active") {
dispatch(setTaskStatus({ key: "eligibilityCheck", status: "pending", message: payload.message ?? "Selenium running..." }));
} else if (payload.status === "completed") {
socket.off("job:update", handler);
resolve(payload.result ?? {});
} else if (payload.status === "failed") {
socket.off("job:update", handler);
reject(new Error(payload.error ?? "Selenium job failed"));
}
};
socket.on("job:update", handler);
});
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",
});
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 new Promise<any>((resolve, reject) => {
const handler = (payload: any) => {
if (String(payload.jobId) !== String(jobId)) return;
if (payload.status === "active") {
dispatch(setTaskStatus({ key: "eligibilityCheck", status: "pending", message: payload.message ?? "Selenium running..." }));
} else if (payload.status === "completed") {
socket.off("job:update", handler);
resolve(payload.result ?? {});
} else if (payload.status === "failed") {
socket.off("job:update", handler);
reject(new Error(payload.error ?? "Selenium job failed"));
}
};
socket.on("job:update", handler);
});
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);
}
};
// 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
}
};
// handling case-1, when redirect happens from appointment page:
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const appointmentId = params.get("appointmentId");
const action = params.get("action"); // 'eligibility' | 'claim'
if (!appointmentId) return;
const id = Number(appointmentId);
if (Number.isNaN(id) || id <= 0) return;
if (!action || (action !== "eligibility" && action !== "claim")) return;
let cancelled = false;
(async () => {
try {
const res = await apiRequest("GET", `/api/appointments/${id}/patient`);
if (!res.ok) {
let body: any = null;
try {
body = await res.json();
} catch {}
if (!cancelled) {
toast({
title: "Failed to load patient",
description:
body?.message ??
body?.error ??
`Could not fetch patient for appointment ${id}.`,
variant: "destructive",
});
}
return;
}
const data = await res.json();
const patient = data?.patient ?? data;
if (!cancelled && patient) {
// set selectedPatient as before
setSelectedPatient(patient as Patient);
clearUrlParams(["appointmentId", "action"]);
}
} catch (err: any) {
if (!cancelled) {
console.error("Error fetching patient for appointment:", err);
toast({
title: "Error",
description:
err?.message ?? "An error occurred while fetching patient.",
variant: "destructive",
});
}
}
})();
return () => {
cancelled = true;
};
}, [location]);
// handling case-1, when redirect happens from appointment page:
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;
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) {
// ✅ ONLY prefill patient
setSelectedPatient(patient as Patient);
// ✅ clean URL (no auto selenium)
clearUrlParams(["appointmentId", "action"]);
}
} catch (err) {
console.error("Failed to fetch patient from appointment", err);
}
})();
return () => {
cancelled = true;
};
}, [location]);
return (
<div>
<SeleniumTaskBanner
status={status}
message={message}
show={show}
onClear={() => dispatch(clearTaskStatus("eligibilityCheck"))}
/>
<div className="container mx-auto space-y-6">
<div className="flex justify-between items-center">
<div>
<h1 className="text-3xl font-bold tracking-tight">
Insurance Eligibility
</h1>
<p className="text-muted-foreground">
Check insurance eligibility.
</p>
</div>
</div>
{/* Insurance Eligibility Check Form */}
<Card className="mb-6">
<CardContent>
<div className="grid grid-cols-4 md:grid-cols-4 gap-4 mb-4">
<div className="space-y-2">
<Label htmlFor="memberId">Member ID</Label>
<Input
id="memberId"
placeholder="Enter member ID"
value={memberId}
onChange={(e) => setMemberId(e.target.value)}
/>
</div>
<div className="space-y-2">
<DateInput
label="Date of Birth"
value={dateOfBirth}
onChange={setDateOfBirth}
disableFuture
/>
</div>
<div className="space-y-2">
<Label htmlFor="firstName">First Name <span className="text-muted-foreground font-normal">(Optional)</span></Label>
<Input
id="firstName"
placeholder="Enter first name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="lastName">Last Name <span className="text-muted-foreground font-normal">(Optional)</span></Label>
<Input
id="lastName"
placeholder="Enter last name"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
/>
</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
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 &amp; History
</>
)}
</Button>
<Button
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 &amp; History &amp; Remaining
</>
)}
</Button>
</div>
{/* TEMP PROVIDER BUTTONS */}
<div className="space-y-4 mt-6">
<h3 className="text-sm font-medium text-muted-foreground">
Other provider checks
</h3>
{/* Row 1 */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<DdmaEligibilityButton
memberId={memberId}
dateOfBirth={dateOfBirth}
firstName={firstName}
lastName={lastName}
isFormIncomplete={isFormIncomplete}
onPdfReady={(pdfId, fallbackFilename) => {
setPreviewPdfId(pdfId);
setPreviewFallbackFilename(
fallbackFilename ?? `eligibility_ddma_${memberId}.pdf`,
);
setPreviewOpen(true);
}}
/>
<DeltaInsEligibilityButton
memberId={memberId}
dateOfBirth={dateOfBirth}
firstName={firstName}
lastName={lastName}
isFormIncomplete={isFormIncomplete}
onPdfReady={(pdfId, fallbackFilename) => {
setPreviewPdfId(pdfId);
setPreviewFallbackFilename(
fallbackFilename ?? `eligibility_deltains_${memberId}.pdf`,
);
setPreviewOpen(true);
}}
/>
<Button
className="w-full"
variant="outline"
disabled={isFormIncomplete}
>
<CheckCircle className="h-4 w-4 mr-2" />
BCBS
</Button>
</div>
{/* Row 2 */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<TuftsSCOEligibilityButton
memberId={memberId}
dateOfBirth={dateOfBirth}
firstName={firstName}
lastName={lastName}
isFormIncomplete={isFormIncomplete}
onPdfReady={(pdfId, fallbackFilename) => {
setPreviewPdfId(pdfId);
setPreviewFallbackFilename(
fallbackFilename ?? `eligibility_unitedsco_${memberId}.pdf`,
);
setPreviewOpen(true);
}}
/>
<UnitedSCOEligibilityButton
memberId={memberId}
dateOfBirth={dateOfBirth}
firstName={firstName}
lastName={lastName}
isFormIncomplete={isFormIncomplete}
onPdfReady={(pdfId, fallbackFilename) => {
setPreviewPdfId(pdfId);
setPreviewFallbackFilename(
fallbackFilename ?? `eligibility_unitedsco_${memberId}.pdf`,
);
setPreviewOpen(true);
}}
/>
<CCAEligibilityButton
memberId={memberId}
dateOfBirth={dateOfBirth}
firstName={firstName}
lastName={lastName}
isFormIncomplete={isFormIncomplete}
onPdfReady={(pdfId, fallbackFilename) => {
setPreviewPdfId(pdfId);
setPreviewFallbackFilename(
fallbackFilename ?? `eligibility_cca_${memberId}.pdf`,
);
setPreviewOpen(true);
}}
/>
</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>
{/* AI Call Insurance */}
<Card>
<CardHeader
className="cursor-pointer select-none"
onClick={() => setAiCallOpen((o) => !o)}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="h-9 w-9 rounded-lg bg-violet-100 flex items-center justify-center flex-shrink-0">
<Bot className="h-5 w-5 text-violet-600" />
</div>
<div>
<CardTitle className="text-base">AI Call Insurance/Transportation</CardTitle>
<CardDescription className="mt-0.5">
Use AI to call the insurance company and check eligibility automatically
</CardDescription>
</div>
</div>
{aiCallOpen
? <ChevronUp className="h-4 w-4 text-muted-foreground" />
: <ChevronDown className="h-4 w-4 text-muted-foreground" />}
</div>
</CardHeader>
{aiCallOpen && (
<CardContent className="space-y-5 pt-0">
{/* Patient context banner */}
{selectedPatient && (
<div className="flex items-center gap-2 text-sm bg-violet-50 border border-violet-200 rounded-md px-3 py-2">
<Bot className="h-4 w-4 text-violet-500 flex-shrink-0" />
<span>
Selected patient:{" "}
<span className="font-medium">
{selectedPatient.firstName} {selectedPatient.lastName}
</span>
{selectedPatient.insuranceId && (
<> &mdash; Member ID: <span className="font-medium">{selectedPatient.insuranceId}</span></>
)}
</span>
</div>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-1.5">
<Label>Insurance Company</Label>
{insuranceContacts.length === 0 ? (
<p className="text-sm text-muted-foreground italic py-2">
No insurance contacts saved.{" "}
<a href="/settings/insurancecontact" className="underline text-violet-600">
Add one in Settings Insurance Contact
</a>
</p>
) : (
<Select
value={aiSelectedContactId}
onValueChange={setAiSelectedContactId}
>
<SelectTrigger>
<SelectValue placeholder="Select insurance company…" />
</SelectTrigger>
<SelectContent>
{insuranceContacts.map((c) => (
<SelectItem key={c.id} value={String(c.id)}>
{c.name}
</SelectItem>
))}
</SelectContent>
</Select>
)}
</div>
<div className="space-y-1.5">
<Label>Phone Number</Label>
<Input
readOnly
value={selectedInsuranceContact?.phoneNumber ?? ""}
placeholder="Auto-filled from insurance contact"
className="bg-gray-50 text-gray-600 cursor-default"
/>
</div>
<div className="space-y-1.5 sm:col-span-2">
<Label htmlFor="ai-call-notes">
Additional Notes for AI{" "}
<span className="text-muted-foreground font-normal">(Optional)</span>
</Label>
<Input
id="ai-call-notes"
placeholder="e.g. Ask about dental benefits and annual maximum"
value={aiCallNotes}
onChange={(e) => setAiCallNotes(e.target.value)}
/>
</div>
</div>
<div className="flex items-center gap-3">
<Button
disabled
className="gap-2 bg-violet-600 hover:bg-violet-700 text-white opacity-60 cursor-not-allowed"
>
<PhoneCall className="h-4 w-4" />
Start AI Call
</Button>
<p className="text-xs text-muted-foreground italic">
{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"}
</p>
</div>
</CardContent>
)}
</Card>
{/* Patients Table */}
<Card>
<CardHeader>
<CardTitle>Patient Records</CardTitle>
<CardDescription>
Select Patients and Check Their Eligibility
</CardDescription>
</CardHeader>
<CardContent>
<PatientTable
allowView={true}
allowDelete={true}
allowCheckbox={true}
allowEdit={true}
onSelectPatient={setSelectedPatient}
/>
</CardContent>
</Card>
</div>
{/* Pdf preview modal */}
<PdfPreviewModal
open={previewOpen}
onClose={() => {
setPreviewOpen(false);
setPreviewPdfId(null);
setPreviewFallbackFilename(null);
}}
pdfId={previewPdfId ?? undefined}
fallbackFilename={previewFallbackFilename ?? undefined}
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 &amp; 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 &amp; 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>
);
}