diff --git a/apps/Backend/src/routes/claims.ts b/apps/Backend/src/routes/claims.ts index cb2e69e..5f1e5af 100644 --- a/apps/Backend/src/routes/claims.ts +++ b/apps/Backend/src/routes/claims.ts @@ -246,6 +246,18 @@ router.get("/:id", async (req: Request, res: Response): Promise => { // Create a new claim router.post("/", async (req: Request, res: Response): Promise => { try { + // --- TRANSFORM claimFiles (if provided) into Prisma nested-create shape + if (Array.isArray(req.body.claimFiles)) { + // each item expected: { filename: string, mimeType: string } + req.body.claimFiles = { + create: req.body.claimFiles.map((f: any) => ({ + filename: String(f.filename), + mimeType: String(f.mimeType || f.mime || ""), + })), + }; + } + + // --- TRANSFORM serviceLines if (Array.isArray(req.body.serviceLines)) { req.body.serviceLines = req.body.serviceLines.map( (line: InputServiceLine) => ({ diff --git a/apps/Backend/src/storage/index.ts b/apps/Backend/src/storage/index.ts index d806ae4..d71573f 100644 --- a/apps/Backend/src/storage/index.ts +++ b/apps/Backend/src/storage/index.ts @@ -578,6 +578,7 @@ export const storage: IStorage = { include: { serviceLines: true, staff: true, + claimFiles: true, }, }); }, @@ -600,7 +601,7 @@ export const storage: IStorage = { orderBy: { createdAt: "desc" }, skip: offset, take: limit, - include: { serviceLines: true, staff: true }, + include: { serviceLines: true, staff: true, claimFiles: true }, }); }, diff --git a/apps/Frontend/src/components/claims/claim-form.tsx b/apps/Frontend/src/components/claims/claim-form.tsx index 3efbf7a..720777f 100644 --- a/apps/Frontend/src/components/claims/claim-form.tsx +++ b/apps/Frontend/src/components/claims/claim-form.tsx @@ -48,6 +48,11 @@ import { } from "@/utils/procedureCombosMapping"; import { COMBO_CATEGORIES, PROCEDURE_COMBOS } from "@/utils/procedureCombos"; +interface ClaimFileMeta { + filename: string; + mimeType: string; +} + interface ClaimFormData { patientId: number; appointmentId: number; @@ -63,6 +68,7 @@ interface ClaimFormData { status: string; // default "pending" serviceLines: InputServiceLine[]; claimId?: number; + claimFiles?: ClaimFileMeta[]; } interface ClaimFormProps { @@ -341,6 +347,13 @@ export function ClaimForm({ (line) => line.procedureCode.trim() !== "" ); const { uploadedFiles, insuranceSiteKey, ...formToCreateClaim } = form; + + // build claimFiles metadata from uploadedFiles (only filename + mimeType) + const claimFilesMeta: ClaimFileMeta[] = (uploadedFiles || []).map((f) => ({ + filename: f.name, + mimeType: f.type, + })); + const createdClaim = await onSubmit({ ...formToCreateClaim, serviceLines: filteredServiceLines, @@ -348,6 +361,7 @@ export function ClaimForm({ patientId: patientId, insuranceProvider: "MassHealth", appointmentId: appointmentId!, + claimFiles: claimFilesMeta, }); // 4. sending form data to selenium service @@ -415,6 +429,13 @@ export function ClaimForm({ (line) => line.procedureCode.trim() !== "" ); const { uploadedFiles, insuranceSiteKey, ...formToCreateClaim } = form; + + // build claimFiles metadata from uploadedFiles (only filename + mimeType) + const claimFilesMeta: ClaimFileMeta[] = (uploadedFiles || []).map((f) => ({ + filename: f.name, + mimeType: f.type, + })); + const createdClaim = await onSubmit({ ...formToCreateClaim, serviceLines: filteredServiceLines, @@ -422,6 +443,7 @@ export function ClaimForm({ patientId: patientId, insuranceProvider: "MassHealth", appointmentId: appointmentId!, + claimFiles: claimFilesMeta, }); // 4. Close form diff --git a/apps/Frontend/src/components/claims/claim-view-modal.tsx b/apps/Frontend/src/components/claims/claim-view-modal.tsx index 1b158f1..fd08a43 100644 --- a/apps/Frontend/src/components/claims/claim-view-modal.tsx +++ b/apps/Frontend/src/components/claims/claim-view-modal.tsx @@ -8,7 +8,8 @@ import { import { Button } from "@/components/ui/button"; import React from "react"; import { formatDateToHumanReadable } from "@/utils/dateUtils"; -import { ClaimWithServiceLines } from "@repo/db/types"; +import { ClaimFileMeta, ClaimWithServiceLines } from "@repo/db/types"; +import { FileText, Paperclip } from "lucide-react"; type ClaimViewModalProps = { isOpen: boolean; @@ -25,6 +26,40 @@ export default function ClaimViewModal({ claim, onEditClaim, }: ClaimViewModalProps) { + // Normalizer: supports both ClaimFile[] and nested-create shape { create: ClaimFile[] } + const getClaimFilesArray = ( + c: ClaimWithServiceLines | null + ): ClaimFileMeta[] => { + if (!c) return []; + + // If it's already a plain array (runtime from Prisma include), return it + const maybeFiles = (c as any).claimFiles; + if (!maybeFiles) return []; + + if (Array.isArray(maybeFiles)) { + // ensure each item has filename field (best-effort) + return maybeFiles.map((f: any) => ({ + id: f?.id, + filename: String(f?.filename ?? ""), + mimeType: f?.mimeType ?? f?.mime ?? null, + })); + } + + // Nested-create shape: { create: [...] } + if (maybeFiles && Array.isArray(maybeFiles.create)) { + return maybeFiles.create.map((f: any) => ({ + id: f?.id, + filename: String(f?.filename ?? ""), + mimeType: f?.mimeType ?? f?.mime ?? null, + })); + } + + // No recognized shape -> empty + return []; + }; + + const claimFiles = getClaimFilesArray(claim); + return ( @@ -204,6 +239,37 @@ export default function ClaimViewModal({ + {/* Claim Files (metadata) */} +
+

+ + Attached Files +

+ + {claimFiles.length > 0 ? ( +
    + {claimFiles.map((f) => ( +
  • +
    + +
    +
    {f.filename}
    +
    + {f.mimeType || "unknown"} +
    +
    +
    +
  • + ))} +
+ ) : ( +

No files attached.

+ )} +
+