Sinkronisasi UI & API Admin - User Submenu Data Kesehatan Warga

-Dibagian Tanggal Gak Auto Ngambil Tanggal Yang Udah Dipakai
-Dibagian fasilitas kesehatan : data dokter dan tarif rencananya mau pakai select
This commit is contained in:
2025-08-15 14:07:56 +08:00
parent d7a592c635
commit 8d15563f15
31 changed files with 1778 additions and 536 deletions

View File

@@ -0,0 +1,132 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import grafikkepuasan from "@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan";
import colors from "@/con/colors";
import { Box, Center, Paper, Skeleton, Stack, Text, Title } from "@mantine/core";
import { useMediaQuery, useShallowEffect } from "@mantine/hooks";
import { useEffect, useState } from "react";
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from "recharts";
import { useProxy } from "valtio/utils";
function GrafikPenyakit() {
type PDKMGRAFIK = {
id: string;
nama: string;
tanggal: string | Date; // Allow both string and Date types
jenisKelamin: string;
alamat: string;
penyakit: string;
createdAt?: Date; // Add optional fields that might come from the API
updatedAt?: Date;
deletedAt?: Date | null;
}
const statePenyakit = useProxy(grafikkepuasan)
const [chartData, setChartData] = useState<PDKMGRAFIK[]>([])
const [mounted, setMounted] = useState(false)
const isTablet = useMediaQuery('(max-width: 1024px)')
const isMobile = useMediaQuery('(max-width: 768px)')
const {
data,
page,
loading,
load,
} = statePenyakit.findMany
useShallowEffect(() => {
setMounted(true)
load(page, 10)
}, [page])
useEffect(() => {
setMounted(true)
if (data) {
setChartData(data.map((item) => ({
id: item.id,
nama: item.nama,
tanggal: item.tanggal instanceof Date ? item.tanggal.toISOString() : item.tanggal,
jenisKelamin: item.jenisKelamin,
alamat: item.alamat,
penyakit: item.penyakit,
})))
}
}, [data])
const processDiseaseData = (data: PDKMGRAFIK[]) => {
const diseaseCount: Record<string, number> = {};
data.forEach(item => {
const penyakit = item.penyakit.trim();
if (penyakit) {
diseaseCount[penyakit] = (diseaseCount[penyakit] || 0) + 1;
}
});
return Object.entries(diseaseCount).map(([name, count]) => ({
name,
count
}));
};
// Add this state to store the processed chart data
const [diseaseChartData, setDiseaseChartData] = useState<{ name: string, count: number }[]>([]);
// Update the chart data when data changes
useEffect(() => {
if (data && data.length > 0) {
setDiseaseChartData(processDiseaseData(data));
}
}, [data]);
if (loading || !data) {
return (
<Stack>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box>
{!mounted && !chartData ? (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Center>
<Title pb={10} order={3}>Grafik Hasil Kepuasan Masyarakat</Title>
<Text c='dimmed'>Belum ada data untuk ditampilkan dalam grafik</Text>
</Center>
</Paper>
</Box>
) : (
<Box style={{ width: '100%', minWidth: 300, height: 420, minHeight: 300 }}>
<Paper bg={colors["white-trans-1"]} p={'md'}>
<Title pb={10} order={4}>Grafik Hasil Kepuasan Masyarakat</Title>
{mounted && diseaseChartData.length > 0 && (
<Center>
<BarChart width={isMobile ? 450 : isTablet ? 500 : 550} height={350} data={diseaseChartData} >
<XAxis
dataKey="name"
tick={{ fontSize: 12 }}
interval={0}
angle={-45}
textAnchor="end"
height={70}
/>
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="count" fill={colors['blue-button']} name="Jumlah Penderita" />
</BarChart>
</Center>
)}
</Paper>
</Box>
)}
</Box>
);
}
export default GrafikPenyakit;

View File

@@ -1,49 +1,95 @@
'use client'
/* eslint-disable @typescript-eslint/no-explicit-any */
import colors from '@/con/colors';
import { Stack, Box, Text, Paper, Center, Flex, ColorSwatch, SimpleGrid, Anchor, Divider, Image } from '@mantine/core';
import React from 'react';
import { BarChart as MantineBarChart } from '@mantine/charts';
import { Anchor, Box, Center, ColorSwatch, Divider, Flex, Image, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
import { useEffect, useState } from 'react';
import BackButton from '../../desa/layanan/_com/BackButto';
import { BarChart } from '@mantine/charts';
import Link from 'next/link';
// import { useRouter } from 'next/navigation';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
import { useProxy } from 'valtio/utils';
import GrafikPenyakit from './grafik-penyakit/page';
const dataKematian = [
{
id: 1,
tahun: '2023',
kematianKasar: '1.7',
kematianBayi: '1.4',
kelahiranKasar: '0.5'
},
{
id: 2,
tahun: '2024',
kematianKasar: '1.4',
kematianBayi: '1.8',
kelahiranKasar: '1.5'
},
{
id: 3,
tahun: '2025',
kematianKasar: '2.0',
kematianBayi: '1.5',
kelahiranKasar: '1.2'
},
]
const dataPenyakit = [
{ penyakit: 'Covid', penderita: 335 },
{ penyakit: 'Tuli', penderita: 105 },
{ penyakit: 'Bisul', penderita: 98 },
{ penyakit: 'Panas', penderita: 96 },
{ penyakit: 'Batuk', penderita: 87 },
{ penyakit: 'Sembelit', penderita: 72 },
{ penyakit: 'Demam', penderita: 51 },
{ penyakit: 'Gred', penderita: 36 },
{ penyakit: 'Magh', penderita: 34 },
{ penyakit: 'Farangitis Akut', penderita: 17 },
]
function Page() {
type DataTahunan = {
tahun: string;
totalKelahiran: number;
totalKematian: number;
data: Array<{
id: string;
bulan: string;
kelahiran: number;
kematian: number;
}>;
};
// Count occurrences per year
const countByYear = (data: any[], dateField: string) => {
const counts: Record<string, number> = {};
data?.forEach(item => {
const year = new Date(item[dateField]).getFullYear().toString();
counts[year] = (counts[year] || 0) + 1;
});
return counts;
};
const statePersentase = useProxy(persentasekelahiran);
const [chartData, setChartData] = useState<DataTahunan[]>([]);
const isTablet = useMediaQuery('(max-width: 1024px)');
const isMobile = useMediaQuery('(max-width: 768px)');
useShallowEffect(() => {
statePersentase.kelahiran.findMany.load(1, 1000); // Load all kelahiran data
statePersentase.kematian.findMany.load(1, 1000); // Load all kematian data
}, []);
useEffect(() => {
if (statePersentase.kelahiran.findMany.data && statePersentase.kematian.findMany.data) {
// Count kelahiran and kematian by year
const kelahiranByYear = countByYear(statePersentase.kelahiran.findMany.data, 'tanggal');
const kematianByYear = countByYear(statePersentase.kematian.findMany.data, 'tanggal');
// Get all unique years
const allYears = new Set([
...Object.keys(kelahiranByYear),
...Object.keys(kematianByYear)
]);
// Create data structure for the chart
const dataByYear = Array.from(allYears).reduce<Record<string, DataTahunan>>((acc, year) => {
acc[year] = {
tahun: year,
totalKelahiran: kelahiranByYear[year] || 0,
totalKematian: kematianByYear[year] || 0,
data: []
};
return acc;
}, {});
const sortedData = Object.values(dataByYear).sort((a, b) =>
parseInt(a.tahun) - parseInt(b.tahun)
);
setChartData(sortedData);
}
}, [
statePersentase.kelahiran.findMany.data,
statePersentase.kematian.findMany.data,
]);
if (!statePersentase.kelahiran.findMany.data || !statePersentase.kematian.findMany.data) {
return (
<Stack>
<Skeleton h={500} />
</Stack>
);
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
@@ -61,63 +107,50 @@ function Page() {
<Flex pb={30} justify={'flex-end'} gap={'xl'} align={'center'}>
<Box>
<Flex gap={{ base: 0, md: 5 }} align={'center'}>
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Angka Kematian Kasar</Text>
<ColorSwatch color="#26308A" size={30} />
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Angka Kematian</Text>
<ColorSwatch color="#EF3E3E" size={30} />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 0, md: 5 }} align={'center'}>
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Angka Kematian Bayi</Text>
<ColorSwatch color="#135A9B" size={30} />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 0, md: 5 }} align={'center'}>
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Angka Kelahiran Kasar</Text>
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Angka Kelahiran</Text>
<ColorSwatch color="#3290CA" size={30} />
</Flex>
</Box>
</Flex>
<Center>
<BarChart
h={400}
data={dataKematian}
dataKey="tahun"
series={[
{ name: 'kematianKasar', color: '#26308A' },
{ name: 'kematianBayi', color: '#135A9B' },
{ name: 'kelahiranKasar', color: '#3290CA' },
]}
tickLine="y"
/>
</Center>
</Box>
</Paper>
</Box>
{/* Bar Chart Penyakit */}
<Box>
<Paper p={"xl"} bg={colors['white-trans-1']}>
<Box pb={30}>
<Text pb={30} fw={"bold"} fz={{ base: 'h4', md: 'h3' }} ta={"center"}>
Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik
</Text>
<Center>
<BarChart
p={20}
mb={30}
h={500}
data={dataPenyakit}
dataKey='penyakit'
orientation='vertical'
yAxisProps={{ width: 80 }}
barProps={{ radius: 10 }}
series={[{ name: 'penderita', color: colors['blue-button'] }]}
/>
</Center>
<Text ta={"center"} fw={"bold"} fz={"h4"}>Jumlah Penderita</Text>
{chartData.length === 0 ? (
<Text c="dimmed" ta="center" py="xl">
Belum ada data yang tersedia untuk ditampilkan
</Text>
) : (
<>
{/* Main Chart */}
<Center>
<Box h={400}>
<Box style={{
width: isMobile ? '90vw' : isTablet ? '700px' : '800px',
maxWidth: '100%',
margin: '0 auto'
}}>
<MantineBarChart
h={350}
data={chartData}
dataKey="tahun"
series={[
{ name: 'totalKelahiran', label: 'Total Kelahiran', color: '#3290CA' },
{ name: 'totalKematian', label: 'Total Kematian', color: '#f03e3e' }
]}
tickLine="y"
/>
</Box>
</Box>
</Center>
</>
)}
</Box>
</Paper>
</Box>
<GrafikPenyakit />
{/* Artikel Kesehatan */}
<Box>
<SimpleGrid
@@ -193,7 +226,7 @@ function Page() {
</Text>
</Anchor>
</Box>
<Divider color={colors['blue-button']} px={'xl'}/>
<Divider color={colors['blue-button']} px={'xl'} />
</Stack>
</Paper>
</Box>
@@ -233,7 +266,7 @@ function Page() {
09:00-14:00 WITA
</Text>
<Text fz={'h4'}>
Puskesmas Abiansemal III
Puskesmas Abiansemal III
</Text>
<Anchor component={Link} href={'/darmasaba/kesehatan/data-kesehatan-warga/jadwal-kegiatan'} c={colors['blue-button']} variant='transparent'>
<Text c={colors['blue-button']} fz={'h4'} >
@@ -249,16 +282,16 @@ function Page() {
<Paper p={'xl'} h={'112vh'} bg={colors['white-trans-1']}>
<Stack gap={'xs'}>
<Box>
<Text ta={'center'} fw={"bold"} fz={'h3'} c={colors['blue-button']}>Artikel Kesehatan</Text>
<Image pt={5} src={'/api/img/dbd.png'} alt="" />
<Text ta={'center'} fw={"bold"} fz={'h3'} c={colors['blue-button']}>Artikel Kesehatan</Text>
<Image pt={5} src={'/api/img/dbd.png'} alt="" />
<Text fz={'h4'} fw={'bold'} >
Tips Mencegah Demam Berdarah Saat Musim Hujan
Tips Mencegah Demam Berdarah Saat Musim Hujan
</Text>
<Text fz={'h6'} pb={10}>
Diposting: 12 Februari 2025 | Dinas Kesehatan
Diposting: 12 Februari 2025 | Dinas Kesehatan
</Text>
<Text fz={'h4'} pb={10}>
Yuk Kenali gelaja dan cara penanganan DBD yang efektif untuk melindungi keluarga anda selama musim hujan.
Yuk Kenali gelaja dan cara penanganan DBD yang efektif untuk melindungi keluarga anda selama musim hujan.
</Text>
<Anchor c={'black'} component={Link} href={'/darmasaba/kesehatan/data-kesehatan-warga/artikel-kesehatan'} variant='transparent'>
<Text c={colors['blue-button']} fz={'h4'} >
@@ -268,16 +301,16 @@ function Page() {
</Box>
<Divider color={colors['blue-button']} px={'xl'} />
<Box>
<Text ta={'center'} fw={"bold"} fz={'h3'} c={colors['blue-button']}>Artikel Kesehatan</Text>
<Image pt={5} src={'/api/img/dbd.png'} alt="" />
<Text ta={'center'} fw={"bold"} fz={'h3'} c={colors['blue-button']}>Artikel Kesehatan</Text>
<Image pt={5} src={'/api/img/dbd.png'} alt="" />
<Text fz={'h4'} fw={'bold'} >
Tips Mencegah Demam Berdarah Saat Musim Hujan
Tips Mencegah Demam Berdarah Saat Musim Hujan
</Text>
<Text fz={'h6'} pb={10}>
Diposting: 12 Februari 2025 | Dinas Kesehatan
Diposting: 12 Februari 2025 | Dinas Kesehatan
</Text>
<Text fz={'h4'} pb={10}>
Yuk Kenali gelaja dan cara penanganan DBD yang efektif untuk melindungi keluarga anda selama musim hujan.
Yuk Kenali gelaja dan cara penanganan DBD yang efektif untuk melindungi keluarga anda selama musim hujan.
</Text>
<Anchor c={'black'} href={'/darmasaba/kesehatan/data-kesehatan-warga/artikel-kesehatan'} variant='transparent'>
<Text c={colors['blue-button']} fz={'h4'} >