feat: MH eligibility & history, CMSP eligibility & history & remaining

- Add MH Eligibility & History button: runs full MH eligibility flow then
  clicks member ID → service history, prints both PDFs via CDP, opens
  dual side-by-side PDF modal (eligibility auto-downloads, history does not)
- Add CMSP Eligibility & History & Remaining button: same flow plus
  navigates back to member details, clicks View Accumulator, prints
  accumulator PDF via CDP; opens 3-panel side-by-side PDF modal
- Generalize DualPdfPreviewModal to accept panels[] array (works for 2 or 3 PDFs)
- Auto-download eligibility PDF via direct API URL to avoid Chrome Safe
  Browsing pause on blob: URL downloads
- New backend processors, job types, and routes for both flows
- New Python Selenium workers with stable CSS selectors (ng-bind, href*)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gitead
2026-05-13 23:29:55 -04:00
parent 131733564e
commit 06526cd1bc
11 changed files with 1868 additions and 2 deletions

View File

@@ -15,6 +15,7 @@ import {
} from "../../../../packages/db/types/patient-types";
import { formatDobForAgent } from "../utils/dateUtils";
import { seleniumQueue } from "../queue/queues";
import { enqueueSeleniumJob } from "../queue/jobRunner";
const router = Router();
@@ -166,6 +167,98 @@ router.post(
}
);
router.post(
"/eligibility-history-check",
async (req: Request, res: Response): Promise<any> => {
if (!req.body.data) {
return res.status(400).json({ error: "Missing Insurance Eligibility data for selenium" });
}
if (!req.user || !req.user.id) {
return res.status(401).json({ error: "Unauthorized: user info missing" });
}
const data =
typeof req.body.data === "string" ? JSON.parse(req.body.data) : req.body.data;
const credentials = await storage.getInsuranceCredentialByUserAndSiteKey(req.user.id, "MH");
if (!credentials) {
return res.status(404).json({
error: "No MassHealth credentials found. Please update them in Settings.",
});
}
const insuranceId = String(data.memberId ?? "").trim();
if (!insuranceId) {
return res.status(400).json({ error: "Missing memberId" });
}
const enrichedData = {
...data,
massdhpUsername: credentials.username,
massdhpPassword: credentials.password,
};
const jobId = enqueueSeleniumJob({
jobType: "mh-eligibility-history-check",
userId: req.user.id,
socketId: req.body.socketId,
enrichedPayload: enrichedData,
insuranceId,
formFirstName: data.firstName,
formLastName: data.lastName,
formDob: data.dateOfBirth,
});
return res.json({ jobId, status: "queued" });
}
);
router.post(
"/cmsp-eligibility-history-remaining-check",
async (req: Request, res: Response): Promise<any> => {
if (!req.body.data) {
return res.status(400).json({ error: "Missing data for selenium" });
}
if (!req.user || !req.user.id) {
return res.status(401).json({ error: "Unauthorized: user info missing" });
}
const data =
typeof req.body.data === "string" ? JSON.parse(req.body.data) : req.body.data;
const credentials = await storage.getInsuranceCredentialByUserAndSiteKey(req.user.id, "MH");
if (!credentials) {
return res.status(404).json({
error: "No MassHealth credentials found. Please update them in Settings.",
});
}
const insuranceId = String(data.memberId ?? "").trim();
if (!insuranceId) {
return res.status(400).json({ error: "Missing memberId" });
}
const enrichedData = {
...data,
massdhpUsername: credentials.username,
massdhpPassword: credentials.password,
};
const jobId = enqueueSeleniumJob({
jobType: "cmsp-eligibility-history-remaining-check",
userId: req.user.id,
socketId: req.body.socketId,
enrichedPayload: enrichedData,
insuranceId,
formFirstName: data.firstName,
formLastName: data.lastName,
formDob: data.dateOfBirth,
});
return res.json({ jobId, status: "queued" });
}
);
router.post(
"/claim-status-check",
async (req: Request, res: Response): Promise<any> => {