Compare commits
14 Commits
nico/18-au
...
nico/23-au
| Author | SHA1 | Date | |
|---|---|---|---|
| bb8dab05ba | |||
| 3081e426bd | |||
| 8a275c2a32 | |||
| 8469ebd2e1 | |||
| 760ba4b6d2 | |||
| 20d4c90e60 | |||
| fafbb12a08 | |||
| 01aa0da5cc | |||
| b580978f8e | |||
| 1c01397c0d | |||
| 90a6605efd | |||
| c22d865283 | |||
| 49067f0218 | |||
| d79425d529 |
@@ -3,126 +3,108 @@
|
||||
"id": "cmds9h9ko000pvnberdjnx64b",
|
||||
"name": "1.1 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PERENCANAAN, PELAKSANAAN, PENATAUSAHAAN DAN PERTANGGUNG JAWABAN APBDES BESERTA IMPLEMENTASINYA",
|
||||
"deskripsi": "<p>ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PERENCANAAN, PELAKSANAAN, PENATAUSAHAAN DAN PERTANGGUNG JAWABAN APBDES BESERTA IMPLEMENTASINYA</p>",
|
||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh"
|
||||
},
|
||||
{
|
||||
"id": "cmds9sjmz000svnbesv2133of",
|
||||
"name": "1.2 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP MENGENAI MEKANISME EVALUASI KINERJA PERANGKAT DESA",
|
||||
"deskripsi": "<p>ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP MENGENAI MEKANISME EVALUASI KINERJA PERANGKAT DESA</p>",
|
||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh"
|
||||
},
|
||||
{
|
||||
"id": "cmds9tcpi000vvnbev3ebtlnt",
|
||||
"name": "1.3 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PENGENDALIAN GRATIFIKASI, SUAP DAN KONFLIK KEPENTINGAN",
|
||||
"deskripsi": "<p>ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PENGENDALIAN GRATIFIKASI, SUAP DAN KONFLIK KEPENTINGAN</p>",
|
||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh"
|
||||
},
|
||||
{
|
||||
"id": "cmds9twvj000yvnbep0pq8dzf",
|
||||
"name": "1.4 PERJANJIAN KERJA SAMA ANTARA PELAKSANA KEGIATAN ANGGARAN DENGAN PIHAK PENYEDIA, DAN TELAH MELALUI PROSES PENGADAAN BARANG/JASA DI DESA",
|
||||
"deskripsi": "<p>PERJANJIAN KERJA SAMA ANTARA PELAKSANA KEGIATAN ANGGARAN DENGAN PIHAK PENYEDIA, DAN TELAH MELALUI PROSES PENGADAAN BARANG/JASA DI DESA</p>",
|
||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh"
|
||||
},
|
||||
{
|
||||
"id": "cmds9ugap0011vnbe118yv871",
|
||||
"name": "1.5 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PAKTA INTEGRITAS DAN SEJENISNYA",
|
||||
"deskripsi": "<p>ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PAKTA INTEGRITAS DAN SEJENISNYA</p>",
|
||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh"
|
||||
},
|
||||
{
|
||||
"id": "cmdsa35310014vnbe6qy6l1rz",
|
||||
"name": "2.1 ADANYA KEGIATAN PENGAWASAN DAN EVALUASI KINERJA PERANGKAT DESA",
|
||||
"deskripsi": "<p>ADANYA KEGIATAN PENGAWASAN DAN EVALUASI KINERJA PERANGKAT DESA</p>",
|
||||
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm"
|
||||
},
|
||||
{
|
||||
"id": "cmdsa46590017vnbepp3noso1",
|
||||
"name": "2.2 ADANYA TINDAK LANJUT HASIL PEMBINAAN, PETUNJUK, ARAH, PENGAWASAN, DAN PEMERIKSAAN DARI PEMERINTAH PUSAT/DAERAH",
|
||||
"deskripsi": "<p>ADANYA TINDAK LANJUT HASIL PEMBINAAN, PETUNJUK, ARAH, PENGAWASAN, DAN PEMERIKSAAN DARI PEMERINTAH PUSAT/DAERAH</p>",
|
||||
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm"
|
||||
},
|
||||
{
|
||||
"id": "cmdsa5m7z001avnbe4cvfrcz0",
|
||||
"name": "2.3 TIDAK ADANYA APARATUR DESA DALAM 3(TIGA) TAHUN TERAKHIR YANG TERJERAT TINDAKAN PIDANA KORUPSI",
|
||||
"deskripsi": "<p>TIDAK ADANYA APARATUR DESA DALAM 3(TIGA) TAHUN TERAKHIR YANG TERJERAT TINDAKAN PIDANA KORUPSI</p>",
|
||||
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm"
|
||||
},
|
||||
{
|
||||
"id": "cmdsa8q5q001dvnbemch8j24x",
|
||||
"name": "3.1 ADANYA LAYANAN PENGADUAN BAGI MASYARAKAT",
|
||||
"deskripsi": "<p>ADANYA LAYANAN PENGADUAN BAGI MASYARAKAT</p>",
|
||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl"
|
||||
},
|
||||
{
|
||||
"id": "cmdsa9lbi001gvnbequn2ba7m",
|
||||
"name": "3.2 ADANYA SURVEY KEPUASAN MASYARAKAT (SKM) TERHADAP LAYANAN PEMERINTAH DESA",
|
||||
"deskripsi": "<p>ADANYA SURVEY KEPUASAN MASYARAKAT (SKM) TERHADAP LAYANAN PEMERINTAH DESA</p>",
|
||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl"
|
||||
},
|
||||
{
|
||||
"id": "cmdsaa7aq001jvnbeizh04e67",
|
||||
"name": "3.3 ADANYA KETERBUKAAN AKSES MASYARAKAT TERHADAP INFORMASI LAYANAN PEMERINTAH DESA (KESEHATAN, PENDIDIKAN, SOSIAL, LINGKUNGAN, TRANTIBUMLINMAS, PEKERJAAN UMUM) PEMBANGUNAN, KEPENDUDUKAN, KEUANGAN, DAN PELAYANAN LAINNYA",
|
||||
"deskripsi": "<p>ADANYA KETERBUKAAN AKSES MASYARAKAT TERHADAP INFORMASI LAYANAN PEMERINTAH DESA (KESEHATAN, PENDIDIKAN, SOSIAL, LINGKUNGAN, TRANTIBUMLINMAS, PEKERJAAN UMUM) PEMBANGUNAN, KEPENDUDUKAN, KEUANGAN, DAN PELAYANAN LAINNYA</p>",
|
||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl"
|
||||
},
|
||||
{
|
||||
"id": "cmdsaaw8d001mvnbek3tfefrk",
|
||||
"name": "3.4 ADANYA MEDIA INFORMASI TENTANG APBDES DI BALAI DESA DAN/ATAU TEMPAT LAIN YANG MUDAH DIAKSES OLEH MASYARAKAT",
|
||||
"deskripsi": "<p>ADANYA MEDIA INFORMASI TENTANG APBDES DI BALAI DESA DAN/ATAU TEMPAT LAIN YANG MUDAH DIAKSES OLEH MASYARAKAT</p>",
|
||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl"
|
||||
},
|
||||
{
|
||||
"id": "cmdsabhif001pvnbepm06hry6",
|
||||
"name": "3.5 ADANYA MAKLUMAT PELAYANAN",
|
||||
"deskripsi": "<p>ADANYA MAKLUMAT PELAYANAN</p>",
|
||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl"
|
||||
},
|
||||
{
|
||||
"id": "cmdsag40b001svnbe7krq9khc",
|
||||
"name": "4.1 ADANYA PARTISIPASI DAN KETERLIBATAN MASYARAKAT DALAM PENYUSUNAN RKP DESA",
|
||||
"deskripsi": "<p>ADANYA PARTISIPASI DAN KETERLIBATAN MASYARAKAT DALAM PENYUSUNAN RKP DESA</p>",
|
||||
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv"
|
||||
},
|
||||
{
|
||||
"id": "cmdsagkaf001vvnbejo26w8sa",
|
||||
"name": "4.2 ADANYA KESADARAN MASYARAKAT DALAM MENCEGAH TERJADINYA PRAKTIK GRATIFIKASI, SUAP DAN KONFLIK KEPENTINGAN",
|
||||
"deskripsi": "<p>ADANYA KESADARAN MASYARAKAT DALAM MENCEGAH TERJADINYA PRAKTIK GRATIFIKASI, SUAP DAN KONFLIK KEPENTINGAN</p>",
|
||||
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv"
|
||||
},
|
||||
{
|
||||
"id": "cmdsah4qe001yvnbeiy3mwrvb",
|
||||
"name": "4.3 ADANYA KETERLIBATAN LEMBAGA KEMASYARAKATAN DALAM PELAKSANAAN PEMBANGUNAN DESA",
|
||||
"deskripsi": "<p>ADANYA KETERLIBATAN LEMBAGA KEMASYARAKATAN DALAM PELAKSANAAN PEMBANGUNAN DESA</p>",
|
||||
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv"
|
||||
},
|
||||
{
|
||||
"id": "cmdsak5vn0021vnbemg86aab4",
|
||||
"name": "5.1 ADANYA BUDAYA LOKAL/HUKUM ADAT YANG MENDORONG UPAYA PENCEGAHAN TINDAK PIDANA KORUPSI",
|
||||
"deskripsi": "<p>ADANYA BUDAYA LOKAL/HUKUM ADAT YANG MENDORONG UPAYA PENCEGAHAN TINDAK PIDANA KORUPSI</p>",
|
||||
"kategoriId": "cmds9govb000mvnbesq8b4y99",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9govb000mvnbesq8b4y99"
|
||||
},
|
||||
{
|
||||
"id": "cmdsalc800024vnbezgulhgrb",
|
||||
"name": "5.2 ADANYA TOKOH MASYARAKAT, TOKOH AGAMA, TOKOH ADAT, TOKOH PEMUDA, DAN KAUM PEREMPUAN YANG MENDORONG UPAYA PENCEGAHAN TINDAK PIDANA KORUPSI",
|
||||
"deskripsi": "<p>ADANYA TOKOH MASYARAKAT, TOKOH AGAMA, TOKOH ADAT, TOKOH PEMUDA, DAN KAUM PEREMPUAN YANG MENDORONG UPAYA PENCEGAHAN TINDAK PIDANA KORUPSI</p>",
|
||||
"kategoriId": "cmds9govb000mvnbesq8b4y99",
|
||||
"fileId": ""
|
||||
"kategoriId": "cmds9govb000mvnbesq8b4y99"
|
||||
}
|
||||
]
|
||||
@@ -1,8 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"jenisInformasi": "Peraturan Desa",
|
||||
"deskripsi": "Dokumen yang berisi kebijakan dan regulasi desa",
|
||||
"tanggal": "15 Januari 2024"
|
||||
"id": "cmeppcwzk0000vn5exmudcipd",
|
||||
"jenisInformasi": "Potensi Desa",
|
||||
"deskripsi": "<p>“Potensi desa adalah segenap sumber daya alam dan sumber daya manusia yang dimiliki desa sebagai modal dasar yang perlu dikelola dan dikembangkan bagi kelangsungan dan perkembangan desa. Adapun potensi yang dimiliki Desa Darmasaba yaitu:</p><ol><li><p>TPS3R Pudak Mesari</p></li><li><p>Bumdes Pudak Mesari</p></li><li><p>Pertanian</p></li><li><p>Jogging Track Tegeh Aban, Karang Gadon dan Munduk Uma Desa</p></li><li><p>Taman Beji Cengana</p></li><li><p>Dam Tanah Putih</p></li><li><p>Gumuh Sari Water Park</p></li><li><p>UMKM</p></li><li><p>Kawasan Kuliner</p></li><li><p>IKM berbasis Pengolahan Pangan</p></li><li><p>Genteng</p></li><li><p>Peternakan Ikan Lele</p></li><li><p>Pemotongan Daging”</p></li></ol>",
|
||||
"tanggal": "2021-05-25"
|
||||
},
|
||||
{
|
||||
"id": "cmeppieay0001vn5e8qe658ub",
|
||||
"jenisInformasi": "Layanan Surat Keterangan Desa",
|
||||
"deskripsi": "<p>“Desa Darmasaba menyediakan berbagai jenis layanan surat keterangan untuk kebutuhan administratif, antara lain:</p><ul><li><p>Surat Keterangan Domisili Organisasi</p></li><li><p>Surat Keterangan Penghasilan</p></li><li><p>Surat Keterangan Tidak Mampu</p></li><li><p>Surat Keterangan Kelahiran</p></li><li><p>Surat Keterangan Usaha</p></li><li><p>Surat Keterangan Tempat Usaha</p></li><li><p>Surat Keterangan Belum Kawin</p></li><li><p>Surat Keterangan Kelakuan Baik (Pengantar SKCK)</p></li><li><p>Surat Keterangan Kematian</p></li><li><p>Surat Keterangan Perbedaan Biodata Diri</p></li><li><p>Surat Keterangan Yatim/Piatu/Yatim Piatu<br>Untuk surat keterangan lainnya, masyarakat dapat berkonsultasi langsung ke kantor Perbekel Darmasaba.”<br><em>(Sumber: Laman Layanan Desa Darmasaba)</em></p></li></ul>",
|
||||
"tanggal": "2025-02-21"
|
||||
}
|
||||
]
|
||||
@@ -201,8 +201,8 @@ model PrestasiDesa {
|
||||
deskripsi String @db.Text
|
||||
kategori KategoriPrestasiDesa @relation(fields: [kategoriId], references: [id])
|
||||
kategoriId String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
@@ -223,7 +223,7 @@ model KategoriPrestasiDesa {
|
||||
model Responden {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
tanggal DateTime // misal: 2025-05-01
|
||||
tanggal DateTime @db.Date // misal: 2025-05-01
|
||||
jenisKelamin JenisKelaminResponden @relation(fields: [jenisKelaminId], references: [id])
|
||||
jenisKelaminId String
|
||||
rating PilihanRatingResponden @relation(fields: [ratingId], references: [id])
|
||||
@@ -292,6 +292,9 @@ model PosisiOrganisasiPPID {
|
||||
pegawai PegawaiPPID[]
|
||||
strukturOrganisasi StrukturPPID[] // Relasi balik
|
||||
parentId String?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id])
|
||||
children PosisiOrganisasiPPID[] @relation("Parent")
|
||||
}
|
||||
@@ -1547,7 +1550,7 @@ model DataDemografiPekerjaan {
|
||||
model DetailDataPengangguran {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
month String @db.VarChar(20)
|
||||
year Int
|
||||
year DateTime
|
||||
totalUnemployment Int
|
||||
educatedUnemployment Int
|
||||
uneducatedUnemployment Int
|
||||
|
||||
332
prisma/seed.ts
332
prisma/seed.ts
@@ -1,57 +1,62 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import profilePejabatDesa from "./data/landing-page/profile/profile.json";
|
||||
import penghargaan from "./data/landing-page/penghargaan/penghargaan.json";
|
||||
import programInovasi from "./data/landing-page/profile/programInovasi.json";
|
||||
import mediaSosial from "./data/landing-page/profile/mediaSosial.json";
|
||||
import desaAntiKorupsi from "./data/landing-page/desa-anti-korupsi/desaantiKorpusi.json";
|
||||
import kategoriDesaAntiKorupsi from "./data/landing-page/desa-anti-korupsi/kategoriDesaAntiKorupsi.json";
|
||||
import sdgsDesa from "./data/landing-page/sdgs-desa/sdgs-desa.json";
|
||||
import apbdes from "./data/landing-page/apbdes/apbdes.json";
|
||||
import pelayananSuratKeterangan from "./data/desa/layanan/pelayananSuratKeterangan.json";
|
||||
import categoryPengumuman from "./data/category-pengumuman.json";
|
||||
import kategoriBerita from "./data/kategori-berita.json";
|
||||
import caraMemperolehInformasi from "./data/list-caraMemperolehInformasi.json";
|
||||
import caraMemperolehSalinanInformasi from "./data/list-caraMemperolehSalinanInformasi.json";
|
||||
import jenisInformasiDiminta from "./data/list-jenisInfromasi.json";
|
||||
import layanan from "./data/list-layanan.json";
|
||||
import potensi from "./data/list-potensi.json";
|
||||
import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json";
|
||||
import kategoriPrestasiDesa from "./data/landing-page/prestasi-desa/kategori-prestasi.json";
|
||||
import prestasiDesa from "./data/landing-page/prestasi-desa/prestasi-desa.json";
|
||||
import penghargaan from "./data/landing-page/penghargaan/penghargaan.json";
|
||||
import profilePPID from "./data/ppid/profile-ppid/profilePPid.json";
|
||||
import pegawaiPPID from "./data/ppid/struktur-ppid/pegawai-PPID.json";
|
||||
import posisiOrganisasiPPID from "./data/ppid/struktur-ppid/posisi-organisasi-PPID.json";
|
||||
import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json";
|
||||
import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json";
|
||||
import jenisKelamin from "./data/ppid/ikm/jenis-kelamin/jenis-kelamin.json";
|
||||
import daftarInformasiPublik from "./data/ppid/daftar-informasi-publik-desa-darmasaba/daftarInformasi.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 categoryPengumuman from "./data/category-pengumuman.json";
|
||||
import pelayananPerizinanBerusaha from "./data/desa/layanan/pelayananPerizinanBerusaha.json";
|
||||
import pelayananSuratKeterangan from "./data/desa/layanan/pelayananSuratKeterangan.json";
|
||||
import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSaktiDesa.json";
|
||||
import pelayananPendudukNonPermanen from "./data/desa/layanan/pelayanaPendudukNonPermanen.json";
|
||||
import sejarahDesa from "./data/desa/profile/sejarah_desa.json";
|
||||
import visiMisiDesa from "./data/desa/profile/visi_misi_desa.json";
|
||||
import lambangDesa from "./data/desa/profile/lambang_desa.json";
|
||||
import maskotDesa from "./data/desa/profile/maskot_desa.json";
|
||||
import profilPerbekel from "./data/desa/profile/profil_perbekel.json";
|
||||
import sejarahDesa from "./data/desa/profile/sejarah_desa.json";
|
||||
import visiMisiDesa from "./data/desa/profile/visi_misi_desa.json";
|
||||
import detailDataPengangguran from "./data/ekonomi/jumlah-pengangguran/detail-data-pengangguran.json";
|
||||
import kategoriProduk from "./data/ekonomi/pasar-desa/kategori-produk.json";
|
||||
import hubunganOrganisasi from "./data/ekonomi/struktur-organisasi/hubungan-organisasi.json";
|
||||
import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi.json";
|
||||
import pegawai from "./data/ekonomi/struktur-organisasi/pegawai.json";
|
||||
import detailDataPengangguran from "./data/ekonomi/jumlah-pengangguran/detail-data-pengangguran.json";
|
||||
import tujuanEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan.json";
|
||||
import materiEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan.json";
|
||||
import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi.json";
|
||||
import kategoriBerita from "./data/kategori-berita.json";
|
||||
import contohEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/contoh-kegiatan-di-desa-darmasaba.json";
|
||||
import nilaiKonservasiAdat from "./data/lingkungan/konservasi-adat-bali/nilai-konservasi-adat.json";
|
||||
import materiEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan.json";
|
||||
import tujuanEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan.json";
|
||||
import bentukKonservasiBerdasarkanAdat from "./data/lingkungan/konservasi-adat-bali/bentuk-konservasi.json";
|
||||
import filosofiTriHita from "./data/lingkungan/konservasi-adat-bali/filosofi-tri-hita.json";
|
||||
import tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-program.json";
|
||||
import nilaiKonservasiAdat from "./data/lingkungan/konservasi-adat-bali/nilai-konservasi-adat.json";
|
||||
import caraMemperolehInformasi from "./data/list-caraMemperolehInformasi.json";
|
||||
import caraMemperolehSalinanInformasi from "./data/list-caraMemperolehSalinanInformasi.json";
|
||||
import jenisInformasiDiminta from "./data/list-jenisInfromasi.json";
|
||||
import potensi from "./data/list-potensi.json";
|
||||
import fasilitasBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan.json";
|
||||
import lokasiJadwalBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal.json";
|
||||
import tujuanBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/tujuan-bimbingan-belajar-desa.json";
|
||||
import jenisProgramYangDiselenggarakan from "./data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json";
|
||||
import tempatKegiatan from "./data/pendidikan/pendidikan-non-formal/tempat-kegiatan.json";
|
||||
import tujuanProgram2 from "./data/pendidikan/pendidikan-non-formal/tujuan-program2.json";
|
||||
import programUnggulan from "./data/pendidikan/program-pendidikan-anak/program-unggulan.json";
|
||||
import tujuanBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/tujuan-bimbingan-belajar-desa.json";
|
||||
import lokasiJadwalBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal.json";
|
||||
import fasilitasBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan.json";
|
||||
import tempatKegiatan from "./data/pendidikan/pendidikan-non-formal/tempat-kegiatan.json";
|
||||
import jenisProgramYangDiselenggarakan from "./data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json";
|
||||
import posisiOrganisasiPPID from "./data/ppid/struktur-ppid/posisi-organisasi-PPID.json";
|
||||
import pegawaiPPID from "./data/ppid/struktur-ppid/pegawai-PPID.json";
|
||||
import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSaktiDesa.json";
|
||||
import tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-program.json";
|
||||
|
||||
(async () => {
|
||||
// =========== LANDING PAGE ===========
|
||||
// =========== PROFILE ===========
|
||||
// =========== SUBMENU PROFILE ===========
|
||||
// =========== PROFILE PEJABAT DESA ===========
|
||||
for (const p of profilePejabatDesa) {
|
||||
await prisma.pejabatDesa.upsert({
|
||||
where: { id: p.id },
|
||||
@@ -106,6 +111,90 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
}
|
||||
console.log("media sosial success ...");
|
||||
|
||||
// =========== SUBMENU DESA ANTI KORUPSI ===========
|
||||
// =========== KATEGORI DESA ANTI KORUPSI ===========
|
||||
for (const k of kategoriDesaAntiKorupsi) {
|
||||
await prisma.kategoriDesaAntiKorupsi.upsert({
|
||||
where: { id: k.id },
|
||||
update: {
|
||||
name: k.name,
|
||||
},
|
||||
create: {
|
||||
id: k.id,
|
||||
name: k.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("kategori desa anti korupsi success ...");
|
||||
|
||||
// =========== DESA ANTI KORUPSI ===========
|
||||
for (const p of desaAntiKorupsi) {
|
||||
await prisma.desaAntiKorupsi.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
kategoriId: p.kategoriId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
kategoriId: p.kategoriId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("desa anti korupsi success ...");
|
||||
|
||||
// =========== KATEGORI DESA ANTI KORUPSI ===========
|
||||
for (const p of kategoriDesaAntiKorupsi) {
|
||||
await prisma.kategoriDesaAntiKorupsi.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("desa anti korupsi success ...");
|
||||
|
||||
// =========== KATEGORI PRESTASI DESA===========
|
||||
for (const c of kategoriPrestasiDesa) {
|
||||
await prisma.kategoriPrestasiDesa.upsert({
|
||||
where: { id: c.id },
|
||||
update: {
|
||||
name: c.name,
|
||||
},
|
||||
create: {
|
||||
id: c.id,
|
||||
name: c.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("kategori prestasi desa success ...");
|
||||
|
||||
// =========== PRESTASI DESA===========
|
||||
for (const p of prestasiDesa) {
|
||||
await prisma.prestasiDesa.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
kategoriId: p.kategoriId,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
kategoriId: p.kategoriId,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("prestasi desa success ...");
|
||||
|
||||
// =========== PENGHARGAAN ===========
|
||||
for (const p of penghargaan) {
|
||||
await prisma.penghargaan.upsert({
|
||||
@@ -160,23 +249,6 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
}
|
||||
console.log("pelayanan surat keterangan success ...");
|
||||
|
||||
// =========== LAYANAN ===========
|
||||
for (const l of layanan) {
|
||||
await prisma.layanan.upsert({
|
||||
where: {
|
||||
name: l.name,
|
||||
},
|
||||
update: {
|
||||
name: l.name,
|
||||
},
|
||||
create: {
|
||||
name: l.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("layanan success ...");
|
||||
|
||||
// =========== SDGSDesa ===========
|
||||
for (const l of sdgsDesa) {
|
||||
await prisma.sDGSDesa.upsert({
|
||||
@@ -217,6 +289,9 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
|
||||
console.log("sdgs desa success ...");
|
||||
|
||||
// =========== MENU DESA ===========
|
||||
// =========== SUBMENU PROFILE ===========
|
||||
// =========== SEJARAH DESA ===========
|
||||
for (const l of sejarahDesa) {
|
||||
await prisma.sejarahDesa.upsert({
|
||||
where: {
|
||||
@@ -236,6 +311,7 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
|
||||
console.log("sejarah desa success ...");
|
||||
|
||||
// =========== MASKOT DESA ===========
|
||||
for (const l of maskotDesa) {
|
||||
await prisma.maskotDesa.upsert({
|
||||
where: {
|
||||
@@ -255,6 +331,7 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
|
||||
console.log("maskot desa success ...");
|
||||
|
||||
// =========== LAMBANG DESA ===========
|
||||
for (const l of lambangDesa) {
|
||||
await prisma.lambangDesa.upsert({
|
||||
where: {
|
||||
@@ -274,6 +351,7 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
|
||||
console.log("lambang desa success ...");
|
||||
|
||||
// =========== PROFIL PERBEKEL ===========
|
||||
for (const c of profilPerbekel) {
|
||||
await prisma.profilPerbekel.upsert({
|
||||
where: { id: c.id },
|
||||
@@ -298,6 +376,7 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
"✅ profilePerbekel seeded without imageId (editable later via UI)"
|
||||
);
|
||||
|
||||
// =========== VISI MISI DESA ===========
|
||||
for (const l of visiMisiDesa) {
|
||||
await prisma.visiMisiDesa.upsert({
|
||||
where: {
|
||||
@@ -317,6 +396,35 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
|
||||
console.log("visi misi desa success ...");
|
||||
|
||||
// =========== MENU PPID ===========
|
||||
// =========== SUBMENU PROFILE PPID ===========
|
||||
for (const c of profilePPID) {
|
||||
await prisma.profilePPID.upsert({
|
||||
where: { id: c.id },
|
||||
update: {
|
||||
name: c.name,
|
||||
biodata: c.biodata,
|
||||
riwayat: c.riwayat,
|
||||
pengalaman: c.pengalaman,
|
||||
unggulan: c.unggulan,
|
||||
// imageId tidak di-update
|
||||
},
|
||||
create: {
|
||||
id: c.id,
|
||||
name: c.name,
|
||||
biodata: c.biodata,
|
||||
riwayat: c.riwayat,
|
||||
pengalaman: c.pengalaman,
|
||||
unggulan: c.unggulan,
|
||||
// imageId tidak di-create
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ profilePPID seeded without imageId (editable later via UI)");
|
||||
|
||||
// =========== SUBMENU STRUKTUR PPID ===========
|
||||
// =========== POSISI ORGANISASI PPID ===========
|
||||
|
||||
const flattenedPosisi = posisiOrganisasiPPID.flat();
|
||||
|
||||
// ✅ Urutkan berdasarkan hierarki
|
||||
@@ -341,9 +449,9 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
create: p,
|
||||
});
|
||||
}
|
||||
console.log("✅ Posisi organisasi berhasil");
|
||||
console.log("posisi organisasi berhasil");
|
||||
|
||||
// 2. Seed Pegawai
|
||||
// =========== PEGAWAI PPID ===========
|
||||
const flattenedPegawai = pegawaiPPID.flat();
|
||||
for (const p of flattenedPegawai) {
|
||||
await prisma.pegawaiPPID.upsert({
|
||||
@@ -352,7 +460,70 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
create: p,
|
||||
});
|
||||
}
|
||||
console.log("✅ Pegawai berhasil");
|
||||
console.log("pegawai berhasil");
|
||||
|
||||
// =========== SUBMENU VISI MISI PPID ===========
|
||||
|
||||
for (const v of visiMisiPPID) {
|
||||
await prisma.visiMisiPPID.upsert({
|
||||
where: {
|
||||
id: v.id,
|
||||
},
|
||||
update: {
|
||||
misi: v.misi,
|
||||
visi: v.visi,
|
||||
},
|
||||
create: {
|
||||
id: v.id,
|
||||
misi: v.misi,
|
||||
visi: v.visi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("visi misi PPID success ...");
|
||||
|
||||
// =========== SUBMENU DASAR HUKUM PPID ===========
|
||||
for (const v of dasarHukumPPID) {
|
||||
await prisma.dasarHukumPPID.upsert({
|
||||
where: {
|
||||
id: v.id,
|
||||
},
|
||||
update: {
|
||||
judul: v.judul,
|
||||
content: v.content,
|
||||
},
|
||||
create: {
|
||||
id: v.id,
|
||||
judul: v.judul,
|
||||
content: v.content,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("dasar hukum PPID success ...");
|
||||
|
||||
// =========== SUBMENU DAFTAR INFORMASI PUBLIK PPID ===========
|
||||
for (const v of daftarInformasiPublik) {
|
||||
// Convert string date to Date object
|
||||
const tanggal = new Date(v.tanggal);
|
||||
|
||||
await prisma.daftarInformasiPublik.upsert({
|
||||
where: {
|
||||
id: v.id,
|
||||
},
|
||||
update: {
|
||||
jenisInformasi: v.jenisInformasi,
|
||||
deskripsi: v.deskripsi,
|
||||
tanggal: tanggal,
|
||||
},
|
||||
create: {
|
||||
id: v.id,
|
||||
jenisInformasi: v.jenisInformasi,
|
||||
deskripsi: v.deskripsi,
|
||||
tanggal: tanggal,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("daftar informasi publik PPID success ...");
|
||||
|
||||
for (const l of pelayananPerizinanBerusaha) {
|
||||
await prisma.pelayananPerizinanBerusaha.upsert({
|
||||
@@ -486,48 +657,6 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
}
|
||||
console.log("cara memperoleh salinan informasi success ...");
|
||||
|
||||
for (const c of profilePPID) {
|
||||
await prisma.profilePPID.upsert({
|
||||
where: { id: c.id },
|
||||
update: {
|
||||
name: c.name,
|
||||
biodata: c.biodata,
|
||||
riwayat: c.riwayat,
|
||||
pengalaman: c.pengalaman,
|
||||
unggulan: c.unggulan,
|
||||
// imageId tidak di-update
|
||||
},
|
||||
create: {
|
||||
id: c.id,
|
||||
name: c.name,
|
||||
biodata: c.biodata,
|
||||
riwayat: c.riwayat,
|
||||
pengalaman: c.pengalaman,
|
||||
unggulan: c.unggulan,
|
||||
// imageId tidak di-create
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ profilePPID seeded without imageId (editable later via UI)");
|
||||
|
||||
for (const v of visiMisiPPID) {
|
||||
await prisma.visiMisiPPID.upsert({
|
||||
where: {
|
||||
id: v.id,
|
||||
},
|
||||
update: {
|
||||
misi: v.misi,
|
||||
visi: v.visi,
|
||||
},
|
||||
create: {
|
||||
id: v.id,
|
||||
misi: v.misi,
|
||||
visi: v.visi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("visi misi PPID success ...");
|
||||
|
||||
for (const j of jenisKelamin) {
|
||||
await prisma.jenisKelaminResponden.upsert({
|
||||
where: {
|
||||
@@ -576,24 +705,6 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
}
|
||||
console.log("umur responden success ...");
|
||||
|
||||
for (const v of dasarHukumPPID) {
|
||||
await prisma.dasarHukumPPID.upsert({
|
||||
where: {
|
||||
id: v.id,
|
||||
},
|
||||
update: {
|
||||
judul: v.judul,
|
||||
content: v.content,
|
||||
},
|
||||
create: {
|
||||
id: v.id,
|
||||
judul: v.judul,
|
||||
content: v.content,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("dasar hukum PPID success ...");
|
||||
|
||||
for (const k of kategoriProduk) {
|
||||
await prisma.kategoriProduk.upsert({
|
||||
where: {
|
||||
@@ -681,9 +792,12 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
console.log("hubungan organisasi success ...");
|
||||
|
||||
for (const d of detailDataPengangguran) {
|
||||
// Convert the year to a Date object (using January 1st of the year as the date)
|
||||
const yearAsDate = new Date(d.year, 0, 1);
|
||||
|
||||
await prisma.detailDataPengangguran.upsert({
|
||||
where: {
|
||||
month_year: { month: d.month, year: d.year },
|
||||
month_year: { month: d.month, year: yearAsDate },
|
||||
},
|
||||
update: {
|
||||
totalUnemployment: d.totalUnemployment,
|
||||
@@ -693,7 +807,7 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
||||
},
|
||||
create: {
|
||||
month: d.month,
|
||||
year: d.year,
|
||||
year: yearAsDate,
|
||||
totalUnemployment: d.totalUnemployment,
|
||||
educatedUnemployment: d.educatedUnemployment,
|
||||
uneducatedUnemployment: d.uneducatedUnemployment,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -61,13 +62,39 @@ const lowonganKerjaState = proxy({
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.LowonganPekerjaanGetPayload<{
|
||||
omit: { isActive: true };
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.ekonomi.lowongankerja["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
lowonganKerjaState.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
lowonganKerjaState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
lowonganKerjaState.findMany.page = page;
|
||||
lowonganKerjaState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.ekonomi.lowongankerja["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
lowonganKerjaState.findMany.data = res.data.data ?? [];
|
||||
lowonganKerjaState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
lowonganKerjaState.findMany.data = [];
|
||||
lowonganKerjaState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch lowongan kerja paginated:", err);
|
||||
lowonganKerjaState.findMany.data = [];
|
||||
lowonganKerjaState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
lowonganKerjaState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -53,22 +54,47 @@ const pasarDesa = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<
|
||||
Prisma.PasarDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
KategoriToPasar: {
|
||||
include: {
|
||||
kategori: true;
|
||||
data: null as
|
||||
| Prisma.PasarDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
KategoriToPasar: {
|
||||
include: {
|
||||
kategori: true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.ekonomi.pasardesa["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
pasarDesa.findMany.data = res.data?.data ?? [];
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", categoryId?: string) => {
|
||||
pasarDesa.findMany.loading = true;
|
||||
pasarDesa.findMany.page = page;
|
||||
pasarDesa.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (categoryId) query.categoryId = categoryId;
|
||||
|
||||
const res = await ApiFetch.api.ekonomi.pasardesa["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pasarDesa.findMany.data = res.data.data ?? [];
|
||||
pasarDesa.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
pasarDesa.findMany.data = [];
|
||||
pasarDesa.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch keamanan lingkungan paginated:", err);
|
||||
pasarDesa.findMany.data = [];
|
||||
pasarDesa.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pasarDesa.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -272,14 +298,41 @@ const kategoriProduk = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<{
|
||||
id: string;
|
||||
nama: string;
|
||||
}> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
kategoriProduk.findMany.data = res.data?.data ?? [];
|
||||
data: null as
|
||||
| Prisma.KategoriProdukGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search2: "",
|
||||
load: async (page = 1, limit = 10, search2 = "") => {
|
||||
kategoriProduk.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
kategoriProduk.findMany.page = page;
|
||||
kategoriProduk.findMany.search2 = search2;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search2) query.search2 = search2;
|
||||
|
||||
const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kategoriProduk.findMany.data = res.data.data ?? [];
|
||||
kategoriProduk.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
kategoriProduk.findMany.data = [];
|
||||
kategoriProduk.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch kategori produk paginated:", err);
|
||||
kategoriProduk.findMany.data = [];
|
||||
kategoriProduk.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kategoriProduk.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -11,8 +12,7 @@ const templateForm = z.object({
|
||||
statistik: z.object({
|
||||
tahun: z.string().min(1, "Tahun minimal 1 karakter"),
|
||||
jumlah: z.string().min(1, "Jumlah minimal 1 karakter"),
|
||||
})
|
||||
|
||||
}),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
@@ -21,8 +21,8 @@ const defaultForm = {
|
||||
ikonUrl: "",
|
||||
statistik: {
|
||||
tahun: "",
|
||||
jumlah: ""
|
||||
}
|
||||
jumlah: "",
|
||||
},
|
||||
};
|
||||
|
||||
const programKemiskinanState = proxy({
|
||||
@@ -64,12 +64,35 @@ const programKemiskinanState = proxy({
|
||||
};
|
||||
}>[],
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.ekonomi.programkemiskinan[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
programKemiskinanState.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
programKemiskinanState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
programKemiskinanState.findMany.page = page;
|
||||
programKemiskinanState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.ekonomi.programkemiskinan[
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
programKemiskinanState.findMany.data = res.data.data ?? [];
|
||||
programKemiskinanState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
programKemiskinanState.findMany.data = [];
|
||||
programKemiskinanState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch program kemiskinan paginated:", err);
|
||||
programKemiskinanState.findMany.data = [];
|
||||
programKemiskinanState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
programKemiskinanState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -53,15 +54,39 @@ const keamananLingkunganState = proxy({
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.KeamananLingkunganGetPayload<{
|
||||
include: { image: true };
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.keamanan.keamananlingkungan[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
keamananLingkunganState.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
keamananLingkunganState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
keamananLingkunganState.findMany.page = page;
|
||||
keamananLingkunganState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.keamanan.keamananlingkungan["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
keamananLingkunganState.findMany.data = res.data.data ?? [];
|
||||
keamananLingkunganState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
keamananLingkunganState.findMany.data = [];
|
||||
keamananLingkunganState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch keamanan lingkungan paginated:", err);
|
||||
keamananLingkunganState.findMany.data = [];
|
||||
keamananLingkunganState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
keamananLingkunganState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -63,13 +64,41 @@ const polsekTerdekatState = proxy({
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PolsekTerdekatGetPayload<{
|
||||
include: { layananPolsek: true };
|
||||
include: {
|
||||
layananPolsek: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.keamanan.polsekterdekat["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
polsekTerdekatState.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
polsekTerdekatState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
polsekTerdekatState.findMany.page = page;
|
||||
polsekTerdekatState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.keamanan.polsekterdekat["find-many"].get(
|
||||
{ query }
|
||||
);
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
polsekTerdekatState.findMany.data = res.data.data ?? [];
|
||||
polsekTerdekatState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
polsekTerdekatState.findMany.data = [];
|
||||
polsekTerdekatState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch polsek terdekat paginated:", err);
|
||||
polsekTerdekatState.findMany.data = [];
|
||||
polsekTerdekatState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
polsekTerdekatState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -237,6 +266,29 @@ const polsekTerdekatState = proxy({
|
||||
polsekTerdekatState.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
findFirst: {
|
||||
data: null as Prisma.PolsekTerdekatGetPayload<{
|
||||
include: {
|
||||
layananPolsek: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
load: async () => { // Changed to arrow function
|
||||
polsekTerdekatState.findFirst.loading = true;
|
||||
try {
|
||||
const res = await ApiFetch.api.keamanan.polsekterdekat["find-first"].get();
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
polsekTerdekatState.findFirst.data = res.data.data || null;
|
||||
} else {
|
||||
polsekTerdekatState.findFirst.data = null;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch polsek terdekat terbaru:", err);
|
||||
} finally {
|
||||
polsekTerdekatState.findFirst.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default polsekTerdekatState;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -53,15 +54,39 @@ const tipsKeamananState = proxy({
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.MenuTipsKeamananGetPayload<{
|
||||
include: { image: true };
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.keamanan.menutipskeamanan[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
tipsKeamananState.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
tipsKeamananState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
tipsKeamananState.findMany.page = page;
|
||||
tipsKeamananState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.keamanan.menutipskeamanan["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
tipsKeamananState.findMany.data = res.data.data ?? [];
|
||||
tipsKeamananState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
tipsKeamananState.findMany.data = [];
|
||||
tipsKeamananState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch menu tips keamanan paginated:", err);
|
||||
tipsKeamananState.findMany.data = [];
|
||||
tipsKeamananState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
tipsKeamananState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -50,18 +51,50 @@ const apbdes = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<
|
||||
Prisma.APBDesGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
file: true;
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.apbdes["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
apbdes.findMany.data = res.data?.data ?? [];
|
||||
data: null as
|
||||
| Prisma.APBDesGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
file: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||
apbdes.findMany.loading = true; // Use the full path to access the property
|
||||
apbdes.findMany.page = page;
|
||||
apbdes.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.apbdes[
|
||||
"findMany"
|
||||
].get({
|
||||
query
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
apbdes.findMany.data = res.data.data || [];
|
||||
apbdes.findMany.total = res.data.total || 0;
|
||||
apbdes.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load pegawai:", res.data?.message);
|
||||
apbdes.findMany.data = [];
|
||||
apbdes.findMany.total = 0;
|
||||
apbdes.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pegawai:", error);
|
||||
apbdes.findMany.data = [];
|
||||
apbdes.findMany.total = 0;
|
||||
apbdes.findMany.totalPages = 1;
|
||||
} finally {
|
||||
apbdes.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -60,16 +60,22 @@ const desaAntikorupsi = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
desaAntikorupsi.findMany.loading = true; // Use the full path to access the property
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
desaAntikorupsi.findMany.loading = true; // Use the full path to access the property
|
||||
desaAntikorupsi.findMany.page = page;
|
||||
desaAntikorupsi.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.desaantikorupsi[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
query,
|
||||
});
|
||||
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
desaAntikorupsi.findMany.data = res.data.data || [];
|
||||
desaAntikorupsi.findMany.total = res.data.total || 0;
|
||||
@@ -305,20 +311,25 @@ const kategoriDesaAntiKorupsi = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
kategoriDesaAntiKorupsi.findMany.loading = true; // Use the full path to access the property
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
kategoriDesaAntiKorupsi.findMany.loading = true; // Use the full path to access the property
|
||||
kategoriDesaAntiKorupsi.findMany.page = page;
|
||||
kategoriDesaAntiKorupsi.findMany.search = search;
|
||||
try {
|
||||
const res = await ApiFetch.api.landingpage.kategoridak[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.kategoridak["findMany"].get({
|
||||
query,
|
||||
});
|
||||
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kategoriDesaAntiKorupsi.findMany.data = res.data.data || [];
|
||||
kategoriDesaAntiKorupsi.findMany.total = res.data.total || 0;
|
||||
kategoriDesaAntiKorupsi.findMany.totalPages = res.data.totalPages || 1;
|
||||
kategoriDesaAntiKorupsi.findMany.totalPages =
|
||||
res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load media sosial:", res.data?.message);
|
||||
kategoriDesaAntiKorupsi.findMany.data = [];
|
||||
@@ -363,27 +374,30 @@ const kategoriDesaAntiKorupsi = proxy({
|
||||
try {
|
||||
kategoriDesaAntiKorupsi.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/landingpage/kategoridak/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
const response = await fetch(`/api/landingpage/kategoridak/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Kategori desa anti korupsi berhasil dihapus");
|
||||
toast.success(
|
||||
result.message || "Kategori desa anti korupsi berhasil dihapus"
|
||||
);
|
||||
await kategoriDesaAntiKorupsi.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus kategori desa anti korupsi");
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus kategori desa anti korupsi"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus kategori desa anti korupsi");
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat menghapus kategori desa anti korupsi"
|
||||
);
|
||||
} finally {
|
||||
kategoriDesaAntiKorupsi.delete.loading = false;
|
||||
}
|
||||
|
||||
@@ -181,7 +181,13 @@ const responden = proxy({
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
tanggal: this.form.tanggal,
|
||||
jenisKelaminId: this.form.jenisKelaminId,
|
||||
ratingId: this.form.ratingId,
|
||||
kelompokUmurId: this.form.kelompokUmurId,
|
||||
}),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (!response.ok || !result?.success) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -58,16 +59,43 @@ const prestasiDesa = proxy({
|
||||
Prisma.PrestasiDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategori: true;
|
||||
kategori: {
|
||||
select: {
|
||||
id: true;
|
||||
name: true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.prestasidesa[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
prestasiDesa.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
prestasiDesa.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
prestasiDesa.findMany.page = page;
|
||||
prestasiDesa.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.prestasidesa["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
prestasiDesa.findMany.data = res.data.data ?? [];
|
||||
prestasiDesa.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
prestasiDesa.findMany.data = [];
|
||||
prestasiDesa.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch prestasi desa paginated:", err);
|
||||
prestasiDesa.findMany.data = [];
|
||||
prestasiDesa.findMany.totalPages = 1;
|
||||
} finally {
|
||||
prestasiDesa.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -283,12 +311,34 @@ const kategoriPrestasi = proxy({
|
||||
id: string;
|
||||
name: string;
|
||||
}> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.kategoriprestasi[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
kategoriPrestasi.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
kategoriPrestasi.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
kategoriPrestasi.findMany.page = page;
|
||||
kategoriPrestasi.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.kategoriprestasi["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kategoriPrestasi.findMany.data = res.data.data ?? [];
|
||||
kategoriPrestasi.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
kategoriPrestasi.findMany.data = [];
|
||||
kategoriPrestasi.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch kategori prestasi paginated:", err);
|
||||
kategoriPrestasi.findMany.data = [];
|
||||
kategoriPrestasi.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kategoriPrestasi.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -65,14 +65,19 @@ const programInovasi = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||
programInovasi.findMany.loading = true; // Use the full path to access the property
|
||||
programInovasi.findMany.page = page;
|
||||
programInovasi.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.programinovasi[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
query
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
@@ -482,14 +487,19 @@ const mediaSosial = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||
mediaSosial.findMany.loading = true; // Use the full path to access the property
|
||||
mediaSosial.findMany.page = page;
|
||||
try {
|
||||
mediaSosial.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.mediasosial[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
|
||||
@@ -58,14 +58,19 @@ const sdgsDesa = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||
sdgsDesa.findMany.loading = true; // Use the full path to access the property
|
||||
sdgsDesa.findMany.page = page;
|
||||
sdgsDesa.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.sdgsdesa[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
|
||||
@@ -49,35 +49,38 @@ const daftarInformasiPublik = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
data: null as
|
||||
| Prisma.DaftarInformasiPublikGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
daftarInformasiPublik.findMany.loading = true; // Use the full path to access the property
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
daftarInformasiPublik.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
daftarInformasiPublik.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.ppid.daftarinformasipublik[
|
||||
"find-many"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
daftarInformasiPublik.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.ppid.daftarinformasipublik["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
daftarInformasiPublik.findMany.data = res.data.data || [];
|
||||
daftarInformasiPublik.findMany.total = res.data.total || 0;
|
||||
daftarInformasiPublik.findMany.totalPages = res.data.totalPages || 1;
|
||||
daftarInformasiPublik.findMany.data = res.data.data ?? [];
|
||||
daftarInformasiPublik.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
console.error("Failed to load daftar informasi publik:", res.data?.message);
|
||||
daftarInformasiPublik.findMany.data = [];
|
||||
daftarInformasiPublik.findMany.total = 0;
|
||||
daftarInformasiPublik.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading daftar informasi publik:", error);
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch daftar informasi publik paginated:", err);
|
||||
daftarInformasiPublik.findMany.data = [];
|
||||
daftarInformasiPublik.findMany.total = 0;
|
||||
daftarInformasiPublik.findMany.totalPages = 1;
|
||||
} finally {
|
||||
daftarInformasiPublik.findMany.loading = false;
|
||||
|
||||
@@ -348,18 +348,34 @@ const posisiOrganisasi = proxy({
|
||||
deskripsi: string | null;
|
||||
hierarki: number;
|
||||
}>,
|
||||
async load() {
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
posisiOrganisasi.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
posisiOrganisasi.findMany.page = page;
|
||||
posisiOrganisasi.findMany.search = search;
|
||||
|
||||
try {
|
||||
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
// The API now returns the id field, so we can use it directly
|
||||
this.data = res.data?.data ?? [];
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
posisiOrganisasi.findMany.data = res.data.data ?? [];
|
||||
posisiOrganisasi.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
posisiOrganisasi.findMany.data = [];
|
||||
posisiOrganisasi.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Find many error:", error);
|
||||
this.data = [];
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch posisi organisasi paginated:", err);
|
||||
posisiOrganisasi.findMany.data = [];
|
||||
posisiOrganisasi.findMany.totalPages = 1;
|
||||
} finally {
|
||||
posisiOrganisasi.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -438,9 +454,9 @@ const pegawai = proxy({
|
||||
|
||||
try {
|
||||
pegawai.create.loading = true;
|
||||
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
|
||||
"create"
|
||||
].post(pegawai.create.form);
|
||||
const res = await ApiFetch.api.ppid.strukturppid.pegawai["create"].post(
|
||||
pegawai.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
toast.success("Pegawai berhasil ditambahkan");
|
||||
await pegawai.findMany.load();
|
||||
@@ -457,42 +473,55 @@ const pegawai = proxy({
|
||||
},
|
||||
|
||||
// In struktur-organisasi.ts
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
pegawai.findMany.loading = true; // Use the full path to access the property
|
||||
pegawai.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
|
||||
"find-many"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PegawaiPPIDGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
posisi: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
pegawai.findMany.loading = true; // Use the full path to access the property
|
||||
pegawai.findMany.page = page;
|
||||
pegawai.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pegawai.findMany.data = res.data.data || [];
|
||||
pegawai.findMany.total = res.data.total || 0;
|
||||
pegawai.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load pegawai:", res.data?.message);
|
||||
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
|
||||
"find-many"
|
||||
].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pegawai.findMany.data = res.data.data || [];
|
||||
pegawai.findMany.total = res.data.total || 0;
|
||||
pegawai.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load pegawai:", res.data?.message);
|
||||
pegawai.findMany.data = [];
|
||||
pegawai.findMany.total = 0;
|
||||
pegawai.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pegawai:", error);
|
||||
pegawai.findMany.data = [];
|
||||
pegawai.findMany.total = 0;
|
||||
pegawai.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pegawai.findMany.loading = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pegawai:", error);
|
||||
pegawai.findMany.data = [];
|
||||
pegawai.findMany.total = 0;
|
||||
pegawai.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pegawai.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as
|
||||
| (Prisma.PegawaiGetPayload<{
|
||||
@@ -521,12 +550,9 @@ findMany: {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
try {
|
||||
pegawai.delete.loading = true;
|
||||
const res = await fetch(
|
||||
`/api/ppid/strukturppid/pegawai/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
}
|
||||
);
|
||||
const res = await fetch(`/api/ppid/strukturppid/pegawai/del/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
const json = await res.json();
|
||||
if (res.ok) {
|
||||
toast.success(json.message ?? "Berhasil hapus pegawai");
|
||||
@@ -555,15 +581,12 @@ findMany: {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/ppid/strukturppid/pegawai/${id}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
const response = await fetch(`/api/ppid/strukturppid/pegawai/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
@@ -677,7 +700,7 @@ findMany: {
|
||||
const stateStrukturPPID = proxy({
|
||||
stateStruktur,
|
||||
posisiOrganisasi,
|
||||
pegawai
|
||||
pegawai,
|
||||
});
|
||||
|
||||
export default stateStrukturPPID;
|
||||
|
||||
@@ -143,8 +143,8 @@ function ListDemografiPekerjaan({ search }: { search: string }) {
|
||||
dataKey="pekerjaan"
|
||||
type="stacked"
|
||||
series={[
|
||||
{ name: 'lakiLaki', color: 'red.6', label: 'Laki - Laki' },
|
||||
{ name: 'perempuan', color: 'orange.6', label: 'Perempuan' },
|
||||
{ name: 'lakiLaki', color: '#5082EE', label: 'Laki - Laki' },
|
||||
{ name: 'perempuan', color: '#6EDF9C', label: 'Perempuan' },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -35,7 +35,7 @@ function EditJumlahPendudukMiskin() {
|
||||
// Set the ID before submitting
|
||||
stateJPM.update.id = id;
|
||||
await stateJPM.update.submit();
|
||||
router.push('/admin/ekonomi/jumlah-penduduk-miskin-2024-2025')
|
||||
router.push('/admin/ekonomi/jumlah-penduduk-miskin')
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
|
||||
@@ -32,7 +32,7 @@ function CreateJumlahPendudukMiskin() {
|
||||
}
|
||||
}
|
||||
resetForm();
|
||||
router.push("/admin/ekonomi/jumlah-penduduk-miskin-2024-2025");
|
||||
router.push("/admin/ekonomi/jumlah-penduduk-miskin");
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
|
||||
@@ -91,7 +91,7 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulList
|
||||
title='List Jumlah Penduduk Miskin'
|
||||
href='/admin/ekonomi/jumlah-penduduk-miskin-2024-2025/create'
|
||||
href='/admin/ekonomi/jumlah-penduduk-miskin/create'
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
@@ -108,7 +108,7 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
|
||||
<TableTd>{item.year}</TableTd>
|
||||
<TableTd>{item.totalPoorPopulation}</TableTd>
|
||||
<TableTd>
|
||||
<Button color='green' onClick={() => router.push(`/admin/ekonomi/jumlah-penduduk-miskin-2024-2025/${item.id}`)}>
|
||||
<Button color='green' onClick={() => router.push(`/admin/ekonomi/jumlah-penduduk-miskin/${item.id}`)}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
|
||||
@@ -8,29 +8,36 @@ import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Cell, Pie, PieChart } from 'recharts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import JudulListTab from '../../../_com/judulListTab';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur';
|
||||
|
||||
function GrafikBerdasarkanPendidikan() {
|
||||
const [search, setSearch] = useState("")
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Grafik Pengangguran Berdasarkan Pendidikan</Title>
|
||||
<ListGrafikBerdasarkanPendidikan />
|
||||
<HeaderSearch
|
||||
title='Detail Data Pengangguran Berdasarkan Pendidikan'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListGrafikBerdasarkanPendidikan search={search}/>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListGrafikBerdasarkanPendidikan() {
|
||||
function ListGrafikBerdasarkanPendidikan({search}: {search: string}) {
|
||||
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanPendidikan)
|
||||
const [donutData, setDonutData] = useState<any[]>([]);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const router = useRouter();
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
|
||||
const handleDelete = async () => {
|
||||
@@ -56,11 +63,11 @@ function ListGrafikBerdasarkanPendidikan() {
|
||||
const D3 = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.D3 || 0), 0);
|
||||
const S1 = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.S1 || 0), 0);
|
||||
setDonutData([
|
||||
{ name: 'SD', value: SD, color: colors['blue-button'], key: 'SD' },
|
||||
{ name: 'SMP', value: SMP, color: '#10A85AFF', key: 'SMP' },
|
||||
{ name: 'SMA', value: SMA, color: '#C07B13FF', key: 'SMA' },
|
||||
{ name: 'D3', value: D3, color: '#1094A8FF', key: 'D3' },
|
||||
{ name: 'S1', value: S1, color: '#A83610FF', key: 'S1' },
|
||||
{ name: 'SD', value: SD, color: '#4b6Ef5', key: 'SD' },
|
||||
{ name: 'SMP', value: SMP, color: '#14b885', key: 'SMP' },
|
||||
{ name: 'SMA', value: SMA, color: '#E6A03B', key: 'SMA' },
|
||||
{ name: 'D3', value: D3, color: '#DB524D', key: 'D3' },
|
||||
{ name: 'S1', value: S1, color: '#1018A8FF', key: 'S1' },
|
||||
]);
|
||||
}
|
||||
}, [stategrafik.findMany.data])
|
||||
@@ -88,13 +95,9 @@ function ListGrafikBerdasarkanPendidikan() {
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Paper bg={colors['white-1']} p={"md"}>
|
||||
<JudulListTab
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
<JudulList
|
||||
title='List Grafik Pengangguran Berdasarkan Pendidikan'
|
||||
href='/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/create'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={16} />}
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
|
||||
@@ -8,29 +8,37 @@ import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Cell, Pie, PieChart } from 'recharts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import JudulListTab from '../../../_com/judulListTab';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur';
|
||||
|
||||
|
||||
function GrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
const [search, setSearch] = useState("")
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Grafik Pengangguran Berdasarkan Usia Kerja</Title>
|
||||
<ListGrafikBerdasarkanUsiaKerjaYangMenganggur />
|
||||
<HeaderSearch
|
||||
title='Detail Data Pengangguran'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListGrafikBerdasarkanUsiaKerjaYangMenganggur search={search} />
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
function ListGrafikBerdasarkanUsiaKerjaYangMenganggur({search}: {search: string}) {
|
||||
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur)
|
||||
const [donutData, setDonutData] = useState<any[]>([]);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const router = useRouter();
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
|
||||
const handleDelete = async () => {
|
||||
@@ -85,13 +93,9 @@ function ListGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Paper bg={colors['white-1']} p={"md"}>
|
||||
<JudulListTab
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
<JudulList
|
||||
title='List Pengangguran Berdasarkan Usia Kerja'
|
||||
href='/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={16} />}
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
|
||||
@@ -66,19 +66,23 @@ function EditDetailDataPengangguran() {
|
||||
const data = stateDetail.findUnique.data;
|
||||
|
||||
if (data) {
|
||||
// Convert year from Date to number
|
||||
const yearValue = data.year instanceof Date ? data.year.getFullYear() : data.year;
|
||||
|
||||
// Set the ID for update
|
||||
stateDetail.update.id = id;
|
||||
|
||||
// Isi state Valtio untuk update
|
||||
// Update Valtio state with converted year
|
||||
stateDetail.update.form = {
|
||||
...data,
|
||||
year: yearValue,
|
||||
percentageChange: data.percentageChange || 0 // Ensure it's always a number
|
||||
};
|
||||
|
||||
// Isi local formData supaya input bisa dikontrol
|
||||
// Update local formData with converted year
|
||||
setFormData({
|
||||
month: data.month,
|
||||
year: data.year,
|
||||
year: yearValue,
|
||||
totalUnemployment: data.totalUnemployment,
|
||||
educatedUnemployment: data.educatedUnemployment,
|
||||
uneducatedUnemployment: data.uneducatedUnemployment,
|
||||
|
||||
@@ -25,7 +25,7 @@ function DetailJumlahPengangguran() {
|
||||
stateDetail.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran")
|
||||
router.push("/admin/ekonomi/jumlah-pengangguran")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ function DetailJumlahPengangguran() {
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"}>Tahun</Text>
|
||||
<Text>{stateDetail.findUnique.data?.year}</Text>
|
||||
<Text>{stateDetail.findUnique.data?.year ? new Date(stateDetail.findUnique.data.year).getFullYear() : ''}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"}>Bulan</Text>
|
||||
@@ -86,7 +86,7 @@ function DetailJumlahPengangguran() {
|
||||
color={"red"}>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/${stateDetail.findUnique.data?.id}/edit`)} color="green">
|
||||
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/${stateDetail.findUnique.data?.id}/edit`)} color="green">
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
@@ -108,4 +108,3 @@ function DetailJumlahPengangguran() {
|
||||
}
|
||||
|
||||
export default DetailJumlahPengangguran;
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ function CreateJumlahPengangguran() {
|
||||
/>
|
||||
<TextInput
|
||||
label="Tahun"
|
||||
type="number"
|
||||
type="date"
|
||||
value={stateDetail.create.form.year}
|
||||
onChange={(e) =>
|
||||
(stateDetail.create.form.year = Number(e.currentTarget.value))
|
||||
|
||||
@@ -8,21 +8,29 @@ import { useEffect, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
import { BarChart } from '@mantine/charts';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
import jumlahPengangguranState from '../../_state/ekonomi/jumlah-pengangguran';
|
||||
import JudulListTab from '../../_com/judulListTab';
|
||||
|
||||
function DetailDataPengangguran() {
|
||||
const [search, setSearch] = useState("")
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Detail Data Pengangguran</Title>
|
||||
<ListDetailDataPengangguran />
|
||||
<HeaderSearch
|
||||
title='Detail Data Pengangguran'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListDetailDataPengangguran search={search} />
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListDetailDataPengangguran() {
|
||||
function ListDetailDataPengangguran({search}: {search: string}) {
|
||||
|
||||
type DetailDataPengangguran = {
|
||||
id: string;
|
||||
@@ -37,7 +45,6 @@ function ListDetailDataPengangguran() {
|
||||
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
|
||||
const stateDetail = useProxy(jumlahPengangguranState.jumlahPengangguran)
|
||||
const router = useRouter();
|
||||
const [search, setSearch] = useState("")
|
||||
|
||||
useShallowEffect(() => {
|
||||
setMounted(true)
|
||||
@@ -50,7 +57,7 @@ function ListDetailDataPengangguran() {
|
||||
setChartData(stateDetail.findMany.data.map((item) => ({
|
||||
id: item.id,
|
||||
month: item.month,
|
||||
year: item.year,
|
||||
year: item.year instanceof Date ? item.year.getFullYear() : Number(item.year),
|
||||
educatedUnemployment: Number(item.educatedUnemployment),
|
||||
uneducatedUnemployment: Number(item.uneducatedUnemployment),
|
||||
percentageChange: Number(item.percentageChange),
|
||||
@@ -78,13 +85,9 @@ function ListDetailDataPengangguran() {
|
||||
<Box>
|
||||
<Stack gap={"md"}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulListTab
|
||||
<JudulList
|
||||
title='List Detail Data Pengangguran'
|
||||
href='/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/create'
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={16} />}
|
||||
href='/admin/ekonomi/jumlah-pengangguran/create'
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
@@ -102,7 +105,7 @@ function ListDetailDataPengangguran() {
|
||||
<TableTd>{item.educatedUnemployment}</TableTd>
|
||||
<TableTd>{item.uneducatedUnemployment}</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/${item.id}`)}>
|
||||
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
|
||||
@@ -55,17 +55,17 @@ function EditLowonganKerja() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
lowonganKerjaState.update.form = {
|
||||
...lowonganKerjaState.update.form,
|
||||
posisi: formData.posisi,
|
||||
namaPerusahaan: formData.namaPerusahaan,
|
||||
lokasi: formData.lokasi,
|
||||
tipePekerjaan: formData.tipePekerjaan,
|
||||
gaji: formData.gaji,
|
||||
deskripsi: formData.deskripsi,
|
||||
kualifikasi: formData.kualifikasi,
|
||||
}
|
||||
await lowonganState.update.update()
|
||||
// Set the ID for the update
|
||||
lowonganState.update.id = params?.id as string;
|
||||
|
||||
// Update the form state
|
||||
lowonganState.update.form = {
|
||||
...lowonganState.update.form,
|
||||
...formData
|
||||
};
|
||||
|
||||
// Call the update function
|
||||
await lowonganState.update.update();
|
||||
toast.success("Lowongan kerja berhasil diperbarui!");
|
||||
router.push("/admin/ekonomi/lowongan-kerja-lokal");
|
||||
} catch (error) {
|
||||
@@ -88,7 +88,7 @@ function EditLowonganKerja() {
|
||||
<TextInput
|
||||
value={formData.posisi}
|
||||
onChange={(val) => {
|
||||
formData.posisi = val.target.value;
|
||||
setFormData(prev => ({ ...prev, posisi: val.target.value }));
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Posisi</Text>}
|
||||
placeholder='Masukkan posisi'
|
||||
@@ -96,7 +96,7 @@ function EditLowonganKerja() {
|
||||
<TextInput
|
||||
value={formData.namaPerusahaan}
|
||||
onChange={(val) => {
|
||||
formData.namaPerusahaan = val.target.value;
|
||||
setFormData(prev => ({ ...prev, namaPerusahaan: val.target.value }));
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Nama Perusahaan</Text>}
|
||||
placeholder='Masukkan nama perusahaan'
|
||||
@@ -104,7 +104,7 @@ function EditLowonganKerja() {
|
||||
<TextInput
|
||||
value={formData.lokasi}
|
||||
onChange={(val) => {
|
||||
formData.lokasi = val.target.value;
|
||||
setFormData(prev => ({ ...prev, lokasi: val.target.value }));
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Lokasi</Text>}
|
||||
placeholder='Masukkan lokasi'
|
||||
@@ -112,7 +112,7 @@ function EditLowonganKerja() {
|
||||
<TextInput
|
||||
value={formData.tipePekerjaan}
|
||||
onChange={(val) => {
|
||||
formData.tipePekerjaan = val.target.value;
|
||||
setFormData(prev => ({ ...prev, tipePekerjaan: val.target.value }));
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Tipe Pekerjaan</Text>}
|
||||
placeholder='Masukkan tipe pekerjaan'
|
||||
@@ -120,7 +120,7 @@ function EditLowonganKerja() {
|
||||
<TextInput
|
||||
value={formData.gaji}
|
||||
onChange={(val) => {
|
||||
formData.gaji = val.target.value;
|
||||
setFormData(prev => ({ ...prev, gaji: val.target.value }));
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Gaji selama 1 bulan</Text>}
|
||||
placeholder='Masukkan gaji'
|
||||
@@ -130,7 +130,7 @@ function EditLowonganKerja() {
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => {
|
||||
formData.deskripsi = val;
|
||||
setFormData(prev => ({ ...prev, deskripsi: val }));
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -139,7 +139,7 @@ function EditLowonganKerja() {
|
||||
<EditEditor
|
||||
value={formData.kualifikasi}
|
||||
onChange={(val) => {
|
||||
formData.kualifikasi = val;
|
||||
setFormData(prev => ({ ...prev, kualifikasi: val }));
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
@@ -30,20 +30,21 @@ function ListLowonganKerjaLokal({ search }: { search: string }) {
|
||||
const lowonganState = useProxy(lowonganKerjaState)
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = lowonganState.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
lowonganState.findMany.load();
|
||||
}, [])
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = (lowonganState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.posisi.toLowerCase().includes(keyword) ||
|
||||
item.namaPerusahaan.toLowerCase().includes(keyword) ||
|
||||
item.lokasi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
const filteredData = data || []
|
||||
|
||||
if (!lowonganState.findMany.data) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -60,18 +61,24 @@ function ListLowonganKerjaLokal({ search }: { search: string }) {
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Bekerja Sebagai</TableTh>
|
||||
<TableTh>Nama Usaha</TableTh>
|
||||
<TableTh>Alamat Usaha</TableTh>
|
||||
<TableTh>Pekerjaan</TableTh>
|
||||
<TableTh>Nama Perusahaan</TableTh>
|
||||
<TableTh>Lokasi</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.posisi}</TableTd>
|
||||
<TableTd>{item.namaPerusahaan}</TableTd>
|
||||
<TableTd>{item.lokasi}</TableTd>
|
||||
<TableTd>
|
||||
<Text fz={"md"}>{item.posisi}</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text fz={"md"}>{item.namaPerusahaan}</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text fz={"md"}>{item.lokasi}</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/ekonomi/lowongan-kerja-lokal/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
@@ -82,6 +89,14 @@ function ListLowonganKerjaLokal({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my="md"
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
@@ -13,31 +13,39 @@ import pasarDesaState from '../../../_state/ekonomi/pasar-desa/pasar-desa';
|
||||
|
||||
|
||||
function PasarDesa() {
|
||||
const [search, setSearch] = useState("")
|
||||
const [search2, setSearch2] = useState("")
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Kategori Produk'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
value={search2}
|
||||
onChange={(e) => setSearch2(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPasarDesa search={search} />
|
||||
<ListPasarDesa search2={search2} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPasarDesa({ search }: { search: string }) {
|
||||
function ListPasarDesa({ search2 }: { search2: string }) {
|
||||
const statePasar = useProxy(pasarDesaState.kategoriProduk)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
// const params = useParams()
|
||||
const router = useRouter()
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = statePasar.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
statePasar.findMany.load()
|
||||
}, [])
|
||||
load(page, 10, search2)
|
||||
}, [page, search2])
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
@@ -47,14 +55,9 @@ function ListPasarDesa({ search }: { search: string }) {
|
||||
}
|
||||
}
|
||||
|
||||
const filteredData = (statePasar.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
const filteredData = data || []
|
||||
|
||||
if (!statePasar.findMany.data) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -99,6 +102,14 @@ function ListPasarDesa({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my={"md"}
|
||||
/>
|
||||
</Center>
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useRouter } from 'next/navigation';
|
||||
@@ -30,21 +30,21 @@ function ListPasarDesa({ search }: { search: string }) {
|
||||
const statePasar = useProxy(pasarDesaState.pasarDesa)
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = statePasar.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
statePasar.findMany.load()
|
||||
}, [])
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = (statePasar.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama.toLowerCase().includes(keyword) ||
|
||||
item.harga.toString().toLowerCase().includes(keyword) ||
|
||||
item.rating.toString().toLowerCase().includes(keyword) ||
|
||||
item.alamatUsaha.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
const filteredData = data || []
|
||||
|
||||
if (!statePasar.findMany.data) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -86,6 +86,14 @@ function ListPasarDesa({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my={"md"}
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
@@ -23,7 +23,7 @@ function ProgramKemiskinan() {
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListProgramKemiskinan search={search}/>
|
||||
<ListProgramKemiskinan search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -34,14 +34,22 @@ function ListProgramKemiskinan({ search }: { search: string }) {
|
||||
const [lineChart, setLineChart] = useState<any[]>([]);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = programState.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
setMounted(true)
|
||||
programState.findMany.load()
|
||||
load(page, 10, search)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (programState.findMany.data) {
|
||||
const chartData = programState.findMany.data
|
||||
if (data) {
|
||||
const chartData = data
|
||||
.filter(item => item.statistik)
|
||||
.map(item => ({
|
||||
tahun: item.statistik?.tahun,
|
||||
@@ -52,18 +60,11 @@ function ListProgramKemiskinan({ search }: { search: string }) {
|
||||
setLineChart(chartData);
|
||||
|
||||
}
|
||||
}, [programState.findMany.data])
|
||||
}, [data])
|
||||
|
||||
const filteredData = (programState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword) ||
|
||||
item.statistik?.tahun.toString().includes(keyword)
|
||||
);
|
||||
});
|
||||
const filteredData = data || []
|
||||
|
||||
if (!programState.findMany.data) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -112,7 +113,7 @@ function ListProgramKemiskinan({ search }: { search: string }) {
|
||||
<Box >
|
||||
<Title pb={10} order={3}>Grafik Berdasarkan Responden</Title>
|
||||
{mounted && lineChart.length > 0 ? (<Box style={{ width: '100%', height: 'auto', }}>
|
||||
<Box w={"100%"} style={{overflowX: 'auto'}}>
|
||||
<Box w={"100%"} style={{ overflowX: 'auto' }}>
|
||||
<LineChart
|
||||
width={820}
|
||||
height={300}
|
||||
@@ -143,6 +144,14 @@ function ListProgramKemiskinan({ search }: { search: string }) {
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my={"md"}
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Group, Paper, Stack, Text, 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';
|
||||
import grafikSektorUnggulan from '../../../_state/ekonomi/sektor-unggulan-desa';
|
||||
import CreateEditor from '../../../_com/createEditor';
|
||||
|
||||
function CreateSektorUnggulanDesa() {
|
||||
const stateGrafik= useProxy(grafikSektorUnggulan);
|
||||
const stateGrafik = useProxy(grafikSektorUnggulan);
|
||||
const [chartData, setChartData] = useState<any[]>([]);
|
||||
const router = useRouter()
|
||||
|
||||
@@ -54,15 +55,15 @@ function CreateSektorUnggulanDesa() {
|
||||
stateGrafik.create.form.name = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Deskripsi Sektor Unggulan"
|
||||
type="text"
|
||||
value={stateGrafik.create.form.description}
|
||||
placeholder="Masukkan deskripsi sektor unggulan"
|
||||
onChange={(val) => {
|
||||
stateGrafik.create.form.description = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Sektor Ungggulan</Text>
|
||||
<CreateEditor
|
||||
value={stateGrafik.create.form.description}
|
||||
onChange={(val) => {
|
||||
stateGrafik.create.form.description = val;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<TextInput
|
||||
label="Jumlah"
|
||||
type="number"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||
import { Box, Button, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
@@ -30,7 +30,7 @@ function SektorUnggulanDesa() {
|
||||
function ListSektorUnggulanDesa({ search }: { search: string }) {
|
||||
const router = useRouter();
|
||||
const state = useProxy(grafikSektorUnggulan);
|
||||
const [chartData, setChartData] = useState<{id: string; name: string; description: string | null; value: number | null}[]>([]);
|
||||
const [chartData, setChartData] = useState<{ id: string; name: string; description: string | null; value: number | null }[]>([]);
|
||||
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
|
||||
const isTablet = useMediaQuery('(max-width: 1024px)')
|
||||
const isMobile = useMediaQuery('(max-width: 768px)')
|
||||
@@ -61,38 +61,41 @@ function ListSektorUnggulanDesa({ search }: { search: string }) {
|
||||
}, [state.findMany.data]);
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulList
|
||||
title='List Sektor Unggulan Desa'
|
||||
href='/admin/ekonomi/sektor-unggulan-desa/create'
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama Sektor Unggulan</TableTh>
|
||||
<TableTh>Deskripsi Sektor Unggulan</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>{item.description}</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/ekonomi/sektor-unggulan-desa/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulList
|
||||
title='List Sektor Unggulan Desa'
|
||||
href='/admin/ekonomi/sektor-unggulan-desa/create'
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama Sektor Unggulan</TableTh>
|
||||
<TableTh>Deskripsi Sektor Unggulan</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate={"end"} fz={'sm'} lineClamp={1} dangerouslySetInnerHTML={{ __html: item.description || '' }}></Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/ekonomi/sektor-unggulan-desa/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
|
||||
{/* Chart */}
|
||||
{!mounted && !chartData ? (
|
||||
{/* Chart */}
|
||||
{!mounted && !chartData ? (
|
||||
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title pb={10} order={3}>Grafik Hasil Kepuasan Masyarakat</Title>
|
||||
@@ -115,6 +118,7 @@ function ListSektorUnggulanDesa({ search }: { search: string }) {
|
||||
</Paper>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ function DetailKeamananLingkungan() {
|
||||
{keamananState.findUnique.data ? (
|
||||
<Paper key={keamananState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
|
||||
<Image w={{ base: 150, md: 490}} src={keamananState.findUnique.data?.image?.link} alt="gambar" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Judul Keamanan Lingkungan</Text>
|
||||
<Text fz={"lg"}>{keamananState.findUnique.data?.name}</Text>
|
||||
@@ -62,10 +66,6 @@ function DetailKeamananLingkungan() {
|
||||
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
|
||||
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: keamananState.findUnique.data?.deskripsi }} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
|
||||
<Image w={{ base: 150, md: 150, lg: 150 }} src={keamananState.findUnique.data?.image?.link} alt="gambar" />
|
||||
</Box>
|
||||
<Flex gap={"xs"} mt={10}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
@@ -30,19 +30,21 @@ function ListKeamananLingkungan({ search }: { search: string }) {
|
||||
const keamananState = useProxy(keamananLingkunganState)
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = keamananState.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
keamananState.findMany.load()
|
||||
}, [])
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = (keamananState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
const filteredData = data || []
|
||||
|
||||
if (!keamananState.findMany.data) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -67,9 +69,15 @@ function ListKeamananLingkungan({ search }: { search: string }) {
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>
|
||||
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
||||
<Box w={180}>
|
||||
<Text fz={"md"} truncate={"end"} lineClamp={1}>{item.name}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Box w={250}>
|
||||
<Text fz={"md"} truncate={"end"} lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/keamanan/keamanan-lingkungan-pecalang-patwal/${item.id}`)}>
|
||||
@@ -81,6 +89,14 @@ function ListKeamananLingkungan({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my={"md"}
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
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';
|
||||
@@ -21,7 +21,7 @@ function PolsekTerdekat() {
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPolsekTerdekat search={search}/>
|
||||
<ListPolsekTerdekat search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -30,20 +30,21 @@ function ListPolsekTerdekat({ search }: { search: string }) {
|
||||
const polsekState = useProxy(polsekTerdekat)
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = polsekState.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
polsekState.findMany.load()
|
||||
}, [])
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = (polsekState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama.toLowerCase().includes(keyword) ||
|
||||
item.jarakKeDesa.toLowerCase().includes(keyword) ||
|
||||
item.alamat.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
const filteredData = data || []
|
||||
|
||||
if (!polsekState.findMany.data) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -64,24 +65,44 @@ function ListPolsekTerdekat({ search }: { search: string }) {
|
||||
<TableTh>Jarak Polsek</TableTh>
|
||||
<TableTh>Alamat</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.nama}</TableTd>
|
||||
<TableTd>{item.jarakKeDesa}</TableTd>
|
||||
<TableTd>{item.alamat}</TableTd>
|
||||
<TableTd>
|
||||
<Box w={180}>
|
||||
<Text fz='md' truncate={"end"} lineClamp={1}>{item.nama}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Box w={180}>
|
||||
<Text fz='md' truncate={"end"} lineClamp={1}>{item.jarakKeDesa}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Box w={250}>
|
||||
<Text fz='md' truncate={"end"} lineClamp={1}>{item.alamat}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/keamanan/polsek-terdekat/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
<IconDeviceImac size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Table>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my="md"
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
@@ -30,19 +30,21 @@ function ListTipsKeamanan({ search }: { search: string }) {
|
||||
const stateKeamanan = useProxy(tipsKeamananState)
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = stateKeamanan.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
stateKeamanan.findMany.load()
|
||||
}, [])
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = (stateKeamanan.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.judul.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
const filteredData = data || []
|
||||
|
||||
if (!stateKeamanan.findMany.data) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -67,9 +69,15 @@ function ListTipsKeamanan({ search }: { search: string }) {
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.judul}</TableTd>
|
||||
<TableTd>
|
||||
<Text fz={"xs"} dangerouslySetInnerHTML={{__html: item.deskripsi}} />
|
||||
<Box w={150}>
|
||||
<Text fz={"md"} truncate={"end"} lineClamp={1}>{item.judul}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Box w={250}>
|
||||
<Text fz={"md"} truncate={"end"} lineClamp={1} dangerouslySetInnerHTML={{__html: item.deskripsi}} />
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/keamanan/tips-keamanan/${item.id}`)}>
|
||||
@@ -81,6 +89,14 @@ function ListTipsKeamanan({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my="md"
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ function DetailFasilitasKesehatan() {
|
||||
<GridCol span={12}>
|
||||
<Text fz={"xl"} fw={"bold"}>Detail Fasilitas Kesehatan</Text>
|
||||
</GridCol>
|
||||
<GridCol span={12}>
|
||||
{/* <GridCol span={12}>
|
||||
<Flex gap={"xs"}>
|
||||
<Button color={colors['blue-button']} onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${params?.id}/dokter-tenaga-medis`)}>
|
||||
Tambah Dokter
|
||||
@@ -60,7 +60,7 @@ function DetailFasilitasKesehatan() {
|
||||
Tambah Layanan
|
||||
</Button>
|
||||
</Flex>
|
||||
</GridCol>
|
||||
</GridCol> */}
|
||||
</Grid>
|
||||
{stateFasilitasKesehatan.findUnique.data ? (
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { ActionIcon, Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { ActionIcon, Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconDeviceImacCog, IconFile, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
@@ -30,19 +30,22 @@ function APBDes() {
|
||||
function ListAPBDes({ search }: { search: string }) {
|
||||
const listState = useProxy(apbdes)
|
||||
const router = useRouter();
|
||||
useEffect(() => {
|
||||
listState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (listState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.jumlah.toLowerCase().includes(keyword)
|
||||
)
|
||||
});
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = listState.findMany
|
||||
|
||||
if (!listState.findMany.data) {
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = data || []
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -88,7 +91,7 @@ function ListAPBDes({ search }: { search: string }) {
|
||||
rel="noopener noreferrer"
|
||||
variant='transparent'
|
||||
>
|
||||
<IconFile size={25} color={colors['blue-button']}/>
|
||||
<IconFile size={25} color={colors['blue-button']} />
|
||||
</ActionIcon>
|
||||
) : (
|
||||
<Text>Tidak ada dokumen tersedia</Text>
|
||||
@@ -106,6 +109,14 @@ function ListAPBDes({ search }: { search: string }) {
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my={"md"}
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
@@ -50,19 +50,11 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
load(page, 10)
|
||||
}, [page])
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
}, [data, search]);
|
||||
const filteredData = data || []
|
||||
|
||||
// Handle loading state
|
||||
if (loading || !data) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'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 { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
@@ -38,22 +38,11 @@ function ListDesaAntiKorupsi({ search }: { search: string }) {
|
||||
load,
|
||||
} = listState.findMany;
|
||||
|
||||
useEffect(() => {
|
||||
load(page, 10);
|
||||
}, [page]);
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi?.toLowerCase().includes(keyword) ||
|
||||
item.kategori?.name?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
.sort((a, b) => b.createdAt - a.createdAt);
|
||||
}, [data, search]);
|
||||
const filteredData = data || []
|
||||
|
||||
// Handle loading state
|
||||
if (loading || !data) {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
function LayoutTabsKepuasan({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const tabs = [
|
||||
{
|
||||
label: "Grafik Kepuasan Masyarakat",
|
||||
value: "grafikkepuasannamasyarakat",
|
||||
href: "/admin/landing-page/indeks-kepuasan-masyarakat/grafik-kepuasan-masyarakat"
|
||||
},
|
||||
{
|
||||
label: "Responden",
|
||||
value: "responden",
|
||||
href: "/admin/landing-page/indeks-kepuasan-masyarakat/responden"
|
||||
},
|
||||
|
||||
];
|
||||
const curentTab = tabs.find(tab => tab.href === pathname)
|
||||
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
|
||||
|
||||
const handleTabChange = (value: string | null) => {
|
||||
const tab = tabs.find(t => t.value === value)
|
||||
if (tab) {
|
||||
router.push(tab.href)
|
||||
}
|
||||
setActiveTab(value)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const match = tabs.find(tab => tab.href === pathname)
|
||||
if (match) {
|
||||
setActiveTab(match.value)
|
||||
}
|
||||
}, [pathname])
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={3}>Indeks Kepuasan Masyarakat</Title>
|
||||
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
|
||||
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
||||
{tabs.map((e, i) => (
|
||||
<TabsTab key={i} value={e.value}>{e.label}</TabsTab>
|
||||
))}
|
||||
</TabsList>
|
||||
{tabs.map((e, i) => (
|
||||
<TabsPanel key={i} value={e.value}>
|
||||
{/* Konten dummy, bisa diganti tergantung routing */}
|
||||
<></>
|
||||
</TabsPanel>
|
||||
))}
|
||||
</Tabs>
|
||||
{children}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default LayoutTabsKepuasan;
|
||||
@@ -0,0 +1,256 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client';
|
||||
import colors from '@/con/colors';
|
||||
import { PieChart, BarChart } from '@mantine/charts'; // ✅ Ganti recharts dengan Mantine
|
||||
import {
|
||||
Box,
|
||||
Center,
|
||||
Flex,
|
||||
Paper,
|
||||
SimpleGrid,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
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<ChartDataItem[]>([]);
|
||||
const [donutDataRating, setDonutDataRating] = useState<ChartDataItem[]>([]);
|
||||
const [donutDataKelompokUmur, setDonutDataKelompokUmur] = useState<ChartDataItem[]>([]);
|
||||
const [barChartData, setBarChartData] = useState<Array<{ month: string; count: number }>>([]);
|
||||
|
||||
useShallowEffect(() => {
|
||||
if (!data && !loading) {
|
||||
state.findMany.load();
|
||||
return;
|
||||
}
|
||||
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-laki', value: totalLaki, color: colors['blue-button'] },
|
||||
{ name: 'Perempuan', value: totalPerempuan, color: '#10A85AFF' },
|
||||
]);
|
||||
|
||||
// Update rating chart data
|
||||
setDonutDataRating([
|
||||
{ name: 'Sangat Baik', value: totalSangatBaik, color: colors['blue-button'] },
|
||||
{ name: 'Baik', value: totalBaik, color: '#10A85AFF' },
|
||||
{ name: 'Kurang Baik', value: totalKurangBaik, color: '#FFA500' },
|
||||
{ name: 'Sangat Kurang Baik', value: totalSangatKurangBaik, color: '#FF4500' },
|
||||
]);
|
||||
|
||||
// Update age group chart data
|
||||
setDonutDataKelompokUmur([
|
||||
{ name: 'Muda', value: totalMuda, color: colors['blue-button'] },
|
||||
{ name: 'Dewasa', value: totalDewasa, color: '#10A85AFF' },
|
||||
{ name: 'Lansia', value: totalLansia, color: '#FFA500' },
|
||||
]);
|
||||
|
||||
// Process data for bar chart (group by month)
|
||||
const monthYearMap = new Map<string, number>();
|
||||
|
||||
data.forEach((item: any) => {
|
||||
// Try both createdAt and tanggal fields
|
||||
const dateValue = item.tanggal || item.createdAt;
|
||||
if (!dateValue) return;
|
||||
|
||||
const parsedDate = new Date(dateValue);
|
||||
if (isNaN(parsedDate.getTime())) return;
|
||||
|
||||
const month = parsedDate.getMonth() + 1;
|
||||
const year = parsedDate.getFullYear();
|
||||
const monthYearKey = `${year}-${String(month).padStart(2, '0')}`;
|
||||
|
||||
monthYearMap.set(monthYearKey, (monthYearMap.get(monthYearKey) || 0) + 1);
|
||||
});
|
||||
|
||||
// Convert map to array and sort by date
|
||||
const barData = Array.from(monthYearMap.entries())
|
||||
.map(([key, count]) => {
|
||||
const [year, month] = key.split('-');
|
||||
const monthName = new Date(Number(year), Number(month) - 1, 1)
|
||||
.toLocaleString('id-ID', { month: 'long' });
|
||||
return {
|
||||
month: `${monthName} ${year}`,
|
||||
count,
|
||||
sortKey: parseInt(`${year}${String(month).padStart(2, '0')}`, 10)
|
||||
};
|
||||
})
|
||||
.sort((a, b) => a.sortKey - b.sortKey)
|
||||
.map(({ month, count }) => ({ month, count }));
|
||||
|
||||
setBarChartData(barData);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton height={730} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Text c="dimmed" ta="center" my="md">
|
||||
Belum ada data untuk ditampilkan
|
||||
</Text>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack gap="xs">
|
||||
{/* Bar Chart - Data per Tanggal */}
|
||||
<Paper bg={colors['white-1']} p="md" radius="md" mb="md">
|
||||
<Title order={4} mb="md" ta="center">Jumlah Responden per Bulan</Title>
|
||||
<Box h={300}>
|
||||
<BarChart
|
||||
h={300}
|
||||
data={barChartData}
|
||||
dataKey="month"
|
||||
series={[{ name: 'count', color: colors['blue-button'] }]}
|
||||
tickLine="y"
|
||||
xAxisLabel="Bulan"
|
||||
yAxisLabel="Jumlah Responden"
|
||||
withTooltip
|
||||
tooltipAnimationDuration={200}
|
||||
/>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
<SimpleGrid cols={{ base: 1, md: 3 }}>
|
||||
{/* Chart Jenis Kelamin */}
|
||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||
<Stack>
|
||||
<Title order={4}>Jenis Kelamin</Title>
|
||||
{donutDataJenisKelamin.every(item => item.value === 0) ? (
|
||||
<Text c="dimmed" ta="center" my="md">
|
||||
Belum ada data untuk ditampilkan dalam grafik
|
||||
</Text>
|
||||
) : (
|
||||
<Box>
|
||||
<Center>
|
||||
<PieChart
|
||||
withLabels
|
||||
withTooltip
|
||||
labelsType="percent"
|
||||
size={250}
|
||||
data={donutDataJenisKelamin}
|
||||
/>
|
||||
</Center>
|
||||
<Stack gap="sm" mt="md">
|
||||
{donutDataJenisKelamin.map((entry) => (
|
||||
<Flex key={entry.name} gap="md" align="center">
|
||||
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
||||
<Text size="sm">{entry.name}: {entry.value}</Text>
|
||||
</Flex>
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Chart Rating */}
|
||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||
<Stack>
|
||||
<Title order={4}>Pilihan</Title>
|
||||
{donutDataRating.every(item => item.value === 0) ? (
|
||||
<Text c="dimmed" ta="center" my="md">
|
||||
Belum ada data untuk ditampilkan dalam grafik
|
||||
</Text>
|
||||
) : (
|
||||
<Box>
|
||||
<Center>
|
||||
<PieChart
|
||||
withLabels
|
||||
withTooltip
|
||||
labelsType="percent"
|
||||
size={250}
|
||||
data={donutDataRating}
|
||||
/>
|
||||
</Center>
|
||||
<Stack gap="sm" mt="md">
|
||||
{donutDataRating.map((entry) => (
|
||||
<Flex key={entry.name} gap="md" align="center">
|
||||
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
||||
<Text size="sm">{entry.name}: {entry.value}</Text>
|
||||
</Flex>
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Chart Kelompok Umur */}
|
||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||
<Stack>
|
||||
<Title order={4}>Umur</Title>
|
||||
{donutDataKelompokUmur.every(item => item.value === 0) ? (
|
||||
<Text c="dimmed" ta="center" my="md">
|
||||
Belum ada data untuk ditampilkan dalam grafik
|
||||
</Text>
|
||||
) : (
|
||||
<Box>
|
||||
<Center>
|
||||
<PieChart
|
||||
withLabels
|
||||
withTooltip
|
||||
labelsType="percent"
|
||||
size={250}
|
||||
data={donutDataKelompokUmur}
|
||||
/>
|
||||
</Center>
|
||||
<Stack gap="sm" mt="md">
|
||||
{donutDataKelompokUmur.map((entry) => (
|
||||
<Flex key={entry.name} gap="md" align="center">
|
||||
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
||||
<Text size="sm">{entry.name}: {entry.value}</Text>
|
||||
</Flex>
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
</SimpleGrid>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import LayoutTabsKepuasan from './_lib/layoutTab';
|
||||
|
||||
function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<LayoutTabsKepuasan>
|
||||
{children}
|
||||
</LayoutTabsKepuasan>
|
||||
);
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,190 @@
|
||||
'use client'
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import React, { useEffect, useState } 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';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
interface FormResponden {
|
||||
name: string;
|
||||
tanggal: string;
|
||||
jenisKelaminId: string;
|
||||
ratingId: string;
|
||||
kelompokUmurId: string;
|
||||
}
|
||||
|
||||
function EditResponden() {
|
||||
const router = useRouter()
|
||||
const params = useParams() as { id: string }
|
||||
const state = useProxy(indeksKepuasanState.responden)
|
||||
const id = params.id
|
||||
const [formData, setFormData] = useState<FormResponden>({
|
||||
name: '',
|
||||
tanggal: '',
|
||||
jenisKelaminId: '',
|
||||
ratingId: '',
|
||||
kelompokUmurId: '',
|
||||
})
|
||||
useEffect(() => {
|
||||
indeksKepuasanState.jenisKelaminResponden.findMany.load();
|
||||
indeksKepuasanState.pilihanRatingResponden.findMany.load();
|
||||
indeksKepuasanState.kelompokUmurResponden.findMany.load();
|
||||
|
||||
const loadResponden = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await state.update.load(id);
|
||||
if (data) {
|
||||
state.update.id = id;
|
||||
|
||||
state.update.form = {
|
||||
name: data.name,
|
||||
tanggal: data.tanggal,
|
||||
jenisKelaminId: data.jenisKelaminId,
|
||||
ratingId: data.ratingId,
|
||||
kelompokUmurId: data.kelompokUmurId,
|
||||
};
|
||||
|
||||
setFormData({
|
||||
name: data.name,
|
||||
tanggal: data.tanggal,
|
||||
jenisKelaminId: data.jenisKelaminId,
|
||||
ratingId: data.ratingId,
|
||||
kelompokUmurId: data.kelompokUmurId,
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading program penghijauan:", error);
|
||||
toast.error("Gagal memuat data program penghijauan");
|
||||
}
|
||||
}
|
||||
|
||||
loadResponden();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
state.update.id = id;
|
||||
state.update.form = { ...formData }; // <-- sinkronisasi manual
|
||||
await state.update.submit();
|
||||
router.push('/admin/ppid/ikm-desa-darmasaba/responden')
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack size={20} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>Edit Responden</Title>
|
||||
<TextInput
|
||||
label="Nama"
|
||||
type='text'
|
||||
placeholder="masukkan nama"
|
||||
value={formData.name}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
name: val.currentTarget.value
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Tanggal"
|
||||
type="date"
|
||||
placeholder='Pilih tanggal'
|
||||
value={formData.tanggal ? new Date(formData.tanggal).toISOString().split('T')[0] : ''}
|
||||
onChange={(e) => {
|
||||
const selectedDate = e.currentTarget.value;
|
||||
setFormData({
|
||||
...formData,
|
||||
tanggal: selectedDate,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
key={"jenisKelamin"}
|
||||
label={<Text fw="bold" fz="sm">Jenis Kelamin</Text>}
|
||||
placeholder="Pilih jenis kelamin"
|
||||
value={formData.jenisKelaminId}
|
||||
onChange={(val) => setFormData({ ...formData, jenisKelaminId: val || "" })}
|
||||
data={
|
||||
(indeksKepuasanState.jenisKelaminResponden.findMany.data || [])
|
||||
.filter(Boolean) // Hapus null/undefined
|
||||
.map((v) => ({
|
||||
value: v.id || '',
|
||||
label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
|
||||
}))
|
||||
}
|
||||
disabled={indeksKepuasanState.jenisKelaminResponden.findMany.loading} // ✅ disable saat loading
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
error={!formData.jenisKelaminId ? "Pilih jenis kelamin" : undefined}
|
||||
/>
|
||||
<Select
|
||||
key={"rating"}
|
||||
value={formData.ratingId}
|
||||
onChange={(val) => setFormData({ ...formData, ratingId: val || "" })}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Rating</Text>}
|
||||
placeholder='Pilih rating'
|
||||
data={
|
||||
(indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
|
||||
.filter(Boolean)
|
||||
.map((v) => ({
|
||||
value: v.id || '',
|
||||
label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
|
||||
}))
|
||||
}
|
||||
disabled={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
error={!formData.ratingId ? "Pilih rating" : undefined}
|
||||
/>
|
||||
|
||||
<Select
|
||||
key={"kelompokUmur"}
|
||||
value={formData.kelompokUmurId}
|
||||
onChange={(val) => setFormData({ ...formData, kelompokUmurId: val || "" })}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Kelompok Umur</Text>}
|
||||
placeholder='Pilih kelompok umur'
|
||||
data={
|
||||
(indeksKepuasanState.kelompokUmurResponden.findMany.data || [])
|
||||
.filter(Boolean)
|
||||
.map((v) => ({
|
||||
value: v.id || '',
|
||||
label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
|
||||
}))
|
||||
}
|
||||
disabled={indeksKepuasanState.kelompokUmurResponden.findMany.loading}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
error={!formData.kelompokUmurId ? "Pilih kelompok umur" : undefined}
|
||||
/>
|
||||
|
||||
<Button
|
||||
mt={10}
|
||||
bg={colors['blue-button']}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditResponden;
|
||||
@@ -0,0 +1,116 @@
|
||||
'use client'
|
||||
|
||||
import { ModalKonfirmasiHapus } from "@/app/admin/(dashboard)/_com/modalKonfirmasiHapus"
|
||||
import indeksKepuasanState from "@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan"
|
||||
import colors from "@/con/colors"
|
||||
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from "@mantine/core"
|
||||
import { useShallowEffect } from "@mantine/hooks"
|
||||
import { IconArrowBack, IconEdit, IconX } from "@tabler/icons-react"
|
||||
import { useRouter, useParams } from "next/navigation"
|
||||
import { useState } from "react"
|
||||
import { useProxy } from "valtio/utils"
|
||||
|
||||
export default function DetailResponden() {
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const stateDetail = useProxy(indeksKepuasanState.responden)
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
|
||||
useShallowEffect(() => {
|
||||
stateDetail.findUnique.load(params?.id as string)
|
||||
}, [params?.id])
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
stateDetail.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/ppid/ikm-desa-darmasaba/responden")
|
||||
}
|
||||
}
|
||||
|
||||
if (!stateDetail.findUnique.data) {
|
||||
return (
|
||||
<Stack>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Detail Responden</Text>
|
||||
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Nama Responden</Text>
|
||||
<Text fz={"lg"}>{stateDetail.findUnique.data?.name}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Tanggal</Text>
|
||||
<Text fz={"lg"}>{
|
||||
stateDetail.findUnique.data?.tanggal
|
||||
? new Date(stateDetail.findUnique.data.tanggal).toLocaleDateString('id-ID')
|
||||
: '-'
|
||||
}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Jenis Kelamin</Text>
|
||||
<Text fz={"lg"}>{stateDetail.findUnique.data?.jenisKelamin?.name}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Rating</Text>
|
||||
<Text fz={"lg"}>{stateDetail.findUnique.data?.rating?.name}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Kelompok Umur</Text>
|
||||
<Text fz={"lg"}>{stateDetail.findUnique.data?.kelompokUmur?.name}</Text>
|
||||
</Box>
|
||||
<Flex gap={"xs"} mt={10}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (stateDetail.findUnique.data) {
|
||||
setSelectedId(stateDetail.findUnique.data.id);
|
||||
setModalHapus(true);
|
||||
}
|
||||
}}
|
||||
disabled={stateDetail.delete.loading || !stateDetail.findUnique.data}
|
||||
color={"red"}
|
||||
>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (stateDetail.findUnique.data) {
|
||||
router.push(`/admin/ppid/ikm-desa-darmasaba/responden/${stateDetail.findUnique.data.id}/edit`);
|
||||
}
|
||||
}}
|
||||
disabled={!stateDetail.findUnique.data}
|
||||
color={"green"}
|
||||
>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text="Apakah anda yakin ingin menghapus responden ini?"
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
'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, Select, Text } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
|
||||
function RespondenCreate() {
|
||||
const router = useRouter();
|
||||
const stategrafikBerdasarkanResponden = useProxy(indeksKepuasanState.responden)
|
||||
const [donutData, setDonutData] = useState<any[]>([]);
|
||||
|
||||
const resetForm = () => {
|
||||
stategrafikBerdasarkanResponden.create.form = {
|
||||
...stategrafikBerdasarkanResponden.create.form,
|
||||
name: "",
|
||||
tanggal: "",
|
||||
jenisKelaminId: "",
|
||||
ratingId: "",
|
||||
kelompokUmurId: "",
|
||||
}
|
||||
}
|
||||
|
||||
useShallowEffect(() => {
|
||||
indeksKepuasanState.jenisKelaminResponden.findMany.load()
|
||||
indeksKepuasanState.pilihanRatingResponden.findMany.load()
|
||||
indeksKepuasanState.kelompokUmurResponden.findMany.load()
|
||||
})
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const id = await stategrafikBerdasarkanResponden.create.create();
|
||||
if (typeof id !== 'undefined') {
|
||||
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/responden");
|
||||
} catch (error) {
|
||||
console.error('Error submitting form:', error);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack size={20} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik</Title>
|
||||
<TextInput
|
||||
label="Nama"
|
||||
type='text'
|
||||
placeholder="masukkan nama"
|
||||
value={stategrafikBerdasarkanResponden.create.form.name}
|
||||
onChange={(val) => {
|
||||
stategrafikBerdasarkanResponden.create.form.name = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Tanggal"
|
||||
type="date"
|
||||
placeholder="masukkan tanggal"
|
||||
value={stategrafikBerdasarkanResponden.create.form.tanggal}
|
||||
onChange={(val) => {
|
||||
stategrafikBerdasarkanResponden.create.form.tanggal = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
key={"jenisKelamin"}
|
||||
label={"Jenis Kelamin"}
|
||||
placeholder={indeksKepuasanState.jenisKelaminResponden.findMany.loading ? 'Memuat...' : 'Pilih jenis kelamin'}
|
||||
value={stategrafikBerdasarkanResponden.create.form.jenisKelaminId || ""}
|
||||
onChange={(val) => {
|
||||
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}
|
||||
/>
|
||||
<Select
|
||||
key={"rating_responden"}
|
||||
label={"Rating"}
|
||||
placeholder={indeksKepuasanState.pilihanRatingResponden.findMany.loading ? 'Memuat...' : 'Pilih rating'}
|
||||
value={stategrafikBerdasarkanResponden.create.form.ratingId || ""}
|
||||
onChange={(val) => {
|
||||
stategrafikBerdasarkanResponden.create.form.ratingId = val ?? "";
|
||||
}}
|
||||
data={
|
||||
(indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
|
||||
.filter(Boolean) // Hapus null, undefined, dll
|
||||
.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name || 'Tanpa Nama',
|
||||
}))
|
||||
}
|
||||
disabled={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
|
||||
/>
|
||||
<Select
|
||||
key={"kelompokUmur"}
|
||||
label={"Kelompok Umur"}
|
||||
placeholder={indeksKepuasanState.kelompokUmurResponden.findMany.loading ? 'Memuat...' : 'Pilih kelompok umur'}
|
||||
value={stategrafikBerdasarkanResponden.create.form.kelompokUmurId || ""}
|
||||
onChange={(val) => {
|
||||
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}
|
||||
/>
|
||||
<Button
|
||||
mt={10}
|
||||
bg={colors['blue-button']}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default RespondenCreate;
|
||||
@@ -0,0 +1,144 @@
|
||||
'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 { 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 indeksKepuasanState from '../../../_state/landing-page/indeks-kepuasan';
|
||||
|
||||
function Responden() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Responden'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListResponden search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<Stack py={10}>
|
||||
<Skeleton height={730} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper p="md">
|
||||
<Stack>
|
||||
<Title>Responden</Title>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>No</TableTh>
|
||||
<TableTh>Nama</TableTh>
|
||||
<TableTh>Tanggal</TableTh>
|
||||
<TableTh>Jenis Kelamin</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
</Table>
|
||||
<Text ta="center">Tidak ada data berdasarkan jenis kelamin responden yang tersedia</Text>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box >
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap="xs">
|
||||
<Paper bg={colors['white-1']} p="md" h={{ base: 730, md: 650 }}>
|
||||
<Title mb={10} order={3}>List Responden</Title>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '5%', textAlign: 'center' }}>No</TableTh>
|
||||
<TableTh style={{ width: '20%', textAlign: 'center' }}>Nama</TableTh>
|
||||
<TableTh style={{ width: '20%', textAlign: 'center' }}>Tanggal</TableTh>
|
||||
<TableTh style={{ width: '20%', textAlign: 'center' }}>Jenis Kelamin</TableTh>
|
||||
<TableTh style={{ width: '15%', textAlign: 'center' }}>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length === 0 ? (
|
||||
<TableTr>
|
||||
<TableTd colSpan={6}>
|
||||
<Text ta='center' c='dimmed'>Belum ada data responden</Text>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
) : (
|
||||
filteredData.map((item, index) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd style={{ width: '5%', textAlign: 'center' }}>{index + 1}</TableTd>
|
||||
<TableTd style={{ width: '20%', textAlign: 'center' }}>{item.name}</TableTd>
|
||||
<TableTd style={{ width: '20%', textAlign: 'center' }}>{item.tanggal
|
||||
? new Date(item.tanggal).toLocaleDateString('id-ID')
|
||||
: '-'}</TableTd>
|
||||
<TableTd style={{ width: '20%', textAlign: 'center' }}>{item.jenisKelamin.name}</TableTd>
|
||||
<TableTd style={{ width: '15%', textAlign: 'center' }}>
|
||||
<Button color='green' onClick={() => router.push(`/admin/ppid/ikm-desa-darmasaba/responden/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))
|
||||
)}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => {
|
||||
load(newPage, 10);
|
||||
window.scrollTo(0, 0);
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
/>
|
||||
</Center>
|
||||
|
||||
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Responden;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
@@ -42,18 +42,21 @@ function ListKategoriPrestasi({ search }: { search: string }) {
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = stateKategori.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
stateKategori.findMany.load()
|
||||
}, [])
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = (stateKategori.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
const filteredData = data || []
|
||||
|
||||
if (!stateKategori.findMany.data) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -100,6 +103,14 @@ function ListKategoriPrestasi({ search }: { search: string }) {
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my={"md"}
|
||||
/>
|
||||
</Center>
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -30,20 +30,36 @@ function ListPrestasiDesa() {
|
||||
function ListPrestasi({ search }: { search: string }) {
|
||||
const listState = useProxy(prestasiState.prestasiDesa)
|
||||
const router = useRouter();
|
||||
useEffect(() => {
|
||||
listState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (listState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword) ||
|
||||
item.kategori?.name?.toLowerCase().includes(keyword)
|
||||
);
|
||||
const{
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = listState.findMany
|
||||
|
||||
// Debug log
|
||||
console.log('ListPrestasi state:', {
|
||||
loading,
|
||||
data: data?.length,
|
||||
page,
|
||||
totalPages,
|
||||
search
|
||||
});
|
||||
|
||||
if (!listState.findMany.data) {
|
||||
useEffect(() => {
|
||||
console.log('Loading data...', { page, search });
|
||||
load(page, 10, search).then(() => {
|
||||
console.log('Data loaded:', listState.findMany.data);
|
||||
}).catch(error => {
|
||||
console.error('Error loading data:', error);
|
||||
});
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = data || []
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -82,7 +98,7 @@ function ListPrestasi({ search }: { search: string }) {
|
||||
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>{item.kategori?.name}</TableTd>
|
||||
<TableTd>{item.kategori?.name || 'No Category'}</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/landing-page/prestasi-desa/list-prestasi-desa/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
@@ -95,6 +111,14 @@ function ListPrestasi({ search }: { search: string }) {
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my={"md"}
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Center, Image, 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 { useEffect, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
@@ -38,20 +38,11 @@ function ListMediaSosial({ search }: { search: string }) {
|
||||
load,
|
||||
} = stateMediaSosial.findMany;
|
||||
|
||||
useEffect(() => {
|
||||
load(page, 10)
|
||||
}, [page])
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(keyword) ||
|
||||
item.iconUrl?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
}, [data, search]);
|
||||
const filteredData = data || []
|
||||
|
||||
// Handle loading state
|
||||
if (loading || !data) {
|
||||
|
||||
@@ -55,7 +55,10 @@ function DetailProgramInovasi() {
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
|
||||
<Text fz={"lg"}>{stateProgramInovasi.findUnique.data?.description}</Text>
|
||||
<Text
|
||||
fz={"lg"}
|
||||
|
||||
>{stateProgramInovasi.findUnique.data?.description}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Link</Text>
|
||||
@@ -63,6 +66,12 @@ function DetailProgramInovasi() {
|
||||
href={stateProgramInovasi.findUnique.data?.link || "#"}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{
|
||||
wordWrap: 'break-word',
|
||||
whiteSpace: 'pre-wrap',
|
||||
overflowWrap: 'break-word',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
{stateProgramInovasi.findUnique.data?.link || "Tidak ada link"}
|
||||
</a>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'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 { useEffect, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
@@ -38,21 +38,11 @@ function ListProgramInovasi({ search }: { search: string }) {
|
||||
load,
|
||||
} = stateProgramInovasi.findMany;
|
||||
|
||||
useEffect(() => {
|
||||
load(page, 10);
|
||||
}, [page]);
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(keyword) ||
|
||||
item.description?.toLowerCase().includes(keyword) ||
|
||||
item.link?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
}, [data, search]);
|
||||
const filteredData = data || []
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'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 { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import sdgsDesa from '../../_state/landing-page/sdgs-desa';
|
||||
import JudulList from '../../_com/judulList';
|
||||
import sdgsDesa from '../../_state/landing-page/sdgs-desa';
|
||||
|
||||
|
||||
function SdgsDesa() {
|
||||
@@ -39,20 +39,11 @@ function ListSdgsDesa({ search }: { search: string }) {
|
||||
load,
|
||||
} = listState.findMany;
|
||||
|
||||
useEffect(() => {
|
||||
load(page, 10)
|
||||
}, [])
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(keyword) ||
|
||||
item.jumlah?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
}, [data, search]);
|
||||
const filteredData = data || []
|
||||
|
||||
// Handle loading state
|
||||
if (loading || !data) {
|
||||
|
||||
@@ -105,9 +105,19 @@ function ListDaftarInformasi({ search }: { search: string }) {
|
||||
<TableTbody>
|
||||
{filteredData.map((item, index) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd style={{ textAlign: 'center' }}>{index + 1}</TableTd>
|
||||
<TableTd style={{ wordWrap: 'break-word' }}>{item.jenisInformasi}</TableTd>
|
||||
<TableTd style={{ wordWrap: 'break-word' }} dangerouslySetInnerHTML={{ __html: item.deskripsi }}></TableTd>
|
||||
<TableTd style={{ textAlign: 'center' }}>
|
||||
<Text mt={10} fz={"md"}>{index + 1}</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ wordWrap: 'break-word' }}>
|
||||
<Box w={200}>
|
||||
<Text mt={10} fz={"md"} truncate={"end"} lineClamp={1}>{item.jenisInformasi}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd style={{ wordWrap: 'break-word' }}>
|
||||
<Box w={200}>
|
||||
<Text fz={"md"} truncate={"end"} lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }}></Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd style={{ textAlign: 'center' }}>
|
||||
<Button bg={"green"} onClick={() => router.push(`/admin/ppid/daftar-informasi-publik-desa-darmasaba/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
|
||||
@@ -41,15 +41,11 @@ function EditResponden() {
|
||||
try {
|
||||
const data = await state.update.load(id);
|
||||
if (data) {
|
||||
const formattedDate = data.tanggal && !isNaN(new Date(data.tanggal).getTime())
|
||||
? new Date(data.tanggal).toISOString().split('T')[0]
|
||||
: '';
|
||||
// ⬇️ FIX PENTING: tambahkan ini
|
||||
state.update.id = id;
|
||||
|
||||
state.update.form = {
|
||||
name: data.name,
|
||||
tanggal: formattedDate,
|
||||
tanggal: data.tanggal,
|
||||
jenisKelaminId: data.jenisKelaminId,
|
||||
ratingId: data.ratingId,
|
||||
kelompokUmurId: data.kelompokUmurId,
|
||||
@@ -76,6 +72,7 @@ function EditResponden() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
state.update.id = id;
|
||||
state.update.form = { ...formData }; // <-- sinkronisasi manual
|
||||
await state.update.submit();
|
||||
router.push('/admin/ppid/ikm-desa-darmasaba/responden')
|
||||
}
|
||||
@@ -103,12 +100,15 @@ function EditResponden() {
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Tanggal"
|
||||
type="date"
|
||||
value={formData.tanggal}
|
||||
placeholder='Pilih tanggal'
|
||||
value={formData.tanggal ? new Date(formData.tanggal).toISOString().split('T')[0] : ''}
|
||||
onChange={(e) => {
|
||||
const selectedDate = e.currentTarget.value;
|
||||
setFormData({
|
||||
...formData,
|
||||
tanggal: e.currentTarget.value, // ✅ sudah format YYYY-MM-DD
|
||||
tanggal: selectedDate,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,7 @@ import colors from '@/con/colors';
|
||||
import { Badge, Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, ThemeIcon } from '@mantine/core';
|
||||
import { IconCheck, IconDeviceImacCog, IconSearch, IconX } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
@@ -39,22 +39,10 @@ function ListPegawaiPPID({ search }: { search: string }) {
|
||||
} = stateOrganisasi.findMany;
|
||||
|
||||
useEffect(() => {
|
||||
load(page, 10);
|
||||
}, [page]);
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.namaLengkap?.toLowerCase().includes(keyword) ||
|
||||
item.gelarAkademik?.toLowerCase().includes(keyword) ||
|
||||
item.telepon?.toLowerCase().includes(keyword) ||
|
||||
item.posisi?.nama?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
.sort((a, b) => a.posisi?.hierarki - b.posisi?.hierarki);
|
||||
}, [data, search]);
|
||||
const filteredData = data || []
|
||||
|
||||
// Handle loading state
|
||||
if (loading || !data) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -33,9 +33,17 @@ function ListPosisiOrganisasiPPID({ search }: { search: string }) {
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = stateOrganisasi.findMany;
|
||||
|
||||
useEffect(() => {
|
||||
stateOrganisasi.findMany.load()
|
||||
}, [])
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
|
||||
const handleHapus = async () => {
|
||||
if (selectedId) {
|
||||
@@ -45,17 +53,9 @@ function ListPosisiOrganisasiPPID({ search }: { search: string }) {
|
||||
}
|
||||
}
|
||||
|
||||
const filteredData = (stateOrganisasi.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama?.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi?.toLowerCase().includes(keyword) ||
|
||||
item.hierarki?.toString().toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
.sort((a, b) => a.hierarki - b.hierarki);
|
||||
const filteredData = data || []
|
||||
|
||||
if (!stateOrganisasi.findMany.data) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -120,6 +120,14 @@ function ListPosisiOrganisasiPPID({ search }: { search: string }) {
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my={"md"}
|
||||
/>
|
||||
</Center>
|
||||
{/* Modal Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
|
||||
@@ -17,7 +17,7 @@ export const navBar = [
|
||||
{
|
||||
id: "Landing_Page_3",
|
||||
name: "Indeks Kepuasan Masyarakat",
|
||||
path: "/admin/landing-page/indeks-kepuasan-masyarakat/list-survey"
|
||||
path: "/admin/landing-page/indeks-kepuasan-masyarakat/grafik-kepuasan-masyarakat"
|
||||
},
|
||||
{
|
||||
id: "Landing_Page_4",
|
||||
|
||||
@@ -96,28 +96,36 @@ export default async function detailDataPengangguranCreate(context: Context) {
|
||||
// Cari bulan sebelumnya
|
||||
const currentMonthIndex = monthOrder.indexOf(body.month);
|
||||
const prevMonth = currentMonthIndex > 0 ? monthOrder[currentMonthIndex - 1] : "Des";
|
||||
const prevYear = currentMonthIndex > 0 ? body.year : body.year - 1;
|
||||
|
||||
|
||||
// Handle year as number for calculations
|
||||
const currentYear = typeof body.year === 'number' ? body.year : new Date(body.year).getFullYear();
|
||||
const prevYear = currentMonthIndex > 0 ? currentYear : currentYear - 1;
|
||||
|
||||
// Create date objects for Prisma
|
||||
const currentYearDate = new Date(currentYear, 0, 1);
|
||||
const prevYearDate = new Date(prevYear, 0, 1);
|
||||
|
||||
const prevData = await prisma.detailDataPengangguran.findFirst({
|
||||
where: {
|
||||
month: prevMonth,
|
||||
year: prevYear,
|
||||
year: prevYearDate,
|
||||
},
|
||||
});
|
||||
|
||||
let percentageChange: number | null = null;
|
||||
if (prevData) {
|
||||
const change = ((body.totalUnemployment - prevData.totalUnemployment) / prevData.totalUnemployment) * 100;
|
||||
const change = ((Number(body.totalUnemployment) - Number(prevData.totalUnemployment)) / Number(prevData.totalUnemployment)) * 100;
|
||||
percentageChange = parseFloat(change.toFixed(1));
|
||||
}
|
||||
|
||||
// Create the new record with properly typed values
|
||||
const created = await prisma.detailDataPengangguran.create({
|
||||
data: {
|
||||
month: body.month,
|
||||
year: body.year,
|
||||
totalUnemployment: body.totalUnemployment,
|
||||
educatedUnemployment: body.educatedUnemployment,
|
||||
uneducatedUnemployment: body.uneducatedUnemployment,
|
||||
year: currentYearDate,
|
||||
totalUnemployment: Number(body.totalUnemployment),
|
||||
educatedUnemployment: Number(body.educatedUnemployment),
|
||||
uneducatedUnemployment: Number(body.uneducatedUnemployment),
|
||||
percentageChange,
|
||||
},
|
||||
select: {
|
||||
|
||||
@@ -15,7 +15,7 @@ export default async function findByMonthYear(context: Context) {
|
||||
const data = await prisma.detailDataPengangguran.findFirst({
|
||||
where: {
|
||||
month,
|
||||
year: Number(year),
|
||||
year: new Date(Number(year), 0, 1), // Convert year number to Date object for Prisma
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -81,13 +81,18 @@ export default async function detailDataPengangguranUpdate(context: Context) {
|
||||
return { success: false, message: "ID tidak ditemukan" };
|
||||
}
|
||||
|
||||
const { month, year, totalUnemployment, educatedUnemployment, uneducatedUnemployment } = context.body as {
|
||||
const { month, year: yearInput, totalUnemployment, educatedUnemployment, uneducatedUnemployment } = context.body as {
|
||||
month: string;
|
||||
year: number;
|
||||
totalUnemployment: number;
|
||||
educatedUnemployment: number;
|
||||
uneducatedUnemployment: number;
|
||||
year: number | string | Date;
|
||||
totalUnemployment: number | string;
|
||||
educatedUnemployment: number | string;
|
||||
uneducatedUnemployment: number | string;
|
||||
};
|
||||
|
||||
// Normalize year to Date object
|
||||
const year = typeof yearInput === 'number' ? new Date(yearInput, 0, 1) :
|
||||
yearInput instanceof Date ? yearInput :
|
||||
new Date(parseInt(yearInput as string), 0, 1);
|
||||
|
||||
const duplicate = await prisma.detailDataPengangguran.findFirst({
|
||||
where: {
|
||||
@@ -109,18 +114,19 @@ export default async function detailDataPengangguranUpdate(context: Context) {
|
||||
|
||||
const currentMonthIndex = monthOrder.indexOf(month);
|
||||
const prevMonth = currentMonthIndex > 0 ? monthOrder[currentMonthIndex - 1] : "Des";
|
||||
const prevYear = currentMonthIndex > 0 ? year : year - 1;
|
||||
const currentYear = year.getFullYear();
|
||||
const prevYear = currentMonthIndex > 0 ? currentYear : currentYear - 1;
|
||||
|
||||
const prevData = await prisma.detailDataPengangguran.findFirst({
|
||||
where: {
|
||||
month: prevMonth,
|
||||
year: prevYear,
|
||||
year: new Date(prevYear, 0, 1),
|
||||
},
|
||||
});
|
||||
|
||||
let percentageChange: number | null = null;
|
||||
if (prevData) {
|
||||
const change = ((totalUnemployment - prevData.totalUnemployment) / prevData.totalUnemployment) * 100;
|
||||
const change = ((Number(totalUnemployment) - Number(prevData.totalUnemployment)) / Number(prevData.totalUnemployment)) * 100;
|
||||
percentageChange = parseFloat(change.toFixed(1));
|
||||
}
|
||||
|
||||
@@ -128,10 +134,10 @@ export default async function detailDataPengangguranUpdate(context: Context) {
|
||||
where: { id },
|
||||
data: {
|
||||
month,
|
||||
year,
|
||||
totalUnemployment,
|
||||
educatedUnemployment,
|
||||
uneducatedUnemployment,
|
||||
year: new Date(year), // Ensure it's a new Date instance
|
||||
totalUnemployment: Number(totalUnemployment),
|
||||
educatedUnemployment: Number(educatedUnemployment),
|
||||
uneducatedUnemployment: Number(uneducatedUnemployment),
|
||||
percentageChange,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,21 +1,55 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function lowonganKerjaFindMany() {
|
||||
try {
|
||||
const data = await prisma.lowonganPekerjaan.findMany({
|
||||
where: { isActive: true },
|
||||
});
|
||||
async function lowonganKerjaFindMany(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;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success fetch lowongan kerja",
|
||||
data,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Find many error:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed fetch lowongan kerja",
|
||||
};
|
||||
}
|
||||
}
|
||||
// Buat where clause
|
||||
const where: any = { isActive: true };
|
||||
|
||||
// Tambahkan pencarian (jika ada)
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ posisi: { contains: search, mode: 'insensitive' } },
|
||||
{ namaPerusahaan: { contains: search, mode: 'insensitive' } },
|
||||
{ lokasi: { contains: search, mode: 'insensitive' } },
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// Ambil data dan total count secara paralel
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.lowonganPekerjaan.findMany({
|
||||
where,
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma.lowonganPekerjaan.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil ambil lowongan kerja 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 lowongan kerja",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default lowonganKerjaFindMany;
|
||||
@@ -1,26 +1,84 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function pasarDesaFindMany() {
|
||||
const data = await prisma.pasarDesa.findMany({
|
||||
where: {
|
||||
isActive: true, // Opsional filter
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
include: {
|
||||
image: true,
|
||||
KategoriToPasar: {
|
||||
async function pasarDesaFindMany(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 categoryId = context.query.categoryId as string | undefined;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
// Buat where clause
|
||||
const where: any = { isActive: true };
|
||||
|
||||
// Tambahkan filter kategori (jika ada)
|
||||
if (categoryId) {
|
||||
where.KategoriToPasar = {
|
||||
some: {
|
||||
kategoriId: categoryId
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Tambahkan pencarian (jika ada)
|
||||
if (search) {
|
||||
where.AND = where.AND || [];
|
||||
where.AND.push({
|
||||
OR: [
|
||||
{ nama: { contains: search, mode: 'insensitive' } },
|
||||
{ alamatUsaha: { contains: search, mode: 'insensitive' } },
|
||||
{
|
||||
KategoriToPasar: {
|
||||
some: {
|
||||
kategori: {
|
||||
nama: { contains: search, mode: 'insensitive' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Ambil data dan total count secara paralel
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.pasarDesa.findMany({
|
||||
where,
|
||||
include: {
|
||||
kategori: true,
|
||||
image: true,
|
||||
KategoriToPasar: {
|
||||
include: {
|
||||
kategori: true
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma.pasarDesa.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil mengambil semua data pasar desa",
|
||||
data,
|
||||
};
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil ambil pasar desa 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 pasar desa",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default pasarDesaFindMany;
|
||||
@@ -1,15 +1,60 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function kategoriProdukFindMany() {
|
||||
const data = await prisma.kategoriProduk.findMany();
|
||||
return {
|
||||
success: true,
|
||||
data: data.map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
nama: item.nama,
|
||||
}
|
||||
}),
|
||||
async function kategoriProdukFindMany(context: Context) {
|
||||
// Ambil parameter dari query
|
||||
const page = Number(context.query.page) || 1;
|
||||
const limit = Number(context.query.limit) || 10;
|
||||
const search2 = (context.query.search as string) || '';
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
// Buat where clause
|
||||
const where: any = { isActive: true };
|
||||
|
||||
// Tambahkan pencarian (jika ada)
|
||||
if (search2) {
|
||||
where.OR = [
|
||||
{ nama: { contains: search2, mode: 'insensitive' } },
|
||||
{KategoriToPasar : {
|
||||
some: {
|
||||
kategori: {
|
||||
nama: { contains: search2, mode: 'insensitive' }
|
||||
}
|
||||
}
|
||||
}}
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// Ambil data dan total count secara paralel
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.kategoriProduk.findMany({
|
||||
where,
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma.kategoriProduk.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil ambil kategori produk 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 kategori produk",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default kategoriProdukFindMany;
|
||||
@@ -1,18 +1,53 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function programKemiskinanFindMany() {
|
||||
const data = await prisma.programKemiskinan.findMany({
|
||||
include: {
|
||||
statistik: true, // ikut sertakan relasinya
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success get all program layanan",
|
||||
data,
|
||||
};
|
||||
export default async function programKemiskinanFindMany(context: Context) {
|
||||
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 = [
|
||||
{ nama: { contains: search, mode: 'insensitive' } },
|
||||
{ deskripsi: { contains: search, mode: 'insensitive' } },
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// Ambil data dan total count secara paralel
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.programKemiskinan.findMany({
|
||||
where,
|
||||
include: {
|
||||
statistik: true,
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma.programKemiskinan.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil ambil program kemiskinan 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 program kemiskinan",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,57 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function keamananLingkunganFindMany() {
|
||||
try {
|
||||
const data = await prisma.keamananLingkungan.findMany({
|
||||
where: { isActive: true },
|
||||
include: {
|
||||
image: true,
|
||||
},
|
||||
});
|
||||
async function keamananLingkunganFindMany(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;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success fetch keamanan lingkungan",
|
||||
data,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Find many error:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed fetch keamanan lingkungan",
|
||||
};
|
||||
}
|
||||
}
|
||||
// Buat where clause
|
||||
const where: any = { isActive: true };
|
||||
|
||||
// Tambahkan pencarian (jika ada)
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ name: { contains: search, mode: 'insensitive' } },
|
||||
{ deskripsi: { contains: search, mode: 'insensitive' } },
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// Ambil data dan total count secara paralel
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.keamananLingkungan.findMany({
|
||||
where,
|
||||
include: {
|
||||
image: true,
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma.keamananLingkungan.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil ambil keamanan lingkungan 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 keamanan lingkungan",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default keamananLingkunganFindMany;
|
||||
@@ -0,0 +1,31 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// find-first.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
async function polsekTerdekatFindFirst() {
|
||||
const where: any = { isActive: true };
|
||||
|
||||
try {
|
||||
const data = await prisma.polsekTerdekat.findFirst({
|
||||
where,
|
||||
include: {
|
||||
layananPolsek: true,
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil ambil polsek terdekat terbaru",
|
||||
data,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Error di findFirst:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal ambil polsek terdekat terbaru",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default polsekTerdekatFindFirst;
|
||||
@@ -1,24 +1,57 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function polsekTerdekatFindMany() {
|
||||
try {
|
||||
const data = await prisma.polsekTerdekat.findMany({
|
||||
where: { isActive: true },
|
||||
include: {
|
||||
layananPolsek: true,
|
||||
},
|
||||
});
|
||||
async function polsekTerdekatFindMany(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;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success fetch polsek terdekat",
|
||||
data,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Find many error:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed fetch polsek terdekat",
|
||||
};
|
||||
}
|
||||
}
|
||||
// Buat where clause
|
||||
const where: any = { isActive: true };
|
||||
|
||||
// Tambahkan pencarian (jika ada)
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ nama: { contains: search, mode: 'insensitive' } },
|
||||
{ alamat: { contains: search, mode: 'insensitive' } },
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// Ambil data dan total count secara paralel
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.polsekTerdekat.findMany({
|
||||
where,
|
||||
include: {
|
||||
layananPolsek: true,
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma.polsekTerdekat.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil ambil polsek terdekat 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 polsek terdekat",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default polsekTerdekatFindMany;
|
||||
@@ -4,6 +4,7 @@ import polsekTerdekatDelete from "./del";
|
||||
import polsekTerdekatFindMany from "./findMany";
|
||||
import polsekTerdekatFindUnique from "./findUnique";
|
||||
import polsekTerdekatUpdate from "./updt";
|
||||
import polsekTerdekatFindFirst from "./findFirst";
|
||||
|
||||
|
||||
const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/Polsek Terdekat"] })
|
||||
@@ -12,6 +13,7 @@ const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/
|
||||
const response = await polsekTerdekatFindUnique(new Request(context.request));
|
||||
return response;
|
||||
})
|
||||
.get("/find-first", polsekTerdekatFindFirst)
|
||||
.post("/create", polsekTerdekatCreate, {
|
||||
body: t.Object({
|
||||
nama: t.String(),
|
||||
|
||||
@@ -1,24 +1,57 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function menuTipsKeamananFindMany() {
|
||||
try {
|
||||
const data = await prisma.menuTipsKeamanan.findMany({
|
||||
where: { isActive: true },
|
||||
include: {
|
||||
image: true,
|
||||
},
|
||||
});
|
||||
async function tipsKeamananFindMany(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;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success fetch menu tips keamanan",
|
||||
data,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Find many error:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed fetch menu tips keamanan",
|
||||
};
|
||||
}
|
||||
}
|
||||
// Buat where clause
|
||||
const where: any = { isActive: true };
|
||||
|
||||
// Tambahkan pencarian (jika ada)
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ judul: { contains: search, mode: 'insensitive' } },
|
||||
{ deskripsi: { contains: search, mode: 'insensitive' } },
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// Ambil data dan total count secara paralel
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.menuTipsKeamanan.findMany({
|
||||
where,
|
||||
include: {
|
||||
image: true,
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma.menuTipsKeamanan.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil ambil menu tips keamanan 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 tips keamanan",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default tipsKeamananFindMany;
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
@@ -5,12 +6,23 @@ import { Context } from "elysia";
|
||||
async function apbdesFindMany(context: Context) {
|
||||
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 {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.aPBDes.findMany({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
include: {
|
||||
image: true,
|
||||
file: true,
|
||||
@@ -20,7 +32,7 @@ async function apbdesFindMany(context: Context) {
|
||||
orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.aPBDes.count({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
}),
|
||||
]);
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const APBDes = new Elysia({
|
||||
})
|
||||
|
||||
// ✅ Find all
|
||||
.get("/find-many", apbdesFindMany)
|
||||
.get("/findMany", apbdesFindMany)
|
||||
|
||||
// ✅ Find by ID
|
||||
.get("/:id", apbdesFindUnique)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
@@ -5,23 +6,35 @@ import { Context } from "elysia";
|
||||
async function desaAntiKorupsiFindMany(context: Context) {
|
||||
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" } },
|
||||
{ deskripsi: { contains: search, mode: "insensitive" } },
|
||||
{ kategori: { name: { contains: search, mode: "insensitive" } } },
|
||||
];
|
||||
}
|
||||
try {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.desaAntiKorupsi.findMany({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
include: {
|
||||
kategori: true,
|
||||
file: true,
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { name: 'asc' }, // opsional, kalau mau urut berdasarkan waktu
|
||||
orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.desaAntiKorupsi.count({
|
||||
where: { isActive: true }
|
||||
})
|
||||
where,
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
@@ -5,18 +6,29 @@ import { Context } from "elysia";
|
||||
async function kategoriDesaAntiKorupsiFindMany(context: Context) {
|
||||
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 {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.kategoriDesaAntiKorupsi.findMany({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { name: 'asc' }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.kategoriDesaAntiKorupsi.count({
|
||||
where: { isActive: true }
|
||||
where,
|
||||
})
|
||||
]);
|
||||
|
||||
|
||||
@@ -15,16 +15,12 @@ export default async function respondenCreate(context: Context) {
|
||||
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
|
||||
tanggal: tanggal.toISOString(), // Use the Date object
|
||||
jenisKelaminId: body.jenisKelaminId,
|
||||
ratingId: body.ratingId,
|
||||
kelompokUmurId: body.kelompokUmurId,
|
||||
|
||||
@@ -2,35 +2,40 @@ import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormUpdate = {
|
||||
name: string;
|
||||
tanggal: string;
|
||||
jenisKelaminId: string;
|
||||
ratingId: string;
|
||||
kelompokUmurId: string;
|
||||
}
|
||||
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;
|
||||
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);
|
||||
}
|
||||
try {
|
||||
const result = await prisma.responden.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: body.name,
|
||||
tanggal: new Date(body.tanggal).toISOString(),
|
||||
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);
|
||||
// Instead of throwing an error, return a proper JSON response
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengupdate responden: " + (error as Error).message,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
@@ -5,22 +6,45 @@ import { Context } from "elysia";
|
||||
async function prestasiDesaFindMany(context: Context) {
|
||||
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 = {};
|
||||
|
||||
// Tambahkan pencarian (jika ada)
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ name: { contains: search, mode: 'insensitive' } },
|
||||
{ deskripsi: { contains: search, mode: 'insensitive' } },
|
||||
{ kategori: { name: { contains: search, mode: 'insensitive' } } },
|
||||
];
|
||||
// Tetap filter hanya yang aktif saat melakukan pencarian
|
||||
where.isActive = true;
|
||||
} else {
|
||||
// Jika tidak ada pencarian, tampilkan hanya data yang aktif
|
||||
where.isActive = true;
|
||||
}
|
||||
|
||||
try {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.prestasiDesa.findMany({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
include: {
|
||||
image: true,
|
||||
kategori: true,
|
||||
kategori: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true
|
||||
}
|
||||
},
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
orderBy: { createdAt: "desc" },
|
||||
}),
|
||||
prisma.prestasiDesa.count({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
}),
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,15 +1,52 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function kategoriPrestasiFindMany() {
|
||||
const data = await prisma.kategoriPrestasiDesa.findMany();
|
||||
return {
|
||||
success: true,
|
||||
data: data.map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
}
|
||||
}),
|
||||
async function kategoriPrestasiFindMany(context: Context) {
|
||||
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 {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.kategoriPrestasiDesa.findMany({
|
||||
where,
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.kategoriPrestasiDesa.count({
|
||||
where,
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success fetch Kategori Prestasi Desa with pagination",
|
||||
data,
|
||||
page,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
total,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Find many paginated error:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed fetch Kategori Prestasi Desa with pagination",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default kategoriPrestasiFindMany;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
@@ -5,12 +6,23 @@ import { Context } from "elysia";
|
||||
async function mediaSosialFindMany(context: Context) {
|
||||
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 {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.mediaSosial.findMany({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
include: {
|
||||
image: true,
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// // /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
@@ -5,12 +6,23 @@ import { Context } from "elysia";
|
||||
async function programInovasiFindMany(context: Context) {
|
||||
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;
|
||||
|
||||
const where: any = { isActive: true };
|
||||
|
||||
// Tambahkan pencarian (jika ada)
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ name: { contains: search, mode: 'insensitive' } },
|
||||
{ description: { contains: search, mode: 'insensitive' } },
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.programInovasi.findMany({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
include: {
|
||||
image: true,
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
@@ -5,12 +6,23 @@ import { Context } from "elysia";
|
||||
async function sdgsDesaFindMany(context: Context) {
|
||||
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 {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.sDGSDesa.findMany({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
include: {
|
||||
image: true,
|
||||
},
|
||||
@@ -19,7 +31,7 @@ async function sdgsDesaFindMany(context: Context) {
|
||||
orderBy: { jumlah: "desc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.sDGSDesa.count({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
}),
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,44 +1,54 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
// Di findMany.ts
|
||||
export default async function daftarInformasiPublikFindMany(context: Context) {
|
||||
async function daftarInformasiPublikFindMany(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 = [
|
||||
{ jenisInformasi: { contains: search, mode: 'insensitive' } },
|
||||
{ deskripsi: { contains: search, mode: 'insensitive' } },
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// Ambil data dan total count secara paralel
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.daftarInformasiPublik.findMany({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma.daftarInformasiPublik.count({
|
||||
where: { isActive: true }
|
||||
})
|
||||
prisma.daftarInformasiPublik.count({ where }),
|
||||
]);
|
||||
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success fetch daftar informasi publik with pagination",
|
||||
message: "Berhasil ambil daftar informasi publik dengan pagination",
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
limit,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Find many paginated error:", e);
|
||||
console.error("Error di findMany paginated:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed fetch daftar informasi publik with pagination",
|
||||
data: [],
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
message: "Gagal mengambil data daftar informasi publik",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default daftarInformasiPublikFindMany;
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
@@ -5,22 +6,34 @@ import { Context } from "elysia";
|
||||
export default async function pegawaiFindMany(context: Context) {
|
||||
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 = [
|
||||
{ namaLengkap: { contains: search, mode: 'insensitive' } },
|
||||
{ alamat: { contains: search, mode: 'insensitive' } },
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.pegawaiPPID.findMany({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
include: {
|
||||
posisi: true,
|
||||
image: true,
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
take: limit,
|
||||
orderBy: { posisi: { hierarki: 'asc' } },
|
||||
}),
|
||||
prisma.pegawaiPPID.count({
|
||||
where: { isActive: true }
|
||||
where,
|
||||
})
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,17 +1,58 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/posisi-organisasi/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function posisiOrganisasiFindMany() {
|
||||
const data = await prisma.posisiOrganisasiPPID.findMany();
|
||||
async function posisiOrganisasiFindMany(context: Context) {
|
||||
const page = Number(context.query.page) || 1;
|
||||
const limit = Number(context.query.limit) || 10;
|
||||
const search = (context.query.search as string) || "";
|
||||
|
||||
const where: any = { isActive: true };
|
||||
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ nama: { contains: search, mode: "insensitive" } },
|
||||
{ deskripsi: { contains: search, mode: "insensitive" } },
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
const whereClause = {
|
||||
...where,
|
||||
isActive: true
|
||||
};
|
||||
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.posisiOrganisasiPPID.findMany({
|
||||
where: whereClause,
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
orderBy: { hierarki: "asc" },
|
||||
}),
|
||||
prisma.posisiOrganisasiPPID.count({ where: whereClause }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil mengambil semua data posisi organisasi",
|
||||
data: data.map((item: any) => ({
|
||||
id: item.id,
|
||||
nama: item.nama,
|
||||
deskripsi: item.deskripsi,
|
||||
hierarki: item.hierarki,
|
||||
})),
|
||||
success: true,
|
||||
message: "Berhasil mengambil data posisi organisasi dengan pagination",
|
||||
data: data.map((item: any) => ({
|
||||
id: item.id,
|
||||
nama: item.nama,
|
||||
deskripsi: item.deskripsi,
|
||||
hierarki: item.hierarki,
|
||||
})),
|
||||
page,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
total,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Find many paginated error:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data posisi organisasi",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default posisiOrganisasiFindMany;
|
||||
|
||||
@@ -1,21 +1,157 @@
|
||||
'use client'
|
||||
import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Text, Image, Paper } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { Box, Grid, GridCol, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
|
||||
|
||||
function Page() {
|
||||
const state = useProxy(PendapatanAsliDesa.ApbDesa);
|
||||
|
||||
useShallowEffect(() => {
|
||||
state.findMany.load();
|
||||
}, []);
|
||||
|
||||
useShallowEffect(() => {
|
||||
PendapatanAsliDesa.pembiayaan.findMany.load();
|
||||
PendapatanAsliDesa.belanja.findMany.load();
|
||||
PendapatanAsliDesa.pendapatan.findMany.load();
|
||||
}, []);
|
||||
|
||||
// Get the latest APB data
|
||||
const latestApb = state.findMany.data?.[0];
|
||||
|
||||
// Calculate totals
|
||||
const totalPendapatan = latestApb?.pendapatan?.reduce((sum, item) => sum + (item?.value || 0), 0) || 0;
|
||||
const totalBelanja = latestApb?.belanja?.reduce((sum, item) => sum + (item?.value || 0), 0) || 0;
|
||||
const totalPembiayaan = latestApb?.pembiayaan?.reduce((sum, item) => sum + (item?.value || 0), 0) || 0;
|
||||
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Stack pos="relative" bg={colors.Bg} py="xl" gap="lg">
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Pendapatan Asli Desa
|
||||
</Text>
|
||||
<Text ta="center" fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw="bold">
|
||||
Pendapatan Asli Desa
|
||||
</Text>
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Stack gap={'lg'} justify='center'>
|
||||
<Paper bg={colors['white-1']} p={"xl"}>
|
||||
<Image src="/pa-desa.png" alt=''/>
|
||||
<Stack gap="lg" justify="center">
|
||||
<Paper bg={colors['white-1']} p="xl">
|
||||
<SimpleGrid cols={{ base: 1, md: 3 }} spacing="md">
|
||||
{/* Pendapatan Card */}
|
||||
<Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Pendapatan</Title>
|
||||
{PendapatanAsliDesa.pendapatan.findMany.data?.map((item) => (
|
||||
<Box key={item.id}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="md" fw={500}>{item.name}</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="md" fw={500}>{new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0
|
||||
}).format(item.value)}</Text>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</Box>
|
||||
))}
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="lg" fw={600} mb="xs">Total Pendapatan</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="xl" fw={700} c={colors['blue-button']}>
|
||||
{new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0
|
||||
}).format(totalPendapatan)}
|
||||
</Text>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
{/* Belanja Card */}
|
||||
<Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Belanja</Title>
|
||||
{PendapatanAsliDesa.belanja.findMany.data?.map((item) => (
|
||||
<Box key={item.id}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="md" fw={500}>{item.name}</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="md" fw={500}>{new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0
|
||||
}).format(item.value)}</Text>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</Box>
|
||||
))}
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="lg" fw={600} mb="xs">Total Belanja</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="xl" fw={700} c="orange">
|
||||
{new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0
|
||||
}).format(totalBelanja)}
|
||||
</Text>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
{/* Pembiayaan Card */}
|
||||
<Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Pembiayaan</Title>
|
||||
{PendapatanAsliDesa.pembiayaan.findMany.data?.map((item) => (
|
||||
<Box key={item.id}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="md" fw={500}>{item.name}</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="md" fw={500}>{new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0
|
||||
}).format(item.value)}</Text>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</Box>
|
||||
))}
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="lg" fw={600} mb="xs">Total Pembiayaan</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 6 }}>
|
||||
<Text fz="xl" fw={700} c="green">
|
||||
{new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0
|
||||
}).format(totalPembiayaan)}
|
||||
</Text>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
</SimpleGrid>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
@@ -1,192 +1,32 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Paper, Text, ColorSwatch, Flex } from '@mantine/core';
|
||||
import { Stack, Box, Paper, Text, ColorSwatch, Flex, Skeleton } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { BarChart } from '@mantine/charts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import grafikDemografiPekerjaan from '@/app/admin/(dashboard)/_state/ekonomi/demografi-pekerjaan';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
|
||||
const data = [
|
||||
{
|
||||
id: 1,
|
||||
Pekerjaan: 'Guru',
|
||||
laki: 2,
|
||||
perempuan: 3
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
Pekerjaan: 'Belajar/Mahasiswa',
|
||||
laki: 37,
|
||||
perempuan: 38
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
Pekerjaan: 'Karyawan Bumdn',
|
||||
laki: 1,
|
||||
perempuan: 0
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
Pekerjaan: 'Buruh Tani/Perkebunan',
|
||||
laki: 1,
|
||||
perempuan: 0
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
Pekerjaan: 'Karyawan Swasta',
|
||||
laki: 3,
|
||||
perempuan: 17
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
Pekerjaan: 'Karyawan Honorer',
|
||||
laki: 2,
|
||||
perempuan: 1
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
Pekerjaan: 'Buruh Harian Lepas',
|
||||
laki: 8,
|
||||
perempuan: 5
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
Pekerjaan: 'Belum/Tidak Bekerja',
|
||||
laki: 87,
|
||||
perempuan: 44
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
Pekerjaan: ' Kepolisian RI (Polri)',
|
||||
laki: 4,
|
||||
perempuan: 0
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
Pekerjaan: 'Wiraswasta Mengurus Rumah Tangga',
|
||||
laki: 1,
|
||||
perempuan: 7
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
Pekerjaan: 'Dosen',
|
||||
laki: 1,
|
||||
perempuan: 1
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
Pekerjaan: 'Perangkat Desa',
|
||||
laki: 17,
|
||||
perempuan: 19
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
Pekerjaan: 'Nelayan',
|
||||
laki: 3,
|
||||
perempuan: 0
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
Pekerjaan: 'Penyuluh Pertanian',
|
||||
laki: 33,
|
||||
perempuan: 24
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
Pekerjaan: 'Tukang Las/Pandai Besi',
|
||||
laki: 5,
|
||||
perempuan: 0
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
Pekerjaan: 'Sopir/Driver',
|
||||
laki: 10,
|
||||
perempuan: 3
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
Pekerjaan: 'Teknisi/Listrik',
|
||||
laki: 25,
|
||||
perempuan: 0
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
Pekerjaan: 'Montir/Mekanik',
|
||||
laki: 25,
|
||||
perempuan: 0
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
Pekerjaan: 'Karyawan Hotel/Pariwisata',
|
||||
laki: 2,
|
||||
perempuan: 52
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
Pekerjaan: 'Pengrajin (Batik, Anyaman, Kayu)',
|
||||
laki: 5,
|
||||
perempuan: 25
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
Pekerjaan: 'Tukang Bangunan',
|
||||
laki: 25,
|
||||
perempuan: 5
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
Pekerjaan: 'Tukang Kayu/Furnitur',
|
||||
laki: 25,
|
||||
perempuan: 0
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
Pekerjaan: 'Penjahit',
|
||||
laki: 2,
|
||||
perempuan: 35
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
Pekerjaan: 'Pedagang Pasar',
|
||||
laki: 25,
|
||||
perempuan: 30
|
||||
},
|
||||
{
|
||||
id: 25,
|
||||
Pekerjaan: 'Warung Makan/Penjual Makanan',
|
||||
laki: 15,
|
||||
perempuan: 30
|
||||
},
|
||||
{
|
||||
id: 26,
|
||||
Pekerjaan: 'Satpam/Security',
|
||||
laki: 20,
|
||||
perempuan: 5
|
||||
},
|
||||
{
|
||||
id: 27,
|
||||
Pekerjaan: 'Pengusaha Kecil (UMKM)',
|
||||
laki: 5,
|
||||
perempuan: 20
|
||||
},
|
||||
{
|
||||
id: 28,
|
||||
Pekerjaan: 'Karyawan Restoran/Kafe',
|
||||
laki: 5,
|
||||
perempuan: 15
|
||||
},
|
||||
{
|
||||
id: 29,
|
||||
Pekerjaan: 'Freelancer',
|
||||
laki: 20,
|
||||
perempuan: 10
|
||||
},
|
||||
{
|
||||
id: 30,
|
||||
Pekerjaan: 'Fotografer',
|
||||
laki: 25,
|
||||
perempuan: 9
|
||||
},
|
||||
]
|
||||
function Page() {
|
||||
const state = useProxy(grafikDemografiPekerjaan)
|
||||
|
||||
const {
|
||||
data,
|
||||
} = state.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
state.findMany.load()
|
||||
}, [])
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
@@ -207,7 +47,13 @@ function Page() {
|
||||
p={10}
|
||||
mb={50}
|
||||
h={400}
|
||||
data={data}
|
||||
w={150}
|
||||
data={data.map((item) => ({
|
||||
id: item.id,
|
||||
Pekerjaan: item.pekerjaan,
|
||||
laki: item.lakiLaki,
|
||||
perempuan: item.perempuan,
|
||||
}))}
|
||||
dataKey="Pekerjaan"
|
||||
series={[
|
||||
{ name: 'laki', color: '#5082EE' },
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, CheckIcon, Combobox, ComboboxChevron, ComboboxOption, ComboboxOptions, ComboboxTarget, Group, InputBase, InputPlaceholder, Paper, SimpleGrid, Stack, Text, useCombobox } from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { BarChart } from '@mantine/charts';
|
||||
|
||||
const data = [
|
||||
{
|
||||
id: 1,
|
||||
tahun: '2024',
|
||||
Penduduk: 400000
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
tahun: '2025',
|
||||
Penduduk: 450000
|
||||
},
|
||||
|
||||
]
|
||||
const tahun = [
|
||||
'2024',
|
||||
'2025'
|
||||
];
|
||||
function Page() {
|
||||
const combobox = useCombobox({
|
||||
onDropdownClose: () => combobox.resetSelectedOption(),
|
||||
onDropdownOpen: (eventSource) => {
|
||||
if (eventSource === 'keyboard') {
|
||||
combobox.selectActiveOption();
|
||||
} else {
|
||||
combobox.updateSelectedOptionIndex('active');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const [value, setValue] = useState<string | null>('2024');
|
||||
|
||||
const options = tahun.map((item) => (
|
||||
<ComboboxOption value={item} key={item} active={item === value}>
|
||||
<Group gap="xs">
|
||||
{item === value && <CheckIcon size={12} />}
|
||||
<span>{item}</span>
|
||||
</Group>
|
||||
</ComboboxOption>
|
||||
));
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
<Box px={{ base: 'md', md: 100 }} >
|
||||
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Jumlah Penduduk Miskin Tahun 2024-2025
|
||||
</Text>
|
||||
</Box>
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Stack gap={'lg'} justify='center'>
|
||||
<SimpleGrid
|
||||
pb={20}
|
||||
cols={{
|
||||
base: 1,
|
||||
md: 2
|
||||
}}
|
||||
>
|
||||
<Paper p={'xl'}>
|
||||
<Text fz={'h3'}>Tahun: 2024</Text>
|
||||
<Text fw={"bold"} fz={'h1'}>4,800,000 Orang</Text>
|
||||
</Paper>
|
||||
<Paper p={'xl'}>
|
||||
<Text>Pilih Tahun</Text>
|
||||
<Combobox
|
||||
store={combobox}
|
||||
resetSelectionOnOptionHover
|
||||
withinPortal={false}
|
||||
onOptionSubmit={(val) => {
|
||||
setValue(val);
|
||||
combobox.updateSelectedOptionIndex('active');
|
||||
}}
|
||||
>
|
||||
<ComboboxTarget targetType="button">
|
||||
<InputBase
|
||||
component="button"
|
||||
type="button"
|
||||
pointer
|
||||
rightSection={<ComboboxChevron />}
|
||||
rightSectionPointerEvents="none"
|
||||
onClick={() => combobox.toggleDropdown()}
|
||||
>
|
||||
{value || <InputPlaceholder>Pick value</InputPlaceholder>}
|
||||
</InputBase>
|
||||
</ComboboxTarget>
|
||||
|
||||
<Combobox.Dropdown>
|
||||
<ComboboxOptions>{options}</ComboboxOptions>
|
||||
</Combobox.Dropdown>
|
||||
</Combobox>
|
||||
</Paper>
|
||||
</SimpleGrid>
|
||||
<Paper p={'xl'}>
|
||||
<Text pb={10} fw={'bold'} fz={'h4'}>Jumlah Penduduk Miskin Per Tahun</Text>
|
||||
<BarChart
|
||||
p={10}
|
||||
h={300}
|
||||
data={data}
|
||||
dataKey="tahun"
|
||||
series={[
|
||||
{ name: 'Penduduk', color: '#8785D3' },
|
||||
]}
|
||||
tickLine="y"
|
||||
/>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,82 @@
|
||||
'use client'
|
||||
import jumlahPendudukMiskin from '@/app/admin/(dashboard)/_state/ekonomi/jumlah-penduduk-miskin';
|
||||
import colors from '@/con/colors';
|
||||
import { BarChart } from '@mantine/charts';
|
||||
import { Box, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
|
||||
function Page() {
|
||||
type JPMGrafik = {
|
||||
id: string;
|
||||
year: number;
|
||||
totalPoorPopulation: number;
|
||||
}
|
||||
const state = useProxy(jumlahPendudukMiskin)
|
||||
const [chartData, setChartData] = useState<JPMGrafik[]>([])
|
||||
|
||||
|
||||
useShallowEffect(() => {
|
||||
state.findMany.load()
|
||||
}, [])
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (state.findMany.data) {
|
||||
setChartData(state.findMany.data.map((item) => ({
|
||||
id: item.id,
|
||||
year: Number(item.year),
|
||||
totalPoorPopulation: Number(item.totalPoorPopulation),
|
||||
})));
|
||||
}
|
||||
}, [state.findMany.data]);
|
||||
|
||||
if (!state.findMany.data) {
|
||||
return (
|
||||
<Stack>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
<Box px={{ base: 'md', md: 100 }} >
|
||||
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Jumlah Penduduk Miskin
|
||||
</Text>
|
||||
</Box>
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Stack gap={'lg'} justify='center'>
|
||||
<Paper p={'xl'}>
|
||||
<Text fz={'h3'}>Jumlah Data Penduduk Miskin</Text>
|
||||
<Text fw={"bold"} fz={'h1'}>
|
||||
{state.findMany.data?.reduce((sum, item) => sum + (Number(item.totalPoorPopulation) || 0), 0).toLocaleString()} Orang
|
||||
</Text>
|
||||
</Paper>
|
||||
<Paper p={'xl'}>
|
||||
<Text pb={10} fw={'bold'} fz={'h4'}>Jumlah Penduduk Miskin Per Tahun</Text>
|
||||
<BarChart
|
||||
h={300}
|
||||
data={chartData}
|
||||
dataKey="year"
|
||||
series={[
|
||||
{ name: 'totalPoorPopulation', color: 'blue.6', label: 'Jumlah Penduduk Miskin' },
|
||||
]}
|
||||
xAxisLabel="Tahun"
|
||||
yAxisLabel="Jumlah Penduduk"
|
||||
/>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -1,62 +1,76 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import grafikNganggur from '@/app/admin/(dashboard)/_state/ekonomi/usia-kerja-nganggur';
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Text, Center, Paper, ColorSwatch, Flex } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { PieChart } from '@mantine/charts';
|
||||
import { Box, Center, ColorSwatch, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
|
||||
const datausiaKerja = [
|
||||
{
|
||||
id: 1,
|
||||
name: '18 - 25',
|
||||
value: 45,
|
||||
color: 'indigo.6'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '26 - 35',
|
||||
value: 35,
|
||||
color: 'teal.6'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '36 - 45',
|
||||
value: 15,
|
||||
color: 'yellow.6'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '46+',
|
||||
value: 5,
|
||||
color: 'red.6'
|
||||
},
|
||||
]
|
||||
const datakerjaPendidikan = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'SD',
|
||||
value: 10,
|
||||
color: 'indigo.6'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'SMP',
|
||||
value: 20,
|
||||
color: 'teal.6'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'SMA/SMK',
|
||||
value: 45,
|
||||
color: 'yellow.6'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'D3/S1',
|
||||
value: 25,
|
||||
color: 'red.6'
|
||||
},
|
||||
]
|
||||
function Page() {
|
||||
const stateGrafikNganggur = useProxy(grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur)
|
||||
const stateGrafikNganggurPendidikan = useProxy(grafikNganggur.grafikBerdasarkanPendidikan)
|
||||
const [donutGrafikNganggurData, setDonutGrafikNganggurData] = useState<any[]>([])
|
||||
const [donutGrafikNganggurDataPendidikan, setDonutGrafikNganggurDataPendidikan] = useState<any[]>([])
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const [mounted2, setMounted2] = useState(false)
|
||||
|
||||
useShallowEffect(() => {
|
||||
setMounted(true)
|
||||
setMounted2(true)
|
||||
stateGrafikNganggur.findMany.load()
|
||||
stateGrafikNganggurPendidikan.findMany.load()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (stateGrafikNganggur.findMany.data) {
|
||||
const totalUsia18_25 = stateGrafikNganggur.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia18_25 || 0), 0);
|
||||
const totalUsia26_35 = stateGrafikNganggur.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia26_35 || 0), 0);
|
||||
const totalUsia36_45 = stateGrafikNganggur.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia36_45 || 0), 0);
|
||||
const totalUsia46_keatas = stateGrafikNganggur.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia46_keatas || 0), 0);
|
||||
setDonutGrafikNganggurData([
|
||||
{ name: 'usia 18 - 25', value: totalUsia18_25, color: '#4b6Ef5', key: 'usia18_25' },
|
||||
{ name: 'usia 26 - 35', value: totalUsia26_35, color: '#14b885', key: 'usia26_35' },
|
||||
{ name: 'usia 36 - 45', value: totalUsia36_45, color: '#E6A03B', key: 'usia36_45' },
|
||||
{ name: 'usia 46 +', value: totalUsia46_keatas, color: '#DB524D', key: 'usia46_keatas' },
|
||||
])
|
||||
}
|
||||
}, [stateGrafikNganggur.findMany.data])
|
||||
|
||||
useEffect(() => {
|
||||
if (stateGrafikNganggurPendidikan.findMany.data) {
|
||||
const SD = stateGrafikNganggurPendidikan.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.SD || 0), 0);
|
||||
const SMP = stateGrafikNganggurPendidikan.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.SMP || 0), 0);
|
||||
const SMA = stateGrafikNganggurPendidikan.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.SMA || 0), 0);
|
||||
const D3 = stateGrafikNganggurPendidikan.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.D3 || 0), 0);
|
||||
const S1 = stateGrafikNganggurPendidikan.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.S1 || 0), 0);
|
||||
setDonutGrafikNganggurDataPendidikan([
|
||||
{ name: 'SD', value: SD, color: '#4b6Ef5', key: 'SD' },
|
||||
{ name: 'SMP', value: SMP, color: '#14b885', key: 'SMP' },
|
||||
{ name: 'SMA', value: SMA, color: '#E6A03B', key: 'SMA' },
|
||||
{ name: 'D3', value: D3, color: '#DB524D', key: 'D3' },
|
||||
{ name: 'S1', value: S1, color: '#1018A8FF', key: 'S1' },
|
||||
]);
|
||||
}
|
||||
}, [stateGrafikNganggurPendidikan.findMany.data])
|
||||
|
||||
if (!stateGrafikNganggur.findMany.data) {
|
||||
return (
|
||||
<Box>
|
||||
<Skeleton h={500} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
if (!stateGrafikNganggur.findMany.data) {
|
||||
return (
|
||||
<Box>
|
||||
<Skeleton h={500} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
@@ -71,9 +85,18 @@ function Page() {
|
||||
<Stack gap={'lg'} justify='center'>
|
||||
<Paper p={'lg'}>
|
||||
<Text fw={'bold'} fz={'h3'}>Pengangguran Berdasarkan Usia</Text>
|
||||
<Center>
|
||||
<PieChart size={300} withLabelsLine labelsPosition="outside" labelsType="percent" withLabels data={datausiaKerja} withTooltip tooltipDataSource="segment" mx="auto" />
|
||||
</Center>
|
||||
{mounted && donutGrafikNganggurData.length > 0 ? (<Box style={{ width: '100%', height: 'auto', minHeight: 200 }}>
|
||||
<PieChart
|
||||
size={300}
|
||||
withLabelsLine
|
||||
labelsPosition="outside"
|
||||
labelsType="percent"
|
||||
withLabels
|
||||
data={donutGrafikNganggurData}
|
||||
withTooltip
|
||||
tooltipDataSource="segment"
|
||||
mx="auto" />
|
||||
</Box>) : <Skeleton h={500} />}
|
||||
<Flex pb={30} justify={'center'} gap={'xl'} align={'center'}>
|
||||
<Box>
|
||||
<Flex gap={{ base: 5, md: 8 }} align={'center'}>
|
||||
@@ -103,9 +126,18 @@ function Page() {
|
||||
</Paper>
|
||||
<Paper p={'lg'}>
|
||||
<Text fw={'bold'} fz={'h3'}>Pengangguran Berdasarkan Pendidikan</Text>
|
||||
<Center>
|
||||
<PieChart size={300} withLabelsLine labelsPosition="outside" labelsType="percent" withLabels data={datakerjaPendidikan} withTooltip tooltipDataSource="segment" mx="auto" />
|
||||
</Center>
|
||||
{mounted2 && donutGrafikNganggurDataPendidikan.length > 0 ? (<Center>
|
||||
<PieChart
|
||||
size={300}
|
||||
withLabelsLine
|
||||
labelsPosition="outside"
|
||||
labelsType="percent"
|
||||
withLabels
|
||||
data={donutGrafikNganggurDataPendidikan}
|
||||
withTooltip
|
||||
tooltipDataSource="segment"
|
||||
mx="auto" />
|
||||
</Center>) : <Skeleton h={500} />}
|
||||
<Flex pb={30} justify={'center'} gap={'xl'} align={'center'}>
|
||||
<Box>
|
||||
<Flex gap={{ base: 5, md: 8 }} align={'center'}>
|
||||
@@ -127,10 +159,16 @@ function Page() {
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex gap={{ base: 5, md: 8 }} align={'center'}>
|
||||
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>D3/S1</Text>
|
||||
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>D3</Text>
|
||||
<ColorSwatch color="#DB524D" size={30} />
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex gap={{ base: 5, md: 8 }} align={'center'}>
|
||||
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>S1</Text>
|
||||
<ColorSwatch color="#1018A8FF" size={30} />
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Paper>
|
||||
</Stack>
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
import colors from '@/con/colors';
|
||||
import { BarChart } from '@mantine/charts';
|
||||
import { Box, Button, Center, ColorSwatch, Flex, Group, Paper, SimpleGrid, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconChevronDown, IconDownload, IconSchool, IconSchoolOff, IconUserOff } from '@tabler/icons-react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
|
||||
const data1 = [
|
||||
{
|
||||
id: 1,
|
||||
icon: <IconUserOff size={35} />,
|
||||
judul: 'Total Pengangguran',
|
||||
jumlah: '140',
|
||||
persentase: <Text fz={'h4'} c={'green'}>-12.5% dari 2024</Text>
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: <IconSchool size={35} />,
|
||||
judul: 'Pengangguran Terdidik',
|
||||
jumlah: '80',
|
||||
persentase: <Text fz={'h4'} c={'gray'}>57.1% dari total</Text>
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: <IconSchoolOff size={35} />,
|
||||
judul: 'Pengangguran Tidak Terdidik',
|
||||
jumlah: '60',
|
||||
persentase: <Text fz={'h4'} c={'gray'}>42.9% dari total</Text>
|
||||
},
|
||||
]
|
||||
|
||||
const dataPengangguran = [
|
||||
{
|
||||
id: 1,
|
||||
bulan: 'Jan',
|
||||
berpendidikan: 98,
|
||||
takberpendidikan: 74,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
bulan: 'Feb',
|
||||
berpendidikan: 85,
|
||||
takberpendidikan: 74,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
bulan: 'Mar',
|
||||
berpendidikan: 76,
|
||||
takberpendidikan: 55,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
bulan: 'Apr',
|
||||
berpendidikan: 98,
|
||||
takberpendidikan: 74,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
bulan: 'Mei',
|
||||
berpendidikan: 74,
|
||||
takberpendidikan: 54,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
bulan: 'Jun',
|
||||
berpendidikan: 55,
|
||||
takberpendidikan: 50,
|
||||
},
|
||||
]
|
||||
const dataTable = [
|
||||
{ bulan: 'Jan', total: 160, terdidik: 95, takterdidik: 65, perubahan: '-' },
|
||||
{ bulan: 'Feb', total: 155, terdidik: 90, takterdidik: 65, perubahan: '-3.1%' },
|
||||
{ bulan: 'Mar', total: 150, terdidik: 88, takterdidik: 62, perubahan: '-3.2%' },
|
||||
{ bulan: 'Apr', total: 148, terdidik: 85, takterdidik: 63, perubahan: '-1.3%' },
|
||||
{ bulan: 'Mei', total: 145, terdidik: 82, takterdidik: 63, perubahan: '-2.0%' },
|
||||
{ bulan: 'Jun', total: 140, terdidik: 80, takterdidik: 60, perubahan: '-3.4%' },
|
||||
]
|
||||
function Page() {
|
||||
const rows = dataTable.map((element) => (
|
||||
<TableTr key={element.bulan}>
|
||||
<TableTd ta={'center'}>{element.bulan}</TableTd>
|
||||
<TableTd ta={'center'}>{element.total}</TableTd>
|
||||
<TableTd ta={'center'}>{element.terdidik}</TableTd>
|
||||
<TableTd ta={'center'}>{element.takterdidik}</TableTd>
|
||||
<TableTd ta={'center'}>{element.perubahan}</TableTd>
|
||||
</TableTr>
|
||||
));
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
<Box px={{ base: 'md', md: 100 }} >
|
||||
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Jumlah Pengangguran 2024 - 2025
|
||||
</Text>
|
||||
<Group py={20} align='center' justify='space-between'>
|
||||
<Text fz={'h4'} fw={"bold"}>DATA PENGANGGURAN DESA</Text>
|
||||
<Flex gap={'xl'}>
|
||||
<Button c={'black'} bg={colors['white-1']} rightSection={<IconChevronDown size={20} />}>2025</Button>
|
||||
<Button leftSection={<IconDownload size={20} />}>Export</Button>
|
||||
</Flex>
|
||||
</Group>
|
||||
</Box>
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Stack gap={'lg'} justify='center'>
|
||||
<SimpleGrid
|
||||
cols={1}
|
||||
pb={20}
|
||||
>
|
||||
{data1.map((v, k) => {
|
||||
return (
|
||||
<Paper px={25} key={k} py={'lg'} bg={colors['white-1']} shadow={'md'}>
|
||||
<Flex justify={'space-between'} align={'center'}>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Text fz={'h4'}>{v.judul}</Text>
|
||||
<Text fz={'h2'} fw={'bold'}>{v.jumlah}</Text>
|
||||
{v.persentase}
|
||||
</Stack>
|
||||
</Box>
|
||||
{v.icon}
|
||||
</Flex>
|
||||
</Paper>
|
||||
)
|
||||
})}
|
||||
</SimpleGrid>
|
||||
<Paper p={'lg'}>
|
||||
<Flex pb={30} justify={'flex-end'} gap={'xl'} align={'center'}>
|
||||
<Box>
|
||||
<Flex gap={{ base: 0, md: 5 }} align={'center'}>
|
||||
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Pengangguran Berpendidikan</Text>
|
||||
<ColorSwatch color="#5082EE" size={30} />
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex gap={{ base: 0, md: 5 }} align={'center'}>
|
||||
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Pengangguran Tak Berpendidikan</Text>
|
||||
<ColorSwatch color="#DA524C" size={30} />
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Center>
|
||||
<BarChart
|
||||
p={10}
|
||||
h={400}
|
||||
data={dataPengangguran}
|
||||
dataKey="bulan"
|
||||
series={[
|
||||
{ name: 'berpendidikan', color: '#5082EE' },
|
||||
{ name: 'takberpendidikan', color: '#DA524C' },
|
||||
]}
|
||||
tickLine="y"
|
||||
/>
|
||||
</Center>
|
||||
</Paper>
|
||||
<Paper p={'lg'}>
|
||||
<Text fw={'bold'} fz={'h4'}>Detail Data Pengangguran</Text>
|
||||
<Table striped highlightOnHover>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh ta={'center'}>Bulan</TableTh>
|
||||
<TableTh ta={'center'}>Total</TableTh>
|
||||
<TableTh ta={'center'}>Terdidik</TableTh>
|
||||
<TableTh ta={'center'}>Tidak Terdidik</TableTh>
|
||||
<TableTh ta={'center'}>Perubahan</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>{rows}</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
182
src/app/darmasaba/(pages)/ekonomi/jumlah-pengangguran/page.tsx
Normal file
182
src/app/darmasaba/(pages)/ekonomi/jumlah-pengangguran/page.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
'use client'
|
||||
import jumlahPengangguranState from '@/app/admin/(dashboard)/_state/ekonomi/jumlah-pengangguran';
|
||||
import colors from '@/con/colors';
|
||||
import { BarChart } from '@mantine/charts';
|
||||
import { Box, ColorSwatch, Flex, Group, Paper, SimpleGrid, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconSchool, IconSchoolOff, IconUserOff } from '@tabler/icons-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
|
||||
type DataPengangguran = {
|
||||
id: string;
|
||||
bulan: string;
|
||||
berpendidikan: number;
|
||||
takberpendidikan: number;
|
||||
}
|
||||
|
||||
function Page() {
|
||||
const [chartData, setChartData] = useState<DataPengangguran[]>([])
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const state = useProxy(jumlahPengangguranState.jumlahPengangguran)
|
||||
// const [selectedYear, setSelectedYear] = useState<string | null>(null);
|
||||
|
||||
useShallowEffect(() => {
|
||||
setMounted(true)
|
||||
state.findMany.load()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
if (state.findMany.data) {
|
||||
setChartData(state.findMany.data.map((item) => ({
|
||||
id: item.id,
|
||||
bulan: item.month,
|
||||
berpendidikan: Number(item.educatedUnemployment),
|
||||
takberpendidikan: Number(item.uneducatedUnemployment),
|
||||
})));
|
||||
}
|
||||
}, [state.findMany.data]);
|
||||
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
<Box px={{ base: 'md', md: 100 }} >
|
||||
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Jumlah Pengangguran
|
||||
</Text>
|
||||
<Group py={20} align='center' justify='space-between'>
|
||||
<Text fz={'h4'} fw={"bold"}>DATA PENGANGGURAN DESA</Text>
|
||||
</Group>
|
||||
</Box>
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Stack gap={'lg'} justify='center'>
|
||||
<SimpleGrid
|
||||
cols={1}
|
||||
pb={20}
|
||||
>
|
||||
<SimpleGrid cols={{ base: 1, md: 3 }} spacing="md">
|
||||
{/* Total Unemployment Card */}
|
||||
<Paper px={25} py={'lg'} bg={colors['white-1']} shadow="md">
|
||||
<Flex direction="column" gap="md">
|
||||
<IconUserOff size={35} color={colors['blue-button']} />
|
||||
<Text fz="h4" fw={600}>Total Pengangguran</Text>
|
||||
<Text fz="h2" fw={700} c={colors['blue-button']}>
|
||||
{state.findMany.data?.[0]?.totalUnemployment || 0} Orang
|
||||
</Text>
|
||||
<Text fz="sm" c="dimmed">
|
||||
Data tahun {state.findMany.data?.[0]?.year ? new Date(state.findMany.data[0].year).getFullYear() : ''}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Paper>
|
||||
|
||||
{/* Educated Unemployment Card */}
|
||||
<Paper px={25} py={'lg'} bg={colors['white-1']} shadow="md">
|
||||
<Flex direction="column" gap="md">
|
||||
<IconSchool size={35} color="#5082EE" />
|
||||
<Text fz="h4" fw={600}>Pengangguran Terdidik</Text>
|
||||
<Text fz="h2" fw={700} c="#5082EE">
|
||||
{state.findMany.data?.[0]?.educatedUnemployment || 0} Orang
|
||||
</Text>
|
||||
<Text fz="sm" c="dimmed">
|
||||
{state.findMany.data?.[0]?.totalUnemployment ?
|
||||
`${Math.round((state.findMany.data[0].educatedUnemployment / state.findMany.data[0].totalUnemployment) * 100)}% dari total` :
|
||||
'0% dari total'}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Paper>
|
||||
|
||||
{/* Uneducated Unemployment Card */}
|
||||
<Paper px={25} py={'lg'} bg={colors['white-1']} shadow="md">
|
||||
<Flex direction="column" gap="md">
|
||||
<IconSchoolOff size={35} color="#DA524C" />
|
||||
<Text fz="h4" fw={600}>Pengangguran Tidak Terdidik</Text>
|
||||
<Text fz="h2" fw={700} c="#DA524C">
|
||||
{state.findMany.data?.[0]?.uneducatedUnemployment || 0} Orang
|
||||
</Text>
|
||||
<Text fz="sm" c="dimmed">
|
||||
{state.findMany.data?.[0]?.totalUnemployment ?
|
||||
`${Math.round((state.findMany.data[0].uneducatedUnemployment / state.findMany.data[0].totalUnemployment) * 100)}% dari total` :
|
||||
'0% dari total'}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Paper>
|
||||
</SimpleGrid>
|
||||
</SimpleGrid>
|
||||
<Paper p={'lg'}>
|
||||
<Flex pb={30} justify={'flex-end'} gap={'xl'} align={'center'}>
|
||||
<Box>
|
||||
<Flex gap={{ base: 0, md: 5 }} align={'center'}>
|
||||
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Pengangguran Berpendidikan</Text>
|
||||
<ColorSwatch color="#5082EE" size={30} />
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex gap={{ base: 0, md: 5 }} align={'center'}>
|
||||
<Text fw={'bold'} fz={{ base: 'md', md: 'h4' }}>Pengangguran Tak Berpendidikan</Text>
|
||||
<ColorSwatch color="#DA524C" size={30} />
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
{!mounted || chartData.length === 0 ? (
|
||||
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title pb={10} order={3}>Data Pengangguran Terdidik dan Tidak Terdidik</Title>
|
||||
<Text c='dimmed'>Belum ada data untuk ditampilkan dalam grafik</Text>
|
||||
</Paper>
|
||||
</Box>
|
||||
) : (
|
||||
<Box style={{ width: '100%', minWidth: 300, height: 550, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title pb={10} order={4}>Data Pengangguran Terdidik dan Tidak Terdidik</Title>
|
||||
<Box w={{ base: '100%', md: '70%' }}>
|
||||
<BarChart
|
||||
h={450}
|
||||
data={chartData}
|
||||
dataKey="bulan"
|
||||
series={[
|
||||
{ name: 'berpendidikan', color: '#5082EE', label: 'Terdidik' },
|
||||
{ name: 'takberpendidikan', color: '#DA524C', label: 'Tidak Terdidik' },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
</Paper>
|
||||
<Paper p={'lg'}>
|
||||
<Text fw={'bold'} fz={'h4'}>Detail Data Pengangguran</Text>
|
||||
<Table striped highlightOnHover>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh ta={'center'}>Bulan</TableTh>
|
||||
<TableTh ta={'center'}>Total</TableTh>
|
||||
<TableTh ta={'center'}>Terdidik</TableTh>
|
||||
<TableTh ta={'center'}>Tidak Terdidik</TableTh>
|
||||
<TableTh ta={'center'}>Perubahan</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{state.findMany.data?.map((item, index) => (
|
||||
<TableTr key={item?.id ? String(item.id) : `row-${index}`}>
|
||||
<TableTd ta={'center'}>{item.month}</TableTd>
|
||||
<TableTd ta={'center'}>{item.totalUnemployment}</TableTd>
|
||||
<TableTd ta={'center'}>{item.educatedUnemployment}</TableTd>
|
||||
<TableTd ta={'center'}>{item.uneducatedUnemployment}</TableTd>
|
||||
<TableTd ta={'center'}>{item.percentageChange}</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -1,64 +1,52 @@
|
||||
'use client'
|
||||
import lowonganKerjaState from '@/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja';
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Text, TextInput, Group, SimpleGrid, Paper, Flex, Button } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { Box, Button, Center, Flex, Group, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { IconBriefcase, IconClock, IconMapPin, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
|
||||
const data = [
|
||||
{
|
||||
id: 1,
|
||||
kerja: 'Kasir',
|
||||
tempat: 'Toko Sumber Rejeki',
|
||||
alamat: 'Desa Munggu , Badung',
|
||||
gaji: 'Rp. 2.500.000 / bulan'
|
||||
const formatCurrency = (value: string | number) => {
|
||||
// Convert to string if it's a number
|
||||
const numStr = typeof value === 'number' ? value.toString() : value;
|
||||
|
||||
// Remove all non-digit characters
|
||||
const digitsOnly = numStr.replace(/\D/g, '');
|
||||
|
||||
// Format with thousand separators
|
||||
const formatted = digitsOnly.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
|
||||
return `Rp.${formatted}`;
|
||||
};
|
||||
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
kerja: 'Kasir',
|
||||
tempat: 'Toko Sumber Rejeki',
|
||||
alamat: 'Desa Munggu , Badung',
|
||||
gaji: 'Rp. 2.500.000 / bulan'
|
||||
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
kerja: 'Kasir',
|
||||
tempat: 'Toko Sumber Rejeki',
|
||||
alamat: 'Desa Munggu , Badung',
|
||||
gaji: 'Rp. 2.500.000 / bulan'
|
||||
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
kerja: 'Kasir',
|
||||
tempat: 'Toko Sumber Rejeki',
|
||||
alamat: 'Desa Munggu , Badung',
|
||||
gaji: 'Rp. 2.500.000 / bulan'
|
||||
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
kerja: 'Kasir',
|
||||
tempat: 'Toko Sumber Rejeki',
|
||||
alamat: 'Desa Munggu , Badung',
|
||||
gaji: 'Rp. 2.500.000 / bulan'
|
||||
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
kerja: 'Kasir',
|
||||
tempat: 'Toko Sumber Rejeki',
|
||||
alamat: 'Desa Munggu , Badung',
|
||||
gaji: 'Rp. 2.500.000 / bulan'
|
||||
|
||||
},
|
||||
|
||||
]
|
||||
function Page() {
|
||||
const state = useProxy(lowonganKerjaState)
|
||||
const router = useRouter()
|
||||
const [search, setSearch] = useState('')
|
||||
const [debouncedSearch] = useDebouncedValue(search, 500); // 500ms delay
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = state.findMany
|
||||
|
||||
useEffect(() => {
|
||||
load(page, 6, debouncedSearch)
|
||||
}, [page, debouncedSearch, load])
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
@@ -74,6 +62,8 @@ function Page() {
|
||||
w={{ base: 500, md: 700 }}
|
||||
placeholder='Cari Pekerjaan'
|
||||
leftSection={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
</Group>
|
||||
</Box>
|
||||
@@ -93,15 +83,15 @@ function Page() {
|
||||
<Flex gap={'xl'} align={'center'}>
|
||||
<IconBriefcase color={colors['blue-button']} size={50} />
|
||||
<Box>
|
||||
<Text fw={'bold'} fz={'h4'} c={colors['blue-button']}>{v.kerja}</Text>
|
||||
<Text fz={'h4'}>{v.tempat}</Text>
|
||||
<Text fw={'bold'} fz={'h4'} c={colors['blue-button']}>{v.posisi}</Text>
|
||||
<Text fz={'h4'}>{v.namaPerusahaan}</Text>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex gap={'xl'} align={'center'}>
|
||||
<IconMapPin color={colors['blue-button']} size={50} />
|
||||
<Text fz={'h4'}>{v.alamat}</Text>
|
||||
<Text fz={'h4'}>{v.lokasi}</Text>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box>
|
||||
@@ -109,7 +99,7 @@ function Page() {
|
||||
<IconClock color={colors['blue-button']} size={50} />
|
||||
<Box>
|
||||
<Text fw={'bold'} fz={'h4'} c={colors['blue-button']}>Full Time</Text>
|
||||
<Text fz={'h4'}>{v.gaji}</Text>
|
||||
<Text fz={'h4'}>{formatCurrency(v.gaji)}</Text>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
@@ -119,6 +109,14 @@ function Page() {
|
||||
)
|
||||
})}
|
||||
</SimpleGrid>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my="md"
|
||||
/>
|
||||
</Center>
|
||||
</Stack>
|
||||
</Box >
|
||||
</Stack >
|
||||
|
||||
@@ -1,90 +1,76 @@
|
||||
'use client'
|
||||
import pasarDesaState from '@/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Combobox, Flex, Image, InputBase, InputPlaceholder, Paper, SimpleGrid, Stack, Text, TextInput, useCombobox } from '@mantine/core';
|
||||
import { Box, Center, Flex, Grid, GridCol, Image, Pagination, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconMapPinFilled, IconSearch, IconShoppingCartFilled, IconStarFilled } from '@tabler/icons-react';
|
||||
import { motion } from 'motion/react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
|
||||
const groceries = [
|
||||
'Makanan',
|
||||
'Minuman',
|
||||
'Pakaian',
|
||||
'Alat Dapur',
|
||||
'Alat Mandi',
|
||||
'Furniture',
|
||||
];
|
||||
|
||||
const dataBarang = [
|
||||
{
|
||||
id: 1,
|
||||
image: '/api/img/semat.png',
|
||||
judul: 'Semat Bambu / Semat Banten',
|
||||
harga: 'Rp. 3000 / pcs',
|
||||
bintang: '4.9',
|
||||
alamat: 'Jl. Kecubung no.6'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
image: '/api/img/kerupuk.png',
|
||||
judul: 'Kerupuk Babi',
|
||||
harga: 'Rp. 12000 / pcs',
|
||||
bintang: '4.9',
|
||||
alamat: 'Jl. Kenari no.7'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
image: '/api/img/beras.png',
|
||||
judul: 'beras Merah Organik',
|
||||
harga: 'Rp. 40000 / 1 kg',
|
||||
bintang: '4.9',
|
||||
alamat: 'Jl. Mawar no.8'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
image: '/api/img/genteng.png',
|
||||
judul: 'Genteng',
|
||||
harga: 'Rp. 3600 / pcs',
|
||||
bintang: '4.9',
|
||||
alamat: 'Jl. Kecubung no.16'
|
||||
},
|
||||
|
||||
]
|
||||
function Page() {
|
||||
const [search, setSearch] = useState('');
|
||||
const combobox = useCombobox({
|
||||
onDropdownClose: () => {
|
||||
combobox.resetSelectedOption();
|
||||
combobox.focusTarget();
|
||||
setSearch('');
|
||||
},
|
||||
|
||||
onDropdownOpen: () => {
|
||||
combobox.focusSearchInput();
|
||||
},
|
||||
});
|
||||
|
||||
const [value, setValue] = useState<string | null>(null);
|
||||
|
||||
const options = groceries
|
||||
.filter((item) => item.toLowerCase().includes(search.toLowerCase().trim()))
|
||||
.map((item) => (
|
||||
<Combobox.Option value={item} key={item}>
|
||||
{item}
|
||||
</Combobox.Option>
|
||||
));
|
||||
const router = useRouter()
|
||||
const state = useProxy(pasarDesaState.pasarDesa)
|
||||
const [search, setSearch] = useState('');
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
loading,
|
||||
totalPages,
|
||||
load,
|
||||
} = state.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
pasarDesaState.kategoriProduk.findMany.load()
|
||||
}, [])
|
||||
|
||||
// Filter data based on selected category
|
||||
const filteredData = selectedCategory
|
||||
? data?.filter(item =>
|
||||
item.KategoriToPasar?.some(kategori => kategori.kategoriId === selectedCategory)
|
||||
)
|
||||
: data;
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 4, search, selectedCategory || undefined)
|
||||
}, [page, search, selectedCategory])
|
||||
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Pasar Desa
|
||||
</Text>
|
||||
<Text px={{ base: 20, md: 150 }} ta={"center"} fz={{ base: "h4", md: "h3" }} >
|
||||
<Grid align='center' px={{ base: 'md', md: 100 }}>
|
||||
<GridCol span={{ base: 12, md: 9 }}>
|
||||
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Pasar Desa
|
||||
</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 3 }}>
|
||||
<TextInput
|
||||
radius={"lg"}
|
||||
placeholder='Cari Produk'
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
leftSection={<IconSearch size={20} />}
|
||||
w={{ base: "50%", md: "100%" }}
|
||||
/>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz={{ base: "h4", md: "h3" }} >
|
||||
Pasar Desa Online merupakan Media Promosi yang bertujuan untuk membantu warga desa dalam memasarkan dan memperkenalkan produknya kepada masyarakat.
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -98,48 +84,23 @@ function Page() {
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Combobox
|
||||
store={combobox}
|
||||
withinPortal={false}
|
||||
onOptionSubmit={(val) => {
|
||||
setValue(val);
|
||||
combobox.closeDropdown();
|
||||
}}
|
||||
>
|
||||
<Combobox.Target>
|
||||
<InputBase
|
||||
component="button"
|
||||
type="button"
|
||||
pointer
|
||||
rightSection={<Combobox.Chevron />}
|
||||
onClick={() => combobox.toggleDropdown()}
|
||||
rightSectionPointerEvents="none"
|
||||
>
|
||||
{value || <InputPlaceholder>Kategori</InputPlaceholder>}
|
||||
</InputBase>
|
||||
</Combobox.Target>
|
||||
|
||||
<Combobox.Dropdown>
|
||||
<Combobox.Search
|
||||
value={search}
|
||||
onChange={(event) => setSearch(event.currentTarget.value)}
|
||||
placeholder="Search groceries"
|
||||
/>
|
||||
<Combobox.Options>
|
||||
{options.length > 0 ? options : <Combobox.Empty>Nothing found</Combobox.Empty>}
|
||||
</Combobox.Options>
|
||||
</Combobox.Dropdown>
|
||||
</Combobox>
|
||||
</Box>
|
||||
<Box>
|
||||
<TextInput
|
||||
placeholder='Cari Produk'
|
||||
leftSection={<IconSearch size={20} />}
|
||||
<Select
|
||||
placeholder="Pilih Kategori"
|
||||
data={pasarDesaState.kategoriProduk.findMany.data?.map((v) => ({
|
||||
value: v.id,
|
||||
label: v.nama
|
||||
})) || []}
|
||||
value={selectedCategory}
|
||||
onChange={setSelectedCategory}
|
||||
clearable
|
||||
searchable
|
||||
nothingFoundMessage="Tidak ada kategori ditemukan"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
<SimpleGrid cols={{ base: 1, md: 4 }}>
|
||||
{dataBarang.map((v, k) => {
|
||||
{filteredData?.map((v, k) => {
|
||||
return (
|
||||
<Stack key={k}>
|
||||
<motion.div
|
||||
@@ -148,18 +109,25 @@ function Page() {
|
||||
whileTap={{ scale: 0.8 }}
|
||||
>
|
||||
<Paper p={'lg'}>
|
||||
<Image radius={'lg'} src={v.image} alt='' />
|
||||
<Text py={10} fw={'bold'} fz={'lg'}>{v.judul}</Text>
|
||||
<Text fz={'md'}>{v.harga}</Text>
|
||||
<Image
|
||||
radius={'lg'}
|
||||
src={v.image?.link || '/placeholder-product.jpg'}
|
||||
alt={v.nama}
|
||||
h={200}
|
||||
w='100%'
|
||||
style={{ objectFit: 'cover' }}
|
||||
/>
|
||||
<Text py={10} fw={'bold'} fz={'lg'}>{v.nama}</Text>
|
||||
<Text fz={'md'}>Rp {v.harga.toLocaleString('id-ID')}</Text>
|
||||
<Flex py={10} gap={'md'}>
|
||||
<IconStarFilled size={20} color='#EBCB09' />
|
||||
<Text fz={'sm'} ml={2}>{v.bintang}</Text>
|
||||
<Text fz={'sm'} ml={2}>{v.rating}</Text>
|
||||
</Flex>
|
||||
<Flex justify={'space-between'} align={'center'}>
|
||||
<Box>
|
||||
<Flex gap={'md'} align={'center'}>
|
||||
<IconMapPinFilled size={20} color='red' />
|
||||
<Text fz={'sm'} ml={2}>{v.alamat}</Text>
|
||||
<Text fz={'sm'} ml={2}>{v.alamatUsaha}</Text>
|
||||
</Flex>
|
||||
</Box>
|
||||
<IconShoppingCartFilled size={20} color={colors['blue-button']} />
|
||||
@@ -170,6 +138,14 @@ function Page() {
|
||||
)
|
||||
})}
|
||||
</SimpleGrid>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)} // ini penting!
|
||||
total={totalPages}
|
||||
my="md"
|
||||
/>
|
||||
</Center>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
@@ -1,64 +1,82 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Text, SimpleGrid, Paper } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { Stack, Box, Text, SimpleGrid, Paper, Skeleton, Center, Pagination, Grid, GridCol, TextInput } from '@mantine/core';
|
||||
import React, { useState } from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { LineChart } from '@mantine/charts';
|
||||
import { CartesianGrid, Legend, Line, LineChart as RechartsLineChart, Tooltip, XAxis, YAxis } from 'recharts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import programKemiskinanState from '@/app/admin/(dashboard)/_state/ekonomi/program-kemiskinan';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconSearch } from '@tabler/icons-react';
|
||||
|
||||
interface StatistikData {
|
||||
id: string;
|
||||
tahun: number;
|
||||
jumlah: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
interface ProgramKemiskinanData {
|
||||
id: string;
|
||||
nama: string;
|
||||
deskripsi: string;
|
||||
ikonUrl: string | null;
|
||||
statistik: StatistikData | null;
|
||||
isActive: boolean;
|
||||
statistikId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
const data = [
|
||||
{
|
||||
id: 1,
|
||||
judul: 'Bantuan Tunai',
|
||||
deskripsi: 'Bantuan keuangan langsung bagi keluarga kurang mampu'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
judul: 'Pelatihan Kerja',
|
||||
deskripsi: 'Program pelatihan keterampilan untuk meningkatkan peluang kerja'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
judul: 'Subsidi Pangan',
|
||||
deskripsi: 'Distribusi bahan pangan bersubsidi bagi masyarakat kurang mampu'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
judul: 'Layanan Kesehatan Gratis',
|
||||
deskripsi: 'Akses kesehatan gratis bagi masyarakat kurang mampu'
|
||||
},
|
||||
]
|
||||
const dataStatistik = [
|
||||
{
|
||||
id: 1,
|
||||
tahun: '2022',
|
||||
Kemiskinan: 400000
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
tahun: '2023',
|
||||
Kemiskinan: 450000
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
tahun: '2024',
|
||||
Kemiskinan: 500000
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
tahun: '2025',
|
||||
Kemiskinan: 400000
|
||||
},
|
||||
]
|
||||
function Page() {
|
||||
const [search, setSearch] = useState('')
|
||||
const state = useProxy(programKemiskinanState)
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load
|
||||
} = state.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 2, search)
|
||||
}, [page, search])
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
<Box px={{ base: 'md', md: 100 }} >
|
||||
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Program Kemiskinan
|
||||
</Text>
|
||||
<Text ta={'center'} fz={'h4'}>Berbagai program bantuan untuk mengurangi kemiskinan dan meningkatkan kesejahteraan masyarakat</Text>
|
||||
<Grid align='center'>
|
||||
<GridCol span={{ base: 12, md: 9 }}>
|
||||
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Program Kemiskinan
|
||||
</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 3 }}>
|
||||
<TextInput
|
||||
radius={"lg"}
|
||||
placeholder='Cari Program'
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
leftSection={<IconSearch size={20} />}
|
||||
w={{ base: "50%", md: "100%" }}
|
||||
/>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Text fz={'h4'}>Berbagai program bantuan untuk mengurangi kemiskinan dan meningkatkan kesejahteraan masyarakat</Text>
|
||||
</Box>
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Stack gap={'lg'} justify='center'>
|
||||
@@ -69,26 +87,66 @@ function Page() {
|
||||
md: 2
|
||||
}}
|
||||
>
|
||||
{data.map((v, k) => {
|
||||
{state.findMany.data.map((v, k) => {
|
||||
return (
|
||||
<Paper p={'xl'} key={k}>
|
||||
<Text fz={'h3'} fw={'bold'} c={colors['blue-button']}>{v.judul}</Text>
|
||||
<Text fz={'lg'} c={'black'}>{v.deskripsi}</Text>
|
||||
<Text fz={'h3'} fw={'bold'} c={colors['blue-button']}>{v.nama}</Text>
|
||||
<Text fz={'lg'} c={'black'} dangerouslySetInnerHTML={{ __html: v.deskripsi }}></Text>
|
||||
</Paper>
|
||||
)
|
||||
})}
|
||||
</SimpleGrid>
|
||||
<Paper p={'xl'}>
|
||||
<Text fz={'h4'} fw={'bold'} c={colors['blue-button']}>Statistik Kemiskinan Masyarakat</Text>
|
||||
<LineChart
|
||||
h={300}
|
||||
data={dataStatistik}
|
||||
dataKey="tahun"
|
||||
series={[
|
||||
{ name: 'Kemiskinan', color: colors['blue-button'] },
|
||||
]}
|
||||
curveType="linear"
|
||||
<Center my={10}>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
my={"md"}
|
||||
/>
|
||||
</Center>
|
||||
<Paper p={'xl'}>
|
||||
<Text fz={'h3'} fw={'bold'} c={colors['blue-button']} mb="md">Statistik Kemiskinan Masyarakat</Text>
|
||||
<Box style={{ width: '100%', height: 'auto' }}>
|
||||
{data.length > 0 && data[0]?.statistik ? (
|
||||
<Box w="100%" style={{ overflowX: 'auto' }}>
|
||||
<Center>
|
||||
<RechartsLineChart
|
||||
width={800}
|
||||
height={300}
|
||||
data={state.findMany.data
|
||||
.filter((item): item is ProgramKemiskinanData & { statistik: StatistikData } =>
|
||||
item.statistik !== null
|
||||
)
|
||||
.map(item => ({
|
||||
tahun: item.statistik.tahun,
|
||||
jumlah: item.statistik.jumlah
|
||||
}))
|
||||
.sort((a, b) => a.tahun - b.tahun)
|
||||
}
|
||||
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="tahun" />
|
||||
<YAxis />
|
||||
<Tooltip
|
||||
formatter={(value: number, name: string) => [`${value} orang`, name]}
|
||||
labelFormatter={(label: number) => `Tahun: ${label}`}
|
||||
/>
|
||||
<Legend />
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="jumlah"
|
||||
name="Jumlah Masyarakat Miskin"
|
||||
stroke={colors['blue-button']}
|
||||
activeDot={{ r: 8 }}
|
||||
/>
|
||||
</RechartsLineChart>
|
||||
</Center>
|
||||
</Box>
|
||||
) : (
|
||||
<Text c="dimmed">Belum ada data statistik yang tersedia</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Text, Paper } from '@mantine/core';
|
||||
import { Stack, Box, Text, Paper, Skeleton } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { BarChart } from '@mantine/charts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import grafikSektorUnggulan from '@/app/admin/(dashboard)/_state/ekonomi/sektor-unggulan-desa';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
|
||||
const data = [
|
||||
{
|
||||
id: 1,
|
||||
sektor: 'Sektor Pertanian',
|
||||
Ton: 20
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
sektor: 'Sektor Peternakan',
|
||||
Ton: 5
|
||||
},
|
||||
|
||||
]
|
||||
function Page() {
|
||||
const state = useProxy(grafikSektorUnggulan)
|
||||
|
||||
const {
|
||||
data,
|
||||
loading,
|
||||
} = state.findMany
|
||||
|
||||
useShallowEffect(() => {
|
||||
state.findMany.load()
|
||||
}, [])
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
@@ -31,28 +41,24 @@ function Page() {
|
||||
</Box>
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Stack gap={'lg'} justify='center'>
|
||||
<Paper p={'xl'}>
|
||||
<Text fw={'bold'} fz={'h4'}>Jumlah Penduduk Miskin Per Tahun</Text>
|
||||
<Text fz={'h4'} ta={'justify'}>
|
||||
Pertanian di Darmasaba berfokus pada padi, sayuran, dan hortikultura yang dikembangkan dengan metode pertanian
|
||||
organik serta sistem irigasi tradisional yang efisien. Keberlanjutan dalam pertanian juga didukung dengan pemanfaatan
|
||||
teknologi modern untuk meningkatkan produktivitas hasil panen.
|
||||
</Text>
|
||||
</Paper>
|
||||
<Paper p={'xl'}>
|
||||
<Text fw={'bold'} fz={'h4'}>Sektor Peternakan</Text>
|
||||
<Text fz={'h4'} ta={'justify'}>
|
||||
Di bidang peternakan, Desa Darmasaba memiliki potensi besar dalam pengembangan sapi, ayam, dan babi. Sistem
|
||||
peternakan yang diterapkan mengutamakan pengelolaan pakan alami dan perawatan hewan yang sehat, sehingga
|
||||
menghasilkan produk ternak berkualitas tinggi.
|
||||
</Text>
|
||||
</Paper>
|
||||
{data.map((v, k) => {
|
||||
return (
|
||||
<Paper p={'xl'} key={k}>
|
||||
<Text fw={'bold'} fz={'h4'}>{v.name}</Text>
|
||||
<Text fz={'h4'} ta={'justify'} dangerouslySetInnerHTML={{ __html: v.description || '' }} />
|
||||
</Paper>
|
||||
)
|
||||
})}
|
||||
<Paper p={'xl'}>
|
||||
<Text pb={10} fw={'bold'} fz={'h4'}>Statistik Sektor Unggulan Darmasaba</Text>
|
||||
<BarChart
|
||||
p={10}
|
||||
h={300}
|
||||
data={data}
|
||||
data={data.map((item) => ({
|
||||
id: item.id,
|
||||
sektor: item.name,
|
||||
Ton: item.value,
|
||||
}))}
|
||||
dataKey="sektor"
|
||||
series={[
|
||||
{ name: 'Ton', color: colors['blue-button'] },
|
||||
|
||||
@@ -1,67 +1,61 @@
|
||||
'use client'
|
||||
import keamananLingkunganState from '@/app/admin/(dashboard)/_state/keamanan/keamanan-lingkungan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Center, Image, List, ListItem, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
|
||||
import { Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconSearch } from '@tabler/icons-react';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
|
||||
const data1 = [
|
||||
{
|
||||
id: 1,
|
||||
judul: 'Peran Pecalang dalam Keamanan Desa',
|
||||
image: '/api/img/pecalang.png',
|
||||
pengertian: 'Pecalang adalah petugas keamanan adat di Bali yang memiliki peran penting dalam menjaga ketertiban dan budaya lokal. Tugas mereka meliputi:',
|
||||
deskripsi: <List>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Mengamankan upacara adat dan kegiatan keagamaan.</ListItem>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Mengatur lalu lintas saat ada perayaan atau kegiatan besar.</ListItem>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Berpatroli untuk mencegah gangguan keamanan di lingkungan desa.</ListItem>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Berkoordinasi dengan aparat desa dan kepolisian dalam penanganan situasi darurat.</ListItem>
|
||||
</List>
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
judul: 'Patwal (Patroli Pengawal) Desa',
|
||||
image: '/api/img/patwal-1.png',
|
||||
pengertian: 'Selain Pecalang, Desa Darmasaba juga memiliki Patwal yang bertugas menjaga keamanan sehari-hari. Peran mereka antara lain:',
|
||||
deskripsi: <List>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Berpatroli secara rutin untuk memastikan lingkungan tetap aman.</ListItem>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Menjaga ketertiban lalu lintas di area desa.</ListItem>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Melakukan tindakan preventif terhadap potensi gangguan keamanan.</ListItem>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Siap siaga dalam keadaan darurat untuk membantu warga.</ListItem>
|
||||
</List>
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
judul: 'Layanan Keamanan yang Tersedia',
|
||||
image: '/api/img/pospecalang.png',
|
||||
pengertian: 'Jika terjadi keadaan darurat atau membutuhkan bantuan keamanan, warga dapat menghubungi:',
|
||||
deskripsi: <List>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Pos Pecalang Desa: [Masukkan Nomor Kontak].</ListItem>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Patwal Desa Darmasaba: [Masukkan Nomor Kontak].</ListItem>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Polsek Terdekat: 110 (Layanan Kepolisian).</ListItem>
|
||||
</List>
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
judul: 'Program Keamanan Desa',
|
||||
image: '/api/img/rond.png',
|
||||
pengertian: 'Untuk meningkatkan keamanan, Desa Darmasaba menjalankan berbagai program, seperti:',
|
||||
deskripsi: <List>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Ronda Malam Warga: Kegiatan jaga malam secara bergilir oleh warga bersama Pecalang dan Patwal.</ListItem>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}>Sosialisasi Keamanan: Edukasi bagi warga tentang cara menjaga keamanan lingkungan.</ListItem>
|
||||
<ListItem fz={{ base: 'h4', md: 'lg' }}> Pengawasan Kamera CCTV: Memantau titik- titik strategis untuk mencegah tindak kejahatan.</ListItem>
|
||||
</List>
|
||||
}
|
||||
]
|
||||
|
||||
function Page() {
|
||||
const state = useProxy(keamananLingkunganState)
|
||||
const [search, setSearch] = useState('')
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = state.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 3, search)
|
||||
}, [page, search])
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||
<Box px={{ base: 'md', md: 100 }}>
|
||||
<BackButton />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Keamanan Lingkungan (Pecalang / Patwal)
|
||||
</Text>
|
||||
<Text px={{ base: 20, md: 150 }} ta={"center"} fz={{ base: "h4", md: "h3" }} >
|
||||
<Grid align='center' px={{ base: 'md', md: 100 }}>
|
||||
<GridCol span={{ base: 12, md: 9 }}>
|
||||
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Keamanan Lingkungan (Pecalang / Patwal)
|
||||
</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 3 }}>
|
||||
<TextInput
|
||||
radius={"lg"}
|
||||
placeholder='Cari Puskesmas'
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
leftSection={<IconSearch size={20} />}
|
||||
w={{ base: "50%", md: "100%" }}
|
||||
/>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz={{ base: "h4", md: "h3" }} >
|
||||
Keamanan dan ketertiban lingkungan di Desa Darmasaba dijaga melalui peran aktif Pecalang dan Patwal (Patroli Pengawal). Mereka bertugas memastikan desa tetap aman, tertib, dan kondusif bagi seluruh warga.
|
||||
</Text>
|
||||
</Box>
|
||||
@@ -73,24 +67,19 @@ function Page() {
|
||||
base: 1,
|
||||
md: 3,
|
||||
}}>
|
||||
{data1.map((v, k) => {
|
||||
{data.map((v, k) => {
|
||||
return (
|
||||
<Paper radius={10} key={k} bg={colors["white-trans-1"]}>
|
||||
<Stack gap={'xs'}>
|
||||
<Center px={10} py={20}>
|
||||
<Image src={v.image} alt='' />
|
||||
<Image src={v.image?.link} alt='' />
|
||||
</Center>
|
||||
<Box px={'lg'}>
|
||||
<Box pb={20}>
|
||||
<Text pb={10} c={colors["blue-button"]} fw={"bold"} fz={"h3"}>
|
||||
{v.judul}
|
||||
{v.name}
|
||||
</Text>
|
||||
<Text pb={10} fz={"h4"} ta={'justify'}>
|
||||
{v.pengertian}
|
||||
</Text>
|
||||
<Box px={10}>
|
||||
{v.deskripsi}
|
||||
</Box>
|
||||
<Text pb={10} fz={"h4"} ta={'justify'} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
@@ -99,7 +88,15 @@ function Page() {
|
||||
})}
|
||||
</SimpleGrid>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)} // ini penting!
|
||||
total={totalPages}
|
||||
my="md"
|
||||
/>
|
||||
</Center>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user