'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"; import { Button } from "@/components/ui/button"; import { ExternalLink } from "lucide-react"; import Link from "next/link"; const Chart = dynamic(() => import('react-apexcharts'), { ssr: false }); interface MKBelumDiambilData { tahun_angkatan: number; jenis_mk: string; jumlah_belum_diambil: number; total_mata_kuliah: number; total_mahasiswa: number; } interface MKBelumDiambilChartProps { selectedYear?: string; height?: string; showDetailButton?: boolean; } export default function MKBelumDiambilChart({ selectedYear = 'all', height = "h-[300px] sm:h-[350px] md:h-[300px]", showDetailButton = true }: MKBelumDiambilChartProps) { 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 [options, setOptions] = useState({ chart: { type: 'bar', stacked: true, toolbar: { show: true, }, background: theme === 'dark' ? '#0F172B' : '#fff', }, plotOptions: { bar: { horizontal: true, barHeight: '70%', }, }, dataLabels: { enabled: true, formatter: function (val: number, opts: any) { const dataPointIndex = opts.dataPointIndex; // Hitung total untuk tahun angkatan ini const allSeries = opts.w.config.series; let totalForYear = 0; allSeries.forEach((series: any) => { totalForYear += series.data[dataPointIndex] || 0; }); if (totalForYear === 0 || val === 0) return ''; const percentage = ((val / totalForYear) * 100).toFixed(0); return percentage + '%'; }, style: { fontSize: '12px', colors: [theme === 'dark' ? '#fff' : '#000'] } }, stroke: { show: true, width: 2, colors: ['transparent'], }, xaxis: { categories: [], title: { text: 'Jumlah Mata Kuliah Belum Diambil', style: { fontSize: '14px', fontWeight: 'bold', color: theme === 'dark' ? '#fff' : '#000' } }, labels: { style: { fontSize: '12px', colors: theme === 'dark' ? '#fff' : '#000' } }, min: 0, tickAmount: 5 }, yaxis: { title: { text: 'Tahun Angkatan', style: { fontSize: '14px', fontWeight: 'bold', color: theme === 'dark' ? '#fff' : '#000' } }, labels: { style: { fontSize: '12px', colors: theme === 'dark' ? '#fff' : '#000' } } }, fill: { opacity: 1, }, legend: { position: 'top', fontSize: '14px', markers: { size: 12, }, itemMargin: { horizontal: 10, }, labels: { colors: theme === 'dark' ? '#fff' : '#000' } }, colors: ['#3B82F6', '#10B981', '#F59E0B'], // Wajib (blue), Pilihan Wajib (green), Pilihan (amber) tooltip: { theme: theme === 'dark' ? 'dark' : 'light', shared: true, intersect: false, custom: function({ series, seriesIndex, dataPointIndex, w }: any) { const tahun = w.globals.labels[dataPointIndex]; const isDark = theme === 'dark'; // Hitung total untuk tahun ini let total = 0; series.forEach((seriesData: number[]) => { total += seriesData[dataPointIndex] || 0; }); const jenisMKNames = w.config.series.map((s: any) => s.name); const jenisMKColors = w.config.colors; let tooltipContent = `
Angkatan ${tahun}
`; // Tambahkan setiap jenis MK series.forEach((seriesData: number[], index: number) => { const value = seriesData[dataPointIndex] || 0; if (value > 0) { const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : '0'; tooltipContent += `
${jenisMKNames[index]} ${value} (${percentage}%)
`; } }); // Tambahkan total tooltipContent += `
Total MK Belum Diambil ${total}
`; return tooltipContent; } } }); // Update theme when it changes useEffect(() => { const currentTheme = theme; setOptions(prev => ({ ...prev, chart: { ...prev.chart, background: currentTheme === 'dark' ? '#0F172B' : '#fff', }, dataLabels: { ...prev.dataLabels, formatter: function (val: number, opts: any) { const dataPointIndex = opts.dataPointIndex; // Hitung total untuk tahun angkatan ini const allSeries = opts.w.config.series; let totalForYear = 0; allSeries.forEach((series: any) => { totalForYear += series.data[dataPointIndex] || 0; }); if (totalForYear === 0 || val === 0) return ''; const percentage = ((val / totalForYear) * 100).toFixed(0); return percentage + '%'; }, style: { ...prev.dataLabels?.style, colors: [currentTheme === 'dark' ? '#fff' : '#000'] } }, xaxis: { ...prev.xaxis, title: { ...prev.xaxis?.title, style: { ...prev.xaxis?.title?.style, color: currentTheme === 'dark' ? '#fff' : '#000' } }, labels: { ...prev.xaxis?.labels, style: { ...prev.xaxis?.labels?.style, colors: currentTheme === 'dark' ? '#fff' : '#000' } } }, yaxis: { ...prev.yaxis, title: { ...(Array.isArray(prev.yaxis) ? prev.yaxis[0]?.title : prev.yaxis?.title), style: { ...(Array.isArray(prev.yaxis) ? prev.yaxis[0]?.title?.style : prev.yaxis?.title?.style), color: currentTheme === 'dark' ? '#fff' : '#000' } }, labels: { ...(Array.isArray(prev.yaxis) ? prev.yaxis[0]?.labels : prev.yaxis?.labels), style: { ...(Array.isArray(prev.yaxis) ? prev.yaxis[0]?.labels?.style : prev.yaxis?.labels?.style), colors: currentTheme === 'dark' ? '#fff' : '#000' } } }, legend: { ...prev.legend, labels: { ...prev.legend?.labels, colors: currentTheme === 'dark' ? '#fff' : '#000' } }, tooltip: { ...prev.tooltip, theme: currentTheme === 'dark' ? 'dark' : 'light', custom: function({ series, seriesIndex, dataPointIndex, w }: any) { const tahun = w.globals.labels[dataPointIndex]; const isDark = currentTheme === 'dark'; // Hitung total untuk tahun ini let total = 0; series.forEach((seriesData: number[]) => { total += seriesData[dataPointIndex] || 0; }); const jenisMKNames = w.config.series.map((s: any) => s.name); const jenisMKColors = w.config.colors; let tooltipContent = `
Angkatan ${tahun}
`; // Tambahkan setiap jenis MK series.forEach((seriesData: number[], index: number) => { const value = seriesData[dataPointIndex] || 0; if (value > 0) { const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : '0'; tooltipContent += `
${jenisMKNames[index]} ${value} (${percentage}%)
`; } }); // Tambahkan total tooltipContent += `
Total MK Belum Diambil ${total}
`; return tooltipContent; } } })); }, [theme]); useEffect(() => { setMounted(true); }, []); useEffect(() => { const fetchData = async () => { try { setLoading(true); setError(null); const url = selectedYear === 'all' ? '/api/mahasiswa/mkbelumdiambil' : `/api/mahasiswa/mkbelumdiambil?tahun_angkatan=${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'); } setData(result); // Ambil tahun angkatan unik dan urutkan const tahunAngkatan = [...new Set(result.map(item => item.tahun_angkatan))].sort((a, b) => a - b); // Ambil jenis MK unik dan urutkan sesuai urutan: Wajib, Pilihan Wajib, Pilihan const jenisMKOrder = ['Wajib', 'Pilihan Wajib', 'Pilihan']; const jenisMK = [...new Set(result.map(item => item.jenis_mk))].sort((a, b) => { return jenisMKOrder.indexOf(a) - jenisMKOrder.indexOf(b); }); // Buat series data const seriesData = jenisMK.map(jenis => ({ name: jenis, data: tahunAngkatan.map(tahun => { const item = result.find(d => d.tahun_angkatan === tahun && d.jenis_mk === jenis); return item ? item.jumlah_belum_diambil : 0; }), })); setSeries(seriesData); setOptions(prev => ({ ...prev, xaxis: { ...prev.xaxis, categories: tahunAngkatan, }, })); } 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]); if (!mounted) { return null; } if (loading) { return ( Loading... ); } if (error) { return ( Error: {error} ); } if (data.length === 0) { return ( Mata Kuliah Belum Diambil {selectedYear !== 'all' ? ` Angkatan ${selectedYear}` : ''}

Tidak ada data yang tersedia untuk periode ini.

); } return (
Mata Kuliah Belum Diambil Berdasarkan Jenis MK {selectedYear !== 'all' ? ` Angkatan ${selectedYear}` : ''} {showDetailButton && ( )}
); }