437 lines
13 KiB
TypeScript
437 lines
13 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import supabase from '@/lib/db';
|
|
|
|
// Define the NilaiMahasiswa type
|
|
interface NilaiMahasiswa {
|
|
id_nilai: number;
|
|
id_mahasiswa: number;
|
|
id_mk: number;
|
|
nim: string;
|
|
nama: string;
|
|
kode_mk: string;
|
|
nama_mk: string;
|
|
nilai_huruf: 'A' | 'B+' | 'B' | 'C+' | 'C' | 'D+' | 'D' | 'E';
|
|
nilai_angka: number;
|
|
semester: number;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
// GET - Ambil semua data nilai mahasiswa atau satu nilai spesifik berdasarkan ID
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
const { searchParams } = new URL(request.url);
|
|
const id = searchParams.get('id');
|
|
const search = searchParams.get('search');
|
|
const semester = searchParams.get('semester');
|
|
const nilai_huruf = searchParams.get('nilai_huruf');
|
|
|
|
if (id) {
|
|
// Ambil nilai spesifik berdasarkan ID dengan join ke mahasiswa dan mata_kuliah
|
|
const { data, error } = await supabase
|
|
.from('nilai_mahasiswa')
|
|
.select(`
|
|
id_nilai,
|
|
id_mahasiswa,
|
|
id_mk,
|
|
nilai_huruf,
|
|
nilai_angka,
|
|
semester,
|
|
created_at,
|
|
updated_at,
|
|
mahasiswa!inner(nim, nama),
|
|
mata_kuliah!inner(kode_mk, nama_mk)
|
|
`)
|
|
.eq('id_nilai', id)
|
|
.single();
|
|
|
|
if (error || !data) {
|
|
return NextResponse.json({ message: 'Nilai mahasiswa not found' }, { status: 404 });
|
|
}
|
|
|
|
// Transformasi data untuk meratakan field yang di-join
|
|
const transformedData: any = {
|
|
...data,
|
|
nim: (data.mahasiswa as any)?.nim || '',
|
|
nama: (data.mahasiswa as any)?.nama || '',
|
|
kode_mk: (data.mata_kuliah as any)?.kode_mk || '',
|
|
nama_mk: (data.mata_kuliah as any)?.nama_mk || ''
|
|
};
|
|
delete transformedData.mahasiswa;
|
|
delete transformedData.mata_kuliah;
|
|
|
|
return NextResponse.json(transformedData);
|
|
} else {
|
|
// Ambil semua nilai mahasiswa dengan join
|
|
let query = supabase
|
|
.from('nilai_mahasiswa')
|
|
.select(`
|
|
id_nilai,
|
|
id_mahasiswa,
|
|
id_mk,
|
|
nilai_huruf,
|
|
nilai_angka,
|
|
semester,
|
|
created_at,
|
|
updated_at,
|
|
mahasiswa!inner(nim, nama),
|
|
mata_kuliah!inner(kode_mk, nama_mk)
|
|
`);
|
|
|
|
// Add semester filter if provided
|
|
if (semester && semester !== 'all') {
|
|
query = query.eq('semester', parseInt(semester));
|
|
}
|
|
|
|
// Add nilai_huruf filter if provided
|
|
if (nilai_huruf && nilai_huruf !== 'all') {
|
|
query = query.eq('nilai_huruf', nilai_huruf);
|
|
}
|
|
|
|
// Add order by
|
|
query = query.order('semester', { ascending: true });
|
|
|
|
const { data, error } = await query;
|
|
|
|
if (error) {
|
|
console.error('Error fetching data:', error);
|
|
return NextResponse.json({ message: 'Internal Server Error' }, { status: 500 });
|
|
}
|
|
|
|
// Transformasi data untuk meratakan field yang di-join
|
|
let transformedData = data.map((item: any) => ({
|
|
...item,
|
|
nim: item.mahasiswa?.nim || '',
|
|
nama: item.mahasiswa?.nama || '',
|
|
kode_mk: item.mata_kuliah?.kode_mk || '',
|
|
nama_mk: item.mata_kuliah?.nama_mk || ''
|
|
})).map((item: any) => {
|
|
const { mahasiswa, mata_kuliah, ...rest } = item;
|
|
return rest;
|
|
});
|
|
|
|
// Client-side search filtering (untuk joined tables)
|
|
if (search) {
|
|
const searchLower = search.toLowerCase();
|
|
transformedData = transformedData.filter((item: any) => {
|
|
return (
|
|
item.nim?.toLowerCase().includes(searchLower) ||
|
|
item.nama?.toLowerCase().includes(searchLower) ||
|
|
item.kode_mk?.toLowerCase().includes(searchLower) ||
|
|
item.nama_mk?.toLowerCase().includes(searchLower)
|
|
);
|
|
});
|
|
}
|
|
|
|
// Sort by nim after filtering
|
|
transformedData.sort((a: any, b: any) => {
|
|
if (a.semester !== b.semester) {
|
|
return a.semester - b.semester;
|
|
}
|
|
return a.nim.localeCompare(b.nim);
|
|
});
|
|
|
|
return NextResponse.json(transformedData);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching data:', error);
|
|
return NextResponse.json({ message: 'Internal Server Error' }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
// POST - Buat data nilai mahasiswa baru
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const body = await request.json();
|
|
const {
|
|
nim,
|
|
id_mk,
|
|
nilai_huruf,
|
|
nilai_angka,
|
|
semester
|
|
} = body;
|
|
|
|
// Validasi field yang wajib diisi
|
|
if (!nim || !id_mk || !nilai_huruf || nilai_angka === undefined || !semester) {
|
|
return NextResponse.json(
|
|
{ message: 'Missing required fields: nim, id_mk, nilai_huruf, nilai_angka, semester' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Validasi nilai huruf
|
|
const validNilaiHuruf = ['A', 'B+', 'B', 'C+', 'C', 'D+', 'D', 'E'];
|
|
if (!validNilaiHuruf.includes(nilai_huruf)) {
|
|
return NextResponse.json(
|
|
{ message: 'Invalid nilai_huruf. Must be one of: A, B+, B, C+, C, D+, D, E' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Validasi nilai angka
|
|
if (nilai_angka < 0 || nilai_angka > 4) {
|
|
return NextResponse.json(
|
|
{ message: 'nilai_angka must be between 0 and 4' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Validasi semester
|
|
if (semester <= 0) {
|
|
return NextResponse.json(
|
|
{ message: 'semester must be greater than 0' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Cek apakah mahasiswa dengan NIM tersebut ada
|
|
const { data: mahasiswa, error: mahasiswaError } = await supabase
|
|
.from('mahasiswa')
|
|
.select('id_mahasiswa, nim, nama')
|
|
.eq('nim', nim)
|
|
.single();
|
|
|
|
if (mahasiswaError || !mahasiswa) {
|
|
return NextResponse.json(
|
|
{ message: `Mahasiswa dengan NIM ${nim} tidak terdaftar` },
|
|
{ status: 404 }
|
|
);
|
|
}
|
|
|
|
// Cek apakah mata kuliah ada
|
|
const { data: mataKuliah, error: mkError } = await supabase
|
|
.from('mata_kuliah')
|
|
.select('id_mk, kode_mk, nama_mk')
|
|
.eq('id_mk', id_mk)
|
|
.single();
|
|
|
|
if (mkError || !mataKuliah) {
|
|
return NextResponse.json(
|
|
{ message: 'Mata kuliah not found' },
|
|
{ status: 404 }
|
|
);
|
|
}
|
|
|
|
// Cek apakah nilai untuk mahasiswa dan mata kuliah ini sudah ada
|
|
const { data: existingNilai, error: checkError } = await supabase
|
|
.from('nilai_mahasiswa')
|
|
.select('id_nilai')
|
|
.eq('id_mahasiswa', mahasiswa.id_mahasiswa)
|
|
.eq('id_mk', id_mk)
|
|
.single();
|
|
|
|
if (existingNilai) {
|
|
return NextResponse.json(
|
|
{ message: `Nilai untuk mahasiswa ${nim} pada mata kuliah ${mataKuliah.kode_mk} sudah ada` },
|
|
{ status: 409 }
|
|
);
|
|
}
|
|
|
|
// Insert nilai baru
|
|
const { data, error } = await supabase
|
|
.from('nilai_mahasiswa')
|
|
.insert({
|
|
id_mahasiswa: mahasiswa.id_mahasiswa,
|
|
id_mk: parseInt(id_mk),
|
|
nilai_huruf,
|
|
nilai_angka: parseFloat(nilai_angka),
|
|
semester: parseInt(semester)
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (error) {
|
|
console.error('Error creating nilai mahasiswa:', error);
|
|
return NextResponse.json({ message: 'Internal Server Error' }, { status: 500 });
|
|
}
|
|
|
|
return NextResponse.json(
|
|
{
|
|
message: 'Nilai mahasiswa berhasil ditambahkan',
|
|
id: data.id_nilai,
|
|
mahasiswa: mahasiswa.nama,
|
|
mata_kuliah: mataKuliah.nama_mk
|
|
},
|
|
{ status: 201 }
|
|
);
|
|
} catch (error) {
|
|
console.error('Error creating nilai mahasiswa:', error);
|
|
return NextResponse.json({ message: 'Internal Server Error' }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
// PUT - Update data nilai mahasiswa yang sudah ada
|
|
export async function PUT(request: NextRequest) {
|
|
try {
|
|
const { searchParams } = new URL(request.url);
|
|
const id = searchParams.get('id');
|
|
|
|
if (!id) {
|
|
return NextResponse.json({ message: 'ID is required' }, { status: 400 });
|
|
}
|
|
|
|
const body = await request.json();
|
|
const {
|
|
nim,
|
|
id_mk,
|
|
nilai_huruf,
|
|
nilai_angka,
|
|
semester
|
|
} = body;
|
|
|
|
// Validasi field yang wajib diisi
|
|
if (!nim || !id_mk || !nilai_huruf || nilai_angka === undefined || !semester) {
|
|
return NextResponse.json(
|
|
{ message: 'Missing required fields: nim, id_mk, nilai_huruf, nilai_angka, semester' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Validasi nilai huruf
|
|
const validNilaiHuruf = ['A', 'B+', 'B', 'C+', 'C', 'D+', 'D', 'E'];
|
|
if (!validNilaiHuruf.includes(nilai_huruf)) {
|
|
return NextResponse.json(
|
|
{ message: 'Invalid nilai_huruf. Must be one of: A, B+, B, C+, C, D+, D, E' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Validasi nilai angka
|
|
if (nilai_angka < 0 || nilai_angka > 4) {
|
|
return NextResponse.json(
|
|
{ message: 'nilai_angka must be between 0 and 4' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Validasi semester
|
|
if (semester <= 0) {
|
|
return NextResponse.json(
|
|
{ message: 'semester must be greater than 0' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Cek apakah nilai ada
|
|
const { data: existing, error: checkError } = await supabase
|
|
.from('nilai_mahasiswa')
|
|
.select('*')
|
|
.eq('id_nilai', id)
|
|
.single();
|
|
|
|
if (checkError || !existing) {
|
|
return NextResponse.json({ message: 'Nilai mahasiswa not found' }, { status: 404 });
|
|
}
|
|
|
|
// Cek apakah mahasiswa dengan NIM tersebut ada
|
|
const { data: mahasiswa, error: mahasiswaError } = await supabase
|
|
.from('mahasiswa')
|
|
.select('id_mahasiswa, nim, nama')
|
|
.eq('nim', nim)
|
|
.single();
|
|
|
|
if (mahasiswaError || !mahasiswa) {
|
|
return NextResponse.json(
|
|
{ message: `Mahasiswa dengan NIM ${nim} tidak terdaftar` },
|
|
{ status: 404 }
|
|
);
|
|
}
|
|
|
|
// Cek apakah mata kuliah ada
|
|
const { data: mataKuliah, error: mkError } = await supabase
|
|
.from('mata_kuliah')
|
|
.select('id_mk, kode_mk, nama_mk')
|
|
.eq('id_mk', id_mk)
|
|
.single();
|
|
|
|
if (mkError || !mataKuliah) {
|
|
return NextResponse.json(
|
|
{ message: 'Mata kuliah not found' },
|
|
{ status: 404 }
|
|
);
|
|
}
|
|
|
|
// Cek apakah kombinasi mahasiswa-mata kuliah sudah digunakan oleh nilai lain
|
|
const { data: duplicateNilai, error: duplicateError } = await supabase
|
|
.from('nilai_mahasiswa')
|
|
.select('id_nilai')
|
|
.eq('id_mahasiswa', mahasiswa.id_mahasiswa)
|
|
.eq('id_mk', id_mk)
|
|
.neq('id_nilai', id)
|
|
.single();
|
|
|
|
if (duplicateNilai) {
|
|
return NextResponse.json(
|
|
{ message: `Nilai untuk mahasiswa ${nim} pada mata kuliah ${mataKuliah.kode_mk} sudah ada` },
|
|
{ status: 409 }
|
|
);
|
|
}
|
|
|
|
// Update nilai
|
|
const { error } = await supabase
|
|
.from('nilai_mahasiswa')
|
|
.update({
|
|
id_mahasiswa: mahasiswa.id_mahasiswa,
|
|
id_mk: parseInt(id_mk),
|
|
nilai_huruf,
|
|
nilai_angka: parseFloat(nilai_angka),
|
|
semester: parseInt(semester),
|
|
updated_at: new Date().toISOString()
|
|
})
|
|
.eq('id_nilai', id);
|
|
|
|
if (error) {
|
|
console.error('Error updating nilai mahasiswa:', error);
|
|
return NextResponse.json({ message: 'Internal Server Error' }, { status: 500 });
|
|
}
|
|
|
|
return NextResponse.json({ message: 'Nilai mahasiswa berhasil diperbarui' });
|
|
} catch (error) {
|
|
console.error('Error updating nilai mahasiswa:', error);
|
|
return NextResponse.json({ message: 'Internal Server Error' }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
// DELETE - Hapus data nilai mahasiswa
|
|
export async function DELETE(request: NextRequest) {
|
|
try {
|
|
const { searchParams } = new URL(request.url);
|
|
const id = searchParams.get('id');
|
|
|
|
if (!id) {
|
|
return NextResponse.json({ message: 'ID is required' }, { status: 400 });
|
|
}
|
|
|
|
// Check if nilai exists
|
|
const { data: existing, error: checkError } = await supabase
|
|
.from('nilai_mahasiswa')
|
|
.select(`
|
|
id_nilai,
|
|
mahasiswa!inner(nim, nama),
|
|
mata_kuliah!inner(kode_mk, nama_mk)
|
|
`)
|
|
.eq('id_nilai', id)
|
|
.single();
|
|
|
|
if (checkError || !existing) {
|
|
return NextResponse.json({ message: 'Nilai mahasiswa not found' }, { status: 404 });
|
|
}
|
|
|
|
// Hapus nilai
|
|
const { error } = await supabase
|
|
.from('nilai_mahasiswa')
|
|
.delete()
|
|
.eq('id_nilai', id);
|
|
|
|
if (error) {
|
|
console.error('Error deleting nilai mahasiswa:', error);
|
|
return NextResponse.json({ message: 'Internal Server Error' }, { status: 500 });
|
|
}
|
|
|
|
return NextResponse.json({ message: 'Nilai mahasiswa berhasil dihapus' });
|
|
} catch (error) {
|
|
console.error('Error deleting nilai mahasiswa:', error);
|
|
return NextResponse.json({ message: 'Internal Server Error' }, { status: 500 });
|
|
}
|
|
}
|