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(); 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(); 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 } ); } }