Coba terus
This commit is contained in:
214
app/api/keloladata/kelompok-keahlian/route.ts
Normal file
214
app/api/keloladata/kelompok-keahlian/route.ts
Normal file
@@ -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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<Array<{id_kk: number, nama_kelompok: string}>>([]);
|
||||
|
||||
// State for kelompok keahlian CRUD
|
||||
const [kelompokKeahlianData, setKelompokKeahlianData] = useState<KelompokKeahlian[]>([]);
|
||||
const [kelompokKeahlianFormMode, setKelompokKeahlianFormMode] = useState<"add" | "edit">("add");
|
||||
const [kelompokKeahlianFormData, setKelompokKeahlianFormData] = useState<Partial<KelompokKeahlian>>({});
|
||||
const [isKelompokKeahlianSubmitting, setIsKelompokKeahlianSubmitting] = useState(false);
|
||||
const [isKelompokKeahlianDialogOpen, setIsKelompokKeahlianDialogOpen] = useState(false);
|
||||
const [deleteKelompokKeahlianId, setDeleteKelompokKeahlianId] = useState<number | null>(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<string>();
|
||||
@@ -505,6 +638,13 @@ export default function DataTableMahasiswa() {
|
||||
)}
|
||||
Update Semester Mahasiswa Aktif
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleKelompokKeahlianAdd}
|
||||
>
|
||||
<Users className="mr-2 h-4 w-4" />
|
||||
Kelola Kelompok Keahlian
|
||||
</Button>
|
||||
<EditJenisPendaftaran onUpdateSuccess={fetchMahasiswa} />
|
||||
<UploadExcelMahasiswa onUploadSuccess={fetchMahasiswa} />
|
||||
</div>
|
||||
@@ -701,17 +841,17 @@ export default function DataTableMahasiswa() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Add/Edit Dialog */}
|
||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||||
<DialogContent className="sm:max-w-[900px] max-h-[90vh] overflow-y-auto">
|
||||
{/* Add/Edit Dialog */}
|
||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||||
<DialogContent className="w-[95vw] max-w-[1200px] max-h-[90vh] overflow-y-auto p-4 sm:p-6">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{formMode === "add" ? "Tambah Mahasiswa" : "Edit Mahasiswa"}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="grid gap-6 py-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div className="grid gap-4 sm:gap-6 py-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="nim" className="text-sm font-medium">
|
||||
NIM <span className="text-destructive">*</span>
|
||||
@@ -838,7 +978,7 @@ export default function DataTableMahasiswa() {
|
||||
value={formData.id_kelompok_keahlian?.toString() || ""}
|
||||
onValueChange={(value) => handleSelectChange("id_kelompok_keahlian", value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Pilih Kelompok Keahlian" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -886,13 +1026,13 @@ export default function DataTableMahasiswa() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<DialogFooter className="flex flex-col sm:flex-row gap-2 sm:gap-0">
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="outline">
|
||||
<Button type="button" variant="outline" className="w-full sm:w-auto">
|
||||
Batal
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<Button type="submit" disabled={isSubmitting}>
|
||||
<Button type="submit" disabled={isSubmitting} className="w-full sm:w-auto">
|
||||
{isSubmitting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
{formMode === "add" ? "Tambah" : "Simpan"}
|
||||
</Button>
|
||||
@@ -903,7 +1043,7 @@ export default function DataTableMahasiswa() {
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<Dialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
|
||||
<DialogContent className="sm:max-w-[400px]">
|
||||
<DialogContent className="w-[95vw] max-w-[400px] p-4 sm:p-6">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Konfirmasi Hapus</DialogTitle>
|
||||
</DialogHeader>
|
||||
@@ -913,9 +1053,9 @@ export default function DataTableMahasiswa() {
|
||||
Tindakan ini tidak dapat dibatalkan.
|
||||
</p>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<DialogFooter className="flex flex-col sm:flex-row gap-2 sm:gap-0">
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="outline">
|
||||
<Button type="button" variant="outline" className="w-full sm:w-auto">
|
||||
Batal
|
||||
</Button>
|
||||
</DialogClose>
|
||||
@@ -923,6 +1063,7 @@ export default function DataTableMahasiswa() {
|
||||
variant="destructive"
|
||||
onClick={handleDelete}
|
||||
disabled={isDeleting}
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
{isDeleting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
Hapus
|
||||
@@ -930,6 +1071,128 @@ export default function DataTableMahasiswa() {
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Kelompok Keahlian CRUD Dialog */}
|
||||
<Dialog open={isKelompokKeahlianDialogOpen} onOpenChange={setIsKelompokKeahlianDialogOpen}>
|
||||
<DialogContent className="w-[95vw] max-w-[600px] max-h-[90vh] overflow-y-auto p-4 sm:p-6">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{kelompokKeahlianFormMode === "add" ? "Tambah Kelompok Keahlian" : "Edit Kelompok Keahlian"}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
{/* Kelompok Keahlian Table */}
|
||||
<div className="border rounded-md mb-4 overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="min-w-[60px]">ID</TableHead>
|
||||
<TableHead className="min-w-[200px]">Nama Kelompok</TableHead>
|
||||
<TableHead className="text-right min-w-[100px]">Aksi</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{kelompokKeahlianData.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={3} className="text-center py-4">
|
||||
Tidak ada data kelompok keahlian
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
kelompokKeahlianData.map((kelompok) => (
|
||||
<TableRow key={kelompok.id_kk}>
|
||||
<TableCell className="font-medium">{kelompok.id_kk}</TableCell>
|
||||
<TableCell className="break-words">{kelompok.nama_kelompok}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<div className="flex justify-end gap-1 sm:gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => handleKelompokKeahlianEdit(kelompok)}
|
||||
className="h-8 w-8 p-0 sm:h-9 sm:w-auto sm:px-3"
|
||||
>
|
||||
<Pencil className="h-3 w-3 sm:h-4 sm:w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-destructive hover:bg-destructive/10 h-8 w-8 p-0 sm:h-9 sm:w-auto sm:px-3"
|
||||
onClick={() => handleKelompokKeahlianDeleteConfirm(kelompok.id_kk)}
|
||||
>
|
||||
<Trash2 className="h-3 w-3 sm:h-4 sm:w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{/* Kelompok Keahlian Form */}
|
||||
<form onSubmit={handleKelompokKeahlianSubmit}>
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="nama_kelompok" className="text-sm font-medium">
|
||||
Nama Kelompok Keahlian <span className="text-destructive">*</span>
|
||||
</label>
|
||||
<Input
|
||||
id="nama_kelompok"
|
||||
name="nama_kelompok"
|
||||
value={kelompokKeahlianFormData.nama_kelompok || ""}
|
||||
onChange={(e) => setKelompokKeahlianFormData(prev => ({ ...prev, nama_kelompok: e.target.value }))}
|
||||
required
|
||||
placeholder="Masukkan nama kelompok keahlian"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="flex flex-col sm:flex-row gap-2 sm:gap-0">
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="outline" className="w-full sm:w-auto">
|
||||
Tutup
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<Button type="submit" disabled={isKelompokKeahlianSubmitting} className="w-full sm:w-auto">
|
||||
{isKelompokKeahlianSubmitting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
{kelompokKeahlianFormMode === "add" ? "Tambah" : "Simpan"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Kelompok Keahlian Delete Confirmation Dialog */}
|
||||
<Dialog open={isKelompokKeahlianDeleteDialogOpen} onOpenChange={setIsKelompokKeahlianDeleteDialogOpen}>
|
||||
<DialogContent className="w-[95vw] max-w-[400px] p-4 sm:p-6">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Konfirmasi Hapus Kelompok Keahlian</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="py-4">
|
||||
<p>Apakah Anda yakin ingin menghapus kelompok keahlian ini?</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Tindakan ini tidak dapat dibatalkan.
|
||||
</p>
|
||||
</div>
|
||||
<DialogFooter className="flex flex-col sm:flex-row gap-2 sm:gap-0">
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="outline" className="w-full sm:w-auto">
|
||||
Batal
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleKelompokKeahlianDelete}
|
||||
disabled={isKelompokKeahlianDeleting}
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
{isKelompokKeahlianDeleting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
Hapus
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user