"use client"; import { useState, useEffect } from "react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter, DialogClose } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@/components/ui/pagination"; import { PlusCircle, Pencil, Trash2, Search, X, Loader2, RefreshCw, Users } from "lucide-react"; import EditJenisPendaftaran from "@/components/datatable/edit-jenis-pendaftaran"; import UploadExcelMahasiswa from "@/components/datatable/upload-excel-mahasiswa"; import { useToast } from "@/components/ui/toast-provider"; // Define the Mahasiswa type based on API route structure interface Mahasiswa { nim: string; nama: string; jk: "Pria" | "Wanita"; agama: string | null; kabupaten: string | null; provinsi: string | null; jenis_pendaftaran: string | null; tahun_angkatan: string; ipk: number | null; id_kelompok_keahlian: number | null; nama_kelompok_keahlian: string | null; status_kuliah: "Aktif" | "Cuti" | "Lulus" | "Non-Aktif"; semester: number; created_at: string; updated_at: string; } // Define the KelompokKeahlian type interface KelompokKeahlian { id_kk: number; nama_kelompok: string; } export default function DataTableMahasiswa() { const { showSuccess, showError } = useToast(); // State for data const [mahasiswa, setMahasiswa] = useState([]); const [filteredData, setFilteredData] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // State for filtering const [searchTerm, setSearchTerm] = useState(""); const [filterAngkatan, setFilterAngkatan] = useState(""); const [filterStatus, setFilterStatus] = useState(""); // State for pagination const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [paginatedData, setPaginatedData] = useState([]); // State for form const [formMode, setFormMode] = useState<"add" | "edit">("add"); const [formData, setFormData] = useState>({ jk: "Pria" }); const [isSubmitting, setIsSubmitting] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false); // State for delete confirmation const [deleteNim, setDeleteNim] = useState(null); const [isDeleting, setIsDeleting] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); // State for updating semester const [isUpdatingSemester, setIsUpdatingSemester] = useState(false); // State for jenis pendaftaran options const [jenisPendaftaranOptions, setJenisPendaftaranOptions] = useState([]); // State for kelompok keahlian options const [kelompokKeahlianOptions, setKelompokKeahlianOptions] = useState>([]); // State for kelompok keahlian CRUD const [kelompokKeahlianData, setKelompokKeahlianData] = useState([]); const [kelompokKeahlianFormMode, setKelompokKeahlianFormMode] = useState<"add" | "edit">("add"); const [kelompokKeahlianFormData, setKelompokKeahlianFormData] = useState>({}); const [isKelompokKeahlianSubmitting, setIsKelompokKeahlianSubmitting] = useState(false); const [isKelompokKeahlianDialogOpen, setIsKelompokKeahlianDialogOpen] = useState(false); const [deleteKelompokKeahlianId, setDeleteKelompokKeahlianId] = useState(null); const [isKelompokKeahlianDeleting, setIsKelompokKeahlianDeleting] = useState(false); const [isKelompokKeahlianDeleteDialogOpen, setIsKelompokKeahlianDeleteDialogOpen] = useState(false); // Fetch data on component mount useEffect(() => { fetchMahasiswa(); fetchJenisPendaftaranOptions(); fetchKelompokKeahlianOptions(); fetchKelompokKeahlianData(); }, []); // Filter data when search term or filter changes useEffect(() => { filterData(); }, [searchTerm, filterAngkatan, filterStatus, mahasiswa]); // Update paginated data when filtered data or pagination settings change useEffect(() => { paginateData(); }, [filteredData, currentPage, pageSize]); // Fetch mahasiswa data from API const fetchMahasiswa = async () => { try { setLoading(true); const response = await fetch("/api/keloladata/data-mahasiswa"); if (!response.ok) { throw new Error("Failed to fetch data"); } const data = await response.json(); setMahasiswa(data); setFilteredData(data); setError(null); } catch (err) { setError("Error fetching data. Please try again later."); console.error("Error fetching data:", err); } finally { setLoading(false); } }; // Fetch kelompok keahlian data const fetchKelompokKeahlianData = async () => { try { const response = await fetch("/api/keloladata/kelompok-keahlian"); if (!response.ok) { throw new Error("Failed to fetch kelompok keahlian data"); } const data = await response.json(); setKelompokKeahlianData(data); } catch (err) { console.error("Error fetching kelompok keahlian data:", err); } }; // Update semester for active students const handleUpdateSemester = async () => { try { setIsUpdatingSemester(true); const response = await fetch("/api/keloladata/update-semester", { method: "POST", headers: { "Content-Type": "application/json", }, }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || "Failed to update semesters"); } showSuccess("Berhasil!", "Semester mahasiswa aktif berhasil diperbarui"); // Refresh data after successful update await fetchMahasiswa(); } catch (err) { console.error("Error updating semesters:", err); showError("Gagal!", (err as Error).message); } finally { setIsUpdatingSemester(false); } }; // Filter data based on search term and filters const filterData = () => { let filtered = [...mahasiswa]; // Filter by search term (NIM or name) if (searchTerm) { filtered = filtered.filter( (item) => item.nim.toLowerCase().includes(searchTerm.toLowerCase()) || item.nama.toLowerCase().includes(searchTerm.toLowerCase()) ); } // Filter by angkatan if (filterAngkatan && filterAngkatan !== "all") { filtered = filtered.filter((item) => item.tahun_angkatan === filterAngkatan); } // Filter by status if (filterStatus && filterStatus !== "all") { filtered = filtered.filter((item) => item.status_kuliah === filterStatus); } setFilteredData(filtered); // Reset to first page when filters change setCurrentPage(1); }; // Paginate data const paginateData = () => { const startIndex = (currentPage - 1) * pageSize; const endIndex = startIndex + pageSize; setPaginatedData(filteredData.slice(startIndex, endIndex)); }; // Get total number of pages const getTotalPages = () => { return Math.ceil(filteredData.length / pageSize); }; // Handle page change const handlePageChange = (page: number) => { setCurrentPage(page); }; // Handle page size change const handlePageSizeChange = (size: string) => { setPageSize(Number(size)); setCurrentPage(1); // Reset to first page when changing page size }; // Reset form data const resetForm = () => { setFormData({ jk: "Pria", status_kuliah: "Aktif", semester: 1 }); }; // Handle form input changes const handleInputChange = (e: React.ChangeEvent) => { const { name, value } = e.target; if (name === "semester") { const numValue = value === "" ? 1 : parseInt(value); setFormData((prev) => ({ ...prev, [name]: numValue })); } else { setFormData((prev) => ({ ...prev, [name]: value })); } }; // Handle select input changes const handleSelectChange = (name: string, value: string) => { // Handle numeric fields if (name === "id_kelompok_keahlian") { const numValue = value === "" ? null : parseInt(value); setFormData((prev) => ({ ...prev, [name]: numValue })); } else if (name === "semester") { const numValue = value === "" ? 1 : parseInt(value); setFormData((prev) => ({ ...prev, [name]: numValue })); } else { setFormData((prev) => ({ ...prev, [name]: value })); } }; // Fetch jenis pendaftaran options const fetchJenisPendaftaranOptions = async () => { try { const response = await fetch("/api/keloladata/setting-jenis-pendaftaran"); if (!response.ok) { throw new Error("Failed to fetch jenis pendaftaran options"); } const data = await response.json(); const options = data.map((item: any) => item.jenis_pendaftaran); setJenisPendaftaranOptions(options); } catch (err) { console.error("Error fetching jenis pendaftaran options:", err); } }; // Fetch kelompok keahlian options const fetchKelompokKeahlianOptions = async () => { try { const response = await fetch("/api/keloladata/data-kelompok-keahlian"); if (!response.ok) { throw new Error("Failed to fetch kelompok keahlian options"); } const data = await response.json(); setKelompokKeahlianOptions(data); } catch (err) { console.error("Error fetching kelompok keahlian options:", err); } }; // Open form dialog for adding new mahasiswa const handleAdd = () => { setFormMode("add"); resetForm(); setIsDialogOpen(true); // Make sure we have the latest options fetchJenisPendaftaranOptions(); fetchKelompokKeahlianOptions(); }; // Open form dialog for editing mahasiswa const handleEdit = (data: Mahasiswa) => { setFormMode("edit"); setFormData(data); setIsDialogOpen(true); // Make sure we have the latest options fetchJenisPendaftaranOptions(); fetchKelompokKeahlianOptions(); }; // Open delete confirmation dialog const handleDeleteConfirm = (nim: string) => { setDeleteNim(nim); setIsDeleteDialogOpen(true); }; // Submit form for add/edit const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { setIsSubmitting(true); if (formMode === "add") { // Add new mahasiswa const response = await fetch("/api/keloladata/data-mahasiswa", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(formData), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || "Failed to add mahasiswa"); } showSuccess("Data mahasiswa berhasil ditambahkan!"); } else { // Edit existing mahasiswa const response = await fetch(`/api/keloladata/data-mahasiswa?nim=${formData.nim}`, { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify(formData), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || "Failed to update mahasiswa"); } showSuccess("Data mahasiswa berhasil diperbarui!"); } // Refresh data after successful operation await fetchMahasiswa(); setIsDialogOpen(false); resetForm(); } catch (err) { console.error("Error submitting form:", err); showError(`Gagal ${formMode === "add" ? "menambahkan" : "memperbarui"} data mahasiswa.`); } finally { setIsSubmitting(false); } }; // Delete mahasiswa const handleDelete = async () => { if (!deleteNim) return; try { setIsDeleting(true); const response = await fetch(`/api/keloladata/data-mahasiswa?nim=${deleteNim}`, { method: "DELETE", }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || "Failed to delete mahasiswa"); } // Refresh data after successful deletion await fetchMahasiswa(); setIsDeleteDialogOpen(false); setDeleteNim(null); showSuccess("Data mahasiswa berhasil dihapus!"); } catch (err) { console.error("Error deleting mahasiswa:", err); showError("Gagal menghapus data mahasiswa."); } finally { setIsDeleting(false); } }; // Kelompok Keahlian CRUD functions const handleKelompokKeahlianAdd = () => { setKelompokKeahlianFormMode("add"); setKelompokKeahlianFormData({}); setIsKelompokKeahlianDialogOpen(true); }; const handleKelompokKeahlianEdit = (data: KelompokKeahlian) => { setKelompokKeahlianFormMode("edit"); setKelompokKeahlianFormData(data); setIsKelompokKeahlianDialogOpen(true); }; const handleKelompokKeahlianDeleteConfirm = (id: number) => { setDeleteKelompokKeahlianId(id); setIsKelompokKeahlianDeleteDialogOpen(true); }; const handleKelompokKeahlianSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { setIsKelompokKeahlianSubmitting(true); if (kelompokKeahlianFormMode === "add") { // Add new kelompok keahlian const response = await fetch("/api/keloladata/kelompok-keahlian", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(kelompokKeahlianFormData), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || "Failed to add kelompok keahlian"); } showSuccess("Kelompok keahlian berhasil ditambahkan!"); } else { // Edit existing kelompok keahlian const response = await fetch("/api/keloladata/kelompok-keahlian", { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify(kelompokKeahlianFormData), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || "Failed to update kelompok keahlian"); } showSuccess("Kelompok keahlian berhasil diperbarui!"); } // Refresh data after successful operation await fetchKelompokKeahlianData(); await fetchKelompokKeahlianOptions(); setIsKelompokKeahlianDialogOpen(false); setKelompokKeahlianFormData({}); } catch (err) { console.error("Error submitting kelompok keahlian form:", err); showError(`Gagal ${kelompokKeahlianFormMode === "add" ? "menambahkan" : "memperbarui"} kelompok keahlian.`); } finally { setIsKelompokKeahlianSubmitting(false); } }; const handleKelompokKeahlianDelete = async () => { if (!deleteKelompokKeahlianId) return; try { setIsKelompokKeahlianDeleting(true); const response = await fetch(`/api/keloladata/kelompok-keahlian?id_kk=${deleteKelompokKeahlianId}`, { method: "DELETE", }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || "Failed to delete kelompok keahlian"); } // Refresh data after successful deletion await fetchKelompokKeahlianData(); await fetchKelompokKeahlianOptions(); setIsKelompokKeahlianDeleteDialogOpen(false); setDeleteKelompokKeahlianId(null); showSuccess("Kelompok keahlian berhasil dihapus!"); } catch (err) { console.error("Error deleting kelompok keahlian:", err); showError("Gagal menghapus kelompok keahlian."); } finally { setIsKelompokKeahlianDeleting(false); } }; // Get unique angkatan years for filter const getUniqueAngkatan = () => { const years = new Set(); mahasiswa.forEach((m) => years.add(m.tahun_angkatan)); return Array.from(years).sort(); }; // Generate pagination items const renderPaginationItems = () => { const totalPages = getTotalPages(); const items = []; // Always show first page items.push( handlePageChange(1)} > 1 ); // Show ellipsis if needed if (currentPage > 3) { items.push( ); } // Show pages around current page for (let i = Math.max(2, currentPage - 1); i <= Math.min(totalPages - 1, currentPage + 1); i++) { if (i === 1 || i === totalPages) continue; // Skip first and last pages as they're always shown items.push( handlePageChange(i)} > {i} ); } // Show ellipsis if needed if (currentPage < totalPages - 2) { items.push( ); } // Always show last page if there's more than one page if (totalPages > 1) { items.push( handlePageChange(totalPages)} > {totalPages} ); } return items; }; // Calculate the range of entries being displayed const getDisplayRange = () => { if (filteredData.length === 0) return { start: 0, end: 0 }; const start = (currentPage - 1) * pageSize + 1; const end = Math.min(currentPage * pageSize, filteredData.length); return { start, end }; }; return (

Data Mahasiswa

{/* Filters */}
setSearchTerm(e.target.value)} /> {searchTerm && ( setSearchTerm("")} /> )}
{/* Show entries selector */}
Show entries
{/* Table */} {loading ? (
) : error ? (
{error}
) : (
NIM Nama Jenis Kelamin Agama Kabupaten Provinsi Jenis Pendaftaran Tahun Angkatan Semester IPK Status Kuliah Kelompok Keahlian Aksi {paginatedData.length === 0 ? ( Tidak ada data yang sesuai dengan filter ) : ( paginatedData.map((mhs) => ( {mhs.nim} {mhs.nama} {mhs.jk} {mhs.agama} {mhs.kabupaten} {mhs.provinsi} {mhs.jenis_pendaftaran} {mhs.tahun_angkatan} {mhs.semester} {mhs.ipk ? Number(mhs.ipk).toFixed(2) : "-"} {mhs.status_kuliah} {mhs.nama_kelompok_keahlian || "-"}
)) )}
)} {/* Pagination info and controls */} {!loading && !error && filteredData.length > 0 && (
Showing {getDisplayRange().start} to {getDisplayRange().end} of {filteredData.length} entries
handlePageChange(Math.max(1, currentPage - 1))} className={currentPage === 1 ? "pointer-events-none opacity-50" : ""} /> {renderPaginationItems()} handlePageChange(Math.min(getTotalPages(), currentPage + 1))} className={currentPage === getTotalPages() ? "pointer-events-none opacity-50" : ""} />
)} {/* Add/Edit Dialog */} {formMode === "add" ? "Tambah Mahasiswa" : "Edit Mahasiswa"}
{/* Delete Confirmation Dialog */} Konfirmasi Hapus

Apakah Anda yakin ingin menghapus data mahasiswa ini?

Tindakan ini tidak dapat dibatalkan.

{/* Kelompok Keahlian CRUD Dialog */} {kelompokKeahlianFormMode === "add" ? "Tambah Kelompok Keahlian" : "Edit Kelompok Keahlian"} {/* Kelompok Keahlian Table */}
ID Nama Kelompok Aksi {kelompokKeahlianData.length === 0 ? ( Tidak ada data kelompok keahlian ) : ( kelompokKeahlianData.map((kelompok) => ( {kelompok.id_kk} {kelompok.nama_kelompok}
)) )}
{/* Kelompok Keahlian Form */}
setKelompokKeahlianFormData(prev => ({ ...prev, nama_kelompok: e.target.value }))} required placeholder="Masukkan nama kelompok keahlian" className="w-full" />
{/* Kelompok Keahlian Delete Confirmation Dialog */} Konfirmasi Hapus Kelompok Keahlian

Apakah Anda yakin ingin menghapus kelompok keahlian ini?

Tindakan ini tidak dapat dibatalkan.

); }