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

@@ -1,12 +0,0 @@
[
{
"year": 2025,
"totalUnemployment": 140,
"educatedUnemployment": 80,
"percentageEducatedOfTotal": 57.1,
"productiveAgePopulation": 125,
"percentageProductiveOfTotal": 89.3,
"percentageChangeFromPreviousYear": -12.5
}
]

View File

@@ -1,8 +0,0 @@
[
{
"recordedDate": "2025-06-30",
"count": 95,
"percentageOfTotal": 67.9
}
]

View File

@@ -0,0 +1,19 @@
-- CreateTable
CREATE TABLE "DetailDataPengangguran" (
"id" UUID NOT NULL,
"month" VARCHAR(20) NOT NULL,
"year" INTEGER NOT NULL,
"totalUnemployment" INTEGER NOT NULL,
"educatedUnemployment" INTEGER NOT NULL,
"uneducatedUnemployment" INTEGER NOT NULL,
"percentageChange" DOUBLE PRECISION,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "DetailDataPengangguran_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "DetailDataPengangguran_month_year_key" ON "DetailDataPengangguran"("month", "year");

View File

@@ -1279,30 +1279,3 @@ model DetailDataPengangguran {
@@unique([month, year]) @@unique([month, year])
} }
model RingkasanDataPengangguran {
id String @id @default(uuid()) @db.Uuid
year Int @unique
totalUnemployment Int
educatedUnemployment Int
percentageEducatedOfTotal Float?
productiveAgePopulation Int?
percentageProductiveOfTotal Float?
percentageChangeFromPreviousYear Float?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model SedangMencariKerja {
id String @id @default(uuid()) @db.Uuid
count Int
percentageOfTotal Float?
recordedDate DateTime @unique @db.Date
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}

View File

@@ -22,8 +22,6 @@ import hubunganOrganisasi from "./data/ekonomi/struktur-organisasi/hubungan-orga
import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi.json"; import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi.json";
import pegawai from "./data/ekonomi/struktur-organisasi/pegawai.json"; import pegawai from "./data/ekonomi/struktur-organisasi/pegawai.json";
import detailDataPengangguran from './data/ekonomi/jumlah-pengangguran/detail-data-pengangguran.json'; import detailDataPengangguran from './data/ekonomi/jumlah-pengangguran/detail-data-pengangguran.json';
import ringkasanDataPengangguran from './data/ekonomi/jumlah-pengangguran/ringkasan-data-pengangguran.json';
import sedangMencariKerja from './data/ekonomi/jumlah-pengangguran/sedang-mencari-kerja.json';
(async () => { (async () => {
for (const l of layanan) { for (const l of layanan) {
@@ -457,52 +455,6 @@ import sedangMencariKerja from './data/ekonomi/jumlah-pengangguran/sedang-mencar
}); });
} }
console.log("📊 detailDataPengangguran success ..."); console.log("📊 detailDataPengangguran success ...");
// RingkasanDataPengangguran
for (const r of ringkasanDataPengangguran) {
await prisma.ringkasanDataPengangguran.upsert({
where: {
year: r.year,
},
update: {
totalUnemployment: r.totalUnemployment,
educatedUnemployment: r.educatedUnemployment,
percentageEducatedOfTotal: r.percentageEducatedOfTotal,
productiveAgePopulation: r.productiveAgePopulation,
percentageProductiveOfTotal: r.percentageProductiveOfTotal,
percentageChangeFromPreviousYear: r.percentageChangeFromPreviousYear,
},
create: {
year: r.year,
totalUnemployment: r.totalUnemployment,
educatedUnemployment: r.educatedUnemployment,
percentageEducatedOfTotal: r.percentageEducatedOfTotal,
productiveAgePopulation: r.productiveAgePopulation,
percentageProductiveOfTotal: r.percentageProductiveOfTotal,
percentageChangeFromPreviousYear: r.percentageChangeFromPreviousYear,
},
});
}
console.log("📈 ringkasanDataPengangguran success ...");
// SedangMencariKerja
for (const s of sedangMencariKerja) {
await prisma.sedangMencariKerja.upsert({
where: {
recordedDate: new Date(s.recordedDate),
},
update: {
count: s.count,
percentageOfTotal: s.percentageOfTotal,
},
create: {
recordedDate: new Date(s.recordedDate),
count: s.count,
percentageOfTotal: s.percentageOfTotal,
},
});
}
console.log("💼 sedangMencariKerja success ...");
})() })()
.then(() => prisma.$disconnect()) .then(() => prisma.$disconnect())
.catch((e) => { .catch((e) => {

View File

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

View File

@@ -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({ const jumlahPengangguranState = proxy({
jumlahPengangguran, jumlahPengangguran,
ringkasanData,
sedangMencariKerja,
}); });
export default jumlahPengangguranState; 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 { Box, Button, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import JudulListTab from '../../../_com/jusulListTab'; import JudulListTab from '../../../_com/judulListTab';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import stateGallery from '../../../_state/desa/gallery'; import stateGallery from '../../../_state/desa/gallery';
import { useShallowEffect } from '@mantine/hooks'; 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 { Box, Button, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import JudulListTab from '../../../_com/jusulListTab'; import JudulListTab from '../../../_com/judulListTab';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import stateGallery from '../../../_state/desa/gallery'; import stateGallery from '../../../_state/desa/gallery';
import { useShallowEffect } from '@mantine/hooks'; import { useShallowEffect } from '@mantine/hooks';

View File

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

View File

@@ -1,5 +1,5 @@
'use client' 'use client'
import JudulListTab from '@/app/admin/(dashboard)/_com/jusulListTab'; import JudulListTab from '@/app/admin/(dashboard)/_com/judulListTab';
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; 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 { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import JudulListTab from '../../_com/jusulListTab'; import JudulListTab from '../../_com/judulListTab';
import { useState } from 'react'; import { useState } from 'react';
import HeaderSearch from '../../_com/header'; import HeaderSearch from '../../_com/header';

View File

@@ -8,7 +8,7 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts'; import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/jusulListTab'; import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur'; 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 { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts'; import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/jusulListTab'; import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur'; 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' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Badge, Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; 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 { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header'; import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList'; import JudulList from '../../../_com/judulList';
import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi'; import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
import { useState } from 'react';
function Pegawai() { function Pegawai() {
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
@@ -30,7 +30,7 @@ function ListPegawai({ search }: { search: string }) {
const stateOrganisasi = useProxy(strukturorganisasiState.pegawai); const stateOrganisasi = useProxy(strukturorganisasiState.pegawai);
const router = useRouter(); const router = useRouter();
useShallowEffect(() => { useEffect(() => {
const loadData = async () => { const loadData = async () => {
try { try {
// Clear existing data to ensure we see the loading state // Clear existing data to ensure we see the loading state

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts'; import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/jusulListTab'; import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import HeaderSearch from '../../../_com/header'; 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 { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import JudulListTab from '../../../_com/jusulListTab'; import JudulListTab from '../../../_com/judulListTab';
function KeteranganBankSampahTerdekat() { function KeteranganBankSampahTerdekat() {
const router = useRouter(); 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 { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import JudulListTab from '../../../_com/jusulListTab'; import JudulListTab from '../../../_com/judulListTab';
function ListPengelolaanSampahBankSampah() { function ListPengelolaanSampahBankSampah() {
const router = useRouter(); const router = useRouter();

View File

@@ -9,7 +9,7 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts'; import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/jusulListTab'; import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import HeaderSearch from '../../../_com/header'; import HeaderSearch from '../../../_com/header';

View File

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

View File

@@ -9,7 +9,7 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts'; import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/jusulListTab'; import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import HeaderSearch from '../../../_com/header'; import HeaderSearch from '../../../_com/header';

View File

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

View File

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

View File

@@ -1,10 +1,10 @@
import Elysia, { t } from "elysia"; import Elysia, { t } from "elysia";
import detailDataPengangguranFindMany from "./findMany";
import detailDataPengangguranCreate from "./create"; import detailDataPengangguranCreate from "./create";
import detailDataPengangguranUpdate from "./updt";
import detailDataPengangguranFindUnique from "./findUnique";
import detailDataPengangguranDelete from "./del"; import detailDataPengangguranDelete from "./del";
import findByMonthYear from "./findByMonthYear"; import findByMonthYear from "./findByMonthYear";
import detailDataPengangguranFindMany from "./findMany";
import detailDataPengangguranFindUnique from "./findUnique";
import detailDataPengangguranUpdate from "./updt";
const DetailDataPengangguran = new Elysia({ const DetailDataPengangguran = new Elysia({
prefix: "/detaildatapengangguran", prefix: "/detaildatapengangguran",
@@ -50,6 +50,6 @@ const DetailDataPengangguran = new Elysia({
month: t.String(), month: t.String(),
year: t.String(), year: t.String(),
}), }),
}); })
export default DetailDataPengangguran; export default DetailDataPengangguran;

View File

@@ -1,13 +1,10 @@
import Elysia from "elysia"; import Elysia from "elysia";
import DetailDataPengangguran from "./detail-data-pengangguran"; import DetailDataPengangguran from "./detail-data-pengangguran";
import RingkasanDataPengangguran from "./ringkasan-data-pengangguran";
import SedangMencariKerja from "./sedang-mencari-kerja";
const JumlahPengangguran = new Elysia({ const JumlahPengangguran = new Elysia({
prefix: "/jumlahpengangguran", prefix: "/jumlahpengangguran",
}) })
.use(DetailDataPengangguran) .use(DetailDataPengangguran)
.use(RingkasanDataPengangguran)
.use(SedangMencariKerja)
export default JumlahPengangguran; export default JumlahPengangguran;

View File

@@ -1,61 +0,0 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.RingkasanDataPengangguranGetPayload<{
select: {
year: true;
totalUnemployment: true;
educatedUnemployment: true;
percentageEducatedOfTotal: true;
productiveAgePopulation: true;
percentageProductiveOfTotal: true;
percentageChangeFromPreviousYear: true;
};
}>;
export default async function ringkasanDataPengangguranCreate(context: Context) {
const body = context.body as FormCreate;
const existing = await prisma.ringkasanDataPengangguran.findFirst({
where: {
year: body.year,
},
});
if (existing) {
return {
success: false,
message: `Data tahun ${body.year} sudah ada.`,
data: null,
};
}
const created = await prisma.ringkasanDataPengangguran.create({
data: {
year: body.year,
totalUnemployment: body.totalUnemployment,
educatedUnemployment: body.educatedUnemployment,
percentageEducatedOfTotal: body.percentageEducatedOfTotal,
productiveAgePopulation: body.productiveAgePopulation,
percentageProductiveOfTotal: body.percentageProductiveOfTotal,
percentageChangeFromPreviousYear: body.percentageChangeFromPreviousYear,
},
select: {
id: true,
year: true,
totalUnemployment: true,
educatedUnemployment: true,
percentageEducatedOfTotal: true,
productiveAgePopulation: true,
percentageProductiveOfTotal: true,
percentageChangeFromPreviousYear: true,
},
});
return {
success: true,
message: "Berhasil menambahkan data ringkasan pengangguran",
data: created,
};
}

View File

@@ -1,38 +0,0 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function ringkasanDataPengangguranDelete(context: Context) {
const id = context.params?.id;
if (!id) {
return {
success: false,
message: "ID tidak ditemukan",
}
}
const existing = await prisma.ringkasanDataPengangguran.findUnique({
where: {
id: id,
},
})
if (!existing) {
return {
success: false,
message: "Data tidak ditemukan",
}
}
const deleted = await prisma.ringkasanDataPengangguran.delete({
where: {
id: id,
},
})
return {
success: true,
message: "Berhasil menghapus data ringkasan pengangguran",
data: deleted,
}
}

View File

@@ -1,10 +0,0 @@
import prisma from "@/lib/prisma";
export default async function ringkasanDataPengangguranFindMany() {
const res = await prisma.ringkasanDataPengangguran.findMany({
orderBy: [{ year: "desc" }],
});
return {
data: res,
};
}

View File

@@ -1,45 +0,0 @@
import prisma from "@/lib/prisma";
export default async function ringkasanDataPengangguranFindUnique(request: Request) {
const url = new URL(request.url);
const pathSegments = url.pathname.split('/');
const id = pathSegments[pathSegments.length - 1];
if (!id) {
return Response.json({
success: false,
message: "ID tidak boleh kosong",
}, { status: 400 });
}
try {
if (typeof id !== 'string') {
return Response.json({
success: false,
message: "ID tidak valid",
}, { status: 400 });
}
const data = await prisma.ringkasanDataPengangguran.findUnique({
where: { id },
});
if (!data) {
return Response.json({
success: false,
message: "Data tidak ditemukan",
}, { status: 404 });
}
return Response.json({
success: true,
data: data,
}, { status: 200 });
} catch (error) {
console.error("Error fetching data:", error);
return Response.json({
success: false,
message: "Terjadi kesalahan saat mengambil data",
}, { status: 500 });
}
}

View File

@@ -1,50 +0,0 @@
import Elysia, { t } from "elysia";
import ringkasanDataPengangguranFindUnique from "./findUnique";
import ringkasanDataPengangguranFindMany from "./findMany";
import ringkasanDataPengangguranCreate from "./create";
import ringkasanDataPengangguranUpdate from "./updt";
import ringkasanDataPengangguranDelete from "./del";
const RingkasanDataPengangguran = new Elysia({
prefix: "/ringkasandatapengangguran",
tags: ["Ekonomi/Jumlah Pengangguran/Ringkasan Data Pengangguran"],
})
.get("/:id", async (context) => {
const response = await ringkasanDataPengangguranFindUnique(
new Request(context.request)
);
return response;
})
.get("/find-many", ringkasanDataPengangguranFindMany)
.post("/create", ringkasanDataPengangguranCreate, {
body: t.Object({
year: t.Number(),
totalUnemployment: t.Number(),
educatedUnemployment: t.Number(),
percentageEducatedOfTotal: t.Number(),
productiveAgePopulation: t.Number(),
percentageProductiveOfTotal: t.Number(),
percentageChangeFromPreviousYear: t.Number(),
}),
})
.put("/:id", ringkasanDataPengangguranUpdate, {
params: t.Object({
id: t.String(),
}),
body: t.Object({
year: t.Number(),
totalUnemployment: t.Number(),
educatedUnemployment: t.Number(),
percentageEducatedOfTotal: t.Number(),
productiveAgePopulation: t.Number(),
percentageProductiveOfTotal: t.Number(),
percentageChangeFromPreviousYear: t.Number(),
}),
})
.delete("/del/:id", ringkasanDataPengangguranDelete, {
params: t.Object({
id: t.String(),
}),
});
export default RingkasanDataPengangguran;

View File

@@ -1,70 +0,0 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function ringkasanDataPengangguranUpdate(context: Context) {
const id = context.params?.id;
if (!id) {
return {
success: false,
message: "ID tidak ditemukan",
}
}
const { year, totalUnemployment, educatedUnemployment, percentageEducatedOfTotal, productiveAgePopulation, percentageProductiveOfTotal, percentageChangeFromPreviousYear } = context.body as {
year: number;
totalUnemployment: number;
educatedUnemployment: number;
percentageEducatedOfTotal: number;
productiveAgePopulation: number;
percentageProductiveOfTotal: number;
percentageChangeFromPreviousYear: number;
}
const duplicate = await prisma.ringkasanDataPengangguran.findFirst({
where: {
year,
NOT: { id },
},
});
if (duplicate) {
return {
success: false,
message: `Data tahun ${year} sudah ada.`,
};
}
const existing = await prisma.ringkasanDataPengangguran.findUnique({
where: {
id: id,
},
})
if (!existing) {
return {
success: false,
message: "Data tidak ditemukan",
}
}
const updated = await prisma.ringkasanDataPengangguran.update({
where: { id },
data: {
year,
totalUnemployment,
educatedUnemployment,
percentageEducatedOfTotal,
productiveAgePopulation,
percentageProductiveOfTotal,
percentageChangeFromPreviousYear,
},
})
return {
success: true,
message: "Data berhasil diupdate",
data: updated,
}
}

View File

@@ -1,49 +0,0 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.SedangMencariKerjaGetPayload<{
select: {
count: true;
percentageOfTotal: true;
recordedDate: true;
};
}>;
export default async function sedangMencariKerjaCreate(context: Context) {
const body = context.body as FormCreate;
const existing = await prisma.sedangMencariKerja.findFirst({
where: {
recordedDate: body.recordedDate,
},
});
if (existing) {
return {
success: false,
message: "Data sudah ada",
data: null,
};
}
const created = await prisma.sedangMencariKerja.create({
data: {
count: body.count,
percentageOfTotal: body.percentageOfTotal,
recordedDate: body.recordedDate,
},
select: {
id: true,
count: true,
percentageOfTotal: true,
recordedDate: true,
},
});
return {
success: true,
message: "Berhasil menambahkan data sedang mencari kerja",
data: created,
};
}

View File

@@ -1,38 +0,0 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function sedangMencariKerjaDelete(context: Context) {
const id = context.params?.id;
if (!id) {
return {
success: false,
message: "ID tidak ditemukan",
}
}
const existing = await prisma.sedangMencariKerja.findUnique({
where: {
id: id,
},
})
if (!existing) {
return {
success: false,
message: "Data tidak ditemukan",
}
}
const deleted = await prisma.sedangMencariKerja.delete({
where: {
id: id,
},
})
return {
success: true,
message: "Berhasil menghapus data sedang mencari kerja",
data: deleted,
}
}

View File

@@ -1,10 +0,0 @@
import prisma from "@/lib/prisma";
export default async function sedangMencariKerjaFindMany() {
const res = await prisma.sedangMencariKerja.findMany({
orderBy: [{ recordedDate: "desc" }],
});
return {
data: res,
};
}

View File

@@ -1,46 +0,0 @@
import prisma from "@/lib/prisma";
export default async function sedangMencariKerjaFindUnique(request: Request) {
const url = new URL(request.url);
const pathSegments = url.pathname.split('/');
const id = pathSegments[pathSegments.length - 1];
if (!id) {
return Response.json({
success: false,
message: "ID tidak boleh kosong",
}, { status: 400 });
}
try {
if (typeof id !== 'string') {
return Response.json({
success: false,
message: "ID tidak valid",
}, { status: 400 });
}
const data = await prisma.sedangMencariKerja.findUnique({
where: { id },
});
if (!data) {
return Response.json({
success: false,
message: "Data tidak ditemukan",
}, { status: 404 });
}
return Response.json({
success: true,
message: "Data ditemukan",
data,
}, { status: 200 });
} catch (error) {
console.error("Error fetching data:", error);
return Response.json({
success: false,
message: "Terjadi kesalahan saat mengambil data",
}, { status: 500 });
}
}

View File

@@ -1,42 +0,0 @@
import Elysia, { t } from "elysia";
import sedangMencariKerjaFindUnique from "./findUnique";
import sedangMencariKerjaFindMany from "./findMany";
import sedangMencariKerjaCreate from "./create";
import sedangMencariKerjaUpdate from "./updt";
import sedangMencariKerjaDelete from "./del";
const SedangMencariKerja = new Elysia({
prefix: "/sedangmencarikerja",
tags: ["Ekonomi/Jumlah Pengangguran/Sedang Mencari Kerja"],
})
.get("/:id", async (context) => {
const response = await sedangMencariKerjaFindUnique(
new Request(context.request)
);
return response;
})
.get("/find-many", sedangMencariKerjaFindMany)
.post("/create", sedangMencariKerjaCreate, {
body: t.Object({
count: t.Number(),
percentageOfTotal: t.Number(),
recordedDate: t.String(),
}),
})
.put("/:id", sedangMencariKerjaUpdate, {
params: t.Object({
id: t.String(),
}),
body: t.Object({
count: t.Number(),
percentageOfTotal: t.Number(),
recordedDate: t.String(),
}),
})
.delete("/del/:id", sedangMencariKerjaDelete, {
params: t.Object({
id: t.String(),
}),
});
export default SedangMencariKerja;

View File

@@ -1,49 +0,0 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function sedangMencariKerjaUpdate(context: Context) {
const id = context.params?.id;
if (!id) {
return {
success: false,
message: "ID tidak ditemukan",
}
}
const { count, percentageOfTotal, recordedDate } = context.body as {
count: number;
percentageOfTotal: number;
recordedDate: string;
}
const existing = await prisma.sedangMencariKerja.findUnique({
where: {
id: id,
},
})
if (!existing) {
return {
success: false,
message: "Data tidak ditemukan",
}
}
const updated = await prisma.sedangMencariKerja.update({
where: {
id: id,
},
data: {
count: count,
percentageOfTotal: percentageOfTotal,
recordedDate: recordedDate,
},
})
return {
success: true,
message: "Berhasil mengupdate data sedang mencari kerja",
data: updated,
}
}

View File

@@ -1,9 +1,8 @@
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Stack, Box, Text, Group, Flex, Button, SimpleGrid, Paper, Center, ColorSwatch, TableTd, TableTr, Table, TableTbody, TableTh, TableThead } from '@mantine/core';
import React from 'react';
import BackButton from '../../desa/layanan/_com/BackButto';
import { IconBriefcase, IconChevronDown, IconDownload, IconSchool, IconUserOff, IconUsersGroup } from '@tabler/icons-react';
import { BarChart } from '@mantine/charts'; 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 = [ const data1 = [
{ {
@@ -22,17 +21,10 @@ const data1 = [
}, },
{ {
id: 3, id: 3,
icon: <IconUsersGroup size={35} />, icon: <IconSchoolOff size={35} />,
judul: 'Usia Produktif', judul: 'Pengangguran Tidak Terdidik',
jumlah: '125', jumlah: '60',
persentase: <Text fz={'h4'} c={'gray'}>89.3% dari total</Text> persentase: <Text fz={'h4'} c={'gray'}>42.9% dari total</Text>
},
{
id: 4,
icon: <IconBriefcase size={35} />,
judul: 'Sedang Mencari Kerja',
jumlah: '95',
persentase: <Text fz={'h4'} c={'red'}>67.9% dari total</Text>
}, },
] ]