'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 JenisPendaftaranData { tahun_angkatan: number; jenis_pendaftaran: string; jumlah: number; } interface Props { tahunAngkatan: string; } export default function JenisPendaftaranPerAngkatanChart({ tahunAngkatan }: Props) { const { theme } = useTheme(); const [mounted, setMounted] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [data, setData] = useState([]); const [series, setSeries] = useState([]); const [chartColors, setChartColors] = useState([]); const [options, setOptions] = useState({ chart: { type: 'pie', toolbar: { show: true, tools: { download: true, selection: true, zoom: true, zoomin: true, zoomout: true, pan: true, reset: true } } }, labels: [], colors: [], legend: { position: 'bottom', fontSize: '14px', markers: { size: 12, strokeWidth: 0 }, itemMargin: { horizontal: 10 }, labels: { colors: theme === 'dark' ? '#fff' : '#000' } }, dataLabels: { enabled: true, formatter: function (val: number, opts: any) { return `${val.toFixed(0)}%`; }, style: { fontSize: '14px', fontFamily: 'Inter, sans-serif', fontWeight: '500' }, offsetY: 0, dropShadow: { enabled: false } }, tooltip: { theme: theme === 'dark' ? 'dark' : 'light', y: { formatter: function (val: number) { return val + " mahasiswa" } } } }); // Update theme when it changes useEffect(() => { setOptions(prev => ({ ...prev, chart: { ...prev.chart, background: theme === 'dark' ? '#0F172B' : '#fff', }, legend: { ...prev.legend, labels: { ...prev.legend?.labels, colors: theme === 'dark' ? '#fff' : '#000' } }, tooltip: { ...prev.tooltip, theme: theme === 'dark' ? 'dark' : 'light' }, // Preserve computed colors across theme changes colors: chartColors.length > 0 ? chartColors : prev.colors, })); }, [theme, chartColors]); // Update dataLabels formatter when data changes useEffect(() => { if (data.length > 0) { setOptions(prev => ({ ...prev, dataLabels: { ...prev.dataLabels, formatter: function(val: number, opts: any) { // Calculate the percentage based on the current data const total = data.reduce((sum, item) => sum + item.jumlah, 0); const seriesIndex = opts.seriesIndex; const jenisPendaftaran = prev.labels?.[seriesIndex]; if (jenisPendaftaran) { const item = data.find(d => d.jenis_pendaftaran === jenisPendaftaran); const jumlah = item ? item.jumlah : 0; const percentage = (jumlah / total) * 100; return `${percentage.toFixed(0)}%`; } return `${val.toFixed(0)}%`; } } })); } }, [data]); useEffect(() => { setMounted(true); }, []); useEffect(() => { const fetchData = async () => { try { setLoading(true); setError(null); // Fetch year-filtered data and global data in parallel const [response, allResponse] = await Promise.all([ fetch(`/api/mahasiswa/jenis-pendaftaran?tahun_angkatan=${tahunAngkatan}`), fetch('/api/mahasiswa/jenis-pendaftaran'), ]); if (!response.ok) { throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`); } const result = await response.json(); const allResult = allResponse.ok ? await allResponse.json() : result; if (!Array.isArray(result)) { throw new Error('Invalid data format received from server'); } // Build global sorted jenis list (same order as bar chart) for stable color assignment const colorPalette = ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899', '#06B6D4', '#F97316']; const allJenis: string[] = [...new Set((allResult as any[]).map(item => item.jenis_pendaftaran as string))].sort() as string[]; // Process data for pie chart const jenisPendaftaran = [...new Set(result.map(item => item.jenis_pendaftaran))].sort() as string[]; const jumlahData = jenisPendaftaran.map((jenis: string) => { const item = result.find((d: any) => d.jenis_pendaftaran === jenis); return item ? item.jumlah : 0; }); // Assign colors based on global sorted position to match bar chart colors const computedColors = jenisPendaftaran.map((jenis: string) => { const idx = allJenis.indexOf(jenis); return idx >= 0 ? colorPalette[idx % colorPalette.length] : '#999999'; }); setChartColors(computedColors); setSeries(jumlahData); setOptions(prev => ({ ...prev, labels: jenisPendaftaran, colors: computedColors, })); // Store processed data 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); } }; if (tahunAngkatan) { fetchData(); } }, [tahunAngkatan]); if (!mounted) { return null; } 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, 0); return ( Persentase Jumlah Mahasiswa Berdasarkan Jenis Pendaftaran Angkatan {tahunAngkatan}
); }