Admin: Ubah Pie Chart Submenu IKM, Menu PPID

This commit is contained in:
2025-08-13 00:34:44 +08:00
parent a1d55e2b0a
commit a6832cad40

View File

@@ -1,10 +1,20 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
'use client' 'use client';
import React, { useEffect, useState } from 'react';
import { Box, Center, Flex, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
import colors from '@/con/colors'; import colors from '@/con/colors';
import { PieChart, Pie, Cell } from 'recharts'; import { PieChart } from '@mantine/charts'; // ✅ Ganti recharts dengan Mantine
import {
Box,
Center,
Flex,
Paper,
SimpleGrid,
Skeleton,
Stack,
Text,
Title,
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useShallowEffect } from '@mantine/hooks';
import { useState } from 'react';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import indeksKepuasanState from '../../../_state/landing-page/indeks-kepuasan'; import indeksKepuasanState from '../../../_state/landing-page/indeks-kepuasan';
@@ -21,13 +31,8 @@ function Page() {
const [donutDataJenisKelamin, setDonutDataJenisKelamin] = useState<ChartDataItem[]>([]); const [donutDataJenisKelamin, setDonutDataJenisKelamin] = useState<ChartDataItem[]>([]);
const [donutDataRating, setDonutDataRating] = useState<ChartDataItem[]>([]); const [donutDataRating, setDonutDataRating] = useState<ChartDataItem[]>([]);
const [donutDataKelompokUmur, setDonutDataKelompokUmur] = useState<ChartDataItem[]>([]); const [donutDataKelompokUmur, setDonutDataKelompokUmur] = useState<ChartDataItem[]>([]);
const [mounted, setMounted] = useState(false);
useShallowEffect(() => { useShallowEffect(() => {
setMounted(true);
}, []);
useEffect(() => {
if (data) { if (data) {
// Hitung total berdasarkan jenis kelamin // Hitung total berdasarkan jenis kelamin
const totalLaki = data.filter((item: any) => item.jenisKelamin?.name?.toLowerCase() === 'laki-laki').length; const totalLaki = data.filter((item: any) => item.jenisKelamin?.name?.toLowerCase() === 'laki-laki').length;
@@ -43,28 +48,29 @@ function Page() {
const totalMuda = data.filter((item: any) => item.kelompokUmur?.name?.toLowerCase() === 'muda').length; const totalMuda = data.filter((item: any) => item.kelompokUmur?.name?.toLowerCase() === 'muda').length;
const totalDewasa = data.filter((item: any) => item.kelompokUmur?.name?.toLowerCase() === 'dewasa').length; const totalDewasa = data.filter((item: any) => item.kelompokUmur?.name?.toLowerCase() === 'dewasa').length;
const totalLansia = data.filter((item: any) => item.kelompokUmur?.name?.toLowerCase() === 'lansia').length; const totalLansia = data.filter((item: any) => item.kelompokUmur?.name?.toLowerCase() === 'lansia').length;
// Update gender chart data // Update gender chart data
setDonutDataJenisKelamin([ setDonutDataJenisKelamin([
{ name: 'laki', value: totalLaki, color: colors['blue-button'], label: 'Laki-laki' }, { name: 'Laki-laki', value: totalLaki, color: colors['blue-button'] },
{ name: 'perempuan', value: totalPerempuan, color: '#10A85AFF', label: 'Perempuan' } { name: 'Perempuan', value: totalPerempuan, color: '#10A85AFF' },
]); ]);
// Update rating chart data // Update rating chart data
setDonutDataRating([ setDonutDataRating([
{ name: 'sangat_baik', value: totalSangatBaik, color: colors['blue-button'], label: 'Sangat Baik' }, { name: 'Sangat Baik', value: totalSangatBaik, color: colors['blue-button'] },
{ name: 'baik', value: totalBaik, color: '#10A85AFF', label: 'Baik' }, { name: 'Baik', value: totalBaik, color: '#10A85AFF' },
{ name: 'kurang_baik', value: totalKurangBaik, color: '#FFA500', label: 'Kurang Baik' }, { name: 'Kurang Baik', value: totalKurangBaik, color: '#FFA500' },
{ name: 'sangat_kurang_baik', value: totalSangatKurangBaik, color: '#FF4500', label: 'Sangat Kurang Baik' } { name: 'Sangat Kurang Baik', value: totalSangatKurangBaik, color: '#FF4500' },
]); ]);
// Update age group chart data // Update age group chart data
setDonutDataKelompokUmur([ setDonutDataKelompokUmur([
{ name: 'muda', value: totalMuda, color: colors['blue-button'], label: 'Muda' }, { name: 'Muda', value: totalMuda, color: colors['blue-button'] },
{ name: 'dewasa', value: totalDewasa, color: '#10A85AFF', label: 'Dewasa' }, { name: 'Dewasa', value: totalDewasa, color: '#10A85AFF' },
{ name: 'lansia', value: totalLansia, color: '#FFA500', label: 'Lansia' } { name: 'Lansia', value: totalLansia, color: '#FFA500' },
]); ]);
} }
}, [data]) }, [data]);
if (loading || !data) { if (loading || !data) {
return ( return (
@@ -77,137 +83,115 @@ function Page() {
if (data.length === 0) { if (data.length === 0) {
return ( return (
<Stack py={10}> <Stack py={10}>
<Text c='dimmed' ta="center" my="md">Belum ada data untuk ditampilkan</Text> <Text c="dimmed" ta="center" my="md">
Belum ada data untuk ditampilkan
</Text>
</Stack> </Stack>
) );
} }
return ( return (
<Stack> <Stack gap="xs">
{/* Chart */}
<Box>
<Paper bg={colors['white-1']} p={'md'} style={{ height: '100%' }}>
<Stack>
<Title pb={10} order={4} size="h4">Grafik Berdasarkan Jenis Kelamin Responden</Title>
{mounted && donutDataJenisKelamin.length === 0 ? (
<Text c='dimmed' ta="center" my="md">Belum ada data untuk ditampilkan dalam grafik</Text>
) : (
<Box>
<Center>
<PieChart width={250} height={350}>
<Pie
data={donutDataJenisKelamin}
dataKey="value"
nameKey="name"
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={100}
label={({ percent }: { percent: number }) => `${(percent * 100).toFixed(0)}%`}
labelLine={false}
>
{donutDataJenisKelamin.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</Center>
<Stack gap="sm" mt="md">
{donutDataJenisKelamin.map((entry) => (
<Flex key={entry.name} gap="md" align="center">
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
<Text size="sm">{entry.label}: {entry.value || 0}</Text>
</Flex>
))}
</Stack>
</Box>
)}
</Stack>
</Paper>
</Box>
<Box>
{/* Rating Chart */}
<Paper bg={colors['white-1']} p={'md'} style={{ height: '100%' }}>
<Stack>
<Title pb={10} order={4}>Grafik Berdasarkan Rating Responden</Title>
{mounted && donutDataRating.length === 0 ? (
<Text c='dimmed' ta="center" my="md">Belum ada data untuk ditampilkan dalam grafik</Text>
) : (
<Box>
<Center>
<PieChart width={350} height={350}>
<Pie
data={donutDataRating}
dataKey="value"
nameKey="name"
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={100}
label={({ percent }: { percent: number }) => `${(percent * 100).toFixed(0)}%`}
labelLine={false}
>
{donutDataRating.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</Center>
<Stack gap="sm" mt="md">
{donutDataRating.map((entry) => (
<Flex key={entry.name} gap="md" align="center">
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
<Text size="sm">{entry.label}: {entry.value || 0}</Text>
</Flex>
))}
</Stack>
</Box>
)}
</Stack>
</Paper>
</Box>
{/* Age Group Chart */}
<Box>
<Paper bg={colors['white-1']} p={'md'} style={{ height: '100%' }}> <SimpleGrid cols={{ base: 1, md: 3 }}>
<Stack> {/* Chart Jenis Kelamin */}
<Title pb={10} order={4}>Grafik Berdasarkan Kelompok Umur</Title> <Paper bg={colors['white-1']} p="md" radius="md">
{mounted && donutDataKelompokUmur.length === 0 ? ( <Stack>
<Text c='dimmed' ta="center" my="md">Belum ada data untuk ditampilkan dalam grafik</Text> <Title order={4}>Jenis Kelamin</Title>
) : ( {donutDataJenisKelamin.every(item => item.value === 0) ? (
<Box > <Text c="dimmed" ta="center" my="md">
<Center> Belum ada data untuk ditampilkan dalam grafik
<PieChart width={350} height={350}> </Text>
<Pie ) : (
data={donutDataKelompokUmur} <Box>
dataKey="value" <Center>
nameKey="name" <PieChart
cx="50%" withLabels
cy="50%" withTooltip
innerRadius={60} labelsType="percent"
outerRadius={100} size={250}
label={({ percent }: { percent: number }) => `${(percent * 100).toFixed(0)}%`} data={donutDataJenisKelamin}
labelLine={false} />
> </Center>
{donutDataKelompokUmur.map((entry, index) => ( <Stack gap="sm" mt="md">
<Cell key={`cell-${index}`} fill={entry.color} /> {donutDataJenisKelamin.map((entry) => (
))} <Flex key={entry.name} gap="md" align="center">
</Pie> <Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
</PieChart> <Text size="sm">{entry.name}: {entry.value}</Text>
</Center> </Flex>
<Stack gap="sm" mt="md"> ))}
{donutDataKelompokUmur.map((entry) => ( </Stack>
<Flex key={entry.name} gap="md" align="center"> </Box>
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} /> )}
<Text size="sm">{entry.label}: {entry.value || 0}</Text> </Stack>
</Flex> </Paper>
))}
</Stack> {/* Chart Rating */}
</Box> <Paper bg={colors['white-1']} p="md" radius="md">
)} <Stack>
</Stack> <Title order={4}>Pilihan</Title>
</Paper> {donutDataRating.every(item => item.value === 0) ? (
</Box> <Text c="dimmed" ta="center" my="md">
Belum ada data untuk ditampilkan dalam grafik
</Text>
) : (
<Box>
<Center>
<PieChart
withLabels
withTooltip
labelsType="percent"
size={250}
data={donutDataRating}
/>
</Center>
<Stack gap="sm" mt="md">
{donutDataRating.map((entry) => (
<Flex key={entry.name} gap="md" align="center">
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
<Text size="sm">{entry.name}: {entry.value}</Text>
</Flex>
))}
</Stack>
</Box>
)}
</Stack>
</Paper>
{/* Chart Kelompok Umur */}
<Paper bg={colors['white-1']} p="md" radius="md">
<Stack>
<Title order={4}>Umur</Title>
{donutDataKelompokUmur.every(item => item.value === 0) ? (
<Text c="dimmed" ta="center" my="md">
Belum ada data untuk ditampilkan dalam grafik
</Text>
) : (
<Box>
<Center>
<PieChart
withLabels
withTooltip
labelsType="percent"
size={250}
data={donutDataKelompokUmur}
/>
</Center>
<Stack gap="sm" mt="md">
{donutDataKelompokUmur.map((entry) => (
<Flex key={entry.name} gap="md" align="center">
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
<Text size="sm">{entry.name}: {entry.value}</Text>
</Flex>
))}
</Stack>
</Box>
)}
</Stack>
</Paper>
</SimpleGrid>
</Stack> </Stack>
); );
} }