158 lines
7.0 KiB
TypeScript
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(),
|
|
]}
|
|
/>
|
|
)
|
|
}
|