dstpabuaran.com/resources/js/pages/admin/manage/invoice/columns.tsx
Yoga Pangestu 2a0b99c599 feat: implement invoice management features including index, create, update, delete, and print functionalities
- Added InvoiceIndex component for displaying a list of invoices with pagination and search capabilities.
- Created InvoiceShareButton component for sharing invoice details via WhatsApp.
- Developed InvoicePrint component for printing invoice details.
- Defined routes for invoice management including share and print functionalities.
- Implemented InvoiceTest to cover authentication, authorization, and CRUD operations for invoices.
2026-08-22 15:13:28 +07:00

174 lines
5.3 KiB
TypeScript

import type { ColumnDef } from '@tanstack/react-table';
import { Pencil, Printer, Trash2 } from 'lucide-react';
import { RowActions } from '@/components/data-display';
import { Badge } from '@/components/ui/badge';
import { edit, print } from '@/routes/admin/manage/invoices';
import { InvoiceShareButton } from './invoice-share-button';
export type Invoice = {
id: number;
invoice_number: string;
customer_name: string;
amount: number;
formatted_amount: string;
date: string;
formatted_date: string;
due_date: string | null;
formatted_due_date: string | null;
status: 'unpaid' | 'paid';
status_label: string;
description: string | null;
items_count: number;
};
export type InvoiceItem = {
id?: number;
name: string;
quantity: number;
price: number;
};
export type InvoiceForEdit = {
id: number;
invoice_number: string;
customer_name: string;
customer_phone: string | null;
customer_address: string | null;
date: string;
due_date: string | null;
status: 'unpaid' | 'paid';
description: string | null;
items: InvoiceItem[];
};
function getStatusBadge(status: string) {
const statusConfig: Record<string, { label: string; className: string }> = {
unpaid: {
label: 'Belum Lunas',
className: 'bg-yellow-100 text-yellow-800 hover:bg-yellow-100',
},
paid: {
label: 'Lunas',
className: 'bg-green-100 text-green-800 hover:bg-green-100',
},
};
const config = statusConfig[status] ?? statusConfig.unpaid;
return (
<Badge variant="secondary" className={config.className}>
{config.label}
</Badge>
);
}
type CreateColumnsParams = {
handleDeleteClick: (invoice: Invoice) => void;
can: (permission: string) => boolean;
};
export function createInvoiceColumns(
params: CreateColumnsParams,
): ColumnDef<Invoice>[] {
const { handleDeleteClick, can } = params;
const columns: ColumnDef<Invoice>[] = [
{
accessorKey: 'invoice_number',
header: () => <span>Nomor Invoice</span>,
cell: ({ row }) => (
<span className="font-medium">
{row.getValue('invoice_number') as string}
</span>
),
},
{
accessorKey: 'customer_name',
header: () => <span>Pelanggan</span>,
cell: ({ row }) => (
<span>{row.getValue('customer_name') as string}</span>
),
},
{
accessorKey: 'items_count',
header: () => <span>Item</span>,
cell: ({ row }) => (
<span>{row.getValue('items_count') as number}</span>
),
},
{
accessorKey: 'formatted_amount',
header: () => <span>Jumlah</span>,
cell: ({ row }) => (
<span>{row.getValue('formatted_amount') as string}</span>
),
},
{
accessorKey: 'formatted_date',
header: () => <span>Tanggal</span>,
cell: ({ row }) => (
<span>{row.getValue('formatted_date') as string}</span>
),
},
{
accessorKey: 'formatted_due_date',
header: () => <span>Jatuh Tempo</span>,
cell: ({ row }) => (
<span>
{(row.getValue('formatted_due_date') as string) ?? '-'}
</span>
),
},
{
accessorKey: 'status',
header: () => <span>Status</span>,
cell: ({ row }) => (
<span>{getStatusBadge(row.getValue('status') as string)}</span>
),
},
];
columns.push({
id: 'actions',
header: () => <span className="block text-center">Aksi</span>,
meta: {
className: 'w-[170px] text-center',
headerClassName: 'w-[170px] text-center',
},
cell: ({ row }) => {
const invoice = row.original;
return (
<div className="flex items-center justify-center gap-1">
<InvoiceShareButton invoice={invoice} />
<RowActions
actions={[
{
label: 'Cetak PDF',
icon: <Printer className="h-4 w-4" />,
href: print.url(invoice.id),
},
{
label: 'Edit',
icon: <Pencil className="h-4 w-4" />,
show: can('invoices.update'),
href: edit.url(invoice.id),
},
{
label: 'Hapus',
icon: (
<Trash2 className="h-4 w-4 text-destructive" />
),
show: can('invoices.delete'),
onClick: () => handleDeleteClick(invoice),
},
]}
/>
</div>
);
},
});
return columns;
}