feat: rename MH Status to MH Eligibility&Appointment, add MH Eligibility&Claims/PreAuth button
This commit is contained in:
@@ -42,7 +42,7 @@ export default function InsuranceStatusPage() {
|
|||||||
(state) => state.seleniumTasks.eligibilityCheck,
|
(state) => state.seleniumTasks.eligibilityCheck,
|
||||||
);
|
);
|
||||||
const [selectedPatient, setSelectedPatient] = useState<Patient | null>(null);
|
const [selectedPatient, setSelectedPatient] = useState<Patient | null>(null);
|
||||||
const [location] = useLocation();
|
const [location, setLocation] = useLocation();
|
||||||
|
|
||||||
// Insurance eligibility and claim check form fields
|
// Insurance eligibility and claim check form fields
|
||||||
const [memberId, setMemberId] = useState("");
|
const [memberId, setMemberId] = useState("");
|
||||||
@@ -52,7 +52,10 @@ export default function InsuranceStatusPage() {
|
|||||||
const isFormIncomplete = !memberId || !dateOfBirth;
|
const isFormIncomplete = !memberId || !dateOfBirth;
|
||||||
const [isCheckingEligibilityStatus, setIsCheckingEligibilityStatus] =
|
const [isCheckingEligibilityStatus, setIsCheckingEligibilityStatus] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [isCheckingClaimStatus, setIsCheckingClaimStatus] = useState(false);
|
const [isCheckingEligibilityAppointment, setIsCheckingEligibilityAppointment] =
|
||||||
|
useState(false);
|
||||||
|
const [isCheckingEligibilityClaimsPreAuth, setIsCheckingEligibilityClaimsPreAuth] =
|
||||||
|
useState(false);
|
||||||
|
|
||||||
// PDF preview modal state
|
// PDF preview modal state
|
||||||
const [previewOpen, setPreviewOpen] = useState(false);
|
const [previewOpen, setPreviewOpen] = useState(false);
|
||||||
@@ -114,8 +117,8 @@ export default function InsuranceStatusPage() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// handle eligibility selenium
|
// Shared: run MH eligibility selenium job, return jobResult or throw
|
||||||
const handleEligibilityCheckSelenium = async () => {
|
const runMHEligibilitySelenium = async (): Promise<any> => {
|
||||||
const formattedDob = dateOfBirth ? formatLocalDate(dateOfBirth) : "";
|
const formattedDob = dateOfBirth ? formatLocalDate(dateOfBirth) : "";
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
@@ -125,197 +128,55 @@ export default function InsuranceStatusPage() {
|
|||||||
firstName: firstName || undefined,
|
firstName: firstName || undefined,
|
||||||
lastName: lastName || undefined,
|
lastName: lastName || undefined,
|
||||||
};
|
};
|
||||||
try {
|
|
||||||
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;
|
dispatch(
|
||||||
if (!jobId) throw new Error("No jobId returned from server");
|
setTaskStatus({
|
||||||
|
key: "eligibilityCheck",
|
||||||
|
status: "pending",
|
||||||
|
message: "Sending Data to Selenium...",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
dispatch(
|
const response = await apiRequest(
|
||||||
setTaskStatus({
|
"POST",
|
||||||
key: "eligibilityCheck",
|
"/api/insurance-status/eligibility-check",
|
||||||
status: "pending",
|
{ data: data, socketId: socket.id },
|
||||||
message: "Selenium browser starting...",
|
);
|
||||||
}),
|
const enqueueResult = await response.json();
|
||||||
);
|
if (enqueueResult.error) throw new Error(enqueueResult.error);
|
||||||
|
|
||||||
// Wait for the BullMQ worker to emit job:update on this socket
|
const jobId = enqueueResult.jobId;
|
||||||
const jobResult = await new Promise<any>((resolve, reject) => {
|
if (!jobId) throw new Error("No jobId returned from server");
|
||||||
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(
|
dispatch(
|
||||||
setTaskStatus({
|
setTaskStatus({
|
||||||
key: "eligibilityCheck",
|
key: "eligibilityCheck",
|
||||||
status: "success",
|
status: "pending",
|
||||||
message:
|
message: "Selenium browser starting...",
|
||||||
"Patient status is updated, and its eligibility pdf is uploaded at Document Page.",
|
}),
|
||||||
}),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
toast({
|
return new Promise<any>((resolve, reject) => {
|
||||||
title: "Selenium service done.",
|
const handler = (payload: any) => {
|
||||||
description:
|
if (String(payload.jobId) !== String(jobId)) return;
|
||||||
"Your Patient Eligibility is fetched and updated, Kindly search through the patient.",
|
if (payload.status === "active") {
|
||||||
variant: "default",
|
dispatch(
|
||||||
});
|
setTaskStatus({
|
||||||
|
key: "eligibilityCheck",
|
||||||
setSelectedPatient(null);
|
status: "pending",
|
||||||
|
message: payload.message ?? "Selenium running...",
|
||||||
// If worker returned pdfFileId: open preview modal
|
}),
|
||||||
if (jobResult.pdfFileId) {
|
);
|
||||||
setPreviewPdfId(Number(jobResult.pdfFileId));
|
} else if (payload.status === "completed") {
|
||||||
setPreviewFallbackFilename(
|
socket.off("job:update", handler);
|
||||||
jobResult.pdfFilename ?? `eligibility_${memberId}.pdf`,
|
resolve(payload.result ?? {});
|
||||||
);
|
} else if (payload.status === "failed") {
|
||||||
setPreviewOpen(true);
|
socket.off("job:update", handler);
|
||||||
}
|
reject(new Error(payload.error ?? "Selenium job failed"));
|
||||||
} catch (error: any) {
|
}
|
||||||
dispatch(
|
};
|
||||||
setTaskStatus({
|
socket.on("job:update", handler);
|
||||||
key: "eligibilityCheck",
|
});
|
||||||
status: "error",
|
|
||||||
message: error.message || "Selenium submission failed",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
toast({
|
|
||||||
title: "Selenium service error",
|
|
||||||
description: error.message || "An error occurred.",
|
|
||||||
variant: "destructive",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Claim Status Check Selenium
|
|
||||||
const handleStatusCheckSelenium = async () => {
|
|
||||||
const formattedDob = dateOfBirth ? formatLocalDate(dateOfBirth) : "";
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
memberId,
|
|
||||||
dateOfBirth: formattedDob,
|
|
||||||
insuranceSiteKey: "MH",
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
dispatch(
|
|
||||||
setTaskStatus({
|
|
||||||
key: "eligibilityCheck",
|
|
||||||
status: "pending",
|
|
||||||
message: "Sending Data to Selenium...",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const response = await apiRequest(
|
|
||||||
"POST",
|
|
||||||
"/api/insurance-status/claim-status-check",
|
|
||||||
{ data: JSON.stringify(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...",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Wait for the BullMQ worker to emit job:update on this socket
|
|
||||||
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:
|
|
||||||
"Claim status is updated, and its pdf is uploaded at Document Page.",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: "Selenium service done.",
|
|
||||||
description:
|
|
||||||
"Your Claim Status is fetched and updated, Kindly search through the patient.",
|
|
||||||
variant: "default",
|
|
||||||
});
|
|
||||||
|
|
||||||
setSelectedPatient(null);
|
|
||||||
|
|
||||||
// If worker returned pdfFileId: open preview modal
|
|
||||||
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",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddPatient = async () => {
|
const handleAddPatient = async () => {
|
||||||
@@ -331,56 +192,125 @@ export default function InsuranceStatusPage() {
|
|||||||
await addPatientMutation.mutateAsync(newPatient);
|
await addPatientMutation.mutateAsync(newPatient);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle insurance provider eligibility button clicks
|
// MH Eligibility — check eligibility, save to DB, open PDF preview
|
||||||
const handleMHEligibilityButton = async () => {
|
const handleMHEligibilityButton = async () => {
|
||||||
// Form Fields check
|
|
||||||
if (!memberId || !dateOfBirth) {
|
if (!memberId || !dateOfBirth) {
|
||||||
toast({
|
toast({
|
||||||
title: "Missing Fields",
|
title: "Missing Fields",
|
||||||
description:
|
description: "Please fill in all the required fields: Member ID, Date of Birth.",
|
||||||
"Please fill in all the required fields: Member ID, Date of Birth.",
|
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsCheckingEligibilityStatus(true);
|
setIsCheckingEligibilityStatus(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await handleEligibilityCheckSelenium();
|
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 });
|
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 {
|
} finally {
|
||||||
setIsCheckingEligibilityStatus(false);
|
setIsCheckingEligibilityStatus(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle insurance provider Status Check button clicks
|
// MH Eligibility & Appointment — check eligibility, save to DB, navigate to appointments
|
||||||
const handleMHStatusButton = async () => {
|
const handleMHEligibilityAppointmentButton = async () => {
|
||||||
// Form Fields check
|
if (!memberId || !dateOfBirth) {
|
||||||
if (!memberId || !dateOfBirth || !firstName) {
|
|
||||||
toast({
|
toast({
|
||||||
title: "Missing Fields",
|
title: "Missing Fields",
|
||||||
description:
|
description: "Please fill in all the required fields: Member ID, Date of Birth.",
|
||||||
"Please fill in all the required fields: Member ID, Date of Birth, First Name.",
|
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsCheckingClaimStatus(true);
|
setIsCheckingEligibilityAppointment(true);
|
||||||
|
|
||||||
// Adding patient if same patient exists then it will skip.
|
|
||||||
try {
|
try {
|
||||||
if (!selectedPatient) {
|
await runMHEligibilitySelenium();
|
||||||
await handleAddPatient();
|
|
||||||
}
|
|
||||||
|
|
||||||
await handleStatusCheckSelenium();
|
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 });
|
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 {
|
} finally {
|
||||||
setIsCheckingClaimStatus(false);
|
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -585,11 +515,11 @@ export default function InsuranceStatusPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleMHStatusButton()}
|
onClick={() => handleMHEligibilityAppointmentButton()}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
disabled={isCheckingClaimStatus}
|
disabled={isCheckingEligibilityAppointment}
|
||||||
>
|
>
|
||||||
{isCheckingClaimStatus ? (
|
{isCheckingEligibilityAppointment ? (
|
||||||
<>
|
<>
|
||||||
<LoaderCircleIcon className="h-4 w-4 mr-2 animate-spin" />
|
<LoaderCircleIcon className="h-4 w-4 mr-2 animate-spin" />
|
||||||
Processing...
|
Processing...
|
||||||
@@ -597,7 +527,25 @@ export default function InsuranceStatusPage() {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<CheckCircle className="h-4 w-4 mr-2" />
|
<CheckCircle className="h-4 w-4 mr-2" />
|
||||||
MH Status
|
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>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user