From 5e137ba65823b8e0eba6a497b04d2851ce2eebf9 Mon Sep 17 00:00:00 2001 From: nico Date: Thu, 14 Aug 2025 11:48:57 +0800 Subject: [PATCH] Fix Admin Submenu Posyandu, Menu Kesehatan, dan Sinkronisasi UI & API Admin - User Submenu Posyandu --- prisma/schema.prisma | 21 +-- prisma/seed.ts | 81 +++++------- .../_state/kesehatan/posyandu/posyandu.ts | 51 ++++++-- .../desa/berita/list-berita/page.tsx | 2 +- .../kesehatan/posyandu/[id]/edit/page.tsx | 13 ++ .../kesehatan/posyandu/[id]/page.tsx | 4 + .../kesehatan/posyandu/create/page.tsx | 10 ++ .../(dashboard)/kesehatan/posyandu/page.tsx | 49 +++++-- .../_lib/kesehatan/posyandu/create.ts | 2 + .../_lib/kesehatan/posyandu/find-many.ts | 71 +++++++--- .../_lib/kesehatan/posyandu/index.ts | 2 + .../_lib/kesehatan/posyandu/updt.ts | 3 + .../(pages)/kesehatan/posyandu/page.tsx | 121 ++++++++++-------- 13 files changed, 273 insertions(+), 157 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7a181220..674c980a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1027,16 +1027,17 @@ model DoctorSign { // ========================================= POSYANDU ========================================= // model Posyandu { - id String @id @default(cuid()) - name String - nomor String - deskripsi String - image FileStorage @relation(fields: [imageId], references: [id]) - imageId String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - deletedAt DateTime @default(now()) - isActive Boolean @default(true) + id String @id @default(cuid()) + name String + nomor String + deskripsi String + jadwalPelayanan String + image FileStorage @relation(fields: [imageId], references: [id]) + imageId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) } // ========================================= PUSKESMAS ========================================= // diff --git a/prisma/seed.ts b/prisma/seed.ts index 574eaef9..f4290d00 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -125,8 +125,8 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak } console.log("penghargaan success ..."); - // =========== LAYANAN DESA =========== - for (const p of pelayananSuratKeterangan) { + // =========== LAYANAN DESA =========== + for (const p of pelayananSuratKeterangan) { await prisma.pelayananSuratKeterangan.upsert({ where: { id: p.id }, update: { @@ -317,63 +317,42 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak console.log("visi misi desa success ..."); - // Flatten the nested array structure for posisiOrganisasiPPID - const flattenedPosisiOrganisasiPPID = posisiOrganisasiPPID.flat(); + const flattenedPosisi = posisiOrganisasiPPID.flat(); + + // ✅ Urutkan berdasarkan hierarki + const sortedPosisi = flattenedPosisi.sort((a, b) => a.hierarki - b.hierarki); + + for (const p of sortedPosisi) { + console.log(`Seeding: ${p.nama} (id: ${p.id}, parent: ${p.parentId})`); + + if (p.parentId) { + const parentExists = flattenedPosisi.some((pos) => pos.id === p.parentId); + if (!parentExists) { + console.warn( + `⚠️ Parent tidak ditemukan: ${p.parentId} untuk ${p.nama}` + ); + continue; + } + } - for (const p of flattenedPosisiOrganisasiPPID) { await prisma.posisiOrganisasiPPID.upsert({ - where: { - id: p.id, - }, - update: { - nama: p.nama, - deskripsi: p.deskripsi, - hierarki: p.hierarki, - parentId: p.parentId, - }, - create: { - id: p.id, - nama: p.nama, - deskripsi: p.deskripsi, - hierarki: p.hierarki, - parentId: p.parentId, - }, + where: { id: p.id }, + update: p, + create: p, }); } - console.log("posisi organisasi success ..."); + console.log("✅ Posisi organisasi berhasil"); - // Flatten the nested array structure for pegawaiPPID - const flattenedPegawaiPPID = pegawaiPPID.flat(); - - for (const p of flattenedPegawaiPPID) { + // 2. Seed Pegawai + const flattenedPegawai = pegawaiPPID.flat(); + for (const p of flattenedPegawai) { await prisma.pegawaiPPID.upsert({ - where: { - id: p.id, - }, - update: { - namaLengkap: p.namaLengkap, - tanggalMasuk: new Date(p.tanggalMasuk), - email: p.email, - gelarAkademik: p.gelarAkademik, - telepon: p.telepon, - alamat: p.alamat, - posisiId: p.posisiId, - isActive: p.isActive, - }, - create: { - id: p.id, - namaLengkap: p.namaLengkap, - tanggalMasuk: new Date(p.tanggalMasuk), - email: p.email, - gelarAkademik: p.gelarAkademik, - telepon: p.telepon, - alamat: p.alamat, - posisiId: p.posisiId, - isActive: p.isActive, - }, + where: { id: p.id }, + update: p, + create: p, }); } - console.log("pegawai success ..."); + console.log("✅ Pegawai berhasil"); for (const l of pelayananPerizinanBerusaha) { await prisma.pelayananPerizinanBerusaha.upsert({ diff --git a/src/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu.ts b/src/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu.ts index 83d9ded2..38ccec4c 100644 --- a/src/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu.ts +++ b/src/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import ApiFetch from "@/lib/api-fetch"; import { Prisma } from "@prisma/client"; import { toast } from "react-toastify"; @@ -9,6 +10,7 @@ const templateForm = z.object({ nomor: z.string().min(1, { message: "Nomor is required" }), deskripsi: z.string().min(1, { message: "Deskripsi is required" }), imageId: z.string().nonempty(), + jadwalPelayanan: z.string().min(1, { message: "Jadwal Pelayanan is required" }), }); const defaultForm = { @@ -16,6 +18,7 @@ const defaultForm = { nomor: "", deskripsi: "", imageId: "", + jadwalPelayanan: "", }; const posyandustate = proxy({ @@ -50,19 +53,43 @@ const posyandustate = proxy({ } }, findMany: { - data: null as - | Prisma.PosyanduGetPayload<{ - include: { + data: null as + | Prisma.PosyanduGetPayload<{ + include: { image: true; + }; + }>[] + | null, + page: 1, + totalPages: 1, + loading: false, + search: "", + load: async (page = 1, limit = 10, search = "") => { + posyandustate.findMany.loading = true; // ✅ Akses langsung via nama path + posyandustate.findMany.page = page; + posyandustate.findMany.search = search; + + try { + const query: any = { page, limit }; + if (search) query.search = search; + + const res = await ApiFetch.api.kesehatan.posyandu["find-many"].get({ query }); + + if (res.status === 200 && res.data?.success) { + posyandustate.findMany.data = res.data.data ?? []; + posyandustate.findMany.totalPages = res.data.totalPages ?? 1; + } else { + posyandustate.findMany.data = []; + posyandustate.findMany.totalPages = 1; } - }>[] - | null, - async load() { - const res = await ApiFetch.api.kesehatan.posyandu["find-many"].get(); - if (res.status === 200) { - posyandustate.findMany.data = res.data?.data ?? []; - } - } + } catch (err) { + console.error("Gagal fetch posyandu paginated:", err); + posyandustate.findMany.data = []; + posyandustate.findMany.totalPages = 1; + } finally { + posyandustate.findMany.loading = false; + } + }, }, findUnique: { data: null as @@ -148,6 +175,7 @@ const posyandustate = proxy({ nomor: data.nomor, deskripsi: data.deskripsi, imageId: data.imageId || "", + jadwalPelayanan: data.jadwalPelayanan || "", }; return data; } else { @@ -181,6 +209,7 @@ const posyandustate = proxy({ nomor: this.form.nomor, deskripsi: this.form.deskripsi, imageId: this.form.imageId, + jadwalPelayanan: this.form.jadwalPelayanan, }), }); diff --git a/src/app/admin/(dashboard)/desa/berita/list-berita/page.tsx b/src/app/admin/(dashboard)/desa/berita/list-berita/page.tsx index 00447ffc..3d4729e8 100644 --- a/src/app/admin/(dashboard)/desa/berita/list-berita/page.tsx +++ b/src/app/admin/(dashboard)/desa/berita/list-berita/page.tsx @@ -47,7 +47,7 @@ function ListBerita({ search }: { search: string }) { if (loading || !data) { return ; } - + const filteredData = data || []; return ( diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx index 66133acf..a6861418 100644 --- a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx @@ -25,6 +25,7 @@ function EditPosyandu() { nomor: statePosyandu.edit.form.nomor || '', deskripsi: statePosyandu.edit.form.deskripsi || '', imageId: statePosyandu.edit.form.imageId || '', + jadwalPelayanan: statePosyandu.edit.form.jadwalPelayanan || '', }); useEffect(() => { @@ -40,6 +41,7 @@ function EditPosyandu() { nomor: data.nomor || '', deskripsi: data.deskripsi || '', imageId: data.imageId || '', + jadwalPelayanan: data.jadwalPelayanan || '', }); if (data?.image?.link) { @@ -62,6 +64,7 @@ function EditPosyandu() { nomor: formData.nomor, deskripsi: formData.deskripsi, imageId: formData.imageId, + jadwalPelayanan: formData.jadwalPelayanan, } if (file) { @@ -173,6 +176,16 @@ function EditPosyandu() { }} /> + + Jadwal Pelayanan + { + setFormData({ ...formData, jadwalPelayanan: htmlContent }); + statePosyandu.edit.form.jadwalPelayanan = htmlContent; + }} + /> + diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx index 415a17c4..84f10b68 100644 --- a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx @@ -63,6 +63,10 @@ function DetailPosyandu() { Deskripsi Posyandu + + Jadwal Pelayanan + + Gambar gambar diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx index 6784da6b..9ebdd4e0 100644 --- a/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx @@ -23,6 +23,7 @@ function CreatePosyandu() { nomor: "", deskripsi: "", imageId: "", + jadwalPelayanan: "", }; setFile(null); @@ -147,6 +148,15 @@ function CreatePosyandu() { }} /> + + Jadwal Pelayanan + { + statePosyandu.create.form.jadwalPelayanan = htmlContent; + }} + /> + diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/page.tsx index 03e2e6be..6144b417 100644 --- a/src/app/admin/(dashboard)/kesehatan/posyandu/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/posyandu/page.tsx @@ -1,6 +1,6 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; +import { Box, Button, Center, Pagination, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import HeaderSearch from '../../_com/header'; import JudulList from '../../_com/judulList'; @@ -30,19 +30,21 @@ function ListPosyandu({ search }: { search: string }) { const statePosyandu = useProxy(posyandustate) const router = useRouter(); + const { + data, + page, + totalPages, + loading, + load, + } = statePosyandu.findMany; + useShallowEffect(() => { - statePosyandu.findMany.load() - }, []) + load(page, 10, search) + }, [page, search]) - const filteredData = (statePosyandu.findMany.data || []).filter(item => { - const keyword = search.toLowerCase(); - return ( - item.name.toLowerCase().includes(keyword) || - item.nomor.toString().toLowerCase().includes(keyword) - ); - }); + const filteredData = data || []; - if (!statePosyandu.findMany.data) { + if (loading || !data) { return ( @@ -70,10 +72,20 @@ function ListPosyandu({ search }: { search: string }) { {filteredData.map((item) => ( - {item.name} - {item.nomor} - + + {item.name} + + + + + {item.nomor} + + + + + +