Change Database

This commit is contained in:
Randa Firman Putra
2025-06-20 00:45:19 +07:00
parent e028039ee2
commit 2f7ab6c0a9
45 changed files with 1896 additions and 953 deletions

View File

@@ -1,8 +1,7 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
interface AsalDaerah extends RowDataPacket {
interface AsalDaerah {
kabupaten: string;
jumlah: number;
}
@@ -18,18 +17,46 @@ export async function GET(request: Request) {
);
}
const connection = await pool.getConnection();
try {
const query = `
SELECT kabupaten, COUNT(*) AS jumlah
FROM mahasiswa
WHERE tahun_angkatan = ?
GROUP BY kabupaten
ORDER BY jumlah DESC, kabupaten ASC
`;
const { data, error } = await supabase
.from('mahasiswa')
.select('kabupaten')
.eq('tahun_angkatan', parseInt(tahunAngkatan));
const [results] = await connection.query<AsalDaerah[]>(query, [tahunAngkatan]);
if (error) {
console.error('Error fetching asal daerah per angkatan:', error);
return NextResponse.json(
{ error: 'Failed to fetch asal daerah data' },
{
status: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
// Group by kabupaten and count
const groupedData = data.reduce((acc, item) => {
acc[item.kabupaten] = (acc[item.kabupaten] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format and sort
const results: AsalDaerah[] = Object.entries(groupedData)
.map(([kabupaten, jumlah]) => ({
kabupaten,
jumlah
}))
.sort((a, b) => {
// Sort by jumlah DESC, then by kabupaten ASC
if (a.jumlah !== b.jumlah) {
return b.jumlah - a.jumlah;
}
return a.kabupaten.localeCompare(b.kabupaten);
});
return NextResponse.json(results, {
headers: {
@@ -52,7 +79,5 @@ export async function GET(request: Request) {
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,5 +1,5 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET(request: Request) {
try {
@@ -7,37 +7,66 @@ export async function GET(request: Request) {
const tahunAngkatan = searchParams.get('tahunAngkatan');
const jenisBeasiswa = searchParams.get('jenisBeasiswa');
let query = `
SELECT
m.tahun_angkatan,
m.kabupaten,
COUNT(m.nim) AS jumlah_mahasiswa
FROM
mahasiswa m
JOIN
beasiswa_mahasiswa s ON m.nim = s.nim
WHERE
s.jenis_beasiswa = ?
`;
let query = supabase
.from('mahasiswa')
.select(`
tahun_angkatan,
kabupaten,
beasiswa_mahasiswa!inner(
jenis_beasiswa
)
`)
.eq('beasiswa_mahasiswa.jenis_beasiswa', jenisBeasiswa);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = ?`;
query = query.eq('tahun_angkatan', tahunAngkatan);
}
query += `
GROUP BY
m.kabupaten, m.tahun_angkatan
ORDER BY
m.tahun_angkatan ASC, m.kabupaten
`;
const { data, error } = await query;
const params = [jenisBeasiswa];
if (tahunAngkatan && tahunAngkatan !== 'all') {
params.push(tahunAngkatan);
if (error) {
console.error('Supabase error:', error);
return NextResponse.json(
{ error: 'Database error' },
{ status: 500 }
);
}
const [rows] = await pool.query(query, params);
return NextResponse.json(rows);
// Group and count the data in JavaScript
const groupedData = data.reduce((acc: any[], row: any) => {
const tahunAngkatanValue = row.tahun_angkatan;
const kabupaten = row.kabupaten;
if (!kabupaten) return acc;
const existingGroup = acc.find(
(item: any) =>
item.tahun_angkatan === tahunAngkatanValue &&
item.kabupaten === kabupaten
);
if (existingGroup) {
existingGroup.jumlah_mahasiswa++;
} else {
acc.push({
tahun_angkatan: tahunAngkatanValue,
kabupaten: kabupaten,
jumlah_mahasiswa: 1
});
}
return acc;
}, []);
// Sort the results
const sortedData = groupedData.sort((a: any, b: any) => {
if (a.tahun_angkatan !== b.tahun_angkatan) {
return a.tahun_angkatan - b.tahun_angkatan;
}
return a.kabupaten.localeCompare(b.kabupaten);
});
return NextResponse.json(sortedData);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,38 +1,65 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
interface AsalDaerahLulus {
tahun_angkatan: number;
kabupaten: string;
jumlah_lulus_tepat_waktu: number;
}
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahunAngkatan');
let query = `
SELECT
m.tahun_angkatan,
m.kabupaten,
COUNT(m.nim) AS jumlah_lulus_tepat_waktu
FROM
mahasiswa m
JOIN
status_mahasiswa s ON m.nim = s.nim
WHERE
s.status_kuliah = 'Lulus'
AND s.semester <= 8
`;
let query = supabase
.from('status_mahasiswa')
.select('semester, mahasiswa!inner(tahun_angkatan, kabupaten, nim)')
.eq('status_kuliah', 'Lulus')
.lte('semester', 8);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = '${tahunAngkatan}'`;
query = query.eq('mahasiswa.tahun_angkatan', parseInt(tahunAngkatan));
}
query += `
GROUP BY
m.tahun_angkatan, m.kabupaten
ORDER BY
m.tahun_angkatan DESC, jumlah_lulus_tepat_waktu DESC
`;
const { data, error } = await query;
const [rows] = await pool.query(query);
return NextResponse.json(rows);
if (error) {
console.error('Error fetching asal daerah lulus data:', error);
return NextResponse.json(
{ error: 'Failed to fetch asal daerah lulus data' },
{ status: 500 }
);
}
// Group by tahun_angkatan and kabupaten
const groupedData = data.reduce((acc, item: any) => {
const tahun_angkatan = item.mahasiswa.tahun_angkatan;
const kabupaten = item.mahasiswa.kabupaten;
const key = `${tahun_angkatan}-${kabupaten}`;
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format and sort
const results: AsalDaerahLulus[] = Object.entries(groupedData)
.map(([key, jumlah_lulus_tepat_waktu]) => {
const [tahun_angkatan, kabupaten] = key.split('-');
return {
tahun_angkatan: parseInt(tahun_angkatan),
kabupaten,
jumlah_lulus_tepat_waktu
};
})
.sort((a, b) => {
// Sort by tahun_angkatan DESC, jumlah_lulus_tepat_waktu DESC
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return b.jumlah_lulus_tepat_waktu - a.jumlah_lulus_tepat_waktu;
});
return NextResponse.json(results);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,5 +1,5 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET(request: Request) {
try {
@@ -7,37 +7,66 @@ export async function GET(request: Request) {
const tahunAngkatan = searchParams.get('tahunAngkatan');
const jenisPrestasi = searchParams.get('jenisPrestasi');
let query = `
SELECT
m.tahun_angkatan,
m.kabupaten,
COUNT(m.nim) AS asal_daerah_mahasiswa_prestasi
FROM
mahasiswa m
JOIN
prestasi_mahasiswa s ON m.nim = s.nim
WHERE
s.jenis_prestasi = ?
`;
let query = supabase
.from('mahasiswa')
.select(`
tahun_angkatan,
kabupaten,
prestasi_mahasiswa!inner(
jenis_prestasi
)
`)
.eq('prestasi_mahasiswa.jenis_prestasi', jenisPrestasi);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = ?`;
query = query.eq('tahun_angkatan', tahunAngkatan);
}
query += `
GROUP BY
m.tahun_angkatan, m.kabupaten
ORDER BY
m.tahun_angkatan DESC, m.kabupaten
`;
const { data, error } = await query;
const params = [jenisPrestasi];
if (tahunAngkatan && tahunAngkatan !== 'all') {
params.push(tahunAngkatan);
if (error) {
console.error('Supabase error:', error);
return NextResponse.json(
{ error: 'Database error' },
{ status: 500 }
);
}
const [rows] = await pool.query(query, params);
return NextResponse.json(rows);
// Group and count the data in JavaScript
const groupedData = data.reduce((acc: any[], row: any) => {
const tahunAngkatanValue = row.tahun_angkatan;
const kabupaten = row.kabupaten;
if (!tahunAngkatanValue || !kabupaten) return acc;
const existingGroup = acc.find(
(item: any) =>
item.tahun_angkatan === tahunAngkatanValue &&
item.kabupaten === kabupaten
);
if (existingGroup) {
existingGroup.asal_daerah_mahasiswa_prestasi++;
} else {
acc.push({
tahun_angkatan: tahunAngkatanValue,
kabupaten: kabupaten,
asal_daerah_mahasiswa_prestasi: 1
});
}
return acc;
}, []);
// Sort the results
const sortedData = groupedData.sort((a: any, b: any) => {
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.kabupaten.localeCompare(b.kabupaten);
});
return NextResponse.json(sortedData);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,8 +1,7 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
interface AsalDaerahStatus extends RowDataPacket {
interface AsalDaerahStatus {
kabupaten: string;
tahun_angkatan?: number;
status_kuliah: string;
@@ -13,39 +12,78 @@ export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahun_angkatan');
const statusKuliah = searchParams.get('status_kuliah');
const connection = await pool.getConnection();
try {
let query = `
SELECT
m.kabupaten,
${tahunAngkatan && tahunAngkatan !== 'all' ? 'm.tahun_angkatan,' : ''}
s.status_kuliah,
COUNT(m.nim) AS total_mahasiswa
FROM
mahasiswa m
JOIN
status_mahasiswa s ON m.nim = s.nim
WHERE
s.status_kuliah = ?
`;
const params: any[] = [statusKuliah];
try {
let query = supabase
.from('status_mahasiswa')
.select('status_kuliah, mahasiswa!inner(kabupaten, tahun_angkatan, nim)')
.eq('status_kuliah', statusKuliah);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = ?`;
params.push(tahunAngkatan);
query = query.eq('mahasiswa.tahun_angkatan', parseInt(tahunAngkatan));
}
query += `
GROUP BY
m.kabupaten${tahunAngkatan && tahunAngkatan !== 'all' ? ', m.tahun_angkatan' : ''}, s.status_kuliah
ORDER BY
${tahunAngkatan && tahunAngkatan !== 'all' ? 'm.tahun_angkatan ASC,' : ''} m.kabupaten, s.status_kuliah
`;
const { data, error } = await query;
const [results] = await connection.query<AsalDaerahStatus[]>(query, params);
if (error) {
console.error('Error fetching asal daerah status:', error);
return NextResponse.json(
{ error: 'Failed to fetch asal daerah status data' },
{
status: 500,
headers: {
'Cache-Control': 'public, max-age=60, stale-while-revalidate=30',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
// Group by kabupaten, tahun_angkatan (optional), status_kuliah
const groupedData = data.reduce((acc, item: any) => {
const kabupaten = item.mahasiswa.kabupaten;
const tahun_angkatan = tahunAngkatan && tahunAngkatan !== 'all' ? item.mahasiswa.tahun_angkatan : undefined;
const status_kuliah = item.status_kuliah;
const key = tahun_angkatan !== undefined
? `${kabupaten}-${tahun_angkatan}-${status_kuliah}`
: `${kabupaten}-${status_kuliah}`;
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format and sort
const results: AsalDaerahStatus[] = Object.entries(groupedData)
.map(([key, total_mahasiswa]) => {
const parts = key.split('-');
if (tahunAngkatan && tahunAngkatan !== 'all') {
const [kabupaten, tahun_angkatan, status_kuliah] = parts;
return {
kabupaten,
tahun_angkatan: parseInt(tahun_angkatan),
status_kuliah,
total_mahasiswa
};
} else {
const [kabupaten, status_kuliah] = parts;
return {
kabupaten,
status_kuliah,
total_mahasiswa
};
}
})
.sort((a, b) => {
// Sort by tahun_angkatan ASC (if exists), kabupaten ASC, status_kuliah ASC
if (a.tahun_angkatan !== undefined && b.tahun_angkatan !== undefined && a.tahun_angkatan !== b.tahun_angkatan) {
return a.tahun_angkatan - b.tahun_angkatan;
}
if (a.kabupaten !== b.kabupaten) {
return a.kabupaten.localeCompare(b.kabupaten);
}
return a.status_kuliah.localeCompare(b.status_kuliah);
});
return NextResponse.json(results, {
headers: {
@@ -59,16 +97,15 @@ export async function GET(request: Request) {
console.error('Error fetching asal daerah status:', error);
return NextResponse.json(
{ error: 'Failed to fetch asal daerah status data' },
{
{
status: 500,
headers: {
'Cache-Control': 'public, max-age=60, stale-while-revalidate=30',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,22 +1,45 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
interface AsalDaerah extends RowDataPacket {
interface AsalDaerah {
kabupaten: string;
jumlah: number;
}
export async function GET() {
const connection = await pool.getConnection();
try {
const [results] = await connection.query<AsalDaerah[]>(`
SELECT kabupaten, COUNT(*) AS jumlah
FROM mahasiswa
GROUP BY kabupaten
ORDER BY kabupaten ASC
`);
const { data, error } = await supabase
.from('mahasiswa')
.select('kabupaten')
.order('kabupaten', { ascending: true });
if (error) {
console.error('Error fetching asal daerah:', error);
return NextResponse.json(
{ error: 'Failed to fetch asal daerah data' },
{
status: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
// Group by kabupaten and count
const groupedData = data.reduce((acc, item) => {
const kabupaten = item.kabupaten;
acc[kabupaten] = (acc[kabupaten] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to array format
const results: AsalDaerah[] = Object.entries(groupedData).map(([kabupaten, jumlah]) => ({
kabupaten,
jumlah
}));
return NextResponse.json(results, {
headers: {
@@ -39,7 +62,5 @@ export async function GET() {
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,5 +1,11 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
interface GenderData {
tahun_angkatan: number;
jk: string;
jumlah: number;
}
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
@@ -12,15 +18,39 @@ export async function GET(request: Request) {
);
}
const connection = await pool.getConnection();
try {
const [results] = await connection.query(`
SELECT tahun_angkatan, jk, COUNT(*) AS jumlah
FROM mahasiswa
WHERE tahun_angkatan = ?
GROUP BY jk
`, [tahun]);
const { data, error } = await supabase
.from('mahasiswa')
.select('tahun_angkatan, jk')
.eq('tahun_angkatan', parseInt(tahun));
if (error) {
console.error('Error fetching gender per angkatan:', error);
return NextResponse.json(
{ error: 'Failed to fetch gender per angkatan data' },
{
status: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
// Group by jk and count
const groupedData = data.reduce((acc, item) => {
acc[item.jk] = (acc[item.jk] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format
const results: GenderData[] = Object.entries(groupedData).map(([jk, jumlah]) => ({
tahun_angkatan: parseInt(tahun),
jk,
jumlah
}));
return NextResponse.json(results, {
headers: {
@@ -43,7 +73,5 @@ export async function GET(request: Request) {
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,30 +1,66 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const jenisBeasiswa = searchParams.get('jenisBeasiswa');
const query = `
SELECT
m.tahun_angkatan,
COUNT(m.nim) AS total_mahasiswa_beasiswa,
ROUND(AVG(m.ipk), 2) AS rata_rata_ipk
FROM
mahasiswa m
JOIN
beasiswa_mahasiswa s ON m.nim = s.nim
WHERE
s.jenis_beasiswa = ?
GROUP BY
m.tahun_angkatan
ORDER BY
m.tahun_angkatan ASC
`;
const { data, error } = await supabase
.from('mahasiswa')
.select(`
tahun_angkatan,
ipk,
beasiswa_mahasiswa!inner(
jenis_beasiswa
)
`)
.eq('beasiswa_mahasiswa.jenis_beasiswa', jenisBeasiswa);
const [rows] = await pool.query(query, [jenisBeasiswa]);
return NextResponse.json(rows);
if (error) {
console.error('Supabase error:', error);
return NextResponse.json(
{ error: 'Database error' },
{ status: 500 }
);
}
// Group and calculate statistics in JavaScript
const groupedData = data.reduce((acc: any[], row: any) => {
const tahunAngkatan = row.tahun_angkatan;
const ipk = row.ipk;
if (!ipk) return acc;
const existingGroup = acc.find(
(item: any) => item.tahun_angkatan === tahunAngkatan
);
if (existingGroup) {
existingGroup.total_mahasiswa_beasiswa++;
existingGroup.total_ipk += ipk;
} else {
acc.push({
tahun_angkatan: tahunAngkatan,
total_mahasiswa_beasiswa: 1,
total_ipk: ipk
});
}
return acc;
}, []);
// Calculate average IPK and format the results
const result = groupedData.map((group: any) => ({
tahun_angkatan: group.tahun_angkatan,
total_mahasiswa_beasiswa: group.total_mahasiswa_beasiswa,
rata_rata_ipk: Math.round((group.total_ipk / group.total_mahasiswa_beasiswa) * 100) / 100
}));
// Sort by tahun_angkatan ascending
const sortedData = result.sort((a: any, b: any) => a.tahun_angkatan - b.tahun_angkatan);
return NextResponse.json(sortedData);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,55 +0,0 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahun_angkatan');
if (!tahunAngkatan) {
return NextResponse.json(
{ error: 'Tahun angkatan diperlukan' },
{ status: 400 }
);
}
const connection = await pool.getConnection();
try {
const query = `
SELECT
jk,
ROUND(AVG(ipk), 2) as rata_rata_ipk
FROM mahasiswa
WHERE tahun_angkatan = ?
GROUP BY jk
ORDER BY jk ASC
`;
const [results] = await connection.query<RowDataPacket[]>(query, [tahunAngkatan]);
return NextResponse.json(results, {
headers: {
'Cache-Control': 'public, max-age=60, stale-while-revalidate=30',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
} catch (error) {
console.error('Error fetching IPK data:', error);
return NextResponse.json(
{ error: 'Failed to fetch IPK data' },
{
status: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,37 +1,67 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
interface IpkLulusTepat {
tahun_angkatan: number;
rata_rata_ipk: number;
}
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahunAngkatan');
let query = `
SELECT
m.tahun_angkatan,
ROUND(AVG(m.ipk), 2) AS rata_rata_ipk
FROM
mahasiswa m
JOIN
status_mahasiswa s ON m.nim = s.nim
WHERE
s.status_kuliah = 'Lulus'
AND s.semester <= 8
`;
let query = supabase
.from('status_mahasiswa')
.select('semester, mahasiswa!inner(tahun_angkatan, ipk)')
.eq('status_kuliah', 'Lulus')
.lte('semester', 8)
.not('mahasiswa.ipk', 'is', null);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = '${tahunAngkatan}'`;
query = query.eq('mahasiswa.tahun_angkatan', parseInt(tahunAngkatan));
}
query += `
GROUP BY
m.tahun_angkatan
ORDER BY
m.tahun_angkatan ASC
`;
const { data, error } = await query;
const [rows] = await pool.query(query);
return NextResponse.json(rows);
if (error) {
console.error('Error fetching IPK lulus tepat data:', error);
return NextResponse.json(
{ error: 'Failed to fetch IPK lulus tepat data' },
{ status: 500 }
);
}
// Group by tahun_angkatan and calculate average IPK
const groupedData = data.reduce((acc, item: any) => {
const tahun_angkatan = item.mahasiswa.tahun_angkatan;
const ipk = item.mahasiswa.ipk;
if (!acc[tahun_angkatan]) {
acc[tahun_angkatan] = {
total_ipk: 0,
count: 0
};
}
acc[tahun_angkatan].total_ipk += ipk;
acc[tahun_angkatan].count += 1;
return acc;
}, {} as Record<number, { total_ipk: number; count: number }>);
// Convert to final format and sort
const results: IpkLulusTepat[] = Object.entries(groupedData)
.map(([tahun_angkatan, data]) => ({
tahun_angkatan: parseInt(tahun_angkatan),
rata_rata_ipk: Math.round((data.total_ipk / data.count) * 100) / 100
}))
.sort((a, b) => {
// Sort by tahun_angkatan ASC
return a.tahun_angkatan - b.tahun_angkatan;
});
return NextResponse.json(results);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,30 +1,66 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const jenisPrestasi = searchParams.get('jenisPrestasi');
const query = `
SELECT
m.tahun_angkatan,
COUNT(m.nim) AS total_mahasiswa_prestasi,
ROUND(AVG(m.ipk), 2) AS rata_rata_ipk
FROM
mahasiswa m
JOIN
prestasi_mahasiswa s ON m.nim = s.nim
WHERE
s.jenis_prestasi = ?
GROUP BY
m.tahun_angkatan
ORDER BY
m.tahun_angkatan ASC
`;
const { data, error } = await supabase
.from('mahasiswa')
.select(`
tahun_angkatan,
ipk,
prestasi_mahasiswa!inner(
jenis_prestasi
)
`)
.eq('prestasi_mahasiswa.jenis_prestasi', jenisPrestasi);
const [rows] = await pool.query(query, [jenisPrestasi]);
return NextResponse.json(rows);
if (error) {
console.error('Supabase error:', error);
return NextResponse.json(
{ error: 'Database error' },
{ status: 500 }
);
}
// Group and calculate statistics in JavaScript
const groupedData = data.reduce((acc: any[], row: any) => {
const tahunAngkatan = row.tahun_angkatan;
const ipk = row.ipk;
if (!ipk) return acc;
const existingGroup = acc.find(
(item: any) => item.tahun_angkatan === tahunAngkatan
);
if (existingGroup) {
existingGroup.total_mahasiswa_prestasi++;
existingGroup.total_ipk += ipk;
} else {
acc.push({
tahun_angkatan: tahunAngkatan,
total_mahasiswa_prestasi: 1,
total_ipk: ipk
});
}
return acc;
}, []);
// Calculate average IPK and format the results
const result = groupedData.map((group: any) => ({
tahun_angkatan: group.tahun_angkatan,
total_mahasiswa_prestasi: group.total_mahasiswa_prestasi,
rata_rata_ipk: Math.round((group.total_ipk / group.total_mahasiswa_prestasi) * 100) / 100
}));
// Sort by tahun_angkatan ascending
const sortedData = result.sort((a: any, b: any) => a.tahun_angkatan - b.tahun_angkatan);
return NextResponse.json(sortedData);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,5 +1,5 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
interface IpkStatus {
tahun_angkatan: number;
@@ -22,37 +22,65 @@ export async function GET(request: Request) {
);
}
let query = `
SELECT
m.tahun_angkatan,
s.status_kuliah,
COUNT(m.nim) AS total_mahasiswa,
ROUND(AVG(m.ipk), 2) AS rata_rata_ipk
FROM
mahasiswa m
JOIN
status_mahasiswa s ON m.nim = s.nim
WHERE
s.status_kuliah = ?
`;
const params: any[] = [statusKuliah];
let query = supabase
.from('status_mahasiswa')
.select('status_kuliah, mahasiswa!inner(tahun_angkatan, ipk, nim)')
.eq('status_kuliah', statusKuliah)
.not('mahasiswa.ipk', 'is', null);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ' AND m.tahun_angkatan = ?';
params.push(tahunAngkatan);
query = query.eq('mahasiswa.tahun_angkatan', parseInt(tahunAngkatan));
}
query += `
GROUP BY
m.tahun_angkatan, s.status_kuliah
ORDER BY
m.tahun_angkatan DESC, s.status_kuliah
`;
const { data, error } = await query;
const [rows] = await pool.query(query, params);
if (error) {
console.error('Error fetching IPK status data:', error);
return NextResponse.json(
{ error: 'Failed to fetch IPK status data' },
{ status: 500 }
);
}
return NextResponse.json(rows);
// Group by tahun_angkatan and status_kuliah
const groupedData = data.reduce((acc, item: any) => {
const tahun_angkatan = item.mahasiswa.tahun_angkatan;
const status_kuliah = item.status_kuliah;
const ipk = item.mahasiswa.ipk;
const key = `${tahun_angkatan}-${status_kuliah}`;
if (!acc[key]) {
acc[key] = {
tahun_angkatan,
status_kuliah,
total_mahasiswa: 0,
total_ipk: 0
};
}
acc[key].total_mahasiswa += 1;
acc[key].total_ipk += ipk;
return acc;
}, {} as Record<string, { tahun_angkatan: number; status_kuliah: string; total_mahasiswa: number; total_ipk: number }>);
// Convert to final format and calculate average IPK
const results: IpkStatus[] = Object.values(groupedData)
.map(item => ({
tahun_angkatan: item.tahun_angkatan,
status_kuliah: item.status_kuliah,
total_mahasiswa: item.total_mahasiswa,
rata_rata_ipk: Math.round((item.total_ipk / item.total_mahasiswa) * 100) / 100
}))
.sort((a, b) => {
// Sort by tahun_angkatan DESC, status_kuliah ASC
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.status_kuliah.localeCompare(b.status_kuliah);
});
return NextResponse.json(results);
} catch (error) {
console.error('Error in ipk-status route:', error);
return NextResponse.json(

View File

@@ -1,21 +1,49 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
interface IPKData extends RowDataPacket {
interface IPKData {
tahun_angkatan: number;
rata_rata_ipk: number;
}
export async function GET() {
const connection = await pool.getConnection();
try {
const [results] = await connection.query<IPKData[]>(`
SELECT tahun_angkatan, ROUND(AVG(ipk), 2) AS rata_rata_ipk
FROM mahasiswa
GROUP BY tahun_angkatan
`);
const { data, error } = await supabase
.from('mahasiswa')
.select('tahun_angkatan, ipk')
.not('ipk', 'is', null);
if (error) {
console.error('Error fetching IPK data:', error);
return NextResponse.json(
{ error: 'Failed to fetch IPK data' },
{
status: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
// Group by tahun_angkatan and calculate average IPK
const groupedData = data.reduce((acc, item) => {
const tahun = item.tahun_angkatan;
if (!acc[tahun]) {
acc[tahun] = { sum: 0, count: 0 };
}
acc[tahun].sum += item.ipk || 0;
acc[tahun].count += 1;
return acc;
}, {} as Record<number, { sum: number; count: number }>);
// Convert to final format
const results: IPKData[] = Object.entries(groupedData).map(([tahun, data]) => ({
tahun_angkatan: parseInt(tahun),
rata_rata_ipk: Math.round((data.sum / data.count) * 100) / 100
}));
return NextResponse.json(results, {
headers: {
@@ -38,7 +66,5 @@ export async function GET() {
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,15 +1,26 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET() {
try {
const [rows] = await pool.query(`
SELECT DISTINCT jenis_beasiswa
FROM beasiswa_mahasiswa
ORDER BY jenis_beasiswa ASC
`);
const { data, error } = await supabase
.from('mahasiswa')
.select('jenis_beasiswa')
.not('jenis_beasiswa', 'is', null)
.order('jenis_beasiswa', { ascending: true });
return NextResponse.json(rows);
if (error) {
console.error('Error fetching data:', error);
return NextResponse.json(
{ error: 'Internal Server Error' },
{ status: 500 }
);
}
// Get unique jenis_beasiswa values
const uniqueBeasiswa = [...new Set(data.map(item => item.jenis_beasiswa))];
return NextResponse.json(uniqueBeasiswa);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,5 +1,5 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET(request: Request) {
try {
@@ -7,37 +7,66 @@ export async function GET(request: Request) {
const tahunAngkatan = searchParams.get('tahunAngkatan');
const jenisBeasiswa = searchParams.get('jenisBeasiswa');
let query = `
SELECT
m.tahun_angkatan,
m.jenis_pendaftaran,
COUNT(m.nim) AS jumlah_mahasiswa_beasiswa
FROM
mahasiswa m
JOIN
beasiswa_mahasiswa s ON m.nim = s.nim
WHERE
s.jenis_beasiswa = ?
`;
let query = supabase
.from('beasiswa_mahasiswa')
.select(`
jenis_pendaftaran,
jenis_beasiswa,
mahasiswa!inner(
tahun_angkatan
)
`)
.eq('jenis_beasiswa', jenisBeasiswa);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = ?`;
query = query.eq('mahasiswa.tahun_angkatan', tahunAngkatan);
}
query += `
GROUP BY
m.tahun_angkatan, m.jenis_pendaftaran
ORDER BY
m.tahun_angkatan DESC, m.jenis_pendaftaran
`;
const { data, error } = await query;
const params = [jenisBeasiswa];
if (tahunAngkatan && tahunAngkatan !== 'all') {
params.push(tahunAngkatan);
if (error) {
console.error('Supabase error:', error);
return NextResponse.json(
{ error: 'Database error' },
{ status: 500 }
);
}
const [rows] = await pool.query(query, params);
return NextResponse.json(rows);
// Group and count the data in JavaScript
const groupedData = data.reduce((acc: any[], row: any) => {
const tahunAngkatanValue = row.mahasiswa?.tahun_angkatan;
const jenisPendaftaran = row.jenis_pendaftaran;
if (!jenisPendaftaran || !tahunAngkatanValue) return acc;
const existingGroup = acc.find(
(item: any) =>
item.tahun_angkatan === tahunAngkatanValue &&
item.jenis_pendaftaran === jenisPendaftaran
);
if (existingGroup) {
existingGroup.jumlah_mahasiswa_beasiswa++;
} else {
acc.push({
tahun_angkatan: tahunAngkatanValue,
jenis_pendaftaran: jenisPendaftaran,
jumlah_mahasiswa_beasiswa: 1
});
}
return acc;
}, []);
// Sort the results
const sortedData = groupedData.sort((a: any, b: any) => {
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.jenis_pendaftaran.localeCompare(b.jenis_pendaftaran);
});
return NextResponse.json(sortedData);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,5 +1,5 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
interface JenisPendaftaranLulus {
tahun_angkatan: number;
@@ -12,37 +12,54 @@ export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahun_angkatan');
let query = `
SELECT
m.tahun_angkatan,
m.jenis_pendaftaran,
COUNT(m.nim) AS jumlah_lulus_tepat_waktu
FROM
mahasiswa m
JOIN
status_mahasiswa s ON m.nim = s.nim
WHERE
s.status_kuliah = 'Lulus'
AND s.semester <= 8
`;
const queryParams: any[] = [];
let query = supabase
.from('status_mahasiswa')
.select('semester, mahasiswa!inner(tahun_angkatan, jenis_pendaftaran, nim)')
.eq('status_kuliah', 'Lulus')
.lte('semester', 8);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = ?`;
queryParams.push(parseInt(tahunAngkatan));
query = query.eq('mahasiswa.tahun_angkatan', parseInt(tahunAngkatan));
}
query += `
GROUP BY
m.tahun_angkatan, m.jenis_pendaftaran
ORDER BY
m.tahun_angkatan DESC, m.jenis_pendaftaran
`;
const { data, error } = await query;
const [rows] = await pool.query(query, queryParams);
if (error) {
console.error('Error fetching jenis pendaftaran lulus data:', error);
return NextResponse.json(
{ error: 'Failed to fetch jenis pendaftaran lulus data' },
{ status: 500 }
);
}
return NextResponse.json(rows);
// Group by tahun_angkatan and jenis_pendaftaran
const groupedData = data.reduce((acc, item: any) => {
const tahun_angkatan = item.mahasiswa.tahun_angkatan;
const jenis_pendaftaran = item.mahasiswa.jenis_pendaftaran;
const key = `${tahun_angkatan}-${jenis_pendaftaran}`;
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format and sort
const results: JenisPendaftaranLulus[] = Object.entries(groupedData)
.map(([key, jumlah_lulus_tepat_waktu]) => {
const [tahun_angkatan, jenis_pendaftaran] = key.split('-');
return {
tahun_angkatan: parseInt(tahun_angkatan),
jenis_pendaftaran,
jumlah_lulus_tepat_waktu
};
})
.sort((a, b) => {
// Sort by tahun_angkatan DESC, jenis_pendaftaran ASC
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.jenis_pendaftaran.localeCompare(b.jenis_pendaftaran);
});
return NextResponse.json(results);
} catch (error) {
console.error('Detailed error in GET /api/mahasiswa/jenis-pendaftaran-lulus:', error);
return NextResponse.json(

View File

@@ -1,30 +1,65 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const jenisPrestasi = searchParams.get('jenisPrestasi');
const query = `
SELECT
m.tahun_angkatan,
m.jenis_pendaftaran,
COUNT(m.nim) AS jenis_pendaftaran_mahasiswa_prestasi
FROM
mahasiswa m
JOIN
prestasi_mahasiswa s ON m.nim = s.nim
WHERE
s.jenis_prestasi = ?
GROUP BY
m.tahun_angkatan, m.jenis_pendaftaran
ORDER BY
m.tahun_angkatan DESC, m.jenis_pendaftaran
`;
const { data, error } = await supabase
.from('mahasiswa')
.select(`
tahun_angkatan,
jenis_pendaftaran,
prestasi_mahasiswa!inner(
jenis_prestasi
)
`)
.eq('prestasi_mahasiswa.jenis_prestasi', jenisPrestasi);
const [rows] = await pool.query(query, [jenisPrestasi]);
return NextResponse.json(rows);
if (error) {
console.error('Supabase error:', error);
return NextResponse.json(
{ error: 'Database error' },
{ status: 500 }
);
}
// Group and count the data in JavaScript
const groupedData = data.reduce((acc: any[], row: any) => {
const tahunAngkatan = row.tahun_angkatan;
const jenisPendaftaran = row.jenis_pendaftaran;
if (!tahunAngkatan || !jenisPendaftaran) return acc;
const existingGroup = acc.find(
(item: any) =>
item.tahun_angkatan === tahunAngkatan &&
item.jenis_pendaftaran === jenisPendaftaran
);
if (existingGroup) {
existingGroup.jenis_pendaftaran_mahasiswa_prestasi++;
} else {
acc.push({
tahun_angkatan: tahunAngkatan,
jenis_pendaftaran: jenisPendaftaran,
jenis_pendaftaran_mahasiswa_prestasi: 1
});
}
return acc;
}, []);
// Sort the results
const sortedData = groupedData.sort((a: any, b: any) => {
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.jenis_pendaftaran.localeCompare(b.jenis_pendaftaran);
});
return NextResponse.json(sortedData);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,8 +1,7 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
interface JenisPendaftaranStatus extends RowDataPacket {
interface JenisPendaftaranStatus {
jenis_pendaftaran: string;
tahun_angkatan: number;
status_kuliah: string;
@@ -13,39 +12,66 @@ export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahun_angkatan');
const statusKuliah = searchParams.get('status_kuliah');
const connection = await pool.getConnection();
try {
let query = `
SELECT
m.jenis_pendaftaran,
m.tahun_angkatan,
s.status_kuliah,
COUNT(m.nim) AS total_mahasiswa
FROM
mahasiswa m
JOIN
status_mahasiswa s ON m.nim = s.nim
WHERE
s.status_kuliah = ?
`;
const params: any[] = [statusKuliah];
try {
let query = supabase
.from('status_mahasiswa')
.select('status_kuliah, mahasiswa!inner(jenis_pendaftaran, tahun_angkatan, nim)')
.eq('status_kuliah', statusKuliah);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = ?`;
params.push(tahunAngkatan);
query = query.eq('mahasiswa.tahun_angkatan', parseInt(tahunAngkatan));
}
query += `
GROUP BY
m.jenis_pendaftaran, m.tahun_angkatan, s.status_kuliah
ORDER BY
m.tahun_angkatan DESC, m.jenis_pendaftaran, s.status_kuliah
`;
const { data, error } = await query;
const [results] = await connection.query<JenisPendaftaranStatus[]>(query, params);
if (error) {
console.error('Error fetching jenis pendaftaran status:', error);
return NextResponse.json(
{ error: 'Failed to fetch jenis pendaftaran status data' },
{
status: 500,
headers: {
'Cache-Control': 'public, max-age=60, stale-while-revalidate=30',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
// Group by jenis_pendaftaran, tahun_angkatan, status_kuliah
const groupedData = data.reduce((acc, item: any) => {
const jenis_pendaftaran = item.mahasiswa.jenis_pendaftaran;
const tahun_angkatan = item.mahasiswa.tahun_angkatan;
const status_kuliah = item.status_kuliah;
const key = `${jenis_pendaftaran}-${tahun_angkatan}-${status_kuliah}`;
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format and sort
const results: JenisPendaftaranStatus[] = Object.entries(groupedData)
.map(([key, total_mahasiswa]) => {
const [jenis_pendaftaran, tahun_angkatan, status_kuliah] = key.split('-');
return {
jenis_pendaftaran,
tahun_angkatan: parseInt(tahun_angkatan),
status_kuliah,
total_mahasiswa
};
})
.sort((a, b) => {
// Sort by tahun_angkatan DESC, jenis_pendaftaran ASC, status_kuliah ASC
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
if (a.jenis_pendaftaran !== b.jenis_pendaftaran) {
return a.jenis_pendaftaran.localeCompare(b.jenis_pendaftaran);
}
return a.status_kuliah.localeCompare(b.status_kuliah);
});
return NextResponse.json(results, {
headers: {
@@ -59,16 +85,15 @@ export async function GET(request: Request) {
console.error('Error fetching jenis pendaftaran status:', error);
return NextResponse.json(
{ error: 'Failed to fetch jenis pendaftaran status data' },
{
{
status: 500,
headers: {
'Cache-Control': 'public, max-age=60, stale-while-revalidate=30',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,8 +1,7 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
interface JenisPendaftaran extends RowDataPacket {
interface JenisPendaftaran {
tahun_angkatan: number;
jenis_pendaftaran: string;
jumlah: number;
@@ -12,27 +11,54 @@ export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahun_angkatan');
const connection = await pool.getConnection();
try {
let query = `
SELECT tahun_angkatan, jenis_pendaftaran, COUNT(*) AS jumlah
FROM mahasiswa
`;
const params: any[] = [];
let query = supabase
.from('mahasiswa')
.select('tahun_angkatan, jenis_pendaftaran');
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` WHERE tahun_angkatan = ?`;
params.push(tahunAngkatan);
query = query.eq('tahun_angkatan', parseInt(tahunAngkatan));
}
query += `
GROUP BY tahun_angkatan, jenis_pendaftaran
ORDER BY tahun_angkatan DESC, jenis_pendaftaran
`;
const { data, error } = await query;
const [results] = await connection.query<JenisPendaftaran[]>(query, params);
if (error) {
console.error('Error fetching jenis pendaftaran:', error);
return NextResponse.json(
{ error: 'Failed to fetch jenis pendaftaran data' },
{
status: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
// Group by tahun_angkatan and jenis_pendaftaran
const groupedData = data.reduce((acc, item) => {
const key = `${item.tahun_angkatan}-${item.jenis_pendaftaran}`;
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format
const results: JenisPendaftaran[] = Object.entries(groupedData).map(([key, jumlah]) => {
const [tahun_angkatan, jenis_pendaftaran] = key.split('-');
return {
tahun_angkatan: parseInt(tahun_angkatan),
jenis_pendaftaran,
jumlah
};
}).sort((a, b) => {
// Sort by tahun_angkatan DESC, then by jenis_pendaftaran
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.jenis_pendaftaran.localeCompare(b.jenis_pendaftaran);
});
return NextResponse.json(results, {
headers: {
@@ -55,7 +81,5 @@ export async function GET(request: Request) {
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,18 +1,28 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET() {
try {
const [rows] = await pool.query(`
SELECT jenis_prestasi
FROM prestasi_mahasiswa
WHERE jenis_prestasi = 'Akademik' OR jenis_prestasi = 'Non-Akademik'
GROUP BY jenis_prestasi
ORDER BY jenis_prestasi ASC
`);
const { data, error } = await supabase
.from('mahasiswa')
.select('jenis_prestasi')
.in('jenis_prestasi', ['Akademik', 'Non-Akademik'])
.order('jenis_prestasi', { ascending: true });
return NextResponse.json(rows);
if (error) {
console.error('Error fetching data:', error);
return NextResponse.json(
{ error: 'Internal Server Error' },
{ status: 500 }
);
}
// Get unique jenis_prestasi values
const uniquePrestasi = [...new Set(data.map(item => item.jenis_prestasi))];
return NextResponse.json(uniquePrestasi);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(
{ error: 'Internal Server Error' },
{ status: 500 }

View File

@@ -1,5 +1,5 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
interface LulusTepatWaktu {
tahun_angkatan: number;
@@ -12,37 +12,56 @@ export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahun_angkatan');
let query = `
SELECT
m.tahun_angkatan,
m.jk,
COUNT(m.nim) AS jumlah_lulus_tepat_waktu
FROM
mahasiswa m
JOIN
status_mahasiswa s ON m.nim = s.nim
WHERE
s.status_kuliah = 'Lulus'
AND s.semester <= 8
`;
const params: any[] = [];
let query = supabase
.from('status_mahasiswa')
.select('semester, mahasiswa!inner(tahun_angkatan, jk, nim)')
.eq('status_kuliah', 'Lulus')
.lte('semester', 8);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ' AND m.tahun_angkatan = ?';
params.push(tahunAngkatan);
query = query.eq('mahasiswa.tahun_angkatan', parseInt(tahunAngkatan));
}
query += `
GROUP BY
m.tahun_angkatan, m.jk
ORDER BY
m.tahun_angkatan DESC, m.jk
`;
const { data, error } = await query;
const [rows] = await pool.query(query, params);
return NextResponse.json(rows);
if (error) {
console.error('Error fetching lulus tepat waktu data:', error);
return NextResponse.json(
{ error: 'Failed to fetch lulus tepat waktu data' },
{ status: 500 }
);
}
// Group by tahun_angkatan and jk
const groupedData = data.reduce((acc, item: any) => {
const tahun_angkatan = item.mahasiswa.tahun_angkatan;
const jk = item.mahasiswa.jk;
const key = `${tahun_angkatan}-${jk}`;
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format and sort
const results: LulusTepatWaktu[] = Object.entries(groupedData)
.map(([key, jumlah_lulus_tepat_waktu]) => {
const [tahun_angkatan, jk] = key.split('-');
return {
tahun_angkatan: parseInt(tahun_angkatan),
jk,
jumlah_lulus_tepat_waktu
};
})
.sort((a, b) => {
// Sort by tahun_angkatan DESC, jk ASC
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.jk.localeCompare(b.jk);
});
return NextResponse.json(results);
} catch (error) {
console.error('Error in lulus-tepat-waktu route:', error);
return NextResponse.json(
{ error: 'Internal Server Error' },
{ status: 500 }

View File

@@ -1,5 +1,5 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET(request: Request) {
try {
@@ -7,37 +7,68 @@ export async function GET(request: Request) {
const tahunAngkatan = searchParams.get('tahunAngkatan');
const jenisBeasiswa = searchParams.get('jenisBeasiswa');
let query = `
SELECT
m.tahun_angkatan,
s.nama_beasiswa,
COUNT(m.nim) AS jumlah_nama_beasiswa
FROM
mahasiswa m
JOIN
beasiswa_mahasiswa s ON m.nim = s.nim
WHERE
s.jenis_beasiswa = ?
`;
let query = supabase
.from('beasiswa_mahasiswa')
.select(`
nama_beasiswa,
jenis_beasiswa,
mahasiswa!inner(
tahun_angkatan
)
`)
.eq('jenis_beasiswa', jenisBeasiswa);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = ?`;
query = query.eq('mahasiswa.tahun_angkatan', tahunAngkatan);
}
query += `
GROUP BY
m.tahun_angkatan, s.nama_beasiswa, s.jenis_beasiswa
ORDER BY
m.tahun_angkatan DESC, s.nama_beasiswa, s.jenis_beasiswa
`;
const { data, error } = await query;
const params = [jenisBeasiswa];
if (tahunAngkatan && tahunAngkatan !== 'all') {
params.push(tahunAngkatan);
if (error) {
console.error('Supabase error:', error);
return NextResponse.json(
{ error: 'Database error' },
{ status: 500 }
);
}
const [rows] = await pool.query(query, params);
return NextResponse.json(rows);
// Group and count the data in JavaScript
const groupedData = data.reduce((acc: any[], row: any) => {
const tahunAngkatanValue = row.mahasiswa?.tahun_angkatan;
const namaBeasiswa = row.nama_beasiswa;
if (!namaBeasiswa || !tahunAngkatanValue) {
return acc;
}
const existingGroup = acc.find(
(item: any) =>
item.tahun_angkatan === tahunAngkatanValue &&
item.nama_beasiswa === namaBeasiswa
);
if (existingGroup) {
existingGroup.jumlah_nama_beasiswa++;
} else {
acc.push({
tahun_angkatan: tahunAngkatanValue,
nama_beasiswa: namaBeasiswa,
jumlah_nama_beasiswa: 1
});
}
return acc;
}, []);
// Sort the results by tahun_angkatan ascending (as expected by component)
const sortedData = groupedData.sort((a: any, b: any) => {
if (a.tahun_angkatan !== b.tahun_angkatan) {
return a.tahun_angkatan - b.tahun_angkatan;
}
return a.nama_beasiswa.localeCompare(b.nama_beasiswa);
});
return NextResponse.json(sortedData);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,10 +1,9 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
import { cookies } from 'next/headers';
import { jwtVerify } from 'jose';
interface MahasiswaProfile extends RowDataPacket {
interface MahasiswaProfile {
nim: string;
nama: string;
jk: 'Pria' | 'Wanita';
@@ -20,7 +19,6 @@ interface MahasiswaProfile extends RowDataPacket {
}
export async function GET(request: Request) {
let connection;
try {
// Get token from cookies
const cookieStore = await cookies();
@@ -41,47 +39,57 @@ export async function GET(request: Request) {
const nim = payload.nim as string;
// Get connection from pool
connection = await pool.getConnection();
// Get mahasiswa data
const { data: mahasiswaData, error: mahasiswaError } = await supabase
.from('mahasiswa')
.select(`
nim,
nama,
jk,
agama,
kabupaten,
provinsi,
jenis_pendaftaran,
status_beasiswa,
tahun_angkatan,
ipk,
prestasi
`)
.eq('nim', nim)
.single();
const query = `
SELECT
m.nim,
m.nama,
m.jk,
m.agama,
m.kabupaten,
m.provinsi,
m.jenis_pendaftaran,
m.status_beasiswa,
m.tahun_angkatan,
m.ipk,
m.prestasi,
s.status_kuliah
FROM
mahasiswa m
LEFT JOIN
status_mahasiswa s ON m.nim = s.nim
WHERE
m.nim = ?
`;
const [rows] = await connection.query<MahasiswaProfile[]>(query, [nim]);
if (rows.length === 0) {
connection.release();
if (mahasiswaError || !mahasiswaData) {
return NextResponse.json(
{ error: 'Data mahasiswa tidak ditemukan' },
{ status: 404 }
);
}
connection.release();
return NextResponse.json(rows[0]);
// Get status_kuliah separately
const { data: statusData, error: statusError } = await supabase
.from('status_mahasiswa')
.select('status_kuliah')
.eq('nim', nim)
.single();
// Transform the data to match the expected interface
const profile: MahasiswaProfile = {
nim: mahasiswaData.nim,
nama: mahasiswaData.nama,
jk: mahasiswaData.jk,
agama: mahasiswaData.agama,
kabupaten: mahasiswaData.kabupaten,
provinsi: mahasiswaData.provinsi,
jenis_pendaftaran: mahasiswaData.jenis_pendaftaran,
status_beasiswa: mahasiswaData.status_beasiswa,
tahun_angkatan: mahasiswaData.tahun_angkatan,
ipk: mahasiswaData.ipk,
prestasi: mahasiswaData.prestasi,
status_kuliah: statusData?.status_kuliah || ''
};
return NextResponse.json(profile);
} catch (error) {
if (connection) {
connection.release();
}
console.error('Error fetching profile data:', error);
return NextResponse.json(
{ error: 'Internal Server Error' },

View File

@@ -1,8 +1,7 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
interface MahasiswaStatistik extends RowDataPacket {
interface MahasiswaStatistik {
tahun_angkatan: number;
total_mahasiswa: number;
pria: number;
@@ -23,19 +22,52 @@ export async function OPTIONS() {
}
export async function GET() {
const connection = await pool.getConnection();
try {
// Query untuk mendapatkan statistik mahasiswa per tahun angkatan
const [results] = await connection.query<MahasiswaStatistik[]>(`
SELECT
tahun_angkatan,
COUNT(*) as total_mahasiswa,
SUM(CASE WHEN jk = 'Pria' THEN 1 ELSE 0 END) as pria,
SUM(CASE WHEN jk = 'Wanita' THEN 1 ELSE 0 END) as wanita
FROM mahasiswa
GROUP BY tahun_angkatan
`);
// Get all mahasiswa data
const { data, error } = await supabase
.from('mahasiswa')
.select('tahun_angkatan, jk');
if (error) {
console.error('Error fetching mahasiswa statistik:', error);
return NextResponse.json(
{ error: 'Failed to fetch mahasiswa statistik' },
{
status: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
// Group by tahun_angkatan and calculate statistics
const groupedData = data.reduce((acc, item) => {
const tahun = item.tahun_angkatan;
if (!acc[tahun]) {
acc[tahun] = {
tahun_angkatan: tahun,
total_mahasiswa: 0,
pria: 0,
wanita: 0
};
}
acc[tahun].total_mahasiswa += 1;
if (item.jk === 'Pria') {
acc[tahun].pria += 1;
} else if (item.jk === 'Wanita') {
acc[tahun].wanita += 1;
}
return acc;
}, {} as Record<number, MahasiswaStatistik>);
// Convert to array and sort by tahun_angkatan
const results: MahasiswaStatistik[] = Object.values(groupedData)
.sort((a, b) => a.tahun_angkatan - b.tahun_angkatan);
// Menambahkan header cache dan CORS
return NextResponse.json(results, {
@@ -59,7 +91,5 @@ export async function GET() {
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,42 +1,83 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
interface StatusKuliah extends RowDataPacket {
interface StatusKuliah {
tahun_angkatan: number;
status_kuliah: string;
jumlah: number;
}
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahun_angkatan');
const connection = await pool.getConnection();
try {
let query = `
SELECT m.tahun_angkatan, s.status_kuliah, COUNT(*) AS jumlah
FROM mahasiswa m
JOIN status_mahasiswa s ON m.nim = s.nim
WHERE s.status_kuliah IN ('Lulus', 'Cuti', 'Aktif', 'DO')
`;
const { searchParams } = new URL(request.url);
const tahunAngkatan = searchParams.get('tahun_angkatan');
const params: any[] = [];
let query = supabase
.from('mahasiswa')
.select(`
tahun_angkatan,
status_mahasiswa!inner(
status_kuliah
)
`)
.in('status_mahasiswa.status_kuliah', ['Lulus', 'Cuti', 'Aktif', 'DO']);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = ?`;
params.push(tahunAngkatan);
query = query.eq('tahun_angkatan', tahunAngkatan);
}
query += `
GROUP BY m.tahun_angkatan, s.status_kuliah
ORDER BY m.tahun_angkatan, s.status_kuliah
`;
const { data, error } = await query;
const [results] = await connection.query<StatusKuliah[]>(query, params);
if (error) {
console.error('Supabase error:', error);
return NextResponse.json(
{ error: 'Failed to fetch status kuliah data' },
{
status: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
return NextResponse.json(results, {
// Group and count the data in JavaScript
const groupedData = data.reduce((acc: any[], row: any) => {
const tahunAngkatanValue = row.tahun_angkatan;
const statusKuliah = row.status_mahasiswa?.status_kuliah;
if (!tahunAngkatanValue || !statusKuliah) return acc;
const existingGroup = acc.find(
(item: any) =>
item.tahun_angkatan === tahunAngkatanValue &&
item.status_kuliah === statusKuliah
);
if (existingGroup) {
existingGroup.jumlah++;
} else {
acc.push({
tahun_angkatan: tahunAngkatanValue,
status_kuliah: statusKuliah,
jumlah: 1
});
}
return acc;
}, []);
// Sort the results
const sortedData = groupedData.sort((a: any, b: any) => {
if (a.tahun_angkatan !== b.tahun_angkatan) {
return a.tahun_angkatan - b.tahun_angkatan;
}
return a.status_kuliah.localeCompare(b.status_kuliah);
});
return NextResponse.json(sortedData, {
headers: {
'Cache-Control': 'public, max-age=60, stale-while-revalidate=30',
'Access-Control-Allow-Origin': '*',
@@ -57,7 +98,5 @@ export async function GET(request: Request) {
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,8 +1,7 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
interface StatusMahasiswa extends RowDataPacket {
interface StatusMahasiswa {
tahun_angkatan: number;
jk: string;
total_mahasiswa: number;
@@ -13,41 +12,57 @@ export async function GET(request: Request) {
const tahunAngkatan = searchParams.get('tahun_angkatan');
const statusKuliah = searchParams.get('status_kuliah');
const connection = await pool.getConnection();
try {
let query = `
SELECT
m.tahun_angkatan,
CASE
WHEN m.jk = 'Pria' THEN 'L'
WHEN m.jk = 'Wanita' THEN 'P'
ELSE m.jk
END as jk,
COUNT(m.nim) AS total_mahasiswa
FROM
mahasiswa m
JOIN
status_mahasiswa s ON m.nim = s.nim
WHERE
s.status_kuliah = ?
`;
const params: any[] = [statusKuliah];
let query = supabase
.from('status_mahasiswa')
.select('status_kuliah, mahasiswa!inner(tahun_angkatan, jk)')
.eq('status_kuliah', statusKuliah);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = ?`;
params.push(tahunAngkatan);
query = query.eq('mahasiswa.tahun_angkatan', parseInt(tahunAngkatan));
}
query += `
GROUP BY
m.tahun_angkatan, m.jk
ORDER BY
m.tahun_angkatan DESC, m.jk
`;
const { data, error } = await query;
const [results] = await connection.query<StatusMahasiswa[]>(query, params);
if (error) {
console.error('Error fetching status mahasiswa:', error);
return NextResponse.json(
{ error: 'Failed to fetch status mahasiswa data' },
{
status: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
// Group by tahun_angkatan and jk
const groupedData = data.reduce((acc, item: any) => {
const tahun_angkatan = item.mahasiswa.tahun_angkatan;
const jk = item.mahasiswa.jk;
const key = `${tahun_angkatan}-${jk}`;
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format
const results: StatusMahasiswa[] = Object.entries(groupedData).map(([key, total_mahasiswa]) => {
const [tahun_angkatan, jk] = key.split('-');
return {
tahun_angkatan: parseInt(tahun_angkatan),
jk: jk === 'Pria' ? 'L' : jk === 'Wanita' ? 'P' : jk,
total_mahasiswa
};
}).sort((a, b) => {
// Sort by tahun_angkatan DESC, then by jk
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.jk.localeCompare(b.jk);
});
return NextResponse.json(results, {
headers: {
@@ -70,7 +85,5 @@ export async function GET(request: Request) {
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,18 +1,47 @@
import { NextResponse } from 'next/server';
import db from '@/lib/db';
import supabase from '@/lib/db';
interface StatusData {
tahun_angkatan: number;
status_kuliah: string;
jumlah: number;
}
export async function GET() {
try {
const query = `
SELECT m.tahun_angkatan, s.status_kuliah, COUNT(*) AS jumlah
FROM mahasiswa m
JOIN status_mahasiswa s ON m.nim = s.nim
WHERE s.status_kuliah IN ('Lulus', 'Cuti', 'Aktif', 'DO')
GROUP BY m.tahun_angkatan, s.status_kuliah;
`;
const { data, error } = await supabase
.from('status_mahasiswa')
.select('status_kuliah, mahasiswa!inner(tahun_angkatan)')
.in('status_kuliah', ['Lulus', 'Cuti', 'Aktif', 'DO']);
const [rows] = await db.query(query);
return NextResponse.json(rows);
if (error) {
console.error('Error fetching status data:', error);
return NextResponse.json(
{ error: 'Failed to fetch status data' },
{ status: 500 }
);
}
// Group by tahun_angkatan and status_kuliah
const groupedData = data.reduce((acc, item: any) => {
const tahun_angkatan = item.mahasiswa.tahun_angkatan;
const status_kuliah = item.status_kuliah;
const key = `${tahun_angkatan}-${status_kuliah}`;
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format
const results: StatusData[] = Object.entries(groupedData).map(([key, jumlah]) => {
const [tahun_angkatan, status_kuliah] = key.split('-');
return {
tahun_angkatan: parseInt(tahun_angkatan),
status_kuliah,
jumlah
};
});
return NextResponse.json(results);
} catch (error) {
console.error('Error fetching status data:', error);
return NextResponse.json(

View File

@@ -1,20 +1,38 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET() {
const connection = await pool.getConnection();
try {
const currentYear = new Date().getFullYear();
const [results] = await connection.query(`
SELECT DISTINCT tahun_angkatan
FROM mahasiswa
WHERE tahun_angkatan >= ?
ORDER BY tahun_angkatan DESC
LIMIT 7
`, [currentYear - 6]);
const { data, error } = await supabase
.from('mahasiswa')
.select('tahun_angkatan')
.gte('tahun_angkatan', currentYear - 10)
.order('tahun_angkatan', { ascending: false });
return NextResponse.json(results, {
if (error) {
console.error('Error fetching tahun angkatan:', error);
return NextResponse.json(
{ error: 'Failed to fetch tahun angkatan data' },
{
status: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
}
);
}
// Get unique tahun_angkatan values and limit to 7 most recent
const uniqueYears = [...new Set(data.map(item => item.tahun_angkatan))]
.sort((a, b) => b - a) // Sort descending
.slice(0, 7); // Take only 7 most recent years
console.log('Available years:', uniqueYears); // Debug log
return NextResponse.json(uniqueYears, {
headers: {
'Cache-Control': 'public, max-age=60, stale-while-revalidate=30',
'Access-Control-Allow-Origin': '*',
@@ -35,7 +53,5 @@ export async function GET() {
},
}
);
} finally {
connection.release();
}
}

View File

@@ -1,30 +1,65 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const jenisPrestasi = searchParams.get('jenisPrestasi');
const query = `
SELECT
m.tahun_angkatan,
s.tingkat,
COUNT(m.nim) AS tingkat_mahasiswa_prestasi
FROM
mahasiswa m
JOIN
prestasi_mahasiswa s ON m.nim = s.nim
WHERE
s.jenis_prestasi = ?
GROUP BY
m.tahun_angkatan, s.tingkat
ORDER BY
m.tahun_angkatan DESC, s.tingkat
`;
const { data, error } = await supabase
.from('mahasiswa')
.select(`
tahun_angkatan,
prestasi_mahasiswa!inner(
tingkat_prestasi,
jenis_prestasi
)
`)
.eq('prestasi_mahasiswa.jenis_prestasi', jenisPrestasi);
const [rows] = await pool.query(query, [jenisPrestasi]);
return NextResponse.json(rows);
if (error) {
console.error('Supabase error:', error);
return NextResponse.json(
{ error: 'Database error' },
{ status: 500 }
);
}
// Group and count the data in JavaScript
const groupedData = data.reduce((acc: any[], row: any) => {
const tahunAngkatan = row.tahun_angkatan;
const tingkatPrestasi = row.prestasi_mahasiswa?.tingkat_prestasi;
if (!tahunAngkatan || !tingkatPrestasi) return acc;
const existingGroup = acc.find(
(item: any) =>
item.tahun_angkatan === tahunAngkatan &&
item.tingkat_prestasi === tingkatPrestasi
);
if (existingGroup) {
existingGroup.tingkat_mahasiswa_prestasi++;
} else {
acc.push({
tahun_angkatan: tahunAngkatan,
tingkat_prestasi: tingkatPrestasi,
tingkat_mahasiswa_prestasi: 1
});
}
return acc;
}, []);
// Sort the results
const sortedData = groupedData.sort((a: any, b: any) => {
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.tingkat_prestasi.localeCompare(b.tingkat_prestasi);
});
return NextResponse.json(sortedData);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,5 +1,11 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
interface BeasiswaData {
tahun_angkatan: number;
jk: string;
jumlah_mahasiswa_beasiswa: number;
}
export async function GET(request: Request) {
try {
@@ -7,37 +13,51 @@ export async function GET(request: Request) {
const tahunAngkatan = searchParams.get('tahunAngkatan');
const jenisBeasiswa = searchParams.get('jenisBeasiswa');
let query = `
SELECT
m.tahun_angkatan,
m.jk,
COUNT(m.nim) AS jumlah_mahasiswa_beasiswa
FROM
mahasiswa m
JOIN
beasiswa_mahasiswa s ON m.nim = s.nim
WHERE
s.jenis_beasiswa = ?
`;
let query = supabase
.from('beasiswa_mahasiswa')
.select('jenis_beasiswa, mahasiswa!inner(tahun_angkatan, jk, nim)')
.eq('jenis_beasiswa', jenisBeasiswa);
if (tahunAngkatan && tahunAngkatan !== 'all') {
query += ` AND m.tahun_angkatan = ?`;
query = query.eq('mahasiswa.tahun_angkatan', parseInt(tahunAngkatan));
}
query += `
GROUP BY
m.tahun_angkatan, m.jk
ORDER BY
m.tahun_angkatan DESC, m.jk
`;
const { data, error } = await query;
const params = [jenisBeasiswa];
if (tahunAngkatan && tahunAngkatan !== 'all') {
params.push(tahunAngkatan);
if (error) {
console.error('Error fetching data:', error);
return NextResponse.json(
{ error: 'Internal Server Error' },
{ status: 500 }
);
}
const [rows] = await pool.query(query, params);
return NextResponse.json(rows);
// Group by tahun_angkatan and jk
const groupedData = data.reduce((acc, item: any) => {
const tahun_angkatan = item.mahasiswa.tahun_angkatan;
const jk = item.mahasiswa.jk;
const key = `${tahun_angkatan}-${jk}`;
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// Convert to final format
const results: BeasiswaData[] = Object.entries(groupedData).map(([key, jumlah_mahasiswa_beasiswa]) => {
const [tahun_angkatan, jk] = key.split('-');
return {
tahun_angkatan: parseInt(tahun_angkatan),
jk,
jumlah_mahasiswa_beasiswa
};
}).sort((a, b) => {
// Sort by tahun_angkatan DESC, then by jk
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.jk.localeCompare(b.jk);
});
return NextResponse.json(results);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,30 +1,69 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import supabase from '@/lib/db';
interface PrestasiData {
tahun_angkatan: number;
jk: string;
jumlah_mahasiswa_prestasi: number;
}
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const jenisPrestasi = searchParams.get('jenisPrestasi');
const query = `
SELECT
m.tahun_angkatan,
m.jk,
COUNT(m.nim) AS jumlah_mahasiswa_prestasi
FROM
mahasiswa m
JOIN
prestasi_mahasiswa s ON m.nim = s.nim
WHERE
s.jenis_prestasi = ?
GROUP BY
m.tahun_angkatan, m.jk
ORDER BY
m.tahun_angkatan DESC, m.jk
`;
const { data, error } = await supabase
.from('mahasiswa')
.select(`
tahun_angkatan,
jk,
prestasi_mahasiswa!inner(
jenis_prestasi
)
`)
.eq('prestasi_mahasiswa.jenis_prestasi', jenisPrestasi);
const [rows] = await pool.query(query, [jenisPrestasi]);
return NextResponse.json(rows);
if (error) {
console.error('Error fetching data:', error);
return NextResponse.json(
{ error: 'Internal Server Error' },
{ status: 500 }
);
}
// Group by tahun_angkatan and jk
const groupedData = data.reduce((acc: any[], item: any) => {
const tahunAngkatan = item.tahun_angkatan;
const jk = item.jk;
if (!tahunAngkatan || !jk) return acc;
const existingGroup = acc.find(
(group: any) => group.tahun_angkatan === tahunAngkatan && group.jk === jk
);
if (existingGroup) {
existingGroup.jumlah_mahasiswa_prestasi++;
} else {
acc.push({
tahun_angkatan: tahunAngkatan,
jk: jk,
jumlah_mahasiswa_prestasi: 1
});
}
return acc;
}, []);
// Sort by tahun_angkatan DESC, then by jk
const sortedData = groupedData.sort((a: any, b: any) => {
if (a.tahun_angkatan !== b.tahun_angkatan) {
return b.tahun_angkatan - a.tahun_angkatan;
}
return a.jk.localeCompare(b.jk);
});
return NextResponse.json(sortedData);
} catch (error) {
console.error('Error fetching data:', error);
return NextResponse.json(

View File

@@ -1,8 +1,7 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import { RowDataPacket } from 'mysql2';
import supabase from '@/lib/db';
interface MahasiswaTotal extends RowDataPacket {
interface MahasiswaTotal {
total_mahasiswa: number;
mahasiswa_aktif: number;
total_lulus: number;
@@ -16,6 +15,13 @@ interface MahasiswaTotal extends RowDataPacket {
total_mahasiswa_aktif_lulus: number;
}
interface IPKData {
nim: string;
mahasiswa: {
ipk: number;
};
}
// Fungsi untuk menangani preflight request (OPTIONS)
export async function OPTIONS() {
return new NextResponse(null, {
@@ -30,41 +36,99 @@ export async function OPTIONS() {
}
export async function GET() {
const connection = await pool.getConnection();
try {
// Query gabungan untuk semua data
const [results] = await connection.query<MahasiswaTotal[]>(`
SELECT
(SELECT COUNT(*) FROM mahasiswa) AS total_mahasiswa,
(SELECT COUNT(*) FROM mahasiswa m
JOIN status_mahasiswa s ON m.nim = s.nim
WHERE s.status_kuliah = 'Aktif') AS mahasiswa_aktif,
(SELECT COUNT(*) FROM mahasiswa m
JOIN status_mahasiswa s ON m.nim = s.nim
WHERE s.status_kuliah = 'Lulus') AS total_lulus,
(SELECT COUNT(*) FROM mahasiswa m
JOIN status_mahasiswa s ON m.nim = s.nim
WHERE s.status_kuliah = 'Lulus' AND m.jk = 'Pria') AS pria_lulus,
(SELECT COUNT(*) FROM mahasiswa m
JOIN status_mahasiswa s ON m.nim = s.nim
WHERE s.status_kuliah = 'Lulus' AND m.jk = 'Wanita') AS wanita_lulus,
(SELECT COUNT(*) FROM prestasi_mahasiswa) AS total_berprestasi,
(SELECT COUNT(*) FROM prestasi_mahasiswa WHERE jenis_prestasi = 'Akademik') AS prestasi_akademik,
(SELECT COUNT(*) FROM prestasi_mahasiswa WHERE jenis_prestasi = 'Non-Akademik') AS prestasi_non_akademik,
(SELECT COUNT(*) FROM mahasiswa m
JOIN status_mahasiswa s ON m.nim = s.nim
WHERE s.status_kuliah IN ('Aktif', 'Lulus')) AS total_mahasiswa_aktif_lulus,
(SELECT ROUND(AVG(m.ipk), 2) FROM mahasiswa m
JOIN status_mahasiswa s ON m.nim = s.nim
WHERE s.status_kuliah = 'Aktif') AS ipk_rata_rata_aktif,
(SELECT ROUND(AVG(m.ipk), 2) FROM mahasiswa m
JOIN status_mahasiswa s ON m.nim = s.nim
WHERE s.status_kuliah = 'Lulus') AS ipk_rata_rata_lulus
`);
// Get total mahasiswa
const { count: totalMahasiswa } = await supabase
.from('mahasiswa')
.select('*', { count: 'exact', head: true });
// Get mahasiswa aktif
const { count: mahasiswaAktif } = await supabase
.from('status_mahasiswa')
.select('nim', { count: 'exact', head: true })
.eq('status_kuliah', 'Aktif');
// Get total lulus
const { count: totalLulus } = await supabase
.from('status_mahasiswa')
.select('nim', { count: 'exact', head: true })
.eq('status_kuliah', 'Lulus');
// Get pria lulus
const { count: priaLulus } = await supabase
.from('status_mahasiswa')
.select('nim, mahasiswa!inner(jk)', { count: 'exact', head: true })
.eq('status_kuliah', 'Lulus')
.eq('mahasiswa.jk', 'Pria');
// Get wanita lulus
const { count: wanitaLulus } = await supabase
.from('status_mahasiswa')
.select('nim, mahasiswa!inner(jk)', { count: 'exact', head: true })
.eq('status_kuliah', 'Lulus')
.eq('mahasiswa.jk', 'Wanita');
// Get total berprestasi
const { count: totalBerprestasi } = await supabase
.from('prestasi_mahasiswa')
.select('*', { count: 'exact', head: true });
// Get prestasi akademik
const { count: prestasiAkademik } = await supabase
.from('prestasi_mahasiswa')
.select('*', { count: 'exact', head: true })
.eq('jenis_prestasi', 'Akademik');
// Get prestasi non-akademik
const { count: prestasiNonAkademik } = await supabase
.from('prestasi_mahasiswa')
.select('*', { count: 'exact', head: true })
.eq('jenis_prestasi', 'Non-Akademik');
// Get total mahasiswa aktif + lulus
const { count: totalMahasiswaAktifLulus } = await supabase
.from('status_mahasiswa')
.select('nim', { count: 'exact', head: true })
.in('status_kuliah', ['Aktif', 'Lulus']);
// Get IPK rata-rata aktif
const { data: ipkAktifData } = await supabase
.from('status_mahasiswa')
.select('nim, mahasiswa!inner(ipk)')
.eq('status_kuliah', 'Aktif')
.not('mahasiswa.ipk', 'is', null);
const ipkRataRataAktif = ipkAktifData && ipkAktifData.length > 0
? Math.round((ipkAktifData.reduce((sum, item: any) => sum + (item.mahasiswa.ipk || 0), 0) / ipkAktifData.length) * 100) / 100
: 0;
// Get IPK rata-rata lulus
const { data: ipkLulusData } = await supabase
.from('status_mahasiswa')
.select('nim, mahasiswa!inner(ipk)')
.eq('status_kuliah', 'Lulus')
.not('mahasiswa.ipk', 'is', null);
const ipkRataRataLulus = ipkLulusData && ipkLulusData.length > 0
? Math.round((ipkLulusData.reduce((sum, item: any) => sum + (item.mahasiswa.ipk || 0), 0) / ipkLulusData.length) * 100) / 100
: 0;
const results: MahasiswaTotal = {
total_mahasiswa: totalMahasiswa || 0,
mahasiswa_aktif: mahasiswaAktif || 0,
total_lulus: totalLulus || 0,
pria_lulus: priaLulus || 0,
wanita_lulus: wanitaLulus || 0,
total_berprestasi: totalBerprestasi || 0,
prestasi_akademik: prestasiAkademik || 0,
prestasi_non_akademik: prestasiNonAkademik || 0,
ipk_rata_rata_aktif: ipkRataRataAktif,
ipk_rata_rata_lulus: ipkRataRataLulus,
total_mahasiswa_aktif_lulus: totalMahasiswaAktifLulus || 0,
};
// Menambahkan header cache dan CORS
return NextResponse.json(results[0], {
return NextResponse.json(results, {
headers: {
'Cache-Control': 'public, max-age=60, stale-while-revalidate=30',
'Access-Control-Allow-Origin': '*',
@@ -85,7 +149,5 @@ export async function GET() {
},
}
);
} finally {
connection.release();
}
}