257 lines
9.9 KiB
TypeScript
257 lines
9.9 KiB
TypeScript
import { DataTableColumnHeader } from '@/components/data-table-column-header';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from '@/components/ui/tooltip';
|
|
import { usePermission } from '@/hooks/use-permission';
|
|
import * as orderRoutes from '@/routes/order';
|
|
import type { Order } from '@/types/order';
|
|
import { ORDER_CHANNEL, ORDER_STATUS, PAYMENT_METHOD } from '@/types/order';
|
|
import { Link } from '@inertiajs/react';
|
|
import type { ColumnDef } from '@tanstack/react-table';
|
|
import { FileText, Pencil, Printer, Trash2 } from 'lucide-react';
|
|
|
|
interface ColumnProps {
|
|
onDelete: (order: Order) => void;
|
|
onPrint: (order: Order) => void;
|
|
}
|
|
|
|
export const getColumns = ({
|
|
onDelete,
|
|
onPrint,
|
|
}: ColumnProps): ColumnDef<Order>[] => [
|
|
{
|
|
accessorKey: 'user.name',
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="Pegawai" />
|
|
),
|
|
cell: ({ row }) => (
|
|
<span className="text-sm">{row.original.user?.name || '-'}</span>
|
|
),
|
|
meta: { title: 'Pegawai' },
|
|
},
|
|
{
|
|
accessorKey: 'order_number',
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="No. Order" />
|
|
),
|
|
cell: ({ row }) => (
|
|
<div className="flex flex-col">
|
|
<span className="font-medium">{row.original.order_number}</span>
|
|
<span className="text-xs text-muted-foreground">
|
|
{row.original.created_at_formatted}
|
|
</span>
|
|
</div>
|
|
),
|
|
meta: { title: 'No. Order' },
|
|
},
|
|
{
|
|
accessorKey: 'customer_name',
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="Pelanggan" />
|
|
),
|
|
cell: ({ row }) => (
|
|
<div className="flex flex-col">
|
|
<span>{row.original.customer_name}</span>
|
|
<Badge
|
|
variant="outline"
|
|
className="h-4 w-fit px-1 py-0 text-[10px]"
|
|
>
|
|
{ORDER_CHANNEL[row.original.order_channel] ||
|
|
row.original.order_channel}
|
|
</Badge>
|
|
</div>
|
|
),
|
|
meta: { title: 'Pelanggan' },
|
|
},
|
|
{
|
|
accessorKey: 'cogs',
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="Modal" />
|
|
),
|
|
cell: ({ row }) => (
|
|
<span className="text-sm text-muted-foreground">
|
|
{row.original.cogs_formatted}
|
|
</span>
|
|
),
|
|
meta: { title: 'Modal' },
|
|
},
|
|
{
|
|
accessorKey: 'items',
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="Item" />
|
|
),
|
|
cell: ({ row }) => {
|
|
const items = row.original.items;
|
|
|
|
if (!items || !Array.isArray(items) || items.length === 0) {
|
|
return (
|
|
<span className="text-xs text-muted-foreground italic">
|
|
-
|
|
</span>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-col gap-1">
|
|
{items.slice(0, 2).map((item, i) => (
|
|
<div key={i} className="text-xs">
|
|
<span className="text-muted-foreground">
|
|
{item.product?.name}:
|
|
</span>{' '}
|
|
<span className="font-medium text-primary">
|
|
{item.qty} x {item.price_formatted}
|
|
</span>
|
|
</div>
|
|
))}
|
|
{items.length > 2 && (
|
|
<span className="text-[10px] text-muted-foreground italic">
|
|
+{items.length - 2} item lainnya...
|
|
</span>
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
meta: { title: 'Item' },
|
|
},
|
|
{
|
|
accessorKey: 'total',
|
|
header: ({ column }) => (
|
|
<DataTableColumnHeader column={column} title="Total" />
|
|
),
|
|
cell: ({ row }) => {
|
|
const order = row.original;
|
|
|
|
return (
|
|
<div className="flex flex-col gap-1">
|
|
<div className="flex flex-col">
|
|
<span className="text-[10px] text-muted-foreground">
|
|
Subtotal:
|
|
</span>
|
|
<span className="text-sm font-medium">
|
|
{order.subtotal_formatted}
|
|
</span>
|
|
</div>
|
|
<div className="flex flex-col">
|
|
<span className="text-[10px] text-muted-foreground">
|
|
Potongan:
|
|
</span>
|
|
<span className="text-sm text-red-500">
|
|
- {order.discount_formatted}
|
|
</span>
|
|
</div>
|
|
<div className="mt-1 flex flex-col border-t pt-1">
|
|
<span className="text-[10px] text-muted-foreground">
|
|
Total Tagihan:
|
|
</span>
|
|
<span className="text-base font-bold text-primary">
|
|
{order.total_formatted}
|
|
</span>
|
|
</div>
|
|
<div className="flex flex-col">
|
|
<span className="text-[10px] text-muted-foreground">
|
|
Dibayar ({PAYMENT_METHOD[order.payment_method]}):
|
|
</span>
|
|
<span className="text-sm font-medium text-green-600">
|
|
{order.payment_formatted}
|
|
</span>
|
|
</div>
|
|
<Badge
|
|
className="mt-1 h-4 w-fit py-0 text-[10px]"
|
|
variant={
|
|
ORDER_STATUS[order.order_status]?.color ?? 'outline'
|
|
}
|
|
>
|
|
{ORDER_STATUS[order.order_status]?.label ??
|
|
order.order_status}
|
|
</Badge>
|
|
</div>
|
|
);
|
|
},
|
|
meta: { title: 'Total' },
|
|
},
|
|
{
|
|
id: 'actions',
|
|
header: 'Aksi',
|
|
cell: ({ row }) => {
|
|
const order = row.original;
|
|
const { hasPermission } = usePermission();
|
|
|
|
return (
|
|
<div className="flex items-center gap-2">
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Link href={orderRoutes.show(order.id).url}>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="text-emerald-600 hover:bg-emerald-50 hover:text-emerald-700 dark:hover:bg-emerald-950/20"
|
|
>
|
|
<FileText className="size-4" />
|
|
</Button>
|
|
</Link>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>Invoice</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="text-blue-600 hover:bg-blue-50 hover:text-blue-700 dark:hover:bg-blue-950/20"
|
|
onClick={() => onPrint(order)}
|
|
>
|
|
<Printer className="size-4" />
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>Cetak Struk</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
{hasPermission('Edit:Order') && (
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Link href={orderRoutes.edit(order.id).url}>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="text-yellow-600 hover:bg-yellow-50 hover:text-yellow-700 dark:hover:bg-yellow-950/20"
|
|
>
|
|
<Pencil className="size-4" />
|
|
</Button>
|
|
</Link>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>Ubah</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
)}
|
|
{hasPermission('Delete:Order') && (
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="text-red-600 hover:bg-red-50 hover:text-red-700 dark:hover:bg-red-950/20"
|
|
onClick={() => onDelete(order)}
|
|
>
|
|
<Trash2 className="size-4" />
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>Hapus</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
meta: { title: 'Aksi' },
|
|
},
|
|
];
|