import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { formatDateToHumanReadable, formatLocalDate, parseLocalDate, } from "@/utils/dateUtils"; import React, { useState } from "react"; import { PaymentStatus, paymentStatusOptions, PaymentMethod, paymentMethodOptions, PaymentWithExtras, NewTransactionPayload, } from "@repo/db/types"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Input } from "@/components/ui/input"; import { toast } from "@/hooks/use-toast"; import { X } from "lucide-react"; import { DateInput } from "@/components/ui/dateInput"; type PaymentEditModalProps = { isOpen: boolean; onOpenChange: (open: boolean) => void; onClose: () => void; onEditServiceLine: (payload: NewTransactionPayload) => void; isUpdatingServiceLine?: boolean; onUpdateStatus: (paymentId: number, status: PaymentStatus) => void; isUpdatingStatus?: boolean; payment: PaymentWithExtras | null; }; export default function PaymentEditModal({ isOpen, onOpenChange, onClose, payment, onEditServiceLine, isUpdatingServiceLine, onUpdateStatus, isUpdatingStatus, }: PaymentEditModalProps) { if (!payment) return null; const [expandedLineId, setExpandedLineId] = useState(null); const [paymentStatus, setPaymentStatus] = React.useState( payment.status ); const [formState, setFormState] = useState(() => { return { serviceLineId: 0, transactionId: "", paidAmount: 0, adjustedAmount: 0, method: paymentMethodOptions[1] as PaymentMethod, receivedDate: formatLocalDate(new Date()), payerName: "", notes: "", }; }); const handleEditServiceLine = (lineId: number) => { if (expandedLineId === lineId) { // Closing current line setExpandedLineId(null); return; } // Find line data const line = payment.claim.serviceLines.find((sl) => sl.id === lineId); if (!line) return; // updating form to show its data, while expanding. setFormState({ serviceLineId: line.id, transactionId: "", paidAmount: Number(line.totalDue) > 0 ? Number(line.totalDue) : 0, adjustedAmount: 0, method: paymentMethodOptions[1] as PaymentMethod, receivedDate: formatLocalDate(new Date()), payerName: "", notes: "", }); setExpandedLineId(lineId); }; const updateField = (field: string, value: any) => { setFormState((prev) => ({ ...prev, [field]: value, })); }; const handleSavePayment = async () => { if (!formState.serviceLineId) { toast({ title: "Error", description: "No service line selected.", variant: "destructive", }); return; } const paidAmount = Number(formState.paidAmount) || 0; const adjustedAmount = Number(formState.adjustedAmount) || 0; if (paidAmount < 0 || adjustedAmount < 0) { toast({ title: "Invalid Amount", description: "Amounts cannot be negative.", variant: "destructive", }); return; } if (paidAmount === 0 && adjustedAmount === 0) { toast({ title: "Invalid Amount", description: "Either paid or adjusted amount must be greater than zero.", variant: "destructive", }); return; } const line = payment.claim.serviceLines.find( (sl) => sl.id === formState.serviceLineId ); if (!line) { toast({ title: "Error", description: "Selected service line not found.", variant: "destructive", }); return; } const dueAmount = Number(line.totalDue); if (paidAmount > dueAmount) { toast({ title: "Invalid Payment", description: `Paid amount ($${paidAmount.toFixed( 2 )}) cannot exceed due amount ($${dueAmount.toFixed(2)}).`, variant: "destructive", }); return; } const payload: NewTransactionPayload = { paymentId: payment.id, serviceLineTransactions: [ { serviceLineId: formState.serviceLineId, transactionId: formState.transactionId || undefined, paidAmount: Number(formState.paidAmount), adjustedAmount: Number(formState.adjustedAmount) || 0, method: formState.method, receivedDate: parseLocalDate(formState.receivedDate), payerName: formState.payerName?.trim() || undefined, notes: formState.notes?.trim() || undefined, }, ], }; try { await onEditServiceLine(payload); toast({ title: "Success", description: "Payment Transaction added successfully.", }); setExpandedLineId(null); } catch (err) { console.error(err); toast({ title: "Error", description: "Failed to save payment." }); } }; const handlePayFullDue = async ( line: (typeof payment.claim.serviceLines)[0] ) => { if (!line || !payment) { toast({ title: "Error", description: "Service line or payment data missing.", variant: "destructive", }); return; } const dueAmount = Number(line.totalDue); if (isNaN(dueAmount) || dueAmount <= 0) { toast({ title: "No Due", description: "This service line has no outstanding balance.", variant: "destructive", }); return; } const payload: NewTransactionPayload = { paymentId: payment.id, serviceLineTransactions: [ { serviceLineId: line.id, paidAmount: dueAmount, adjustedAmount: 0, method: paymentMethodOptions[1] as PaymentMethod, // Maybe make dynamic later receivedDate: new Date(), }, ], }; try { await onEditServiceLine(payload); toast({ title: "Success", description: `Full due amount ($${dueAmount.toFixed( 2 )}) paid for ${line.procedureCode}`, }); } catch (err) { console.error(err); toast({ title: "Error", description: "Failed to update payment.", variant: "destructive", }); } }; return (
Edit Payment View and manage payments applied to service lines. {/* Close button in top-right */}
{/* Claim + Patient Info */}

{payment.claim.patientName}

Claim #{payment.claimId.toString().padStart(4, "0")} Service Date:{" "} {formatDateToHumanReadable(payment.claim.serviceDate)}
{/* Payment Summary + Metadata */}
{/* Payment Info */}

Payment Info

Total Billed:{" "} ${Number(payment.totalBilled || 0).toFixed(2)}

Total Paid:{" "} ${Number(payment.totalPaid || 0).toFixed(2)}

Total Due:{" "} ${Number(payment.totalDue || 0).toFixed(2)}

{/* Status Selector */}
{/* Metadata */}

Metadata

Created At:{" "} {payment.createdAt ? formatDateToHumanReadable(payment.createdAt) : "N/A"}

Last Updated At:{" "} {payment.updatedAt ? formatDateToHumanReadable(payment.updatedAt) : "N/A"}

{/* Service Lines Payments */}

Service Lines

{payment.claim.serviceLines.length > 0 ? ( payment.claim.serviceLines.map((line) => { const isExpanded = expandedLineId === line.id; return (
{/* Top Info */}

Procedure Code:{" "} {line.procedureCode}

Billed:{" "} ${Number(line.totalBilled || 0).toFixed(2)}

Paid:{" "} ${Number(line.totalPaid || 0).toFixed(2)}

Adjusted:{" "} ${Number(line.totalAdjusted || 0).toFixed(2)}

Due:{" "} ${Number(line.totalDue || 0).toFixed(2)}

{/* Action Buttons */}
{/* Expanded Partial Payment Form */} {isExpanded && (
updateField( "paidAmount", parseFloat(e.target.value) ) } />
updateField( "adjustedAmount", parseFloat(e.target.value) ) } />
{ if (date) { const localDate = formatLocalDate(date); updateField("receivedDate", localDate); } else { updateField("receivedDate", null); } }} disableFuture />
updateField("payerName", e.target.value) } />
updateField("notes", e.target.value) } />
)}
); }) ) : (

No service lines available.

)}
{/* Transactions Overview */}

All Transactions

{payment.serviceLineTransactions.length > 0 ? ( payment.serviceLineTransactions.map((tx) => (
{/* Transaction ID */} {tx.id && (

Transaction ID:{" "} {tx.id}

)} {/* Procedure Code */} {tx.serviceLine?.procedureCode && (

Procedure Code:{" "} {tx.serviceLine.procedureCode}

)} {/* Paid Amount */}

Paid Amount:{" "} ${Number(tx.paidAmount).toFixed(2)}

{/* Adjusted Amount */} {Number(tx.adjustedAmount) > 0 && (

Adjusted Amount: {" "} ${Number(tx.adjustedAmount).toFixed(2)}

)} {/* Date */}

Date:{" "} {formatDateToHumanReadable(tx.receivedDate)}

{/* Method */}

Method:{" "} {tx.method}

{/* Payer Name */} {tx.payerName && tx.payerName.trim() !== "" && (

Payer Name:{" "} {tx.payerName}

)} {/* Notes */} {tx.notes && tx.notes.trim() !== "" && (

Notes:{" "} {tx.notes}

)}
)) ) : (

No transactions recorded.

)}
{/* Actions */}
); }