dress/resources/js/pages/admin/master/product/index.tsx

133 lines
4.8 KiB
TypeScript

import { Head, Link } from '@inertiajs/react';
import type { Product, Category } from '@/types';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Trash2, Plus } from 'lucide-react';
import { DataTable } from '@/components/data-table';
import productRoutes from '@/routes/product';
import { useProductIndex } from './hooks/use-product-index';
import { getColumns } from './partials/columns';
import { ImagePreviewDialog } from '../../../../components/modal/image-preview';
import { DeleteConfirmation } from '@/components/modal/delete-confirmation';
export default function ProductIndex({ products, categories }: { products: Product[], categories: Category[] }) {
const {
selectedImage,
isDeleteDialogOpen,
isBulkDeleteDialogOpen,
productToDelete,
rowsToDelete,
rowSelection,
setRowSelection,
setRowsToDelete,
setIsDeleteDialogOpen,
setIsBulkDeleteDialogOpen,
onDelete,
confirmDelete,
confirmBulkDelete,
onToggleStatus,
onPreviewImage,
closeImagePreview,
} = useProductIndex();
const columns = getColumns({ onDelete, onToggleStatus, onPreviewImage });
return (
<div className="flex flex-col gap-6 p-6">
<Head title="Produk" />
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold tracking-tight">Produk</h1>
</div>
<Link href={productRoutes.create().url}>
<Button className="gap-2">
<Plus className="size-4" />
Tambah
</Button>
</Link>
</div>
<Card className="overflow-hidden border-none shadow-lg bg-card/50 backdrop-blur-sm p-5">
<CardContent className="p-0">
<DataTable
columns={columns}
data={products}
rowSelection={rowSelection}
onRowSelectionChange={setRowSelection}
filters={[
{
columnId: 'is_active',
title: 'Status',
options: [
{ label: 'Aktif', value: 'true' },
{ label: 'Tidak Aktif', value: 'false' },
]
},
{
columnId: 'categories',
title: 'Kategori',
options: categories.map((category) => ({
label: category.name,
value: String(category.id),
})),
},
]}
bulkActions={[
{
label: 'Hapus Terpilih',
onClick: (rows) => {
setRowsToDelete(rows);
setIsBulkDeleteDialogOpen(true);
},
icon: Trash2,
variant: 'destructive'
},
]}
/>
</CardContent>
</Card>
{/* Single Delete Confirmation */}
<DeleteConfirmation
isOpen={isDeleteDialogOpen}
onOpenChange={setIsDeleteDialogOpen}
title="Hapus produk?"
description={
<>
Tindakan ini tidak dapat dibatalkan. Produk <strong>{productToDelete?.name}</strong> akan dihapus secara permanen.
</>
}
onDelete={confirmDelete}
/>
{/* Bulk Delete Confirmation */}
<DeleteConfirmation
isOpen={isBulkDeleteDialogOpen}
onOpenChange={setIsBulkDeleteDialogOpen}
title={`Hapus ${rowsToDelete.length} produk?`}
description={
<>
Tindakan ini tidak dapat dibatalkan. <strong>{rowsToDelete.length}</strong> item yang terpilih akan dihapus secara permanen.
</>
}
onDelete={confirmBulkDelete}
/>
<ImagePreviewDialog
imageUrl={selectedImage}
onClose={closeImagePreview}
/>
</div>
);
}
ProductIndex.layout = {
breadcrumbs: [
{
title: 'Master',
},
],
};