From a1d55e2b0a49cf6e4aa171d13d136e22213f31e9 Mon Sep 17 00:00:00 2001 From: nico Date: Wed, 13 Aug 2025 00:07:57 +0800 Subject: [PATCH] Fix Admin Menu PPID, Submenu IKM --- .../ppid/ikm/jenis-kelamin/jenis-kelamin.json | 10 + .../rating-responden.json | 18 + .../ikm/umur-responden/umur-responden.json | 14 + prisma/schema.prisma | 86 +- prisma/seed.ts | 51 ++ .../_state/landing-page/indeks-kepuasan.ts | 788 ++++++++++++++++++ .../grafikBerdasarkanJenisKelamin.ts | 90 +- .../_lib}/layoutTabs.tsx | 29 +- .../[id]/page.tsx | 78 -- .../create/page.tsx | 83 -- .../page.tsx | 227 ----- .../[id]/page.tsx | 98 --- .../create/page.tsx | 98 --- .../grafik_berdasarkan_responden/page.tsx | 251 ------ .../grafik_berdasarkan_umur/[id]/page.tsx | 97 --- .../grafik_berdasarkan_umur/create/page.tsx | 98 --- .../grafik_berdasarkan_umur/page.tsx | 252 ------ .../[id]/page.tsx | 81 -- .../create/page.tsx | 83 -- .../grafik_hasil_kepuasan_masyarakat/page.tsx | 211 ----- .../indeks-kepuasan-masyarakat/page.tsx | 215 +++++ .../ppid/ikm-desa-darmasaba/layout.tsx | 19 +- .../responden/[id]/edit/page.tsx | 137 +++ .../responden/[id]/page.tsx | 91 ++ .../responden/create/page.tsx | 148 ++++ .../ikm-desa-darmasaba/responden/page.tsx | 151 ++++ src/app/admin/_com/list_PageAdmin.tsx | 2 +- .../jenis-kelamin-responden/create.ts | 26 + .../jenis-kelamin-responden/del.ts | 16 + .../jenis-kelamin-responden/findMany.ts | 53 ++ .../jenis-kelamin-responden/findUnique.ts | 46 + .../jenis-kelamin-responden/index.ts | 33 + .../jenis-kelamin-responden/updt.ts | 28 + .../pilihan-rating-responden/create.ts | 26 + .../pilihan-rating-responden/del.ts | 16 + .../pilihan-rating-responden/findMany.ts | 53 ++ .../pilihan-rating-responden/findUnique.ts | 46 + .../pilihan-rating-responden/index.ts | 33 + .../pilihan-rating-responden/updt.ts | 28 + .../indeks_kepuasan/responden/create.ts | 43 + .../indeks_kepuasan/responden/del.ts | 33 + .../indeks_kepuasan/responden/findMany.ts | 61 ++ .../indeks_kepuasan/responden/findUnique.ts | 51 ++ .../indeks_kepuasan/responden/index.ts | 42 + .../indeks_kepuasan/responden/updt.ts | 36 + .../indeks_kepuasan/umur-responden/create.ts | 26 + .../indeks_kepuasan/umur-responden/del.ts | 16 + .../umur-responden/findMany.ts | 53 ++ .../umur-responden/findUnique.ts | 46 + .../indeks_kepuasan/umur-responden/index.ts | 33 + .../indeks_kepuasan/umur-responden/updt.ts | 28 + .../[[...slugs]]/_lib/landing_page/index.ts | 8 + .../desa/berita/[kategori]/Content.tsx | 2 +- .../(pages)/desa/berita/semua/page.tsx | 4 +- .../(pages)/desa/galery/_lib/searchBar.tsx | 51 +- .../(pages)/desa/galery/foto/Content.tsx | 66 +- .../(pages)/desa/galery/video/Content.tsx | 48 +- .../darmasaba/(pages)/desa/profile/page.tsx | 4 +- .../pengaduan-masyarakat/page.tsx | 2 +- .../pendidikan/data-pendidikan/page.tsx | 2 +- .../(tambahan)/penghargaan/[id]/page.tsx | 2 +- .../_com/main-page/kepuasan/index.tsx | 5 +- .../main-page/landing-page/ProfileView.tsx | 60 ++ .../_com/main-page/landing-page/index.tsx | 62 +- 64 files changed, 2865 insertions(+), 1829 deletions(-) create mode 100644 prisma/data/ppid/ikm/jenis-kelamin/jenis-kelamin.json create mode 100644 prisma/data/ppid/ikm/pilihan-rating-responden/rating-responden.json create mode 100644 prisma/data/ppid/ikm/umur-responden/umur-responden.json create mode 100644 src/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan.ts rename src/app/admin/(dashboard)/ppid/{_com => ikm-desa-darmasaba/_lib}/layoutTabs.tsx (62%) delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/[id]/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/create/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/[id]/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/create/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/[id]/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/create/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/[id]/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/create/page.tsx delete mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/page.tsx create mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/indeks-kepuasan-masyarakat/page.tsx create mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/[id]/edit/page.tsx create mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/[id]/page.tsx create mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/create/page.tsx create mode 100644 src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/page.tsx create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/updt.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/updt.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/updt.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/updt.ts create mode 100644 src/app/darmasaba/_com/main-page/landing-page/ProfileView.tsx diff --git a/prisma/data/ppid/ikm/jenis-kelamin/jenis-kelamin.json b/prisma/data/ppid/ikm/jenis-kelamin/jenis-kelamin.json new file mode 100644 index 00000000..6998305e --- /dev/null +++ b/prisma/data/ppid/ikm/jenis-kelamin/jenis-kelamin.json @@ -0,0 +1,10 @@ +[ + { + "id": "cme8bt5o5000007lb9xp11unb", + "name": "Laki-laki" + }, + { + "id": "cme8btctl000107lbh2hocgg8", + "name": "Perempuan" + } +] \ No newline at end of file diff --git a/prisma/data/ppid/ikm/pilihan-rating-responden/rating-responden.json b/prisma/data/ppid/ikm/pilihan-rating-responden/rating-responden.json new file mode 100644 index 00000000..92f9406f --- /dev/null +++ b/prisma/data/ppid/ikm/pilihan-rating-responden/rating-responden.json @@ -0,0 +1,18 @@ +[ + { + "id": "cme8buup6000207lb54q9b0az", + "name": "Sangat Baik" + }, + { + "id": "cme8bv15o000307lbft9b0vzy", + "name": "Baik" + }, + { + "id": "cme8bvjvu000507lbgfsveog6", + "name": "Kurang Baik" + }, + { + "id": "cme8bvvm6000607lbh6rn2ubm", + "name": "Sangat Kurang Baik" + } +] \ No newline at end of file diff --git a/prisma/data/ppid/ikm/umur-responden/umur-responden.json b/prisma/data/ppid/ikm/umur-responden/umur-responden.json new file mode 100644 index 00000000..f71add3c --- /dev/null +++ b/prisma/data/ppid/ikm/umur-responden/umur-responden.json @@ -0,0 +1,14 @@ +[ + { + "id": "cme8bwgwu000707lbawc6fz3a", + "name": "Muda" + }, + { + "id": "cme8hnx09000b07jl3ipifb1k", + "name": "Dewasa" + }, + { + "id": "cme8ho7dv000c07jlc7lr4b4w", + "name": "Lansia" + } +] \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index fbbe82dd..410178b8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -50,23 +50,22 @@ model AppMenuChild { // ========================================= FILE STORAGE ========================================= // model FileStorage { - id String @id @default(cuid()) - name String @unique - realName String - path String - mimeType String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - deletedAt DateTime? - isActive Boolean @default(true) - link String - category String // "image" / "document" / "other" - Berita Berita[] - PotensiDesa PotensiDesa[] - Posyandu Posyandu[] - StrukturPPID StrukturPPID[] - GalleryFoto GalleryFoto[] - + id String @id @default(cuid()) + name String @unique + realName String + path String + mimeType String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime? + isActive Boolean @default(true) + link String + category String // "image" / "document" / "other" + Berita Berita[] + PotensiDesa PotensiDesa[] + Posyandu Posyandu[] + StrukturPPID StrukturPPID[] + GalleryFoto GalleryFoto[] Pelapor Pelapor[] Penghargaan Penghargaan[] ProfileDesaImage ProfileDesaImage[] @@ -98,10 +97,8 @@ model FileStorage { APBDesImage APBDes[] @relation("APBDesImage") APBDesFile APBDes[] @relation("APBDesFile") PrestasiDesa PrestasiDesa[] - - DataPerpustakaan DataPerpustakaan[] - - PegawaiPPID PegawaiPPID[] + DataPerpustakaan DataPerpustakaan[] + PegawaiPPID PegawaiPPID[] } //========================================= MENU LANDING PAGE ========================================= // @@ -221,6 +218,53 @@ model KategoriPrestasiDesa { PrestasiDesa PrestasiDesa[] } +//========================================= INDEKS KEPUASAAN MASYARAKAT ========================================= // +model Responden { + id String @id @default(cuid()) + name String @unique + tanggal DateTime // misal: 2025-05-01 + jenisKelamin JenisKelaminResponden @relation(fields: [jenisKelaminId], references: [id]) + jenisKelaminId String + rating PilihanRatingResponden @relation(fields: [ratingId], references: [id]) + ratingId String + kelompokUmur UmurResponden @relation(fields: [kelompokUmurId], references: [id]) + kelompokUmurId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} + +model JenisKelaminResponden { + id String @id @default(cuid()) + name String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) + Responden Responden[] +} + +model PilihanRatingResponden { + id String @id @default(cuid()) + name String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) + Responden Responden[] +} + +model UmurResponden { + id String @id @default(cuid()) + name String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) + Responden Responden[] +} + //========================================= MENU PPID ========================================= // //========================================= STRUKTUR PPID ========================================= // diff --git a/prisma/seed.ts b/prisma/seed.ts index a6ffdb36..574eaef9 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -16,6 +16,9 @@ import potensi from "./data/list-potensi.json"; import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json"; import profilePPID from "./data/ppid/profile-ppid/profilePPid.json"; import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json"; +import jenisKelamin from "./data/ppid/ikm/jenis-kelamin/jenis-kelamin.json"; +import pilihanRatingResponden from "./data/ppid/ikm/pilihan-rating-responden/rating-responden.json"; +import umurResponden from "./data/ppid/ikm/umur-responden/umur-responden.json"; import pelayananPerizinanBerusaha from "./data/desa/layanan/pelayananPerizinanBerusaha.json"; import pelayananPendudukNonPermanen from "./data/desa/layanan/pelayanaPendudukNonPermanen.json"; import sejarahDesa from "./data/desa/profile/sejarah_desa.json"; @@ -546,6 +549,54 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak } console.log("visi misi PPID success ..."); + for (const j of jenisKelamin) { + await prisma.jenisKelaminResponden.upsert({ + where: { + id: j.id, + }, + update: { + name: j.name, + }, + create: { + id: j.id, + name: j.name, + }, + }); + } + console.log("jenis kelamin responden success ..."); + + for (const r of pilihanRatingResponden) { + await prisma.pilihanRatingResponden.upsert({ + where: { + id: r.id, + }, + update: { + name: r.name, + }, + create: { + id: r.id, + name: r.name, + }, + }); + } + console.log("pilihan rating responden success ..."); + + for (const u of umurResponden) { + await prisma.umurResponden.upsert({ + where: { + id: u.id, + }, + update: { + name: u.name, + }, + create: { + id: u.id, + name: u.name, + }, + }); + } + console.log("umur responden success ..."); + for (const v of dasarHukumPPID) { await prisma.dasarHukumPPID.upsert({ where: { diff --git a/src/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan.ts b/src/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan.ts new file mode 100644 index 00000000..d503b30a --- /dev/null +++ b/src/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan.ts @@ -0,0 +1,788 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import ApiFetch from "@/lib/api-fetch"; +import { Prisma } from "@prisma/client"; +import { toast } from "react-toastify"; +import { proxy } from "valtio"; +import { z } from "zod"; + +// Template form responden + +const templateResponden = z.object({ + name: z.string().min(1, "Nama harus diisi"), + tanggal: z.string().min(1, "Tanggal harus diisi"), + jenisKelaminId: z.string().min(1, "Jenis kelamin harus diisi"), + ratingId: z.string().min(1, "Rating harus diisi"), + kelompokUmurId: z.string().min(1, "Kelompok umur harus diisi"), +}); + +const defaultFormResponden = { + name: "", + tanggal: "", + jenisKelaminId: "", + ratingId: "", + kelompokUmurId: "", +}; + +const responden = proxy({ + create: { + form: { ...defaultFormResponden }, + loading: false, + async create() { + const cek = templateResponden.safeParse(responden.create.form); + if (!cek.success) { + const err = cek.error.issues.map((i) => i.message).join("\n"); + toast.error(err); + return; + } + + try { + responden.create.loading = true; + const res = await ApiFetch.api.landingpage.responden["create"].post( + responden.create.form + ); + if (res.status === 200) { + toast.success("Responden berhasil ditambahkan"); + await responden.findMany.load(); + } else { + toast.error(res.data?.message ?? "Gagal tambah responden"); + } + } catch (error) { + console.error("Gagal create:", error); + toast.error("Terjadi kesalahan saat menambahkan responden"); + } finally { + responden.create.loading = false; + } + }, + }, + findMany: { + data: null as any[] | null, + page: 1, + totalPages: 1, + total: 0, + loading: false, + load: async (page = 1, limit = 10) => { + // Change to arrow function + responden.findMany.loading = true; // Use the full path to access the property + responden.findMany.page = page; + try { + const res = await ApiFetch.api.landingpage.responden["findMany"].get({ + query: { page, limit }, + }); + + if (res.status === 200 && res.data?.success) { + responden.findMany.data = res.data.data || []; + responden.findMany.total = res.data.total || 0; + responden.findMany.totalPages = res.data.totalPages || 1; + } else { + console.error("Failed to load responden:", res.data?.message); + responden.findMany.data = []; + responden.findMany.total = 0; + responden.findMany.totalPages = 1; + } + } catch (error) { + console.error("Error loading responden:", error); + responden.findMany.data = []; + responden.findMany.total = 0; + responden.findMany.totalPages = 1; + } finally { + responden.findMany.loading = false; + } + }, + }, + findUnique: { + data: null as Prisma.RespondenGetPayload<{ + include: { + jenisKelamin: true; + rating: true; + kelompokUmur: true; + }; + }> | null, + async load(id: string) { + try { + const res = await fetch(`/api/landingpage/responden/${id}`); + if (res.ok) { + const data = await res.json(); + responden.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + responden.findUnique.data = null; + } + } catch (error) { + console.error("Error loading responden:", error); + responden.findUnique.data = null; + } + }, + }, + update: { + id: "", + form: { ...defaultFormResponden }, + loading: false, + async byId() { + // Method implementation if needed + }, + async submit() { + const id = this.id; + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + const cek = templateResponden.safeParse(this.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return null; + } + this.loading = true; + try { + const response = await fetch(`/api/landingpage/responden/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(this.form), + }); + const result = await response.json(); + if (!response.ok || !result?.success) { + throw new Error(result?.message || "Gagal update data"); + } + toast.success("Berhasil update data!"); + await responden.findMany.load(); + return result.data; + } catch (error) { + console.error("Error update data:", error); + toast.error("Gagal update data responden"); + } finally { + this.loading = false; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + responden.delete.loading = true; + + const response = await fetch(`/api/landingpage/responden/del/${id}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "responden berhasil dihapus"); + await responden.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus responden"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus responden"); + } finally { + responden.delete.loading = false; + } + }, + }, +}); + +// Template form jenis kelamin responden +const templateJenisKelaminResponden = z.object({ + name: z.string().min(1, "Nama harus diisi"), +}); + +const defaultFormJenisKelaminResponden = { + name: "", +}; + +const jenisKelaminResponden = proxy({ + create: { + form: { ...defaultFormJenisKelaminResponden }, + loading: false, + async create() { + const cek = templateJenisKelaminResponden.safeParse( + jenisKelaminResponden.create.form + ); + if (!cek.success) { + const err = cek.error.issues.map((i) => i.message).join("\n"); + toast.error(err); + return; + } + jenisKelaminResponden.create.loading = true; + try { + jenisKelaminResponden.create.loading = true; + const res = await ApiFetch.api.landingpage.jeniskelaminresponden[ + "create" + ].post(jenisKelaminResponden.create.form); + if (res.status === 200) { + toast.success("Jenis kelamin responden berhasil ditambahkan"); + await jenisKelaminResponden.findMany.load(); + } else { + toast.error( + res.data?.message ?? "Gagal tambah jenis kelamin responden" + ); + } + } catch (error) { + console.error("Gagal create:", error); + toast.error( + "Terjadi kesalahan saat menambahkan jenis kelamin responden" + ); + } finally { + jenisKelaminResponden.create.loading = false; + } + }, + }, + findMany: { + data: null as any[] | null, + page: 1, + totalPages: 1, + total: 0, + loading: false, + load: async (page = 1, limit = 10) => { + // Change to arrow function + jenisKelaminResponden.findMany.loading = true; // Use the full path to access the property + jenisKelaminResponden.findMany.page = page; + try { + const res = await ApiFetch.api.landingpage.jeniskelaminresponden[ + "findMany" + ].get({ + query: { page, limit }, + }); + + if (res.status === 200 && res.data?.success) { + jenisKelaminResponden.findMany.data = res.data.data || []; + jenisKelaminResponden.findMany.total = res.data.total || 0; + jenisKelaminResponden.findMany.totalPages = res.data.totalPages || 1; + } else { + console.error( + "Failed to load jenis kelamin responden:", + res.data?.message + ); + jenisKelaminResponden.findMany.data = []; + jenisKelaminResponden.findMany.total = 0; + jenisKelaminResponden.findMany.totalPages = 1; + } + } catch (error) { + console.error("Error loading jenis kelamin responden:", error); + jenisKelaminResponden.findMany.data = []; + jenisKelaminResponden.findMany.total = 0; + jenisKelaminResponden.findMany.totalPages = 1; + } finally { + jenisKelaminResponden.findMany.loading = false; + } + }, + }, + findUnique: { + data: null as Prisma.JenisKelaminRespondenGetPayload<{ + omit: { + isActive: true; + }; + }> | null, + async load(id: string) { + try { + const res = await fetch(`/api/landingpage/jeniskelaminresponden/${id}`); + if (res.ok) { + const data = await res.json(); + jenisKelaminResponden.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + jenisKelaminResponden.findUnique.data = null; + } + } catch (error) { + console.error("Error loading jenis kelamin responden:", error); + jenisKelaminResponden.findUnique.data = null; + } + }, + }, + update: { + id: "", + form: { ...defaultFormJenisKelaminResponden }, + loading: false, + async byId() { + // Method implementation if needed + }, + async submit() { + const id = this.id; + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + const cek = templateJenisKelaminResponden.safeParse(this.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return null; + } + this.loading = true; + try { + const response = await fetch( + `/api/landingpage/jeniskelaminresponden/${id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(this.form), + } + ); + const result = await response.json(); + if (!response.ok || !result?.success) { + throw new Error(result?.message || "Gagal update data"); + } + toast.success("Berhasil update data!"); + await jenisKelaminResponden.findMany.load(); + return result.data; + } catch (error) { + console.error("Error update data:", error); + toast.error("Gagal update data jenis kelamin responden"); + } finally { + this.loading = false; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + jenisKelaminResponden.delete.loading = true; + + const response = await fetch( + `/api/landingpage/jeniskelaminresponden/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success( + result.message || "jenis kelamin responden berhasil dihapus" + ); + await jenisKelaminResponden.findMany.load(); // refresh list + } else { + toast.error( + result?.message || "Gagal menghapus jenis kelamin responden" + ); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus jenis kelamin responden"); + } finally { + jenisKelaminResponden.delete.loading = false; + } + }, + }, +}); + +// Template form pilihan rating responden + +const templatePilihanRatingResponden = z.object({ + name: z.string().min(1, "Nama harus diisi"), +}); + +const defaultFormPilihanRatingResponden = { + name: "", +}; + +const pilihanRatingResponden = proxy({ + create: { + form: { ...defaultFormPilihanRatingResponden }, + loading: false, + async create() { + const cek = templatePilihanRatingResponden.safeParse( + pilihanRatingResponden.create.form + ); + if (!cek.success) { + const err = cek.error.issues.map((i) => i.message).join("\n"); + toast.error(err); + return; + } + pilihanRatingResponden.create.loading = true; + try { + pilihanRatingResponden.create.loading = true; + const res = await ApiFetch.api.landingpage.pilihanratingresponden[ + "create" + ].post(pilihanRatingResponden.create.form); + if (res.status === 200) { + toast.success("Jenis kelamin responden berhasil ditambahkan"); + await pilihanRatingResponden.findMany.load(); + } else { + toast.error( + res.data?.message ?? "Gagal tambah jenis kelamin responden" + ); + } + } catch (error) { + console.error("Gagal create:", error); + toast.error( + "Terjadi kesalahan saat menambahkan jenis kelamin responden" + ); + } finally { + pilihanRatingResponden.create.loading = false; + } + }, + }, + findMany: { + data: null as any[] | null, + page: 1, + totalPages: 1, + total: 0, + loading: false, + load: async (page = 1, limit = 10) => { + // Change to arrow function + pilihanRatingResponden.findMany.loading = true; // Use the full path to access the property + pilihanRatingResponden.findMany.page = page; + try { + const res = await ApiFetch.api.landingpage.pilihanratingresponden[ + "findMany" + ].get({ + query: { page, limit }, + }); + + if (res.status === 200 && res.data?.success) { + pilihanRatingResponden.findMany.data = res.data.data || []; + pilihanRatingResponden.findMany.total = res.data.total || 0; + pilihanRatingResponden.findMany.totalPages = res.data.totalPages || 1; + } else { + console.error( + "Failed to load pilihan rating responden:", + res.data?.message + ); + pilihanRatingResponden.findMany.data = []; + pilihanRatingResponden.findMany.total = 0; + pilihanRatingResponden.findMany.totalPages = 1; + } + } catch (error) { + console.error("Error loading pilihan rating responden:", error); + pilihanRatingResponden.findMany.data = []; + pilihanRatingResponden.findMany.total = 0; + pilihanRatingResponden.findMany.totalPages = 1; + } finally { + pilihanRatingResponden.findMany.loading = false; + } + }, + }, + findUnique: { + data: null as Prisma.PilihanRatingRespondenGetPayload<{ + omit: { + isActive: true; + }; + }> | null, + async load(id: string) { + try { + const res = await fetch(`/api/landingpage/pilihanratingresponden/${id}`); + if (res.ok) { + const data = await res.json(); + pilihanRatingResponden.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + pilihanRatingResponden.findUnique.data = null; + } + } catch (error) { + console.error("Error loading pilihan rating responden:", error); + pilihanRatingResponden.findUnique.data = null; + } + }, + }, + update: { + id: "", + form: { ...defaultFormPilihanRatingResponden }, + loading: false, + async byId() { + // Method implementation if needed + }, + async submit() { + const id = this.id; + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + const cek = templatePilihanRatingResponden.safeParse(this.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return null; + } + this.loading = true; + try { + const response = await fetch( + `/api/landingpage/pilihanratingresponden/${id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(this.form), + } + ); + const result = await response.json(); + if (!response.ok || !result?.success) { + throw new Error(result?.message || "Gagal update data"); + } + toast.success("Berhasil update data!"); + await pilihanRatingResponden.findMany.load(); + return result.data; + } catch (error) { + console.error("Error update data:", error); + toast.error("Gagal update data pilihan rating responden"); + } finally { + this.loading = false; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + pilihanRatingResponden.delete.loading = true; + + const response = await fetch( + `/api/landingpage/pilihanratingresponden/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success( + result.message || "pilihan rating responden berhasil dihapus" + ); + await pilihanRatingResponden.findMany.load(); // refresh list + } else { + toast.error( + result?.message || "Gagal menghapus pilihan rating responden" + ); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus pilihan rating responden"); + } finally { + pilihanRatingResponden.delete.loading = false; + } + }, + }, +}); + +// Template form kelompok umur responden + +const templateKelompokUmurResponden = z.object({ + name: z.string().min(1, "Nama harus diisi"), +}); + +const defaultFormKelompokUmurResponden = { + name: "", +}; + +const kelompokUmurResponden = proxy({ + create: { + form: { ...defaultFormKelompokUmurResponden }, + loading: false, + async create() { + const cek = templateKelompokUmurResponden.safeParse( + kelompokUmurResponden.create.form + ); + if (!cek.success) { + const err = cek.error.issues.map((i) => i.message).join("\n"); + toast.error(err); + return; + } + kelompokUmurResponden.create.loading = true; + try { + kelompokUmurResponden.create.loading = true; + const res = await ApiFetch.api.landingpage.umurresponden["create"].post( + kelompokUmurResponden.create.form + ); + if (res.status === 200) { + toast.success("Kelompok umur responden berhasil ditambahkan"); + await kelompokUmurResponden.findMany.load(); + } else { + toast.error( + res.data?.message ?? "Gagal tambah kelompok umur responden" + ); + } + } catch (error) { + console.error("Gagal create:", error); + toast.error( + "Terjadi kesalahan saat menambahkan kelompok umur responden" + ); + } finally { + kelompokUmurResponden.create.loading = false; + } + }, + }, + findMany: { + data: null as any[] | null, + page: 1, + totalPages: 1, + total: 0, + loading: false, + load: async (page = 1, limit = 10) => { + // Change to arrow function + kelompokUmurResponden.findMany.loading = true; // Use the full path to access the property + kelompokUmurResponden.findMany.page = page; + try { + const res = await ApiFetch.api.landingpage.umurresponden[ + "findMany" + ].get({ + query: { page, limit }, + }); + + if (res.status === 200 && res.data?.success) { + kelompokUmurResponden.findMany.data = res.data.data || []; + kelompokUmurResponden.findMany.total = res.data.total || 0; + kelompokUmurResponden.findMany.totalPages = res.data.totalPages || 1; + } else { + console.error( + "Failed to load kelompok umur responden:", + res.data?.message + ); + kelompokUmurResponden.findMany.data = []; + kelompokUmurResponden.findMany.total = 0; + kelompokUmurResponden.findMany.totalPages = 1; + } + } catch (error) { + console.error("Error loading kelompok umur responden:", error); + kelompokUmurResponden.findMany.data = []; + kelompokUmurResponden.findMany.total = 0; + kelompokUmurResponden.findMany.totalPages = 1; + } finally { + kelompokUmurResponden.findMany.loading = false; + } + }, + }, + findUnique: { + data: null as Prisma.UmurRespondenGetPayload<{ + omit: { + isActive: true; + }; + }> | null, + async load(id: string) { + try { + const res = await fetch(`/api/landingpage/umurresponden/${id}`); + if (res.ok) { + const data = await res.json(); + kelompokUmurResponden.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + kelompokUmurResponden.findUnique.data = null; + } + } catch (error) { + console.error("Error loading kelompok umur responden:", error); + kelompokUmurResponden.findUnique.data = null; + } + }, + }, + update: { + id: "", + form: { ...defaultFormKelompokUmurResponden }, + loading: false, + async byId() { + // Method implementation if needed + }, + async submit() { + const id = this.id; + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + const cek = templateKelompokUmurResponden.safeParse(this.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return null; + } + this.loading = true; + try { + const response = await fetch(`/api/landingpage/umurresponden/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(this.form), + }); + const result = await response.json(); + if (!response.ok || !result?.success) { + throw new Error(result?.message || "Gagal update data"); + } + toast.success("Berhasil update data!"); + await kelompokUmurResponden.findMany.load(); + return result.data; + } catch (error) { + console.error("Error update data:", error); + toast.error("Gagal update data kelompok umur responden"); + } finally { + this.loading = false; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + kelompokUmurResponden.delete.loading = true; + + const response = await fetch( + `/api/landingpage/umurresponden/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success( + result.message || "kelompok umur responden berhasil dihapus" + ); + await kelompokUmurResponden.findMany.load(); // refresh list + } else { + toast.error( + result?.message || "Gagal menghapus kelompok umur responden" + ); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus kelompok umur responden"); + } finally { + kelompokUmurResponden.delete.loading = false; + } + }, + }, +}); + +const indeksKepuasanState = proxy({ + responden, + kelompokUmurResponden, + jenisKelaminResponden, + pilihanRatingResponden +}) + +export default indeksKepuasanState \ No newline at end of file diff --git a/src/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin.ts b/src/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin.ts index 19539fdc..0e302bc6 100644 --- a/src/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin.ts +++ b/src/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin.ts @@ -8,7 +8,7 @@ import { z } from "zod"; const templateGrafikJenisKelamin = z.object({ laki: z.string().min(1, "Data laki-laki harus diisi"), perempuan: z.string().min(1, "Data perempuan harus diisi"), -}); +}); const defaultForm = { laki: "", @@ -17,10 +17,12 @@ const defaultForm = { const grafikBerdasarkanJenisKelamin = proxy({ create: { - form: {...defaultForm}, + form: { ...defaultForm }, loading: false, - async create(){ - const cek = templateGrafikJenisKelamin.safeParse(grafikBerdasarkanJenisKelamin.create.form); + async create() { + const cek = templateGrafikJenisKelamin.safeParse( + grafikBerdasarkanJenisKelamin.create.form + ); if (!cek.success) { const err = cek.error.issues.map((i) => i.message).join("\n"); toast.error(err); @@ -33,14 +35,20 @@ const grafikBerdasarkanJenisKelamin = proxy({ "create" ].post(grafikBerdasarkanJenisKelamin.create.form); if (res.status === 200) { - toast.success("Grafik berdasarkan jenis kelamin berhasil ditambahkan"); + toast.success( + "Grafik berdasarkan jenis kelamin berhasil ditambahkan" + ); await grafikBerdasarkanJenisKelamin.findMany.load(); } else { - toast.error(res.data?.message ?? "Gagal tambah grafik berdasarkan jenis kelamin"); + toast.error( + res.data?.message ?? "Gagal tambah grafik berdasarkan jenis kelamin" + ); } } catch (error) { console.error("Gagal create:", error); - toast.error("Terjadi kesalahan saat menambahkan grafik berdasarkan jenis kelamin"); + toast.error( + "Terjadi kesalahan saat menambahkan grafik berdasarkan jenis kelamin" + ); } finally { grafikBerdasarkanJenisKelamin.create.loading = false; } @@ -52,8 +60,9 @@ const grafikBerdasarkanJenisKelamin = proxy({ totalPages: 1, total: 0, loading: false, - load: async (page = 1, limit = 10) => { // Change to arrow function - grafikBerdasarkanJenisKelamin.findMany.loading = true; // Use the full path to access the property + load: async (page = 1, limit = 10) => { + // Change to arrow function + grafikBerdasarkanJenisKelamin.findMany.loading = true; // Use the full path to access the property grafikBerdasarkanJenisKelamin.findMany.page = page; try { const res = await ApiFetch.api.ppid.grafikberdasarkanjeniskelamin[ @@ -61,13 +70,17 @@ const grafikBerdasarkanJenisKelamin = proxy({ ].get({ query: { page, limit }, }); - + if (res.status === 200 && res.data?.success) { grafikBerdasarkanJenisKelamin.findMany.data = res.data.data || []; grafikBerdasarkanJenisKelamin.findMany.total = res.data.total || 0; - grafikBerdasarkanJenisKelamin.findMany.totalPages = res.data.totalPages || 1; + grafikBerdasarkanJenisKelamin.findMany.totalPages = + res.data.totalPages || 1; } else { - console.error("Failed to load grafik berdasarkan jenis kelamin:", res.data?.message); + console.error( + "Failed to load grafik berdasarkan jenis kelamin:", + res.data?.message + ); grafikBerdasarkanJenisKelamin.findMany.data = []; grafikBerdasarkanJenisKelamin.findMany.total = 0; grafikBerdasarkanJenisKelamin.findMany.totalPages = 1; @@ -106,7 +119,7 @@ const grafikBerdasarkanJenisKelamin = proxy({ }, update: { id: "", - form: {...defaultForm}, + form: { ...defaultForm }, loading: false, async byId() { // Method implementation if needed @@ -119,20 +132,24 @@ const grafikBerdasarkanJenisKelamin = proxy({ } const cek = templateGrafikJenisKelamin.safeParse(this.form); if (!cek.success) { - const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`; + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; toast.error(err); return null; } this.loading = true; try { const response = await fetch( - `/api/ppid/grafikberdasarkanjeniskelamin/${id}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(this.form), - }); + `/api/ppid/grafikberdasarkanjeniskelamin/${id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(this.form), + } + ); const result = await response.json(); if (!response.ok || !result?.success) { throw new Error(result?.message || "Gagal update data"); @@ -156,29 +173,40 @@ const grafikBerdasarkanJenisKelamin = proxy({ try { grafikBerdasarkanJenisKelamin.delete.loading = true; - const response = await fetch(`/api/ppid/grafikberdasarkanjeniskelamin/del/${id}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - }); + const response = await fetch( + `/api/ppid/grafikberdasarkanjeniskelamin/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); const result = await response.json(); if (response.ok && result?.success) { - toast.success(result.message || "Grafik berdasarkan jenis kelamin berhasil dihapus"); + toast.success( + result.message || + "Grafik berdasarkan jenis kelamin berhasil dihapus" + ); await grafikBerdasarkanJenisKelamin.findMany.load(); // refresh list } else { - toast.error(result?.message || "Gagal menghapus grafik berdasarkan jenis kelamin"); + toast.error( + result?.message || + "Gagal menghapus grafik berdasarkan jenis kelamin" + ); } } catch (error) { console.error("Gagal delete:", error); - toast.error("Terjadi kesalahan saat menghapus grafik berdasarkan jenis kelamin"); + toast.error( + "Terjadi kesalahan saat menghapus grafik berdasarkan jenis kelamin" + ); } finally { grafikBerdasarkanJenisKelamin.delete.loading = false; } }, - } + }, }); export default grafikBerdasarkanJenisKelamin; diff --git a/src/app/admin/(dashboard)/ppid/_com/layoutTabs.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/_lib/layoutTabs.tsx similarity index 62% rename from src/app/admin/(dashboard)/ppid/_com/layoutTabs.tsx rename to src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/_lib/layoutTabs.tsx index 6003362d..fc16deb2 100644 --- a/src/app/admin/(dashboard)/ppid/_com/layoutTabs.tsx +++ b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/_lib/layoutTabs.tsx @@ -5,30 +5,21 @@ 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 }) { +function LayoutTabsIKM({ children }: { children: React.ReactNode }) { const router = useRouter() const pathname = usePathname() const tabs = [ { - label: "Grafik Hasil Kepuasan Masyarakat", - value: "grafikhasilkepuasamanmasyarakat", - href: "/admin/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat" + label: "Indeks Kepuasan Masyarakat", + value: "indekskepuasannamasyarakat", + href: "/admin/ppid/ikm-desa-darmasaba/indeks-kepuasan-masyarakat" }, { - label: "Grafik Berdasarkan Jenis Kelamin Responden", - value: "grafikberdasarkanjeniskelaminresponden", - href: "/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden" + label: "Responden", + value: "responden", + href: "/admin/ppid/ikm-desa-darmasaba/responden" }, - { - label: "Grafik Berdasarkan Pilihan Responden", - value: "grafikberdasarkanpilihanresponden", - href: "/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden" - }, - { - label: "Grafik Berdasarkan Umur Responden", - value: "grafikberdasarkanumurresponden", - href: "/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur" - } + ]; const curentTab = tabs.find(tab => tab.href === pathname) const [activeTab, setActiveTab] = useState(curentTab?.value || tabs[0].value); @@ -50,7 +41,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) { return ( - Indeks Kepuasan Masyarakat (IKM) Desa Darmasaba + IKM Desa Darmasaba {tabs.map((e, i) => ( @@ -69,4 +60,4 @@ function LayoutTabs({ children }: { children: React.ReactNode }) { ); } -export default LayoutTabs; \ No newline at end of file +export default LayoutTabsIKM; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/[id]/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/[id]/page.tsx deleted file mode 100644 index e3c00ade..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/[id]/page.tsx +++ /dev/null @@ -1,78 +0,0 @@ -'use client' -/* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect } from 'react'; -import { useRouter, useParams } from 'next/navigation'; -import { useProxy } from 'valtio/utils'; -import grafikBerdasarkanJenisKelamin from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin'; -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Title, TextInput } from '@mantine/core'; -import { IconArrowBack } from '@tabler/icons-react'; - -function EditGrafikBerdasarkanJenisKelaminResponden() { - const router = useRouter() - const params = useParams() as { id: string } - const stategrafikBerdasarkanJenisKelamin = useProxy(grafikBerdasarkanJenisKelamin) - const id = params.id - - useEffect(() => { - if(id){ - stategrafikBerdasarkanJenisKelamin.findUnique.load(id).then(() => { - const data = stategrafikBerdasarkanJenisKelamin.findUnique.data - if(data){ - stategrafikBerdasarkanJenisKelamin.update.form = { - laki: data.laki || '', - perempuan: data.perempuan || '', - } - } - }) - } - }, [id]) - - const handleSubmit = async () => { - stategrafikBerdasarkanJenisKelamin.update.id = id; - await stategrafikBerdasarkanJenisKelamin.update.submit(); - router.push('/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden') - } - - return ( - - - - - - - Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik - { - stategrafikBerdasarkanJenisKelamin.update.form.laki = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanJenisKelamin.update.form.perempuan = val.currentTarget.value; - }} - /> - - - - - ); -} - -export default EditGrafikBerdasarkanJenisKelaminResponden; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/create/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/create/page.tsx deleted file mode 100644 index 1b18f995..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/create/page.tsx +++ /dev/null @@ -1,83 +0,0 @@ -'use client' -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import React from 'react'; -import { useRouter } from 'next/navigation'; -import grafikBerdasarkanJenisKelamin from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin'; -import { useProxy } from 'valtio/utils'; -import { useState } from 'react'; -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Title, TextInput } from '@mantine/core'; -import { IconArrowBack } from '@tabler/icons-react'; - -function GrafikBerdasarkanJenisKelaminRespondenCreate() { - const router = useRouter(); - const stategrafikBerdasarkanJenisKelamin = useProxy(grafikBerdasarkanJenisKelamin) - const [donutData, setDonutData] = useState([]); - - const resetForm = () => { - stategrafikBerdasarkanJenisKelamin.create.form = { - ...stategrafikBerdasarkanJenisKelamin.create.form, - laki: "", - perempuan: "", - } - } - - const handleSubmit = async () => { - try { - const id = await stategrafikBerdasarkanJenisKelamin.create.create(); - if (typeof id !== 'undefined') { - const idStr = String(id); - await stategrafikBerdasarkanJenisKelamin.findUnique.load(idStr); - if (stategrafikBerdasarkanJenisKelamin.findUnique.data) { - setDonutData([stategrafikBerdasarkanJenisKelamin.findUnique.data]); - } - } - resetForm(); - router.push("/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden"); - } catch (error) { - console.error('Error submitting form:', error); - } - } - return ( - - - - - - - Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik - { - stategrafikBerdasarkanJenisKelamin.create.form.laki = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanJenisKelamin.create.form.perempuan = val.currentTarget.value; - }} - /> - - - - - ); -} - -export default GrafikBerdasarkanJenisKelaminRespondenCreate; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/page.tsx deleted file mode 100644 index c0295249..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden/page.tsx +++ /dev/null @@ -1,227 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -'use client' -import grafikBerdasarkanJenisKelamin from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin'; -import colors from '@/con/colors'; -import { Box, Button, Center, Flex, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core'; -import { useShallowEffect } from '@mantine/hooks'; -import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; -import { Cell, Pie, PieChart } from 'recharts'; -import { useProxy } from 'valtio/utils'; -import HeaderSearch from '../../../_com/header'; -import JudulList from '../../../_com/judulList'; -import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; - -function GrafikBerdasarkanJenisKelamin() { - const [search, setSearch] = useState(""); - return ( - - } - value={search} - onChange={(e) => setSearch(e.currentTarget.value)} - /> - - - ); -} - -function ListGrafikBerdasarkanJenisKelamin({ search }: { search: string }) { - const stategrafikBerdasarkanJenisKelamin = useProxy(grafikBerdasarkanJenisKelamin) - const [donutData, setDonutData] = useState([]); - const [mounted, setMounted] = useState(false); - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) - const router = useRouter(); - const { data, page, totalPages, loading, load } = stategrafikBerdasarkanJenisKelamin.findMany - - - useShallowEffect(() => { - setMounted(true); - load(page, 10) - }, [page]); - - useEffect(() => { - if (data) { - const totalLaki = data.reduce((acc: number, cur: any) => acc + Number(cur.laki || 0), 0); - const totalPerempuan = data.reduce((acc: number, cur: any) => acc + Number(cur.perempuan || 0), 0); - setDonutData([ - { name: 'laki', value: totalLaki, color: colors['blue-button'], key: 'laki' }, - { name: 'perempuan', value: totalPerempuan, color: '#10A85AFF', key: 'perempuan' } - ]); - } - }, [data]) - - const filteredData = (data || []).filter(item => { - const keyword = search.toLowerCase(); - return ( - item.laki.toString().toLowerCase().includes(keyword) || - item.perempuan.toString().toLowerCase().includes(keyword) - ); - }); - - const handleDelete = async () => { - if (selectedId) { - await grafikBerdasarkanJenisKelamin.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - - stategrafikBerdasarkanJenisKelamin.findMany.load() - } - } - - if (loading || !data) { - return ( - - - - ); - } - if (data.length === 0) { - return ( - - - - - - - - No - Laki-laki - Perempuan - Edit - Delete - - -
- Tidak ada data berdasarkan jenis kelamin responden yang tersedia -
-
-
- ); - } - - return ( - - - - - - - - No - Laki-laki - Perempuan - Edit - Delete - - - - {filteredData.length === 0 ? ( - - - Belum ada data grafik responden - - - ) : ( - filteredData.map((item, index) => ( - - {index + 1} - {item.laki} - {item.perempuan} - - - - - - - - )) - )} - -
-
-
- { - load(newPage, 10); - window.scrollTo(0, 0); - }} - total={totalPages} - mt="md" - mb="md" - /> -
- - {/* Chart */} - - - - Grafik Berdasarkan Jenis Kelamin Responden - {mounted && donutData.length === 0 ? (Belum ada data untuk ditampilkan dalam grafik) : ( - - - {donutData.map((entry, index) => ( - - ))} - - - - - Laki-laki: {donutData.find((entry) => entry.name === 'laki')?.value} - - - - Perempuan: {donutData.find((entry) => entry.name === 'perempuan')?.value} - - - )} - - - -
- - {/* Modal Konfirmasi Hapus */} - setModalHapus(false)} - onConfirm={handleDelete} - text='Apakah anda yakin ingin menghapus grafik berdasarkan hasil responden ini?' - /> -
- ); -} - -export default GrafikBerdasarkanJenisKelamin; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/[id]/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/[id]/page.tsx deleted file mode 100644 index beed61a7..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/[id]/page.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -'use client' -import React, { useEffect } from 'react'; -import { useRouter, useParams } from 'next/navigation'; -import { useProxy } from 'valtio/utils'; -import grafikBerdasarkanResponden from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanResponden'; -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Title, TextInput } from '@mantine/core'; -import { IconArrowBack } from '@tabler/icons-react'; - -function EditGrafikBerdasarkanResponden() { - const router = useRouter() - const params = useParams() as { id: string } - const stateGrafikResponden = useProxy(grafikBerdasarkanResponden) - const id = params.id - - useEffect(() => { - if(id){ - stateGrafikResponden.findUnique.load(id).then(() => { - const data = stateGrafikResponden.findUnique.data - if(data){ - stateGrafikResponden.update.form = { - sangatbaik: data.sangatbaik || '', - baik: data.baik || '', - kurangbaik: data.kurangbaik || '', - tidakbaik: data.tidakbaik || '', - } - } - }) - } - }, [id]) - - const handleSubmit = async () => { - stateGrafikResponden.update.id = id; - await stateGrafikResponden.update.submit(); - router.push('/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden') - } - - return ( - - - - - - - Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik - { - stateGrafikResponden.update.form.sangatbaik = val.currentTarget.value; - }} - /> - { - stateGrafikResponden.update.form.baik = val.currentTarget.value; - }} - /> - { - stateGrafikResponden.update.form.kurangbaik = val.currentTarget.value; - }} - /> - { - stateGrafikResponden.update.form.tidakbaik = val.currentTarget.value; - }} - /> - - - - - ); -} - -export default EditGrafikBerdasarkanResponden; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/create/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/create/page.tsx deleted file mode 100644 index 6a0e4875..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/create/page.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -'use client' -import grafikBerdasarkanResponden from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanResponden'; -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, TextInput, Title } from '@mantine/core'; -import { IconArrowBack } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { useState } from 'react'; -import { useProxy } from 'valtio/utils'; - -function GrafikBerdasarkanRespondenCreate() { - const router = useRouter() - const stategrafikBerdasarkanResponden = useProxy(grafikBerdasarkanResponden) - const [donutData, setDonutData] = useState([]); - - const resetForm = () => { - stategrafikBerdasarkanResponden.create.form = { - ...stategrafikBerdasarkanResponden.create.form, - sangatbaik: "", - baik: "", - kurangbaik: "", - tidakbaik: "", - } - } - - const handleSubmit = async () => { - const id = await stategrafikBerdasarkanResponden.create.create(); - if (id) { - const idStr = String(id); - await stategrafikBerdasarkanResponden.findUnique.load(idStr); - if (stategrafikBerdasarkanResponden.findUnique.data) { - setDonutData([stategrafikBerdasarkanResponden.findUnique.data]); - } - } - resetForm(); - router.push("/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden") - } - return ( - - - - - - - Grafik Hasil Kepuasan Masyarakat Berdasarkan Responden - { - stategrafikBerdasarkanResponden.create.form.sangatbaik = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanResponden.create.form.baik = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanResponden.create.form.kurangbaik = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanResponden.create.form.tidakbaik = val.currentTarget.value; - }} - /> - - - - - ); -} - -export default GrafikBerdasarkanRespondenCreate; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/page.tsx deleted file mode 100644 index d8eee7fb..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden/page.tsx +++ /dev/null @@ -1,251 +0,0 @@ -'use client' -/* eslint-disable @typescript-eslint/no-explicit-any */ -import colors from '@/con/colors'; -import { Box, Button, Center, Flex, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core'; -import { useShallowEffect } from '@mantine/hooks'; -import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; -import { Cell, Pie, PieChart } from 'recharts'; -import { useSnapshot } from 'valtio'; -import HeaderSearch from '../../../_com/header'; -import JudulList from '../../../_com/judulList'; -import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; -import grafikBerdasarkanResponden from '../../../_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanResponden'; - -function GrafikBerdasarkanResponden() { - const [search, setSearch] = useState(""); - return ( - - } - value={search} - onChange={(e) => setSearch(e.currentTarget.value)} - /> - - - ); -} - -function ListGrafikBerdasarkanResponden({ search }: { search: string }) { - const stategrafikBerdasarkanResponden = useSnapshot(grafikBerdasarkanResponden) - const [donutData, setDonutData] = useState([]); - const [mounted, setMounted] = useState(false); - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) - const router = useRouter(); - - const { data, page, totalPages, loading, load } = stategrafikBerdasarkanResponden.findMany - - useShallowEffect(() => { - setMounted(true) - load(page, 10) - }, [page]) - - const filteredData = (data || []).filter(item => { - const keyword = search.toLowerCase(); - return ( - item.sangatbaik.toString().toLowerCase().includes(keyword) || - item.baik.toString().toLowerCase().includes(keyword) || - item.kurangbaik.toString().toLowerCase().includes(keyword) || - item.tidakbaik.toString().toLowerCase().includes(keyword) - ); - }); - - useEffect(() => { - if (data) { - const totalSangatBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.sangatbaik || 0), 0); - const totalBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.baik || 0), 0); - const totalKurangBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.kurangbaik || 0), 0); - const totalTidakBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.tidakbaik || 0), 0); - setDonutData([ - { name: 'sangatbaik', value: totalSangatBaik, color: colors['blue-button'], key: 'sangatbaik' }, - { name: 'baik', value: totalBaik, color: '#10A85AFF', key: 'baik' }, - { name: 'kurangbaik', value: totalKurangBaik, color: '#B3AA12FF', key: 'kurangbaik' }, - { name: 'tidakbaik', value: totalTidakBaik, color: '#B21313FF', key: 'tidakbaik' } - ]); - } - - }, [data]) - - const handleDelete = async () => { - if (selectedId) { - await stategrafikBerdasarkanResponden.delete.byId(selectedId); - setModalHapus(false); - setSelectedId(null); - - // Refresh data agar chart & tabel ikut update - stategrafikBerdasarkanResponden.findMany.load(); - } - } - - if (loading || !data) { - return ( - - - - ); - } - if (data.length === 0) { - return ( - - - - - - - - No - Sangat Baik - Baik - Kurang Baik - Tidak Baik - Edit - Delete - - -
- Tidak ada data grafik berdasarkan responden yang tersedia -
-
-
- ); - } - return ( - - - - - - - - No - Sangat Baik - Baik - Kurang Baik - Tidak Baik - Edit - Delete - - - - {filteredData.length === 0 ? ( - - - Belum ada data grafik responden - - - ) : ( - filteredData.map((item, index) => ( - - {index + 1} - {item.sangatbaik} - {item.baik} - {item.kurangbaik} - {item.tidakbaik} - - - - - - - - )) - )} - - -
-
-
- { - load(newPage, 10); - window.scrollTo(0, 0); - }} - total={totalPages} - mt="md" - mb="md" - /> -
- - {/* Chart */} - - - - Grafik Berdasarkan Pilihan Responden - {mounted && donutData.length > 0 ? ( - - - {donutData.map((entry, index) => ( - - ))} - - - - - Sangat Baik: {donutData.find((entry) => entry.name === 'sangatbaik')?.value} - - - - Baik: {donutData.find((entry) => entry.name === 'baik')?.value} - - - - Kurang Baik: {donutData.find((entry) => entry.name === 'kurangbaik')?.value} - - - - Tidak Baik: {donutData.find((entry) => entry.name === 'tidakbaik')?.value} - - - ) : ( - Belum ada data untuk ditampilkan dalam grafik - )} - - - -
- - {/* Modal Konfirmasi Hapus */} - setModalHapus(false)} - onConfirm={handleDelete} - text='Apakah anda yakin ingin menghapus grafik berdasarkan hasil responden ini?' - /> -
- ); -} - -export default GrafikBerdasarkanResponden; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/[id]/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/[id]/page.tsx deleted file mode 100644 index 373150fa..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/[id]/page.tsx +++ /dev/null @@ -1,97 +0,0 @@ -'use client' -/* eslint-disable react-hooks/exhaustive-deps */ -import grafikBerdasarkanUmur from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanUmur'; -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Title, TextInput } from '@mantine/core'; -import { IconArrowBack } from '@tabler/icons-react'; -import { useParams, useRouter } from 'next/navigation'; -import React, { useEffect } from 'react'; -import { useProxy } from 'valtio/utils'; - -function EditGrafikBerdasarakanUmur() { - const router = useRouter() - const params = useParams() as { id: string } - const stategrafikBerdasarkanUmur = useProxy(grafikBerdasarkanUmur) - const id = params.id - - useEffect(() => { - if(id){ - stategrafikBerdasarkanUmur.findUnique.load(id).then(() => { - const data = stategrafikBerdasarkanUmur.findUnique.data - if(data){ - stategrafikBerdasarkanUmur.update.form = { - remaja: data.remaja || '', - dewasa: data.dewasa || '', - orangtua: data.orangtua || '', - lansia: data.lansia || '', - } - } - }) - } - }, [id]) - - const handleSubmit = async () => { - stategrafikBerdasarkanUmur.update.id = id; - await stategrafikBerdasarkanUmur.update.submit(); - router.push('/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur') - } - return ( - - - - - - - Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik - { - stategrafikBerdasarkanUmur.update.form.remaja = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanUmur.update.form.dewasa = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanUmur.update.form.orangtua = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanUmur.update.form.lansia = val.currentTarget.value; - }} - /> - - - - - ); -} - -export default EditGrafikBerdasarakanUmur; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/create/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/create/page.tsx deleted file mode 100644 index e84e7c3e..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/create/page.tsx +++ /dev/null @@ -1,98 +0,0 @@ -'use client' -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import grafikBerdasarkanUmur from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanUmur'; -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Title, TextInput } from '@mantine/core'; -import { IconArrowBack } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import React, { useState } from 'react'; -import { useProxy } from 'valtio/utils'; - -function GrafikBerdasarakanUmurCreate() { - const stategrafikBerdasarkanUmur = useProxy(grafikBerdasarkanUmur) - const [donutData, setDonutData] = useState([]); - const router = useRouter() - - const resetForm = () => { - stategrafikBerdasarkanUmur.create.form = { - ...stategrafikBerdasarkanUmur.create.form, - remaja: "", - dewasa: "", - orangtua: "", - lansia: "", - } - } - - const handleSubmit = async () => { - const id = await stategrafikBerdasarkanUmur.create.create(); - if (id) { - const idStr = String(id); - await stategrafikBerdasarkanUmur.findUnique.load(idStr); - if (stategrafikBerdasarkanUmur.findUnique.data) { - setDonutData([stategrafikBerdasarkanUmur.findUnique.data]); - } - } - resetForm(); - router.push("/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur") - } - return ( - - - - - - - Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik - { - stategrafikBerdasarkanUmur.create.form.remaja = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanUmur.create.form.dewasa = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanUmur.create.form.orangtua = val.currentTarget.value; - }} - /> - { - stategrafikBerdasarkanUmur.create.form.lansia = val.currentTarget.value; - }} - /> - - - - - ); -} - -export default GrafikBerdasarakanUmurCreate; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/page.tsx deleted file mode 100644 index 3902edfd..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur/page.tsx +++ /dev/null @@ -1,252 +0,0 @@ -'use client' -/* eslint-disable @typescript-eslint/no-explicit-any */ -import grafikBerdasarkanUmur from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanUmur'; -import colors from '@/con/colors'; -import { Box, Button, Center, Flex, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core'; -import { useShallowEffect } from '@mantine/hooks'; -import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; -import { Cell, Pie, PieChart } from 'recharts'; -import { useProxy } from 'valtio/utils'; -import JudulListTab from '../../../_com/judulListTab'; -import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; -import HeaderSearch from '../../../_com/header'; -import JudulList from '../../../_com/judulList'; - -function GrafikBerdasarakanUmur() { - const [search, setSearch] = useState(""); - return ( - - } - value={search} - onChange={(e) => setSearch(e.currentTarget.value)} - /> - - - ); -} - -function ListGrafikBerdasarakanUmur({ search }: { search: string }) { - const stategrafikBerdasarkanUmur = useProxy(grafikBerdasarkanUmur) - const [donutData, setDonutData] = useState([]); - const [mounted, setMounted] = useState(false); - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) - const router = useRouter() - const { data, page, totalPages, loading, load } = stategrafikBerdasarkanUmur.findMany - - useShallowEffect(() => { - setMounted(true); - load(page, 10) - }, [page]); - - useEffect(() => { - if (data) { - const totalRemaja = data.reduce((acc: number, cur: any) => acc + Number(cur.remaja || 0), 0); - const totalDewasa = data.reduce((acc: number, cur: any) => acc + Number(cur.dewasa || 0), 0); - const totalOrangtua = data.reduce((acc: number, cur: any) => acc + Number(cur.orangtua || 0), 0); - const totalLansia = data.reduce((acc: number, cur: any) => acc + Number(cur.lansia || 0), 0); - setDonutData([ - { name: 'remaja', value: totalRemaja, color: colors['blue-button'], key: 'remaja' }, - { name: 'dewasa', value: totalDewasa, color: '#D32711FF', key: 'dewasa' }, - { name: 'orangtua', value: totalOrangtua, color: '#B46B04FF', key: 'orangtua' }, - { name: 'lansia', value: totalLansia, color: '#038617FF', key: 'lansia' } - ]); - } - }, [data]) - - const filteredData = (data || []).filter(item => { - const keyword = search.toLowerCase(); - return ( - item.remaja.toString().toLowerCase().includes(keyword) || - item.dewasa.toString().toLowerCase().includes(keyword) || - item.orangtua.toString().toLowerCase().includes(keyword) || - item.lansia.toString().toLowerCase().includes(keyword) - ); - }); - - const handleDelete = async () => { - if (selectedId) { - await grafikBerdasarkanUmur.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - - stategrafikBerdasarkanUmur.findMany.load() - } - } - - if (loading || !data) { - return ( - - - - ); - } - if (data.length === 0) { - return ( - - - - - - - - No - Remaja - Dewasa - Orangtua - Lansia - Edit - Delete - - -
- Tidak ada data grafik berdasarkan umur responden yang tersedia -
-
-
- ); - } - - return ( - - - - } - /> - - - - No - Remaja - Dewasa - Orangtua - Lansia - Edit - Delete - - - - {filteredData.length === 0 ? ( - - - Belum ada data grafik responden - - - ) : ( - filteredData.map((item) => ( - - {filteredData.indexOf(item) + 1} - {item.remaja} - {item.dewasa} - {item.orangtua} - {item.lansia} - - - - - - - - )) - )} - - -
-
-
- { - load(newPage, 10); - window.scrollTo(0, 0); - }} - total={totalPages} - mt="md" - mb="md" - /> -
- - {/* Chart */} - - - - Grafik Umur Berdasarkan Responden - {mounted && donutData.length > 0 ? ( - - - {donutData.map((entry, index) => ( - - ))} - - - - - Remaja: {donutData.find((entry) => entry.name === 'remaja')?.value} - - - - Dewasa: {donutData.find((entry) => entry.name === 'dewasa')?.value} - - - - Orangtua: {donutData.find((entry) => entry.name === 'orangtua')?.value} - - - - Lansia: {donutData.find((entry) => entry.name === 'lansia')?.value} - - - ) : ( - Belum ada data untuk ditampilkan dalam grafik - )} - - - -
- - {/* Modal Konfirmasi Hapus */} - setModalHapus(false)} - onConfirm={handleDelete} - text='Apakah anda yakin ingin menghapus grafik berdasarkan hasil responden ini?' - /> -
- ); -} - -export default GrafikBerdasarakanUmur; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/[id]/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/[id]/page.tsx deleted file mode 100644 index 09676dcb..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/[id]/page.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -'use client' - -import grafikHasilKepuasanMasyarakat from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikHasilKepuasan'; -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, TextInput, Title } from '@mantine/core'; -import { IconArrowBack } from '@tabler/icons-react'; -import { useRouter, useParams } from 'next/navigation'; -import { useEffect } from 'react'; -import { useProxy } from 'valtio/utils'; - -function EditGrafikHasilKepuasan() { - const router = useRouter() - const params = useParams() as { id: string } - const grafikHasilKepuasan = useProxy(grafikHasilKepuasanMasyarakat) - - const id = params.id - - // Load data saat komponen mount - useEffect(() => { - if (id) { - grafikHasilKepuasan.findUnique.load(id).then(() => { - const data = grafikHasilKepuasan.findUnique.data - if (data) { - grafikHasilKepuasan.update.form = { - label: data.label || '', - kepuasan: data.kepuasan || '', - } - } - }) - } - }, [id]) - - const handleSubmit = async () => { - // Set the ID before submitting - grafikHasilKepuasan.update.id = id; - await grafikHasilKepuasan.update.submit(); - router.push('/admin/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat') - } - - return ( - - - - - - - Edit Grafik Hasil Kepuasan Masyarakat - { - grafikHasilKepuasan.update.form.label = val.currentTarget.value; - }} - /> - { - grafikHasilKepuasan.update.form.kepuasan = val.currentTarget.value; - }} - /> - - - - - ) -} - -export default EditGrafikHasilKepuasan; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/create/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/create/page.tsx deleted file mode 100644 index 6947d435..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/create/page.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -'use client' - -import grafikHasilKepuasanMasyarakat from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikHasilKepuasan'; -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, TextInput, Title } from '@mantine/core'; -import { IconArrowBack } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { useState } from 'react'; -import { useProxy } from 'valtio/utils'; - - -function GrafikHasilKepuasan() { - const router = useRouter() - const grafikHasilKepuasan = useProxy(grafikHasilKepuasanMasyarakat) - const [chartData, setChartData] = useState([]); - - -const resetForm = () => { - grafikHasilKepuasan.create.form = { - ...grafikHasilKepuasan.create.form, - label: "", - kepuasan: "", - } -} - -const handleSubmit = async () => { - const id = await grafikHasilKepuasan.create.create(); - if (id) { - // Ensure id is a string - const idStr = String(id); - await grafikHasilKepuasan.findUnique.load(idStr); - if (grafikHasilKepuasan.findUnique.data) { - setChartData([grafikHasilKepuasan.findUnique.data]); - } - } - resetForm(); - router.push("/admin/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat") -} - return ( - - - - - - - - Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik - { - grafikHasilKepuasan.create.form.label = val.currentTarget.value; - }} - /> - { - grafikHasilKepuasan.create.form.kepuasan = val.currentTarget.value; - }} - /> - - - - - ); -} - - -export default GrafikHasilKepuasan; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/page.tsx deleted file mode 100644 index e27ffc2b..00000000 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat/page.tsx +++ /dev/null @@ -1,211 +0,0 @@ -'use client' -import colors from '@/con/colors'; -import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core'; -import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; -import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; -import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts'; -import { useSnapshot } from 'valtio'; -import HeaderSearch from '../../../_com/header'; -import JudulList from '../../../_com/judulList'; -import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; -import grafikHasilKepuasanMasyarakat from '../../../_state/ppid/indeks_kepuasan_masyarakat/grafikHasilKepuasan'; - - -function GrafikHasilKepuasanMasyarakat() { - const [search, setSearch] = useState(""); - return ( - - } - value={search} - onChange={(e) => setSearch(e.currentTarget.value)} - /> - - - ); -} - -function ListGrafikHasilKepuasanMasyarakat({ search }: { search: string }) { - type IKMGrafik = { - id: string; - label: string; - kepuasan: number; - } - - - const stateGrafikHasilKepuasan = useSnapshot(grafikHasilKepuasanMasyarakat) - const [mounted, setMounted] = useState(false); - const [chartData, setChartData] = useState([]); - const isTablet = useMediaQuery('(max-width: 1024px)') - const isMobile = useMediaQuery('(max-width: 768px)') - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) - const router = useRouter(); - - const { data, page, totalPages, loading, load } = stateGrafikHasilKepuasan.findMany - - - useShallowEffect(() => { - setMounted(true) - load(page, 10) - }, [page]) - - useEffect(() => { - if (data) { - setChartData( - data.map((item) => ({ - id: item.id, - label: item.label, - kepuasan: Number(item.kepuasan), - })) - ); - } - }, [data]); - - const filteredData = (data || []).filter(item => { - const keyword = search.toLowerCase(); - return ( - item.label.toLowerCase().includes(keyword) || - item.kepuasan.toString().toLowerCase().includes(keyword) - ); - }); - - const handleDelete = () => { - if (selectedId) { - stateGrafikHasilKepuasan.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - - stateGrafikHasilKepuasan.findMany.load() - } - } - - if (loading || !data) { - return ( - - - - ); - } - if (data.length === 0) { - return ( - - - - - - - - No - Label - Jumlah Kepuasan - Edit - Delete - - -
- Tidak ada data grafik hasil kepuasan masyarakat yang tersedia -
-
-
- ); - } - return ( - - - - - - - - No - Label - Jumlah Kepuasan - Edit - Delete - - - - {filteredData.map((item, index) => ( - - {index + 1} - {item.label} - {item.kepuasan} - - - - - - - - ))} - -
-
-
- { - load(newPage, 10); - window.scrollTo(0, 0); - }} - total={totalPages} - mt="md" - mb="md" - /> -
- - {/* Chart */} - - - - Grafik Hasil Kepuasan Masyarakat - {mounted && chartData.length > 0 ? ( - - - - - - - - ) : ( - Belum ada data untuk ditampilkan dalam grafik - )} - - - -
- - {/* Modal Konfirmasi Hapus */} - setModalHapus(false)} - onConfirm={handleDelete} - text='Apakah anda yakin ingin menghapus grafik hasil kepuasan masyarakat ini?' - /> -
- - ); -} - -export default GrafikHasilKepuasanMasyarakat; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/indeks-kepuasan-masyarakat/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/indeks-kepuasan-masyarakat/page.tsx new file mode 100644 index 00000000..29055b11 --- /dev/null +++ b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/indeks-kepuasan-masyarakat/page.tsx @@ -0,0 +1,215 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +'use client' +import React, { useEffect, useState } from 'react'; +import { Box, Center, Flex, Paper, Skeleton, Stack, Text, Title } from '@mantine/core'; +import colors from '@/con/colors'; +import { PieChart, Pie, Cell } from 'recharts'; +import { useShallowEffect } from '@mantine/hooks'; +import { useProxy } from 'valtio/utils'; +import indeksKepuasanState from '../../../_state/landing-page/indeks-kepuasan'; + +interface ChartDataItem { + name: string; + value: number; + color: string; + label?: string; +} + +function Page() { + const state = useProxy(indeksKepuasanState.responden); + const { data, loading } = state.findMany; + const [donutDataJenisKelamin, setDonutDataJenisKelamin] = useState([]); + const [donutDataRating, setDonutDataRating] = useState([]); + const [donutDataKelompokUmur, setDonutDataKelompokUmur] = useState([]); + const [mounted, setMounted] = useState(false); + + useShallowEffect(() => { + setMounted(true); + }, []); + + useEffect(() => { + if (data) { + // Hitung total berdasarkan jenis kelamin + const totalLaki = data.filter((item: any) => item.jenisKelamin?.name?.toLowerCase() === 'laki-laki').length; + const totalPerempuan = data.filter((item: any) => item.jenisKelamin?.name?.toLowerCase() === 'perempuan').length; + + // Hitung total berdasarkan rating + const totalSangatBaik = data.filter((item: any) => item.rating?.name?.toLowerCase() === 'sangat baik').length; + const totalBaik = data.filter((item: any) => item.rating?.name?.toLowerCase() === 'baik').length; + const totalKurangBaik = data.filter((item: any) => item.rating?.name?.toLowerCase() === 'kurang baik').length; + const totalSangatKurangBaik = data.filter((item: any) => item.rating?.name?.toLowerCase() === 'sangat kurang baik').length; + + // Hitung total berdasarkan kelompok umur + const totalMuda = data.filter((item: any) => item.kelompokUmur?.name?.toLowerCase() === 'muda').length; + const totalDewasa = data.filter((item: any) => item.kelompokUmur?.name?.toLowerCase() === 'dewasa').length; + const totalLansia = data.filter((item: any) => item.kelompokUmur?.name?.toLowerCase() === 'lansia').length; + // Update gender chart data + setDonutDataJenisKelamin([ + { name: 'laki', value: totalLaki, color: colors['blue-button'], label: 'Laki-laki' }, + { name: 'perempuan', value: totalPerempuan, color: '#10A85AFF', label: 'Perempuan' } + ]); + + // Update rating chart data + setDonutDataRating([ + { name: 'sangat_baik', value: totalSangatBaik, color: colors['blue-button'], label: 'Sangat Baik' }, + { name: 'baik', value: totalBaik, color: '#10A85AFF', label: 'Baik' }, + { name: 'kurang_baik', value: totalKurangBaik, color: '#FFA500', label: 'Kurang Baik' }, + { name: 'sangat_kurang_baik', value: totalSangatKurangBaik, color: '#FF4500', label: 'Sangat Kurang Baik' } + ]); + + // Update age group chart data + setDonutDataKelompokUmur([ + { name: 'muda', value: totalMuda, color: colors['blue-button'], label: 'Muda' }, + { name: 'dewasa', value: totalDewasa, color: '#10A85AFF', label: 'Dewasa' }, + { name: 'lansia', value: totalLansia, color: '#FFA500', label: 'Lansia' } + ]); + } + }, [data]) + + if (loading || !data) { + return ( + + + + ); + } + + if (data.length === 0) { + return ( + + Belum ada data untuk ditampilkan + + ) + } + + return ( + + {/* Chart */} + + + + Grafik Berdasarkan Jenis Kelamin Responden + {mounted && donutDataJenisKelamin.length === 0 ? ( + Belum ada data untuk ditampilkan dalam grafik + ) : ( + +
+ + `${(percent * 100).toFixed(0)}%`} + labelLine={false} + > + {donutDataJenisKelamin.map((entry, index) => ( + + ))} + + +
+ + {donutDataJenisKelamin.map((entry) => ( + + + {entry.label}: {entry.value || 0} + + ))} + +
+ )} +
+
+
+ + {/* Rating Chart */} + + + Grafik Berdasarkan Rating Responden + {mounted && donutDataRating.length === 0 ? ( + Belum ada data untuk ditampilkan dalam grafik + ) : ( + +
+ + `${(percent * 100).toFixed(0)}%`} + labelLine={false} + > + {donutDataRating.map((entry, index) => ( + + ))} + + +
+ + {donutDataRating.map((entry) => ( + + + {entry.label}: {entry.value || 0} + + ))} + +
+ )} +
+
+
+ + {/* Age Group Chart */} + + + + Grafik Berdasarkan Kelompok Umur + {mounted && donutDataKelompokUmur.length === 0 ? ( + Belum ada data untuk ditampilkan dalam grafik + ) : ( + +
+ + `${(percent * 100).toFixed(0)}%`} + labelLine={false} + > + {donutDataKelompokUmur.map((entry, index) => ( + + ))} + + +
+ + {donutDataKelompokUmur.map((entry) => ( + + + {entry.label}: {entry.value || 0} + + ))} + +
+ )} +
+
+
+
+ ); +} + +export default Page; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/layout.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/layout.tsx index 0b71bb62..f94af0c1 100644 --- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/layout.tsx +++ b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/layout.tsx @@ -1,10 +1,13 @@ 'use client' -import LayoutTabs from "../_com/layoutTabs"; +import React from 'react'; +import LayoutTabsIKM from './_lib/layoutTabs'; -export default function Layout({children} : {children: React.ReactNode}) { - return ( - - {children} - - ) -} \ No newline at end of file +function Layout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} + +export default Layout; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/[id]/edit/page.tsx new file mode 100644 index 00000000..ecf1674a --- /dev/null +++ b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/[id]/edit/page.tsx @@ -0,0 +1,137 @@ +'use client' +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useEffect } from 'react'; +import { useRouter, useParams } from 'next/navigation'; +import { useProxy } from 'valtio/utils'; +import colors from '@/con/colors'; +import { Box, Button, Paper, Stack, Title, TextInput, Text, Select } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan'; + +function EditResponden() { + const router = useRouter() + const params = useParams() as { id: string } + const state = useProxy(indeksKepuasanState.responden) + const id = params.id + + useEffect(() => { + if (id) { + state.findUnique.load(id).then(() => { + const data = state.findUnique.data + if (data) { + state.update.form = { + name: data.name || '', + tanggal: data.tanggal ? new Date(data.tanggal).toISOString() : new Date().toISOString(), + jenisKelaminId: data.jenisKelaminId || '', + ratingId: data.ratingId || '', + kelompokUmurId: data.kelompokUmurId || '', + } + } + }) + } + }, [id]) + + const handleSubmit = async () => { + state.update.id = id; + await state.update.submit(); + router.push('/admin/ppid/ikm-desa-darmasaba/responden') + } + + return ( + + + + + + + Edit Responden + { + state.update.form.name = val.currentTarget.value; + }} + /> + { + state.update.form.tanggal = val.currentTarget.value; + }} + /> + { + state.update.form.ratingId = val || ""; + }} + label={Rating} + placeholder='Pilih rating' + data={ + indeksKepuasanState.pilihanRatingResponden.findMany.data?.map((v) => ({ + value: v.id, + label: v.nama + })) || [] + } + clearable + searchable + required + error={!state.update.form.ratingId ? "Pilih rating" : undefined} + /> + + { + stategrafikBerdasarkanResponden.create.form.jenisKelaminId = val ?? ""; + }} + data={ + (indeksKepuasanState.jenisKelaminResponden.findMany.data || []) + .filter(Boolean) // Hapus null, undefined, dll + .map((item) => ({ + value: item.id, + label: item.name || 'Tanpa Nama', + })) + } + disabled={indeksKepuasanState.jenisKelaminResponden.findMany.loading} + /> + { + stategrafikBerdasarkanResponden.create.form.kelompokUmurId = val ?? ""; + }} + data={ + (indeksKepuasanState.kelompokUmurResponden.findMany.data || []) + .filter(Boolean) // Hapus null, undefined, dll + .map((item) => ({ + value: item.id, + label: item.name || 'Tanpa Nama', + })) + } + disabled={indeksKepuasanState.kelompokUmurResponden.findMany.loading} + /> + + + + + ); +} + +export default RespondenCreate; diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/page.tsx new file mode 100644 index 00000000..85aa6fba --- /dev/null +++ b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/page.tsx @@ -0,0 +1,151 @@ +'use client'; +import colors from '@/con/colors'; +import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } 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 HeaderSearch from '../../../_com/header'; +import JudulList from '../../../_com/judulList'; +import indeksKepuasanState from '../../../_state/landing-page/indeks-kepuasan'; + +function Responden() { + const [search, setSearch] = useState(""); + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +interface ListRespondenProps { + search: string; +} + +function ListResponden({ search }: ListRespondenProps) { + const state = useProxy(indeksKepuasanState.responden); + const router = useRouter(); + const { data, page, totalPages, loading, load } = state.findMany; + + + useShallowEffect(() => { + load(page, 10) + }, [page]); + + + const filteredData = (data || []).filter(item => { + const keyword = search.toLowerCase(); + return ( + item.name.toLowerCase().includes(keyword) + ); + }); + + if (loading || !data) { + return ( + + + + ); + } + if (data.length === 0) { + return ( + + + + + + + + No + Nama + Tanggal + Jenis Kelamin + Detail + + +
+ Tidak ada data berdasarkan jenis kelamin responden yang tersedia +
+
+
+ ); + } + + return ( + + + + + + + + No + Nama + Tanggal + Jenis Kelamin + Detail + + + + {filteredData.length === 0 ? ( + + + Belum ada data responden + + + ) : ( + filteredData.map((item, index) => ( + + {index + 1} + {item.name} + {item.tanggal + ? new Date(item.tanggal).toLocaleDateString('id-ID') + : '-'} + {item.jenisKelamin.name} + + + + + )) + )} + +
+
+
+ { + load(newPage, 10); + window.scrollTo(0, 0); + }} + total={totalPages} + mt="md" + mb="md" + /> +
+ + +
+
+ ); +} + +export default Responden; + + diff --git a/src/app/admin/_com/list_PageAdmin.tsx b/src/app/admin/_com/list_PageAdmin.tsx index cfc1fca4..2f6f0650 100644 --- a/src/app/admin/_com/list_PageAdmin.tsx +++ b/src/app/admin/_com/list_PageAdmin.tsx @@ -79,7 +79,7 @@ export const navBar = [ { id: "PPID_8", name: "IKM Desa Darmasaba", - path: "/admin/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat" + path: "/admin/ppid/ikm-desa-darmasaba/indeks-kepuasan-masyarakat" }, ] diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/create.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/create.ts new file mode 100644 index 00000000..cb1562e9 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/create.ts @@ -0,0 +1,26 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + name: string; +} + +export default async function jenisKelaminRespondenCreate(context: Context) { + const body = (await context.body) as FormCreate; + + try { + const result = await prisma.jenisKelaminResponden.create({ + data: { + name: body.name, + }, + }); + return { + success: true, + message: "Berhasil membuat jenis kelamin responden", + data: result, + }; + } catch (error) { + console.error("Error creating jenis kelamin responden:", error); + throw new Error("Gagal membuat jenis kelamin responden: " + (error as Error).message); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/del.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/del.ts new file mode 100644 index 00000000..8aa9f0e7 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/del.ts @@ -0,0 +1,16 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function jenisKelaminRespondenDelete(context: Context) { + const id = context.params.id as string; + + await prisma.jenisKelaminResponden.delete({ + where: { id }, + }); + + return { + status: 200, + success: true, + message: "Success delete jenis kelamin responden", + }; +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/findMany.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/findMany.ts new file mode 100644 index 00000000..897198e6 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/findMany.ts @@ -0,0 +1,53 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// /api/berita/findManyPaginated.ts +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function jenisKelaminRespondenFindMany(context: Context) { + // Ambil parameter dari query + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const search = (context.query.search as string) || ''; + const skip = (page - 1) * limit; + + // Buat where clause + const where: any = { isActive: true }; + + // Tambahkan pencarian (jika ada) + if (search) { + where.OR = [ + { name: { contains: search, mode: 'insensitive' } }, + ]; + } + + try { + // Ambil data dan total count secara paralel + const [data, total] = await Promise.all([ + prisma.jenisKelaminResponden.findMany({ + where, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, + }), + prisma.jenisKelaminResponden.count({ where }), + ]); + + return { + success: true, + message: "Berhasil ambil jenis kelamin responden dengan pagination", + data, + page, + limit, + total, + totalPages: Math.ceil(total / limit), + }; + } catch (e) { + console.error("Error di findMany paginated:", e); + return { + success: false, + message: "Gagal mengambil data jenis kelamin responden", + }; + } +} + +export default jenisKelaminRespondenFindMany; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/findUnique.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/findUnique.ts new file mode 100644 index 00000000..fed0d350 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/findUnique.ts @@ -0,0 +1,46 @@ +import prisma from "@/lib/prisma"; + +export default async function jenisKelaminRespondenFindUnique(request: Request) { + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if (!id) { + return { + success: false, + message: "ID is required", + } + } + + try { + if (typeof id !== 'string') { + return { + success: false, + message: "ID is required", + } + } + + const data = await prisma.jenisKelaminResponden.findUnique({ + where: { id }, + }); + + if (!data) { + return { + success: false, + message: "Data not found", + } + } + + return { + success: true, + message: "Success get jenis kelamin responden", + data, + } + } catch (error) { + console.error("Find by ID error:", error); + return { + success: false, + message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'), + } + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/index.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/index.ts new file mode 100644 index 00000000..bb4bf25e --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/index.ts @@ -0,0 +1,33 @@ +import Elysia, { t } from "elysia"; +import jenisKelaminRespondenCreate from "./create"; +import jenisKelaminRespondenDelete from "./del"; +import jenisKelaminRespondenFindMany from "./findMany"; +import jenisKelaminRespondenFindUnique from "./findUnique"; +import jenisKelaminRespondenUpdate from "./updt"; + +const JenisKelaminResponden = new Elysia({ + prefix: "/jeniskelaminresponden", + tags: ["PPID / Indeks Kepuasan / Jenis Kelamin Responden"], +}) + + .post("/create", jenisKelaminRespondenCreate, { + body: t.Object({ + name: t.String(), + }), + }) + + .get("/findMany", jenisKelaminRespondenFindMany) + .get("/:id", async (context) => { + const response = await jenisKelaminRespondenFindUnique( + new Request(context.request) + ); + return response; + }) + .put("/:id", jenisKelaminRespondenUpdate, { + body: t.Object({ + name: t.String(), + }), + }) + .delete("/del/:id", jenisKelaminRespondenDelete); + +export default JenisKelaminResponden; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/updt.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/updt.ts new file mode 100644 index 00000000..50ad90b9 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/jenis-kelamin-responden/updt.ts @@ -0,0 +1,28 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormUpdate = { + name: string; +} + +export default async function jenisKelaminRespondenUpdate(context: Context) { + const body = (await context.body) as FormUpdate; + const id = context.params.id as string; + + try { + const result = await prisma.jenisKelaminResponden.update({ + where: { id }, + data: { + name: body.name, + }, + }); + return { + success: true, + message: "Berhasil mengupdate jenis kelamin responden", + data: result, + }; + } catch (error) { + console.error("Error updating jenis kelamin responden:", error); + throw new Error("Gagal mengupdate jenis kelamin responden: " + (error as Error).message); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/create.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/create.ts new file mode 100644 index 00000000..3ffa13eb --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/create.ts @@ -0,0 +1,26 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + name: string; +} + +export default async function pilihanRatingRespondenCreate(context: Context) { + const body = (await context.body) as FormCreate; + + try { + const result = await prisma.pilihanRatingResponden.create({ + data: { + name: body.name, + }, + }); + return { + success: true, + message: "Berhasil membuat pilihan rating responden", + data: result, + }; + } catch (error) { + console.error("Error creating pilihan rating responden:", error); + throw new Error("Gagal membuat pilihan rating responden: " + (error as Error).message); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/del.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/del.ts new file mode 100644 index 00000000..cefe67fc --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/del.ts @@ -0,0 +1,16 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function pilihanRatingRespondenDelete(context: Context) { + const id = context.params.id as string; + + await prisma.pilihanRatingResponden.delete({ + where: { id }, + }); + + return { + status: 200, + success: true, + message: "Success delete pilihan rating responden", + }; +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/findMany.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/findMany.ts new file mode 100644 index 00000000..8b0ee94c --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/findMany.ts @@ -0,0 +1,53 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// /api/berita/findManyPaginated.ts +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function pilihanRatingRespondenFindMany(context: Context) { + // Ambil parameter dari query + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const search = (context.query.search as string) || ''; + const skip = (page - 1) * limit; + + // Buat where clause + const where: any = { isActive: true }; + + // Tambahkan pencarian (jika ada) + if (search) { + where.OR = [ + { name: { contains: search, mode: 'insensitive' } }, + ]; + } + + try { + // Ambil data dan total count secara paralel + const [data, total] = await Promise.all([ + prisma.pilihanRatingResponden.findMany({ + where, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, + }), + prisma.pilihanRatingResponden.count({ where }), + ]); + + return { + success: true, + message: "Berhasil ambil pilihan rating responden dengan pagination", + data, + page, + limit, + total, + totalPages: Math.ceil(total / limit), + }; + } catch (e) { + console.error("Error di findMany paginated:", e); + return { + success: false, + message: "Gagal mengambil data pilihan rating responden", + }; + } +} + +export default pilihanRatingRespondenFindMany; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/findUnique.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/findUnique.ts new file mode 100644 index 00000000..213ed9af --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/findUnique.ts @@ -0,0 +1,46 @@ +import prisma from "@/lib/prisma"; + +export default async function pilihanRatingRespondenFindUnique(request: Request) { + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if (!id) { + return { + success: false, + message: "ID is required", + } + } + + try { + if (typeof id !== 'string') { + return { + success: false, + message: "ID is required", + } + } + + const data = await prisma.pilihanRatingResponden.findUnique({ + where: { id }, + }); + + if (!data) { + return { + success: false, + message: "Data not found", + } + } + + return { + success: true, + message: "Success get pilihan rating responden", + data, + } + } catch (error) { + console.error("Find by ID error:", error); + return { + success: false, + message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'), + } + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/index.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/index.ts new file mode 100644 index 00000000..5c492eeb --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/index.ts @@ -0,0 +1,33 @@ +import Elysia, { t } from "elysia"; +import pilihanRatingRespondenCreate from "./create"; +import pilihanRatingRespondenDelete from "./del"; +import pilihanRatingRespondenFindMany from "./findMany"; +import pilihanRatingRespondenFindUnique from "./findUnique"; +import pilihanRatingRespondenUpdate from "./updt"; + +const PilihanRatingResponden = new Elysia({ + prefix: "/pilihanratingresponden", + tags: ["PPID / Indeks Kepuasan / Pilihan Rating Responden"], +}) + + .post("/create", pilihanRatingRespondenCreate, { + body: t.Object({ + name: t.String(), + }), + }) + + .get("/findMany", pilihanRatingRespondenFindMany) + .get("/:id", async (context) => { + const response = await pilihanRatingRespondenFindUnique( + new Request(context.request) + ); + return response; + }) + .put("/:id", pilihanRatingRespondenUpdate, { + body: t.Object({ + name: t.String(), + }), + }) + .delete("/del/:id", pilihanRatingRespondenDelete); + +export default PilihanRatingResponden; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/updt.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/updt.ts new file mode 100644 index 00000000..0c511293 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/pilihan-rating-responden/updt.ts @@ -0,0 +1,28 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormUpdate = { + name: string; +} + +export default async function pilihanRatingRespondenUpdate(context: Context) { + const body = (await context.body) as FormUpdate; + const id = context.params.id as string; + + try { + const result = await prisma.pilihanRatingResponden.update({ + where: { id }, + data: { + name: body.name, + }, + }); + return { + success: true, + message: "Berhasil mengupdate pilihan rating responden", + data: result, + }; + } catch (error) { + console.error("Error updating pilihan rating responden:", error); + throw new Error("Gagal mengupdate pilihan rating responden: " + (error as Error).message); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/create.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/create.ts new file mode 100644 index 00000000..b1858818 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/create.ts @@ -0,0 +1,43 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + name: string; + tanggal: string; + jenisKelaminId: string; + ratingId: string; + kelompokUmurId: string; +} + +export default async function respondenCreate(context: Context) { + const body = (await context.body) as FormCreate; + + try { + // Convert the date string to a Date object + const tanggal = new Date(body.tanggal); + + // Validate the date + if (isNaN(tanggal.getTime())) { + throw new Error('Tanggal tidak valid'); + } + + const result = await prisma.responden.create({ + data: { + name: body.name, + tanggal: tanggal, // Use the Date object + jenisKelaminId: body.jenisKelaminId, + ratingId: body.ratingId, + kelompokUmurId: body.kelompokUmurId, + }, + }); + return { + success: true, + message: "Berhasil membuat responden", + data: result, + }; + } catch (error) { + console.error("Error creating responden:", error); + throw new Error("Gagal membuat responden: " + (error as Error).message); + } +} + \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/del.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/del.ts new file mode 100644 index 00000000..2e85b091 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/del.ts @@ -0,0 +1,33 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function respondenDelete(context: Context) { + const id = context.params?.id as string; + + const responden = await prisma.responden.findUnique({ + where: { id }, + include: { + jenisKelamin: true, + rating: true, + kelompokUmur: true, + }, + }); + + if (!responden) { + return { + status: 404, + success: false, + message: "Responden tidak ditemukan", + }; + } + + await prisma.responden.delete({ + where: { id }, + }); + + return { + status: 200, + success: true, + message: "Success delete responden", +}; +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/findMany.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/findMany.ts new file mode 100644 index 00000000..34660900 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/findMany.ts @@ -0,0 +1,61 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// /api/berita/findManyPaginated.ts +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function respondenFindMany(context: Context) { + // Ambil parameter dari query + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const search = (context.query.search as string) || ''; + const skip = (page - 1) * limit; + + // Buat where clause + const where: any = { isActive: true }; + + // Tambahkan pencarian (jika ada) + if (search) { + where.OR = [ + { tanggal: { contains: search, mode: 'insensitive' } }, + { jenisKelamin: { name: { contains: search, mode: 'insensitive' } } }, + { rating: { name: { contains: search, mode: 'insensitive' } } }, + { kelompokUmur: { name: { contains: search, mode: 'insensitive' } } } + ]; + } + + try { + // Ambil data dan total count secara paralel + const [data, total] = await Promise.all([ + prisma.responden.findMany({ + where, + include: { + jenisKelamin: true, + rating: true, + kelompokUmur: true, + }, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, + }), + prisma.responden.count({ where }), + ]); + + return { + success: true, + message: "Berhasil ambil responden dengan pagination", + data, + page, + limit, + total, + totalPages: Math.ceil(total / limit), + }; + } catch (e) { + console.error("Error di findMany paginated:", e); + return { + success: false, + message: "Gagal mengambil data responden", + }; + } +} + +export default respondenFindMany; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/findUnique.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/findUnique.ts new file mode 100644 index 00000000..bc48a35d --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/findUnique.ts @@ -0,0 +1,51 @@ +import prisma from "@/lib/prisma"; + +export default async function respondenFindUnique(request: Request) { + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if (!id) { + return { + success: false, + message: "ID is required", + } + } + + try { + if (typeof id !== 'string') { + return { + success: false, + message: "ID is required", + } + } + + const data = await prisma.responden.findUnique({ + where: { id }, + include: { + jenisKelamin: true, + rating: true, + kelompokUmur: true, + }, + }); + + if (!data) { + return { + success: false, + message: "Data not found", + } + } + + return { + success: true, + message: "Success fetch responden by ID", + data, + } + } catch (e) { + console.error("Find by ID error:", e); + return { + success: false, + message: "Gagal mengambil responden: " + (e instanceof Error ? e.message : 'Unknown error'), + } + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/index.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/index.ts new file mode 100644 index 00000000..0b057cb4 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/index.ts @@ -0,0 +1,42 @@ +import Elysia from "elysia"; +import { t } from "elysia"; +import respondenFindMany from "./findMany"; +import respondenFindUnique from "./findUnique"; +import respondenCreate from "./create"; +import respondenUpdate from "./updt"; +import respondenDelete from "./del"; + +const Responden = new Elysia({ prefix: "/responden", tags: ["Desa/Responden"] }) + .get("/findMany", respondenFindMany) + .get("/:id", async (context) => { + const response = await respondenFindUnique(new Request(context.request)); + return response; + }) + .post("/create", respondenCreate, { + body: t.Object({ + name: t.String(), + tanggal: t.String(), + jenisKelaminId: t.String(), + ratingId: t.String(), + kelompokUmurId: t.String(), + }), + }) + .delete("/del/:id", respondenDelete) + .put( + "/:id", + async (context) => { + const response = await respondenUpdate(context); + return response; + }, + { + body: t.Object({ + name: t.String(), + tanggal: t.String(), + jenisKelaminId: t.String(), + ratingId: t.String(), + kelompokUmurId: t.String(), + }), + } + ); + +export default Responden; diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/updt.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/updt.ts new file mode 100644 index 00000000..13252ae0 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/responden/updt.ts @@ -0,0 +1,36 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormUpdate = { + name: string; + tanggal: string; + jenisKelaminId: string; + ratingId: string; + kelompokUmurId: string; +} + +export default async function respondenUpdate(context: Context) { + const body = (await context.body) as FormUpdate; + const id = context.params.id as string; + + try { + const result = await prisma.responden.update({ + where: { id }, + data: { + name: body.name, + tanggal: body.tanggal, + jenisKelaminId: body.jenisKelaminId, + ratingId: body.ratingId, + kelompokUmurId: body.kelompokUmurId, + }, + }); + return { + success: true, + message: "Berhasil mengupdate responden", + data: result, + }; + } catch (error) { + console.error("Error updating responden:", error); + throw new Error("Gagal mengupdate responden: " + (error as Error).message); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/create.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/create.ts new file mode 100644 index 00000000..c4d9cf43 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/create.ts @@ -0,0 +1,26 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + name: string; +} + +export default async function umurRespondenCreate(context: Context) { + const body = (await context.body) as FormCreate; + + try { + const result = await prisma.umurResponden.create({ + data: { + name: body.name, + }, + }); + return { + success: true, + message: "Berhasil membuat umur responden", + data: result, + }; + } catch (error) { + console.error("Error creating umur responden:", error); + throw new Error("Gagal membuat umur responden: " + (error as Error).message); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/del.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/del.ts new file mode 100644 index 00000000..86a70751 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/del.ts @@ -0,0 +1,16 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function umurRespondenDelete(context: Context) { + const id = context.params.id as string; + + await prisma.umurResponden.delete({ + where: { id }, + }); + + return { + status: 200, + success: true, + message: "Success delete umur responden", + }; +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/findMany.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/findMany.ts new file mode 100644 index 00000000..c9c4cd2e --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/findMany.ts @@ -0,0 +1,53 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// /api/berita/findManyPaginated.ts +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function umurRespondenFindMany(context: Context) { + // Ambil parameter dari query + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const search = (context.query.search as string) || ''; + const skip = (page - 1) * limit; + + // Buat where clause + const where: any = { isActive: true }; + + // Tambahkan pencarian (jika ada) + if (search) { + where.OR = [ + { name: { contains: search, mode: 'insensitive' } }, + ]; + } + + try { + // Ambil data dan total count secara paralel + const [data, total] = await Promise.all([ + prisma.umurResponden.findMany({ + where, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, + }), + prisma.umurResponden.count({ where }), + ]); + + return { + success: true, + message: "Berhasil ambil umur responden dengan pagination", + data, + page, + limit, + total, + totalPages: Math.ceil(total / limit), + }; + } catch (e) { + console.error("Error di findMany paginated:", e); + return { + success: false, + message: "Gagal mengambil data umur responden", + }; + } +} + +export default umurRespondenFindMany; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/findUnique.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/findUnique.ts new file mode 100644 index 00000000..1393ba2a --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/findUnique.ts @@ -0,0 +1,46 @@ +import prisma from "@/lib/prisma"; + +export default async function umurRespondenFindUnique(request: Request) { + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if (!id) { + return { + success: false, + message: "ID is required", + } + } + + try { + if (typeof id !== 'string') { + return { + success: false, + message: "ID is required", + } + } + + const data = await prisma.umurResponden.findUnique({ + where: { id }, + }); + + if (!data) { + return { + success: false, + message: "Data not found", + } + } + + return { + success: true, + message: "Success get umur responden", + data, + } + } catch (error) { + console.error("Find by ID error:", error); + return { + success: false, + message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'), + } + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/index.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/index.ts new file mode 100644 index 00000000..87b7e4b1 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/index.ts @@ -0,0 +1,33 @@ +import Elysia, { t } from "elysia"; +import umurRespondenCreate from "./create"; +import umurRespondenDelete from "./del"; +import umurRespondenFindMany from "./findMany"; +import umurRespondenFindUnique from "./findUnique"; +import umurRespondenUpdate from "./updt"; + +const UmurResponden = new Elysia({ + prefix: "/umurresponden", + tags: ["PPID / Indeks Kepuasan / Umur Responden"], +}) + + .post("/create", umurRespondenCreate, { + body: t.Object({ + name: t.String(), + }), + }) + + .get("/findMany", umurRespondenFindMany) + .get("/:id", async (context) => { + const response = await umurRespondenFindUnique( + new Request(context.request) + ); + return response; + }) + .put("/:id", umurRespondenUpdate, { + body: t.Object({ + name: t.String(), + }), + }) + .delete("/del/:id", umurRespondenDelete); + +export default UmurResponden; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/updt.ts b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/updt.ts new file mode 100644 index 00000000..c00565ae --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/landing_page/indeks_kepuasan/umur-responden/updt.ts @@ -0,0 +1,28 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormUpdate = { + name: string; +} + +export default async function umurRespondenUpdate(context: Context) { + const body = (await context.body) as FormUpdate; + const id = context.params.id as string; + + try { + const result = await prisma.umurResponden.update({ + where: { id }, + data: { + name: body.name, + }, + }); + return { + success: true, + message: "Berhasil mengupdate umur responden", + data: result, + }; + } catch (error) { + console.error("Error updating umur responden:", error); + throw new Error("Gagal mengupdate umur responden: " + (error as Error).message); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/landing_page/index.ts b/src/app/api/[[...slugs]]/_lib/landing_page/index.ts index 7c524fe1..27238a4e 100644 --- a/src/app/api/[[...slugs]]/_lib/landing_page/index.ts +++ b/src/app/api/[[...slugs]]/_lib/landing_page/index.ts @@ -8,6 +8,10 @@ import SDGSDesa from "./sdgs-desa"; import APBDes from "./apbdes"; import PrestasiDesa from "./prestasi-desa"; import KategoriPrestasi from "./prestasi-desa/kategori-prestasi"; +import JenisKelaminResponden from "./indeks_kepuasan/jenis-kelamin-responden"; +import PilihanRatingResponden from "./indeks_kepuasan/pilihan-rating-responden"; +import UmurResponden from "./indeks_kepuasan/umur-responden"; +import Responden from "./indeks_kepuasan/responden"; const LandingPage = new Elysia({ prefix: "/api/landingpage", @@ -23,5 +27,9 @@ const LandingPage = new Elysia({ .use(APBDes) .use(PrestasiDesa) .use(KategoriPrestasi) +.use(JenisKelaminResponden) +.use(PilihanRatingResponden) +.use(UmurResponden) +.use(Responden) export default LandingPage diff --git a/src/app/darmasaba/(pages)/desa/berita/[kategori]/Content.tsx b/src/app/darmasaba/(pages)/desa/berita/[kategori]/Content.tsx index 0749adec..22deaa42 100644 --- a/src/app/darmasaba/(pages)/desa/berita/[kategori]/Content.tsx +++ b/src/app/darmasaba/(pages)/desa/berita/[kategori]/Content.tsx @@ -124,7 +124,7 @@ export default function Content({ kategori }: { kategori: string }) { p="lg" radius="md" withBorder - onClick={() => router.push(`/desa/berita/${item.id}`)} + onClick={() => router.push(`/darmasaba/desa/berita/${item.id}`)} style={{ cursor: 'pointer' }} > diff --git a/src/app/darmasaba/(pages)/desa/berita/semua/page.tsx b/src/app/darmasaba/(pages)/desa/berita/semua/page.tsx index 16921de6..37558539 100644 --- a/src/app/darmasaba/(pages)/desa/berita/semua/page.tsx +++ b/src/app/darmasaba/(pages)/desa/berita/semua/page.tsx @@ -94,7 +94,7 @@ function Semua() { @@ -155,7 +155,7 @@ function Semua() { })} - + ))} diff --git a/src/app/darmasaba/(pages)/desa/galery/_lib/searchBar.tsx b/src/app/darmasaba/(pages)/desa/galery/_lib/searchBar.tsx index f8ff0583..6e805525 100644 --- a/src/app/darmasaba/(pages)/desa/galery/_lib/searchBar.tsx +++ b/src/app/darmasaba/(pages)/desa/galery/_lib/searchBar.tsx @@ -4,8 +4,8 @@ import { TextInput } from '@mantine/core'; import { IconSearch } from '@tabler/icons-react'; -import { useRouter, useSearchParams } from 'next/navigation'; -import React, { useState } from 'react'; +import { usePathname, useSearchParams } from 'next/navigation'; +import { useEffect, useRef, useState } from 'react'; export type SearchBarProps = { placeholder?: string; @@ -16,31 +16,54 @@ export function SearchBar({ placeholder = "pencarian", searchIcon = , }: SearchBarProps) { - const router = useRouter(); const searchParams = useSearchParams(); // Get initial search value from URL const [searchValue, setSearchValue] = useState(searchParams.get('search') || ''); - // Handle search input change with debounce + const pathname = usePathname(); + const typingTimeoutRef = useRef(null); + const handleSearchChange = (event: React.ChangeEvent) => { const value = event.target.value; setSearchValue(value); - // Update URL with debounce - const params = new URLSearchParams(searchParams.toString()); - if (value) { - params.set('search', value); - } else { - params.delete('search'); + // Clear previous timeout + if (typingTimeoutRef.current) { + window.clearTimeout(typingTimeoutRef.current); } - // Only update URL if the search value has actually changed - if (params.toString() !== searchParams.toString()) { - router.push(`?${params.toString()}`); - } + // Set new timeout + typingTimeoutRef.current = window.setTimeout(() => { + const params = new URLSearchParams(searchParams.toString()); + + // Always reset to first page when search changes + params.set('page', '1'); + + if (value) { + params.set('search', value); + } else { + params.delete('search'); + } + + // Update URL without adding to history + const newUrl = `${pathname}?${params.toString()}`; + window.history.replaceState({ ...window.history.state, as: newUrl, url: newUrl }, '', newUrl); + + // Dispatch a custom event that content components can listen to + window.dispatchEvent(new CustomEvent('searchUpdate', { detail: { search: value } })); + }, 500); }; + // Clean up timeout on unmount + useEffect(() => { + return () => { + if (typingTimeoutRef.current) { + window.clearTimeout(typingTimeoutRef.current); + } + }; + }, []); + return ( { - setLoading(true); + // Handle search and pagination changes + const loadData = useCallback((pageNum: number, searchTerm: string) => { + setLoading(true); + // Using the load function from the component's scope + const loadFn = async () => { try { - const query: Record = { - category: 'image', - page: pageNum.toString(), - limit: limit.toString(), - }; - if (searchTerm) query.search = searchTerm; - - const response = await ApiFetch.api.fileStorage.findMany.get({ query }); + const response = await ApiFetch.api.fileStorage.findMany.get({ + query: { + category: 'image', + page: pageNum.toString(), + limit: '10', + ...(searchTerm && { search: searchTerm }) + } + }); if (response.status === 200 && response.data) { setFiles(response.data.data || []); @@ -49,14 +51,44 @@ interface FileItem { setLoading(false); } }; - - // ✅ Baca dari URL — AMAN karena ssr: false - useEffect(() => { + + loadFn(); + }, []); + + // Initial load and URL change handler + useEffect(() => { + const handleRouteChange = () => { const urlParams = new URLSearchParams(window.location.search); const urlSearch = urlParams.get('search') || ''; + const urlPage = parseInt(urlParams.get('page') || '1'); + setSearch(urlSearch); - load(1, 10, urlSearch.trim()); - }, []); + setPage(urlPage); + loadData(urlPage, urlSearch); + }; + + // Handle search updates from the search bar + const handleSearchUpdate = (e: Event) => { + const { search } = (e as CustomEvent).detail; + + setSearch(search); + setPage(1); // Reset to first page on new search + loadData(1, search); + }; + + // Initial load + handleRouteChange(); + + // Set up event listeners + window.addEventListener('popstate', handleRouteChange); + window.addEventListener('searchUpdate', handleSearchUpdate as EventListener); + + // Cleanup + return () => { + window.removeEventListener('popstate', handleRouteChange); + window.removeEventListener('searchUpdate', handleSearchUpdate as EventListener); + }; + }, [loadData]); // ✅ Fetch data useEffect(() => { diff --git a/src/app/darmasaba/(pages)/desa/galery/video/Content.tsx b/src/app/darmasaba/(pages)/desa/galery/video/Content.tsx index 2c084de0..2b74a462 100644 --- a/src/app/darmasaba/(pages)/desa/galery/video/Content.tsx +++ b/src/app/darmasaba/(pages)/desa/galery/video/Content.tsx @@ -1,34 +1,60 @@ -/* eslint-disable react-hooks/exhaustive-deps */ 'use client'; import stateGallery from '@/app/admin/(dashboard)/_state/desa/gallery'; import colors from '@/con/colors'; import { Box, Center, Pagination, Paper, SimpleGrid, Spoiler, Stack, Text } from '@mantine/core'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useSnapshot } from 'valtio'; export default function VideoContent() { const [expanded, setExpanded] = useState(false); - const [currentSearch, setCurrentSearch] = useState(''); const videoState = useSnapshot(stateGallery.video); const { data, page, totalPages, loading, - load, } = videoState.findMany; - // ✅ Baca dari URL hanya di client - useEffect(() => { - const urlParams = new URLSearchParams(window.location.search); - const urlSearch = urlParams.get('search') || ''; - setCurrentSearch(urlSearch); - load(1, 10, urlSearch.trim()); + // Handle search and pagination changes + const loadData = useCallback((pageNum: number, searchTerm: string) => { + stateGallery.video.findMany.load(pageNum, 10, searchTerm.trim()); }, []); + // Initial load and URL change handler + useEffect(() => { + const handleRouteChange = () => { + const urlParams = new URLSearchParams(window.location.search); + const urlSearch = urlParams.get('search') || ''; + const urlPage = parseInt(urlParams.get('page') || '1'); + + loadData(urlPage, urlSearch); + }; + + // Handle search updates from the search bar + const handleSearchUpdate = (e: Event) => { + const { search } = (e as CustomEvent).detail; + loadData(1, search); + }; + + // Initial load + handleRouteChange(); + + // Set up event listeners + window.addEventListener('popstate', handleRouteChange); + window.addEventListener('searchUpdate', handleSearchUpdate as EventListener); + + // Cleanup + return () => { + window.removeEventListener('popstate', handleRouteChange); + window.removeEventListener('searchUpdate', handleSearchUpdate as EventListener); + }; + }, [loadData]); + const handlePageChange = (newPage: number) => { - load(newPage, 10, currentSearch.trim()); + const params = new URLSearchParams(window.location.search); + const search = params.get('search') || ''; + loadData(newPage, search); }; const dataVideo = data || []; diff --git a/src/app/darmasaba/(pages)/desa/profile/page.tsx b/src/app/darmasaba/(pages)/desa/profile/page.tsx index 3d4925af..3ae4fdbd 100644 --- a/src/app/darmasaba/(pages)/desa/profile/page.tsx +++ b/src/app/darmasaba/(pages)/desa/profile/page.tsx @@ -7,7 +7,7 @@ import VisimisiDesa from './ui/visimisiDesa'; import LambangDesa from './ui/lambangDesa'; import MaskotDesa from './ui/maskotDesa'; import ProfilPerbekel from './ui/profilPerbekel'; -import LembagaDesa from './ui/lembagaDesa'; +// import LembagaDesa from './ui/lembagaDesa'; import MotoDesa from './ui/motoDesa'; import SemuaPerbekel from './ui/semuaPerbekel'; @@ -31,7 +31,7 @@ function Page() { - + {/* */} diff --git a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx index e1641ea9..f861c328 100644 --- a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx +++ b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx @@ -97,7 +97,7 @@ function PengaduanMasyarakat() { > - Ajukan Administrasi Online + Ajukan Pengaduan Masyarakat Nama} placeholder="masukkan nama" diff --git a/src/app/darmasaba/(pages)/pendidikan/data-pendidikan/page.tsx b/src/app/darmasaba/(pages)/pendidikan/data-pendidikan/page.tsx index eecba2a0..85932406 100644 --- a/src/app/darmasaba/(pages)/pendidikan/data-pendidikan/page.tsx +++ b/src/app/darmasaba/(pages)/pendidikan/data-pendidikan/page.tsx @@ -50,7 +50,7 @@ function Page() { data={data} dataKey="kategori" series={[ - { name: 'jumlah', color: 'violet.6' }, + { name: 'jumlah', color: colors['blue-button'] }, ]} tickLine="y" xAxisProps={{ diff --git a/src/app/darmasaba/(tambahan)/penghargaan/[id]/page.tsx b/src/app/darmasaba/(tambahan)/penghargaan/[id]/page.tsx index cb5cd9ff..ec29d9af 100644 --- a/src/app/darmasaba/(tambahan)/penghargaan/[id]/page.tsx +++ b/src/app/darmasaba/(tambahan)/penghargaan/[id]/page.tsx @@ -49,7 +49,7 @@ function Page() { return ( - + diff --git a/src/app/darmasaba/_com/main-page/kepuasan/index.tsx b/src/app/darmasaba/_com/main-page/kepuasan/index.tsx index 865784a9..56796d46 100644 --- a/src/app/darmasaba/_com/main-page/kepuasan/index.tsx +++ b/src/app/darmasaba/_com/main-page/kepuasan/index.tsx @@ -87,9 +87,8 @@ function Kepuasan() { Pelayanan Terhadap Publik Desa Darmasaba - 95.00 - Sangat Baik - Total 2500 responden + Total Responden + 2500 | null; +} + +function ProfileView({ data }: ProfileViewProps) { + if (!data) { + return
No profile data available
; + } + + return ( + + {data.image?.link && ( + {data.name + )} + + + {data.position} + + {data.name} + + + + + ); +} + +export default ProfileView; \ No newline at end of file diff --git a/src/app/darmasaba/_com/main-page/landing-page/index.tsx b/src/app/darmasaba/_com/main-page/landing-page/index.tsx index c5a09ea5..64160485 100644 --- a/src/app/darmasaba/_com/main-page/landing-page/index.tsx +++ b/src/app/darmasaba/_com/main-page/landing-page/index.tsx @@ -16,6 +16,7 @@ import { import { useEffect, useState } from "react"; import ModuleView from "./ModuleView"; import SosmedView from "./SosmedView"; +import ProfileView from "./ProfileView"; const getDayOfWeek = () => { const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu']; @@ -59,6 +60,7 @@ const getWorkStatus = (day: string, currentTime: string): { status: string; mess function LandingPage() { const [socialMedia, setSocialMedia] = useState[]>([]); + const [profile, setProfile] = useState | null>(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { @@ -87,7 +89,21 @@ function LandingPage() { } }; + const fetchProfile = async () => { + try { + const response = await fetch(`/api/landingpage/pejabatdesa/edit`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const result = await response.json(); + setProfile(result.data || null); // Handle single object response + } catch (error) { + console.error('Error fetching profile:', error); + setProfile(null); + } + }; fetchSocialMedia(); + fetchProfile(); }, []); const [workStatus, setWorkStatus] = useState<{ status: string; message: string }> @@ -296,45 +312,13 @@ function LandingPage() {
- - perbekel - - - Perbekel Desa Darmasaba - - I.B. Surya Prabhawa Manuaba, S.H.,M.H.,NL.P. - - - - + {isLoading ? ( + + ) : profile ? ( + + ) : ( +
No profile available
+ )}
);