claim recent table done
This commit is contained in:
@@ -146,6 +146,7 @@ type ClaimWithServiceLines = Claim & {
|
|||||||
toothSurface: string | null;
|
toothSurface: string | null;
|
||||||
billedAmount: number;
|
billedAmount: number;
|
||||||
}[];
|
}[];
|
||||||
|
staff: Staff | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pdf types:
|
// Pdf types:
|
||||||
@@ -538,7 +539,7 @@ export const storage: IStorage = {
|
|||||||
orderBy: { createdAt: "desc" },
|
orderBy: { createdAt: "desc" },
|
||||||
skip: offset,
|
skip: offset,
|
||||||
take: limit,
|
take: limit,
|
||||||
include: { serviceLines: true },
|
include: { serviceLines: true, staff: true },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
254
apps/Frontend/src/components/claims/claim-edit-modal.tsx
Normal file
254
apps/Frontend/src/components/claims/claim-edit-modal.tsx
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogDescription,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import { formatDateToHumanReadable } from "@/utils/dateUtils";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { z } from "zod";
|
||||||
|
import {
|
||||||
|
ClaimUncheckedCreateInputObjectSchema,
|
||||||
|
StaffUncheckedCreateInputObjectSchema,
|
||||||
|
} from "@repo/db/usedSchemas";
|
||||||
|
import { ClaimStatus } from "./claims-recent-table";
|
||||||
|
|
||||||
|
type Claim = z.infer<typeof ClaimUncheckedCreateInputObjectSchema>;
|
||||||
|
type Staff = z.infer<typeof StaffUncheckedCreateInputObjectSchema>;
|
||||||
|
|
||||||
|
type ClaimWithServiceLines = Claim & {
|
||||||
|
serviceLines: {
|
||||||
|
id: number;
|
||||||
|
claimId: number;
|
||||||
|
procedureCode: string;
|
||||||
|
procedureDate: Date;
|
||||||
|
oralCavityArea: string | null;
|
||||||
|
toothNumber: string | null;
|
||||||
|
toothSurface: string | null;
|
||||||
|
billedAmount: number;
|
||||||
|
}[];
|
||||||
|
staff: Staff | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ClaimEditModalProps = {
|
||||||
|
isOpen: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
onClose: () => void;
|
||||||
|
claim: ClaimWithServiceLines | null;
|
||||||
|
onSave: (updatedClaim: ClaimWithServiceLines) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ClaimEditModal({
|
||||||
|
isOpen,
|
||||||
|
onOpenChange,
|
||||||
|
onClose,
|
||||||
|
claim,
|
||||||
|
onSave,
|
||||||
|
}: ClaimEditModalProps) {
|
||||||
|
const [status, setStatus] = useState<ClaimStatus>(
|
||||||
|
claim?.status ?? ("PENDING" as ClaimStatus)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!claim) return null;
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
const updatedClaim: ClaimWithServiceLines = {
|
||||||
|
...claim,
|
||||||
|
status,
|
||||||
|
};
|
||||||
|
|
||||||
|
onSave(updatedClaim);
|
||||||
|
onOpenChange(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent className="sm:max-w-[700px] max-h-[90vh] overflow-y-auto">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Edit Claim Status</DialogTitle>
|
||||||
|
<DialogDescription>Update the status of the claim.</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Patient Details */}
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="h-16 w-16 rounded-full bg-blue-600 text-white flex items-center justify-center text-xl font-medium">
|
||||||
|
{claim.patientName.charAt(0)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-xl font-semibold">{claim.patientName}</h3>
|
||||||
|
<p className="text-gray-500">
|
||||||
|
Claim ID: {claim.id?.toString().padStart(4, "0")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Basic Info */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 pt-4">
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-gray-900">Basic Information</h4>
|
||||||
|
<div className="mt-2 space-y-2">
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Date of Birth:</span>{" "}
|
||||||
|
{new Date(claim.dateOfBirth).toLocaleDateString()}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Service Date:</span>{" "}
|
||||||
|
{new Date(claim.serviceDate).toLocaleDateString()}
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">Status:</span>
|
||||||
|
<Select
|
||||||
|
value={status}
|
||||||
|
onValueChange={(value) => setStatus(value as ClaimStatus)}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="mt-1 w-full">
|
||||||
|
<SelectValue placeholder="Select status" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="PENDING">Pending</SelectItem>
|
||||||
|
<SelectItem value="REVIEW">Review</SelectItem>
|
||||||
|
<SelectItem value="APPROVED">Approved</SelectItem>
|
||||||
|
<SelectItem value="CANCELLED">Cancelled</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-gray-900">Insurance Details</h4>
|
||||||
|
<div className="mt-2 space-y-2">
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Insurance Provider:</span>{" "}
|
||||||
|
{claim.insuranceProvider || "N/A"}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Member ID:</span>{" "}
|
||||||
|
{claim.memberId}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Remarks:</span>{" "}
|
||||||
|
{claim.remarks || "N/A"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Timestamps */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h4 className="font-medium text-gray-900">Timestamps</h4>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Created At:</span>{" "}
|
||||||
|
{formatDateToHumanReadable(claim.createdAt)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Updated At:</span>{" "}
|
||||||
|
{formatDateToHumanReadable(claim.updatedAt)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Staff Info */}
|
||||||
|
{claim.staff && (
|
||||||
|
<div className="space-y-2 pt-4">
|
||||||
|
<h4 className="font-medium text-gray-900">Assigned Staff</h4>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Name:</span> {claim.staff.name}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Role:</span> {claim.staff.role}
|
||||||
|
</p>
|
||||||
|
{claim.staff.email && (
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Email:</span>{" "}
|
||||||
|
{claim.staff.email}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{claim.staff.phone && (
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Phone:</span>{" "}
|
||||||
|
{claim.staff.phone}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Service Lines */}
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-gray-900 pt-4">Service Lines</h4>
|
||||||
|
<div className="mt-2 space-y-3">
|
||||||
|
{claim.serviceLines.length > 0 ? (
|
||||||
|
<>
|
||||||
|
{claim.serviceLines.map((line) => (
|
||||||
|
<div
|
||||||
|
key={line.id}
|
||||||
|
className="border p-3 rounded-md bg-gray-50"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Procedure Code:</span>{" "}
|
||||||
|
{line.procedureCode}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Procedure Date:</span>{" "}
|
||||||
|
{new Date(line.procedureDate).toLocaleDateString()}
|
||||||
|
</p>
|
||||||
|
{line.oralCavityArea && (
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">
|
||||||
|
Oral Cavity Area:
|
||||||
|
</span>{" "}
|
||||||
|
{line.oralCavityArea}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{line.toothNumber && (
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Tooth Number:</span>{" "}
|
||||||
|
{line.toothNumber}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{line.toothSurface && (
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Tooth Surface:</span>{" "}
|
||||||
|
{line.toothSurface}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Billed Amount:</span> $
|
||||||
|
{line.billedAmount.toFixed(2)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="text-right font-semibold text-gray-900 pt-2 border-t mt-4">
|
||||||
|
Total Billed Amount: $
|
||||||
|
{claim.serviceLines
|
||||||
|
.reduce((total, line) => total + line.billedAmount, 0)
|
||||||
|
.toFixed(2)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<p className="text-gray-500">No service lines available.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<div className="flex justify-end space-x-2 pt-4">
|
||||||
|
<Button variant="outline" onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleSave}>Save Changes</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,12 +6,17 @@ import {
|
|||||||
DialogDescription,
|
DialogDescription,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ClaimUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
|
import {
|
||||||
|
ClaimUncheckedCreateInputObjectSchema,
|
||||||
|
StaffUncheckedCreateInputObjectSchema,
|
||||||
|
} from "@repo/db/usedSchemas";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { formatDateToHumanReadable } from "@/utils/dateUtils";
|
||||||
|
|
||||||
//creating types out of schema auto generated.
|
//creating types out of schema auto generated.
|
||||||
type Claim = z.infer<typeof ClaimUncheckedCreateInputObjectSchema>;
|
type Claim = z.infer<typeof ClaimUncheckedCreateInputObjectSchema>;
|
||||||
|
type Staff = z.infer<typeof StaffUncheckedCreateInputObjectSchema>;
|
||||||
|
|
||||||
type ClaimWithServiceLines = Claim & {
|
type ClaimWithServiceLines = Claim & {
|
||||||
serviceLines: {
|
serviceLines: {
|
||||||
@@ -24,6 +29,7 @@ type ClaimWithServiceLines = Claim & {
|
|||||||
toothSurface: string | null;
|
toothSurface: string | null;
|
||||||
billedAmount: number;
|
billedAmount: number;
|
||||||
}[];
|
}[];
|
||||||
|
staff: Staff | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ClaimViewModalProps = {
|
type ClaimViewModalProps = {
|
||||||
@@ -43,8 +49,7 @@ export default function ClaimViewModal({
|
|||||||
}: ClaimViewModalProps) {
|
}: ClaimViewModalProps) {
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="sm:max-w-[700px] max-h-[90vh] overflow-y-auto">
|
<DialogContent className="sm:max-w-[700px] max-h-[90vh] overflow-y-auto">
|
||||||
|
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Claim Details</DialogTitle>
|
<DialogTitle>Claim Details</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
@@ -70,10 +75,6 @@ export default function ClaimViewModal({
|
|||||||
<div>
|
<div>
|
||||||
<h4 className="font-medium text-gray-900">Basic Information</h4>
|
<h4 className="font-medium text-gray-900">Basic Information</h4>
|
||||||
<div className="mt-2 space-y-2">
|
<div className="mt-2 space-y-2">
|
||||||
<p>
|
|
||||||
<span className="text-gray-500">Member ID:</span>{" "}
|
|
||||||
{claim.memberId}
|
|
||||||
</p>
|
|
||||||
<p>
|
<p>
|
||||||
<span className="text-gray-500">Date of Birth:</span>{" "}
|
<span className="text-gray-500">Date of Birth:</span>{" "}
|
||||||
{new Date(claim.dateOfBirth).toLocaleDateString()}
|
{new Date(claim.dateOfBirth).toLocaleDateString()}
|
||||||
@@ -105,12 +106,16 @@ export default function ClaimViewModal({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-medium text-gray-900">Insurance</h4>
|
<h4 className="font-medium text-gray-900">Insurance Details</h4>
|
||||||
<div className="mt-2 space-y-2">
|
<div className="mt-2 space-y-2">
|
||||||
<p>
|
<p>
|
||||||
<span className="text-gray-500">Provider:</span>{" "}
|
<span className="text-gray-500">Insurance Provider:</span>{" "}
|
||||||
{claim.insuranceProvider || "N/A"}
|
{claim.insuranceProvider || "N/A"}
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Member ID:</span>{" "}
|
||||||
|
{claim.memberId}
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span className="text-gray-500">Remarks:</span>{" "}
|
<span className="text-gray-500">Remarks:</span>{" "}
|
||||||
{claim.remarks || "N/A"}
|
{claim.remarks || "N/A"}
|
||||||
@@ -119,49 +124,98 @@ export default function ClaimViewModal({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Metadata */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h4 className="font-medium text-gray-900">Timestamps</h4>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Created At:</span>{" "}
|
||||||
|
{formatDateToHumanReadable(claim.createdAt)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Updated At:</span>{" "}
|
||||||
|
{formatDateToHumanReadable(claim.updatedAt)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{claim.staff && (
|
||||||
|
<div className="space-y-2 pt-4">
|
||||||
|
<h4 className="font-medium text-gray-900">Assigned Staff</h4>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Name:</span>{" "}
|
||||||
|
{claim.staff.name}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Role:</span>{" "}
|
||||||
|
{claim.staff.role}
|
||||||
|
</p>
|
||||||
|
{claim.staff.email && (
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Email:</span>{" "}
|
||||||
|
{claim.staff.email}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{claim.staff.phone && (
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Phone:</span>{" "}
|
||||||
|
{claim.staff.phone}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-medium text-gray-900 pt-4">Service Lines</h4>
|
<h4 className="font-medium text-gray-900 pt-4">Service Lines</h4>
|
||||||
<div className="mt-2 space-y-3">
|
<div className="mt-2 space-y-3">
|
||||||
{claim.serviceLines.length > 0 ? (
|
{claim.serviceLines.length > 0 ? (
|
||||||
claim.serviceLines.map((line, index) => (
|
<>
|
||||||
<div
|
{claim.serviceLines.map((line, index) => (
|
||||||
key={line.id}
|
<div
|
||||||
className="border p-3 rounded-md bg-gray-50"
|
key={line.id}
|
||||||
>
|
className="border p-3 rounded-md bg-gray-50"
|
||||||
<p>
|
>
|
||||||
<span className="text-gray-500">Procedure Code:</span>{" "}
|
|
||||||
{line.procedureCode}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span className="text-gray-500">Procedure Date:</span>{" "}
|
|
||||||
{new Date(line.procedureDate).toLocaleDateString()}
|
|
||||||
</p>
|
|
||||||
{line.oralCavityArea && (
|
|
||||||
<p>
|
<p>
|
||||||
<span className="text-gray-500">
|
<span className="text-gray-500">Procedure Code:</span>{" "}
|
||||||
Oral Cavity Area:
|
{line.procedureCode}
|
||||||
</span>{" "}
|
|
||||||
{line.oralCavityArea}
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
|
||||||
{line.toothNumber && (
|
|
||||||
<p>
|
<p>
|
||||||
<span className="text-gray-500">Tooth Number:</span>{" "}
|
<span className="text-gray-500">Procedure Date:</span>{" "}
|
||||||
{line.toothNumber}
|
{new Date(line.procedureDate).toLocaleDateString()}
|
||||||
</p>
|
</p>
|
||||||
)}
|
{line.oralCavityArea && (
|
||||||
{line.toothSurface && (
|
<p>
|
||||||
|
<span className="text-gray-500">
|
||||||
|
Oral Cavity Area:
|
||||||
|
</span>{" "}
|
||||||
|
{line.oralCavityArea}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{line.toothNumber && (
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">Tooth Number:</span>{" "}
|
||||||
|
{line.toothNumber}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{line.toothSurface && (
|
||||||
|
<p>
|
||||||
|
<span className="text-gray-500">
|
||||||
|
Tooth Surface:
|
||||||
|
</span>{" "}
|
||||||
|
{line.toothSurface}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<p>
|
<p>
|
||||||
<span className="text-gray-500">Tooth Surface:</span>{" "}
|
<span className="text-gray-500">Billed Amount:</span>{" "}
|
||||||
{line.toothSurface}
|
${line.billedAmount.toFixed(2)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
</div>
|
||||||
<p>
|
))}
|
||||||
<span className="text-gray-500">Billed Amount:</span> $
|
<div className="text-right font-semibold text-gray-900 pt-2 border-t mt-4">
|
||||||
{line.billedAmount.toFixed(2)}
|
Total Billed Amount: $
|
||||||
</p>
|
{claim.serviceLines
|
||||||
|
.reduce((total, line) => total + line.billedAmount, 0)
|
||||||
|
.toFixed(2)}
|
||||||
</div>
|
</div>
|
||||||
))
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-gray-500">No service lines available.</p>
|
<p className="text-gray-500">No service lines available.</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
PatientUncheckedCreateInputObjectSchema,
|
PatientUncheckedCreateInputObjectSchema,
|
||||||
ClaimUncheckedCreateInputObjectSchema,
|
ClaimUncheckedCreateInputObjectSchema,
|
||||||
ClaimStatusSchema,
|
ClaimStatusSchema,
|
||||||
|
StaffUncheckedCreateInputObjectSchema,
|
||||||
} from "@repo/db/usedSchemas";
|
} from "@repo/db/usedSchemas";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
@@ -42,10 +43,12 @@ import { cn } from "@/lib/utils";
|
|||||||
import { formatDateToHumanReadable } from "@/utils/dateUtils";
|
import { formatDateToHumanReadable } from "@/utils/dateUtils";
|
||||||
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import ClaimViewModal from "./claim-view-modal";
|
import ClaimViewModal from "./claim-view-modal";
|
||||||
|
import ClaimEditModal from "./claim-edit-modal";
|
||||||
|
|
||||||
//creating types out of schema auto generated.
|
//creating types out of schema auto generated.
|
||||||
type Claim = z.infer<typeof ClaimUncheckedCreateInputObjectSchema>;
|
type Claim = z.infer<typeof ClaimUncheckedCreateInputObjectSchema>;
|
||||||
export type ClaimStatus = z.infer<typeof ClaimStatusSchema>;
|
export type ClaimStatus = z.infer<typeof ClaimStatusSchema>;
|
||||||
|
type Staff = z.infer<typeof StaffUncheckedCreateInputObjectSchema>;
|
||||||
|
|
||||||
type ClaimWithServiceLines = Claim & {
|
type ClaimWithServiceLines = Claim & {
|
||||||
serviceLines: {
|
serviceLines: {
|
||||||
@@ -58,6 +61,7 @@ type ClaimWithServiceLines = Claim & {
|
|||||||
toothSurface: string | null;
|
toothSurface: string | null;
|
||||||
billedAmount: number;
|
billedAmount: number;
|
||||||
}[];
|
}[];
|
||||||
|
staff: Staff | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PatientSchema = (
|
const PatientSchema = (
|
||||||
@@ -140,6 +144,42 @@ export default function ClaimsRecentTable({
|
|||||||
placeholderData: { claims: [], totalCount: 0 },
|
placeholderData: { claims: [], totalCount: 0 },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const updateClaimMutation = useMutation({
|
||||||
|
mutationFn: async (claim: ClaimWithServiceLines) => {
|
||||||
|
const response = await apiRequest("PUT", `/api/claims/${claim.id}`, {
|
||||||
|
status: claim.status,
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = await response.json();
|
||||||
|
throw new Error(error.message || "Failed to update claim");
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
setIsEditClaimOpen(false);
|
||||||
|
toast({
|
||||||
|
title: "Success",
|
||||||
|
description: "Claim updated successfully!",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [
|
||||||
|
"claims-recent",
|
||||||
|
{
|
||||||
|
page: currentPage,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: `Update failed: ${error.message}`,
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const deleteClaimMutation = useMutation({
|
const deleteClaimMutation = useMutation({
|
||||||
mutationFn: async (id: number) => {
|
mutationFn: async (id: number) => {
|
||||||
await apiRequest("DELETE", `/api/claims/${id}`);
|
await apiRequest("DELETE", `/api/claims/${id}`);
|
||||||
@@ -475,6 +515,18 @@ export default function ClaimsRecentTable({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isEditClaimOpen && currentClaim && (
|
||||||
|
<ClaimEditModal
|
||||||
|
isOpen={isEditClaimOpen}
|
||||||
|
onClose={() => setIsEditClaimOpen(false)}
|
||||||
|
onOpenChange={(open) => setIsEditClaimOpen(open)}
|
||||||
|
claim={currentClaim}
|
||||||
|
onSave={(updatedClaim) => {
|
||||||
|
updateClaimMutation.mutate(updatedClaim);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
{totalPages > 1 && (
|
{totalPages > 1 && (
|
||||||
<div className="bg-white px-4 py-3 border-t border-gray-200">
|
<div className="bg-white px-4 py-3 border-t border-gray-200">
|
||||||
|
|||||||
@@ -80,7 +80,10 @@ export function normalizeToISOString(date: Date | string): string {
|
|||||||
* @param dateInput The date as a string (e.g., ISO, YYYY-MM-DD) or a Date object.
|
* @param dateInput The date as a string (e.g., ISO, YYYY-MM-DD) or a Date object.
|
||||||
* @returns A formatted date string.
|
* @returns A formatted date string.
|
||||||
*/
|
*/
|
||||||
export const formatDateToHumanReadable = (dateInput: string | Date): string => {
|
export const formatDateToHumanReadable = (
|
||||||
|
dateInput?: string | Date
|
||||||
|
): string => {
|
||||||
|
if (!dateInput) return "N/A";
|
||||||
// Create a Date object from the input.
|
// Create a Date object from the input.
|
||||||
// The Date constructor is quite flexible with various string formats.
|
// The Date constructor is quite flexible with various string formats.
|
||||||
const date = new Date(dateInput);
|
const date = new Date(dateInput);
|
||||||
|
|||||||
Reference in New Issue
Block a user