dress/resources/js/pages/admin/finance/expense/index.tsx

147 lines
5.2 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, Plus } 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';
import { DeleteConfirmation } from '@/components/modal/delete-confirmation';
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} className="gap-2">
<Plus className="size-4" />
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 */}
<DeleteConfirmation
isOpen={isDeleteDialogOpen}
onOpenChange={setIsDeleteDialogOpen}
title="Hapus pengeluaran?"
description={
<>
Tindakan ini tidak dapat dibatalkan. Pengeluaran <strong>{expenseToDelete?.name}</strong> akan dihapus secara permanen.
</>
}
onDelete={confirmDelete}
/>
{/* Bulk Delete Confirmation */}
<DeleteConfirmation
isOpen={isBulkDeleteDialogOpen}
onOpenChange={setIsBulkDeleteDialogOpen}
title={`Hapus ${rowsToDelete.length} pengeluaran?`}
description={
<>
Tindakan ini tidak dapat dibatalkan. <strong>{rowsToDelete.length}</strong> item yang terpilih akan dihapus secara permanen.
</>
}
onDelete={confirmBulkDelete}
/>
</div>
);
}
ExpenseIndex.layout = {
breadcrumbs: [
{
title: 'Keuangan',
},
],
};