From 7ff65246c18363e1899a36ae38ed9475c74604c5 Mon Sep 17 00:00:00 2001 From: Potenz Date: Fri, 15 Aug 2025 17:52:50 +0530 Subject: [PATCH] payemnt of paitientss - table added --- apps/Backend/src/routes/payments.ts | 38 +++++---- apps/Backend/src/storage/index.ts | 78 +++++++++++-------- .../payments/payment-view-modal.tsx | 35 --------- .../payments/payments-of-patient-table.tsx | 72 +++++++++++++++++ .../payments/payments-recent-table.tsx | 16 ++-- apps/Frontend/src/pages/payments-page.tsx | 4 + 6 files changed, 150 insertions(+), 93 deletions(-) delete mode 100644 apps/Frontend/src/components/payments/payment-view-modal.tsx create mode 100644 apps/Frontend/src/components/payments/payments-of-patient-table.tsx diff --git a/apps/Backend/src/routes/payments.ts b/apps/Backend/src/routes/payments.ts index 25885cd..14ea08a 100644 --- a/apps/Backend/src/routes/payments.ts +++ b/apps/Backend/src/routes/payments.ts @@ -94,26 +94,30 @@ router.get( "/patient/:patientId", async (req: Request, res: Response): Promise => { try { - const userId = req.user?.id; - if (!userId) return res.status(401).json({ message: "Unauthorized" }); + const patientIdParam = req.params.patientId; + if (!patientIdParam) { + return res.status(400).json({ message: "Missing patientId" }); + } + const patientId = parseInt(patientIdParam); + if (isNaN(patientId)) { + return res.status(400).json({ message: "Invalid patientId" }); + } + const limit = parseInt(req.query.limit as string) || 10; + const offset = parseInt(req.query.offset as string) || 0; - const parsedPatientId = parseIntOrError( - req.params.patientId, - "Patient ID" - ); + if (isNaN(patientId)) { + return res.status(400).json({ message: "Invalid patient ID" }); + } - const payments = await storage.getPaymentsByPatientId( - parsedPatientId, - userId - ); + const [payments, totalCount] = await Promise.all([ + storage.getRecentPaymentsByPatientId(patientId, limit, offset), + storage.getTotalPaymentCountByPatient(patientId), + ]); - if (!payments) - return res.status(404).json({ message: "No payments found for claim" }); - - res.status(200).json(payments); - } catch (err) { - console.error("Failed to fetch patient payments:", err); - res.status(500).json({ message: "Failed to fetch patient payments" }); + res.json({ payments, totalCount }); + } catch (error) { + console.error("Failed to retrieve payments for patient:", error); + res.status(500).json({ message: "Failed to retrieve patient payments" }); } } ); diff --git a/apps/Backend/src/storage/index.ts b/apps/Backend/src/storage/index.ts index fa35457..d47f688 100644 --- a/apps/Backend/src/storage/index.ts +++ b/apps/Backend/src/storage/index.ts @@ -183,14 +183,16 @@ export interface IStorage { ): Promise; deletePayment(id: number, userId: number): Promise; getPaymentById(id: number, userId: number): Promise; + getRecentPaymentsByPatientId( + patientId: number, + limit: number, + offset: number + ): Promise; + getTotalPaymentCountByPatient(patientId: number): Promise; getPaymentsByClaimId( claimId: number, userId: number ): Promise; - getPaymentsByPatientId( - patientId: number, - userId: number - ): Promise; getRecentPaymentsByUser( userId: number, limit: number, @@ -762,6 +764,45 @@ export const storage: IStorage = { await db.payment.delete({ where: { id } }); }, + async getRecentPaymentsByPatientId( + patientId: number, + limit: number, + offset: number + ): Promise { + const payments = await db.payment.findMany({ + where: { claim: { patientId } }, + orderBy: { createdAt: "desc" }, + skip: offset, + take: limit, + include: { + claim: { + include: { + serviceLines: true, + }, + }, + serviceLineTransactions: { + include: { + serviceLine: true, + }, + }, + updatedBy: true, + }, + }); + + return payments.map((payment) => ({ + ...payment, + patientName: payment.claim?.patientName ?? "", + paymentDate: payment.createdAt, + paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER", + })); + }, + + async getTotalPaymentCountByPatient(patientId: number): Promise { + return db.payment.count({ + where: { claim: { patientId } }, + }); + }, + async getPaymentById( id: number, userId: number @@ -824,35 +865,6 @@ export const storage: IStorage = { }; }, - async getPaymentsByPatientId( - patientId: number, - userId: number - ): Promise { - const payments = await db.payment.findMany({ - where: { patientId, userId }, - include: { - claim: { - include: { - serviceLines: true, - }, - }, - serviceLineTransactions: { - include: { - serviceLine: true, - }, - }, - updatedBy: true, - }, - }); - - return payments.map((payment) => ({ - ...payment, - patientName: payment.claim?.patientName ?? "", - paymentDate: payment.createdAt, - paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER", - })); - }, - async getRecentPaymentsByUser( userId: number, limit: number, diff --git a/apps/Frontend/src/components/payments/payment-view-modal.tsx b/apps/Frontend/src/components/payments/payment-view-modal.tsx deleted file mode 100644 index 5e7b713..0000000 --- a/apps/Frontend/src/components/payments/payment-view-modal.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { formatDateToHumanReadable } from "@/utils/dateUtils"; -import { z } from "zod"; -import { PaymentUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas"; - -type Payment = z.infer; - -interface PaymentViewModalProps { - isOpen: boolean; - onClose: () => void; - payment: Payment; -} - -export default function PaymentViewModal({ isOpen, onClose, payment }: PaymentViewModalProps) { - return ( - - - - Payment Details - - -
-
Payment ID: PAY-{payment.id.toString().padStart(4, "0")}
-
Claim ID: {payment.claimId}
-
Payer Name: {payment.payerName}
-
Amount Paid: ${payment.amountPaid.toFixed(2)}
-
Payment Date: {formatDateToHumanReadable(payment.paymentDate)}
-
Payment Method: {payment.paymentMethod}
-
Note: {payment.note || "—"}
-
Created At: {formatDateToHumanReadable(payment.createdAt)}
-
-
-
- ); -} diff --git a/apps/Frontend/src/components/payments/payments-of-patient-table.tsx b/apps/Frontend/src/components/payments/payments-of-patient-table.tsx new file mode 100644 index 0000000..ee29ed5 --- /dev/null +++ b/apps/Frontend/src/components/payments/payments-of-patient-table.tsx @@ -0,0 +1,72 @@ +import { useState } from "react"; +import { PatientTable } from "../patients/patient-table"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Patient } from "@repo/db/types"; +import PaymentsRecentTable from "./payments-recent-table"; + +export default function PaymentsOfPatientModal() { + const [selectedPatient, setSelectedPatient] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); + const [paymentsPage, setPaymentsPage] = useState(1); + + const handleSelectPatient = (patient: Patient | null) => { + if (patient) { + setSelectedPatient(patient); + setPaymentsPage(1); + setIsModalOpen(true); + } else { + setSelectedPatient(null); + setIsModalOpen(false); + } + }; + + return ( +
+ {/* Payments Section */} + {selectedPatient && ( + + + + Payments for {selectedPatient.firstName}{" "} + {selectedPatient.lastName} + + + Displaying recent payments for the selected patient. + + + + + + + )} + + {/* Patients Section */} + + + Patient Records + + Select any patient and View all their recent payments. + + + + + + +
+ ); +} diff --git a/apps/Frontend/src/components/payments/payments-recent-table.tsx b/apps/Frontend/src/components/payments/payments-recent-table.tsx index 319f415..8e6eb84 100644 --- a/apps/Frontend/src/components/payments/payments-recent-table.tsx +++ b/apps/Frontend/src/components/payments/payments-recent-table.tsx @@ -54,7 +54,7 @@ interface PaymentsRecentTableProps { allowCheckbox?: boolean; onSelectPayment?: (payment: PaymentWithExtras | null) => void; onPageChange?: (page: number) => void; - claimId?: number; + patientId?: number; } export default function PaymentsRecentTable({ @@ -63,7 +63,7 @@ export default function PaymentsRecentTable({ allowCheckbox, onSelectPayment, onPageChange, - claimId, + patientId, }: PaymentsRecentTableProps) { const { toast } = useToast(); @@ -91,8 +91,8 @@ export default function PaymentsRecentTable({ }; const getPaymentsQueryKey = () => - claimId - ? ["payments-recent", "claim", claimId, currentPage] + patientId + ? ["payments-recent", "patient", patientId, currentPage] : ["payments-recent", "global", currentPage]; const { @@ -102,8 +102,8 @@ export default function PaymentsRecentTable({ } = useQuery({ queryKey: getPaymentsQueryKey(), queryFn: async () => { - const endpoint = claimId - ? `/api/payments/claim/${claimId}?limit=${paymentsPerPage}&offset=${offset}` + const endpoint = patientId + ? `/api/payments/patient/${patientId}?limit=${paymentsPerPage}&offset=${offset}` : `/api/payments/recent?limit=${paymentsPerPage}&offset=${offset}`; const res = await apiRequest("GET", endpoint); @@ -272,7 +272,7 @@ export default function PaymentsRecentTable({ useEffect(() => { setCurrentPage(1); - }, [claimId]); + }, [patientId]); const totalPages = useMemo( () => Math.ceil((paymentsData?.totalCount || 0) / paymentsPerPage), @@ -412,7 +412,7 @@ export default function PaymentsRecentTable({ Error loading payments. - ) : paymentsData?.payments.length === 0 ? ( + ) : (paymentsData?.payments?.length ?? 0) === 0 ? ( + + {/* Recent Payments by Patients*/} +