import { NextRequest, NextResponse } from 'next/server'; import supabase from '@/lib/db'; interface MKBelumDiambilData { tahun_angkatan: number; jenis_mk: string; jumlah_belum_diambil: number; total_mata_kuliah: number; total_mahasiswa: number; } export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url); const tahunAngkatan = searchParams.get('tahun_angkatan'); // 1. Ambil semua mata kuliah const { data: mataKuliah, error: errMK } = await supabase .from('mata_kuliah') .select('id_mk, jenis_mk'); if (errMK) { console.error('Error fetching mata kuliah:', errMK); throw errMK; } // 2. Hitung total mata kuliah per kategori (total_mk CTE) const totalMKPerJenis = new Map(); mataKuliah?.forEach((mk) => { const jenis = mk.jenis_mk; totalMKPerJenis.set(jenis, (totalMKPerJenis.get(jenis) || 0) + 1); }); // 3. Ambil data mahasiswa let mahasiswaQuery = supabase .from('mahasiswa') .select('id_mahasiswa, tahun_angkatan'); // 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; } // 4. Hitung total mahasiswa per angkatan (total_mhs CTE) const totalMhsPerAngkatan = new Map(); mahasiswa?.forEach((m) => { const tahun = m.tahun_angkatan; if (tahun) { totalMhsPerAngkatan.set(tahun, (totalMhsPerAngkatan.get(tahun) || 0) + 1); } }); // 5. Ambil semua nilai mahasiswa 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; } // 6. Buat Map untuk cek apakah mahasiswa sudah mengambil MK (dengan nilai != E) // Key: "id_mahasiswa-id_mk", Value: true jika sudah diambil dan nilai != E 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); } }); // 7. Hitung mata kuliah belum diambil per angkatan per jenis MK (belum_ambil CTE) // Map dengan key yang unik menggunakan object const belumAmbilMap = new Map(); // CROSS JOIN mahasiswa dengan mata_kuliah mahasiswa?.forEach((mhs) => { if (!mhs.tahun_angkatan) return; mataKuliah?.forEach((mk) => { const key = `${mhs.id_mahasiswa}-${mk.id_mk}`; // WHERE NOT EXISTS: jika tidak ada di sudahAmbilMap, berarti belum diambil if (!sudahAmbilMap.has(key)) { // Gunakan separator yang aman (||) untuk menghindari konflik dengan nama jenis_mk const groupKey = `${mhs.tahun_angkatan}||${mk.jenis_mk}`; const existing = belumAmbilMap.get(groupKey); if (existing) { existing.jumlah += 1; } else { belumAmbilMap.set(groupKey, { tahun: mhs.tahun_angkatan, jenis: mk.jenis_mk, jumlah: 1 }); } } }); }); // 8. Gabungkan semua data (JOIN) const results: MKBelumDiambilData[] = []; belumAmbilMap.forEach((value) => { const tahun_angkatan = value.tahun; const jenis_mk = value.jenis; const jumlah_belum_diambil = value.jumlah; const total_mata_kuliah = totalMKPerJenis.get(jenis_mk) || 0; const total_mahasiswa = totalMhsPerAngkatan.get(tahun_angkatan) || 0; results.push({ tahun_angkatan, jenis_mk, jumlah_belum_diambil, total_mata_kuliah, total_mahasiswa, }); }); // 9. Urutkan berdasarkan tahun_angkatan dan jenis_mk results.sort((a, b) => { if (a.tahun_angkatan !== b.tahun_angkatan) { return a.tahun_angkatan - b.tahun_angkatan; } // Urutkan jenis MK: Wajib, Pilihan Wajib, Pilihan const jenisOrder = ['Wajib', 'Pilihan Wajib', 'Pilihan']; return jenisOrder.indexOf(a.jenis_mk) - jenisOrder.indexOf(b.jenis_mk); }); return NextResponse.json(results, { headers: { 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', }, }); } catch (error) { console.error('Error in mkbelumdiambil API:', error); return NextResponse.json( { error: 'Internal Server Error', message: error instanceof Error ? error.message : 'Unknown error' }, { status: 500 } ); } }