garage-erp/apps/dashboard/modules/bills/bill-payments-section.tsx
2026-04-23 14:38:41 +03:00

158 lines
7.0 KiB
TypeScript

"use client"
import { useRouter } from "next/navigation"
import {
BadgeDollarSignIcon,
CalendarIcon,
CreditCardIcon,
HashIcon,
UserIcon,
} from "lucide-react"
import { CrudResource } from "@/shared/data-view/resource-page"
import { ColumnHeader } from "@/shared/data-view/table-view"
import FormDialog from "@/shared/components/form-dialog"
import { Card, CardContent } from "@/shared/components/ui/card"
import { PAYMENT_MADE_ROUTES } from "@garage/api"
import { PaymentMadeForm } from "@/modules/payment-mades/payment-made-form"
import { useBill } from "./bill-context"
import { formatDate } from "@/shared/utils/formatters"
import { getFullName } from "@/shared/utils/getFullName"
import { toRelation } from "@/shared/lib/utils"
export function BillPaymentsSection() {
const bill = useBill()
const router = useRouter()
return (
<CrudResource<any>
extraParams={{ bill_id: bill?.id }}
routeKey={PAYMENT_MADE_ROUTES.INDEX}
getClient={(api) => ({
list: (query?: any) => api.paymentMades.list(query),
destroy: (id: string) => api.paymentMades.destroy(id),
})}
tableHeader={({ invalidateQuery }) => (
<Card className="mb-4">
<CardContent className="flex items-center justify-between">
<h2 className="text-base font-semibold">Payments Made</h2>
<FormDialog title="Record Payment">
{(resourceId) => (
<PaymentMadeForm
initialData={{
anount: bill?.balance_due,
vendor: toRelation(bill?.vendor?.id, getFullName(bill?.vendor as any)),
payment_made: bill?.balance_due != null ? String(bill.balance_due) : undefined,
}}
billId={bill?.id}
resourceId={resourceId}
onSuccess={() => {
router.refresh()
invalidateQuery()
}}
/>
)}
</FormDialog>
</CardContent>
</Card>
)}
columns={({ actionsColumn }) => [
{
accessorKey: "payment_number",
header: ({ column }) => <ColumnHeader column={column} title="Payment #" />,
cell: ({ row }) => {
const item = row.original as any
return (
<div className="flex items-center gap-2">
<HashIcon className="h-4 w-4 text-muted-foreground" />
<span className="font-medium">{item.payment_number || "—"}</span>
</div>
)
},
},
{
accessorKey: "vendor",
header: ({ column }) => <ColumnHeader column={column} title="Vendor" />,
cell: ({ row }) => {
const item = row.original as any
return (
<div className="flex items-center gap-2">
<UserIcon className="h-4 w-4 text-muted-foreground" />
<span>{item.vendor?.name || item.vendor_name || "—"}</span>
</div>
)
},
},
{
accessorKey: "payment_made",
header: ({ column }) => <ColumnHeader column={column} title="Amount" />,
cell: ({ row }) => {
const item = row.original as any
const amount = item.payment_made != null
? Number(item.payment_made).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
: "—"
return (
<div className="flex items-center gap-2">
<BadgeDollarSignIcon className="h-4 w-4 text-emerald-600" />
<span className="font-semibold text-emerald-700 dark:text-emerald-400">
{amount}
</span>
</div>
)
},
},
{
accessorKey: "payment_mode",
header: ({ column }) => <ColumnHeader column={column} title="Payment Mode" />,
cell: ({ row }) => {
const item = row.original as any
return (
<div className="flex items-center gap-2">
<CreditCardIcon className="h-4 w-4 text-muted-foreground" />
<span className="capitalize">
{item.payment_mode?.name || item.payment_mode_name || "—"}
</span>
</div>
)
},
},
{
accessorKey: "payment_date",
header: ({ column }) => <ColumnHeader column={column} title="Date" />,
cell: ({ row }) => {
const item = row.original as any
return (
<div className="flex items-center gap-2">
<CalendarIcon className="h-4 w-4 text-muted-foreground" />
<span>{formatDate(item.payment_date) || "—"}</span>
</div>
)
},
},
{
accessorKey: "notes",
header: () => <span>Notes</span>,
enableSorting: false,
cell: ({ row }) => {
const item = row.original as any
const notes = item.notes
if (!notes) return <span className="text-muted-foreground"></span>
return (
<span
className="max-w-50 truncate block text-muted-foreground"
title={notes}
>
{notes}
</span>
)
},
},
actionsColumn(),
]}
/>
)
}