feat: add Remaining/Third Party columns, toggle PDF viewer, retire CMSP as separate insurance type

- Add remainingBenefit and thirdPartyInsurance columns to Patient; extract both
  from the MassHealth accumulator/member-detail pages during the eligibility
  check and surface them in the eligibility table
- Add an updated-date display and a toggle to suppress the post-check PDF
  viewer popup (default off) on the insurance eligibility page
- Rename CMSP-prefixed PDF filenames and UI copy to MH, since CMSP and MH now
  share the same worker
- Fold CMSP into MassHealth/MH across patient/claim insurance-type dropdowns
  and the chatbot's eligibility routing, since they're the same insurance
- Remove auto-download of saved eligibility PDFs; downloading is now manual

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 22:32:43 -04:00
co-authored by Claude Sonnet 5
parent f8235787db
commit 52ddfd9f57
96 changed files with 561 additions and 160 deletions
@@ -791,13 +791,12 @@ export function ClaimForm({
const deriveInsuranceSiteKey = (provider: string | null | undefined): string => {
const p = (provider || "").toLowerCase().trim();
if (!p) return "";
if (p.includes("masshealth") || p === "mh" || p === "mass health") return "MH";
if (p.includes("masshealth") || p === "mh" || p === "mass health" || p.includes("cmsp")) return "MH";
if (p.includes("commonwealth care alliance") || p === "cca") return "CCA";
if (p.includes("ddma") || p.includes("delta dental ma")) return "DDMA";
if (p.includes("delta ins") || p === "deltains") return "DeltaIns";
if (p.includes("tufts") || p.includes("dentaquest") || p === "tuftssco") return "TuftsSCO";
if ((p.includes("united") && p.includes("sco")) || p === "unitedsco") return "UnitedSCO";
if (p.includes("cmsp")) return "CMSP";
if (p.includes("bcbs") || p.includes("blue cross")) return "BCBS";
if (p.includes("united aapr") || p === "unitedaapr") return "UnitedAAPR";
if (p.includes("aetna")) return "Aetna";
@@ -2008,7 +2007,6 @@ export function ClaimForm({
<SelectItem value="DeltaIns">Delta Ins</SelectItem>
<SelectItem value="TuftsSCO">Tufts SCO</SelectItem>
<SelectItem value="UnitedSCO">United SCO</SelectItem>
<SelectItem value="CMSP">CMSP</SelectItem>
<SelectItem value="BCBS">BCBS</SelectItem>
<SelectItem value="UnitedAAPR">United AAPR</SelectItem>
<SelectItem value="Aetna">Aetna</SelectItem>
@@ -2596,7 +2594,6 @@ export function ClaimForm({
<SelectItem value="DeltaIns">Delta Ins</SelectItem>
<SelectItem value="TuftsSCO">Tufts SCO</SelectItem>
<SelectItem value="UnitedSCO">United SCO</SelectItem>
<SelectItem value="CMSP">CMSP</SelectItem>
<SelectItem value="BCBS">BCBS</SelectItem>
<SelectItem value="UnitedAAPR">United AAPR</SelectItem>
<SelectItem value="Aetna">Aetna</SelectItem>
@@ -135,7 +135,6 @@ function mdyToISO(mdy: string): string {
const INSURANCE_TYPE_OPTIONS: { value: string; label: string }[] = [
{ value: "mh", label: "MassHealth" },
{ value: "cmsp", label: "CMSP" },
{ value: "ddma", label: "DDMA" },
{ value: "delta-ins", label: "Delta Dental" },
{ value: "united-sco", label: "United SCO" },
@@ -143,13 +142,10 @@ const INSURANCE_TYPE_OPTIONS: { value: string; label: string }[] = [
{ value: "cca", label: "CCA" },
];
function getAutoCheck(dobISO: string): "mh" | "cmsp" {
const [y, m, d] = dobISO.split("-").map(Number);
const today = new Date();
let age = today.getFullYear() - (y ?? 0);
const monthDiff = today.getMonth() + 1 - (m ?? 0);
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < (d ?? 0))) age--;
return age >= 21 ? "mh" : "cmsp";
function getAutoCheck(_dobISO: string): "mh" {
// CMSP and MassHealth eligibility checks now run through the same MH worker,
// so there's no longer an age-based split — everyone is "mh".
return "mh";
}
function parseEligibilityInput(
@@ -64,13 +64,12 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
const normalizeInsuranceProvider = (val: string | null | undefined): string => {
const p = (val || "").toLowerCase().trim();
if (p.includes("masshealth") || p === "mh" || p === "mass health") return "MassHealth";
if (p.includes("masshealth") || p === "mh" || p === "mass health" || p.includes("cmsp")) return "MassHealth";
if (p.includes("commonwealth care alliance") || p === "cca") return "CCA";
if (p.includes("ddma")) return "DDMA";
if (p.includes("delta dental") || p.includes("delta ins") || p === "deltains") return "DeltaIns";
if (p.includes("tufts") || p.includes("dentaquest") || p === "tuftssco") return "TuftsSCO";
if (p.includes("united sco") || p === "unitedsco") return "UnitedSCO";
if (p.includes("cmsp")) return "CMSP";
if (p.includes("bcbs") || p.includes("blue cross")) return "BCBS";
if (p.includes("united aapr") || p === "unitedaapr") return "UnitedAAPR";
if (p.includes("aetna")) return "Aetna";
@@ -451,7 +450,6 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
<SelectItem value="DeltaIns">Delta Ins</SelectItem>
<SelectItem value="TuftsSCO">Tufts SCO</SelectItem>
<SelectItem value="UnitedSCO">United SCO</SelectItem>
<SelectItem value="CMSP">CMSP</SelectItem>
<SelectItem value="BCBS">BCBS</SelectItem>
<SelectItem value="UnitedAAPR">United AAPR</SelectItem>
<SelectItem value="Aetna">Aetna</SelectItem>
@@ -63,6 +63,7 @@ interface PatientTableProps {
allowCheckbox?: boolean;
allowNewClaim?: boolean;
allowFinancial?: boolean;
showEligibilityColumns?: boolean;
onNewClaim?: (patientId: number) => void;
onSelectPatient?: (patient: Patient | null) => void;
onPageChange?: (page: number) => void;
@@ -82,6 +83,7 @@ export function PatientTable({
allowCheckbox,
allowNewClaim,
allowFinancial,
showEligibilityColumns,
onNewClaim,
onSelectPatient,
onPageChange,
@@ -976,6 +978,12 @@ export function PatientTable({
{/*<TableHead>Contact</TableHead>*/}
<TableHead>Insurance</TableHead>
<TableHead>Status</TableHead>
{showEligibilityColumns && (
<>
<TableHead>Remaining</TableHead>
<TableHead>Third Party/2nd Insurance</TableHead>
</>
)}
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
@@ -983,7 +991,7 @@ export function PatientTable({
{isLoading ? (
<TableRow>
<TableCell
colSpan={6}
colSpan={showEligibilityColumns ? 8 : 6}
className="text-center py-8 text-muted-foreground"
>
<LoadingScreen />
@@ -992,7 +1000,7 @@ export function PatientTable({
) : isError ? (
<TableRow>
<TableCell
colSpan={6}
colSpan={showEligibilityColumns ? 8 : 6}
className="text-center py-8 text-red-500"
>
Error loading patients.
@@ -1001,7 +1009,7 @@ export function PatientTable({
) : patientsData?.patients.length === 0 ? (
<TableRow>
<TableCell
colSpan={6}
colSpan={showEligibilityColumns ? 8 : 6}
className="text-center py-8 text-muted-foreground"
>
No patients found.
@@ -1088,8 +1096,28 @@ export function PatientTable({
</span>
)}
</div>
{patient.updatedAt && (
<div className="text-xs text-gray-500 mt-1">
Updated: {formatDateToHumanReadable(patient.updatedAt)}
</div>
)}
</TableCell>
{showEligibilityColumns && (
<>
<TableCell>
<div className="text-sm text-gray-900">
{patient.remainingBenefit || "—"}
</div>
</TableCell>
<TableCell>
<div className="text-sm text-gray-900">
{patient.thirdPartyInsurance || "—"}
</div>
</TableCell>
</>
)}
<TableCell className="text-right">
<div className="flex justify-end">
{allowDelete && isAdmin && (