Files
portaldata/components/charts/StatistikMahasiswaChart.tsx
Randa Firman Putra 6d86e1ca2f Change Alur Aplikasi
2025-07-14 15:07:33 +07:00

326 lines
7.3 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import dynamic from 'next/dynamic';
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { useTheme } from "next-themes";
// Import ApexCharts secara dinamis untuk menghindari error SSR
const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
interface MahasiswaStatistik {
tahun_angkatan: number;
total_mahasiswa: number;
pria: number;
wanita: number;
}
export default function StatistikMahasiswaChart() {
const { theme, systemTheme } = useTheme();
const [statistikData, setStatistikData] = useState<MahasiswaStatistik[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [chartOptions, setChartOptions] = useState({
chart: {
type: 'bar' as const,
stacked: false,
toolbar: {
show: true,
tools: {
download: true,
selection: true,
zoom: true,
zoomin: true,
zoomout: true,
pan: true,
reset: true
},
},
zoom: {
enabled: true,
type: 'x' as const,
autoScaleYaxis: true
},
},
markers: {
size: 10,
shape: 'circle' as const,
strokeWidth: 0,
hover: {
size: 10
}
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: '55%',
},
},
dataLabels: {
enabled: true,
formatter: function (val: number) {
return val.toString()
},
position: 'top',
style: {
fontSize: '12px',
colors: ['#000']
},
},
stroke: {
show: false,
width: [0, 0, 0],
colors: ['transparent', 'transparent', 'transparent'],
curve: 'straight' as const
},
xaxis: {
categories: [] as number[],
title: {
text: 'Tahun Angkatan',
style: {
fontSize: '14px',
fontWeight: 'bold',
color: '#000'
}
},
labels: {
style: {
fontSize: '12px',
colors: '#000'
}
}
},
yaxis: [
{
title: {
text: 'Jumlah Mahasiswa',
style: {
fontSize: '14px',
fontWeight: 'bold',
color: '#000'
}
},
labels: {
style: {
fontSize: '12px',
colors: '#000'
}
},
min:0,
tickAmount: 5
}
],
fill: {
opacity: 1
},
legend: {
position: 'top' as const,
fontSize: '14px',
markers: {
size: 12,
},
itemMargin: {
horizontal: 10,
},
labels: {
colors: '#000'
}
},
colors: ['#3B82F6', '#10B981', '#EC4899'],
tooltip: {
theme: 'light',
y: [
{
formatter: function (val: number) {
return val + " mahasiswa"
}
},
{
formatter: function (val: number) {
return val + " mahasiswa"
}
},
{
formatter: function (val: number) {
return val + " mahasiswa"
}
}
]
}
});
useEffect(() => {
const fetchData = async () => {
try {
const statistikResponse = await fetch('/api/mahasiswa/statistik', {
cache: 'no-store',
});
if (!statistikResponse.ok) {
throw new Error('Failed to fetch statistik data');
}
const statistikData = await statistikResponse.json();
setStatistikData(statistikData);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
console.error('Error fetching data:', err);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
// Update theme when it changes
useEffect(() => {
const currentTheme = theme === 'system' ? systemTheme : theme;
const textColor = currentTheme === 'dark' ? '#fff' : '#000';
const tooltipTheme = currentTheme === 'dark' ? 'dark' : 'light';
setChartOptions(prev => ({
...prev,
chart: {
...prev.chart,
background: currentTheme === 'dark' ? '#0F172B' : '#fff',
},
dataLabels: {
...prev.dataLabels,
style: {
...prev.dataLabels.style,
colors: [textColor]
},
},
xaxis: {
...prev.xaxis,
title: {
...prev.xaxis.title,
style: {
...prev.xaxis.title.style,
color: textColor
}
},
labels: {
...prev.xaxis.labels,
style: {
...prev.xaxis.labels.style,
colors: textColor
}
}
},
yaxis: [
{
...prev.yaxis[0],
title: {
...prev.yaxis[0].title,
style: {
...prev.yaxis[0].title.style,
color: textColor
}
},
labels: {
...prev.yaxis[0].labels,
style: {
...prev.yaxis[0].labels.style,
colors: textColor
}
}
}
],
legend: {
...prev.legend,
labels: {
...prev.legend.labels,
colors: textColor
}
},
tooltip: {
...prev.tooltip,
theme: tooltipTheme
}
}));
}, [theme, systemTheme]);
// Update categories when data changes
useEffect(() => {
if (statistikData.length > 0) {
setChartOptions(prev => ({
...prev,
xaxis: {
...prev.xaxis,
categories: statistikData.map(item => item.tahun_angkatan)
},
yaxis: [
{
...prev.yaxis[0],
}
]
}));
}
}, [statistikData]);
const chartSeries = [
{
name: 'Laki-laki',
type: 'bar' as const,
data: statistikData.map(item => item.pria)
},
{
name: 'Total',
type: 'bar' as const,
data: statistikData.map(item => item.total_mahasiswa)
},
{
name: 'Perempuan',
type: 'bar' as const,
data: statistikData.map(item => item.wanita)
}
];
if (loading) {
return (
<Card className="bg-white dark:bg-slate-900 shadow-lg">
<CardHeader>
<CardTitle className="text-xl font-bold dark:text-white">
Loading...
</CardTitle>
</CardHeader>
</Card>
);
}
if (error) {
return (
<Card className="bg-white dark:bg-slate-900 shadow-lg">
<CardHeader>
<CardTitle className="text-xl font-bold text-red-500 dark:text-red-400">
Error: {error}
</CardTitle>
</CardHeader>
</Card>
);
}
return (
<Card className="bg-white dark:bg-slate-900 shadow-lg">
<CardHeader>
<CardTitle className="text-xl font-bold dark:text-white">
Total Mahasiswa
</CardTitle>
</CardHeader>
<CardContent>
<div className="h-[300px] sm:h-[350px] md:h-[400px] w-full max-w-5xl mx-auto">
<Chart
options={chartOptions}
series={chartSeries}
type="bar"
height="100%"
width="100%"
/>
</div>
</CardContent>
</Card>
);
}