diff --git a/app/api/mahasiswa/nama-beasiswa-dashboard/route.ts b/app/api/mahasiswa/nama-beasiswa-dashboard/route.ts
new file mode 100644
index 0000000..13a8860
--- /dev/null
+++ b/app/api/mahasiswa/nama-beasiswa-dashboard/route.ts
@@ -0,0 +1,77 @@
+import { NextResponse } from 'next/server';
+import supabase from '@/lib/db';
+
+export async function GET(request: Request) {
+ try {
+ const { searchParams } = new URL(request.url);
+ const tahunAngkatan = searchParams.get('tahunAngkatan');
+
+ let query = supabase
+ .from('beasiswa_mahasiswa')
+ .select(`
+ nama_beasiswa,
+ jenis_beasiswa,
+ mahasiswa!inner(
+ tahun_angkatan
+ )
+ `);
+
+ if (tahunAngkatan && tahunAngkatan !== 'all') {
+ query = query.eq('mahasiswa.tahun_angkatan', tahunAngkatan);
+ }
+
+ const { data, error } = await query;
+
+ 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 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(
+ { error: 'Internal Server Error' },
+ { status: 500 }
+ );
+ }
+}
diff --git a/app/api/mahasiswa/provinsi-mahasiswa/route.ts b/app/api/mahasiswa/provinsi-mahasiswa/route.ts
new file mode 100644
index 0000000..9293bd0
--- /dev/null
+++ b/app/api/mahasiswa/provinsi-mahasiswa/route.ts
@@ -0,0 +1,60 @@
+import { NextResponse } from 'next/server';
+import supabase from '@/lib/db';
+
+export async function GET(request: Request) {
+ try {
+ // Get all mahasiswa data and process in JavaScript
+ const { data: mahasiswaData, error } = await supabase
+ .from('mahasiswa')
+ .select('provinsi');
+
+ if (error) {
+ console.error('Supabase error:', error);
+ return NextResponse.json(
+ { error: 'Database error' },
+ { status: 500 }
+ );
+ }
+
+ // Process the data in JavaScript
+ let kalimantanBarat = 0;
+ let luarKalimantanBarat = 0;
+
+ mahasiswaData.forEach((mahasiswa) => {
+ const provinsi = mahasiswa.provinsi?.toLowerCase() || '';
+
+ if (provinsi.includes('kalimantan barat') || provinsi.includes('kalbar')) {
+ kalimantanBarat++;
+ } else if (mahasiswa.provinsi) {
+ luarKalimantanBarat++;
+ }
+ });
+
+ // Transform the data to match the expected format
+ const result = [
+ {
+ provinsi: 'Kalimantan Barat',
+ jumlah_mahasiswa: kalimantanBarat
+ },
+ {
+ provinsi: 'Luar Kalimantan Barat',
+ jumlah_mahasiswa: luarKalimantanBarat
+ }
+ ];
+
+ return NextResponse.json(result, {
+ 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 provinsi data:', error);
+ return NextResponse.json(
+ { error: 'Internal Server Error' },
+ { status: 500 }
+ );
+ }
+}
diff --git a/app/api/mahasiswa/tingkat-prestasi-dashboard/route.ts b/app/api/mahasiswa/tingkat-prestasi-dashboard/route.ts
new file mode 100644
index 0000000..270bcd1
--- /dev/null
+++ b/app/api/mahasiswa/tingkat-prestasi-dashboard/route.ts
@@ -0,0 +1,91 @@
+import { NextResponse } from 'next/server';
+import supabase from '@/lib/db';
+
+export async function GET(request: Request) {
+ try {
+ const { searchParams } = new URL(request.url);
+ const tahunAngkatan = searchParams.get('tahunAngkatan');
+
+ // Build query based on parameters
+ let query = supabase
+ .from('mahasiswa')
+ .select(`
+ tahun_angkatan,
+ prestasi_mahasiswa(
+ tingkat_prestasi,
+ jenis_prestasi
+ )
+ `);
+
+ // Add tahun angkatan filter if provided and not 'all'
+ if (tahunAngkatan && tahunAngkatan !== 'null' && tahunAngkatan !== 'undefined' && tahunAngkatan !== 'all') {
+ query = query.eq('tahun_angkatan', parseInt(tahunAngkatan));
+ }
+
+ const { data, error } = await query;
+
+ 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;
+
+ // Handle array of prestasi_mahasiswa
+ const prestasiArray = Array.isArray(row.prestasi_mahasiswa) ? row.prestasi_mahasiswa : [row.prestasi_mahasiswa];
+
+ if (!tahunAngkatan) {
+ return acc;
+ }
+
+ // Process each prestasi in the array
+ prestasiArray.forEach((prestasi: any) => {
+ if (!prestasi || !prestasi.tingkat_prestasi) {
+ return;
+ }
+
+ const tingkatPrestasi = prestasi.tingkat_prestasi;
+
+ const existingGroup = acc.find(
+ (item: any) =>
+ item.tahun_angkatan === tahunAngkatan &&
+ item.tingkat_prestasi === tingkatPrestasi
+ );
+
+ if (existingGroup) {
+ existingGroup.tingkat_mahasiswa_prestasi++;
+ } else {
+ const newGroup = {
+ tahun_angkatan: tahunAngkatan,
+ tingkat_prestasi: tingkatPrestasi,
+ tingkat_mahasiswa_prestasi: 1
+ };
+ acc.push(newGroup);
+ }
+ });
+
+ 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(
+ { error: 'Internal Server Error' },
+ { status: 500 }
+ );
+ }
+}
diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index 6a95c2c..5b6937b 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -14,6 +14,9 @@ import KelompokKeahlianStatusChart from "@/components/chartsDashboard/kkdashboar
import KelompokKeahlianLulusTepatPieChart from "@/components/chartsDashboard/kkdashboardtepatpiechart";
import MasaStudiAktifChart from "@/components/chartsDashboard/masastudiaktifchart";
import MasaStudiLulusChart from "@/components/chartsDashboard/masastudiluluschart";
+import NamaBeasiswaChart from "@/components/chartsDashboard/NamaBeasiswaDashChart";
+import TingkatPrestasiChart from "@/components/chartsDashboard/TingkatPrestasiDashChart";
+import ProvinsiMahasiswaChart from "@/components/chartsDashboard/ProvinsiMahasiswaPieChart";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
export default function TotalMahasiswaPage() {
@@ -39,23 +42,56 @@ export default function TotalMahasiswaPage() {
{selectedYear === "all" ? (
-
-
-
-
-
-
-
-
-
-
+
+ {/* Overview Section */}
+
+
+
+
+
+ {/* Academic Performance Section */}
+
+
+
+
+
+ {/* Study Duration Section */}
+
+
+
+
+
+ {/* Expertise & Achievement Section */}
+
+
+
+
+
+ {/* Scholarship & Achievement Section */}
+
+
+
+
+
+ {/* Demographics Section */}
+
) : (
-
-
-
-
-
+
+ {/* Overview Section */}
+
+
+
+
+
+ {/* Demographics Section */}
+
)}
diff --git a/components/chartsDashboard/NamaBeasiswaDashChart.tsx b/components/chartsDashboard/NamaBeasiswaDashChart.tsx
new file mode 100644
index 0000000..e88777d
--- /dev/null
+++ b/components/chartsDashboard/NamaBeasiswaDashChart.tsx
@@ -0,0 +1,265 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import dynamic from 'next/dynamic';
+import { ApexOptions } from 'apexcharts';
+import { useTheme } from 'next-themes';
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+
+// Dynamically import ApexCharts to avoid SSR issues
+const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
+
+interface NamaBeasiswaData {
+ tahun_angkatan: number;
+ nama_beasiswa: string;
+ jumlah_nama_beasiswa: number;
+}
+
+interface Props {
+ selectedYear: string;
+}
+
+export default function NamaBeasiswaDashChart({ selectedYear }: Props) {
+ const { theme } = useTheme();
+ const [data, setData] = useState
([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ setLoading(true);
+ setError(null);
+
+ const url = `/api/mahasiswa/nama-beasiswa-dashboard?tahunAngkatan=${selectedYear}`;
+
+ const response = await fetch(url);
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`);
+ }
+
+ const result = await response.json();
+
+ if (!Array.isArray(result)) {
+ throw new Error('Invalid data format received from server');
+ }
+
+ // Sort data by tahun_angkatan
+ const sortedData = result.sort((a: NamaBeasiswaData, b: NamaBeasiswaData) =>
+ a.tahun_angkatan - b.tahun_angkatan
+ );
+
+ setData(sortedData);
+ } catch (err) {
+ console.error('Error in fetchData:', err);
+ setError(err instanceof Error ? err.message : 'An error occurred while fetching data');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [selectedYear]);
+
+ // Process data for series
+ const processSeriesData = () => {
+ if (!data.length) return [];
+
+ const years = [...new Set(data.map(item => item.tahun_angkatan))].sort();
+ const beasiswaTypes = [...new Set(data.map(item => item.nama_beasiswa))].sort();
+
+ return beasiswaTypes.map(beasiswa => ({
+ name: beasiswa,
+ data: years.map(year => {
+ const item = data.find(d => d.tahun_angkatan === year && d.nama_beasiswa === beasiswa);
+ return item ? item.jumlah_nama_beasiswa : 0;
+ })
+ }));
+ };
+
+ const chartOptions: ApexOptions = {
+ chart: {
+ type: 'bar',
+ stacked: false,
+ toolbar: {
+ show: true,
+ tools: {
+ download: true,
+ selection: true,
+ zoom: true,
+ zoomin: true,
+ zoomout: true,
+ pan: true,
+ reset: true
+ }
+ },
+ background: theme === 'dark' ? '#0F172B' : '#fff',
+ },
+ plotOptions: {
+ bar: {
+ horizontal: false,
+ columnWidth: '55%',
+ borderRadius: 1,
+ },
+ },
+ dataLabels: {
+ enabled: true,
+ formatter: function (val: number) {
+ return val.toString();
+ },
+ style: {
+ fontSize: '12px',
+ colors: [theme === 'dark' ? '#fff' : '#000']
+ }
+ },
+ stroke: {
+ show: true,
+ width: 2,
+ colors: ['transparent']
+ },
+ xaxis: {
+ categories: [...new Set(data.map(item => item.tahun_angkatan))].sort(),
+ title: {
+ text: 'Tahun Angkatan',
+ style: {
+ fontSize: '14px',
+ fontWeight: 'bold',
+ color: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ labels: {
+ style: {
+ fontSize: '12px',
+ colors: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ axisBorder: {
+ show: true,
+ color: theme === 'dark' ? '#374151' : '#E5E7EB'
+ },
+ axisTicks: {
+ show: true,
+ color: theme === 'dark' ? '#374151' : '#E5E7EB'
+ },
+ tickAmount: 5,
+ },
+ yaxis: {
+ title: {
+ text: 'Jumlah Mahasiswa',
+ style: {
+ fontSize: '14px',
+ fontWeight: 'bold',
+ color: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ labels: {
+ style: {
+ fontSize: '12px',
+ colors: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ axisBorder: {
+ show: true,
+ color: theme === 'dark' ? '#374151' : '#E5E7EB'
+ }
+ },
+ fill: {
+ opacity: 1
+ },
+ colors: ['#3B82F6', '#EC4899', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#06B6D4'],
+ tooltip: {
+ theme: theme === 'dark' ? 'dark' : 'light',
+ y: {
+ formatter: function (val: number) {
+ return val + " mahasiswa";
+ }
+ }
+ },
+ legend: {
+ position: 'top',
+ fontSize: '14px',
+ markers: {
+ size: 12,
+ },
+ itemMargin: {
+ horizontal: 10,
+ },
+ labels: {
+ colors: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ grid: {
+ borderColor: theme === 'dark' ? '#374151' : '#E5E7EB',
+ strokeDashArray: 4,
+ padding: {
+ top: 20,
+ right: 0,
+ bottom: 0,
+ left: 0
+ }
+ }
+ };
+
+ const series = processSeriesData();
+
+ if (loading) {
+ return (
+
+
+
+ Loading...
+
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+
+ Error: {error}
+
+
+
+ );
+ }
+
+ if (data.length === 0) {
+ return (
+
+
+
+ Tidak ada data yang tersedia
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ Nama Beasiswa Dashboard
+ {selectedYear !== 'all' ? ` Angkatan ${selectedYear}` : ''}
+
+
+
+
+ {typeof window !== 'undefined' && series.length > 0 && (
+
+ )}
+
+
+
+ );
+}
diff --git a/components/chartsDashboard/ProvinsiMahasiswaPieChart.tsx b/components/chartsDashboard/ProvinsiMahasiswaPieChart.tsx
new file mode 100644
index 0000000..8432c4b
--- /dev/null
+++ b/components/chartsDashboard/ProvinsiMahasiswaPieChart.tsx
@@ -0,0 +1,185 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import dynamic from 'next/dynamic';
+import { ApexOptions } from 'apexcharts';
+import { useTheme } from 'next-themes';
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+
+// Dynamically import ApexCharts to avoid SSR issues
+const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
+
+interface ProvinsiMahasiswaData {
+ provinsi: string;
+ jumlah_mahasiswa: number;
+}
+
+export default function ProvinsiMahasiswaPieChart() {
+ const { theme } = useTheme();
+ const [data, setData] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ setLoading(true);
+ setError(null);
+
+ const url = `/api/mahasiswa/provinsi-mahasiswa`;
+
+ const response = await fetch(url);
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`);
+ }
+
+ const result = await response.json();
+
+ if (!Array.isArray(result)) {
+ throw new Error('Invalid data format received from server');
+ }
+
+ setData(result);
+ } catch (err) {
+ console.error('Error in fetchData:', err);
+ setError(err instanceof Error ? err.message : 'An error occurred while fetching data');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, []);
+
+ const chartOptions: ApexOptions = {
+ chart: {
+ type: 'pie',
+ background: theme === 'dark' ? '#0F172B' : '#fff',
+ },
+ labels: data.map(item => item.provinsi),
+ colors: ['#3B82F6', '#10B981'],
+ legend: {
+ position: 'bottom',
+ fontSize: '14px',
+ markers: {
+ size: 12,
+ },
+ itemMargin: {
+ horizontal: 10,
+ vertical: 5,
+ },
+ labels: {
+ colors: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ dataLabels: {
+ enabled: true,
+ formatter: function (val: number) {
+ return `${val.toFixed(0)}%`;
+ },
+ style: {
+ fontSize: '14px',
+ fontWeight: 'bold',
+ colors: [theme === 'dark' ? '#fff' : '#000']
+ },
+ offsetY: -10,
+ dropShadow: {
+ enabled: true,
+ opacity: 0.3,
+ blur: 3,
+ color: '#000',
+ }
+ },
+ tooltip: {
+ theme: theme === 'dark' ? 'dark' : 'light',
+ y: {
+ formatter: function (val: number) {
+ return val + " mahasiswa";
+ }
+ }
+ },
+ plotOptions: {
+ pie: {
+ donut: {
+ size: '0%',
+ },
+ offsetY: 0,
+ },
+ },
+ stroke: {
+ width: 2,
+ colors: [theme === 'dark' ? '#0F172B' : '#fff'],
+ },
+ states: {
+ hover: {
+ filter: {
+ type: 'darken',
+ },
+ },
+ },
+ };
+
+ const series = data.map(item => item.jumlah_mahasiswa);
+
+ if (loading) {
+ return (
+
+
+
+ Loading...
+
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+
+ Error: {error}
+
+
+
+ );
+ }
+
+ if (data.length === 0) {
+ return (
+
+
+
+ Tidak ada data yang tersedia
+
+
+
+ );
+ }
+
+ const totalMahasiswa = data.reduce((sum, item) => sum + item.jumlah_mahasiswa, 0);
+
+ return (
+
+
+
+ Asal Provinsi Mahasiswa
+
+
+
+
+ {typeof window !== 'undefined' && series.length > 0 && (
+
+ )}
+
+
+
+ );
+}
diff --git a/components/chartsDashboard/TingkatPrestasiDashChart.tsx b/components/chartsDashboard/TingkatPrestasiDashChart.tsx
new file mode 100644
index 0000000..56f7d9a
--- /dev/null
+++ b/components/chartsDashboard/TingkatPrestasiDashChart.tsx
@@ -0,0 +1,270 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import dynamic from 'next/dynamic';
+import { ApexOptions } from 'apexcharts';
+import { useTheme } from 'next-themes';
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+
+// Dynamically import ApexCharts to avoid SSR issues
+const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
+
+interface TingkatPrestasiData {
+ tahun_angkatan: string | number;
+ tingkat_prestasi: string;
+ tingkat_mahasiswa_prestasi: number;
+}
+
+interface Props {
+ selectedYear: string;
+}
+
+export default function TingkatPrestasiDashChart({ selectedYear }: Props) {
+ const { theme } = useTheme();
+ const [data, setData] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ setLoading(true);
+ setError(null);
+
+ const url = `/api/mahasiswa/tingkat-prestasi-dashboard?tahunAngkatan=${selectedYear}`;
+
+ const response = await fetch(url);
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`);
+ }
+
+ const result = await response.json();
+
+ if (!Array.isArray(result)) {
+ throw new Error('Invalid data format received from server');
+ }
+
+ // API already returns sorted data, no need to sort again
+ setData(result);
+ } catch (err) {
+ console.error('Error in fetchData:', err);
+ setError(err instanceof Error ? err.message : 'An error occurred while fetching data');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [selectedYear]);
+
+ // Process data for series
+ const processSeriesData = () => {
+ if (!data.length) {
+ return [];
+ }
+
+ const years = [...new Set(data.map(item => item.tahun_angkatan))].sort((a, b) => Number(a) - Number(b));
+ const tingkatTypes = [...new Set(data.map(item => item.tingkat_prestasi))].sort();
+
+ return tingkatTypes.map(tingkat => ({
+ name: tingkat,
+ data: years.map(year => {
+ const item = data.find(d => String(d.tahun_angkatan) === String(year) && d.tingkat_prestasi === tingkat);
+ return item?.tingkat_mahasiswa_prestasi || 0;
+ })
+ }));
+ };
+
+ const chartOptions: ApexOptions = {
+ chart: {
+ type: 'bar',
+ stacked: false,
+ toolbar: {
+ show: true,
+ tools: {
+ download: true,
+ selection: true,
+ zoom: true,
+ zoomin: true,
+ zoomout: true,
+ pan: true,
+ reset: true
+ }
+ },
+ background: theme === 'dark' ? '#0F172B' : '#fff',
+ },
+ plotOptions: {
+ bar: {
+ horizontal: false,
+ columnWidth: '55%',
+ borderRadius: 1,
+ },
+ },
+ dataLabels: {
+ enabled: true,
+ formatter: function (val: number) {
+ return val?.toString() || '0';
+ },
+ style: {
+ fontSize: '12px',
+ colors: [theme === 'dark' ? '#fff' : '#000']
+ }
+ },
+ stroke: {
+ show: true,
+ width: 2,
+ colors: ['transparent']
+ },
+ xaxis: {
+ categories: [...new Set(data.map(item => item.tahun_angkatan))].sort((a, b) => Number(a) - Number(b)),
+ title: {
+ text: 'Tahun Angkatan',
+ style: {
+ fontSize: '14px',
+ fontWeight: 'bold',
+ color: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ labels: {
+ style: {
+ fontSize: '12px',
+ colors: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ axisBorder: {
+ show: true,
+ color: theme === 'dark' ? '#374151' : '#E5E7EB'
+ },
+ axisTicks: {
+ show: true,
+ color: theme === 'dark' ? '#374151' : '#E5E7EB'
+ },
+ },
+ yaxis: {
+ title: {
+ text: 'Jumlah Mahasiswa',
+ style: {
+ fontSize: '14px',
+ fontWeight: 'bold',
+ color: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ labels: {
+ style: {
+ fontSize: '12px',
+ colors: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ axisBorder: {
+ show: true,
+ color: theme === 'dark' ? '#374151' : '#E5E7EB'
+ },
+ tickAmount: 5,
+ },
+ fill: {
+ opacity: 1
+ },
+ colors: ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899', '#06B6D4'],
+ tooltip: {
+ theme: theme === 'dark' ? 'dark' : 'light',
+ y: {
+ formatter: function (val: number) {
+ return val + " mahasiswa";
+ }
+ }
+ },
+ legend: {
+ position: 'top',
+ fontSize: '14px',
+ markers: {
+ size: 12,
+ },
+ itemMargin: {
+ horizontal: 10,
+ },
+ labels: {
+ colors: theme === 'dark' ? '#fff' : '#000'
+ }
+ },
+ grid: {
+ borderColor: theme === 'dark' ? '#374151' : '#E5E7EB',
+ strokeDashArray: 4,
+ padding: {
+ top: 20,
+ right: 0,
+ bottom: 0,
+ left: 0
+ }
+ }
+ };
+
+ const series = processSeriesData();
+
+ if (loading) {
+ return (
+
+
+
+ Loading...
+
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+
+ Error: {error}
+
+
+
+ );
+ }
+
+ if (data.length === 0) {
+ return (
+
+
+
+ Tidak ada data yang tersedia
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ Tingkat Prestasi Dashboard
+ {selectedYear !== 'all' ? ` Angkatan ${selectedYear}` : ''}
+
+
+
+
+ {typeof window !== 'undefined' && series.length > 0 ? (
+
+ ) : (
+
+
+ {series.length === 0 ? 'Tidak ada data untuk ditampilkan' : 'Loading chart...'}
+
+
+ )}
+
+
+
+ );
+}
diff --git a/components/ui/Navbar.tsx b/components/ui/Navbar.tsx
index d7a67d7..73e4d8e 100644
--- a/components/ui/Navbar.tsx
+++ b/components/ui/Navbar.tsx
@@ -115,7 +115,7 @@ const Navbar = () => {
Dashboard
-
+ {/*
+ */}
>
)}
@@ -267,8 +267,12 @@ const MobileNavContent = ({ user, onLogout }: MobileNavContentProps) => {
{user ? (
Menu Utama
+
+
+ Dashboard
+
-
+ {/*
Visualisasi
@@ -290,7 +294,7 @@ const MobileNavContent = ({ user, onLogout }: MobileNavContentProps) => {
Prestasi
-
+
*/}
{/* Kelola Data - Only for Admin */}
{user.role_user === 'admin' && (