ayo commit lagi

This commit is contained in:
Randa Firman Putra
2026-02-22 00:21:37 +07:00
parent 3797daa557
commit 211fd9c955
4 changed files with 49 additions and 49 deletions

View File

@@ -26,6 +26,7 @@ export default function JenisPendaftaranPerAngkatanChart({ tahunAngkatan }: Prop
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [data, setData] = useState<JenisPendaftaranData[]>([]); const [data, setData] = useState<JenisPendaftaranData[]>([]);
const [series, setSeries] = useState<number[]>([]); const [series, setSeries] = useState<number[]>([]);
const [chartColors, setChartColors] = useState<string[]>([]);
const [options, setOptions] = useState<ApexOptions>({ const [options, setOptions] = useState<ApexOptions>({
chart: { chart: {
type: 'pie', type: 'pie',
@@ -43,7 +44,7 @@ export default function JenisPendaftaranPerAngkatanChart({ tahunAngkatan }: Prop
} }
}, },
labels: [], labels: [],
colors: ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899', '#06B6D4', '#F97316'], colors: [],
legend: { legend: {
position: 'bottom', position: 'bottom',
fontSize: '14px', fontSize: '14px',
@@ -101,9 +102,11 @@ export default function JenisPendaftaranPerAngkatanChart({ tahunAngkatan }: Prop
tooltip: { tooltip: {
...prev.tooltip, ...prev.tooltip,
theme: theme === 'dark' ? 'dark' : 'light' theme: theme === 'dark' ? 'dark' : 'light'
} },
// Preserve computed colors across theme changes
colors: chartColors.length > 0 ? chartColors : prev.colors,
})); }));
}, [theme]); }, [theme, chartColors]);
// Update dataLabels formatter when data changes // Update dataLabels formatter when data changes
useEffect(() => { useEffect(() => {
@@ -141,29 +144,46 @@ export default function JenisPendaftaranPerAngkatanChart({ tahunAngkatan }: Prop
setLoading(true); setLoading(true);
setError(null); setError(null);
const response = await fetch(`/api/mahasiswa/jenis-pendaftaran?tahun_angkatan=${tahunAngkatan}`); // 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) { if (!response.ok) {
throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`); throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`);
} }
const result = await response.json(); const result = await response.json();
const allResult = allResponse.ok ? await allResponse.json() : result;
if (!Array.isArray(result)) { if (!Array.isArray(result)) {
throw new Error('Invalid data format received from server'); 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 // Process data for pie chart
const jenisPendaftaran = [...new Set(result.map(item => item.jenis_pendaftaran))].sort(); const jenisPendaftaran = [...new Set(result.map(item => item.jenis_pendaftaran))].sort() as string[];
const jumlahData = jenisPendaftaran.map(jenis => { const jumlahData = jenisPendaftaran.map((jenis: string) => {
const item = result.find(d => d.jenis_pendaftaran === jenis); const item = result.find((d: any) => d.jenis_pendaftaran === jenis);
return item ? item.jumlah : 0; 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); setSeries(jumlahData);
setOptions(prev => ({ setOptions(prev => ({
...prev, ...prev,
labels: jenisPendaftaran, labels: jenisPendaftaran,
colors: computedColors,
})); }));
// Store processed data // Store processed data

View File

@@ -73,15 +73,10 @@ export default function StatistikMahasiswaChart({
const seriesIndex = opts.seriesIndex; const seriesIndex = opts.seriesIndex;
const dataPointIndex = opts.dataPointIndex; const dataPointIndex = opts.dataPointIndex;
// Jika series Total (index 2), tampilkan angka // Hitung total dari Laki-laki (index 0) dan Perempuan (index 1)
if (seriesIndex === 2) { const lakiLakiData = opts.w.config.series[0]?.data || [];
return val.toString(); const perempuanData = opts.w.config.series[1]?.data || [];
} const totalValue = (lakiLakiData[dataPointIndex] || 0) + (perempuanData[dataPointIndex] || 0);
// Untuk Laki-laki (index 0) dan Perempuan (index 1), hitung persentase
// Ambil data total dari series Total (index 2)
const totalSeriesData = opts.w.config.series[2]?.data || [];
const totalValue = totalSeriesData[dataPointIndex] || 0;
if (totalValue === 0 || val === 0) return '0%'; if (totalValue === 0 || val === 0) return '0%';
@@ -97,7 +92,7 @@ export default function StatistikMahasiswaChart({
stroke: { stroke: {
show: true, show: true,
width: 2, width: 2,
colors: ['transparent', 'transparent', 'transparent'], colors: ['transparent', 'transparent'],
curve: 'straight' as const curve: 'straight' as const
}, },
xaxis: { xaxis: {
@@ -153,7 +148,7 @@ export default function StatistikMahasiswaChart({
colors: '#000' colors: '#000'
} }
}, },
colors: ['#3B82F6', '#EC4899', '#10B981'], colors: ['#3B82F6', '#EC4899'],
tooltip: { tooltip: {
theme: 'light', theme: 'light',
shared: true, shared: true,
@@ -161,7 +156,7 @@ export default function StatistikMahasiswaChart({
custom: function({ series, seriesIndex, dataPointIndex, w }: any) { custom: function({ series, seriesIndex, dataPointIndex, w }: any) {
const lakiLaki = series[0][dataPointIndex]; const lakiLaki = series[0][dataPointIndex];
const perempuan = series[1][dataPointIndex]; const perempuan = series[1][dataPointIndex];
const total = series[2][dataPointIndex]; const total = lakiLaki + perempuan;
const tahun = w.globals.labels[dataPointIndex]; const tahun = w.globals.labels[dataPointIndex];
return ` return `
@@ -199,7 +194,6 @@ export default function StatistikMahasiswaChart({
display: flex; display: flex;
align-items: center; align-items: center;
"> ">
<div style="width: 8px; height: 8px; background: #10B981; border-radius: 50%; margin-right: 8px;"></div>
<span style="font-size: 12px; font-weight: 600; color: #1f2937;">Total</span> <span style="font-size: 12px; font-weight: 600; color: #1f2937;">Total</span>
<span style="font-size: 13px; font-weight: 700; color: #10B981; margin-left: auto;">${total}</span> <span style="font-size: 13px; font-weight: 700; color: #10B981; margin-left: auto;">${total}</span>
</div> </div>
@@ -248,18 +242,11 @@ export default function StatistikMahasiswaChart({
dataLabels: { dataLabels: {
...prev.dataLabels, ...prev.dataLabels,
formatter: function (val: number, opts: any) { formatter: function (val: number, opts: any) {
const seriesIndex = opts.seriesIndex;
const dataPointIndex = opts.dataPointIndex; const dataPointIndex = opts.dataPointIndex;
// Jika series Total (index 2), tampilkan angka const lakiLakiData = opts.w.config.series[0]?.data || [];
if (seriesIndex === 2) { const perempuanData = opts.w.config.series[1]?.data || [];
return val.toString(); const totalValue = (lakiLakiData[dataPointIndex] || 0) + (perempuanData[dataPointIndex] || 0);
}
// Untuk Laki-laki (index 0) dan Perempuan (index 1), hitung persentase
// Ambil data total dari series Total (index 2)
const totalSeriesData = opts.w.config.series[2]?.data || [];
const totalValue = totalSeriesData[dataPointIndex] || 0;
if (totalValue === 0 || val === 0) return '0%'; if (totalValue === 0 || val === 0) return '0%';
@@ -320,13 +307,9 @@ export default function StatistikMahasiswaChart({
custom: function({ series, seriesIndex, dataPointIndex, w }: any) { custom: function({ series, seriesIndex, dataPointIndex, w }: any) {
const lakiLaki = series[0][dataPointIndex]; const lakiLaki = series[0][dataPointIndex];
const perempuan = series[1][dataPointIndex]; const perempuan = series[1][dataPointIndex];
const total = series[2][dataPointIndex]; const total = lakiLaki + perempuan;
const tahun = w.globals.labels[dataPointIndex]; const tahun = w.globals.labels[dataPointIndex];
const bgColor = currentTheme === 'dark' ? '#1e293b' : 'white';
const textColor = currentTheme === 'dark' ? '#fff' : '#000';
const borderColor = currentTheme === 'dark' ? '#475569' : '#ccc';
const isDark = currentTheme === 'dark'; const isDark = currentTheme === 'dark';
return ` return `
@@ -364,7 +347,6 @@ export default function StatistikMahasiswaChart({
display: flex; display: flex;
align-items: center; align-items: center;
"> ">
<div style="width: 8px; height: 8px; background: #10B981; border-radius: 50%; margin-right: 8px;"></div>
<span style="font-size: 12px; font-weight: 600; color: ${isDark ? '#f1f5f9' : '#1f2937'};">Total</span> <span style="font-size: 12px; font-weight: 600; color: ${isDark ? '#f1f5f9' : '#1f2937'};">Total</span>
<span style="font-size: 13px; font-weight: 700; color: #10B981; margin-left: auto;">${total}</span> <span style="font-size: 13px; font-weight: 700; color: #10B981; margin-left: auto;">${total}</span>
</div> </div>
@@ -404,11 +386,6 @@ export default function StatistikMahasiswaChart({
name: 'Perempuan', name: 'Perempuan',
type: 'bar' as const, type: 'bar' as const,
data: statistikData.map(item => item.wanita) data: statistikData.map(item => item.wanita)
},
{
name: 'Total',
type: 'bar' as const,
data: statistikData.map(item => item.total_mahasiswa)
} }
]; ];

View File

@@ -70,7 +70,7 @@ export default function StatusMahasiswaFilterPieChart({ selectedYear, selectedSt
background: theme === 'dark' ? '#0F172B' : '#fff', background: theme === 'dark' ? '#0F172B' : '#fff',
}, },
labels: ['Laki-laki', 'Perempuan'], labels: ['Laki-laki', 'Perempuan'],
colors: ['#3B82F6', '#EC4899'], colors: ['#008FFB', '#EC4899'],
legend: { legend: {
position: 'bottom', position: 'bottom',
fontSize: '14px', fontSize: '14px',

View File

@@ -65,9 +65,18 @@ export default function StatusMahasiswaPieChartPerangkatan({ selectedYear }: Pro
fetchData(); fetchData();
}, [selectedYear]); }, [selectedYear]);
// Color mapping consistent with StatusMahasiswaChart bar chart
const STATUS_COLOR_MAP: { [key: string]: string } = {
'Aktif': '#008FFB',
'Lulus': '#00E396',
'Cuti': '#FEB019',
'Non Aktif': '#EF4444',
};
// Prepare data for pie chart // Prepare data for pie chart
const series = data.map(item => item.jumlah); const series = data.map(item => item.jumlah);
const labels = data.map(item => item.status_kuliah); const labels = data.map(item => item.status_kuliah);
const colors = data.map(item => STATUS_COLOR_MAP[item.status_kuliah] || '#775DD0');
const chartOptions: ApexOptions = { const chartOptions: ApexOptions = {
chart: { chart: {
@@ -103,13 +112,7 @@ export default function StatusMahasiswaPieChartPerangkatan({ selectedYear }: Pro
colors: theme === 'dark' ? '#fff' : '#000' colors: theme === 'dark' ? '#fff' : '#000'
} }
}, },
colors: [ colors: colors,
'#00E396', // Aktif - Green
'#008FFB', // Lulus - Blue
'#FEB019', // Cuti - Orange
'#FF4560', // Non-Aktif - Red
'#775DD0', // Lainnya - Purple
],
tooltip: { tooltip: {
theme: theme === 'dark' ? 'dark' : 'light', theme: theme === 'dark' ? 'dark' : 'light',
y: { y: {