diff --git a/app/api/keloladata/kelompok-keahlian/route.ts b/app/api/keloladata/kelompok-keahlian/route.ts new file mode 100644 index 0000000..18dbc60 --- /dev/null +++ b/app/api/keloladata/kelompok-keahlian/route.ts @@ -0,0 +1,214 @@ +import { NextRequest, NextResponse } from 'next/server'; +import supabase from '@/lib/db'; + +// GET - Get all kelompok keahlian +export async function GET() { + try { + const { data, error } = await supabase + .from('kelompok_keahlian') + .select('id_kk, nama_kelompok') + .order('id_kk', { ascending: true }); + + if (error) { + console.error('Error fetching kelompok keahlian:', error); + return NextResponse.json( + { error: 'Failed to fetch kelompok keahlian' }, + { status: 500 } + ); + } + + return NextResponse.json(data); + } catch (error) { + console.error('Error fetching kelompok keahlian:', error); + return NextResponse.json( + { error: 'Failed to fetch kelompok keahlian' }, + { status: 500 } + ); + } +} + +// POST - Create new kelompok keahlian +export async function POST(request: NextRequest) { + try { + const { nama_kelompok } = await request.json(); + + if (!nama_kelompok || nama_kelompok.trim() === '') { + return NextResponse.json( + { error: 'Nama kelompok keahlian is required' }, + { status: 400 } + ); + } + + // Check if nama_kelompok already exists + const { data: existingData, error: existingError } = await supabase + .from('kelompok_keahlian') + .select('id_kk') + .ilike('nama_kelompok', nama_kelompok.trim()); + + if (existingError) { + console.error('Error checking existing kelompok keahlian:', existingError); + return NextResponse.json( + { error: 'Failed to check existing kelompok keahlian' }, + { status: 500 } + ); + } + + if (existingData && existingData.length > 0) { + return NextResponse.json( + { error: 'Kelompok keahlian dengan nama tersebut sudah ada' }, + { status: 409 } + ); + } + + const { data, error } = await supabase + .from('kelompok_keahlian') + .insert([{ nama_kelompok: nama_kelompok.trim() }]) + .select('id_kk, nama_kelompok') + .single(); + + if (error) { + console.error('Error creating kelompok keahlian:', error); + return NextResponse.json( + { error: 'Failed to create kelompok keahlian' }, + { status: 500 } + ); + } + + return NextResponse.json(data, { status: 201 }); + } catch (error) { + console.error('Error creating kelompok keahlian:', error); + return NextResponse.json( + { error: 'Failed to create kelompok keahlian' }, + { status: 500 } + ); + } +} + +// PUT - Update kelompok keahlian +export async function PUT(request: NextRequest) { + try { + const { id_kk, nama_kelompok } = await request.json(); + + if (!id_kk || !nama_kelompok || nama_kelompok.trim() === '') { + return NextResponse.json( + { error: 'ID dan nama kelompok keahlian are required' }, + { status: 400 } + ); + } + + // Check if kelompok keahlian exists + const { data: existingData, error: existingError } = await supabase + .from('kelompok_keahlian') + .select('id_kk') + .eq('id_kk', id_kk) + .single(); + + if (existingError || !existingData) { + return NextResponse.json( + { error: 'Kelompok keahlian tidak ditemukan' }, + { status: 404 } + ); + } + + // Check if nama_kelompok already exists for other records + const { data: duplicateData, error: duplicateError } = await supabase + .from('kelompok_keahlian') + .select('id_kk') + .ilike('nama_kelompok', nama_kelompok.trim()) + .neq('id_kk', id_kk); + + if (duplicateError) { + console.error('Error checking duplicate kelompok keahlian:', duplicateError); + return NextResponse.json( + { error: 'Failed to check duplicate kelompok keahlian' }, + { status: 500 } + ); + } + + if (duplicateData && duplicateData.length > 0) { + return NextResponse.json( + { error: 'Kelompok keahlian dengan nama tersebut sudah ada' }, + { status: 409 } + ); + } + + const { data, error } = await supabase + .from('kelompok_keahlian') + .update({ nama_kelompok: nama_kelompok.trim() }) + .eq('id_kk', id_kk) + .select('id_kk, nama_kelompok') + .single(); + + if (error) { + console.error('Error updating kelompok keahlian:', error); + return NextResponse.json( + { error: 'Failed to update kelompok keahlian' }, + { status: 500 } + ); + } + + return NextResponse.json(data); + } catch (error) { + console.error('Error updating kelompok keahlian:', error); + return NextResponse.json( + { error: 'Failed to update kelompok keahlian' }, + { status: 500 } + ); + } +} + +// DELETE - Delete kelompok keahlian +export async function DELETE(request: NextRequest) { + try { + const { searchParams } = new URL(request.url); + const id_kk = searchParams.get('id_kk'); + + if (!id_kk) { + return NextResponse.json( + { error: 'ID kelompok keahlian is required' }, + { status: 400 } + ); + } + + // Check if kelompok keahlian exists + const { data: existingData, error: existingError } = await supabase + .from('kelompok_keahlian') + .select('id_kk') + .eq('id_kk', id_kk) + .single(); + + if (existingError || !existingData) { + return NextResponse.json( + { error: 'Kelompok keahlian tidak ditemukan' }, + { status: 404 } + ); + } + + // Check if kelompok keahlian is being used in other tables + // You might want to add foreign key checks here depending on your database structure + + const { error } = await supabase + .from('kelompok_keahlian') + .delete() + .eq('id_kk', id_kk); + + if (error) { + console.error('Error deleting kelompok keahlian:', error); + return NextResponse.json( + { error: 'Failed to delete kelompok keahlian' }, + { status: 500 } + ); + } + + return NextResponse.json( + { message: 'Kelompok keahlian berhasil dihapus' }, + { status: 200 } + ); + } catch (error) { + console.error('Error deleting kelompok keahlian:', error); + return NextResponse.json( + { error: 'Failed to delete kelompok keahlian' }, + { status: 500 } + ); + } +} diff --git a/components/datatable/data-table-mahasiswa.tsx b/components/datatable/data-table-mahasiswa.tsx index 2c2ce05..0f81996 100644 --- a/components/datatable/data-table-mahasiswa.tsx +++ b/components/datatable/data-table-mahasiswa.tsx @@ -43,11 +43,13 @@ import { Search, X, Loader2, - RefreshCw + 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; @@ -67,6 +69,12 @@ interface Mahasiswa { 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 @@ -107,11 +115,22 @@ export default function DataTableMahasiswa() { // 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 @@ -146,6 +165,22 @@ export default function DataTableMahasiswa() { } }; + // 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 { @@ -400,6 +435,104 @@ export default function DataTableMahasiswa() { } }; + // 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(); @@ -505,6 +638,13 @@ export default function DataTableMahasiswa() { )} Update Semester Mahasiswa Aktif + @@ -701,17 +841,17 @@ export default function DataTableMahasiswa() { )} - {/* Add/Edit Dialog */} - - + {/* Add/Edit Dialog */} + + {formMode === "add" ? "Tambah Mahasiswa" : "Edit Mahasiswa"}
-
-
+
+
- + - - @@ -903,7 +1043,7 @@ export default function DataTableMahasiswa() { {/* Delete Confirmation Dialog */} - + Konfirmasi Hapus @@ -913,9 +1053,9 @@ export default function DataTableMahasiswa() { Tindakan ini tidak dapat dibatalkan.

- + - @@ -923,6 +1063,7 @@ export default function DataTableMahasiswa() { variant="destructive" onClick={handleDelete} disabled={isDeleting} + className="w-full sm:w-auto" > {isDeleting && } Hapus @@ -930,6 +1071,128 @@ export default function DataTableMahasiswa() {
+ + {/* 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. +

+
+ + + + + + +
+
); } \ No newline at end of file