Sinkronisasi UI & API Admin - User Submenu Jumlah Pengangguran - Jumlah Penduduk Miskin

This commit is contained in:
2025-08-21 17:15:06 +08:00
parent fafbb12a08
commit 20d4c90e60
15 changed files with 431 additions and 407 deletions

View File

@@ -1550,7 +1550,7 @@ model DataDemografiPekerjaan {
model DetailDataPengangguran {
id String @id @default(uuid()) @db.Uuid
month String @db.VarChar(20)
year Int
year DateTime
totalUnemployment Int
educatedUnemployment Int
uneducatedUnemployment Int

View File

@@ -35,7 +35,7 @@ function EditJumlahPendudukMiskin() {
// Set the ID before submitting
stateJPM.update.id = id;
await stateJPM.update.submit();
router.push('/admin/ekonomi/jumlah-penduduk-miskin-2024-2025')
router.push('/admin/ekonomi/jumlah-penduduk-miskin')
}
return (
<Box>

View File

@@ -32,7 +32,7 @@ function CreateJumlahPendudukMiskin() {
}
}
resetForm();
router.push("/admin/ekonomi/jumlah-penduduk-miskin-2024-2025");
router.push("/admin/ekonomi/jumlah-penduduk-miskin");
}
return (
<Box>

View File

@@ -91,7 +91,7 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
<Paper bg={colors['white-1']} p={'md'}>
<JudulList
title='List Jumlah Penduduk Miskin'
href='/admin/ekonomi/jumlah-penduduk-miskin-2024-2025/create'
href='/admin/ekonomi/jumlah-penduduk-miskin/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
@@ -108,7 +108,7 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
<TableTd>{item.year}</TableTd>
<TableTd>{item.totalPoorPopulation}</TableTd>
<TableTd>
<Button color='green' onClick={() => router.push(`/admin/ekonomi/jumlah-penduduk-miskin-2024-2025/${item.id}`)}>
<Button color='green' onClick={() => router.push(`/admin/ekonomi/jumlah-penduduk-miskin/${item.id}`)}>
<IconEdit size={20} />
</Button>
</TableTd>

View File

@@ -8,29 +8,36 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/judulListTab';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur';
function GrafikBerdasarkanPendidikan() {
const [search, setSearch] = useState("")
return (
<Box>
<Stack gap={"xs"}>
<Title order={3}>Grafik Pengangguran Berdasarkan Pendidikan</Title>
<ListGrafikBerdasarkanPendidikan />
<HeaderSearch
title='Detail Data Pengangguran Berdasarkan Pendidikan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListGrafikBerdasarkanPendidikan search={search}/>
</Stack>
</Box>
);
}
function ListGrafikBerdasarkanPendidikan() {
function ListGrafikBerdasarkanPendidikan({search}: {search: string}) {
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanPendidikan)
const [donutData, setDonutData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const router = useRouter();
const [search, setSearch] = useState("");
const handleDelete = async () => {
@@ -56,11 +63,11 @@ function ListGrafikBerdasarkanPendidikan() {
const D3 = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.D3 || 0), 0);
const S1 = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.S1 || 0), 0);
setDonutData([
{ name: 'SD', value: SD, color: colors['blue-button'], key: 'SD' },
{ name: 'SMP', value: SMP, color: '#10A85AFF', key: 'SMP' },
{ name: 'SMA', value: SMA, color: '#C07B13FF', key: 'SMA' },
{ name: 'D3', value: D3, color: '#1094A8FF', key: 'D3' },
{ name: 'S1', value: S1, color: '#A83610FF', key: 'S1' },
{ name: 'SD', value: SD, color: '#4b6Ef5', key: 'SD' },
{ name: 'SMP', value: SMP, color: '#14b885', key: 'SMP' },
{ name: 'SMA', value: SMA, color: '#E6A03B', key: 'SMA' },
{ name: 'D3', value: D3, color: '#DB524D', key: 'D3' },
{ name: 'S1', value: S1, color: '#1018A8FF', key: 'S1' },
]);
}
}, [stategrafik.findMany.data])
@@ -88,13 +95,9 @@ function ListGrafikBerdasarkanPendidikan() {
<Box>
<Stack gap={"xs"}>
<Paper bg={colors['white-1']} p={"md"}>
<JudulListTab
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
<JudulList
title='List Grafik Pengangguran Berdasarkan Pendidikan'
href='/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>

View File

@@ -8,29 +8,37 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/judulListTab';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur';
function GrafikBerdasarkanUsiaKerjaYangMenganggur() {
const [search, setSearch] = useState("")
return (
<Box>
<Stack gap={"xs"}>
<Title order={3}>Grafik Pengangguran Berdasarkan Usia Kerja</Title>
<ListGrafikBerdasarkanUsiaKerjaYangMenganggur />
<HeaderSearch
title='Detail Data Pengangguran'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListGrafikBerdasarkanUsiaKerjaYangMenganggur search={search} />
</Stack>
</Box>
);
}
function ListGrafikBerdasarkanUsiaKerjaYangMenganggur() {
function ListGrafikBerdasarkanUsiaKerjaYangMenganggur({search}: {search: string}) {
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur)
const [donutData, setDonutData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const router = useRouter();
const [search, setSearch] = useState("");
const handleDelete = async () => {
@@ -85,13 +93,9 @@ function ListGrafikBerdasarkanUsiaKerjaYangMenganggur() {
<Box>
<Stack gap={"xs"}>
<Paper bg={colors['white-1']} p={"md"}>
<JudulListTab
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
<JudulList
title='List Pengangguran Berdasarkan Usia Kerja'
href='/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>

View File

@@ -25,7 +25,7 @@ function DetailJumlahPengangguran() {
stateDetail.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
router.push("/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran")
router.push("/admin/ekonomi/jumlah-pengangguran")
}
}
@@ -86,7 +86,7 @@ function DetailJumlahPengangguran() {
color={"red"}>
<IconX size={20} />
</Button>
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/${stateDetail.findUnique.data?.id}/edit`)} color="green">
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/${stateDetail.findUnique.data?.id}/edit`)} color="green">
<IconEdit size={20} />
</Button>
</Flex>

View File

@@ -90,7 +90,7 @@ function CreateJumlahPengangguran() {
/>
<TextInput
label="Tahun"
type="number"
type="date"
value={stateDetail.create.form.year}
onChange={(e) =>
(stateDetail.create.form.year = Number(e.currentTarget.value))

View File

@@ -8,21 +8,29 @@ import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import { BarChart } from '@mantine/charts';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
import jumlahPengangguranState from '../../_state/ekonomi/jumlah-pengangguran';
import JudulListTab from '../../_com/judulListTab';
function DetailDataPengangguran() {
const [search, setSearch] = useState("")
return (
<Box>
<Stack gap={"xs"}>
<Title order={3}>Detail Data Pengangguran</Title>
<ListDetailDataPengangguran />
<HeaderSearch
title='Detail Data Pengangguran'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListDetailDataPengangguran search={search} />
</Stack>
</Box>
);
}
function ListDetailDataPengangguran() {
function ListDetailDataPengangguran({search}: {search: string}) {
type DetailDataPengangguran = {
id: string;
@@ -37,7 +45,6 @@ function ListDetailDataPengangguran() {
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
const stateDetail = useProxy(jumlahPengangguranState.jumlahPengangguran)
const router = useRouter();
const [search, setSearch] = useState("")
useShallowEffect(() => {
setMounted(true)
@@ -78,13 +85,9 @@ function ListDetailDataPengangguran() {
<Box>
<Stack gap={"md"}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
<JudulList
title='List Detail Data Pengangguran'
href='/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/create'
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
href='/admin/ekonomi/jumlah-pengangguran/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
@@ -102,7 +105,7 @@ function ListDetailDataPengangguran() {
<TableTd>{item.educatedUnemployment}</TableTd>
<TableTd>{item.uneducatedUnemployment}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/${item.id}`)}>
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</TableTd>

View File

@@ -1,119 +0,0 @@
'use client'
import colors from '@/con/colors';
import { Box, CheckIcon, Combobox, ComboboxChevron, ComboboxOption, ComboboxOptions, ComboboxTarget, Group, InputBase, InputPlaceholder, Paper, SimpleGrid, Stack, Text, useCombobox } from '@mantine/core';
import { useState } from 'react';
import BackButton from '../../desa/layanan/_com/BackButto';
import { BarChart } from '@mantine/charts';
const data = [
{
id: 1,
tahun: '2024',
Penduduk: 400000
},
{
id: 2,
tahun: '2025',
Penduduk: 450000
},
]
const tahun = [
'2024',
'2025'
];
function Page() {
const combobox = useCombobox({
onDropdownClose: () => combobox.resetSelectedOption(),
onDropdownOpen: (eventSource) => {
if (eventSource === 'keyboard') {
combobox.selectActiveOption();
} else {
combobox.updateSelectedOptionIndex('active');
}
},
});
const [value, setValue] = useState<string | null>('2024');
const options = tahun.map((item) => (
<ComboboxOption value={item} key={item} active={item === value}>
<Group gap="xs">
{item === value && <CheckIcon size={12} />}
<span>{item}</span>
</Group>
</ComboboxOption>
));
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Box px={{ base: 'md', md: 100 }} >
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
Jumlah Penduduk Miskin Tahun 2024-2025
</Text>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'} justify='center'>
<SimpleGrid
pb={20}
cols={{
base: 1,
md: 2
}}
>
<Paper p={'xl'}>
<Text fz={'h3'}>Tahun: 2024</Text>
<Text fw={"bold"} fz={'h1'}>4,800,000 Orang</Text>
</Paper>
<Paper p={'xl'}>
<Text>Pilih Tahun</Text>
<Combobox
store={combobox}
resetSelectionOnOptionHover
withinPortal={false}
onOptionSubmit={(val) => {
setValue(val);
combobox.updateSelectedOptionIndex('active');
}}
>
<ComboboxTarget targetType="button">
<InputBase
component="button"
type="button"
pointer
rightSection={<ComboboxChevron />}
rightSectionPointerEvents="none"
onClick={() => combobox.toggleDropdown()}
>
{value || <InputPlaceholder>Pick value</InputPlaceholder>}
</InputBase>
</ComboboxTarget>
<Combobox.Dropdown>
<ComboboxOptions>{options}</ComboboxOptions>
</Combobox.Dropdown>
</Combobox>
</Paper>
</SimpleGrid>
<Paper p={'xl'}>
<Text pb={10} fw={'bold'} fz={'h4'}>Jumlah Penduduk Miskin Per Tahun</Text>
<BarChart
p={10}
h={300}
data={data}
dataKey="tahun"
series={[
{ name: 'Penduduk', color: '#8785D3' },
]}
tickLine="y"
/>
</Paper>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -0,0 +1,82 @@
'use client'
import jumlahPendudukMiskin from '@/app/admin/(dashboard)/_state/ekonomi/jumlah-penduduk-miskin';
import colors from '@/con/colors';
import { BarChart } from '@mantine/charts';
import { Box, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
function Page() {
type JPMGrafik = {
id: string;
year: number;
totalPoorPopulation: number;
}
const state = useProxy(jumlahPendudukMiskin)
const [chartData, setChartData] = useState<JPMGrafik[]>([])
useShallowEffect(() => {
state.findMany.load()
}, [])
useEffect(() => {
if (state.findMany.data) {
setChartData(state.findMany.data.map((item) => ({
id: item.id,
year: Number(item.year),
totalPoorPopulation: Number(item.totalPoorPopulation),
})));
}
}, [state.findMany.data]);
if (!state.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 }}>
<BackButton />
</Box>
<Box px={{ base: 'md', md: 100 }} >
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
Jumlah Penduduk Miskin
</Text>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'} justify='center'>
<Paper p={'xl'}>
<Text fz={'h3'}>Jumlah Data Penduduk Miskin</Text>
<Text fw={"bold"} fz={'h1'}>
{state.findMany.data?.reduce((sum, item) => sum + (Number(item.totalPoorPopulation) || 0), 0).toLocaleString()} Orang
</Text>
</Paper>
<Paper p={'xl'}>
<Text pb={10} fw={'bold'} fz={'h4'}>Jumlah Penduduk Miskin Per Tahun</Text>
<BarChart
h={300}
data={chartData}
dataKey="year"
series={[
{ name: 'totalPoorPopulation', color: 'blue.6', label: 'Jumlah Penduduk Miskin' },
]}
xAxisLabel="Tahun"
yAxisLabel="Jumlah Penduduk"
/>
</Paper>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,62 +1,76 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import grafikNganggur from '@/app/admin/(dashboard)/_state/ekonomi/usia-kerja-nganggur';
import colors from '@/con/colors';
import { Stack, Box, Text, Center, Paper, ColorSwatch, Flex } from '@mantine/core';
import React from 'react';
import BackButton from '../../desa/layanan/_com/BackButto';
import { PieChart } from '@mantine/charts';
import { Box, Center, ColorSwatch, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
const datausiaKerja = [
{
id: 1,
name: '18 - 25',
value: 45,
color: 'indigo.6'
},
{
id: 2,
name: '26 - 35',
value: 35,
color: 'teal.6'
},
{
id: 3,
name: '36 - 45',
value: 15,
color: 'yellow.6'
},
{
id: 4,
name: '46+',
value: 5,
color: 'red.6'
},
]
const datakerjaPendidikan = [
{
id: 1,
name: 'SD',
value: 10,
color: 'indigo.6'
},
{
id: 2,
name: 'SMP',
value: 20,
color: 'teal.6'
},
{
id: 3,
name: 'SMA/SMK',
value: 45,
color: 'yellow.6'
},
{
id: 4,
name: 'D3/S1',
value: 25,
color: 'red.6'
},
]
function Page() {
const stateGrafikNganggur = useProxy(grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur)
const stateGrafikNganggurPendidikan = useProxy(grafikNganggur.grafikBerdasarkanPendidikan)
const [donutGrafikNganggurData, setDonutGrafikNganggurData] = useState<any[]>([])
const [donutGrafikNganggurDataPendidikan, setDonutGrafikNganggurDataPendidikan] = useState<any[]>([])
const [mounted, setMounted] = useState(false)
const [mounted2, setMounted2] = useState(false)
useShallowEffect(() => {
setMounted(true)
setMounted2(true)
stateGrafikNganggur.findMany.load()
stateGrafikNganggurPendidikan.findMany.load()
}, [])
useEffect(() => {
if (stateGrafikNganggur.findMany.data) {
const totalUsia18_25 = stateGrafikNganggur.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia18_25 || 0), 0);
const totalUsia26_35 = stateGrafikNganggur.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia26_35 || 0), 0);
const totalUsia36_45 = stateGrafikNganggur.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia36_45 || 0), 0);
const totalUsia46_keatas = stateGrafikNganggur.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia46_keatas || 0), 0);
setDonutGrafikNganggurData([
{ name: 'usia 18 - 25', value: totalUsia18_25, color: '#4b6Ef5', key: 'usia18_25' },
{ name: 'usia 26 - 35', value: totalUsia26_35, color: '#14b885', key: 'usia26_35' },
{ name: 'usia 36 - 45', value: totalUsia36_45, color: '#E6A03B', key: 'usia36_45' },
{ name: 'usia 46 +', value: totalUsia46_keatas, color: '#DB524D', key: 'usia46_keatas' },
])
}
}, [stateGrafikNganggur.findMany.data])
useEffect(() => {
if (stateGrafikNganggurPendidikan.findMany.data) {
const SD = stateGrafikNganggurPendidikan.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.SD || 0), 0);
const SMP = stateGrafikNganggurPendidikan.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.SMP || 0), 0);
const SMA = stateGrafikNganggurPendidikan.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.SMA || 0), 0);
const D3 = stateGrafikNganggurPendidikan.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.D3 || 0), 0);
const S1 = stateGrafikNganggurPendidikan.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.S1 || 0), 0);
setDonutGrafikNganggurDataPendidikan([
{ name: 'SD', value: SD, color: '#4b6Ef5', key: 'SD' },
{ name: 'SMP', value: SMP, color: '#14b885', key: 'SMP' },
{ name: 'SMA', value: SMA, color: '#E6A03B', key: 'SMA' },
{ name: 'D3', value: D3, color: '#DB524D', key: 'D3' },
{ name: 'S1', value: S1, color: '#1018A8FF', key: 'S1' },
]);
}
}, [stateGrafikNganggurPendidikan.findMany.data])
if (!stateGrafikNganggur.findMany.data) {
return (
<Box>
<Skeleton h={500} />
</Box>
)
}
if (!stateGrafikNganggur.findMany.data) {
return (
<Box>
<Skeleton h={500} />
</Box>
)
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
@@ -71,9 +85,18 @@ function Page() {
<Stack gap={'lg'} justify='center'>
<Paper p={'lg'}>
<Text fw={'bold'} fz={'h3'}>Pengangguran Berdasarkan Usia</Text>
<Center>
<PieChart size={300} withLabelsLine labelsPosition="outside" labelsType="percent" withLabels data={datausiaKerja} withTooltip tooltipDataSource="segment" mx="auto" />
</Center>
{mounted && donutGrafikNganggurData.length > 0 ? (<Box style={{ width: '100%', height: 'auto', minHeight: 200 }}>
<PieChart
size={300}
withLabelsLine
labelsPosition="outside"
labelsType="percent"
withLabels
data={donutGrafikNganggurData}
withTooltip
tooltipDataSource="segment"
mx="auto" />
</Box>) : <Skeleton h={500} />}
<Flex pb={30} justify={'center'} gap={'xl'} align={'center'}>
<Box>
<Flex gap={{ base: 5, md: 8 }} align={'center'}>
@@ -103,9 +126,18 @@ function Page() {
</Paper>
<Paper p={'lg'}>
<Text fw={'bold'} fz={'h3'}>Pengangguran Berdasarkan Pendidikan</Text>
<Center>
<PieChart size={300} withLabelsLine labelsPosition="outside" labelsType="percent" withLabels data={datakerjaPendidikan} withTooltip tooltipDataSource="segment" mx="auto" />
</Center>
{mounted2 && donutGrafikNganggurDataPendidikan.length > 0 ? (<Center>
<PieChart
size={300}
withLabelsLine
labelsPosition="outside"
labelsType="percent"
withLabels
data={donutGrafikNganggurDataPendidikan}
withTooltip
tooltipDataSource="segment"
mx="auto" />
</Center>) : <Skeleton h={500} />}
<Flex pb={30} justify={'center'} gap={'xl'} align={'center'}>
<Box>
<Flex gap={{ base: 5, md: 8 }} align={'center'}>
@@ -127,10 +159,16 @@ function Page() {
</Box>
<Box>
<Flex gap={{ base: 5, md: 8 }} align={'center'}>
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>D3/S1</Text>
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>D3</Text>
<ColorSwatch color="#DB524D" size={30} />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 5, md: 8 }} align={'center'}>
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>S1</Text>
<ColorSwatch color="#1018A8FF" size={30} />
</Flex>
</Box>
</Flex>
</Paper>
</Stack>

View File

@@ -1,177 +0,0 @@
import colors from '@/con/colors';
import { BarChart } from '@mantine/charts';
import { Box, Button, Center, ColorSwatch, Flex, Group, Paper, SimpleGrid, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconChevronDown, IconDownload, IconSchool, IconSchoolOff, IconUserOff } from '@tabler/icons-react';
import BackButton from '../../desa/layanan/_com/BackButto';
const data1 = [
{
id: 1,
icon: <IconUserOff size={35} />,
judul: 'Total Pengangguran',
jumlah: '140',
persentase: <Text fz={'h4'} c={'green'}>-12.5% dari 2024</Text>
},
{
id: 2,
icon: <IconSchool size={35} />,
judul: 'Pengangguran Terdidik',
jumlah: '80',
persentase: <Text fz={'h4'} c={'gray'}>57.1% dari total</Text>
},
{
id: 3,
icon: <IconSchoolOff size={35} />,
judul: 'Pengangguran Tidak Terdidik',
jumlah: '60',
persentase: <Text fz={'h4'} c={'gray'}>42.9% dari total</Text>
},
]
const dataPengangguran = [
{
id: 1,
bulan: 'Jan',
berpendidikan: 98,
takberpendidikan: 74,
},
{
id: 2,
bulan: 'Feb',
berpendidikan: 85,
takberpendidikan: 74,
},
{
id: 3,
bulan: 'Mar',
berpendidikan: 76,
takberpendidikan: 55,
},
{
id: 4,
bulan: 'Apr',
berpendidikan: 98,
takberpendidikan: 74,
},
{
id: 5,
bulan: 'Mei',
berpendidikan: 74,
takberpendidikan: 54,
},
{
id: 6,
bulan: 'Jun',
berpendidikan: 55,
takberpendidikan: 50,
},
]
const dataTable = [
{ bulan: 'Jan', total: 160, terdidik: 95, takterdidik: 65, perubahan: '-' },
{ bulan: 'Feb', total: 155, terdidik: 90, takterdidik: 65, perubahan: '-3.1%' },
{ bulan: 'Mar', total: 150, terdidik: 88, takterdidik: 62, perubahan: '-3.2%' },
{ bulan: 'Apr', total: 148, terdidik: 85, takterdidik: 63, perubahan: '-1.3%' },
{ bulan: 'Mei', total: 145, terdidik: 82, takterdidik: 63, perubahan: '-2.0%' },
{ bulan: 'Jun', total: 140, terdidik: 80, takterdidik: 60, perubahan: '-3.4%' },
]
function Page() {
const rows = dataTable.map((element) => (
<TableTr key={element.bulan}>
<TableTd ta={'center'}>{element.bulan}</TableTd>
<TableTd ta={'center'}>{element.total}</TableTd>
<TableTd ta={'center'}>{element.terdidik}</TableTd>
<TableTd ta={'center'}>{element.takterdidik}</TableTd>
<TableTd ta={'center'}>{element.perubahan}</TableTd>
</TableTr>
));
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Box px={{ base: 'md', md: 100 }} >
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
Jumlah Pengangguran 2024 - 2025
</Text>
<Group py={20} align='center' justify='space-between'>
<Text fz={'h4'} fw={"bold"}>DATA PENGANGGURAN DESA</Text>
<Flex gap={'xl'}>
<Button c={'black'} bg={colors['white-1']} rightSection={<IconChevronDown size={20} />}>2025</Button>
<Button leftSection={<IconDownload size={20} />}>Export</Button>
</Flex>
</Group>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'} justify='center'>
<SimpleGrid
cols={1}
pb={20}
>
{data1.map((v, k) => {
return (
<Paper px={25} key={k} py={'lg'} bg={colors['white-1']} shadow={'md'}>
<Flex justify={'space-between'} align={'center'}>
<Box>
<Stack>
<Text fz={'h4'}>{v.judul}</Text>
<Text fz={'h2'} fw={'bold'}>{v.jumlah}</Text>
{v.persentase}
</Stack>
</Box>
{v.icon}
</Flex>
</Paper>
)
})}
</SimpleGrid>
<Paper p={'lg'}>
<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' }}>Pengangguran Berpendidikan</Text>
<ColorSwatch color="#5082EE" size={30} />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 0, md: 5 }} align={'center'}>
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Pengangguran Tak Berpendidikan</Text>
<ColorSwatch color="#DA524C" size={30} />
</Flex>
</Box>
</Flex>
<Center>
<BarChart
p={10}
h={400}
data={dataPengangguran}
dataKey="bulan"
series={[
{ name: 'berpendidikan', color: '#5082EE' },
{ name: 'takberpendidikan', color: '#DA524C' },
]}
tickLine="y"
/>
</Center>
</Paper>
<Paper p={'lg'}>
<Text fw={'bold'} fz={'h4'}>Detail Data Pengangguran</Text>
<Table striped highlightOnHover>
<TableThead>
<TableTr>
<TableTh ta={'center'}>Bulan</TableTh>
<TableTh ta={'center'}>Total</TableTh>
<TableTh ta={'center'}>Terdidik</TableTh>
<TableTh ta={'center'}>Tidak Terdidik</TableTh>
<TableTh ta={'center'}>Perubahan</TableTh>
</TableTr>
</TableThead>
<TableTbody>{rows}</TableTbody>
</Table>
</Paper>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -0,0 +1,190 @@
'use client'
import jumlahPengangguranState from '@/app/admin/(dashboard)/_state/ekonomi/jumlah-pengangguran';
import colors from '@/con/colors';
import { BarChart } from '@mantine/charts';
import { Box, ColorSwatch, Flex, Group, Paper, SimpleGrid, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconSchool, IconSchoolOff, IconUserOff } from '@tabler/icons-react';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
type DataPengangguran = {
id: string;
bulan: string;
berpendidikan: number;
takberpendidikan: number;
}
function Page() {
const [chartData, setChartData] = useState<DataPengangguran[]>([])
const [mounted, setMounted] = useState(false)
const state = useProxy(jumlahPengangguranState.jumlahPengangguran)
// const [selectedYear, setSelectedYear] = useState<string | null>(null);
useShallowEffect(() => {
setMounted(true)
state.findMany.load()
}, [])
useEffect(() => {
setMounted(true);
if (state.findMany.data) {
setChartData(state.findMany.data.map((item) => ({
id: item.id,
bulan: item.month,
berpendidikan: Number(item.educatedUnemployment),
takberpendidikan: Number(item.uneducatedUnemployment),
})));
}
}, [state.findMany.data]);
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Box px={{ base: 'md', md: 100 }} >
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
Jumlah Pengangguran
</Text>
<Group py={20} align='center' justify='space-between'>
<Text fz={'h4'} fw={"bold"}>DATA PENGANGGURAN DESA</Text>
{/* <Flex gap={'xl'}>
<Select
value={selectedYear || state.findMany.data?.[0]?.year.toString()}
placeholder="Pilih Tahun"
data={Array.from(new Set(state.findMany.data?.map(item => item.year.toString()) || []))}
onChange={setSelectedYear}
/>
</Flex> */}
</Group>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'} justify='center'>
<SimpleGrid
cols={1}
pb={20}
>
<SimpleGrid cols={{ base: 1, md: 3 }} spacing="md">
{/* Total Unemployment Card */}
<Paper px={25} py={'lg'} bg={colors['white-1']} shadow="md">
<Flex direction="column" gap="md">
<IconUserOff size={35} color={colors['blue-button']} />
<Text fz="h4" fw={600}>Total Pengangguran</Text>
<Text fz="h2" fw={700} c={colors['blue-button']}>
{state.findMany.data?.[0]?.totalUnemployment || 0} Orang
</Text>
<Text fz="sm" c="dimmed">
Data tahun {state.findMany.data?.[0]?.year || ''}
</Text>
</Flex>
</Paper>
{/* Educated Unemployment Card */}
<Paper px={25} py={'lg'} bg={colors['white-1']} shadow="md">
<Flex direction="column" gap="md">
<IconSchool size={35} color="#5082EE" />
<Text fz="h4" fw={600}>Pengangguran Terdidik</Text>
<Text fz="h2" fw={700} c="#5082EE">
{state.findMany.data?.[0]?.educatedUnemployment || 0} Orang
</Text>
<Text fz="sm" c="dimmed">
{state.findMany.data?.[0]?.totalUnemployment ?
`${Math.round((state.findMany.data[0].educatedUnemployment / state.findMany.data[0].totalUnemployment) * 100)}% dari total` :
'0% dari total'}
</Text>
</Flex>
</Paper>
{/* Uneducated Unemployment Card */}
<Paper px={25} py={'lg'} bg={colors['white-1']} shadow="md">
<Flex direction="column" gap="md">
<IconSchoolOff size={35} color="#DA524C" />
<Text fz="h4" fw={600}>Pengangguran Tidak Terdidik</Text>
<Text fz="h2" fw={700} c="#DA524C">
{state.findMany.data?.[0]?.uneducatedUnemployment || 0} Orang
</Text>
<Text fz="sm" c="dimmed">
{state.findMany.data?.[0]?.totalUnemployment ?
`${Math.round((state.findMany.data[0].uneducatedUnemployment / state.findMany.data[0].totalUnemployment) * 100)}% dari total` :
'0% dari total'}
</Text>
</Flex>
</Paper>
</SimpleGrid>
</SimpleGrid>
<Paper p={'lg'}>
<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' }}>Pengangguran Berpendidikan</Text>
<ColorSwatch color="#5082EE" size={30} />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 0, md: 5 }} align={'center'}>
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Pengangguran Tak Berpendidikan</Text>
<ColorSwatch color="#DA524C" size={30} />
</Flex>
</Box>
</Flex>
{!mounted || chartData.length === 0 ? (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={3}>Data Pengangguran Terdidik dan Tidak Terdidik</Title>
<Text c='dimmed'>Belum ada data untuk ditampilkan dalam grafik</Text>
</Paper>
</Box>
) : (
<Box style={{ width: '100%', minWidth: 300, height: 550, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={4}>Data Pengangguran Terdidik dan Tidak Terdidik</Title>
<Box w={{ base: '100%', md: '70%' }}>
<BarChart
h={450}
data={chartData}
dataKey="bulan"
series={[
{ name: 'berpendidikan', color: '#5082EE', label: 'Terdidik' },
{ name: 'takberpendidikan', color: '#DA524C', label: 'Tidak Terdidik' },
]}
/>
</Box>
</Paper>
</Box>
)}
</Paper>
<Paper p={'lg'}>
<Text fw={'bold'} fz={'h4'}>Detail Data Pengangguran</Text>
<Table striped highlightOnHover>
<TableThead>
<TableTr>
<TableTh ta={'center'}>Bulan</TableTh>
<TableTh ta={'center'}>Total</TableTh>
<TableTh ta={'center'}>Terdidik</TableTh>
<TableTh ta={'center'}>Tidak Terdidik</TableTh>
<TableTh ta={'center'}>Perubahan</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{state.findMany.data?.map((item, index) => (
<TableTr key={item?.id ? String(item.id) : `row-${index}`}>
<TableTd ta={'center'}>{item.month}</TableTd>
<TableTd ta={'center'}>{item.totalUnemployment}</TableTd>
<TableTd ta={'center'}>{item.educatedUnemployment}</TableTd>
<TableTd ta={'center'}>{item.uneducatedUnemployment}</TableTd>
<TableTd ta={'center'}>{item.percentageChange}</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -197,8 +197,8 @@ const navbarListMenu = [
},
{
id: "5.5",
name: "Jumlah Pengangguran 2024-2025",
href: "/darmasaba/ekonomi/jumlah-pengangguran-2024-2025"
name: "Jumlah Pengangguran",
href: "/darmasaba/ekonomi/jumlah-pengangguran"
},
{
id: "5.6",
@@ -207,8 +207,8 @@ const navbarListMenu = [
},
{
id: "5.7",
name: "Jumlah Penduduk Miskin 2024-2025",
href: "/darmasaba/ekonomi/jumlah-penduduk-miskin-2024-2025"
name: "Jumlah Penduduk Miskin",
href: "/darmasaba/ekonomi/jumlah-penduduk-miskin"
},
{
id: "5.8",