feat: auto-trigger eligibility selenium from schedule right-click menu

- Remove "Claim Status" from appointment context menu
- Rename "Eligibility Status" → "Check Eligibility"
- Check Eligibility now navigates to insurance-status page and auto-starts
  the correct selenium flow based on the patient's stored insurance provider:
  MassHealth 21+ → MH Eligibility & History
  MassHealth <21  → CMSP Eligibility & History & Remaining
  Delta Dental MA → DDMA selenium
  Delta Dental Ins → Delta Ins selenium (OTP modal if needed)
  United Healthcare SCO → United SCO selenium
  DentaQuest/Tufts → Tufts SCO selenium
  Commonwealth Care Alliance → CCA selenium
  Unknown → scroll to Other provider checks section
- Add autoTrigger/onAutoTriggered props to all five button components

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gitead
2026-05-17 00:12:09 -04:00
co-authored by Claude Sonnet 4.6
parent 4127861d06
commit edec03e893
7 changed files with 154 additions and 93 deletions
+36 -19
View File
@@ -955,15 +955,42 @@ export default function AppointmentsPage() {
// -------------------
// appointment page — update these handlers
const handleCheckEligibility = (appointmentId: number) => {
setLocation(
`/insurance-status?appointmentId=${appointmentId}&action=eligibility`
);
};
const apt = appointments.find((a) => a.id === appointmentId);
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");
const handleCheckClaimStatus = (appointmentId: number) => {
setLocation(
`/insurance-status?appointmentId=${appointmentId}&action=claim`
);
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")) {
autoCheck = "ddma";
} else if (p.includes("delta dental ins")) {
autoCheck = "delta-ins";
} else if (p.includes("united healthcare sco") || p.includes("united sco")) {
autoCheck = "united-sco";
} else if (p.includes("tufts") || p.includes("dentaquest")) {
autoCheck = "tufts-sco";
} else if (p.includes("commonwealth care alliance") || p.includes("cca")) {
autoCheck = "cca";
}
if (autoCheck === "other-providers") {
setLocation(`/insurance-status?appointmentId=${appointmentId}&scrollTo=other-providers`);
} else {
setLocation(`/insurance-status?appointmentId=${appointmentId}&autoCheck=${autoCheck}`);
}
};
const handleClaimsPreAuth = (appointmentId: number) => {
@@ -1602,7 +1629,7 @@ export default function AppointmentsPage() {
>
<span className="flex items-center gap-2">
<Shield className="h-4 w-4" />
Eligibility Status
Check Eligibility
</span>
</Item>
@@ -1650,16 +1677,6 @@ export default function AppointmentsPage() {
</span>
</Item>
{/* Claim Status */}
<Item
onClick={({ props }) => handleCheckClaimStatus(props.appointmentId)}
>
<span className="flex items-center gap-2">
<Shield className="h-4 w-4" />
Claim Status
</span>
</Item>
{/* Chat */}
<Item onClick={({ props }) => handleChat(props.appointmentId)}>
<span className="flex items-center gap-2 text-blue-600">
@@ -164,7 +164,9 @@ export default function InsuranceStatusPage() {
const [cmspAccumulatorPdfId, setCmspAccumulatorPdfId] = useState<number | null>(null);
const [cmspAccumulatorFilename, setCmspAccumulatorFilename] = useState<string | null>(null);
const pendingAutoCheck = useRef<"mh" | "cmsp" | null>(null);
const pendingAutoCheck = useRef<"mh" | "mh-history" | "cmsp" | "ddma" | "delta-ins" | "united-sco" | "tufts-sco" | "cca" | null>(null);
const [triggerTarget, setTriggerTarget] = useState<string | null>(null);
const pendingScrollTo = useRef<string | null>(null);
// Prefill from chatbot
useEffect(() => {
@@ -603,15 +605,18 @@ export default function InsuranceStatusPage() {
}
};
// Auto-trigger from chatbot after prefill
// 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") {
if (check === "mh" || check === "mh-history") {
handleMHEligibilityHistoryButton();
} else {
} 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]);
@@ -635,66 +640,7 @@ export default function InsuranceStatusPage() {
}
};
// 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:
// 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");
@@ -703,6 +649,16 @@ export default function InsuranceStatusPage() {
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 () => {
@@ -714,11 +670,8 @@ export default function InsuranceStatusPage() {
const patient = data?.patient ?? data;
if (!cancelled && patient) {
// ✅ ONLY prefill patient
setSelectedPatient(patient as Patient);
// ✅ clean URL (no auto selenium)
clearUrlParams(["appointmentId", "action"]);
clearUrlParams(["appointmentId", "action", "autoCheck", "scrollTo"]);
}
} catch (err) {
console.error("Failed to fetch patient from appointment", err);
@@ -730,6 +683,23 @@ export default function InsuranceStatusPage() {
};
}, [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 (
<div>
<SeleniumTaskBanner
@@ -852,6 +822,7 @@ export default function InsuranceStatusPage() {
<div className="flex flex-col-2 gap-4 mt-4">
<Button
id="eligibility-mh-history"
className="w-full"
disabled={isCheckingEligibilityHistory}
onClick={() => handleMHEligibilityHistoryButton()}
@@ -870,6 +841,7 @@ export default function InsuranceStatusPage() {
</Button>
<Button
id="eligibility-cmsp"
className="w-full"
disabled={isCheckingCMSP}
onClick={() => handleCMSPButton()}
@@ -889,7 +861,7 @@ export default function InsuranceStatusPage() {
</div>
{/* TEMP PROVIDER BUTTONS */}
<div className="space-y-4 mt-6">
<div id="eligibility-other-providers" className="space-y-4 mt-6">
<h3 className="text-sm font-medium text-muted-foreground">
Other provider checks
</h3>
@@ -902,6 +874,8 @@ export default function InsuranceStatusPage() {
firstName={firstName}
lastName={lastName}
isFormIncomplete={isFormIncomplete}
autoTrigger={triggerTarget === "ddma"}
onAutoTriggered={() => setTriggerTarget(null)}
onPdfReady={(pdfId, fallbackFilename) => {
setPreviewPdfId(pdfId);
setPreviewFallbackFilename(
@@ -917,6 +891,8 @@ export default function InsuranceStatusPage() {
firstName={firstName}
lastName={lastName}
isFormIncomplete={isFormIncomplete}
autoTrigger={triggerTarget === "delta-ins"}
onAutoTriggered={() => setTriggerTarget(null)}
onPdfReady={(pdfId, fallbackFilename) => {
setPreviewPdfId(pdfId);
setPreviewFallbackFilename(
@@ -944,10 +920,12 @@ export default function InsuranceStatusPage() {
firstName={firstName}
lastName={lastName}
isFormIncomplete={isFormIncomplete}
autoTrigger={triggerTarget === "tufts-sco"}
onAutoTriggered={() => setTriggerTarget(null)}
onPdfReady={(pdfId, fallbackFilename) => {
setPreviewPdfId(pdfId);
setPreviewFallbackFilename(
fallbackFilename ?? `eligibility_unitedsco_${memberId}.pdf`,
fallbackFilename ?? `eligibility_tuftssco_${memberId}.pdf`,
);
setPreviewOpen(true);
}}
@@ -959,6 +937,8 @@ export default function InsuranceStatusPage() {
firstName={firstName}
lastName={lastName}
isFormIncomplete={isFormIncomplete}
autoTrigger={triggerTarget === "united-sco"}
onAutoTriggered={() => setTriggerTarget(null)}
onPdfReady={(pdfId, fallbackFilename) => {
setPreviewPdfId(pdfId);
setPreviewFallbackFilename(
@@ -974,6 +954,8 @@ export default function InsuranceStatusPage() {
firstName={firstName}
lastName={lastName}
isFormIncomplete={isFormIncomplete}
autoTrigger={triggerTarget === "cca"}
onAutoTriggered={() => setTriggerTarget(null)}
onPdfReady={(pdfId, fallbackFilename) => {
setPreviewPdfId(pdfId);
setPreviewFallbackFilename(