feat(claimFilesMetadata) - Added feature for having claims uploaded files metadata

This commit is contained in:
2025-09-17 00:29:58 +05:30
parent cb7123afc5
commit d0a984a7e9
6 changed files with 125 additions and 6 deletions

View File

@@ -246,6 +246,18 @@ router.get("/:id", async (req: Request, res: Response): Promise<any> => {
// Create a new claim
router.post("/", async (req: Request, res: Response): Promise<any> => {
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) => ({

View File

@@ -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 },
});
},

View File

@@ -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

View File

@@ -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 (
<Dialog open={isOpen} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[700px] max-h-[90vh] overflow-y-auto">
@@ -204,6 +239,37 @@ export default function ClaimViewModal({
</div>
</div>
{/* Claim Files (metadata) */}
<div className="pt-4">
<h4 className="font-medium text-gray-900 flex items-center space-x-2">
<Paperclip className="w-4 h-4 inline-block" />
<span>Attached Files</span>
</h4>
{claimFiles.length > 0 ? (
<ul className="mt-3 space-y-2">
{claimFiles.map((f) => (
<li
key={f.id ?? f.filename}
className="flex items-center justify-between border rounded-md p-3 bg-white"
>
<div className="flex items-start space-x-3">
<FileText className="w-5 h-5 text-gray-500 mt-1" />
<div>
<div className="font-medium">{f.filename}</div>
<div className="text-xs text-gray-500">
{f.mimeType || "unknown"}
</div>
</div>
</div>
</li>
))}
</ul>
) : (
<p className="mt-2 text-gray-500">No files attached.</p>
)}
</div>
<div className="flex justify-end space-x-2 pt-4">
<Button variant="outline" onClick={() => onOpenChange(false)}>
Close