skl/resources/js/pages/admin/verifikator/index_verifikator.tsx

411 lines
17 KiB
TypeScript

import React, { useEffect, useState } from "react";
import { useForm } from "@inertiajs/react";
import { PageProps } from "@/types";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Table,
TableHeader,
TableRow,
TableHead,
TableBody,
TableCell,
} from "@/components/ui/table";
// import { toast } from "react-toastify";
import { useToast } from "@/hooks/use-toast";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/components/ui/dialog";
import {
Search,
Plus,
Pencil,
Trash2,
ChevronLeft,
ChevronRight,
} from "lucide-react";
import AuthenticatedLayout from "@/layouts/authenticated-layout";
import { Head } from "@inertiajs/react";
import { Toaster } from "@/components/ui/toaster";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
interface Verifikator {
VerifikatorId: number | null;
NamaUnitKerja: string;
NamaKepala: string;
NIP: string;
}
const ITEMS_PER_PAGE = 5;
export default function VerifikatorIndex({
verifikator,
}: PageProps<{ verifikator: Verifikator[] }>) {
const {
data,
setData,
post,
put,
delete: destroy,
reset,
} = useForm<Verifikator>({
VerifikatorId: null,
NamaUnitKerja: "",
NamaKepala: "",
NIP: "",
});
const { toast } = useToast();
const [editing, setEditing] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [deleteConfirm, setDeleteConfirm] = useState<Verifikator | null>(
null
);
const [search, setSearch] = useState("");
const [filteredVerifikator, setFilteredVerifikator] = useState(verifikator);
const [currentPage, setCurrentPage] = useState(1);
useEffect(() => {
let filtered = verifikator;
if (search) {
filtered = filtered.filter(
(v) =>
v.NamaUnitKerja.toLowerCase().includes(
search.toLowerCase()
) ||
v.NamaKepala.toLowerCase().includes(search.toLowerCase()) ||
v.NIP.toLowerCase().includes(search.toLowerCase())
);
}
setFilteredVerifikator(filtered);
setCurrentPage(1);
}, [verifikator, search]);
const totalPages = Math.ceil(filteredVerifikator.length / ITEMS_PER_PAGE);
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
const endIndex = startIndex + ITEMS_PER_PAGE;
const currentItems = filteredVerifikator.slice(startIndex, endIndex);
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value);
};
const handlePageChange = (page: number) => {
setCurrentPage(page);
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (editing) {
put(`/admin/verifikator/${data.VerifikatorId}`, {
onSuccess: () => {
toast({
title: "Berhasil",
description: "Data verifikator berhasil diperbarui",
variant: "default",
});
setIsModalOpen(false);
reset();
setEditing(false);
},
onError: () => {
toast({
title: "Gagal",
description: "Terjadi kesalahan saat memperbarui data",
variant: "destructive",
});
},
});
} else {
post("/admin/verifikator", {
onSuccess: () => {
toast({
title: "Berhasil",
description: "Data verifikator berhasil ditambahkan",
variant: "default",
});
setIsModalOpen(false);
reset();
},
onError: () => {
toast({
title: "Gagal",
description: "Terjadi kesalahan saat menambah data",
variant: "destructive",
});
},
});
}
};
const handleEdit = (verifikator: Verifikator) => {
setData({ ...verifikator });
setEditing(true);
setIsModalOpen(true);
};
const handleDelete = () => {
if (deleteConfirm) {
destroy(`/admin/verifikator/${deleteConfirm.VerifikatorId}`, {
onSuccess: () => {
toast({
title: "Berhasil",
description: "Data verifikator berhasil dihapus",
variant: "default",
});
setDeleteConfirm(null);
},
onError: () => {
toast({
title: "Gagal",
description: "Terjadi kesalahan saat menghapus data",
variant: "destructive",
});
setDeleteConfirm(null);
},
});
}
};
return (
<AuthenticatedLayout header={"Data Dinas Lingkungan Hidup"}>
<Head title="Data Dinas Lingkungan Hidup" />
<div className="container mx-auto p-4">
<Card className="shadow-lg">
<CardHeader>
<div className="flex flex-col space-y-4">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
<div className="relative w-full md:w-96">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-4 w-4" />
<Input
type="text"
placeholder="Cari DLH..."
value={search}
onChange={handleSearch}
className="pl-10 pr-4 w-full"
/>
</div>
<Dialog
open={isModalOpen}
onOpenChange={setIsModalOpen}
>
<DialogTrigger asChild>
<Button
onClick={() => {
setEditing(false);
setIsModalOpen(true);
}}
>
<Plus className="h-4 w-4 mr-2" />
Tambah Dinas
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>
{editing
? "Edit Verifikator"
: "Tambah Verifikator"}
</DialogTitle>
</DialogHeader>
<form
onSubmit={handleSubmit}
className="space-y-4"
>
<div className="space-y-2">
<label>Nama Unit Kerja</label>
<Input
value={data.NamaUnitKerja}
onChange={(e) =>
setData(
"NamaUnitKerja",
e.target.value
)
}
placeholder="Masukkan nama unit kerja"
/>
</div>
<div className="space-y-2">
<label>Nama Kepala</label>
<Input
value={data.NamaKepala}
onChange={(e) =>
setData(
"NamaKepala",
e.target.value
)
}
placeholder="Masukkan nama kepala"
/>
</div>
<div className="space-y-2">
<label>NIP</label>
<Input
value={data.NIP}
onChange={(e) =>
setData(
"NIP",
e.target.value
)
}
placeholder="Masukkan NIP"
/>
</div>
<DialogFooter>
<Button type="submit">
{editing
? "Simpan"
: "Tambah"}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</div>
</div>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>No</TableHead>
<TableHead>Unit Kerja</TableHead>
<TableHead>Nama Kepala</TableHead>
<TableHead>NIP</TableHead>
<TableHead>Aksi</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{currentItems.map((item, index) => (
<TableRow key={item.VerifikatorId}>
<TableCell>
{startIndex + index + 1}
</TableCell>
<TableCell>
{item.NamaUnitKerja}
</TableCell>
<TableCell>{item.NamaKepala}</TableCell>
<TableCell>{item.NIP}</TableCell>
<TableCell className="flex gap-2">
<Button
variant="outline"
onClick={() => handleEdit(item)}
>
<Pencil className="h-4 w-4 mr-2" />
Edit
</Button>
<Button
variant="destructive"
onClick={() =>
setDeleteConfirm(item)
}
>
<Trash2 className="h-4 w-4 mr-2" />
Hapus
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className="flex items-center justify-between mt-4">
<div className="text-sm text-gray-500">
Showing {startIndex + 1} to{" "}
{Math.min(endIndex, filteredVerifikator.length)}{" "}
of {filteredVerifikator.length} entries
</div>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() =>
handlePageChange(currentPage - 1)
}
disabled={currentPage === 1}
>
<ChevronLeft className="h-4 w-4" />
</Button>
{Array.from(
{ length: totalPages },
(_, i) => i + 1
).map((page) => (
<Button
key={page}
variant={
currentPage === page
? "default"
: "outline"
}
size="sm"
onClick={() => handlePageChange(page)}
>
{page}
</Button>
))}
<Button
variant="outline"
size="sm"
onClick={() =>
handlePageChange(currentPage + 1)
}
disabled={currentPage === totalPages}
>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>
</CardContent>
</Card>
</div>
{deleteConfirm && (
<Dialog open={true} onOpenChange={() => setDeleteConfirm(null)}>
<DialogContent>
<DialogHeader>
<DialogTitle>Konfirmasi Hapus</DialogTitle>
</DialogHeader>
<p>
Apakah anda yakin ingin menghapus verifikator "
{deleteConfirm.NamaUnitKerja}"?
</p>
<DialogFooter>
<Button
variant="outline"
onClick={() => setDeleteConfirm(null)}
>
Batal
</Button>
<Button
variant="destructive"
onClick={handleDelete}
>
Hapus
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)}
<Toaster />
</AuthenticatedLayout>
);
}