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:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user