API & UI Menu Ekonomi, Submenu Jumlah Pengangguran

This commit is contained in:
2025-07-10 00:21:33 +08:00
parent d328f64d86
commit 7b2b306849
55 changed files with 205 additions and 1316 deletions

View File

@@ -15,6 +15,8 @@ type JudulListTabProps = {
}
const JudulListTab = ({
title = "",
href = "#",

View File

@@ -53,7 +53,7 @@ const jumlahPengangguran = proxy({
console.error("Gagal ambil data bulan/tahun:", err);
} finally {
jumlahPengangguran.findByMonthYear.loading = false;
}
}
},
},
create: {
@@ -236,423 +236,8 @@ const jumlahPengangguran = proxy({
},
});
const templateRingkasanData = z.object({
year: z.number().min(1, "Tahun harus diisi"),
totalUnemployment: z.number().min(1, "Total pengangguran harus diisi"),
educatedUnemployment: z
.number()
.min(1, "Pengangguran pendidikan harus diisi"),
percentageEducatedOfTotal: z
.number()
.min(1, "Persentase pendidikan harus diisi"),
productiveAgePopulation: z.number().min(1, "Populasi produktif harus diisi"),
percentageProductiveOfTotal: z
.number()
.min(1, "Persentase produktif harus diisi"),
percentageChangeFromPreviousYear: z
.number()
.min(1, "Persentase perubahan harus diisi"),
});
type RingkasanData = {
year: number;
totalUnemployment: number;
educatedUnemployment: number;
percentageEducatedOfTotal: number;
productiveAgePopulation: number;
percentageProductiveOfTotal: number;
percentageChangeFromPreviousYear: number;
};
const ringkasanDataForm: RingkasanData = {
year: 0,
totalUnemployment: 0,
educatedUnemployment: 0,
percentageEducatedOfTotal: 0,
productiveAgePopulation: 0,
percentageProductiveOfTotal: 0,
percentageChangeFromPreviousYear: 0,
};
const ringkasanData = proxy({
create: {
form: ringkasanDataForm,
loading: false,
async create() {
const cek = templateRingkasanData.safeParse(ringkasanData.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return null;
}
try {
ringkasanData.create.loading = true;
const res =
await ApiFetch.api.ekonomi.jumlahpengangguran.ringkasandatapengangguran[
"create"
].post(ringkasanData.create.form);
if (res.status === 200) {
const id = res.data?.data?.id;
if (id) {
toast.success("Success create");
ringkasanData.create.form = { ...ringkasanDataForm };
ringkasanData.findMany.load();
return id;
}
}
toast.error("failed create");
return null;
} catch (error) {
console.log((error as Error).message);
return null;
} finally {
ringkasanData.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.RingkasanDataPengangguranGetPayload<{
omit: { isActive: true };
}>[]
| null,
async load() {
const res =
await ApiFetch.api.ekonomi.jumlahpengangguran.ringkasandatapengangguran[
"find-many"
].get();
if (res.status === 200) {
ringkasanData.findMany.data = res.data?.data ?? [];
}
},
},
findUnique: {
data: null as Prisma.RingkasanDataPengangguranGetPayload<{
omit: { isActive: true };
}> | null,
async load(id: string) {
try {
const res = await fetch(
`/api/ekonomi/jumlahpengangguran/ringkasandatapengangguran/${id}`
);
if (res.ok) {
const data = await res.json();
ringkasanData.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch ringkasanData:", res.statusText);
ringkasanData.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching ringkasanData:", error);
ringkasanData.findUnique.data = null;
}
},
},
update: {
id: "",
form: { ...ringkasanDataForm },
loading: false,
async submit() {
const id = this.id;
if (!id) {
toast.warn("ID tidak valid");
return null;
}
const formData = {
year: this.form.year,
totalUnemployment: this.form.totalUnemployment,
educatedUnemployment: this.form.educatedUnemployment,
percentageEducatedOfTotal: this.form.percentageEducatedOfTotal,
productiveAgePopulation: this.form.productiveAgePopulation,
percentageProductiveOfTotal: this.form.percentageProductiveOfTotal,
percentageChangeFromPreviousYear:
this.form.percentageChangeFromPreviousYear,
};
const cek = templateJumlahPengngguran.safeParse(formData);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return null;
}
try {
this.loading = true;
const res = await fetch(
`/api/ekonomi/jumlahpengangguran/ringkasandatapengangguran/${id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
}
);
const result = await res.json();
if (!res.ok || !result?.success) {
throw new Error(result?.message || "Gagal update data");
}
toast.success("Berhasil update data!");
await ringkasanData.findMany.load();
return result.data;
} catch (error) {
console.error("Update error:", error);
toast.error("Gagal update data ringkasan data pengangguran");
throw error;
} finally {
this.loading = false;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
ringkasanData.delete.loading = true;
const response = await fetch(
`/api/ekonomi/jumlahpengangguran/ringkasandatapengangguran/del/${id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
const result = await response.json();
if (response.ok && result?.success) {
toast.success(
result.message || "Ringkasan data pengangguran berhasil dihapus"
);
await ringkasanData.findMany.load();
} else {
toast.error(
result?.message || "Gagal menghapus ringkasan data pengangguran"
);
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error(
"Terjadi kesalahan saat menghapus ringkasan data pengangguran"
);
} finally {
ringkasanData.delete.loading = false;
}
},
},
});
const templateSedangMencariKerja = z.object({
count: z.number().min(1, "Jumlah harus diisi"),
percentageOfTotal: z.number().min(1, "Persentase harus diisi"),
recordedDate: z.string().min(1, "Tanggal harus diisi"),
});
type SedangMencariKerja = {
count: number;
percentageOfTotal: number;
recordedDate: string;
};
const sedangMencariKerjaForm: SedangMencariKerja = {
count: 0,
percentageOfTotal: 0,
recordedDate: "",
};
const sedangMencariKerja = proxy({
create: {
form: sedangMencariKerjaForm,
loading: false,
async create() {
const cek = templateSedangMencariKerja.safeParse(
sedangMencariKerja.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return null;
}
try {
sedangMencariKerja.create.loading = true;
const res =
await ApiFetch.api.ekonomi.jumlahpengangguran.sedangmencarikerja[
"create"
].post(sedangMencariKerja.create.form);
if (res.status === 200) {
const id = res.data?.data?.id;
if (id) {
toast.success("Success create");
sedangMencariKerja.create.form = { ...sedangMencariKerjaForm };
sedangMencariKerja.findMany.load();
return id;
}
}
toast.error("failed create");
return null;
} catch (error) {
console.log((error as Error).message);
return null;
} finally {
sedangMencariKerja.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.SedangMencariKerjaGetPayload<{
omit: { isActive: true };
}>[]
| null,
async load() {
const res =
await ApiFetch.api.ekonomi.jumlahpengangguran.sedangmencarikerja[
"find-many"
].get();
if (res.status === 200) {
sedangMencariKerja.findMany.data = res.data?.data ?? [];
}
},
},
findUnique: {
data: null as Prisma.SedangMencariKerjaGetPayload<{
omit: { isActive: true };
}> | null,
async load(id: string) {
try {
const res = await fetch(
`/api/ekonomi/jumlahpengangguran/sedangmencarikerja/${id}`
);
if (res.ok) {
const data = await res.json();
sedangMencariKerja.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch sedangMencariKerja:", res.statusText);
sedangMencariKerja.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching sedangMencariKerja:", error);
sedangMencariKerja.findUnique.data = null;
}
},
},
update: {
id: "",
form: { ...sedangMencariKerjaForm },
loading: false,
async submit() {
const id = this.id;
if (!id) {
toast.warn("ID tidak valid");
return null;
}
const formData = {
count: this.form.count,
percentageOfTotal: this.form.percentageOfTotal,
recordedDate: this.form.recordedDate,
};
const cek = templateSedangMencariKerja.safeParse(formData);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return null;
}
try {
this.loading = true;
const res = await fetch(
`/api/ekonomi/jumlahpengangguran/sedangmencarikerja/${id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
}
);
const result = await res.json();
if (!res.ok || !result?.success) {
throw new Error(result?.message || "Gagal update data");
}
toast.success("Berhasil update data!");
await sedangMencariKerja.findMany.load();
return result.data;
} catch (error) {
console.error("Update error:", error);
toast.error("Gagal update data sedang mencari kerja");
throw error;
} finally {
this.loading = false;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
sedangMencariKerja.delete.loading = true;
const response = await fetch(
`/api/ekonomi/jumlahpengangguran/sedangmencarikerja/del/${id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
const result = await response.json();
if (response.ok && result?.success) {
toast.success(
result.message || "Sedang mencari kerja berhasil dihapus"
);
await sedangMencariKerja.findMany.load();
} else {
toast.error(
result?.message || "Gagal menghapus sedang mencari kerja"
);
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus sedang mencari kerja");
} finally {
sedangMencariKerja.delete.loading = false;
}
},
},
});
const jumlahPengangguranState = proxy({
jumlahPengangguran,
ringkasanData,
sedangMencariKerja,
});
export default jumlahPengangguranState;

View File

@@ -3,7 +3,7 @@ import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import JudulListTab from '../../../_com/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
import { useProxy } from 'valtio/utils';
import stateGallery from '../../../_state/desa/gallery';
import { useShallowEffect } from '@mantine/hooks';

View File

@@ -3,7 +3,7 @@ import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import JudulListTab from '../../../_com/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
import { useProxy } from 'valtio/utils';
import stateGallery from '../../../_state/desa/gallery';
import { useShallowEffect } from '@mantine/hooks';

View File

@@ -1,5 +1,5 @@
'use client'
import JudulListTab from '@/app/admin/(dashboard)/_com/jusulListTab';
import JudulListTab from '@/app/admin/(dashboard)/_com/judulListTab';
import colors from '@/con/colors';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';

View File

@@ -1,5 +1,5 @@
'use client'
import JudulListTab from '@/app/admin/(dashboard)/_com/jusulListTab';
import JudulListTab from '@/app/admin/(dashboard)/_com/judulListTab';
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';

View File

@@ -6,7 +6,7 @@ import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../_com/jusulListTab';
import JudulListTab from '../../_com/judulListTab';
import { useState } from 'react';
import HeaderSearch from '../../_com/header';

View File

@@ -8,7 +8,7 @@ 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/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur';

View File

@@ -8,7 +8,7 @@ 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/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur';

View File

@@ -1,67 +0,0 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
function LayoutTabs({ children }: { children: React.ReactNode }) {
const router = useRouter()
const pathname = usePathname()
const tabs = [
{
label: "Detail Data Pengangguran",
value: "detaildatapengangguran",
href: "/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran"
},
{
label: "Ringkasan Data Pengangguran",
value: "ringkasandatapengangguran",
href: "/admin/ekonomi/jumlah-pengangguran/ringkasan-data-pengangguran"
},
{
label: "Sedang Mencari Kerja",
value: "sedangmencarikerja",
href: "/admin/ekonomi/jumlah-pengangguran/sedang-mencari-kerja"
}
];
const curentTab = tabs.find(tab => tab.href === pathname)
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
const handleTabChange = (value: string | null) => {
const tab = tabs.find(t => t.value === value)
if (tab) {
router.push(tab.href)
}
setActiveTab(value)
}
useEffect(() => {
const match = tabs.find(tab => tab.href === pathname)
if (match) {
setActiveTab(match.value)
}
}, [pathname])
return (
<Stack>
<Title order={3}>Jumlah Penduduk Usia Kerja yang Menganggur</Title>
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
{tabs.map((e, i) => (
<TabsTab key={i} value={e.value}>{e.label}</TabsTab>
))}
</TabsList>
{tabs.map((e, i) => (
<TabsPanel key={i} value={e.value}>
{/* Konten dummy, bisa diganti tergantung routing */}
<></>
</TabsPanel>
))}
</Tabs>
{children}
</Stack>
);
}
export default LayoutTabs;

View File

@@ -1,87 +0,0 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/jusulListTab';
import jumlahPengangguranState from '../../../_state/ekonomi/jumlah-pengangguran';
function DetailDataPengangguran() {
return (
<Box>
<Stack gap={"xs"}>
<Title order={3}>Detail Data Pengangguran</Title>
<ListDetailDataPengangguran/>
</Stack>
</Box>
);
}
function ListDetailDataPengangguran() {
const stateDetail = useProxy(jumlahPengangguranState.jumlahPengangguran)
const router = useRouter();
const [search, setSearch] = useState("")
useShallowEffect(() => {
stateDetail.findMany.load()
}, [])
const filteredData = (stateDetail.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.month.toLowerCase().includes(keyword) ||
item.year.toString().toLowerCase().includes(keyword)
);
});
if (!stateDetail.findMany.data) {
return (
<Box>
<Skeleton h={500} />
</Box>
)
}
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
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} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Bulan</TableTh>
<TableTh>Terdidik</TableTh>
<TableTh>Tidak Terdidik</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.month}</TableTd>
<TableTd>{item.educatedUnemployment}</TableTd>
<TableTd>{item.uneducatedUnemployment}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
</Box>
);
}
export default DetailDataPengangguran;

View File

@@ -1,9 +0,0 @@
import LayoutTabs from "./_lib/layoutTabs";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<LayoutTabs>
{children}
</LayoutTabs>
);
}

View File

@@ -0,0 +1,148 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import { BarChart } from '@mantine/charts';
import jumlahPengangguranState from '../../_state/ekonomi/jumlah-pengangguran';
import JudulListTab from '../../_com/judulListTab';
function DetailDataPengangguran() {
return (
<Box>
<Stack gap={"xs"}>
<Title order={3}>Detail Data Pengangguran</Title>
<ListDetailDataPengangguran />
</Stack>
</Box>
);
}
function ListDetailDataPengangguran() {
type DetailDataPengangguran = {
id: string;
month: string;
year: number;
educatedUnemployment: number;
uneducatedUnemployment: number;
percentageChange: number;
totalUnemployment: number;
}
const [chartData, setChartData] = useState<DetailDataPengangguran[]>([]);
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)
stateDetail.findMany.load()
}, [])
useEffect(() => {
setMounted(true);
if (stateDetail.findMany.data) {
setChartData(stateDetail.findMany.data.map((item) => ({
id: item.id,
month: item.month,
year: item.year,
educatedUnemployment: Number(item.educatedUnemployment),
uneducatedUnemployment: Number(item.uneducatedUnemployment),
percentageChange: Number(item.percentageChange),
totalUnemployment: Number(item.totalUnemployment),
})));
}
}, [stateDetail.findMany.data]);
const filteredData = (stateDetail.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.month.toLowerCase().includes(keyword) ||
item.year.toString().toLowerCase().includes(keyword)
);
});
if (!stateDetail.findMany.data) {
return (
<Box>
<Skeleton h={500} />
</Box>
)
}
return (
<Box>
<Stack gap={"md"}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
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} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Bulan</TableTh>
<TableTh>Terdidik</TableTh>
<TableTh>Tidak Terdidik</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.month}</TableTd>
<TableTd>{item.educatedUnemployment}</TableTd>
<TableTd>{item.uneducatedUnemployment}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
{/* Chart */}
{!mounted && !chartData ? (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={3}>Data Kelahiran & Kematian</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 Kelahiran & Kematian</Title>
{mounted && chartData.length > 0 && (
<Box w={{ base: '100%', md: '70%' }}>
<BarChart
h={450}
data={chartData}
dataKey="month"
series={[
{ name: 'educatedUnemployment', color: 'red.6', label: 'Terdidik' },
{ name: 'uneducatedUnemployment', color: 'orange.6', label: 'Tidak Terdidik' },
]}
/>
</Box>
)}
</Paper>
</Box>
)}
</Stack>
</Box>
);
}
export default DetailDataPengangguran;

View File

@@ -1,11 +0,0 @@
import React from 'react';
function Page() {
return (
<div>
Page
</div>
);
}
export default Page;

View File

@@ -1,11 +0,0 @@
import React from 'react';
function Page() {
return (
<div>
Page
</div>
);
}
export default Page;

View File

@@ -1,11 +0,0 @@
import React from 'react';
function Page() {
return (
<div>
Page
</div>
);
}
export default Page;

View File

@@ -1,11 +0,0 @@
import React from 'react';
function Page() {
return (
<div>
Page
</div>
);
}
export default Page;

View File

@@ -1,11 +0,0 @@
import React from 'react';
function Page() {
return (
<div>
Page
</div>
);
}
export default Page;

View File

@@ -1,11 +0,0 @@
import React from 'react';
function Page() {
return (
<div>
Page
</div>
);
}
export default Page;

View File

@@ -1,11 +0,0 @@
import React from 'react';
function Page() {
return (
<div>
Page
</div>
);
}
export default Page;

View File

@@ -1,11 +0,0 @@
import React from 'react';
function Page() {
return (
<div>
Page
</div>
);
}
export default Page;

View File

@@ -1,14 +1,14 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Badge, Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
import { useState } from 'react';
function Pegawai() {
const [search, setSearch] = useState("");
@@ -30,7 +30,7 @@ function ListPegawai({ search }: { search: string }) {
const stateOrganisasi = useProxy(strukturorganisasiState.pegawai);
const router = useRouter();
useShallowEffect(() => {
useEffect(() => {
const loadData = async () => {
try {
// Clear existing data to ensure we see the loading state

View File

@@ -1,15 +1,15 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import { useProxy } from 'valtio/utils';
import { useShallowEffect } from '@mantine/hooks';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
import { useState } from 'react';
function PosisiOrganisasi() {
const [search, setSearch] = useState("");
@@ -33,7 +33,7 @@ function ListPosisiOrganisasi({ search }: { search: string }) {
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
useShallowEffect(() => {
useEffect(() => {
stateOrganisasi.findMany.load()
}, [])

View File

@@ -7,7 +7,7 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikkepuasan from '../../../_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
import HeaderSearch from '../../../_com/header';

View File

@@ -8,7 +8,7 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import HeaderSearch from '../../../_com/header';

View File

@@ -3,7 +3,7 @@ import colors from '@/con/colors';
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import JudulListTab from '../../../_com/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
function KeteranganBankSampahTerdekat() {
const router = useRouter();

View File

@@ -3,7 +3,7 @@ import colors from '@/con/colors';
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import JudulListTab from '../../../_com/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
function ListPengelolaanSampahBankSampah() {
const router = useRouter();

View File

@@ -9,7 +9,7 @@ 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/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import HeaderSearch from '../../../_com/header';

View File

@@ -8,7 +8,7 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts';
import { useSnapshot } from 'valtio';
import JudulListTab from '../../../_com/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikBerdasarkanResponden from '../../../_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanResponden';
import HeaderSearch from '../../../_com/header';

View File

@@ -9,7 +9,7 @@ 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/jusulListTab';
import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import HeaderSearch from '../../../_com/header';

View File

@@ -1,5 +1,5 @@
'use client'
import JudulListTab from '@/app/admin/(dashboard)/_com/jusulListTab';
import JudulListTab from '@/app/admin/(dashboard)/_com/judulListTab';
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';

View File

@@ -235,7 +235,7 @@ export const navBar = [
{
id: "Ekonomi_5",
name: "Jumlah Pengangguran",
path: "/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran"
path: "/admin/ekonomi/jumlah-pengangguran"
},
{
id: "Ekonomi_6",