feat: save insurance PDFs (eligibility/claims/preauth) to Cloud Storage instead of Postgres blobs

New saves go into the patient's Cloud Storage folder under category subfolders
(Eligibility/Claims/PreAuth/Claim Status/Attachments) via a new
storage.savePdfToCloudStorage helper, instead of the legacy PdfGroup/PdfFile
Postgres blob tables. Old records and their read paths are left untouched for
backward compatibility; PDF list/content/delete endpoints and viewers now
transparently handle both legacy numeric ids and new "cloud:<id>" ids.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-08 00:20:37 -04:00
co-authored by Claude Sonnet 5
parent 1190bc0494
commit 99cdfa702a
36 changed files with 496 additions and 296 deletions
@@ -5,7 +5,7 @@ import { Maximize2, Minimize2, Download, X } from "lucide-react";
import { viewDocument } from "@/lib/api/documents";
type Props = {
fileId: number | null;
fileId: number | string | null;
isOpen: boolean;
onClose: () => void;
initialFileName?: string | null;
@@ -60,8 +60,8 @@ export default function DocumentsFilePreviewModal({
setLoading(false);
return;
} else if (isPatientDocument && fileId) {
// For patient documents, use the viewDocument API to get the URL
const documentUrl = viewDocument(fileId);
// For patient documents, use the viewDocument API to get the URL (always a numeric id)
const documentUrl = viewDocument(Number(fileId));
res = await fetch(documentUrl);
} else {
// For PDF files, use the existing endpoint
@@ -92,7 +92,7 @@ interface BcbsMaEligibilityButtonProps {
isFormIncomplete: boolean;
autoTrigger?: boolean;
onAutoTriggered?: () => void;
onPdfReady: (pdfId: number, fallbackFilename: string | null) => void;
onPdfReady: (pdfId: number | string, fallbackFilename: string | null) => void;
}
export function BcbsMaEligibilityButton({
@@ -204,7 +204,7 @@ export function BcbsMaEligibilityButton({
queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE });
const pdfId = data.result?.pdfFileId;
if (pdfId) {
onPdfReady(Number(pdfId), data.result?.pdfFilename ?? `eligibility_bcbs_ma_${memberId}.pdf`);
onPdfReady(pdfId, data.result?.pdfFilename ?? `eligibility_bcbs_ma_${memberId}.pdf`);
}
} else if (data.status === "failed") {
const msg = data.error ?? "BCBS MA eligibility job failed.";
@@ -19,7 +19,7 @@ interface CCAEligibilityButtonProps {
isFormIncomplete: boolean;
autoTrigger?: boolean;
onAutoTriggered?: () => void;
onPdfReady: (pdfId: number, fallbackFilename: string | null) => void;
onPdfReady: (pdfId: number | string, fallbackFilename: string | null) => void;
}
export function CCAEligibilityButton({
@@ -143,7 +143,7 @@ export function CCAEligibilityButton({
queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE });
const pdfId = data.result?.pdfFileId;
if (pdfId) {
onPdfReady(Number(pdfId), data.result?.pdfFilename ?? `eligibility_cca_${memberId}.pdf`);
onPdfReady(pdfId, data.result?.pdfFilename ?? `eligibility_cca_${memberId}.pdf`);
}
} else if (data.status === "failed") {
const msg = data.error ?? "CCA eligibility job failed.";
@@ -90,7 +90,7 @@ interface DdmaEligibilityButtonProps {
isFormIncomplete: boolean;
autoTrigger?: boolean;
onAutoTriggered?: () => void;
onPdfReady: (pdfId: number, fallbackFilename: string | null) => void;
onPdfReady: (pdfId: number | string, fallbackFilename: string | null) => void;
}
export function DdmaEligibilityButton({
@@ -248,7 +248,7 @@ export function DdmaEligibilityButton({
if (pdfId) {
const filename =
data.result?.pdfFilename ?? `eligibility_ddma_${memberId}.pdf`;
onPdfReady(Number(pdfId), filename);
onPdfReady(pdfId, filename);
}
} else if (data.status === "failed") {
const msg = data.error ?? "DDMA eligibility job failed.";
@@ -326,7 +326,7 @@ export function DdmaEligibilityButton({
queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE });
const pdfId = data.result?.pdfFileId;
if (pdfId) {
onPdfReady(Number(pdfId), data.result?.pdfFilename ?? `eligibility_ddma_${memberId}.pdf`);
onPdfReady(pdfId, data.result?.pdfFilename ?? `eligibility_ddma_${memberId}.pdf`);
}
} else if (data.status === "failed") {
const msg = data.error ?? "DDMA eligibility job failed.";
@@ -89,7 +89,7 @@ interface DeltaInsEligibilityButtonProps {
isFormIncomplete: boolean;
autoTrigger?: boolean;
onAutoTriggered?: () => void;
onPdfReady: (pdfId: number, fallbackFilename: string | null) => void;
onPdfReady: (pdfId: number | string, fallbackFilename: string | null) => void;
}
export function DeltaInsEligibilityButton({
@@ -246,7 +246,7 @@ export function DeltaInsEligibilityButton({
queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE });
const pdfId = data.result?.pdfFileId;
if (pdfId) {
onPdfReady(Number(pdfId), data.result?.pdfFilename ?? `eligibility_deltains_${memberId}.pdf`);
onPdfReady(pdfId, data.result?.pdfFilename ?? `eligibility_deltains_${memberId}.pdf`);
}
} else if (data.status === "failed") {
const msg = data.error ?? "Delta Ins eligibility job failed.";
@@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button";
import { apiRequest } from "@/lib/queryClient";
export interface PdfPanelConfig {
pdfId?: number | null;
pdfId?: number | string | null;
fallbackFilename?: string | null;
label: string;
autoDownload?: boolean;
@@ -34,7 +34,7 @@ function parseFilename(header: string | null): string | null {
return null;
}
function usePdfBlob(open: boolean, pdfId?: number | null, fallbackFilename?: string | null) {
function usePdfBlob(open: boolean, pdfId?: number | string | null, fallbackFilename?: string | null) {
const [blobUrl, setBlobUrl] = useState<string | null>(null);
const [filename, setFilename] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
@@ -7,7 +7,7 @@ import { Maximize2, Minimize2 } from "lucide-react";
interface Props {
open: boolean;
onClose: () => void;
pdfId?: number | null;
pdfId?: number | string | null;
fallbackFilename?: string | null;
autoDownload?: boolean;
}
@@ -89,7 +89,7 @@ interface TuftsSCOEligibilityButtonProps {
isFormIncomplete: boolean;
autoTrigger?: boolean;
onAutoTriggered?: () => void;
onPdfReady: (pdfId: number, fallbackFilename: string | null) => void;
onPdfReady: (pdfId: number | string, fallbackFilename: string | null) => void;
}
export function TuftsSCOEligibilityButton({
@@ -243,7 +243,7 @@ export function TuftsSCOEligibilityButton({
queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE });
const pdfId = data.result?.pdfFileId;
if (pdfId) {
onPdfReady(Number(pdfId), data.result?.pdfFilename ?? `eligibility_unitedsco_${memberId}.pdf`);
onPdfReady(pdfId, data.result?.pdfFilename ?? `eligibility_unitedsco_${memberId}.pdf`);
}
} else if (data.status === "failed") {
const msg = data.error ?? "Tufts SCO eligibility job failed.";
@@ -89,7 +89,7 @@ interface UnitedSCOEligibilityButtonProps {
isFormIncomplete: boolean;
autoTrigger?: boolean;
onAutoTriggered?: () => void;
onPdfReady: (pdfId: number, fallbackFilename: string | null) => void;
onPdfReady: (pdfId: number | string, fallbackFilename: string | null) => void;
}
export function UnitedSCOEligibilityButton({
@@ -243,7 +243,7 @@ export function UnitedSCOEligibilityButton({
queryClient.invalidateQueries({ queryKey: QK_PATIENTS_BASE });
const pdfId = data.result?.pdfFileId;
if (pdfId) {
onPdfReady(Number(pdfId), data.result?.pdfFilename ?? `eligibility_unitedsco_${memberId}.pdf`);
onPdfReady(pdfId, data.result?.pdfFilename ?? `eligibility_unitedsco_${memberId}.pdf`);
}
} else if (data.status === "failed") {
const msg = data.error ?? "United SCO eligibility job failed.";