162 lines
6.4 KiB
TypeScript
162 lines
6.4 KiB
TypeScript
import { Head } from '@inertiajs/react';
|
|
import type { Expense } from '@/types';
|
|
import { Card, CardContent } from '@/components/ui/card';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Trash2, X } from 'lucide-react';
|
|
import { DataTable } from '@/components/data-table';
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogMedia,
|
|
AlertDialogTitle,
|
|
} from "@/components/ui/alert-dialog"
|
|
import { Dialog, DialogContent } from "@/components/ui/dialog"
|
|
|
|
import { useExpenseIndex } from './hooks/use-expense-index';
|
|
import { getColumns } from './partials/columns';
|
|
import { ExpenseFormModal } from './partials/expense-form-modal';
|
|
|
|
export default function ExpenseIndex({ expenses }: { expenses: Expense[] }) {
|
|
const {
|
|
isFormOpen,
|
|
selectedExpense,
|
|
isDeleteDialogOpen,
|
|
isBulkDeleteDialogOpen,
|
|
expenseToDelete,
|
|
rowsToDelete,
|
|
rowSelection,
|
|
selectedProof,
|
|
setRowSelection,
|
|
setRowsToDelete,
|
|
setIsDeleteDialogOpen,
|
|
setIsBulkDeleteDialogOpen,
|
|
setSelectedProof,
|
|
onAdd,
|
|
onEdit,
|
|
onDelete,
|
|
confirmDelete,
|
|
confirmBulkDelete,
|
|
closeForm,
|
|
onPreviewImage,
|
|
} = useExpenseIndex();
|
|
|
|
const columns = getColumns({ onEdit, onDelete, onPreviewImage });
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6 p-6">
|
|
<Head title="Pengeluaran" />
|
|
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-bold tracking-tight">Pengeluaran</h1>
|
|
</div>
|
|
<Button onClick={onAdd}>
|
|
Tambah
|
|
</Button>
|
|
</div>
|
|
|
|
<ExpenseFormModal
|
|
isOpen={isFormOpen}
|
|
onClose={closeForm}
|
|
expense={selectedExpense}
|
|
/>
|
|
|
|
<Dialog open={!!selectedProof} onOpenChange={() => setSelectedProof(null)}>
|
|
<DialogContent className="max-w-3xl p-0 overflow-hidden border-none bg-transparent shadow-none">
|
|
<div className="relative group">
|
|
<img
|
|
src={selectedProof || ''}
|
|
alt="Proof"
|
|
className="w-full h-auto max-h-[80vh] object-contain rounded-lg"
|
|
/>
|
|
<button
|
|
onClick={() => setSelectedProof(null)}
|
|
className="absolute top-4 right-4 bg-black/50 hover:bg-black/70 text-white rounded-full p-2 backdrop-blur-sm transition-all shadow-xl"
|
|
>
|
|
<X className="size-5" />
|
|
</button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
|
|
<Card className="overflow-hidden border-none shadow-lg bg-card/50 backdrop-blur-sm p-5">
|
|
<CardContent className="p-0">
|
|
<DataTable
|
|
columns={columns}
|
|
data={expenses}
|
|
rowSelection={rowSelection}
|
|
onRowSelectionChange={setRowSelection}
|
|
bulkActions={[
|
|
{
|
|
label: 'Hapus Terpilih',
|
|
onClick: (rows) => {
|
|
setRowsToDelete(rows);
|
|
setIsBulkDeleteDialogOpen(true);
|
|
},
|
|
icon: Trash2,
|
|
variant: 'destructive'
|
|
},
|
|
]}
|
|
/>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Single Delete Confirmation */}
|
|
<AlertDialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
|
|
<AlertDialogContent size="default">
|
|
<AlertDialogHeader>
|
|
<AlertDialogMedia className="bg-destructive/10 text-destructive dark:bg-destructive/20 dark:text-destructive">
|
|
<Trash2 className="size-5" />
|
|
</AlertDialogMedia>
|
|
<AlertDialogTitle>Hapus pengeluaran?</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
Tindakan ini tidak dapat dibatalkan. Pengeluaran <strong>{expenseToDelete?.name}</strong> akan dihapus secara permanen.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
|
<AlertDialogAction onClick={confirmDelete} variant="destructive">Hapus</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
|
|
{/* Bulk Delete Confirmation */}
|
|
<AlertDialog open={isBulkDeleteDialogOpen} onOpenChange={setIsBulkDeleteDialogOpen}>
|
|
<AlertDialogContent size="default">
|
|
<AlertDialogHeader>
|
|
<AlertDialogMedia className="bg-destructive/10 text-destructive dark:bg-destructive/20 dark:text-destructive">
|
|
<Trash2 className="size-5" />
|
|
</AlertDialogMedia>
|
|
<AlertDialogTitle>Hapus {rowsToDelete.length} pengeluaran?</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
Tindakan ini tidak dapat dibatalkan. <strong>{rowsToDelete.length}</strong> item yang terpilih akan dihapus secara permanen.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
onClick={confirmBulkDelete}
|
|
variant="destructive"
|
|
>
|
|
Hapus
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ExpenseIndex.layout = {
|
|
breadcrumbs: [
|
|
{
|
|
title: 'Keuangan',
|
|
},
|
|
],
|
|
};
|