155 lines
4.6 KiB
TypeScript
155 lines
4.6 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import supabase from '@/lib/db';
|
|
|
|
interface MataKuliahBelumDiambil {
|
|
nama_mk: string;
|
|
jenis_mk: string;
|
|
kode_mk: string;
|
|
sks: number;
|
|
semester: number;
|
|
}
|
|
|
|
interface MahasiswaMKBelumDiambil {
|
|
nim: string;
|
|
nama: string;
|
|
tahun_angkatan: number;
|
|
sks_total: number;
|
|
mk_belum_diambil: MataKuliahBelumDiambil[];
|
|
}
|
|
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
const { searchParams } = new URL(request.url);
|
|
const tahunAngkatan = searchParams.get('tahun_angkatan');
|
|
const jenisMK = searchParams.get('jenis_mk');
|
|
|
|
// 1. Ambil data mahasiswa
|
|
let mahasiswaQuery = supabase
|
|
.from('mahasiswa')
|
|
.select('id_mahasiswa, nim, nama, tahun_angkatan')
|
|
.order('tahun_angkatan', { ascending: true })
|
|
.order('nim', { ascending: true });
|
|
|
|
// Jika ada filter tahun angkatan, terapkan filter
|
|
if (tahunAngkatan && tahunAngkatan !== 'all') {
|
|
mahasiswaQuery = mahasiswaQuery.eq('tahun_angkatan', parseInt(tahunAngkatan));
|
|
}
|
|
|
|
const { data: mahasiswa, error: errMhs } = await mahasiswaQuery;
|
|
|
|
if (errMhs) {
|
|
console.error('Error fetching mahasiswa:', errMhs);
|
|
throw errMhs;
|
|
}
|
|
|
|
// 2. Ambil semua mata kuliah
|
|
let mkQuery = supabase
|
|
.from('mata_kuliah')
|
|
.select('id_mk, kode_mk, nama_mk, jenis_mk, sks, semester')
|
|
.order('semester', { ascending: true })
|
|
.order('nama_mk', { ascending: true });
|
|
|
|
// Filter jenis MK jika ada
|
|
if (jenisMK && jenisMK !== 'all') {
|
|
mkQuery = mkQuery.eq('jenis_mk', jenisMK);
|
|
}
|
|
|
|
const { data: mataKuliah, error: errMK } = await mkQuery;
|
|
|
|
if (errMK) {
|
|
console.error('Error fetching mata kuliah:', errMK);
|
|
throw errMK;
|
|
}
|
|
|
|
// 3. Ambil semua nilai mahasiswa (hanya yang nilai != E, karena E dianggap belum lulus)
|
|
const { data: nilaiMahasiswa, error: errNilai } = await supabase
|
|
.from('nilai_mahasiswa')
|
|
.select('id_mahasiswa, id_mk, nilai_huruf');
|
|
|
|
if (errNilai) {
|
|
console.error('Error fetching nilai mahasiswa:', errNilai);
|
|
throw errNilai;
|
|
}
|
|
|
|
// 4. Buat Map untuk cek MK yang sudah diambil (nilai != E)
|
|
// Key: "id_mahasiswa-id_mk", Value: true
|
|
const sudahAmbilMap = new Map<string, boolean>();
|
|
nilaiMahasiswa?.forEach((nilai) => {
|
|
// Hanya dianggap sudah ambil jika nilai != E
|
|
if (nilai.nilai_huruf !== 'E') {
|
|
const key = `${nilai.id_mahasiswa}-${nilai.id_mk}`;
|
|
sudahAmbilMap.set(key, true);
|
|
}
|
|
});
|
|
|
|
// 5. Hitung SKS total per mahasiswa (dari MK yang sudah lulus, nilai != E)
|
|
const sksTotalMap = new Map<number, number>();
|
|
nilaiMahasiswa?.forEach((nilai) => {
|
|
if (nilai.nilai_huruf !== 'E') {
|
|
const mk = mataKuliah?.find(m => m.id_mk === nilai.id_mk);
|
|
if (mk) {
|
|
const currentSKS = sksTotalMap.get(nilai.id_mahasiswa) || 0;
|
|
sksTotalMap.set(nilai.id_mahasiswa, currentSKS + mk.sks);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 6. Proses data mahasiswa dan MK belum diambil
|
|
const results: MahasiswaMKBelumDiambil[] = [];
|
|
|
|
mahasiswa?.forEach((mhs) => {
|
|
const mkBelumDiambil: MataKuliahBelumDiambil[] = [];
|
|
|
|
// CROSS JOIN: cek setiap MK untuk mahasiswa ini
|
|
mataKuliah?.forEach((mk) => {
|
|
const key = `${mhs.id_mahasiswa}-${mk.id_mk}`;
|
|
|
|
// WHERE nm.id_mk IS NULL: jika tidak ada di sudahAmbilMap, berarti belum diambil
|
|
if (!sudahAmbilMap.has(key)) {
|
|
mkBelumDiambil.push({
|
|
nama_mk: mk.nama_mk,
|
|
jenis_mk: mk.jenis_mk,
|
|
kode_mk: mk.kode_mk,
|
|
sks: mk.sks,
|
|
semester: mk.semester
|
|
});
|
|
}
|
|
});
|
|
|
|
// Hanya tambahkan jika ada MK yang belum diambil
|
|
if (mkBelumDiambil.length > 0) {
|
|
results.push({
|
|
nim: mhs.nim,
|
|
nama: mhs.nama,
|
|
tahun_angkatan: mhs.tahun_angkatan,
|
|
sks_total: sksTotalMap.get(mhs.id_mahasiswa) || 0,
|
|
mk_belum_diambil: mkBelumDiambil
|
|
});
|
|
}
|
|
});
|
|
|
|
// 7. Urutkan berdasarkan tahun_angkatan dan nim
|
|
results.sort((a, b) => {
|
|
if (a.tahun_angkatan !== b.tahun_angkatan) {
|
|
return a.tahun_angkatan - b.tahun_angkatan;
|
|
}
|
|
return a.nim.localeCompare(b.nim);
|
|
});
|
|
|
|
return NextResponse.json(results, {
|
|
headers: {
|
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
'Pragma': 'no-cache',
|
|
'Expires': '0',
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('Error in mk-belum-diambil API:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Internal Server Error', message: error instanceof Error ? error.message : 'Unknown error' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|