Compare commits
66 Commits
nico/23-ju
...
nico/2-sep
| Author | SHA1 | Date | |
|---|---|---|---|
| fa9601e126 | |||
| 7ae83788b4 | |||
| 22ec8d942d | |||
| 9f9a0fb451 | |||
| b6d6583e77 | |||
| a8fd715822 | |||
| f9530c32eb | |||
| f15ef5a275 | |||
| 3a726a3334 | |||
| b21e1f0c2e | |||
| f63249327d | |||
| bb8dab05ba | |||
| 3081e426bd | |||
| 8a275c2a32 | |||
| 8469ebd2e1 | |||
| 760ba4b6d2 | |||
| 20d4c90e60 | |||
| fafbb12a08 | |||
| 01aa0da5cc | |||
| b580978f8e | |||
| 1c01397c0d | |||
| 90a6605efd | |||
| c22d865283 | |||
| 49067f0218 | |||
| d79425d529 | |||
| 4491d23bea | |||
| 1e154ced86 | |||
| bcc51aec12 | |||
| 8d15563f15 | |||
| d7a592c635 | |||
| 5e137ba658 | |||
| c99416c7f8 | |||
| 212e2db1fb | |||
| b8a45bc451 | |||
| 0777b00a7d | |||
| a035039b2c | |||
| a6832cad40 | |||
| a1d55e2b0a | |||
| c1583c21b1 | |||
| 2fe8b8ce1a | |||
| 5cbf7810bc | |||
| b3bf6b0327 | |||
| a65529cb23 | |||
| afc7bced44 | |||
| 0ac9fa1f53 | |||
| d4af56b508 | |||
| b62c4be30a | |||
| ab887c30e6 | |||
| 8e76a83d14 | |||
| a2b68ec78b | |||
| 0e55462adc | |||
| 73ae198158 | |||
| 9d14bb0c56 | |||
| 1cdff53c56 | |||
| 54312e9486 | |||
| 024d5517fa | |||
| 4e61695649 | |||
| c11cc421a4 | |||
| 0109886e00 | |||
| 50e8999205 | |||
| e2e1672c80 | |||
| ac0eb926eb | |||
| b24bcd8019 | |||
| 5601e59922 | |||
| a25cfe8b8a | |||
| b745bd4623 |
13
package.json
13
package.json
@@ -14,8 +14,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@cubejs-client/core": "^0.31.0",
|
||||
"@elysiajs/cookie": "^0.8.0",
|
||||
"@elysiajs/cors": "^1.2.0",
|
||||
"@elysiajs/eden": "^1.3.2",
|
||||
"@elysiajs/jwt": "^1.3.2",
|
||||
"@elysiajs/static": "^1.3.0",
|
||||
"@elysiajs/stream": "^1.1.0",
|
||||
"@elysiajs/swagger": "^1.2.0",
|
||||
@@ -44,8 +46,10 @@
|
||||
"@types/lodash": "^4.17.16",
|
||||
"add": "^2.0.6",
|
||||
"animate.css": "^4.1.1",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"bun": "^1.2.2",
|
||||
"chart.js": "^4.4.8",
|
||||
"classnames": "^2.5.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"elysia": "^1.3.5",
|
||||
"embla-carousel-autoplay": "^8.5.2",
|
||||
@@ -54,6 +58,7 @@
|
||||
"framer-motion": "^12.23.5",
|
||||
"get-port": "^7.1.0",
|
||||
"jotai": "^2.12.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"leaflet": "^1.9.4",
|
||||
"list": "^2.0.19",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -63,12 +68,15 @@
|
||||
"next-view-transitions": "^0.3.4",
|
||||
"node-fetch": "^3.3.2",
|
||||
"p-limit": "^6.2.0",
|
||||
"primeicons": "^7.0.0",
|
||||
"primereact": "^10.9.6",
|
||||
"prisma": "^6.3.1",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-leaflet": "^5.0.0",
|
||||
"react-simple-toasts": "^6.1.0",
|
||||
"react-toastify": "^11.0.5",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"readdirp": "^4.1.1",
|
||||
"recharts": "^2.15.3",
|
||||
"swr": "^2.3.2",
|
||||
@@ -78,15 +86,16 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.1.6",
|
||||
"parcel": "^2.6.2",
|
||||
"postcss": "^8.5.1",
|
||||
"postcss-preset-mantine": "^1.17.0",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"typescript": "^5",
|
||||
"parcel": "^2.6.2"
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
||||
57
prisma/data/desa/layanan/pelayananSuratKeterangan.json
Normal file
57
prisma/data/desa/layanan/pelayananSuratKeterangan.json
Normal file
@@ -0,0 +1,57 @@
|
||||
[
|
||||
{
|
||||
"id" : "cmdxyb9zi0010vniiaeyi55ui",
|
||||
"name" : "Surat Keterangan Beda Biodata Diri",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Fotocopy dokumen bersangkutan yang terdapat perbedaan biodata diri misal : Sertifikat Tanah/Ijazah/Polis Asuransi dan lainnya.</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxycqz40014vniidftrixvf",
|
||||
"name" : "Surat Keterangan Yatim Piatu",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau KIA atau Kartu Keluarga</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdwx3wph0003vnr74us2t7h7",
|
||||
"name" : "Surat Keterangan Domisili Organisasi",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen:</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy Surat Keterangan Terdaftar (SKT) organisasi atau Pengukuhan Kelompok</p></li><li><p>Jika Pengajuan baru pembuatan SKT maka melengkapi Susunan Pengurus lengkap dengan Kop Organisasi</p></li><li><p>Tanggal berdiri/Tahun berdiri/Sejak kapan berdirinya organisasi</p></li></ul><p>Alur Pelayanan:</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxxv3i80004vniidg1mrucc",
|
||||
"name" : "Surat Keterangan Penghasilan",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP orang tua atau Fotocopy Kartu keluarga</p></li><li><p>Membuat Surat Pernyataan Penghasilan bermaterai (disertai jumlah penghasilan)</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxxwp070008vnii9jbdcto7",
|
||||
"name" : "Surat Keterangan Tidak Mampu",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP/KIA atau Kartu Keluarga</p></li><li><p>Fotocopy Kartu Indonesia Pintar/Kartu Perlindungan Sosial/Terdaftar dalam DTKS</p></li><li><p>Jika tidak memiliki Kartu tersebut diatas diwajibkan membuat Surat Pernyataan Tidak Mampu</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxxyfkl000cvnii1bxinnfi",
|
||||
"name" : "Surat Keterangan Kelahiran",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy Surat lahir dari dokter/bidan (jika ada)</p></li><li><p>Fotocopy Kartu Keluarga</p></li><li><p>Fotocopy KTP 2 orang saksi</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxy23pl000gvniihsg38aq4",
|
||||
"name" : "Surat Keterangan Usaha",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Foto Lokasi dan Kegiatan Usaha di cetak dalam selembar kertas (diparaf dan stempel oleh Kelian Banjar Dinas)</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxy4mgt000kvniib1nemjem",
|
||||
"name" : "Surat Keterangan Kematian",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Surat Kematian dari rumah sakit atau dokter (jika ada)</p></li><li><p>tanggal kematian</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxy61a1000ovniif4ytb9hs",
|
||||
"name" : "Surat Keterangan Tempat Usaha",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Foto Lokasi dan Kegiatan Usaha di cetak dalam selembar kertas (diparaf dan stempel oleh Kelian Banjar Dinas)</p></li><li><p>Surat Perjanjian Sewa/Kontrak atau Kwintansi Pembayaran Sewa 3 bulan terakhir bagi yang mengontrak tempat usaha, apabila tempat usaha milik sendiri lampiri dengan dokumen kepemilikan tempat usaha (dapat berupa fotocopy sppt atau Fotocopy Sertipikat Hak Milik)</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxy754q000svniiiz8oqyo0",
|
||||
"name" : "Surat Keterangan Belum Kawin",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Khusus bagi yang berstatus duda atau janda melampirkan fotocopy akta cerai atau dokumen pendukung lainnya</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxy8pi2000wvnii48fc1sxd",
|
||||
"name" : "Surat Keterangan Kelakuan Baik",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
}
|
||||
]
|
||||
20
prisma/data/desa/layanan/pelayananTelunjukSaktiDesa.json
Normal file
20
prisma/data/desa/layanan/pelayananTelunjukSaktiDesa.json
Normal file
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"id": "cmdy0dwx10000vnnb6nmt06rv",
|
||||
"name": "Telunjuk Sakti Desa Akta Kelahiran (Petunjuk Pengajuan pada link berikut : Download",
|
||||
"deskripsi": "Akta Kelahiran",
|
||||
"link": "https://darmasaba.desa.id/storage/files/PERSYARATAN%20DAN%20ALUR%20PENGAJUAN%20AKTA%20KELAHIRAN_(dengan%20contoh%20Formulir).pdf"
|
||||
},
|
||||
{
|
||||
"id": "cmdy0ttpz0001vnnbrvr9jb3z",
|
||||
"name": "Telunjuk Sakti Desa Akta Perkawinan (Petunjuk Pengajuan pada link berikut : Download",
|
||||
"deskripsi": "Akta Perkawinan",
|
||||
"link": "https://darmasaba.desa.id/storage/files/PERSYARATAN%20DAN%20ALUR%20PENGAJUAN%20AKTA%20PERKAWINAN_(dengan%20contoh%20Formulir).pdf"
|
||||
},
|
||||
{
|
||||
"id": "cmdy0vjic0002vnnbcp0e9lgq",
|
||||
"name": "Telunjuk Sakti Desa Akta Kematian (Petunjuk Pengajuan pada link berikut : Download",
|
||||
"deskripsi": "Akta Kematian",
|
||||
"link": "https://darmasaba.desa.id/storage/files/PERSYARATAN%20DAN%20ALUR%20PENGAJUAN%20AKTA%20KEMATIAN_(dengan%20contoh%20Formulir).pdf"
|
||||
}
|
||||
]
|
||||
74
prisma/data/desa/potensi/potensi-desa.json
Normal file
74
prisma/data/desa/potensi/potensi-desa.json
Normal file
@@ -0,0 +1,74 @@
|
||||
[
|
||||
{
|
||||
"id": "cmdyamai40004vnw3sdjbvn48",
|
||||
"name": "TPS3R Pudak Mesari",
|
||||
"deskripsi": "TPS 3R Pudak Mesari Darmasaba layak mendapat penghargaan demikian apresiasi dari Delterra Sosial Indonesia nie Semeton Darmasaba!, Hal tersebut dikarenakan walaupun baru berdiri namun TPS 3R kebanggaan Desa Darmasaba tersebut sudah berjalan dengan sangat baik.",
|
||||
"content": "<p>TPS3R Pudak Mesari adalah Tempat Pengolahan Sampah dengan konsep Reduce, Reuse, dan Recycle (TPS3R) yang berlokasi di Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung, Bali. Fasilitas ini berperan penting dalam pengelolaan sampah berbasis masyarakat, dengan tujuan mengurangi volume sampah yang masuk ke Tempat Pembuangan Akhir (TPA) dan meningkatkan kesadaran warga tentang pentingnya pengelolaan sampah yang berkelanjutan.</p><p>Potensi Desa melalui TPS3R Pudak Mesari:</p><ol><li><p><strong>Peningkatan Kesehatan Lingkungan:</strong></p><p>Dengan pengelolaan sampah yang efektif, desa dapat menjaga kebersihan lingkungan, mengurangi risiko penyakit, dan menciptakan suasana yang lebih nyaman bagi warga.</p></li><li><p><strong>Pemberdayaan Ekonomi Masyarakat:</strong></p><p>TPS3R membuka peluang usaha bagi warga melalui pemilahan dan pengolahan sampah, seperti produksi kompos dari sampah organik dan kerajinan tangan dari sampah anorganik yang dapat meningkatkan pendapatan masyarakat.</p></li><li><p><strong>Edukasi dan Kesadaran Lingkungan:</strong></p><p>Fasilitas ini dapat menjadi pusat edukasi bagi masyarakat tentang pentingnya pengelolaan sampah, mendorong partisipasi aktif dalam menjaga kelestarian lingkungan.</p></li><li><p><strong>Pengembangan Pariwisata Berkelanjutan:</strong></p><p>Dengan lingkungan yang bersih dan asri, Desa Darmasaba memiliki potensi untuk menarik wisatawan yang tertarik pada ekowisata dan budaya lokal, sehingga meningkatkan perekonomian desa.</p></li></ol>"
|
||||
},
|
||||
{
|
||||
"id": "cmdyb7h440003vngjapbc84f7",
|
||||
"name": "Bumdes Pudak Mesari",
|
||||
"deskripsi": "Bumdes Pudak Mesari sangat membantu warga desa Darmasaba dalam mengelola dan membangun sebuah desa yang lebih baik lagi",
|
||||
"content": "<p>Badan Usaha Milik Desa (BUMDes) Pudak Mesari adalah lembaga ekonomi desa yang berperan penting dalam pengembangan potensi dan kesejahteraan masyarakat Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung, Bali. BUMDes ini berfungsi sebagai motor penggerak perekonomian desa melalui berbagai unit usaha yang dikelola secara profesional.</p><p>Potensi dan Peran BUMDes Pudak Mesari:</p><ol><li><p><strong>Pengembangan Usaha Mikro dan Kecil:</strong></p><p>BUMDes Pudak Mesari menyediakan layanan bagi pelaku usaha mikro dan kecil di desa, seperti penyediaan konsumsi dan snack kotak untuk berbagai acara.</p></li><li><p><strong>Pengelolaan Sampah Berbasis Masyarakat:</strong></p><p>Melalui kolaborasi dengan komunitas pemuda peduli lingkungan, BUMDes Pudak Mesari aktif dalam pengelolaan sampah berbasis masyarakat.</p></li><li><p><strong>Peningkatan Kapasitas dan Transparansi:</strong></p><p>Untuk memastikan pengelolaan yang akuntabel, BUMDes Pudak Mesari rutin mengadakan rapat koordinasi dan pendampingan penyusunan laporan pertanggungjawaban.</p></li><li><p><strong>Kolaborasi Internasional:</strong></p><p>Desa Darmasaba, melalui BUMDes Pudak Mesari, menerima kunjungan dari tim Osaki Jepang untuk memperkuat pengelolaan sampah dan lingkungan.</p></li></ol><p>Dengan berbagai inisiatif tersebut, BUMDes Pudak Mesari menunjukkan perannya sebagai pilar utama dalam pengembangan ekonomi dan kesejahteraan masyarakat Desa Darmasaba, sekaligus menjaga kelestarian lingkungan melalui program-program inovatif dan kolaboratif.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdybb53i0007vngjet38spn8",
|
||||
"name": "Taman Beji Cengana",
|
||||
"deskripsi": "Tirta Klebutan di Pura Taman Beji Cengana di Desa Adat Darmasaba, Badung, selain dipercaya nunas Taksu serta pembersihan diri. Tersemat juga asal usul cerita ditemukannya Tirta Klebutan yang tepat berada di pinggir Tukad Cengana tersebut.",
|
||||
"content": "<p>Taman Beji Cengana, terletak di Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung, Bali, adalah situs suci yang memiliki nilai spiritual dan sejarah yang tinggi. Tempat ini dikenal sebagai lokasi untuk ritual pembersihan diri (melukat) dan peribadatan oleh umat Hindu Bali. Keberadaan mata air suci (Tirta Klebutan) di Taman Beji Cengana dipercaya memberikan berkah dan penyucian bagi mereka yang datang untuk berdoa dan melakukan ritual.</p><p>Potensi Desa melalui Taman Beji Cengana:</p><ol><li><p><strong>Pengembangan Pariwisata Spiritual:</strong></p><p>Taman Beji Cengana memiliki potensi besar sebagai destinasi wisata spiritual. Wisatawan yang mencari pengalaman spiritual dan ketenangan batin dapat tertarik untuk mengunjungi tempat ini, mengikuti ritual melukat, dan merasakan suasana sakral yang ditawarkan.</p></li><li><p><strong>Pelestarian Budaya dan Tradisi:</strong></p><p>Dengan mempromosikan Taman Beji Cengana sebagai pusat kegiatan budaya dan ritual tradisional, desa dapat memastikan bahwa warisan budaya dan tradisi lokal tetap lestari.</p></li><li><p><strong>Pendidikan dan Penelitian:</strong></p><p>Taman Beji Cengana dapat dijadikan sebagai pusat pendidikan dan penelitian bagi akademisi, peneliti, dan pelajar yang tertarik mempelajari budaya, agama, dan sejarah Bali.</p></li><li><p><strong>Pengembangan Ekonomi Kreatif:</strong></p><p>Dengan meningkatnya jumlah pengunjung ke Taman Beji Cengana, peluang bagi pengembangan ekonomi kreatif juga terbuka lebar. Masyarakat lokal dapat mengembangkan produk kerajinan tangan, kuliner khas, dan suvenir yang mencerminkan budaya dan tradisi desa.</p></li><li><p><strong>Konservasi Lingkungan:</strong></p><p>Sebagai situs suci dengan mata air alami, Taman Beji Cengana memiliki peran penting dalam konservasi lingkungan. Upaya menjaga kebersihan dan kelestarian mata air serta lingkungan sekitarnya dapat menjadi contoh praktik konservasi yang baik.</p></li></ol><p>Dengan memanfaatkan potensi yang dimiliki Taman Beji Cengana, Desa Darmasaba dapat mengembangkan sektor pariwisata, budaya, pendidikan, ekonomi, dan lingkungan secara berkelanjutan, yang pada gilirannya akan meningkatkan kesejahteraan masyarakat dan pelestarian warisan budaya.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdybckcz000avngjfzpy60uk",
|
||||
"name": "Gumuh Sari Water Park",
|
||||
"deskripsi": "Gumuh Sari Rekreasi atau waterpark, tempat wisata yang asyik dan seru untuk kamu sekeluarga! Tempat liburan di Bali memang seakan nggak ada habisnya. Selalu ada aja destinasi wisata seru yang bisa jadi wishlist. Ada banyak banget tempat wisata yang kamu kunjungi di Bali, mulai dari wisata alam, wisata modern, sampai wisata air.",
|
||||
"content": "<p>Gumuh Sari Waterpark, terletak di Jl. Tegal Gumuh No. 9, Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung, Bali, adalah destinasi rekreasi yang menawarkan berbagai fasilitas untuk pengunjung dari segala usia. Taman rekreasi ini tidak hanya menyediakan wahana air yang menyenangkan, tetapi juga fasilitas olahraga dan kuliner, menjadikannya tempat ideal untuk rekreasi keluarga dan komunitas.</p><p>Potensi Desa melalui Gumuh Sari Waterpark:</p><ol><li><p><strong>Pengembangan Pariwisata Lokal:</strong></p><p>Dengan adanya destinasi seperti Gumuh Sari Waterpark, Desa Darmasaba dapat menarik lebih banyak wisatawan lokal maupun mancanegara. Kehadiran pengunjung ini berpotensi meningkatkan pendapatan desa dan membuka peluang usaha baru bagi masyarakat setempat.</p></li><li><p><strong>Peningkatan Ekonomi Masyarakat:</strong></p><p>Fasilitas seperti restoran dan pusat olahraga di dalam kompleks waterpark memberikan peluang bagi warga lokal untuk terlibat dalam sektor jasa dan perdagangan. Hal ini dapat menciptakan lapangan pekerjaan dan mendukung pertumbuhan ekonomi desa.</p></li><li><p><strong>Pengembangan Fasilitas Olahraga dan Kesehatan:</strong></p><p>Dengan adanya pusat futsal dan gym, Gumuh Sari Waterpark mendorong masyarakat untuk berpartisipasi dalam kegiatan olahraga, yang dapat meningkatkan kesehatan dan kesejahteraan warga.</p></li><li><p><strong>Pemberdayaan Komunitas Melalui Event dan Acara:</strong></p><p>Waterpark ini sering menjadi tuan rumah berbagai acara komunitas, seperti pesta busa dan bola, yang dapat mempererat hubungan antarwarga dan menciptakan lingkungan yang harmonis.</p></li><li><p><strong>Peningkatan Infrastruktur dan Aksesibilitas:</strong></p><p>Dengan meningkatnya jumlah pengunjung, infrastruktur desa seperti jalan, transportasi, dan layanan umum lainnya akan berkembang untuk memenuhi kebutuhan tersebut, yang pada gilirannya meningkatkan kualitas hidup masyarakat setempat.</p></li></ol><p>Melalui pengelolaan dan pengembangan yang tepat, Gumuh Sari Waterpark dapat menjadi motor penggerak bagi kemajuan Desa Darmasaba, meningkatkan kesejahteraan masyarakat, dan menjadikan desa ini sebagai destinasi wisata yang dikenal luas.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdyjuij40002vns5qyyjmzf4",
|
||||
"name": "Pertanian",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, Bali, memiliki potensi pertanian yang besar sebagai bagian dari warisan agraris yang telah diwariskan secara turun-temurun. Dengan kondisi tanah yang subur serta sistem irigasi tradisional subak, pertanian di Darmasaba memainkan peran penting dalam ekonomi dan keberlanjutan lingkungan desa.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, Bali, memiliki potensi pertanian yang besar sebagai bagian dari warisan agraris yang telah diwariskan secara turun-temurun. Dengan kondisi tanah yang subur serta sistem irigasi tradisional subak, pertanian di Darmasaba memainkan peran penting dalam ekonomi dan keberlanjutan lingkungan desa.</p><p>Potensi Desa melalui Pertanian:</p><ol><li><p><strong>Potensi dan Komoditas Unggulan</strong></p><p>Pertanian di Desa Darmasaba mengandalkan berbagai jenis tanaman yang memiliki nilai ekonomi tinggi, di antaranya:</p><p>- Padi : Sebagai salah satu desa yang masih mempertahankan sistem subak, Darmasaba menjadi bagian dari lumbung pangan di Bali.</p><p>- Sayur-mayur : Beberapa jenis sayuran seperti kangkung, bayam, cabai, dan tomat banyak dibudidayakan oleh petani lokal.</p><p>- Buah-buahan tropis : Termasuk pisang, mangga, dan kelapa, yang menjadi sumber pendapatan tambahan bagi petani.</p><p>- Tanaman obat dan rempah : Seperti jahe, kunyit, dan lengkuas, yang memiliki permintaan tinggi baik untuk kebutuhan rumah tangga maupun industri herbal.</p></li><li><p><strong>Sistem Irigasi Tradisional Subak:</strong></p><p>Sebagai bagian dari warisan budaya Bali, sistem irigasi subak masih diterapkan di Darmasaba. Sistem ini memungkinkan distribusi air yang adil di antara lahan pertanian dan membantu menjaga keberlanjutan produksi pangan desa.</p></li><li><p><strong>Pengembangan Pertanian Organik:</strong></p><p>Dengan meningkatnya kesadaran akan pentingnya produk sehat dan ramah lingkungan, beberapa petani di Darmasaba mulai beralih ke metode pertanian organik. Hal ini membuka peluang bagi desa untuk mengembangkan produk-produk pertanian yang memiliki nilai jual lebih tinggi.</p></li><li><p><strong>Agrowisata sebagai Sumber Pendapatan Baru:</strong></p><p>Potensi pertanian Darmasaba juga dapat dikembangkan menjadi agrowisata, di mana wisatawan dapat merasakan pengalaman langsung dalam bertani, mengikuti workshop bercocok tanam, serta menikmati hasil pertanian segar. Hal ini dapat menarik wisatawan lokal maupun mancanegara, meningkatkan perekonomian desa.</p></li><li><p><strong>Pemberdayaan Petani dan UMKM Berbasis Pertanian:</strong></p><p>Dengan adanya BUMDes Pudak Mesari dan dukungan dari pemerintah setempat, petani di Darmasaba dapat diberikan pelatihan dan akses pasar yang lebih luas. Produk pertanian dapat diolah menjadi berbagai produk turunan seperti keripik pisang, sambal khas desa, hingga minuman herbal yang dapat dipasarkan ke luar daerah.</p></li></ol><p>Dengan berbagai potensi yang dimiliki, pertanian di Desa Darmasaba dapat terus berkembang melalui inovasi dan pemanfaatan teknologi pertanian modern. Dukungan dari masyarakat, pemerintah, dan lembaga terkait sangat penting untuk menjaga keberlanjutan sektor pertanian dan meningkatkan kesejahteraan petani di desa ini.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdykerrq0002vn6fy4sv7uvm",
|
||||
"name": "Kawasan Kuliner",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, Bali, memiliki potensi besar dalam sektor kuliner. Sebagai desa yang strategis dan terus berkembang, Darmasaba mulai dikenal sebagai destinasi kuliner yang menawarkan beragam makanan khas Bali hingga makanan modern yang menarik minat wisatawan dan masyarakat lokal.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, Bali, memiliki potensi besar dalam sektor kuliner. Sebagai desa yang strategis dan terus berkembang, Darmasaba mulai dikenal sebagai destinasi kuliner yang menawarkan beragam makanan khas Bali hingga makanan modern yang menarik minat wisatawan dan masyarakat lokal.</p><p>Potensi Desa melalui Kawasan Kuliner:</p><ol><li><p><strong>Ragam Kuliner Khas Bali</strong></p><p>Darmasaba memiliki banyak warung dan rumah makan yang menyajikan hidangan khas Bali yang otentik, seperti:</p><p>- Babi Guling : Salah satu kuliner favorit di Bali yang banyak ditemukan di Darmasaba.</p><p>- Ayam Betutu : Hidangan ayam berbumbu khas yang dimasak dengan teknik khas Bali.</p><p>- Lawar : Campuran daging dan sayuran berbumbu khas Bali.</p><p>- Sate Lilit : Sate khas Bali yang terbuat dari daging cincang yang dibalut pada batang serai.</p></li><li><p><strong>Wisata Kuliner Modern & Cafe Kekinian:</strong></p><p>Selain makanan tradisional, Darmasaba juga mulai berkembang dengan hadirnya cafe dan resto kekinian yang menyajikan menu modern seperti kopi spesial, burger, pizza, dan aneka dessert yang digemari anak muda. Keberadaan tempat-tempat ini menjadikan Darmasaba sebagai pilihan destinasi kuliner bagi wisatawan maupun warga sekitar.</p></li><li><p><strong>Pasar Kuliner Malam:</strong></p><p>Salah satu daya tarik Darmasaba adalah pusat kuliner malam yang menghadirkan aneka makanan kaki lima seperti nasi jinggo, tipat cantok, bakso, dan berbagai jajanan khas Bali. Suasana yang ramai dan harga yang terjangkau membuat pasar kuliner ini menjadi tempat favorit bagi masyarakat lokal.</p></li><li><p><strong>Potensi Ekonomi & UMKM Kuliner:</strong></p><p>Dengan berkembangnya sektor kuliner, banyak pelaku UMKM di Darmasaba mulai merintis usaha makanan, baik dalam bentuk warung makan, katering, hingga produksi makanan ringan seperti keripik, sambal, dan minuman tradisional. Potensi ini dapat terus dikembangkan dengan dukungan pemerintah desa dan promosi melalui media sosial.</p></li><li><p><strong>Kawasan Kuliner Berbasis Pariwisata:</strong></p><p>Untuk menarik lebih banyak pengunjung, Darmasaba berpotensi mengembangkan kawasan kuliner berbasis wisata yang menggabungkan pengalaman makan dengan konsep alam terbuka, pertunjukan seni, dan edukasi kuliner khas Bali. Hal ini dapat menjadi daya tarik tambahan bagi wisatawan yang ingin merasakan pengalaman kuliner yang lebih autentik.</p></li></ol><p>Dengan kekayaan kuliner yang dimiliki, Desa Darmasaba berpotensi menjadi kawasan kuliner unggulan di Kabupaten Badung. Dukungan dari masyarakat, pemerintah desa, serta promosi yang lebih luas dapat menjadikan Darmasaba sebagai destinasi kuliner yang semakin dikenal dan berkembang.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdykhfhf0005vn6fnz25kify",
|
||||
"name": "IKM Berbasis Pengolahan Pangan",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam Industri Kecil dan Menengah (IKM) berbasis pengolahan pangan. Dengan sumber daya alam yang melimpah dan warisan kuliner khas Bali, Darmasaba dapat mengembangkan sektor ini untuk meningkatkan kesejahteraan masyarakat dan menciptakan lapangan kerja baru.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam Industri Kecil dan Menengah (IKM) berbasis pengolahan pangan. Dengan sumber daya alam yang melimpah dan warisan kuliner khas Bali, Darmasaba dapat mengembangkan sektor ini untuk meningkatkan kesejahteraan masyarakat dan menciptakan lapangan kerja baru.</p><p>Potensi dan Peran IKM Berbasis Pengolahan Pangan:</p><ol><li><p><strong>Produk Unggulan Pengolahan Pangan</strong></p><p>Beberapa produk olahan pangan yang potensial dikembangkan di Darmasaba meliputi:</p><p>- Keripik dan Snack Tradisional : Seperti keripik pisang, keripik singkong, dan rengginang.</p><p>- Sambal Khas Bali : Seperti sambal matah dan sambal embe yang banyak diminati pasar lokal dan nasional.</p><p>- Minuman Herbal dan Jamu : Berbasis rempah seperti kunyit asam, beras kencur, dan wedang jahe.</p><p>- Olahan Makanan Berbasis Kelapa : Seperti virgin coconut oil (VCO), serundeng, dan gula aren.</p><p>- Kue Tradisional Bali : Seperti jaje laklak, jaje uli, dan klepon yang dapat dikemas secara modern.</p></li><li><p><strong>Peluang Ekonomi dan Pemberdayaan UMKM:</strong></p><p>IKM berbasis pengolahan pangan dapat membuka peluang bagi masyarakat, terutama ibu rumah tangga dan pemuda desa, untuk berwirausaha. Dengan dukungan modal dan pelatihan dari pemerintah desa atau BUMDes Pudak Mesari, usaha kecil ini dapat berkembang menjadi industri yang lebih besar.</p></li><li><p><strong>Digitalisasi dan Pemasaran Online:</strong></p><p>Darmasaba dapat mengembangkan kawasan sentra IKM sebagai pusat produksi, pelatihan, dan pemasaran produk olahan pangan. Dengan adanya fasilitas ini, para pelaku usaha dapat lebih mudah berkolaborasi, meningkatkan kualitas produk, serta mendapatkan akses ke permodalan dan distribusi yang lebih luas.</p></li><li><p><strong>Pengembangan Kawasan Sentra IKM:</strong></p><p>Dengan berkembangnya sektor kuliner, banyak pelaku UMKM di Darmasaba mulai merintis usaha makanan, baik dalam bentuk warung makan, katering, hingga produksi makanan ringan seperti keripik, sambal, dan minuman tradisional. Potensi ini dapat terus dikembangkan dengan dukungan pemerintah desa dan promosi melalui media sosial.</p></li><li><p><strong>Sinergi dengan Pariwisata dan Agrowisata:</strong></p><p>Dengan berkembangnya sektor wisata di Darmasaba, produk olahan pangan dapat dijadikan suvenir khas desa. Pengunjung dapat membeli oleh-oleh seperti sambal kemasan, jajanan khas, atau minuman herbal sebagai bagian dari pengalaman wisata mereka.</p></li></ol><p>IKM berbasis pengolahan pangan memiliki potensi besar untuk menjadi sektor unggulan di Desa Darmasaba. Dengan inovasi, dukungan teknologi, serta pemasaran yang baik, produk-produk lokal dapat bersaing di pasar yang lebih luas, meningkatkan kesejahteraan masyarakat, dan menjadikan Darmasaba sebagai pusat industri pangan kreatif di Kabupaten Badung.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdykjgmv0008vn6fwg0rr2nh",
|
||||
"name": "Genteng",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam industri genteng yang dikelola oleh Usaha Mikro, Kecil, dan Menengah (UMKM). Sebagai desa yang masih mempertahankan nilai-nilai tradisional dalam pembangunan, industri genteng di Darmasaba berperan penting dalam penyediaan bahan bangunan berkualitas bagi masyarakat lokal maupun luar daerah.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam industri genteng yang dikelola oleh Usaha Mikro, Kecil, dan Menengah (UMKM). Sebagai desa yang masih mempertahankan nilai-nilai tradisional dalam pembangunan, industri genteng di Darmasaba berperan penting dalam penyediaan bahan bangunan berkualitas bagi masyarakat lokal maupun luar daerah.</p><p>Potensi dan Peran UMKM Genteng:</p><ol><li><p><strong>Genteng Tradisional Berkualitas Tinggi</strong></p><p>UMKM di Darmasaba memproduksi genteng dari bahan baku pilihan seperti tanah liat berkualitas, yang menghasilkan genteng dengan daya tahan tinggi, kuat, dan cocok untuk iklim tropis. Beberapa jenis genteng yang dihasilkan meliputi:</p><p>- Genteng Tanah Liat : Kuat, tahan lama, dan memiliki estetika khas tradisional.</p><p>- Genteng Beton : Cocok untuk bangunan modern dengan ketahanan lebih tinggi.</p><p>- Genteng Keramik : Memberikan tampilan elegan dan daya serap air yang lebih rendah.</p></li><li><p><strong>Peluang Ekonomi dan Pemberdayaan Masyarakat:</strong></p><p>Industri genteng di Darmasaba memberikan peluang kerja bagi masyarakat setempat, terutama dalam bidang produksi, distribusi, hingga pemasaran. UMKM genteng juga mendukung keberlanjutan ekonomi desa dengan meningkatkan pendapatan warga serta mengurangi angka pengangguran.</p></li><li><p><strong>Inovasi dan Pengembangan Teknologi</strong></p><p>Beberapa pengrajin genteng di Darmasaba telah mulai mengadopsi teknologi modern dalam proses produksi, seperti:</p><p>- Penggunaan cetakan dan oven pembakaran efisien untuk meningkatkan kualitas dan kapasitas produksi.</p><p>- Teknik pelapisan anti bocor dan anti lumut untuk membuat genteng lebih tahan lama.</p><p>- Desain genteng inovatif yang lebih ringan dan mudah dipasang.</p></li><li><p><strong>Pemasaran dan Ekspansi Pasar</strong></p><p>Dengan meningkatnya pembangunan perumahan dan proyek konstruksi di Bali, permintaan akan genteng berkualitas terus bertambah. UMKM genteng Darmasaba dapat memperluas pasarnya dengan:</p><p>- Menjalin kerja sama dengan kontraktor dan pengembang properti.</p><p>- Mempromosikan produk melalui media sosial dan marketplace online.</p><p>- Menyediakan layanan custom sesuai kebutuhan pelanggan.</p></li><li><p><strong>Keberlanjutan dan Ramah Lingkungan:</strong></p><p>Industri genteng di Darmasaba berpotensi dikembangkan secara lebih ramah lingkungan dengan menerapkan metode produksi yang mengurangi limbah dan emisi. Pemanfaatan energi alternatif serta daur ulang bahan limbah dapat membantu menciptakan industri yang lebih berkelanjutan.</p></li></ol><p>UMKM genteng di Desa Darmasaba memiliki potensi besar untuk terus berkembang sebagai sektor industri unggulan. Dengan inovasi, pemasaran yang lebih luas, serta dukungan dari pemerintah dan masyarakat, industri ini dapat meningkatkan kesejahteraan warga dan memperkuat perekonomian desa.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdyklax3000bvn6fdu53f3xq",
|
||||
"name": "Peternakan Ikan Lele",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam sektor peternakan lele. Dengan kondisi lingkungan yang mendukung serta meningkatnya permintaan ikan lele di pasaran, budidaya ikan lele dapat menjadi salah satu sektor ekonomi unggulan yang mampu meningkatkan kesejahteraan masyarakat desa.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam sektor peternakan lele. Dengan kondisi lingkungan yang mendukung serta meningkatnya permintaan ikan lele di pasaran, budidaya ikan lele dapat menjadi salah satu sektor ekonomi unggulan yang mampu meningkatkan kesejahteraan masyarakat desa.</p><p>Potensi dan Peran Peternakan Ikan Lele:</p><ol><li><p><strong>Kondisi Lingkungan yang Mendukung</strong></p><p>Darmasaba memiliki sumber air yang cukup serta iklim yang cocok untuk budidaya ikan lele. Kolam-kolam budidaya dapat dibuat dengan berbagai sistem, seperti:</p><p>- Kolam Terpal : Mudah dibuat dan lebih efisien dalam perawatan.</p><p>- Kolam Beton : Lebih tahan lama dan cocok untuk produksi skala besar.</p><p>- Sistem Bioflok : Teknologi modern yang dapat meningkatkan kepadatan ikan dan mengurangi limbah.</p></li><li><p><strong>Permintaan Pasar yang Tinggi:</strong></p><p>Lele merupakan salah satu jenis ikan yang memiliki permintaan tinggi di Bali, baik untuk konsumsi rumah tangga, warung makan, hingga restoran. Produk olahan seperti lele goreng, pecel lele, dan abon lele semakin diminati, membuka peluang besar bagi peternak lele di Darmasaba.</p></li><li><p><strong>Inovasi dan Pengembangan Teknologi</strong></p><p>Beberapa pengrajin genteng di Darmasaba telah mulai mengadopsi teknologi modern dalam proses produksi, seperti:</p><p>- Penggunaan cetakan dan oven pembakaran efisien untuk meningkatkan kualitas dan kapasitas produksi.</p><p>- Teknik pelapisan anti bocor dan anti lumut untuk membuat genteng lebih tahan lama.</p><p>- Desain genteng inovatif yang lebih ringan dan mudah dipasang.</p></li><li><p><strong>Pemasaran dan Ekspansi Pasar</strong></p><p>Dengan meningkatnya pembangunan perumahan dan proyek konstruksi di Bali, permintaan akan genteng berkualitas terus bertambah. UMKM genteng Darmasaba dapat memperluas pasarnya dengan:</p><p>- Menjalin kerja sama dengan kontraktor dan pengembang properti.</p><p>- Mempromosikan produk melalui media sosial dan marketplace online.</p><p>- Menyediakan layanan custom sesuai kebutuhan pelanggan.</p></li><li><p><strong>Keberlanjutan dan Ramah Lingkungan:</strong></p><p>Industri genteng di Darmasaba berpotensi dikembangkan secara lebih ramah lingkungan dengan menerapkan metode produksi yang mengurangi limbah dan emisi. Pemanfaatan energi alternatif serta daur ulang bahan limbah dapat membantu menciptakan industri yang lebih berkelanjutan.</p></li></ol><p>UMKM genteng di Desa Darmasaba memiliki potensi besar untuk terus berkembang sebagai sektor industri unggulan. Dengan inovasi, pemasaran yang lebih luas, serta dukungan dari pemerintah dan masyarakat, industri ini dapat meningkatkan kesejahteraan warga dan memperkuat perekonomian desa.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdykpkwf000gvn6ftas2cjje",
|
||||
"name": "Jogging Track Tegeh Aban, Karang Gadon dan Munduk Uma Desa",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi wisata olahraga dan rekreasi melalui Jogging Track Tegeh Aban, Karang Gadon, dan Munduk Uma Desa. Jalur jogging ini tidak hanya menjadi fasilitas olahraga bagi warga, tetapi juga berpotensi dikembangkan sebagai destinasi wisata sehat berbasis alam yang menarik bagi wisatawan lokal maupun luar daerah.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi wisata olahraga dan rekreasi melalui Jogging Track Tegeh Aban, Karang Gadon, dan Munduk Uma Desa. Jalur jogging ini tidak hanya menjadi fasilitas olahraga bagi warga, tetapi juga berpotensi dikembangkan sebagai destinasi wisata sehat berbasis alam yang menarik bagi wisatawan lokal maupun luar daerah.</p><p>Potensi dan Peran Jogging Track Tegeh Aban, Karang Gadon dan Munduk Uma Desa:</p><ol><li><p><strong>Keindahan Alam dan Udara Segar:</strong></p><p>Jogging track yang membentang di Tegeh Aban, Karang Gadon, dan Munduk Uma Desa menawarkan pemandangan alam yang asri dengan udara segar khas pedesaan. Jalur ini melewati area persawahan hijau, perkebunan, serta hutan kecil yang memberikan pengalaman jogging yang lebih menyenangkan dan menenangkan.</p></li><li><p><strong>Fasilitas Olahraga dan Rekreasi</strong></p><p>Selain untuk jogging, jalur ini juga cocok digunakan untuk:</p><p>- Bersepeda santai : Jalur yang nyaman untuk pecinta sepeda.</p><p>- Trekking ringan : Cocok bagi wisatawan yang ingin menikmati suasana pedesaan.</p><p>- Meditasi dan Yoga : Area yang tenang dan alami, ideal untuk relaksasi.</p></li><li><p><strong>Destinasi Wisata Sehat dan Edukasi:</strong></p><p>Jogging track ini berpotensi dikembangkan sebagai wisata sehat berbasis alam, di mana pengunjung bisa menikmati udara segar sambil berolahraga. Selain itu, jalur ini dapat dijadikan sebagai rute edukasi lingkungan, mengenalkan keanekaragaman hayati, pertanian, serta kehidupan masyarakat desa.</p></li><li><p><strong>Potensi Ekonomi bagi Masyarakat</strong></p><p>Dengan meningkatnya jumlah pengunjung, masyarakat sekitar dapat memanfaatkan peluang usaha seperti:</p><p>- Warung sehat dan kuliner lokal : Menyediakan makanan dan minuman sehat bagi para pengunjung.</p><p>- Jasa penyewaan sepeda : Menarik bagi wisatawan yang ingin berkeliling lebih jauh.</p><p>- Pemandu wisata lokal : Memberikan pengalaman lebih bagi wisatawan yang ingin mengenal sejarah dan budaya desa.</p></li><li><p><strong>Pengembangan Berkelanjutan:</strong></p><p>Agar semakin menarik, jogging track ini bisa dilengkapi dengan fasilitas tambahan seperti tempat istirahat, spot foto alami, papan informasi tentang flora dan fauna, serta area taman bunga untuk mempercantik jalur jogging.</p></li></ol><p>Jogging Track Tegeh Aban, Karang Gadon, dan Munduk Uma Desa memiliki potensi besar sebagai destinasi wisata sehat dan olahraga berbasis alam. Dengan pengelolaan yang baik serta dukungan dari pemerintah desa dan masyarakat, jalur ini bisa menjadi ikon baru Desa Darmasaba yang menarik bagi wisatawan serta meningkatkan perekonomian warga setempat.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdykr76v000jvn6fqngibbmq",
|
||||
"name": "Dam Tanah Putih",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi wisata olahraga dan rekreasi melalui Jogging Track Tegeh Aban, Karang Gadon, dan Munduk Uma Desa. Jalur jogging ini tidak hanya menjadi fasilitas olahraga bagi warga, tetapi juga berpotensi dikembangkan sebagai destinasi wisata sehat berbasis alam yang menarik bagi wisatawan lokal maupun luar daerah.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki Dam Tanah Putih sebagai salah satu potensi desa yang bernilai strategis. Selain berfungsi sebagai infrastruktur pengairan, dam ini juga memiliki potensi untuk dikembangkan sebagai destinasi wisata alam, edukasi, dan rekreasi bagi masyarakat lokal maupun wisatawan.</p><p>Potensi dan Peran Dam Tanah Putih:</p><ol><li><p><strong>Fungsi Utama Sebagai Sumber Pengairan</strong></p><p>Dam Tanah Putih memiliki peran penting dalam sistem irigasi yang menopang sektor pertanian di Darmasaba. Air dari dam ini digunakan untuk:</p><p>- Mengairi sawah dan ladang : Menjamin ketersediaan air bagi petani sepanjang tahun.</p><p>- Menjaga keseimbangan ekosistem : Menjadi habitat bagi ikan air tawar dan berbagai biota air.</p><p>- Menampung air hujan : Membantu mengurangi risiko banjir dan kekeringan.</p></li><li><p><strong>Potensi Wisata Alam dan Rekreasi</strong></p><p>Dengan pemandangan alam yang asri dan suasana yang sejuk, Dam Tanah Putih memiliki potensi besar untuk dikembangkan sebagai tempat wisata alam. Beberapa kegiatan yang bisa dikembangkan di area ini antara lain:</p><p>- Trekking dan jogging di sekitar dam : Menikmati udara segar dan pemandangan indah.</p><p>- Berkemah dan piknik : Cocok untuk keluarga dan komunitas yang ingin menikmati alam.</p><p>- Wisata air : Seperti pemancingan atau wisata perahu kecil yang dapat menarik wisatawan.</p><p>- Spot fotografi alam : Keindahan dam dan sekitarnya menjadi latar yang menarik bagi para fotografer.</p></li><li><p><strong>Potensi Ekonomi dan UMKM Lokal</strong></p><p>Dengan pengembangan dam sebagai destinasi wisata, masyarakat sekitar dapat memanfaatkan peluang usaha seperti:</p><p>- Warung makan dan jajanan tradisional : Menyediakan makanan khas Bali bagi wisatawan.</p><p>- Jasa penyewaan alat rekreasi : Seperti pancing atau perahu kecil.</p><p>- Produk kerajinan tangan dan suvenir : Oleh-oleh khas Darmasaba yang menarik bagi pengunjung.</p></li><li><p><strong>Pengembangan Konservasi dan Edukasi Lingkungan</strong></p><p>Dam Tanah Putih juga bisa menjadi tempat edukasi lingkungan dengan konsep konservasi, di mana pengunjung bisa belajar tentang:</p><p>- Pengelolaan sumber daya air yang berkelanjutan.</p><p>- Keanekaragaman hayati di sekitar dam.</p><p>- Pentingnya ekosistem perairan bagi pertanian dan kehidupan masyarakat.</p></li></ol><p>Dengan berbagai fungsi dan keindahannya, Dam Tanah Putih memiliki potensi besar untuk dikembangkan sebagai destinasi wisata alam, rekreasi, serta edukasi lingkungan. Dengan pengelolaan yang baik dan dukungan dari masyarakat serta pemerintah desa, dam ini dapat menjadi aset penting bagi Darmasaba, baik dari sisi ekonomi maupun kelestarian lingkungan.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdyku9qh000mvn6ft76322sv",
|
||||
"name": "UMKM",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam sektor Usaha Mikro, Kecil, dan Menengah (UMKM). Keberadaan UMKM di desa ini tidak hanya menjadi motor penggerak ekonomi lokal, tetapi juga mendukung pelestarian budaya dan kearifan lokal melalui berbagai produk unggulan.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam sektor Usaha Mikro, Kecil, dan Menengah (UMKM). Keberadaan UMKM di desa ini tidak hanya menjadi motor penggerak ekonomi lokal, tetapi juga mendukung pelestarian budaya dan kearifan lokal melalui berbagai produk unggulan.</p><p>Potensi dan Peran UMKM:</p><ol><li><p><strong>Kerajinan Tangan dan Produk Lokal</strong></p><p>Darmasaba memiliki banyak pengrajin yang menghasilkan produk unik dengan nilai seni tinggi, seperti:</p><p>- Genteng dan bahan bangunan tradisional : Genteng khas Darmasaba yang berkualitas tinggi.</p><p>- Kerajinan anyaman dan ukiran : Produk berbasis rotan dan kayu yang banyak diminati pasar lokal dan internasional.</p><p>- Pakaian adat dan kain tradisional : Mendukung pelestarian budaya Bali.</p></li><li><p><strong>Industri Kuliner Khas Darmasaba</strong></p><p>Kuliner khas desa ini memiliki potensi besar untuk dikembangkan sebagai bisnis UMKM, seperti:</p><p>- Babi Guling : Salah satu kuliner favorit yang banyak diminati wisatawan.</p><p>- Jajanan tradisional Bali : Seperti laklak, jaja uli, dan klepon yang masih dibuat dengan cara tradisional.</p><p>- Olahan ikan lele : Seperti abon lele, lele asap, dan pecel lele yang memiliki pasar luas.</p></li><li><p><strong>UMKM Berbasis Pengolahan Pangan</strong></p><p>Beberapa UMKM di Darmasaba mengolah hasil pertanian dan peternakan menjadi produk bernilai tambah, seperti:</p><p>- Keripik singkong dan pisang : Camilan sehat berbasis bahan lokal.</p><p>- Olahan kelapa : Seperti minyak kelapa murni dan gula aren.</p><p>- Produk herbal dan jamu : Menggunakan bahan-bahan alami dari tanaman lokal.</p></li><li><p><strong>Dukungan dan Pengembangan UMKM</strong></p><p>Agar UMKM di Darmasaba semakin berkembang, perlu adanya:</p><p>- Pelatihan dan pendampingan usaha : Untuk meningkatkan kualitas produk dan manajemen usaha.</p><p>- Pemasaran digital : Menggunakan media sosial dan e-commerce untuk menjangkau pasar lebih luas.</p><p>- Kerja sama dengan BUMDes Pudak Mesari : Untuk membantu akses modal dan pengelolaan bisnis yang lebih profesional.</p></li></ol><p>UMKM di Desa Darmasaba memiliki potensi besar dalam berbagai sektor, mulai dari kerajinan tangan, kuliner, hingga wisata berbasis masyarakat. Dengan inovasi, pemasaran yang lebih luas, dan dukungan dari pemerintah desa serta masyarakat, UMKM Darmasaba dapat berkembang pesat dan menjadi tulang punggung perekonomian desa.</p>"
|
||||
}
|
||||
]
|
||||
22
prisma/data/landing-page/apbdes/apbdes.json
Normal file
22
prisma/data/landing-page/apbdes/apbdes.json
Normal file
@@ -0,0 +1,22 @@
|
||||
[
|
||||
{
|
||||
"id": "cmdwq7qp60008vntw67s4j6sq",
|
||||
"name": "Pembiayaan",
|
||||
"jumlah": "295 M"
|
||||
},
|
||||
{
|
||||
"id": "cmdwpsprc0003vntw9o4d33dr",
|
||||
"name": "Pendapatan",
|
||||
"jumlah": "495 M"
|
||||
},
|
||||
{
|
||||
"id": "cmdwqe8xl000cvntwcuqpvdhp",
|
||||
"name": "Belanja",
|
||||
"jumlah": "395 M"
|
||||
},
|
||||
{
|
||||
"id": "cmdwqq4b6000gvntwm07rinx4",
|
||||
"name": "Pangan",
|
||||
"jumlah": "285 M"
|
||||
}
|
||||
]
|
||||
110
prisma/data/landing-page/desa-anti-korupsi/desaantiKorpusi.json
Normal file
110
prisma/data/landing-page/desa-anti-korupsi/desaantiKorpusi.json
Normal file
@@ -0,0 +1,110 @@
|
||||
[
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"id": "cmdsa8q5q001dvnbemch8j24x",
|
||||
"name": "3.1 ADANYA LAYANAN PENGADUAN BAGI MASYARAKAT",
|
||||
"deskripsi": "<p>ADANYA LAYANAN PENGADUAN BAGI MASYARAKAT</p>",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"id": "cmdsabhif001pvnbepm06hry6",
|
||||
"name": "3.5 ADANYA MAKLUMAT PELAYANAN",
|
||||
"deskripsi": "<p>ADANYA MAKLUMAT PELAYANAN</p>",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
[
|
||||
{
|
||||
"id": "cmds9es2o000ivnbe1o0swrvh",
|
||||
"name": "PENGUATAN TATA LAKSANA"
|
||||
},
|
||||
{
|
||||
"id": "cmds9f2ua000jvnbeksu1sfwm",
|
||||
"name": "PENGUATAN PENGAWASAN"
|
||||
},
|
||||
{
|
||||
"id": "cmds9fr73000kvnbe6w281dcl",
|
||||
"name": "PENGUATAN KUALITAS PELAYANAN PUBLIK"
|
||||
},
|
||||
{
|
||||
"id": "cmds9g5ow000lvnbel3rkkwrv",
|
||||
"name": "PENGUATAN PARTISIPASI MASYARAKAT"
|
||||
},
|
||||
{
|
||||
"id": "cmds9govb000mvnbesq8b4y99",
|
||||
"name": "KEARIFAN LOKAL"
|
||||
}
|
||||
]
|
||||
26
prisma/data/landing-page/penghargaan/penghargaan.json
Normal file
26
prisma/data/landing-page/penghargaan/penghargaan.json
Normal file
@@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"id" : "cmdzdewrs0003vnp0yh1klh0m",
|
||||
"name" : "Penghargaan Bhawana Sewaka Nugraha kepada Perbekel Darmasaba",
|
||||
"juara" : "Penghargaan Bhawana Sewaka Nugraha kepada Perbekel Darmasaba",
|
||||
"deskripsi" : "<p>Pada hari Jumat, 27 Desember 2024, sebuah momen membanggakan tercipta bagi Desa Darmasaba. Perbekel Darmasaba, Ida Bagus Surya Prabhawa Manuaba, S.H., M.H., NL.P., menerima Penghargaan Bhawana Sewaka Nugraha sebagai Penggiat Lingkungan Hidup.<br><br>Penghargaan bergengsi ini diserahkan langsung oleh Bapak Irjen. Pol. (Purn.) Drs. Sang Made Mahendra Jaya, M.H., selaku Pejabat Gubernur Provinsi Bali, dalam sebuah acara resmi yang berlangsung di Gedung Jaya Sabha, Denpasar.<br><br>Penghargaan ini merupakan pengakuan atas dedikasi, kerja keras, dan komitmen Perbekel Darmasaba dalam menjaga kelestarian lingkungan serta menginspirasi masyarakat untuk menciptakan desa yang lebih hijau, bersih, dan berkelanjutan.<br><br>Semoga prestasi ini tidak hanya menjadi kebanggaan bagi Desa Darmasaba, tetapi juga memotivasi kita semua untuk terus menjaga lingkungan demi masa depan yang lebih baik. Mari bersama-sama wujudkan Bali yang lestari dan berkelanjutan!</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdzdlwqe0006vnp0lsrp5ybn",
|
||||
"name" : "DESA DARMASABA KEMBALI MERAIH PERINGKAT V DALAM AJANG MANGUPURA AWARD",
|
||||
"juara" : "5",
|
||||
"deskripsi" : "<p>Bangga, Darmasaba Berprestasi!</p><p><br>Pada hari Senin, 18 November 2024, Desa Darmasaba kembali mencetak prestasi gemilang dengan meraih peringkat V dalam ajang bergengsi Mangupura Award. Penganugerahan ini berlangsung dalam rangkaian acara HUT Kota Mangupura ke-15 yang dihadiri oleh Drs. I Ketut Suiasa, S.H., Plt Bupati Badung, serta para tokoh penting lainnya.</p><p><br>Penghargaan ini merupakan buah kerja keras seluruh elemen masyarakat Darmasaba yang telah berpartisipasi aktif dalam memajukan desa. Proses menuju penghargaan ini melalui tahapan yang menantang, yaitu:<br>- Presentasi Desa pada tanggal 19 Juli 2024<br>- Verifikasi Faktual Lapangan oleh Tim Juri Mangupura Award pada tanggal 5 September 2024</p><p><br>✨ Penghargaan ini bukan hanya sebuah pengakuan, tetapi juga menjadi motivasi bagi Desa Darmasaba untuk terus meningkatkan inovasi, pelayanan, pembangunan serta transformasi digital.<br>Mari bersama kita lanjutkan semangat kolaborasi dan inovasi untuk menjadikan Darmasaba sebagai desa yang semakin unggul dan inspiratif!</p><p><br>#DesaDarmasaba<br>#MangupuraAward2024<br>#BanggaDarmasaba<br>#InovasiDesa<br>#HUTKotaMangupura15<br>#KemajuanDesa<br>#PemdesDarmasaba<br>#PerbekelDarmasaba<br>#DarmasabaBisa<br>#DarmasabaJuara<br> <br>@surya_prabhawa <br>@kecamatanabiansemal <br>@dpmdbadungkab <br>@pemkabbadung<br>@prokompimbadung <br>@seputar_darmasaba</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdzdorcb000avnp0ldlsx73f",
|
||||
"name" : "JEGEG DARMASABA MERAIH JUARA 2 DUTA INVESTASI BADUNG 2024",
|
||||
"juara" : "2",
|
||||
"deskripsi" : "<p>JUARA 2</p><p>DUTA INVESTASI KAB. BADUNG</p><p>TAHUN 2024</p><p>Selamat kepada Ni Made Amelia Prasetya Putri (Jegeg Darmasaba Th 2023) telah berhasil mengharumkan nama Desa Darmasaba dengan meraih Juara 2 Duta Invenstasi Kab. Badung dalam ajang Badung Investment Week 2024. Setelah bersaing dengan 40an peserta lainnya dan support penuh dari Pemdes Darmasaba, usaha memang tidak pernah menghianati hasil.</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdzdq8ns000dvnp0v917pevj",
|
||||
"name" : "DARMASABA BORONG JUARA BADUNG INVESTMENT AWARD 2024",
|
||||
"juara" : "DARMASABA BORONG JUARA BADUNG INVESTMENT AWARD 2024",
|
||||
"deskripsi" : "<p>DARMASABA MEMBORONG JUARA</p><p>Darmasaba bisa, Darmasaba juara,</p><p>Pemdes Darmasaba tak henti-henti menorehkan prestasi nie Semeton Darmasaba!</p><p>Pemdes Darmasaba berpartisipasi aktif dalam kegiatan Badung Invesment Week Tahun 2024 yang diselenggarakan oleh Dinas Penanaman Modal dan Pelayanan Terpadu Satu Pintu Kab. Badung. Pemdes Darmasaba melalui talenta-talenta terbaiknya mampu meraih prestasi dalam ajang tersebut diantaranya:</p><p>1. Juara 2 Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024.</p><p>2. Juara 2 Duta Invenstasi Kabupaten Badung 2024.</p><p>3. Juara Favorit Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024.</p><p>Penyerahan penghargaan lomba-lomba tersebut diserahkan dalam acara Badung Invesment Award 2024 pada hari Jumat (18/10/2024) bertempat di Balai Budaya Giri Nata Mandala Puspem Kab. Badung. Penghargaan Juara Lomba diserahkan langsung oleh Plt. Bupati Badung Drs. I Ketut Suiasa, S.H. didampingi Kadis DPMPTSP Kab. Badung Dr. Ir. I Made Agus Aryawan ST., MT.</p>"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": "cmdwrolsl0000vnd3e24q5440",
|
||||
"name": "Olahraga dan Kepemudaan"
|
||||
},
|
||||
{
|
||||
"id": "cmdwrot900001vnd30b5kj96g",
|
||||
"name": "Hukum dan Kesadaran Masyarakat"
|
||||
},
|
||||
{
|
||||
"id": "cmdwrp0pr0002vnd35w6nkjh0",
|
||||
"name": "Tata Kelola dan Inovasi Desa"
|
||||
}
|
||||
]
|
||||
20
prisma/data/landing-page/prestasi-desa/prestasi-desa.json
Normal file
20
prisma/data/landing-page/prestasi-desa/prestasi-desa.json
Normal file
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"id": "cmdwrrxkh0005vnd3p5rxkiev",
|
||||
"name": "Tim Bola Voli Putri Dharma Temaja meraih juara 3 dalam Turnamen Bola Voli Mangupura Cup 2024 kategori Putri Se-Bali",
|
||||
"deskripsi": "<p>Tim Bola Voli Putri Dharma Temaja meraih juara 3 dalam Turnamen Bola Voli Mangupura Cup 2024 kategori Putri Se-Bali</p>",
|
||||
"kategoriId": "cmdwrolsl0000vnd3e24q5440"
|
||||
},
|
||||
{
|
||||
"id": "cmdwrzs740008vnd329ysez5x",
|
||||
"name": "Prestasi Juara 3 dalam Lomba Keluarga Sadar Hukum Kabupaten Badung Tahun 2024",
|
||||
"deskripsi": "<p>Prestasi Juara 3 dalam Lomba Keluarga Sadar Hukum Kabupaten Badung Tahun 2024</p>",
|
||||
"kategoriId": "cmdwrot900001vnd30b5kj96g"
|
||||
},
|
||||
{
|
||||
"id": "cmdws0sgq000bvnd32o7m94im",
|
||||
"name": "Peringkat 5 Dalam Ajang Bergengsi Mangupura Award",
|
||||
"deskripsi": "<p>Peringkat 5 Dalam Ajang Bergengsi Mangupura Award</p>",
|
||||
"kategoriId": "cmdwrp0pr0002vnd35w6nkjh0"
|
||||
}
|
||||
]
|
||||
@@ -1,8 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"name": "I.B Surya Prabhawa Manuaba, S.H., M.H.",
|
||||
"position": "Perbekel Darmasaba periode 2021-2027"
|
||||
}
|
||||
]
|
||||
|
||||
32
prisma/data/landing-page/profile/mediaSosial.json
Normal file
32
prisma/data/landing-page/profile/mediaSosial.json
Normal file
@@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"id": "cmds8w2q60002vnbe6i8qhkuo",
|
||||
"name": "Telephone Desa Darmasaba",
|
||||
"iconUrl": "081239580000"
|
||||
},
|
||||
{
|
||||
"id": "cmds8z7u20005vnbegyyvnbk0",
|
||||
"name": "Email Desa Darmasaba",
|
||||
"iconUrl": "desadarmasaba@badungkab.go.id"
|
||||
},
|
||||
{
|
||||
"id": "cmds9023u0008vnbe3oxmhwyf",
|
||||
"name": "Desa Darmasaba",
|
||||
"iconUrl": "https://www.youtube.com/channel/UCtPw9WOQO7d2HIKzKgel4Xg"
|
||||
},
|
||||
{
|
||||
"id": "cmds90oul000bvnbe2bqkptoi",
|
||||
"name": "Pemerintah Desa Darmasaba",
|
||||
"iconUrl": "https://www.facebook.com/DarmasabaDesaku"
|
||||
},
|
||||
{
|
||||
"id": "cmds91i4e000evnbe8gtf1gub",
|
||||
"name": "ddarmasaba",
|
||||
"iconUrl": "https://www.instagram.com/ddarmasaba/"
|
||||
},
|
||||
{
|
||||
"id": "cmds92de5000hvnbemlu6sq5x",
|
||||
"name": "desa.darmasaba",
|
||||
"iconUrl": "https://www.tiktok.com/@desa.darmasaba?is_from_webapp=1&sender_device=pc"
|
||||
}
|
||||
]
|
||||
7
prisma/data/landing-page/profile/profile.json
Normal file
7
prisma/data/landing-page/profile/profile.json
Normal file
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"name": "I.B Surya Prabhawa Manuaba, S.H., M.H.",
|
||||
"position": "Perbekel Darmasaba periode 2021-2027"
|
||||
}
|
||||
]
|
||||
50
prisma/data/landing-page/profile/programInovasi.json
Normal file
50
prisma/data/landing-page/profile/programInovasi.json
Normal file
@@ -0,0 +1,50 @@
|
||||
[
|
||||
{
|
||||
"id": "cmdr7039z0002vn5rttctt9hn",
|
||||
"name": "Davest",
|
||||
"description": "Darmasaba Village Festval",
|
||||
"link": "https://darmasaba.desa.id/berita/55862-rakor-davest-2024"
|
||||
},
|
||||
{
|
||||
"id": "cmdr755pf0005vn5rp8tyuubw",
|
||||
"name": "Dmangan",
|
||||
"description": "Darmasaba Aman Pangan",
|
||||
"link": "https://darmasaba.desa.id/berita/61452-kader-d-mangan-berhasil-meraih-prestasi-dalam-ajang-lomba-banjar-bali-quis-bbq-tahun-2024"
|
||||
},
|
||||
{
|
||||
"id": "cmdr76nqk0008vn5rdddvcxnr",
|
||||
"name": "Bicara Darmasaba",
|
||||
"description": "Bicara Darmasaba",
|
||||
"link": "https://darmasaba.desa.id/berita/42506-bicara-darmasaba"
|
||||
},
|
||||
{
|
||||
"id": "cmdr77vbw000bvn5rvpmoq31s",
|
||||
"name": "Bares",
|
||||
"description": "Darmasaba Recycling Stock/Exchange",
|
||||
"link": "http://darmasaba.desa.id/berita/56722-bares"
|
||||
},
|
||||
{
|
||||
"id": "cmdr7bxtp000evn5rmy85wihx",
|
||||
"name": "Sajjana Dharma Raksaka",
|
||||
"description": "Sajjana Dharma Raksaka",
|
||||
"link": "https://ppid.badungkab.go.id/storage/dokumen/5RS9dldGkrgzMQq6bKdZsqsVRHI8gffWv4PGfb3r.pdf"
|
||||
},
|
||||
{
|
||||
"id": "cmdr7dlnk000hvn5r9lur3z35",
|
||||
"name": "PDKT",
|
||||
"description": "Perangkat Desa Kuat Teknologi",
|
||||
"link": "https://darmasaba.desa.id/berita/53752-p-d-k-t"
|
||||
},
|
||||
{
|
||||
"id": "cmdr7ftob000mvn5rfhgdtg8v",
|
||||
"name": "GM",
|
||||
"description": "Galah Melah",
|
||||
"link": "https://darmasaba.desa.id/berita/52880-galah-melah"
|
||||
},
|
||||
{
|
||||
"id": "cmdr7glue000pvn5r6onzslju",
|
||||
"name": "Inovasi Desa Darmasaba",
|
||||
"description": "Inovasi Desa Darmasaba",
|
||||
"link": "https://darmasaba.desa.id/produk-lokal-desa"
|
||||
}
|
||||
]
|
||||
114
prisma/data/landing-page/sdgs-desa/sdgs-desa.json
Normal file
114
prisma/data/landing-page/sdgs-desa/sdgs-desa.json
Normal file
@@ -0,0 +1,114 @@
|
||||
[
|
||||
{
|
||||
"id": "cmdsjzdl30002vneknuvo4irv",
|
||||
"name": "Desa Tanpa Kemiskinan",
|
||||
"jumlah": "52.62",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdskargd0005vnek0mu2ofk9",
|
||||
"name": "Desa Tanpa Kelaparan",
|
||||
"jumlah": "35.75",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdskbvl0008vnek5dmieatb",
|
||||
"name": "Desa Sehat Dan Sejahtera",
|
||||
"jumlah": "77.37",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdskcx91000bvneko7tuaoqa",
|
||||
"name": "Pendidikan Desa Berkualitas",
|
||||
"jumlah": "34.11",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdskjare000evnek1hglu0x8",
|
||||
"name": "Keterlibatan Perempuan Desa",
|
||||
"jumlah": "45.70",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdskqcpc0002vnvnqjkqgm92",
|
||||
"name": "Desa Layak Air Bersih Dan Sanitasi",
|
||||
"jumlah": "48.54",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdsktl3x0005vnvne15seefw",
|
||||
"name": "Desa Berenergi Bersih Dan Terbarukan",
|
||||
"jumlah": "99.64",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdskuncw0008vnvcsdqoeog",
|
||||
"name": "Pertumbuhan Ekonomi Desa Merata",
|
||||
"jumlah": "40.92",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdskw83j000bvvn9szqrea6",
|
||||
"name": "Infrastruktur Dan Inovasi Desa Sesuai Kebutuhan",
|
||||
"jumlah": "35.37",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdskwrq7000envnvy0c5nbgf",
|
||||
"name": "Desa Tanpa Kesenjangan",
|
||||
"jumlah": "35.47",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdskxivx000hnvnvsx520gv1",
|
||||
"name": "Kawasan Pemukiman Desa Aman Dan Nyaman",
|
||||
"jumlah": "40.35",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdskzg4c000kvnnkiv61gkt",
|
||||
"name": "Konsumsi Dan Produksi Desa Sadar Lingkungan",
|
||||
"jumlah": "16.67",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdsl07lk000nvnnvnrepsdy5m",
|
||||
"name": "Desa Tanggap Perubahan Iklim",
|
||||
"jumlah": "0.00",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdsl10rq000qvnvnlch9c1yv",
|
||||
"name": "Desa Peduli Lingkungan Laut",
|
||||
"jumlah": "50.00",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdsl1mc2000tvnvn357n8usi",
|
||||
"name": "Desa Peduli Lingkungan Darat",
|
||||
"jumlah": "0.00",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdsl2bx3000wvnvntshi4gnj",
|
||||
"name": "Desa Damai Berkeadilan",
|
||||
"jumlah": "78.65",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdsl2yz3000zvnvnmf60ok7q",
|
||||
"name": "Kemitraan Untuk Pembangunan Desa",
|
||||
"jumlah": "20.00",
|
||||
"imageId": ""
|
||||
},
|
||||
{
|
||||
"id": "cmdsl492h0012vnvnmckm3n2x",
|
||||
"name": "Kelembagaan Desa Dinamis Dan Budaya Desa Adaptif",
|
||||
"jumlah": "47.22",
|
||||
"imageId": ""
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"judul": "Fasilitas yang Disediakan",
|
||||
"deskripsi": "<ul><li><p>Buku-buku pelajaran dan alat tulis</p></li><li><p>Ruang belajar nyaman dan kondusif</p></li><li><p>Modul latihan dan pendampingan tugas</p></li><li><p>Minuman ringan dan dukungan motivasi belajar</p></li></ul>"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"judul": "Lokasi dan Jadwal",
|
||||
"deskripsi": "<ul><li><p>Lokasi: Balai Banjar / Balai Desa Darmasaba / Perpustakaan Desa</p></li><li><p>Jadwal: Setiap hari Senin, Rabu, dan Jumat pukul 16.00–18.00 WITA</p></li><li><p>Peserta: Terbuka untuk semua siswa SD–SMP di wilayah desa</p></li></ul>"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"judul": "Tujuan Program",
|
||||
"deskripsi": "<ul><li><p>Memberikan pendampingan belajar secara gratis bagi siswa SD hingga SMP</p></li><li><p>Membantu siswa dalam menghadapi ujian dan menyelesaikan tugas sekolah</p></li><li><p>Menumbuhkan kepercayaan diri dan kemandirian dalam belajar</p></li><li><p>Meningkatkan kesetaraan pendidikan untuk seluruh anak desa</p></li></ul>"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"judul": "Tempat Kegiatan",
|
||||
"deskripsi": "<p>Program Pendidikan Non Formal yang diselenggarakan di Desa Darmasaba meliputi:</p><p>1) Keaksaraan Fungsional</p><ul><li><p>Untuk warga yang belum bisa membaca dan menulis</p></li></ul><p>2) Pendidikan Kesetaraan (Paket A, B, C)</p><ul><li><p>Setara SD, SMP, dan SMA bagi yang tidak menyelesaikan pendidikan formal</p></li></ul><p>3) Pelatihan Keterampilan</p><ul><li><p>Menjahit, memasak, sablon, pertanian, peternakan, hingga teknologi digital</p></li></ul><p>4) Kursus & Pelatihan Soft Skill</p><ul><li><p>Public speaking, pengelolaan keuangan, kepemimpinan pemuda</p></li></ul><p>5) Pendidikan Keluarga & Parenting</p><ul><li><p>Untuk membekali orang tua dalam mendampingi tumbuh kembang anak</p></li></ul>"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"judul": "Tempat Kegiatan",
|
||||
"deskripsi": "<ul><li><p>Balai Desa Darmasaba</p></li><li><p>TPK, Perpustakaan Desa, atau Posyandu</p></li><li><p>Bisa juga dilakukan secara mobile atau door to door</p></li></ul>"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"judul": "Tujuan Program",
|
||||
"deskripsi": "<ul><li><p>Memberikan kesempatan belajar yang fleksibel bagi warga desa</p></li><li><p>Meningkatkan keterampilan hidup dan kemandirian ekonomi</p></li><li><p>Mendorong partisipasi masyarakat dalam pembangunan desa</p></li><li><p>Mengurangi angka putus sekolah dan meningkatkan kualitas SDM</p></li></ul>"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"judul": "Program Unggulan",
|
||||
"deskripsi": "<ul><li><p>Bimbingan Belajar Gratis: Untuk siswa kurang mampu</p></li><li><p>Gerakan Literasi Desa: Meningkatkan minat baca sejak dini</p></li><li><p>Pelatihan Digital untuk Anak dan Remaja</p></li><li><p>Beasiswa Anak Berprestasi & Kurang Mampu</p></li></ul>"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"judul": "Tujuan Program",
|
||||
"deskripsi": "<ul><li><p>Meningkatkan akses pendidikan yang merata dan berkualitas</p></li><li><p>Menumbuhkan semangat belajar sejak dini</p></li><li><p>Membentuk karakter anak yang berakhlak dan berwawasan lingkungan</p></li><li><p>Mendukung tumbuh kembang anak melalui pendekatan pendidikan yang holistik</p></li></ul>"
|
||||
}
|
||||
]
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
10
prisma/data/ppid/ikm/jenis-kelamin/jenis-kelamin.json
Normal file
10
prisma/data/ppid/ikm/jenis-kelamin/jenis-kelamin.json
Normal file
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"id": "cme8bt5o5000007lb9xp11unb",
|
||||
"name": "Laki-laki"
|
||||
},
|
||||
{
|
||||
"id": "cme8btctl000107lbh2hocgg8",
|
||||
"name": "Perempuan"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
[
|
||||
{
|
||||
"id": "cme8buup6000207lb54q9b0az",
|
||||
"name": "Sangat Baik"
|
||||
},
|
||||
{
|
||||
"id": "cme8bv15o000307lbft9b0vzy",
|
||||
"name": "Baik"
|
||||
},
|
||||
{
|
||||
"id": "cme8bvjvu000507lbgfsveog6",
|
||||
"name": "Kurang Baik"
|
||||
},
|
||||
{
|
||||
"id": "cme8bvvm6000607lbh6rn2ubm",
|
||||
"name": "Sangat Kurang Baik"
|
||||
}
|
||||
]
|
||||
14
prisma/data/ppid/ikm/umur-responden/umur-responden.json
Normal file
14
prisma/data/ppid/ikm/umur-responden/umur-responden.json
Normal file
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": "cme8bwgwu000707lbawc6fz3a",
|
||||
"name": "Muda"
|
||||
},
|
||||
{
|
||||
"id": "cme8hnx09000b07jl3ipifb1k",
|
||||
"name": "Dewasa"
|
||||
},
|
||||
{
|
||||
"id": "cme8ho7dv000c07jlc7lr4b4w",
|
||||
"name": "Lansia"
|
||||
}
|
||||
]
|
||||
91
prisma/data/ppid/struktur-ppid/pegawai-PPID.json
Normal file
91
prisma/data/ppid/struktur-ppid/pegawai-PPID.json
Normal file
@@ -0,0 +1,91 @@
|
||||
[
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"namaLengkap": "Ida Bagus Surya Prabhawa Manuaba, S.H.,M.H., NL.P.",
|
||||
"gelarAkademik": "S.H.,M.H.,NL.P.",
|
||||
"tanggalMasuk": "2020-01-01T00:00:00.000Z",
|
||||
"email": "bagus@desa.id",
|
||||
"telepon": "081234567891",
|
||||
"alamat": "Jl. Raya Desa No. 1",
|
||||
"posisiId": "kepala_desa",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"namaLengkap": "I Ketut Suwanta",
|
||||
"gelarAkademik": "S.Pt",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "suwanta@desa.id",
|
||||
"telepon": "081234567892",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "sekretaris_desa",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440006",
|
||||
"namaLengkap": "Ni Wayan Supardiati",
|
||||
"gelarAkademik": "S.Pd",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "supardiati@desa.id",
|
||||
"telepon": "081234567892",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kaur_keuangan",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440011",
|
||||
"namaLengkap": "I Wayan Agus Juni Artha Saputra",
|
||||
"gelarAkademik": "S.T.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "agus@desa.id",
|
||||
"telepon": "081234567892",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kadus_banjar_dinas_menesa",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440012",
|
||||
"namaLengkap": "I Wayan Sueca",
|
||||
"gelarAkademik": "S.H.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "sueca@desa.id",
|
||||
"telepon": "081234567893",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kadus_banjar_dinas_darmasaba",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440017",
|
||||
"namaLengkap": "Si Gede Ketut Astawa",
|
||||
"gelarAkademik": "S.T.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "astawa@desa.id",
|
||||
"telepon": "081234567893",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kadus_banjar_dinas_bucu",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440018",
|
||||
"namaLengkap": "I Kadek Arya Minarta",
|
||||
"gelarAkademik": "S.T.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "minarta@desa.id",
|
||||
"telepon": "081234567893",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kadus_banjar_dinas_gulingan",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440021",
|
||||
"namaLengkap": "I Gede Andika Pradnya Diputra",
|
||||
"gelarAkademik": "S.E.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "diputra@desa.id",
|
||||
"telepon": "081234567893",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kadus_banjar_dinas_taman",
|
||||
"isActive": true
|
||||
}
|
||||
|
||||
]
|
||||
158
prisma/data/ppid/struktur-ppid/posisi-organisasi-PPID.json
Normal file
158
prisma/data/ppid/struktur-ppid/posisi-organisasi-PPID.json
Normal file
@@ -0,0 +1,158 @@
|
||||
[
|
||||
[
|
||||
{
|
||||
"id": "kepala_desa",
|
||||
"nama": "Kepala Desa",
|
||||
"deskripsi": "Pemimpin desa Darmasaba",
|
||||
"hierarki": 1,
|
||||
"parentId": null
|
||||
},
|
||||
{
|
||||
"id": "kepala_urusan",
|
||||
"nama": "Kepala Urusan",
|
||||
"deskripsi": "Pemimpin urusan desa Darmasaba",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "sekretaris_desa",
|
||||
"nama": "Sekretaris Desa",
|
||||
"deskripsi": "Pengelola administrasi desa",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "kaur_keuangan",
|
||||
"nama": "Kaur Keuangan",
|
||||
"deskripsi": "Pengelola keuangan desa",
|
||||
"hierarki": 3,
|
||||
"parentId": "kaur_umum"
|
||||
},
|
||||
{
|
||||
"id": "kaur_perencanaan",
|
||||
"nama": "Kaur Perencanaan",
|
||||
"deskripsi": "Penyusun program kerja desa",
|
||||
"hierarki": 3,
|
||||
"parentId": "kaur_umum"
|
||||
},
|
||||
{
|
||||
"id": "kaur_umum",
|
||||
"nama": "Kaur Umum & TU",
|
||||
"deskripsi": "Pelayanan umum dan administrasi",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "kasi_pemerintahan",
|
||||
"nama": "Kasi Pemerintahan",
|
||||
"deskripsi": "Urusan pemerintahan dan keamanan",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "kasi_pelayanan",
|
||||
"nama": "Kasi Pelayanan",
|
||||
"deskripsi": "Urusan pelayanan masyarakat",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "kasi_kesejahteraan",
|
||||
"nama": "Kasi Kesejahteraan",
|
||||
"deskripsi": "Urusan sosial dan kesejahteraan",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_cabe",
|
||||
"nama": "Kepala Dusun Banjar Dinas Cabe",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Cabe",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_menesa",
|
||||
"nama": "Kepala Dusun Banjar Dinas Menesa",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Menesa",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_penenjoan",
|
||||
"nama": "Kepala Dusun Banjar Dinas Penenjoan",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Penenjoan",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_telanga",
|
||||
"nama": "Kepala Dusun Banjar Dinas Telanga",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Telanga",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_tengah",
|
||||
"nama": "Kepala Dusun Banjar Dinas Tengah",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Tengah",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_baler_pasar",
|
||||
"nama": "Kepala Dusun Banjar Dinas Baler Pasar",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Baler Pasar",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_bucu",
|
||||
"nama": "Kepala Dusun Banjar Dinas Bucu",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Bucu",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_gulingan",
|
||||
"nama": "Kepala Dusun Banjar Dinas Gulingan",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Gulingan",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_bersih",
|
||||
"nama": "Kepala Dusun Banjar Dinas Bersih",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Bersih",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_umahanyar",
|
||||
"nama": "Kepala Dusun Banjar Dinas Umahanyar",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Umahanyar",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_taman",
|
||||
"nama": "Kepala Dusun Banjar Dinas Taman",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Taman",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_darmasaba",
|
||||
"nama": "Kepala Dusun Banjar Dinas Darmasaba",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Darmasaba",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "staf_desa",
|
||||
"nama": "Staf Desa",
|
||||
"deskripsi": "Staf Desa",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id" : "1",
|
||||
"name" : "Struktur PPID"
|
||||
}
|
||||
]
|
||||
29
prisma/data/user/roles.json
Normal file
29
prisma/data/user/roles.json
Normal file
@@ -0,0 +1,29 @@
|
||||
[
|
||||
{
|
||||
"id": "role_admin_desa",
|
||||
"name": "ADMIN_DESA",
|
||||
"description": "Administrator Desa",
|
||||
"permissions": ["manage_users", "manage_content", "view_reports"],
|
||||
"isActive": true,
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"id": "role_admin_kesehatan",
|
||||
"name": "ADMIN_KESEHATAN",
|
||||
"description": "Administrator Bidang Kesehatan",
|
||||
"permissions": ["manage_health_data", "view_reports"],
|
||||
"isActive": true,
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"id": "role_admin_sekolah",
|
||||
"name": "ADMIN_SEKOLAH",
|
||||
"description": "Administrator Sekolah",
|
||||
"permissions": ["manage_school_data", "view_reports"],
|
||||
"isActive": true,
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
}
|
||||
]
|
||||
35
prisma/data/user/users.json
Normal file
35
prisma/data/user/users.json
Normal file
@@ -0,0 +1,35 @@
|
||||
[
|
||||
{
|
||||
"id": "user_admin_desa",
|
||||
"nama": "Admin Desa",
|
||||
"email": "admin.desa@example.com",
|
||||
"password": "$2b$10$XFDWYOJFxQ7ZM5bA0N4Z0O8u0eKYv58wLsaR7h6XK9bqWJ1YQJQ9q",
|
||||
"roleId": "role_admin_desa",
|
||||
"isActive": true,
|
||||
"lastLogin": "2025-08-31T10:00:00.000Z",
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"id": "user_admin_puskesmas",
|
||||
"nama": "Admin Kesehatan",
|
||||
"email": "admin.kesehatan@example.com",
|
||||
"password": "$2b$10$XFDWYOJFxQ7ZM5bA0N4Z0O8u0eKYv58wLsaR7h6XK9bqWJ1YQJQ9q",
|
||||
"roleId": "role_admin_kesehatan",
|
||||
"isActive": true,
|
||||
"lastLogin": null,
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"id": "user_admin_sekolah",
|
||||
"nama": "Admin Sekolah",
|
||||
"email": "admin.sekolah@example.com",
|
||||
"password": "$2b$10$XFDWYOJFxQ7ZM5bA0N4Z0O8u0eKYv58wLsaR7h6XK9bqWJ1YQJQ9q",
|
||||
"roleId": "role_admin_sekolah",
|
||||
"isActive": true,
|
||||
"lastLogin": null,
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
}
|
||||
]
|
||||
@@ -50,48 +50,57 @@ model AppMenuChild {
|
||||
// ========================================= FILE STORAGE ========================================= //
|
||||
|
||||
model FileStorage {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
realName String
|
||||
path String
|
||||
mimeType String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
isActive Boolean @default(true)
|
||||
link String
|
||||
category String // "image" / "document" / "other"
|
||||
Berita Berita[]
|
||||
PotensiDesa PotensiDesa[]
|
||||
Posyandu Posyandu[]
|
||||
StrukturPPID StrukturPPID[]
|
||||
GalleryFoto GalleryFoto[]
|
||||
PelayananSuratKeterangan PelayananSuratKeterangan[]
|
||||
Penghargaan Penghargaan[]
|
||||
ProfileDesaImage ProfileDesaImage[]
|
||||
ProfilePPID ProfilePPID[]
|
||||
ProfilPerbekel ProfilPerbekel[]
|
||||
Puskesmas Puskesmas[]
|
||||
ProgramKesehatan ProgramKesehatan[]
|
||||
PenangananDarurat PenangananDarurat[]
|
||||
KontakDarurat KontakDarurat[]
|
||||
InfoWabahPenyakit InfoWabahPenyakit[]
|
||||
KeamananLingkungan KeamananLingkungan[]
|
||||
MenuTipsKeamanan MenuTipsKeamanan[]
|
||||
Pelapor Pelapor[]
|
||||
PasarDesa PasarDesa[]
|
||||
KontakDaruratKeamanan KontakDaruratKeamanan[]
|
||||
KontakItem KontakItem[]
|
||||
Pegawai Pegawai[]
|
||||
DesaDigital DesaDigital[]
|
||||
KolaborasiInovasi KolaborasiInovasi[]
|
||||
InfoTekno InfoTekno[]
|
||||
PengaduanMasyarakat PengaduanMasyarakat[]
|
||||
KegiatanDesa KegiatanDesa[]
|
||||
ProgramInovasi ProgramInovasi[]
|
||||
PejabatDesa PejabatDesa[]
|
||||
MediaSosial MediaSosial[]
|
||||
DesaAntiKorupsi DesaAntiKorupsi[]
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
realName String
|
||||
path String
|
||||
mimeType String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
isActive Boolean @default(true)
|
||||
link String
|
||||
category String // "image" / "document" / "other"
|
||||
Berita Berita[]
|
||||
PotensiDesa PotensiDesa[]
|
||||
Posyandu Posyandu[]
|
||||
StrukturPPID StrukturPPID[]
|
||||
GalleryFoto GalleryFoto[]
|
||||
Pelapor Pelapor[]
|
||||
Penghargaan Penghargaan[]
|
||||
ProfileDesaImage ProfileDesaImage[]
|
||||
ProfilePPID ProfilePPID[]
|
||||
ProfilPerbekel ProfilPerbekel[]
|
||||
Puskesmas Puskesmas[]
|
||||
ProgramKesehatan ProgramKesehatan[]
|
||||
PenangananDarurat PenangananDarurat[]
|
||||
KontakDarurat KontakDarurat[]
|
||||
InfoWabahPenyakit InfoWabahPenyakit[]
|
||||
KeamananLingkungan KeamananLingkungan[]
|
||||
MenuTipsKeamanan MenuTipsKeamanan[]
|
||||
PelayananSuratKeteranganImage PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage")
|
||||
PelayananSuratKeteranganImage2 PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage2")
|
||||
PasarDesa PasarDesa[]
|
||||
KontakDaruratKeamanan KontakDaruratKeamanan[]
|
||||
KontakItem KontakItem[]
|
||||
Pegawai Pegawai[]
|
||||
DesaDigital DesaDigital[]
|
||||
InfoTekno InfoTekno[]
|
||||
PengaduanMasyarakat PengaduanMasyarakat[]
|
||||
KegiatanDesa KegiatanDesa[]
|
||||
ProgramInovasi ProgramInovasi[]
|
||||
PejabatDesa PejabatDesa[]
|
||||
MediaSosial MediaSosial[]
|
||||
DesaAntiKorupsi DesaAntiKorupsi[]
|
||||
SDGSDesa SDGSDesa[]
|
||||
APBDesImage APBDes[] @relation("APBDesImage")
|
||||
APBDesFile APBDes[] @relation("APBDesFile")
|
||||
PrestasiDesa PrestasiDesa[]
|
||||
DataPerpustakaan DataPerpustakaan[]
|
||||
PegawaiPPID PegawaiPPID[]
|
||||
PerbekelDariMasaKeMasa PerbekelDariMasaKeMasa[]
|
||||
|
||||
MitraKolaborasi MitraKolaborasi[]
|
||||
}
|
||||
|
||||
//========================================= MENU LANDING PAGE ========================================= //
|
||||
@@ -122,15 +131,15 @@ model ProgramInovasi {
|
||||
}
|
||||
|
||||
model MediaSosial {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
iconUrl String? @db.VarChar(255)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
iconUrl String? @db.VarChar(255)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
isActive Boolean @default(true)
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
//========================================= PROFILE ========================================= //
|
||||
@@ -140,8 +149,8 @@ model DesaAntiKorupsi {
|
||||
deskripsi String @db.Text
|
||||
kategori KategoriDesaAntiKorupsi @relation(fields: [kategoriId], references: [id])
|
||||
kategoriId String
|
||||
file FileStorage @relation(fields: [fileId], references: [id])
|
||||
fileId String
|
||||
file FileStorage? @relation(fields: [fileId], references: [id])
|
||||
fileId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
@@ -158,12 +167,11 @@ model KategoriDesaAntiKorupsi {
|
||||
DesaAntiKorupsi DesaAntiKorupsi[]
|
||||
}
|
||||
|
||||
//========================================= MENU PPID ========================================= //
|
||||
|
||||
//========================================= STRUKTUR PPID ========================================= //
|
||||
model StrukturPPID {
|
||||
//========================================= SDGS Desa ========================================= //
|
||||
model SDGSDesa {
|
||||
id String @id @default(cuid())
|
||||
name String @db.Text
|
||||
name String @unique
|
||||
jumlah String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
@@ -172,6 +180,157 @@ model StrukturPPID {
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
//========================================= APBDes ========================================= //
|
||||
model APBDes {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
jumlah String
|
||||
image FileStorage? @relation("APBDesImage", fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
file FileStorage? @relation("APBDesFile", fields: [fileId], references: [id])
|
||||
fileId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
//========================================= PRESTASI DESA ========================================= //
|
||||
model PrestasiDesa {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
deskripsi String @db.Text
|
||||
kategori KategoriPrestasiDesa @relation(fields: [kategoriId], references: [id])
|
||||
kategoriId String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model KategoriPrestasiDesa {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
PrestasiDesa PrestasiDesa[]
|
||||
}
|
||||
|
||||
//========================================= INDEKS KEPUASAAN MASYARAKAT ========================================= //
|
||||
model Responden {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
tanggal DateTime @db.Date // misal: 2025-05-01
|
||||
jenisKelamin JenisKelaminResponden @relation(fields: [jenisKelaminId], references: [id])
|
||||
jenisKelaminId String
|
||||
rating PilihanRatingResponden @relation(fields: [ratingId], references: [id])
|
||||
ratingId String
|
||||
kelompokUmur UmurResponden @relation(fields: [kelompokUmurId], references: [id])
|
||||
kelompokUmurId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model JenisKelaminResponden {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
Responden Responden[]
|
||||
}
|
||||
|
||||
model PilihanRatingResponden {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
Responden Responden[]
|
||||
}
|
||||
|
||||
model UmurResponden {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
Responden Responden[]
|
||||
}
|
||||
|
||||
//========================================= MENU PPID ========================================= //
|
||||
|
||||
//========================================= STRUKTUR PPID ========================================= //
|
||||
model StrukturPPID {
|
||||
id String @id @default(cuid())
|
||||
name String @db.Text
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
PosisiOrganisasiPPID PosisiOrganisasiPPID? @relation(fields: [posisiOrganisasiPPIDId], references: [id])
|
||||
posisiOrganisasiPPIDId String?
|
||||
PegawaiPPID PegawaiPPID? @relation(fields: [pegawaiPPIDId], references: [id])
|
||||
pegawaiPPIDId String?
|
||||
}
|
||||
|
||||
model PosisiOrganisasiPPID {
|
||||
id String @id @default(cuid())
|
||||
nama String @db.VarChar(100)
|
||||
deskripsi String? @db.Text
|
||||
hierarki Int
|
||||
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")
|
||||
}
|
||||
|
||||
model PegawaiPPID {
|
||||
id String @id @default(cuid())
|
||||
namaLengkap String @db.VarChar(255)
|
||||
gelarAkademik String? @db.VarChar(100)
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
tanggalMasuk DateTime? @db.Date
|
||||
email String? @unique @db.VarChar(255)
|
||||
telepon String? @db.VarChar(20)
|
||||
alamat String? @db.Text
|
||||
posisiId String @db.VarChar(50)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
posisi PosisiOrganisasiPPID @relation(fields: [posisiId], references: [id])
|
||||
strukturOrganisasi StrukturPPID[] // Relasi balik
|
||||
}
|
||||
|
||||
model StrukturOrganisasiPPID {
|
||||
id String @id @default(uuid())
|
||||
posisiOrganisasiId String @db.VarChar(50)
|
||||
pegawaiId String @db.Uuid
|
||||
hubunganOrganisasiId String @db.Uuid
|
||||
posisiOrganisasi PosisiOrganisasi @relation(fields: [posisiOrganisasiId], references: [id])
|
||||
pegawai Pegawai @relation(fields: [pegawaiId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= VISI MISI PPID ========================================= //
|
||||
model VisiMisiPPID {
|
||||
id String @id @default(cuid())
|
||||
@@ -398,6 +557,19 @@ model ProfilPerbekel {
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model PerbekelDariMasaKeMasa {
|
||||
id String @id @default(cuid())
|
||||
nama String @db.Text
|
||||
periode String @db.Text
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
daerah String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= BERITA ========================================= //
|
||||
model Berita {
|
||||
id String @id @default(cuid())
|
||||
@@ -426,17 +598,28 @@ model KategoriBerita {
|
||||
|
||||
// ========================================= POTENSI DESA ========================================= //
|
||||
model PotensiDesa {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsi String
|
||||
kategori String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
content String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsi String
|
||||
kategori KategoriPotensi? @relation(fields: [kategoriId], references: [id])
|
||||
kategoriId String?
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
content String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model KategoriPotensi {
|
||||
id String @id @default(cuid())
|
||||
nama String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
PotensiDesa PotensiDesa[]
|
||||
}
|
||||
|
||||
// ========================================= PENGUMUMAN ========================================= //
|
||||
@@ -489,15 +672,17 @@ model GalleryVideo {
|
||||
|
||||
// ========================================= LAYANAN DESA ========================================= //
|
||||
model PelayananSuratKeterangan {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsi String @db.Text
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
deskripsi String @db.Text
|
||||
image FileStorage? @relation("PelayananSuratKeteranganImage", fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
image2 FileStorage? @relation("PelayananSuratKeteranganImage2", fields: [image2Id], references: [id])
|
||||
image2Id String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model PelayananTelunjukSaktiDesa {
|
||||
@@ -534,16 +719,16 @@ model PelayananPendudukNonPermanen {
|
||||
|
||||
// ========================================= PENGHARGAAN ========================================= //
|
||||
model Penghargaan {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
juara String
|
||||
deskripsi String @db.Text
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
deskripsi String @db.Text
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= MENU KESEHATAN ========================================= //
|
||||
@@ -730,26 +915,56 @@ model PendaftaranJadwalKegiatan {
|
||||
|
||||
// ========================================= PERSENTASE KELAHIRAN & KEMATIAN ========================================= //
|
||||
model DataKematian_Kelahiran {
|
||||
id String @id @default(cuid())
|
||||
tahun String
|
||||
kematianKasar String
|
||||
kematianBayi String
|
||||
kelahiranKasar String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
id String @id @default(cuid())
|
||||
kematian Kematian @relation(fields: [kematianId], references: [id])
|
||||
kematianId String
|
||||
kelahiran Kelahiran @relation(fields: [kelahiranId], references: [id])
|
||||
kelahiranId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model Kelahiran {
|
||||
id String @id @default(cuid())
|
||||
nama String
|
||||
tanggal DateTime
|
||||
jenisKelamin String
|
||||
alamat String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
DataKematian_Kelahiran DataKematian_Kelahiran[]
|
||||
}
|
||||
|
||||
model Kematian {
|
||||
id String @id @default(cuid())
|
||||
nama String
|
||||
tanggal DateTime
|
||||
jenisKelamin String
|
||||
alamat String
|
||||
penyebab String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
DataKematian_Kelahiran DataKematian_Kelahiran[]
|
||||
}
|
||||
|
||||
// ========================================= GRAFIK KEPUASAN ========================================= //
|
||||
model GrafikKepuasan {
|
||||
id String @id @default(cuid())
|
||||
label String
|
||||
jumlah String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
id String @id @default(cuid())
|
||||
nama String
|
||||
tanggal DateTime
|
||||
jenisKelamin String
|
||||
alamat String
|
||||
penyakit String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= ARTIKEL KESEHATAN ========================================= //
|
||||
@@ -846,16 +1061,17 @@ model DoctorSign {
|
||||
|
||||
// ========================================= POSYANDU ========================================= //
|
||||
model Posyandu {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
nomor String
|
||||
deskripsi String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
nomor String
|
||||
deskripsi String
|
||||
jadwalPelayanan String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= PUSKESMAS ========================================= //
|
||||
@@ -1182,8 +1398,9 @@ model PosisiOrganisasi {
|
||||
deskripsi String? @db.Text
|
||||
hierarki Int
|
||||
|
||||
pegawai Pegawai[]
|
||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||
pegawai Pegawai[]
|
||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||
StrukturOrganisasiPPID StrukturOrganisasiPPID[]
|
||||
|
||||
@@map("posisi_organisasi")
|
||||
}
|
||||
@@ -1208,7 +1425,8 @@ model Pegawai {
|
||||
sebagaiAtasan HubunganOrganisasi[] @relation("AtasanToBawahan")
|
||||
sebagaiBawahan HubunganOrganisasi[] @relation("BawahanToAtasan")
|
||||
|
||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||
StrukturOrganisasiPPID StrukturOrganisasiPPID[]
|
||||
|
||||
@@map("pegawai")
|
||||
}
|
||||
@@ -1333,7 +1551,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
|
||||
@@ -1421,18 +1639,27 @@ model ProgramKreatif {
|
||||
|
||||
// ========================================= KOLABORASI INOVASI ========================================= //
|
||||
model KolaborasiInovasi {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
tahun Int
|
||||
slug String @db.Text //deskripsi singkat
|
||||
deskripsi String @db.Text //deskripsi panjang
|
||||
slug String @db.Text //deskripsi singkat
|
||||
deskripsi String @db.Text //deskripsi panjang
|
||||
kolaborator String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model MitraKolaborasi {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= INFO TEKHNOLOGI TEPAT GUNA ========================================= //
|
||||
@@ -1659,3 +1886,270 @@ model NilaiKonservasiAdat {
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= MENU PENDIDIKAN ========================================= //
|
||||
// ========================================= INFO SEKOLAH & PAUD ========================================= //
|
||||
model JenjangPendidikan {
|
||||
id String @id @default(cuid())
|
||||
nama String // TK/PAUD, SD, SMP, SMA/SMK
|
||||
lembagas Lembaga[] // Relasi ke lembaga
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model Lembaga {
|
||||
id String @id @default(cuid())
|
||||
nama String
|
||||
jenjangPendidikan JenjangPendidikan @relation(fields: [jenjangId], references: [id])
|
||||
jenjangId String
|
||||
siswa Siswa[]
|
||||
pengajar Pengajar[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model Siswa {
|
||||
id String @id @default(cuid())
|
||||
nama String
|
||||
lembaga Lembaga @relation(fields: [lembagaId], references: [id])
|
||||
lembagaId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model Pengajar {
|
||||
id String @id @default(cuid())
|
||||
nama String
|
||||
lembaga Lembaga @relation(fields: [lembagaId], references: [id])
|
||||
lembagaId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= BEASISWA DESA ========================================= //
|
||||
model KeunggulanProgram {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model BeasiswaPendaftar {
|
||||
id String @id @default(cuid())
|
||||
namaLengkap String
|
||||
nik String @unique
|
||||
tempatLahir String
|
||||
tanggalLahir DateTime
|
||||
jenisKelamin JenisKelamin
|
||||
kewarganegaraan String
|
||||
agama Agama
|
||||
alamatKTP String
|
||||
alamatDomisili String?
|
||||
noHp String
|
||||
email String @unique
|
||||
statusPernikahan StatusPernikahan
|
||||
ukuranBaju UkuranBaju?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
enum JenisKelamin {
|
||||
LAKI_LAKI
|
||||
PEREMPUAN
|
||||
}
|
||||
|
||||
enum Agama {
|
||||
ISLAM
|
||||
KRISTEN_PROTESTAN
|
||||
KRISTEN_KATOLIK
|
||||
HINDU
|
||||
BUDDHA
|
||||
KONGHUCU
|
||||
LAINNYA
|
||||
}
|
||||
|
||||
enum StatusPernikahan {
|
||||
BELUM_MENIKAH
|
||||
MENIKAH
|
||||
JANDA_DUDA
|
||||
}
|
||||
|
||||
enum UkuranBaju {
|
||||
S
|
||||
M
|
||||
L
|
||||
XL
|
||||
XXL
|
||||
LAINNYA
|
||||
}
|
||||
|
||||
// ========================================= PROGRAM PENDIDIKAN ANAK ========================================= //
|
||||
model TujuanProgram {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model ProgramUnggulan {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= BIMBINGAN BELAJAR DESA ========================================= //
|
||||
model TujuanBimbinganBelajarDesa {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model LokasiJadwalBimbinganBelajarDesa {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model FasilitasBimbinganBelajarDesa {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= PENDIDIKAN NON FORMAL ========================================= //
|
||||
model TujuanPendidikanNonFormal {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model TempatKegiatan {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model JenisProgramYangDiselenggarakan {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= PERPUSTAKAAN ========================================= //
|
||||
model DataPerpustakaan {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
kategori KategoriBuku @relation(fields: [kategoriId], references: [id])
|
||||
kategoriId String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model KategoriBuku {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
DataPerpustakaan DataPerpustakaan[]
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
nama String
|
||||
email String @unique
|
||||
password String?
|
||||
role Role @relation(fields: [roleId], references: [id])
|
||||
roleId String
|
||||
instansi String? // Nama instansi (Puskesmas, Sekolah, dll)
|
||||
isActive Boolean @default(true)
|
||||
lastLogin DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
}
|
||||
|
||||
model Role {
|
||||
id String @id @default(cuid())
|
||||
name String @unique // ADMIN_DESA, ADMIN_KESEHATAN, ADMIN_SEKOLAH
|
||||
description String?
|
||||
permissions Json // Menyimpan permission dalam format JSON
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
users User[]
|
||||
|
||||
@@map("roles")
|
||||
}
|
||||
|
||||
// Tabel untuk menyimpan permission
|
||||
model Permission {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
description String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("permissions")
|
||||
}
|
||||
|
||||
// ========================================= DATA PENDIDIKAN ========================================= //
|
||||
model DataPendidikan {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
jumlah String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
710
prisma/seed.ts
710
prisma/seed.ts
@@ -1,52 +1,341 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
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 profilePejabatDesa from "./data/landing-page/profile/profile.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 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 strukturPPID from "./data/ppid/struktur-ppid/strukturPPID.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 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 bentukKonservasiBerdasarkanAdat from './data/lingkungan/konservasi-adat-bali/bentuk-konservasi.json';
|
||||
import filosofiTriHita from './data/lingkungan/konservasi-adat-bali/filosofi-tri-hita.json';
|
||||
import profilePejabatDesa from './data/landing-page/profile.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 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 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 tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-program.json";
|
||||
import roles from "./data/user/roles.json";
|
||||
import users from "./data/user/users.json";
|
||||
|
||||
(async () => {
|
||||
for (const l of layanan) {
|
||||
await prisma.layanan.upsert({
|
||||
// =========== USER & ROLE ===========
|
||||
for (const r of roles) {
|
||||
await prisma.role.upsert({
|
||||
where: { id: r.id },
|
||||
update: {
|
||||
name: r.name,
|
||||
description: r.description,
|
||||
permissions: r.permissions,
|
||||
isActive: true,
|
||||
},
|
||||
create: {
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
description: r.description,
|
||||
permissions: r.permissions,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Roles seeded");
|
||||
//users
|
||||
for (const u of users) {
|
||||
await prisma.user.upsert({
|
||||
where: { id: u.id },
|
||||
update: {
|
||||
nama: u.nama,
|
||||
email: u.email,
|
||||
password: u.password,
|
||||
roleId: u.roleId,
|
||||
isActive: true,
|
||||
},
|
||||
create: {
|
||||
id: u.id,
|
||||
nama: u.nama,
|
||||
email: u.email,
|
||||
password: u.password,
|
||||
roleId: u.roleId,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ Users seeded");
|
||||
// =========== LANDING PAGE ===========
|
||||
// =========== SUBMENU PROFILE ===========
|
||||
// =========== PROFILE PEJABAT DESA ===========
|
||||
for (const p of profilePejabatDesa) {
|
||||
await prisma.pejabatDesa.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
position: p.position,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
position: p.position,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ profilePejabatDesa seeded without imageId (editable later via UI)"
|
||||
);
|
||||
|
||||
// =========== PROGRAM INOVASI ===========
|
||||
for (const p of programInovasi) {
|
||||
await prisma.programInovasi.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
description: p.description,
|
||||
link: p.link,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
description: p.description,
|
||||
link: p.link,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("program inovasi success ...");
|
||||
|
||||
// =========== MEDIA SOSIAL ===========
|
||||
for (const p of mediaSosial) {
|
||||
await prisma.mediaSosial.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
iconUrl: p.iconUrl,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
iconUrl: p.iconUrl,
|
||||
},
|
||||
});
|
||||
}
|
||||
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({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
juara: p.juara,
|
||||
deskripsi: p.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
juara: p.juara,
|
||||
deskripsi: p.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("penghargaan success ...");
|
||||
|
||||
// =========== LAYANAN DESA ===========
|
||||
for (const p of pelayananSuratKeterangan) {
|
||||
await prisma.pelayananSuratKeterangan.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("pelayanan surat keterangan success ...");
|
||||
|
||||
for (const p of pelayananTelunjukSaktiDesa) {
|
||||
await prisma.pelayananTelunjukSaktiDesa.upsert({
|
||||
where: { id: p.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
link: p.link,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
deskripsi: p.deskripsi,
|
||||
link: p.link,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("pelayanan surat keterangan success ...");
|
||||
|
||||
// =========== SDGSDesa ===========
|
||||
for (const l of sdgsDesa) {
|
||||
await prisma.sDGSDesa.upsert({
|
||||
where: {
|
||||
name: l.name,
|
||||
jumlah: l.jumlah,
|
||||
},
|
||||
update: {
|
||||
name: l.name,
|
||||
jumlah: l.jumlah,
|
||||
},
|
||||
create: {
|
||||
name: l.name,
|
||||
jumlah: l.jumlah,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("layanan success ...");
|
||||
console.log("sdgs desa success ...");
|
||||
|
||||
// =========== APBDes ===========
|
||||
for (const l of apbdes) {
|
||||
await prisma.aPBDes.upsert({
|
||||
where: {
|
||||
name: l.name,
|
||||
jumlah: l.jumlah,
|
||||
},
|
||||
update: {
|
||||
name: l.name,
|
||||
jumlah: l.jumlah,
|
||||
},
|
||||
create: {
|
||||
name: l.name,
|
||||
jumlah: l.jumlah,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log("sdgs desa success ...");
|
||||
|
||||
// =========== MENU DESA ===========
|
||||
// =========== SUBMENU PROFILE ===========
|
||||
// =========== SEJARAH DESA ===========
|
||||
for (const l of sejarahDesa) {
|
||||
await prisma.sejarahDesa.upsert({
|
||||
where: {
|
||||
@@ -66,6 +355,7 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
|
||||
console.log("sejarah desa success ...");
|
||||
|
||||
// =========== MASKOT DESA ===========
|
||||
for (const l of maskotDesa) {
|
||||
await prisma.maskotDesa.upsert({
|
||||
where: {
|
||||
@@ -85,6 +375,7 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
|
||||
console.log("maskot desa success ...");
|
||||
|
||||
// =========== LAMBANG DESA ===========
|
||||
for (const l of lambangDesa) {
|
||||
await prisma.lambangDesa.upsert({
|
||||
where: {
|
||||
@@ -104,6 +395,7 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
|
||||
console.log("lambang desa success ...");
|
||||
|
||||
// =========== PROFIL PERBEKEL ===========
|
||||
for (const c of profilPerbekel) {
|
||||
await prisma.profilPerbekel.upsert({
|
||||
where: { id: c.id },
|
||||
@@ -124,8 +416,11 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ profilePerbekel seeded without imageId (editable later via UI)");
|
||||
console.log(
|
||||
"✅ profilePerbekel seeded without imageId (editable later via UI)"
|
||||
);
|
||||
|
||||
// =========== VISI MISI DESA ===========
|
||||
for (const l of visiMisiDesa) {
|
||||
await prisma.visiMisiDesa.upsert({
|
||||
where: {
|
||||
@@ -145,6 +440,135 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
|
||||
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
|
||||
const sortedPosisi = flattenedPosisi.sort((a, b) => a.hierarki - b.hierarki);
|
||||
|
||||
for (const p of sortedPosisi) {
|
||||
console.log(`Seeding: ${p.nama} (id: ${p.id}, parent: ${p.parentId})`);
|
||||
|
||||
if (p.parentId) {
|
||||
const parentExists = flattenedPosisi.some((pos) => pos.id === p.parentId);
|
||||
if (!parentExists) {
|
||||
console.warn(
|
||||
`⚠️ Parent tidak ditemukan: ${p.parentId} untuk ${p.nama}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.posisiOrganisasiPPID.upsert({
|
||||
where: { id: p.id },
|
||||
update: p,
|
||||
create: p,
|
||||
});
|
||||
}
|
||||
console.log("posisi organisasi berhasil");
|
||||
|
||||
// =========== PEGAWAI PPID ===========
|
||||
const flattenedPegawai = pegawaiPPID.flat();
|
||||
for (const p of flattenedPegawai) {
|
||||
await prisma.pegawaiPPID.upsert({
|
||||
where: { id: p.id },
|
||||
update: p,
|
||||
create: p,
|
||||
});
|
||||
}
|
||||
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({
|
||||
where: {
|
||||
@@ -184,22 +608,6 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
}
|
||||
console.log("pelayanan penduduk non permanen success ...");
|
||||
|
||||
for (const s of strukturPPID) {
|
||||
await prisma.strukturPPID.upsert({
|
||||
where: {
|
||||
id: s.id,
|
||||
},
|
||||
update: {
|
||||
name: s.name,
|
||||
},
|
||||
create: {
|
||||
id: s.id,
|
||||
name: s.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("struktur ppid success ...");
|
||||
|
||||
for (const p of potensi) {
|
||||
await prisma.potensi.upsert({
|
||||
where: {
|
||||
@@ -293,65 +701,53 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
}
|
||||
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({
|
||||
for (const j of jenisKelamin) {
|
||||
await prisma.jenisKelaminResponden.upsert({
|
||||
where: {
|
||||
id: v.id,
|
||||
id: j.id,
|
||||
},
|
||||
update: {
|
||||
misi: v.misi,
|
||||
visi: v.visi,
|
||||
name: j.name,
|
||||
},
|
||||
create: {
|
||||
id: v.id,
|
||||
misi: v.misi,
|
||||
visi: v.visi,
|
||||
id: j.id,
|
||||
name: j.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("visi misi PPID success ...");
|
||||
console.log("jenis kelamin responden success ...");
|
||||
|
||||
for (const v of dasarHukumPPID) {
|
||||
await prisma.dasarHukumPPID.upsert({
|
||||
for (const r of pilihanRatingResponden) {
|
||||
await prisma.pilihanRatingResponden.upsert({
|
||||
where: {
|
||||
id: v.id,
|
||||
id: r.id,
|
||||
},
|
||||
update: {
|
||||
judul: v.judul,
|
||||
content: v.content,
|
||||
name: r.name,
|
||||
},
|
||||
create: {
|
||||
id: v.id,
|
||||
judul: v.judul,
|
||||
content: v.content,
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("dasar hukum PPID success ...");
|
||||
console.log("pilihan rating responden success ...");
|
||||
|
||||
for (const u of umurResponden) {
|
||||
await prisma.umurResponden.upsert({
|
||||
where: {
|
||||
id: u.id,
|
||||
},
|
||||
update: {
|
||||
name: u.name,
|
||||
},
|
||||
create: {
|
||||
id: u.id,
|
||||
name: u.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("umur responden success ...");
|
||||
|
||||
for (const k of kategoriProduk) {
|
||||
await prisma.kategoriProduk.upsert({
|
||||
@@ -369,7 +765,6 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
}
|
||||
console.log("kategori produk success ...");
|
||||
|
||||
|
||||
for (const p of posisiOrganisasi) {
|
||||
await prisma.posisiOrganisasi.upsert({
|
||||
where: {
|
||||
@@ -441,9 +836,12 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
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,
|
||||
@@ -453,7 +851,7 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
},
|
||||
create: {
|
||||
month: d.month,
|
||||
year: d.year,
|
||||
year: yearAsDate,
|
||||
totalUnemployment: d.totalUnemployment,
|
||||
educatedUnemployment: d.educatedUnemployment,
|
||||
uneducatedUnemployment: d.uneducatedUnemployment,
|
||||
@@ -558,7 +956,6 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
|
||||
console.log("bentuk konservasi berdasarkan adat success ...");
|
||||
|
||||
|
||||
for (const n of nilaiKonservasiAdat) {
|
||||
await prisma.nilaiKonservasiAdat.upsert({
|
||||
where: {
|
||||
@@ -578,22 +975,145 @@ import profilePejabatDesa from './data/landing-page/profile.json';
|
||||
|
||||
console.log("nilai konservasi adat success ...");
|
||||
|
||||
for (const p of profilePejabatDesa) {
|
||||
await prisma.pejabatDesa.upsert({
|
||||
where: { id: p.id },
|
||||
for (const t of tujuanProgram) {
|
||||
await prisma.tujuanProgram.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
name: p.name,
|
||||
position: p.position,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
position: p.position,
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ profilePejabatDesa seeded without imageId (editable later via UI)");
|
||||
console.log("✅ tujuan program seeded (editable later via UI)");
|
||||
|
||||
for (const t of programUnggulan) {
|
||||
await prisma.programUnggulan.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("✅ program unggulan seeded (editable later via UI)");
|
||||
|
||||
for (const t of tujuanBimbinganBelajarDesa) {
|
||||
await prisma.tujuanBimbinganBelajarDesa.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ tujuan bimbingan belajar desa seeded (editable later via UI)"
|
||||
);
|
||||
|
||||
for (const t of lokasiJadwalBimbinganBelajarDesa) {
|
||||
await prisma.lokasiJadwalBimbinganBelajarDesa.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ lokasi jadwal bimbingan belajar desa seeded (editable later via UI)"
|
||||
);
|
||||
|
||||
for (const t of fasilitasBimbinganBelajarDesa) {
|
||||
await prisma.fasilitasBimbinganBelajarDesa.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ fasilitas bimbingan belajar desa seeded (editable later via UI)"
|
||||
);
|
||||
|
||||
for (const t of tujuanProgram2) {
|
||||
await prisma.tujuanPendidikanNonFormal.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ fasilitas bimbingan belajar desa seeded (editable later via UI)"
|
||||
);
|
||||
|
||||
for (const t of tempatKegiatan) {
|
||||
await prisma.tempatKegiatan.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ fasilitas bimbingan belajar desa seeded (editable later via UI)"
|
||||
);
|
||||
|
||||
for (const t of jenisProgramYangDiselenggarakan) {
|
||||
await prisma.jenisProgramYangDiselenggarakan.upsert({
|
||||
where: { id: t.id },
|
||||
update: {
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
create: {
|
||||
id: t.id,
|
||||
judul: t.judul,
|
||||
deskripsi: t.deskripsi,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"✅ fasilitas bimbingan belajar desa seeded (editable later via UI)"
|
||||
);
|
||||
})()
|
||||
.then(() => prisma.$disconnect())
|
||||
.catch((e) => {
|
||||
|
||||
62
src/app/admin/(dashboard)/_com/LeafletMultiMarkerMap.tsx
Normal file
62
src/app/admin/(dashboard)/_com/LeafletMultiMarkerMap.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client';
|
||||
|
||||
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
|
||||
import { LatLngExpression } from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import L from 'leaflet';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
type MarkerData = {
|
||||
position: [number, number];
|
||||
popup: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
center: [number, number];
|
||||
markers: MarkerData[];
|
||||
zoom?: number;
|
||||
scrollWheelZoom?: boolean;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
|
||||
export default function LeafletMultiMarkerMap({
|
||||
center,
|
||||
markers,
|
||||
zoom = 13,
|
||||
scrollWheelZoom = true,
|
||||
className = '',
|
||||
style = { height: '100%', width: '100%', zIndex: 0 },
|
||||
}: Props) {
|
||||
// Fix for default marker icons in Next.js
|
||||
useEffect(() => {
|
||||
delete (L.Icon.Default.prototype as any)._getIconUrl;
|
||||
L.Icon.Default.mergeOptions({
|
||||
iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png',
|
||||
iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
|
||||
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png',
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<MapContainer
|
||||
center={center as LatLngExpression}
|
||||
zoom={zoom}
|
||||
scrollWheelZoom={scrollWheelZoom}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
{markers.map((marker, index) => (
|
||||
<Marker key={index} position={marker.position as LatLngExpression}>
|
||||
<Popup>{marker.popup}</Popup>
|
||||
</Marker>
|
||||
))}
|
||||
</MapContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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";
|
||||
@@ -22,19 +23,6 @@ const defaultForm = {
|
||||
kategoriBeritaId: "",
|
||||
};
|
||||
|
||||
// 3. Kategori proxy
|
||||
const category = proxy({
|
||||
findMany: {
|
||||
data: [] as Prisma.KategoriBeritaGetPayload<{ omit: { isActive: true } }>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.berita.category["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
category.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 4. Berita proxy
|
||||
const berita = proxy({
|
||||
create: {
|
||||
@@ -71,6 +59,8 @@ const berita = proxy({
|
||||
},
|
||||
},
|
||||
|
||||
// State untuk berita utama (hanya 1)
|
||||
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.BeritaGetPayload<{
|
||||
@@ -83,38 +73,43 @@ const berita = proxy({
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
|
||||
async load(page = 1, limit = 10) {
|
||||
berita.findMany.loading = true;
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||
berita.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
berita.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.desa.berita["find-many"].get({
|
||||
query: {
|
||||
page,
|
||||
limit,
|
||||
},
|
||||
});
|
||||
berita.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (kategori) query.kategori = kategori;
|
||||
|
||||
const res = await ApiFetch.api.desa.berita["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
berita.findMany.data = res.data.data ?? [];
|
||||
berita.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
berita.findMany.data = [];
|
||||
berita.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch berita paginated:", err);
|
||||
berita.findMany.data = [];
|
||||
berita.findMany.totalPages = 1;
|
||||
} finally {
|
||||
berita.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
findUnique: {
|
||||
data: null as
|
||||
| Prisma.BeritaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategoriBerita: true;
|
||||
};
|
||||
}> | null,
|
||||
data: null as Prisma.BeritaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategoriBerita: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/desa/berita/${id}`);
|
||||
@@ -122,11 +117,11 @@ const berita = proxy({
|
||||
const data = await res.json();
|
||||
berita.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error('Failed to fetch berita:', res.statusText);
|
||||
console.error("Failed to fetch berita:", res.statusText);
|
||||
berita.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching berita:', error);
|
||||
console.error("Error fetching berita:", error);
|
||||
berita.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
@@ -140,14 +135,14 @@ const berita = proxy({
|
||||
berita.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/berita/delete/${id}`, {
|
||||
method: 'DELETE',
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Berita berhasil dihapus");
|
||||
await berita.findMany.load(); // refresh list
|
||||
@@ -172,21 +167,21 @@ const berita = proxy({
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/berita/${id}`, {
|
||||
method: 'GET',
|
||||
method: "GET",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
@@ -203,7 +198,9 @@ const berita = proxy({
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading berita:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
@@ -217,14 +214,14 @@ const berita = proxy({
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
berita.edit.loading = true;
|
||||
|
||||
|
||||
const response = await fetch(`/api/desa/berita/${this.id}`, {
|
||||
method: 'PUT',
|
||||
method: "PUT",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
judul: this.form.judul,
|
||||
@@ -234,14 +231,16 @@ const berita = proxy({
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update berita");
|
||||
await berita.findMany.load(); // refresh list
|
||||
@@ -251,7 +250,11 @@ const berita = proxy({
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating berita:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update berita");
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update berita"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
berita.edit.loading = false;
|
||||
@@ -271,21 +274,22 @@ const berita = proxy({
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load() {
|
||||
// findFirst.load()
|
||||
async load(kategori?: string) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await ApiFetch.api.desa.berita["find-first"].get();
|
||||
const res = await ApiFetch.api.desa.berita["find-first"].get({
|
||||
query: kategori ? { kategori } : {},
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
// Add type assertion to ensure type safety
|
||||
berita.findFirst.data = res.data.data as Prisma.BeritaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategoriBerita: true;
|
||||
};
|
||||
}> | null;
|
||||
this.data = res.data.data || null;
|
||||
} else {
|
||||
this.data = null;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch berita terbaru:", err);
|
||||
this.data = null;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
@@ -299,7 +303,7 @@ const berita = proxy({
|
||||
};
|
||||
}>[],
|
||||
loading: false,
|
||||
|
||||
|
||||
async load() {
|
||||
try {
|
||||
this.loading = true;
|
||||
@@ -313,15 +317,227 @@ const berita = proxy({
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
//=============== Kategori Berita ===============
|
||||
|
||||
const templateKategoriBerita = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
});
|
||||
|
||||
const defaultKategoriBerita = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
const kategoriBerita = proxy({
|
||||
create: {
|
||||
form: { ...defaultKategoriBerita },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateKategoriBerita.safeParse(kategoriBerita.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kategoriBerita.create.loading = true;
|
||||
const res = await ApiFetch.api.desa.kategoriberita["create"].post(
|
||||
kategoriBerita.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
kategoriBerita.findMany.load();
|
||||
return toast.success("Data Kategori Berita Berhasil Dibuat");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return toast.error("failed create");
|
||||
} finally {
|
||||
kategoriBerita.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as Prisma.KategoriBeritaGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[],
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.kategoriberita["findMany"].get();
|
||||
if (res.status === 200) {
|
||||
kategoriBerita.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KategoriBeritaGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/desa/kategoriberita/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kategoriBerita.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
kategoriBerita.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
kategoriBerita.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async delete(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
kategoriBerita.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/kategoriberita/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "Data Kategori Berita berhasil dihapus"
|
||||
);
|
||||
await kategoriBerita.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus Data Kategori Berita"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus Data Kategori Berita");
|
||||
} finally {
|
||||
kategoriBerita.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultKategoriBerita },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/kategoriberita/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori berita:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateKategoriBerita.safeParse(kategoriBerita.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
kategoriBerita.update.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/kategoriberita/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update data kategori berita");
|
||||
await kategoriBerita.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal update data kategori berita"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating data kategori berita:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update data kategori berita"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
kategoriBerita.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
kategoriBerita.update.id = "";
|
||||
kategoriBerita.update.form = { ...defaultKategoriBerita };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 5. State global
|
||||
const stateDashboardBerita = proxy({
|
||||
category,
|
||||
kategoriBerita,
|
||||
berita,
|
||||
});
|
||||
|
||||
|
||||
@@ -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";
|
||||
@@ -68,10 +69,34 @@ const foto = proxy({
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.gallery.foto["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
foto.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
foto.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
foto.findMany.page = page;
|
||||
foto.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.desa.gallery.foto["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
foto.findMany.data = res.data.data ?? [];
|
||||
foto.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
foto.findMany.data = [];
|
||||
foto.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch foto paginated:", err);
|
||||
foto.findMany.data = [];
|
||||
foto.findMany.totalPages = 1;
|
||||
} finally {
|
||||
foto.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -215,6 +240,28 @@ const foto = proxy({
|
||||
foto.update.form = { ...defaultFormFoto };
|
||||
},
|
||||
},
|
||||
findRecent: {
|
||||
data: [] as Prisma.GalleryFotoGetPayload<{
|
||||
include: {
|
||||
imageGalleryFoto: true;
|
||||
};
|
||||
}>[],
|
||||
loading: false,
|
||||
|
||||
async load() {
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await ApiFetch.api.desa.gallery.foto["find-recent"].get();
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
this.data = res.data.data ?? [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal fetch foto recent:", error);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const video = proxy({
|
||||
@@ -257,10 +304,34 @@ const video = proxy({
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.gallery.video["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
video.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
video.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
video.findMany.page = page;
|
||||
video.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.desa.gallery.video["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
video.findMany.data = res.data.data ?? [];
|
||||
video.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
video.findMany.data = [];
|
||||
video.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch video paginated:", err);
|
||||
video.findMany.data = [];
|
||||
video.findMany.totalPages = 1;
|
||||
} finally {
|
||||
video.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";
|
||||
@@ -8,12 +9,14 @@ const templateSuratKeteranganForm = z.object({
|
||||
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
imageId: z.string().nonempty(),
|
||||
image2Id: z.string().nonempty(),
|
||||
});
|
||||
|
||||
const suratKeteranganForm = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
image2Id: "",
|
||||
};
|
||||
|
||||
const telunjukSaktiDesaForm = {
|
||||
@@ -105,15 +108,38 @@ const suratKeterangan = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as Prisma.PelayananSuratKeteranganGetPayload<{
|
||||
include: { image: true };
|
||||
}>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.layanan.pelayanansuratketerangan[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
suratKeterangan.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
suratKeterangan.findMany.loading = true; // Use the full path to access the property
|
||||
suratKeterangan.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.desa.layanan.pelayanansuratketerangan[
|
||||
"find-many"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
suratKeterangan.findMany.data = res.data.data || [];
|
||||
suratKeterangan.findMany.total = res.data.total || 0;
|
||||
suratKeterangan.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load surat keterangan:", res.data?.message);
|
||||
suratKeterangan.findMany.data = [];
|
||||
suratKeterangan.findMany.total = 0;
|
||||
suratKeterangan.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading surat keterangan:", error);
|
||||
suratKeterangan.findMany.data = [];
|
||||
suratKeterangan.findMany.total = 0;
|
||||
suratKeterangan.findMany.totalPages = 1;
|
||||
} finally {
|
||||
suratKeterangan.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -121,6 +147,7 @@ const suratKeterangan = proxy({
|
||||
data: null as Prisma.PelayananSuratKeteranganGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
image2: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
@@ -202,6 +229,7 @@ const suratKeterangan = proxy({
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId || "",
|
||||
image2Id: data.image2Id || "",
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
@@ -238,6 +266,7 @@ const suratKeterangan = proxy({
|
||||
name: this.form.name,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
image2Id: this.form.image2Id,
|
||||
}),
|
||||
}
|
||||
);
|
||||
@@ -307,15 +336,38 @@ const pelayananTelunjukSaktiDesa = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as Prisma.PelayananTelunjukSaktiDesaGetPayload<{
|
||||
omit: { isActive: true };
|
||||
}>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.layanan.pelayanantelunjuksaktidesa[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
pelayananTelunjukSaktiDesa.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
pelayananTelunjukSaktiDesa.findMany.loading = true; // Use the full path to access the property
|
||||
pelayananTelunjukSaktiDesa.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.desa.layanan.pelayanantelunjuksaktidesa[
|
||||
"find-many"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pelayananTelunjukSaktiDesa.findMany.data = res.data.data || [];
|
||||
pelayananTelunjukSaktiDesa.findMany.total = res.data.total || 0;
|
||||
pelayananTelunjukSaktiDesa.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load telunjuk sakti desa:", res.data?.message);
|
||||
pelayananTelunjukSaktiDesa.findMany.data = [];
|
||||
pelayananTelunjukSaktiDesa.findMany.total = 0;
|
||||
pelayananTelunjukSaktiDesa.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading telunjuk sakti desa:", error);
|
||||
pelayananTelunjukSaktiDesa.findMany.data = [];
|
||||
pelayananTelunjukSaktiDesa.findMany.total = 0;
|
||||
pelayananTelunjukSaktiDesa.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pelayananTelunjukSaktiDesa.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";
|
||||
@@ -5,10 +6,10 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(1).max(50),
|
||||
juara: z.string().min(1).max(50),
|
||||
name: z.string().min(1).max(5000),
|
||||
juara: z.string().min(1).max(5000),
|
||||
deskripsi: z.string().min(1).max(5000),
|
||||
imageId: z.string().min(1).max(50),
|
||||
imageId: z.string().min(1).max(5000),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
@@ -50,17 +51,38 @@ const penghargaanState = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PenghargaanGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.penghargaan["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
penghargaanState.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
penghargaanState.findMany.loading = true; // Use the full path to access the property
|
||||
penghargaanState.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.desa.penghargaan[
|
||||
"find-many"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
penghargaanState.findMany.data = res.data.data || [];
|
||||
penghargaanState.findMany.total = res.data.total || 0;
|
||||
penghargaanState.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load penghargaan:", res.data?.message);
|
||||
penghargaanState.findMany.data = [];
|
||||
penghargaanState.findMany.total = 0;
|
||||
penghargaanState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading penghargaan:", error);
|
||||
penghargaanState.findMany.data = [];
|
||||
penghargaanState.findMany.total = 0;
|
||||
penghargaanState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
penghargaanState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,6 +5,228 @@ import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateKategoriPengumuman = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
});
|
||||
|
||||
const defaultKategoriPengumuman = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
const category = proxy({
|
||||
create: {
|
||||
form: { ...defaultKategoriPengumuman },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateKategoriPengumuman.safeParse(category.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
category.create.loading = true;
|
||||
const res = await ApiFetch.api.desa.kategoripengumuman["create"].post(
|
||||
category.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
category.findMany.load();
|
||||
return toast.success("Data Kategori Pengumuman Berhasil Dibuat");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return toast.error("failed create");
|
||||
} finally {
|
||||
category.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as (Prisma.CategoryPengumumanGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> & {
|
||||
_count: {
|
||||
pengumumans: number;
|
||||
};
|
||||
})[],
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.kategoripengumuman["findMany"].get();
|
||||
if (res.status === 200) {
|
||||
category.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.CategoryPengumumanGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/desa/kategoripengumuman/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
category.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
category.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
category.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async delete(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
category.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/kategoripengumuman/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "Data Kategori Pengumuman berhasil dihapus"
|
||||
);
|
||||
await category.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus Data Kategori Pengumuman"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat menghapus Data Kategori Pengumuman"
|
||||
);
|
||||
} finally {
|
||||
category.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultKategoriPengumuman },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/kategoripengumuman/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori pengumuman:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateKategoriPengumuman.safeParse(category.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
category.update.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/desa/kategoripengumuman/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update data kategori pengumuman");
|
||||
await category.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal update data kategori pengumuman"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating data kategori pengumuman:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update data kategori pengumuman"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
category.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
category.update.id = "";
|
||||
category.update.form = { ...defaultKategoriPengumuman };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const templateFormPengumuman = z.object({
|
||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
@@ -12,33 +234,15 @@ const templateFormPengumuman = z.object({
|
||||
categoryPengumumanId: z.string().nonempty(),
|
||||
});
|
||||
|
||||
const category = proxy({
|
||||
findMany: {
|
||||
data: null as
|
||||
| null
|
||||
| Prisma.CategoryPengumumanGetPayload<{ omit: { isActive: true } }>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.pengumuman.category[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
category.findMany.data = (res.data?.data as any) ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type PengumumanForm = Prisma.PengumumanGetPayload<{
|
||||
select: {
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
content: true;
|
||||
categoryPengumumanId: true;
|
||||
};
|
||||
}>;
|
||||
const defaultForm = {
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
content: "",
|
||||
categoryPengumumanId: "",
|
||||
};
|
||||
const pengumuman = proxy({
|
||||
create: {
|
||||
form: {} as PengumumanForm,
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateFormPengumuman.safeParse(pengumuman.create.form);
|
||||
@@ -74,11 +278,35 @@ const pengumuman = proxy({
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.pengumuman["find-many"].get();
|
||||
console.log(res);
|
||||
if (res.status === 200) {
|
||||
pengumuman.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||
pengumuman.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
pengumuman.findMany.page = page;
|
||||
pengumuman.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (kategori) query.kategori = kategori;
|
||||
|
||||
const res = await ApiFetch.api.desa.pengumuman["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pengumuman.findMany.data = res.data.data ?? [];
|
||||
pengumuman.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
pengumuman.findMany.data = [];
|
||||
pengumuman.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch pengumuman paginated:", err);
|
||||
pengumuman.findMany.data = [];
|
||||
pengumuman.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pengumuman.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -112,7 +340,7 @@ const pengumuman = proxy({
|
||||
try {
|
||||
pengumuman.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/pengumuman/delete/${id}`, {
|
||||
const response = await fetch(`/api/desa/pengumuman/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -135,9 +363,9 @@ const pengumuman = proxy({
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
edit: {
|
||||
id: "",
|
||||
form: {} as PengumumanForm,
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
@@ -153,6 +381,7 @@ const pengumuman = proxy({
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
@@ -168,20 +397,21 @@ const pengumuman = proxy({
|
||||
content: data.content,
|
||||
categoryPengumumanId: data.categoryPengumumanId || "",
|
||||
};
|
||||
return data;
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal mengambil data pengumuman");
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengambil data pengumuman");
|
||||
} finally {
|
||||
pengumuman.update.loading = false;
|
||||
console.error("Error loading pengumuman:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateFormPengumuman.safeParse(pengumuman.update.form);
|
||||
const cek = templateFormPengumuman.safeParse(pengumuman.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
@@ -191,7 +421,7 @@ const pengumuman = proxy({
|
||||
}
|
||||
|
||||
try {
|
||||
pengumuman.update.loading = true;
|
||||
pengumuman.edit.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/pengumuman/${this.id}`, {
|
||||
method: "PUT",
|
||||
@@ -202,7 +432,7 @@ const pengumuman = proxy({
|
||||
judul: this.form.judul,
|
||||
deskripsi: this.form.deskripsi,
|
||||
content: this.form.content,
|
||||
categoryPengumumanId: this.form.categoryPengumumanId,
|
||||
categoryPengumumanId: this.form.categoryPengumumanId || null,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -231,9 +461,14 @@ const pengumuman = proxy({
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
pengumuman.update.loading = false;
|
||||
pengumuman.edit.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
pengumuman.edit.id = "";
|
||||
pengumuman.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
findFirst: {
|
||||
data: null as Prisma.PengumumanGetPayload<{
|
||||
|
||||
@@ -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";
|
||||
@@ -5,219 +6,470 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(1).max(50),
|
||||
deskripsi: z.string().min(1).max(5000),
|
||||
kategori: z.string().min(1).max(50),
|
||||
imageId: z.string().min(1).max(50),
|
||||
content: z.string().min(1).max(5000),
|
||||
})
|
||||
name: z.string().min(1).max(5000),
|
||||
deskripsi: z.string().min(1).max(5000),
|
||||
kategoriId: z.string().min(1).max(50),
|
||||
imageId: z.string().min(1).max(50),
|
||||
content: z.string().min(1).max(5000),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
kategori: "",
|
||||
imageId: "",
|
||||
content: "",
|
||||
}
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
kategoriId: "",
|
||||
imageId: "",
|
||||
content: "",
|
||||
};
|
||||
|
||||
const potensiDesa = proxy({
|
||||
create: {
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateForm.safeParse(potensiDesa.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
potensiDesa.create.loading = true;
|
||||
const res = await ApiFetch.api.desa.potensi["create"].post(
|
||||
potensiDesa.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
potensiDesa.findMany.load();
|
||||
return toast.success("Potensi berhasil disimpan!");
|
||||
}
|
||||
return toast.error("Gagal menyimpan potensi");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
potensiDesa.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
potensiDesa.findMany.loading = true; // Use the full path to access the property
|
||||
potensiDesa.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.desa.potensi[
|
||||
"find-many"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
potensiDesa.findMany.data = res.data.data || [];
|
||||
potensiDesa.findMany.total = res.data.total || 0;
|
||||
potensiDesa.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load potensi desa:", res.data?.message);
|
||||
potensiDesa.findMany.data = [];
|
||||
potensiDesa.findMany.total = 0;
|
||||
potensiDesa.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading potensi desa:", error);
|
||||
potensiDesa.findMany.data = [];
|
||||
potensiDesa.findMany.total = 0;
|
||||
potensiDesa.findMany.totalPages = 1;
|
||||
} finally {
|
||||
potensiDesa.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.PotensiDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategori: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/desa/potensi/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
potensiDesa.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch potensi:", res.statusText);
|
||||
potensiDesa.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching potensi:", error);
|
||||
potensiDesa.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
potensiDesa.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/potensi/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Potensi berhasil dihapus");
|
||||
await potensiDesa.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus potensi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus potensi");
|
||||
} finally {
|
||||
potensiDesa.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/potensi/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
kategoriId: data.kategoriId,
|
||||
imageId: data.imageId || "",
|
||||
content: data.content,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading potensi:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(potensiDesa.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
potensiDesa.edit.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/potensi/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsi: this.form.deskripsi,
|
||||
kategoriId: this.form.kategoriId,
|
||||
imageId: this.form.imageId,
|
||||
content: this.form.content,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update potensi");
|
||||
await potensiDesa.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update potensi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating potensi:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update potensi"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
potensiDesa.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
potensiDesa.edit.id = "";
|
||||
potensiDesa.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const templateKategoriPotensi = z.object({
|
||||
nama: z.string().min(1, "Nama harus diisi"),
|
||||
});
|
||||
|
||||
const defaultKategoriPotensi = {
|
||||
nama: "",
|
||||
};
|
||||
|
||||
const kategoriPotensi = proxy({
|
||||
create: {
|
||||
form: { ...defaultKategoriPotensi },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateKategoriPotensi.safeParse(
|
||||
kategoriPotensi.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kategoriPotensi.create.loading = true;
|
||||
const res = await ApiFetch.api.desa.kategoripotensi["create"].post(
|
||||
kategoriPotensi.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
kategoriPotensi.findMany.load();
|
||||
return toast.success("Data Kategori Potensi Berhasil Dibuat");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return toast.error("failed create");
|
||||
} finally {
|
||||
kategoriPotensi.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as Prisma.KategoriPotensiGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[],
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.kategoripotensi["findMany"].get();
|
||||
if (res.status === 200) {
|
||||
kategoriPotensi.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KategoriPotensiGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/desa/kategoripotensi/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kategoriPotensi.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
kategoriPotensi.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
kategoriPotensi.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async delete(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
kategoriPotensi.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/kategoripotensi/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "Data Kategori Potensi berhasil dihapus"
|
||||
);
|
||||
await kategoriPotensi.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus Data Kategori Potensi"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus Data Kategori Potensi");
|
||||
} finally {
|
||||
kategoriPotensi.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultKategoriPotensi },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/kategoripotensi/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
nama: data.nama,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori potensi:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateKategoriPotensi.safeParse(
|
||||
kategoriPotensi.update.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
kategoriPotensi.update.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/kategoripotensi/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nama: this.form.nama,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update data kategori potensi");
|
||||
await kategoriPotensi.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal update data kategori potensi"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating data kategori potensi:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update data kategori potensi"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
kategoriPotensi.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
kategoriPotensi.update.id = "";
|
||||
kategoriPotensi.update.form = { ...defaultKategoriPotensi };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const potensiDesaState = proxy({
|
||||
create: {
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateForm.safeParse(potensiDesaState.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
potensiDesaState.create.loading = true;
|
||||
const res = await ApiFetch.api.desa.potensi["create"].post(potensiDesaState.create.form);
|
||||
if (res.status === 200) {
|
||||
potensiDesaState.findMany.load();
|
||||
return toast.success("Potensi berhasil disimpan!");
|
||||
}
|
||||
return toast.error("Gagal menyimpan potensi");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
potensiDesaState.create.loading = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PotensiDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
}
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.potensi["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
potensiDesaState.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
}
|
||||
},
|
||||
findUnique: {
|
||||
data: null as
|
||||
| Prisma.PotensiDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
}
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/desa/potensi/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
potensiDesaState.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error('Failed to fetch potensi:', res.statusText);
|
||||
potensiDesaState.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching potensi:', error);
|
||||
potensiDesaState.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
potensiDesaState.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/potensi/del/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Potensi berhasil dihapus");
|
||||
await potensiDesaState.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus potensi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus potensi");
|
||||
} finally {
|
||||
potensiDesaState.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
potensiDesa,
|
||||
kategoriPotensi,
|
||||
});
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/potensi/${id}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
kategori: data.kategori,
|
||||
imageId: data.imageId || "",
|
||||
content: data.content,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading potensi:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(potensiDesaState.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
potensiDesaState.edit.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/potensi/${this.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsi: this.form.deskripsi,
|
||||
kategori: this.form.kategori,
|
||||
imageId: this.form.imageId,
|
||||
content: this.form.content,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update potensi");
|
||||
await potensiDesaState.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update potensi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating potensi:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update potensi");
|
||||
return false;
|
||||
} finally {
|
||||
potensiDesaState.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
potensiDesaState.edit.id = "";
|
||||
potensiDesaState.edit.form = { ...defaultForm };
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default potensiDesaState
|
||||
|
||||
|
||||
export default potensiDesaState;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
|
||||
// ========================================= SEJARAH DESA ========================================= //
|
||||
const sejarahDesaForm = z.object({
|
||||
@@ -106,16 +108,13 @@ const sejarahDesa = proxy({
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/desa/profile/sejarah/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
);
|
||||
const response = await fetch(`/api/desa/profile/sejarah/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
@@ -409,16 +408,13 @@ const lambangDesa = proxy({
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/desa/profile/lambang/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
);
|
||||
const response = await fetch(`/api/desa/profile/lambang/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
@@ -588,14 +584,11 @@ const maskotDesa = proxy({
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/desa/profile/maskot/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
);
|
||||
const response = await fetch(`/api/desa/profile/maskot/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
@@ -818,12 +811,247 @@ const profilPerbekel = proxy({
|
||||
},
|
||||
});
|
||||
|
||||
//========================================= MANTAN PERBEKEL ========================================= //
|
||||
const mantanPerbekelForm = z.object({
|
||||
nama: z.string().min(3, "Nama minimal 3 karakter"),
|
||||
daerah: z.string().min(3, "Daerah minimal 3 karakter"),
|
||||
periode: z.string().min(3, "Periode minimal 3 karakter"),
|
||||
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||
});
|
||||
|
||||
const mantanPerbekelDefaultForm = {
|
||||
nama: "",
|
||||
daerah: "",
|
||||
periode: "",
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
const mantanPerbekel = proxy({
|
||||
create: {
|
||||
form: { ...mantanPerbekelDefaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = mantanPerbekelForm.safeParse(mantanPerbekel.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
mantanPerbekel.create.loading = true;
|
||||
const res = await ApiFetch.api.desa.mantanperbekel["create"].post(
|
||||
mantanPerbekel.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
mantanPerbekel.findMany.load();
|
||||
return toast.success("Foto berhasil disimpan!");
|
||||
}
|
||||
return toast.error("Gagal menyimpan foto");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
mantanPerbekel.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
mantanPerbekel.create.form = { ...mantanPerbekelDefaultForm };
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PerbekelDariMasaKeMasaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
mantanPerbekel.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
mantanPerbekel.findMany.page = page;
|
||||
mantanPerbekel.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.desa.mantanperbekel["findMany"].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
mantanPerbekel.findMany.data = res.data.data ?? [];
|
||||
mantanPerbekel.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
mantanPerbekel.findMany.data = [];
|
||||
mantanPerbekel.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch mantan perbekel paginated:", err);
|
||||
mantanPerbekel.findMany.data = [];
|
||||
mantanPerbekel.findMany.totalPages = 1;
|
||||
} finally {
|
||||
mantanPerbekel.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.PerbekelDariMasaKeMasaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/desa/mantanperbekel/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
mantanPerbekel.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch mantan perbekel:", res.statusText);
|
||||
mantanPerbekel.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching mantan perbekel:", error);
|
||||
mantanPerbekel.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
try {
|
||||
mantanPerbekel.delete.loading = true;
|
||||
const response = await fetch(`/api/desa/mantanperbekel/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const result = await response.json();
|
||||
if (response.ok) {
|
||||
toast.success(result.message || "Mantan perbekel berhasil dihapus");
|
||||
await mantanPerbekel.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result.message || "Gagal menghapus mantan perbekel");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus mantan perbekel");
|
||||
} finally {
|
||||
mantanPerbekel.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...mantanPerbekelDefaultForm },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(`/api/desa/mantanperbekel/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
nama: data.nama,
|
||||
daerah: data.daerah,
|
||||
periode: data.periode,
|
||||
imageId: data.imageId || "",
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading foto:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = mantanPerbekelForm.safeParse(mantanPerbekel.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
mantanPerbekel.update.loading = true;
|
||||
const response = await fetch(`/api/desa/mantanperbekel/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nama: this.form.nama,
|
||||
daerah: this.form.daerah,
|
||||
periode: this.form.periode,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(result.message || "Mantan perbekel berhasil diupdate");
|
||||
await mantanPerbekel.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal mengupdate mantan perbekel");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating mantan perbekel:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Gagal mengupdate mantan perbekel"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
mantanPerbekel.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
mantanPerbekel.update.id = "";
|
||||
mantanPerbekel.update.form = { ...mantanPerbekelDefaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const stateProfileDesa = proxy({
|
||||
lambangDesa,
|
||||
maskotDesa,
|
||||
profilPerbekel,
|
||||
visiMisiDesa,
|
||||
sejarahDesa,
|
||||
mantanPerbekel,
|
||||
});
|
||||
|
||||
export default stateProfileDesa;
|
||||
|
||||
@@ -5,176 +5,190 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateJumlahPendudukMiskin = z.object({
|
||||
year: z.number().min(1, "Data tahun harus diisi"),
|
||||
totalPoorPopulation: z.number().min(1, "Data total penduduk miskin harus diisi"),
|
||||
year: z.number().min(1, "Data tahun harus diisi"),
|
||||
totalPoorPopulation: z
|
||||
.number()
|
||||
.min(1, "Data total penduduk miskin harus diisi"),
|
||||
});
|
||||
|
||||
type JumlahPendudukMiskin = Prisma.GrafikJumlahPendudukMiskinGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
year: true;
|
||||
totalPoorPopulation: true;
|
||||
};
|
||||
select: {
|
||||
id: true;
|
||||
year: true;
|
||||
totalPoorPopulation: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const defaultForm: Omit<JumlahPendudukMiskin, 'id'> & { id?: string } = {
|
||||
year: 0,
|
||||
totalPoorPopulation: 0,
|
||||
const defaultForm: Omit<JumlahPendudukMiskin, "id"> & { id?: string } = {
|
||||
year: 0,
|
||||
totalPoorPopulation: 0,
|
||||
};
|
||||
|
||||
const jumlahPendudukMiskin = proxy({
|
||||
create: {
|
||||
form: defaultForm,
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateJumlahPendudukMiskin.safeParse(
|
||||
jumlahPendudukMiskin.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
jumlahPendudukMiskin.create.loading = true;
|
||||
const res = await ApiFetch.api.ekonomi.jumlahpendudukmiskin[
|
||||
"create"
|
||||
].post(jumlahPendudukMiskin.create.form);
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
jumlahPendudukMiskin.create.form = {
|
||||
year: 0,
|
||||
totalPoorPopulation: 0,
|
||||
};
|
||||
jumlahPendudukMiskin.findMany.load();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
jumlahPendudukMiskin.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.GrafikJumlahPendudukMiskinGetPayload<{
|
||||
select: { id: true; year: true; totalPoorPopulation: true; };
|
||||
}>[]
|
||||
| null,
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.ekonomi.jumlahpendudukmiskin[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
jumlahPendudukMiskin.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.GrafikJumlahPendudukMiskinGetPayload<{
|
||||
select: { id: true; year: true; totalPoorPopulation: true; };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/ekonomi/jumlahpendudukmiskin/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
jumlahPendudukMiskin.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
jumlahPendudukMiskin.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading grafik jumlah penduduk miskin:", error);
|
||||
jumlahPendudukMiskin.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: {...defaultForm},
|
||||
loading: false,
|
||||
async byId() {
|
||||
// Method implementation if needed
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
const cek = templateJumlahPendudukMiskin.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues.map((v) => (v.path as string[]).join(".")).join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/ekonomi/jumlahpendudukmiskin/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (!response.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
toast.success("Berhasil update data!");
|
||||
await jumlahPendudukMiskin.findMany.load();
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Error update data grafik jumlah penduduk miskin:", error);
|
||||
toast.error("Gagal update data grafik jumlah penduduk miskin");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
jumlahPendudukMiskin.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/ekonomi/jumlahpendudukmiskin/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Grafik jumlah penduduk miskin berhasil dihapus");
|
||||
await jumlahPendudukMiskin.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus grafik jumlah penduduk miskin");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete grafik jumlah penduduk miskin:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus grafik jumlah penduduk miskin");
|
||||
} finally {
|
||||
jumlahPendudukMiskin.delete.loading = false;
|
||||
}
|
||||
},
|
||||
create: {
|
||||
form: defaultForm,
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateJumlahPendudukMiskin.safeParse(
|
||||
jumlahPendudukMiskin.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
})
|
||||
export default jumlahPendudukMiskin
|
||||
|
||||
|
||||
try {
|
||||
jumlahPendudukMiskin.create.loading = true;
|
||||
const res = await ApiFetch.api.ekonomi.jumlahpendudukmiskin[
|
||||
"create"
|
||||
].post(jumlahPendudukMiskin.create.form);
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
jumlahPendudukMiskin.create.form = {
|
||||
year: 0,
|
||||
totalPoorPopulation: 0,
|
||||
};
|
||||
jumlahPendudukMiskin.findMany.load();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
jumlahPendudukMiskin.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.GrafikJumlahPendudukMiskinGetPayload<{
|
||||
select: { id: true; year: true; totalPoorPopulation: true };
|
||||
}>[]
|
||||
| null,
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.ekonomi.jumlahpendudukmiskin[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
jumlahPendudukMiskin.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.GrafikJumlahPendudukMiskinGetPayload<{
|
||||
select: { id: true; year: true; totalPoorPopulation: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/ekonomi/jumlahpendudukmiskin/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
jumlahPendudukMiskin.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
jumlahPendudukMiskin.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading grafik jumlah penduduk miskin:", error);
|
||||
jumlahPendudukMiskin.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async byId() {
|
||||
// Method implementation if needed
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
const cek = templateJumlahPendudukMiskin.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => (v.path as string[]).join("."))
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/ekonomi/jumlahpendudukmiskin/${id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
);
|
||||
const result = await response.json();
|
||||
if (!response.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
toast.success("Berhasil update data!");
|
||||
await jumlahPendudukMiskin.findMany.load();
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Error update data grafik jumlah penduduk miskin:",
|
||||
error
|
||||
);
|
||||
toast.error("Gagal update data grafik jumlah penduduk miskin");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
jumlahPendudukMiskin.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/ekonomi/jumlahpendudukmiskin/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "Grafik jumlah penduduk miskin berhasil dihapus"
|
||||
);
|
||||
await jumlahPendudukMiskin.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus grafik jumlah penduduk miskin"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete grafik jumlah penduduk miskin:", error);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat menghapus grafik jumlah penduduk miskin"
|
||||
);
|
||||
} finally {
|
||||
jumlahPendudukMiskin.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
export default jumlahPendudukMiskin;
|
||||
|
||||
@@ -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";
|
||||
@@ -55,10 +56,34 @@ const desaDigitalState = proxy({
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.inovasi.desadigital["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
desaDigitalState.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
desaDigitalState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
desaDigitalState.findMany.page = page;
|
||||
desaDigitalState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.inovasi.desadigital["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
desaDigitalState.findMany.data = res.data.data ?? [];
|
||||
desaDigitalState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
desaDigitalState.findMany.data = [];
|
||||
desaDigitalState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch desa digital paginated:", err);
|
||||
desaDigitalState.findMany.data = [];
|
||||
desaDigitalState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
desaDigitalState.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";
|
||||
@@ -55,10 +56,34 @@ const infoTeknoState = proxy({
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.inovasi.infotekno["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
infoTeknoState.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
infoTeknoState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
infoTeknoState.findMany.page = page;
|
||||
infoTeknoState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.inovasi.infotekno["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
infoTeknoState.findMany.data = res.data.data ?? [];
|
||||
infoTeknoState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
infoTeknoState.findMany.data = [];
|
||||
infoTeknoState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch info teknologi paginated:", err);
|
||||
infoTeknoState.findMany.data = [];
|
||||
infoTeknoState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
infoTeknoState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -6,12 +6,11 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(1, "Nama minimal 1 karakter"),
|
||||
tahun: z.number().min(4, "Tahun minimal 4 karakter"),
|
||||
slug: z.string().min(1, "Deskripsi singkat minimal 1 karakter"),
|
||||
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||
kolaborator: z.string().min(1, "Kolaborator minimal 1 karakter"),
|
||||
imageId: z.string().min(1, "Image ID minimal 1 karakter"),
|
||||
name: z.string().min(1, "Nama kolaborasi inovasi harus diisi"),
|
||||
tahun: z.number().min(1900, "Tahun tidak valid").max(new Date().getFullYear() + 1, "Tahun tidak boleh lebih dari tahun depan"),
|
||||
slug: z.string().min(1, "Slug harus dihasilkan otomatis"),
|
||||
deskripsi: z.string().min(1, "Deskripsi harus diisi"),
|
||||
kolaborator: z.string().min(1, "Kolaborator harus diisi"),
|
||||
})
|
||||
|
||||
const defaultForm = {
|
||||
@@ -20,7 +19,6 @@ const defaultForm = {
|
||||
slug: "",
|
||||
deskripsi: "",
|
||||
kolaborator: "",
|
||||
imageId: "",
|
||||
}
|
||||
|
||||
const kolaborasiInovasiState = proxy({
|
||||
@@ -28,27 +26,37 @@ const kolaborasiInovasiState = proxy({
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateForm.safeParse(kolaborasiInovasiState.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate form
|
||||
const validation = templateForm.safeParse(kolaborasiInovasiState.create.form);
|
||||
if (!validation.success) {
|
||||
const errorMessages = validation.error.issues
|
||||
.map(issue => `- ${issue.path.join('.')}: ${issue.message}`)
|
||||
.join('\n');
|
||||
return toast.error(`Validasi gagal:\n${errorMessages}`);
|
||||
}
|
||||
|
||||
kolaborasiInovasiState.create.loading = true;
|
||||
|
||||
const res = await ApiFetch.api.inovasi.kolaborasiinovasi["create"].post(
|
||||
kolaborasiInovasiState.create.form
|
||||
);
|
||||
|
||||
if (res.status === 200) {
|
||||
kolaborasiInovasiState.findMany.load();
|
||||
return toast.success("success create");
|
||||
await kolaborasiInovasiState.findMany.load();
|
||||
return { success: true, data: res.data };
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
console.error('Create failed:', res);
|
||||
toast.error(res.data?.message || "Gagal menyimpan data");
|
||||
return { success: false, error: res.data };
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
console.error('Error in create:', error);
|
||||
toast.error("Terjadi kesalahan saat menyimpan data");
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
};
|
||||
} finally {
|
||||
kolaborasiInovasiState.create.loading = false;
|
||||
}
|
||||
@@ -60,13 +68,21 @@ const kolaborasiInovasiState = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => {
|
||||
// Change to arrow function
|
||||
kolaborasiInovasiState.findMany.loading = true; // Use the full path to access the property
|
||||
search: "",
|
||||
year: "",
|
||||
load: async (page = 1, limit = 10, search = "", year?: string) => {
|
||||
kolaborasiInovasiState.findMany.loading = true;
|
||||
kolaborasiInovasiState.findMany.page = page;
|
||||
kolaborasiInovasiState.findMany.search = search;
|
||||
kolaborasiInovasiState.findMany.year = year || "";
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (year) query.year = year;
|
||||
|
||||
const res = await ApiFetch.api.inovasi.kolaborasiinovasi["find-many"].get({
|
||||
query: { page, limit },
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
@@ -124,7 +140,6 @@ const kolaborasiInovasiState = proxy({
|
||||
slug: data.slug,
|
||||
deskripsi: data.deskripsi,
|
||||
kolaborator: data.kolaborator,
|
||||
imageId: data.imageId,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
@@ -179,7 +194,7 @@ const kolaborasiInovasiState = proxy({
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KolaborasiInovasiGetPayload<{
|
||||
include: { image: true };
|
||||
omit: { isActive: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
|
||||
229
src/app/admin/(dashboard)/_state/inovasi/mitra-kolaborasi.ts
Normal file
229
src/app/admin/(dashboard)/_state/inovasi/mitra-kolaborasi.ts
Normal file
@@ -0,0 +1,229 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const mitraKolaborasiForm = z.object({
|
||||
name: z.string().min(1, { message: "Name is required" }),
|
||||
imageId: z.string().nonempty(),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
const mitraKolaborasi = proxy({
|
||||
create: {
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = mitraKolaborasiForm.safeParse(mitraKolaborasi.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
mitraKolaborasi.create.loading = true;
|
||||
const res = await ApiFetch.api.inovasi.mitrakolaborasi["create"].post(
|
||||
mitraKolaborasi.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
mitraKolaborasi.findMany.load();
|
||||
return toast.success("mitraKolaborasi berhasil disimpan!");
|
||||
}
|
||||
return toast.error("Gagal menyimpan mitraKolaborasi");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
mitraKolaborasi.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
mitraKolaborasi.create.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.MitraKolaborasiGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
mitraKolaborasi.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
mitraKolaborasi.findMany.page = page;
|
||||
mitraKolaborasi.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.inovasi.mitrakolaborasi["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
mitraKolaborasi.findMany.data = res.data.data ?? [];
|
||||
mitraKolaborasi.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
mitraKolaborasi.findMany.data = [];
|
||||
mitraKolaborasi.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch mitraKolaborasi paginated:", err);
|
||||
mitraKolaborasi.findMany.data = [];
|
||||
mitraKolaborasi.findMany.totalPages = 1;
|
||||
} finally {
|
||||
mitraKolaborasi.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.MitraKolaborasiGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/inovasi/mitrakolaborasi/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
mitraKolaborasi.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch mitraKolaborasi:", res.statusText);
|
||||
mitraKolaborasi.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching mitraKolaborasi:", error);
|
||||
mitraKolaborasi.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
try {
|
||||
mitraKolaborasi.delete.loading = true;
|
||||
const response = await fetch(`/api/inovasi/mitrakolaborasi/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const result = await response.json();
|
||||
if (response.ok) {
|
||||
toast.success(result.message || "mitraKolaborasi berhasil dihapus");
|
||||
await mitraKolaborasi.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result.message || "Gagal menghapus mitraKolaborasi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus mitraKolaborasi");
|
||||
} finally {
|
||||
mitraKolaborasi.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(`/api/inovasi/mitrakolaborasi/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
imageId: data.imageId,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading mitraKolaborasi:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = mitraKolaborasiForm.safeParse(mitraKolaborasi.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
mitraKolaborasi.update.loading = true;
|
||||
const response = await fetch(`/api/inovasi/mitrakolaborasi/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(result.message || "mitraKolaborasi berhasil diupdate");
|
||||
await mitraKolaborasi.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal mengupdate mitraKolaborasi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating mitraKolaborasi:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal mengupdate mitraKolaborasi"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
mitraKolaborasi.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
mitraKolaborasi.update.id = "";
|
||||
mitraKolaborasi.update.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default mitraKolaborasi;
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
//fasilitas kesehatan aja
|
||||
// Validasi form
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
@@ -61,7 +62,7 @@ const defaultForm = {
|
||||
},
|
||||
};
|
||||
|
||||
const fasilitasKesehatanState = proxy({
|
||||
const fasilitasKesehatan = proxy({
|
||||
create: {
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
@@ -86,7 +87,7 @@ const fasilitasKesehatanState = proxy({
|
||||
if (res.status === 200) {
|
||||
toast.success("Berhasil menambahkan fasilitas kesehatan");
|
||||
this.resetForm();
|
||||
await fasilitasKesehatanState.findMany.load();
|
||||
await fasilitasKesehatan.findMany.load();
|
||||
return res.data;
|
||||
}
|
||||
} catch (err: any) {
|
||||
@@ -102,7 +103,6 @@ const fasilitasKesehatanState = proxy({
|
||||
this.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.FasilitasKesehatanGetPayload<{
|
||||
@@ -156,7 +156,7 @@ const fasilitasKesehatanState = proxy({
|
||||
const res = await fetch(`/api/kesehatan/fasilitas-kesehatan/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
fasilitasKesehatanState.findUnique.data = data.data ?? null;
|
||||
fasilitasKesehatan.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
toast.error("Gagal load data fasilitas kesehatan");
|
||||
}
|
||||
@@ -176,8 +176,8 @@ const fasilitasKesehatanState = proxy({
|
||||
const result = await res.json();
|
||||
const data = result.data;
|
||||
|
||||
fasilitasKesehatanState.edit.id = data.id;
|
||||
fasilitasKesehatanState.edit.form = {
|
||||
fasilitasKesehatan.edit.id = data.id;
|
||||
fasilitasKesehatan.edit.form = {
|
||||
name: data.name,
|
||||
informasiUmum: {
|
||||
fasilitas: data.informasiumum.fasilitas,
|
||||
@@ -205,7 +205,7 @@ const fasilitasKesehatanState = proxy({
|
||||
};
|
||||
},
|
||||
async submit() {
|
||||
const cek = templateForm.safeParse(fasilitasKesehatanState.edit.form);
|
||||
const cek = templateForm.safeParse(fasilitasKesehatan.edit.form);
|
||||
if (!cek.success) {
|
||||
const errMsg = cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}: ${v.message}`)
|
||||
@@ -215,42 +215,38 @@ const fasilitasKesehatanState = proxy({
|
||||
}
|
||||
|
||||
try {
|
||||
fasilitasKesehatanState.edit.loading = true;
|
||||
fasilitasKesehatan.edit.loading = true;
|
||||
const payload = {
|
||||
name: fasilitasKesehatanState.edit.form.name,
|
||||
name: fasilitasKesehatan.edit.form.name,
|
||||
informasiUmum: {
|
||||
fasilitas:
|
||||
fasilitasKesehatanState.edit.form.informasiUmum.fasilitas,
|
||||
alamat: fasilitasKesehatanState.edit.form.informasiUmum.alamat,
|
||||
fasilitas: fasilitasKesehatan.edit.form.informasiUmum.fasilitas,
|
||||
alamat: fasilitasKesehatan.edit.form.informasiUmum.alamat,
|
||||
jamOperasional:
|
||||
fasilitasKesehatanState.edit.form.informasiUmum.jamOperasional,
|
||||
fasilitasKesehatan.edit.form.informasiUmum.jamOperasional,
|
||||
},
|
||||
layananUnggulan: {
|
||||
content: fasilitasKesehatanState.edit.form.layananUnggulan.content,
|
||||
content: fasilitasKesehatan.edit.form.layananUnggulan.content,
|
||||
},
|
||||
dokterdanTenagaMedis: {
|
||||
name: fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.name,
|
||||
name: fasilitasKesehatan.edit.form.dokterdanTenagaMedis.name,
|
||||
specialist:
|
||||
fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.specialist,
|
||||
jadwal:
|
||||
fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.jadwal,
|
||||
fasilitasKesehatan.edit.form.dokterdanTenagaMedis.specialist,
|
||||
jadwal: fasilitasKesehatan.edit.form.dokterdanTenagaMedis.jadwal,
|
||||
},
|
||||
fasilitasPendukung: {
|
||||
content:
|
||||
fasilitasKesehatanState.edit.form.fasilitasPendukung.content,
|
||||
content: fasilitasKesehatan.edit.form.fasilitasPendukung.content,
|
||||
},
|
||||
prosedurPendaftaran: {
|
||||
content:
|
||||
fasilitasKesehatanState.edit.form.prosedurPendaftaran.content,
|
||||
content: fasilitasKesehatan.edit.form.prosedurPendaftaran.content,
|
||||
},
|
||||
tarifDanLayanan: {
|
||||
layanan: fasilitasKesehatanState.edit.form.tarifDanLayanan.layanan,
|
||||
tarif: fasilitasKesehatanState.edit.form.tarifDanLayanan.tarif,
|
||||
layanan: fasilitasKesehatan.edit.form.tarifDanLayanan.layanan,
|
||||
tarif: fasilitasKesehatan.edit.form.tarifDanLayanan.tarif,
|
||||
},
|
||||
};
|
||||
|
||||
const res = await fetch(
|
||||
`/api/kesehatan/fasilitas-kesehatan/${fasilitasKesehatanState.edit.id}`,
|
||||
`/api/kesehatan/fasilitas-kesehatan/${fasilitasKesehatan.edit.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -264,7 +260,7 @@ const fasilitasKesehatanState = proxy({
|
||||
}
|
||||
|
||||
toast.success("Berhasil update fasilitas kesehatan");
|
||||
await fasilitasKesehatanState.findMany.load();
|
||||
await fasilitasKesehatan.findMany.load();
|
||||
return true;
|
||||
} catch (err) {
|
||||
toast.error(
|
||||
@@ -272,37 +268,297 @@ const fasilitasKesehatanState = proxy({
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
fasilitasKesehatanState.edit.loading = false;
|
||||
fasilitasKesehatan.edit.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
fasilitasKesehatanState.edit.id = "";
|
||||
fasilitasKesehatanState.edit.form = { ...defaultForm };
|
||||
fasilitasKesehatan.edit.id = "";
|
||||
fasilitasKesehatan.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string){
|
||||
async byId(id: string) {
|
||||
try {
|
||||
fasilitasKesehatanState.delete.loading = true;
|
||||
const res = await fetch(`/api/kesehatan/fasilitas-kesehatan/del/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
fasilitasKesehatan.delete.loading = true;
|
||||
const res = await fetch(
|
||||
`/api/kesehatan/fasilitas-kesehatan/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
}
|
||||
);
|
||||
|
||||
const result = await res.json();
|
||||
if (res.ok && result.success) {
|
||||
toast.success("Fasilitas kesehatan berhasil dihapus");
|
||||
await fasilitasKesehatanState.findMany.load();
|
||||
await fasilitasKesehatan.findMany.load();
|
||||
} else {
|
||||
toast.error(result.message || "Gagal menghapus");
|
||||
}
|
||||
} catch {
|
||||
toast.error("Terjadi kesalahan saat menghapus");
|
||||
} finally {
|
||||
fasilitasKesehatanState.delete.loading = false;
|
||||
fasilitasKesehatan.delete.loading = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
//dokter & tenaga medis
|
||||
const templateDokterForm = z.object({
|
||||
name: z.string().min(1, "Nama tidak boleh kosong"),
|
||||
specialist: z.string().min(1, "Spesialis tidak boleh kosong"),
|
||||
jadwal: z.string().min(1, "Jadwal tidak boleh kosong"),
|
||||
});
|
||||
|
||||
const defaultDokterForm = {
|
||||
name: "",
|
||||
specialist: "",
|
||||
jadwal: "",
|
||||
};
|
||||
|
||||
const dokter = proxy({
|
||||
create: {
|
||||
create: {
|
||||
form: defaultDokterForm,
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateDokterForm.safeParse(dokter.create.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
dokter.create.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.doktertenagamedis[
|
||||
"create"
|
||||
].post(dokter.create.create.form);
|
||||
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
dokter.create.create.form = { ...defaultDokterForm };
|
||||
dokter.findMany.load();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
toast.error("failed create");
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
return null;
|
||||
} finally {
|
||||
dokter.create.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.DokterdanTenagaMedisGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
dokter.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
dokter.findMany.page = page;
|
||||
dokter.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.doktertenagamedis[
|
||||
"findMany"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
dokter.findMany.data = res.data.data ?? [];
|
||||
dokter.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
dokter.findMany.data = [];
|
||||
dokter.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch dokter tenaga medis paginated:", err);
|
||||
dokter.findMany.data = [];
|
||||
dokter.findMany.totalPages = 1;
|
||||
} finally {
|
||||
dokter.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.DokterdanTenagaMedisGetPayload<{
|
||||
omit: { isActive: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/doktertenagamedis/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
dokter.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error(
|
||||
"Failed to fetch dokter dan tenaga medis",
|
||||
res.statusText
|
||||
);
|
||||
dokter.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching dokter dan tenaga medis", error);
|
||||
dokter.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultDokterForm },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/doktertenagamedis/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
specialist: data.specialist,
|
||||
jadwal: data.jadwal,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading dokter dan tenaga medis:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
const formData = {
|
||||
name: this.form.name,
|
||||
specialist: this.form.specialist,
|
||||
jadwal: this.form.jadwal,
|
||||
};
|
||||
|
||||
const cek = templateDokterForm.safeParse(formData);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await fetch(`/api/kesehatan/doktertenagamedis/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
|
||||
if (!res.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
|
||||
toast.success("Berhasil update data!");
|
||||
await dokter.findMany.load();
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Update error:", error);
|
||||
toast.error("Gagal update data dokter dan tenaga medis");
|
||||
throw error;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) {
|
||||
return toast.warn("ID tidak valid");
|
||||
}
|
||||
try {
|
||||
dokter.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/kesehatan/doktertenagamedis/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "Dokter dan tenaga medis berhasil dihapus"
|
||||
);
|
||||
await dokter.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus dokter dan tenaga medis"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus dokter dan tenaga medis");
|
||||
} finally {
|
||||
dokter.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const fasilitasKesehatanState = proxy({
|
||||
fasilitasKesehatan,
|
||||
dokter
|
||||
});
|
||||
|
||||
export default fasilitasKesehatanState;
|
||||
|
||||
@@ -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";
|
||||
@@ -5,20 +6,19 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateGrafikKepuasan = z.object({
|
||||
label: z.string().min(2, "Label harus diisi"),
|
||||
jumlah: z.string().min(1, "Jumlah harus diisi"),
|
||||
nama: z.string().min(2, "Nama harus diisi"),
|
||||
tanggal: z.string().min(1, "Tanggal harus diisi"),
|
||||
jenisKelamin: z.string().min(1, "Jenis kelamin harus diisi"),
|
||||
alamat: z.string().min(1, "Alamat harus diisi"),
|
||||
penyakit: z.string().min(1, "Penyakit harus diisi"),
|
||||
});
|
||||
|
||||
type GrafikKepuasan = Prisma.GrafikKepuasanGetPayload<{
|
||||
select: {
|
||||
label: true;
|
||||
jumlah: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const defaultForm: GrafikKepuasan = {
|
||||
label: "",
|
||||
jumlah: ""
|
||||
const defaultForm = {
|
||||
nama: "",
|
||||
tanggal: "",
|
||||
jenisKelamin: "",
|
||||
alamat: "",
|
||||
penyakit: "",
|
||||
};
|
||||
|
||||
const grafikkepuasan = proxy({
|
||||
@@ -36,16 +36,15 @@ const grafikkepuasan = proxy({
|
||||
}
|
||||
try {
|
||||
grafikkepuasan.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.grafikkepuasan["create"].post(grafikkepuasan.create.form);
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.grafikkepuasan["create"].post(
|
||||
grafikkepuasan.create.form
|
||||
);
|
||||
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
const id = res.data?.data;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
grafikkepuasan.create.form = {
|
||||
label: "",
|
||||
jumlah: "",
|
||||
};
|
||||
grafikkepuasan.create.form = { ...defaultForm };
|
||||
grafikkepuasan.findMany.load();
|
||||
return id;
|
||||
}
|
||||
@@ -62,21 +61,49 @@ const grafikkepuasan = proxy({
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.GrafikKepuasanGetPayload<{ omit: { isActive: true } }>[]
|
||||
| Prisma.GrafikKepuasanGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.grafikkepuasan[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
grafikkepuasan.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
grafikkepuasan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
grafikkepuasan.findMany.page = page;
|
||||
grafikkepuasan.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.grafikkepuasan[
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
grafikkepuasan.findMany.data = res.data.data ?? [];
|
||||
grafikkepuasan.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
grafikkepuasan.findMany.data = [];
|
||||
grafikkepuasan.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch berita paginated:", err);
|
||||
grafikkepuasan.findMany.data = [];
|
||||
grafikkepuasan.findMany.totalPages = 1;
|
||||
} finally {
|
||||
grafikkepuasan.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.GrafikKepuasanGetPayload<{
|
||||
omit: { isActive: true }
|
||||
}> | null,
|
||||
omit: { isActive: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/grafikkepuasan/${id}`);
|
||||
@@ -95,88 +122,137 @@ const grafikkepuasan = proxy({
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: {...defaultForm},
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async byId() {
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
const cek = templateGrafikKepuasan.safeParse(grafikkepuasan.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
this.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/grafikkepuasan/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
toast.success("Berhasil update data!");
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/grafikkepuasan/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ Optional: refresh list kalau kamu langsung ke halaman list
|
||||
await grafikkepuasan.findMany.load();
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Error update data:", error);
|
||||
toast.error("Gagal update data grafik kepuasan");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) {
|
||||
return toast.warn("ID tidak valid");
|
||||
}
|
||||
try {
|
||||
grafikkepuasan.delete.loading = true;
|
||||
const result = await response.json();
|
||||
|
||||
const response = await fetch(`/api/kesehatan/grafikkepuasan/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "Grafik kepuasan berhasil dihapus"
|
||||
);
|
||||
await grafikkepuasan.findMany.load(); // refresh list
|
||||
} else {
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
nama: data.nama,
|
||||
tanggal: data.tanggal,
|
||||
jenisKelamin: data.jenisKelamin,
|
||||
alamat: data.alamat,
|
||||
penyakit: data.penyakit,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading grafik kepuasan:", error);
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus grafik kepuasan"
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus grafik kepuasan");
|
||||
} finally {
|
||||
grafikkepuasan.delete.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
const formData = {
|
||||
nama: this.form.nama,
|
||||
tanggal: this.form.tanggal,
|
||||
jenisKelamin: this.form.jenisKelamin,
|
||||
alamat: this.form.alamat,
|
||||
penyakit: this.form.penyakit,
|
||||
};
|
||||
|
||||
const cek = templateGrafikKepuasan.safeParse(formData);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await fetch(`/api/kesehatan/grafikkepuasan/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
|
||||
if (!res.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
|
||||
toast.success("Berhasil update data!");
|
||||
await grafikkepuasan.findMany.load();
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Update error:", error);
|
||||
toast.error("Gagal update data grafik kepuasan");
|
||||
throw error;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) {
|
||||
return toast.warn("ID tidak valid");
|
||||
}
|
||||
try {
|
||||
grafikkepuasan.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/kesehatan/grafikkepuasan/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Grafik kepuasan berhasil dihapus");
|
||||
await grafikkepuasan.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus grafik kepuasan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus grafik kepuasan");
|
||||
} finally {
|
||||
grafikkepuasan.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default grafikkepuasan;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templatePersentase = z.object({
|
||||
//persentase kelahiran kematian
|
||||
|
||||
const templatePersentaseKelahiran = z.object({
|
||||
tahun: z.string().min(4, "Tahun harus diisi"),
|
||||
kematianKasar: z.string().min(1, "Kematian kasar harus diisi"),
|
||||
kelahiranKasar: z.string().min(1, "Kelahiran kasar harus diisi"),
|
||||
@@ -13,18 +16,14 @@ const templatePersentase = z.object({
|
||||
|
||||
type Persentase = Prisma.DataKematian_KelahiranGetPayload<{
|
||||
select: {
|
||||
tahun: true;
|
||||
kematianKasar: true;
|
||||
kelahiranKasar: true;
|
||||
kematianBayi: true;
|
||||
kematianId: true;
|
||||
kelahiranId: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const defaultForm: Persentase = {
|
||||
tahun: "",
|
||||
kematianKasar: "",
|
||||
kelahiranKasar: "",
|
||||
kematianBayi: "",
|
||||
kematianId: "",
|
||||
kelahiranId: "",
|
||||
};
|
||||
|
||||
const persentasekelahiran = proxy({
|
||||
@@ -32,7 +31,9 @@ const persentasekelahiran = proxy({
|
||||
form: defaultForm,
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templatePersentase.safeParse(persentasekelahiran.create.form);
|
||||
const cek = templatePersentaseKelahiran.safeParse(
|
||||
persentasekelahiran.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
@@ -47,7 +48,7 @@ const persentasekelahiran = proxy({
|
||||
].post(persentasekelahiran.create.form);
|
||||
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
const id = res.data?.data;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
persentasekelahiran.create.form = { ...defaultForm };
|
||||
@@ -69,21 +70,51 @@ const persentasekelahiran = proxy({
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.DataKematian_KelahiranGetPayload<{
|
||||
omit: { isActive: true };
|
||||
include: {
|
||||
kematian: true;
|
||||
kelahiran: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.persentasekelahiran[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
persentasekelahiran.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
persentasekelahiran.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
persentasekelahiran.findMany.page = page;
|
||||
persentasekelahiran.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.persentasekelahiran[
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
persentasekelahiran.findMany.data = res.data.data ?? [];
|
||||
persentasekelahiran.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
persentasekelahiran.findMany.data = [];
|
||||
persentasekelahiran.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch berita paginated:", err);
|
||||
persentasekelahiran.findMany.data = [];
|
||||
persentasekelahiran.findMany.totalPages = 1;
|
||||
} finally {
|
||||
persentasekelahiran.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.DataKematian_KelahiranGetPayload<{
|
||||
omit: { isActive: true };
|
||||
include: {
|
||||
kematian: true;
|
||||
kelahiran: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
@@ -114,13 +145,11 @@ const persentasekelahiran = proxy({
|
||||
}
|
||||
|
||||
const formData = {
|
||||
tahun: this.form.tahun,
|
||||
kematianKasar: this.form.kematianKasar,
|
||||
kelahiranKasar: this.form.kelahiranKasar,
|
||||
kematianBayi: this.form.kematianBayi,
|
||||
kematianId: this.form.kematianId,
|
||||
kelahiranId: this.form.kelahiranId,
|
||||
};
|
||||
|
||||
const cek = templatePersentase.safeParse(formData);
|
||||
const cek = templatePersentaseKelahiran.safeParse(formData);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
@@ -197,4 +226,521 @@ const persentasekelahiran = proxy({
|
||||
},
|
||||
});
|
||||
|
||||
export default persentasekelahiran;
|
||||
// data kelahiran
|
||||
|
||||
const templateKelahiran = z.object({
|
||||
nama: z.string().min(1, "Nama harus diisi"),
|
||||
tanggal: z.string().min(4, "Tahun harus diisi"),
|
||||
jenisKelamin: z.string().min(1, "Jenis kelamin harus diisi"),
|
||||
alamat: z.string().min(1, "Alamat harus diisi"),
|
||||
});
|
||||
|
||||
const defaultKelahiran = {
|
||||
nama: "",
|
||||
tanggal: "",
|
||||
jenisKelamin: "",
|
||||
alamat: "",
|
||||
};
|
||||
|
||||
const kelahiran = proxy({
|
||||
create: {
|
||||
form: { ...defaultKelahiran }, // ✅ ini kunci fix-nya
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateKelahiran.safeParse(kelahiran.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kelahiran.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.kelahiran["create"].post(
|
||||
kelahiran.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
kelahiran.findMany.load();
|
||||
return toast.success("Kelahiran berhasil disimpan!");
|
||||
}
|
||||
|
||||
return toast.error("Gagal menyimpan kelahiran");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
kelahiran.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
kelahiran.create.form = { ...defaultKelahiran };
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.KelahiranGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
kelahiran.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
kelahiran.findMany.page = page;
|
||||
kelahiran.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.kelahiran["findMany"].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kelahiran.findMany.data = res.data.data ?? [];
|
||||
kelahiran.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
kelahiran.findMany.data = [];
|
||||
kelahiran.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch kelahiran paginated:", err);
|
||||
kelahiran.findMany.data = [];
|
||||
kelahiran.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kelahiran.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KelahiranGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/kelahiran/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kelahiran.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch kelahiran:", res.statusText);
|
||||
kelahiran.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching kelahiran:", error);
|
||||
kelahiran.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
kelahiran.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/kesehatan/kelahiran/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Kelahiran berhasil dihapus");
|
||||
await kelahiran.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus kelahiran");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus kelahiran");
|
||||
} finally {
|
||||
kelahiran.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultKelahiran },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/kelahiran/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
nama: data.nama,
|
||||
tanggal: data.tanggal,
|
||||
jenisKelamin: data.jenisKelamin,
|
||||
alamat: data.alamat,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading data kelahiran:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateKelahiran.safeParse(kelahiran.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
kelahiran.edit.loading = true;
|
||||
|
||||
const response = await fetch(`/api/kesehatan/kelahiran/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nama: this.form.nama,
|
||||
tanggal: this.form.tanggal,
|
||||
jenisKelamin: this.form.jenisKelamin,
|
||||
alamat: this.form.alamat,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update data kelahiran");
|
||||
await kelahiran.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update data kelahiran");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating data kelahiran:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update data kelahiran"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
kelahiran.edit.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
kelahiran.edit.id = "";
|
||||
kelahiran.edit.form = { ...defaultKelahiran };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
// data kematian
|
||||
|
||||
const templateKematian = z.object({
|
||||
nama: z.string().min(1, "Nama harus diisi"),
|
||||
tanggal: z.string().min(4, "Tahun harus diisi"),
|
||||
jenisKelamin: z.string().min(1, "Jenis kelamin harus diisi"),
|
||||
alamat: z.string().min(1, "Alamat harus diisi"),
|
||||
penyebab: z.string().min(1, "Penyebab harus diisi"),
|
||||
});
|
||||
|
||||
const defaultKematian = {
|
||||
nama: "",
|
||||
tanggal: "",
|
||||
jenisKelamin: "",
|
||||
alamat: "",
|
||||
penyebab: "",
|
||||
};
|
||||
|
||||
const kematian = proxy({
|
||||
create: {
|
||||
form: { ...defaultKematian }, // ✅ ini kunci fix-nya
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateKematian.safeParse(kematian.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kematian.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.kematian["create"].post(
|
||||
kematian.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
kematian.findMany.load();
|
||||
return toast.success("Kematian berhasil disimpan!");
|
||||
}
|
||||
|
||||
return toast.error("Gagal menyimpan kematian");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
kematian.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
kematian.create.form = { ...defaultKematian };
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.KematianGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
kematian.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
kematian.findMany.page = page;
|
||||
kematian.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.kematian["findMany"].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kematian.findMany.data = res.data.data ?? [];
|
||||
kematian.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
kematian.findMany.data = [];
|
||||
kematian.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch kematian paginated:", err);
|
||||
kematian.findMany.data = [];
|
||||
kematian.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kematian.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KematianGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/kematian/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kematian.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch kematian:", res.statusText);
|
||||
kematian.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching kematian:", error);
|
||||
kematian.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
kematian.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/kesehatan/kematian/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Kematian berhasil dihapus");
|
||||
await kematian.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus kematian");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus kematian");
|
||||
} finally {
|
||||
kematian.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultKematian },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/kematian/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
nama: data.nama,
|
||||
tanggal: data.tanggal,
|
||||
jenisKelamin: data.jenisKelamin,
|
||||
alamat: data.alamat,
|
||||
penyebab: data.penyebab,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading data kematian:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateKematian.safeParse(kematian.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
kematian.edit.loading = true;
|
||||
|
||||
const response = await fetch(`/api/kesehatan/kematian/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nama: this.form.nama,
|
||||
tanggal: this.form.tanggal,
|
||||
jenisKelamin: this.form.jenisKelamin,
|
||||
alamat: this.form.alamat,
|
||||
penyebab: this.form.penyebab,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update data kematian");
|
||||
await kematian.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update data kematian");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating data kematian:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update data kematian"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
kematian.edit.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
kematian.edit.id = "";
|
||||
kematian.edit.form = { ...defaultKematian };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const persentaseKelahiranKematian = proxy({
|
||||
persentasekelahiran,
|
||||
kelahiran,
|
||||
kematian
|
||||
});
|
||||
|
||||
export default persentaseKelahiranKematian;
|
||||
|
||||
@@ -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";
|
||||
@@ -20,17 +21,41 @@ const defaultForm = {
|
||||
|
||||
const infoWabahPenyakit = proxy({
|
||||
findMany: {
|
||||
data: [] as Prisma.InfoWabahPenyakitGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.infowabahpenyakit[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
infoWabahPenyakit.findMany.data = res.data?.data ?? [];
|
||||
data: null as
|
||||
| Prisma.InfoWabahPenyakitGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
infoWabahPenyakit.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
infoWabahPenyakit.findMany.page = page;
|
||||
infoWabahPenyakit.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.infowabahpenyakit["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
infoWabahPenyakit.findMany.data = res.data.data ?? [];
|
||||
infoWabahPenyakit.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
infoWabahPenyakit.findMany.data = [];
|
||||
infoWabahPenyakit.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch info wabah penyakit paginated:", err);
|
||||
infoWabahPenyakit.findMany.data = [];
|
||||
infoWabahPenyakit.findMany.totalPages = 1;
|
||||
} finally {
|
||||
infoWabahPenyakit.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";
|
||||
@@ -5,204 +6,241 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
imageId: z.string().nonempty(),
|
||||
})
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
}
|
||||
|
||||
const kontakDarurat = proxy({
|
||||
findMany: {
|
||||
data: [] as Prisma.KontakDaruratGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.kontakdarurat[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
kontakDarurat.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
create:{
|
||||
form: {...defaultForm},
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateForm.safeParse(kontakDarurat.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kontakDarurat.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.kontakdarurat[
|
||||
"create"
|
||||
].post(kontakDarurat.create.form);
|
||||
if (res.status === 200) {
|
||||
kontakDarurat.findMany.load();
|
||||
return toast.success("Kontak Darurat berhasil disimpan!");
|
||||
}
|
||||
|
||||
return toast.error("Gagal menyimpan kontak darurat");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
kontakDarurat.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
kontakDarurat.create.form = {...defaultForm};
|
||||
}
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KontakDaruratGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/kontakdarurat/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kontakDarurat.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
kontakDarurat.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
kontakDarurat.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
try {
|
||||
kontakDarurat.delete.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/kontakdarurat/del/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Kontak darurat berhasil dihapus");
|
||||
await kontakDarurat.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus kontak darurat");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus kontak darurat");
|
||||
} finally {
|
||||
kontakDarurat.delete.loading = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/kontakdarurat/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching kontak darurat:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(kontakDarurat.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kontakDarurat.edit.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/kontakdarurat/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(result.message || "Kontak darurat berhasil diupdate");
|
||||
await kontakDarurat.findMany.load();
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update kontak darurat");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal update:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat mengupdate kontak darurat");
|
||||
return false;
|
||||
} finally {
|
||||
kontakDarurat.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
kontakDarurat.edit.id = "";
|
||||
kontakDarurat.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
name: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
imageId: z.string().nonempty(),
|
||||
});
|
||||
|
||||
export default kontakDarurat
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
const kontakDarurat = proxy({
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.KontakDaruratGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
kontakDarurat.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
kontakDarurat.findMany.page = page;
|
||||
kontakDarurat.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.kontakdarurat[
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kontakDarurat.findMany.data = res.data.data ?? [];
|
||||
kontakDarurat.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
kontakDarurat.findMany.data = [];
|
||||
kontakDarurat.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch kontak darurat paginated:", err);
|
||||
kontakDarurat.findMany.data = [];
|
||||
kontakDarurat.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kontakDarurat.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
create: {
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateForm.safeParse(kontakDarurat.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kontakDarurat.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.kontakdarurat["create"].post(
|
||||
kontakDarurat.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
kontakDarurat.findMany.load();
|
||||
return toast.success("Kontak Darurat berhasil disimpan!");
|
||||
}
|
||||
|
||||
return toast.error("Gagal menyimpan kontak darurat");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
kontakDarurat.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
kontakDarurat.create.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KontakDaruratGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/kontakdarurat/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kontakDarurat.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
kontakDarurat.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
kontakDarurat.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
try {
|
||||
kontakDarurat.delete.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/kontakdarurat/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Kontak darurat berhasil dihapus");
|
||||
await kontakDarurat.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus kontak darurat");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus kontak darurat");
|
||||
} finally {
|
||||
kontakDarurat.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/kontakdarurat/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching kontak darurat:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(kontakDarurat.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kontakDarurat.edit.loading = true;
|
||||
const response = await fetch(
|
||||
`/api/kesehatan/kontakdarurat/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(result.message || "Kontak darurat berhasil diupdate");
|
||||
await kontakDarurat.findMany.load();
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update kontak darurat");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal update:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat mengupdate kontak darurat"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
kontakDarurat.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
kontakDarurat.edit.id = "";
|
||||
kontakDarurat.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default kontakDarurat;
|
||||
|
||||
@@ -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";
|
||||
@@ -17,21 +18,45 @@ const defaultForm = {
|
||||
}
|
||||
|
||||
const penangananDarurat = proxy({
|
||||
findMany: {
|
||||
data: [] as Prisma.PenangananDaruratGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.penanganandarurat[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
penangananDarurat.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PenangananDaruratGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
penangananDarurat.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
penangananDarurat.findMany.page = page;
|
||||
penangananDarurat.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.penanganandarurat["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
penangananDarurat.findMany.data = res.data.data ?? [];
|
||||
penangananDarurat.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
penangananDarurat.findMany.data = [];
|
||||
penangananDarurat.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch berita paginated:", err);
|
||||
penangananDarurat.findMany.data = [];
|
||||
penangananDarurat.findMany.totalPages = 1;
|
||||
} finally {
|
||||
penangananDarurat.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
create:{
|
||||
form: {...defaultForm},
|
||||
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";
|
||||
@@ -9,6 +10,7 @@ const templateForm = z.object({
|
||||
nomor: z.string().min(1, { message: "Nomor is required" }),
|
||||
deskripsi: z.string().min(1, { message: "Deskripsi is required" }),
|
||||
imageId: z.string().nonempty(),
|
||||
jadwalPelayanan: z.string().min(1, { message: "Jadwal Pelayanan is required" }),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
@@ -16,6 +18,7 @@ const defaultForm = {
|
||||
nomor: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
jadwalPelayanan: "",
|
||||
};
|
||||
|
||||
const posyandustate = proxy({
|
||||
@@ -50,19 +53,43 @@ const posyandustate = proxy({
|
||||
}
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PosyanduGetPayload<{
|
||||
include: {
|
||||
data: null as
|
||||
| Prisma.PosyanduGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
posyandustate.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
posyandustate.findMany.page = page;
|
||||
posyandustate.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.posyandu["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
posyandustate.findMany.data = res.data.data ?? [];
|
||||
posyandustate.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
posyandustate.findMany.data = [];
|
||||
posyandustate.findMany.totalPages = 1;
|
||||
}
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.posyandu["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
posyandustate.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch posyandu paginated:", err);
|
||||
posyandustate.findMany.data = [];
|
||||
posyandustate.findMany.totalPages = 1;
|
||||
} finally {
|
||||
posyandustate.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as
|
||||
@@ -148,6 +175,7 @@ const posyandustate = proxy({
|
||||
nomor: data.nomor,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId || "",
|
||||
jadwalPelayanan: data.jadwalPelayanan || "",
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
@@ -181,6 +209,7 @@ const posyandustate = proxy({
|
||||
nomor: this.form.nomor,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
jadwalPelayanan: this.form.jadwalPelayanan,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -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";
|
||||
@@ -20,17 +21,43 @@ const defaultForm = {
|
||||
|
||||
const programKesehatan = proxy({
|
||||
findMany: {
|
||||
data: [] as Prisma.ProgramKesehatanGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.programkesehatan[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
programKesehatan.findMany.data = res.data?.data ?? [];
|
||||
data: null as
|
||||
| Prisma.ProgramKesehatanGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
programKesehatan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
programKesehatan.findMany.page = page;
|
||||
programKesehatan.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.programkesehatan[
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
programKesehatan.findMany.data = res.data.data ?? [];
|
||||
programKesehatan.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
programKesehatan.findMany.data = [];
|
||||
programKesehatan.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch berita paginated:", err);
|
||||
programKesehatan.findMany.data = [];
|
||||
programKesehatan.findMany.totalPages = 1;
|
||||
} finally {
|
||||
programKesehatan.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -97,12 +124,15 @@ const programKesehatan = proxy({
|
||||
try {
|
||||
programKesehatan.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/kesehatan/programkesehatan/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const response = await fetch(
|
||||
`/api/kesehatan/programkesehatan/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
if (response.ok && result?.success) {
|
||||
@@ -156,57 +186,70 @@ const programKesehatan = proxy({
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching program kesehatan:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(programKesehatan.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
const cek = templateForm.safeParse(programKesehatan.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
programKesehatan.edit.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/programkesehatan/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsiSingkat: this.form.deskripsiSingkat,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(result.message || "Program kesehatan berhasil diupdate");
|
||||
await programKesehatan.findMany.load();
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update program kesehatan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal update:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat mengupdate program kesehatan");
|
||||
return false;
|
||||
} finally {
|
||||
programKesehatan.edit.loading = false;
|
||||
try {
|
||||
programKesehatan.edit.loading = true;
|
||||
const response = await fetch(
|
||||
`/api/kesehatan/programkesehatan/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsiSingkat: this.form.deskripsiSingkat,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(
|
||||
result.message || "Program kesehatan berhasil diupdate"
|
||||
);
|
||||
await programKesehatan.findMany.load();
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update program kesehatan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal update:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat mengupdate program kesehatan"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
programKesehatan.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
programKesehatan.edit.id = "";
|
||||
programKesehatan.edit.form = { ...defaultForm };
|
||||
programKesehatan.edit.id = "";
|
||||
programKesehatan.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
@@ -163,13 +164,43 @@ const puskesmasState = proxy({
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: null as Prisma.PuskesmasGetPayload<{
|
||||
include: { image: true; jam: true; kontak: true };
|
||||
}>[] | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.puskesmas["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
puskesmasState.findMany.data = res.data?.data ?? [];
|
||||
data: null as
|
||||
| Prisma.PuskesmasGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
jam: true;
|
||||
kontak: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
puskesmasState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
puskesmasState.findMany.page = page;
|
||||
puskesmasState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.puskesmas["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
puskesmasState.findMany.data = res.data.data ?? [];
|
||||
puskesmasState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
puskesmasState.findMany.data = [];
|
||||
puskesmasState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch berita paginated:", err);
|
||||
puskesmasState.findMany.data = [];
|
||||
puskesmasState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
puskesmasState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
257
src/app/admin/(dashboard)/_state/landing-page/apbdes.ts
Normal file
257
src/app/admin/(dashboard)/_state/landing-page/apbdes.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateapbDesaForm = z.object({
|
||||
name: z.string().min(1, "Judul minimal 1 karakter"),
|
||||
jumlah: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||
imageId: z.string().min(1, "File minimal 1"),
|
||||
fileId: z.string().min(1, "File minimal 1"),
|
||||
});
|
||||
|
||||
const defaultapbdesForm = {
|
||||
name: "",
|
||||
jumlah: "",
|
||||
imageId: "",
|
||||
fileId: "",
|
||||
};
|
||||
|
||||
const apbdes = proxy({
|
||||
create: {
|
||||
form: { ...defaultapbdesForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateapbDesaForm.safeParse(apbdes.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
apbdes.create.loading = true;
|
||||
const res = await ApiFetch.api.landingpage.apbdes["create"].post({
|
||||
...apbdes.create.form,
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
apbdes.findMany.load();
|
||||
return toast.success("Data berhasil ditambahkan");
|
||||
}
|
||||
return toast.error("Gagal menambahkan data");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error("Gagal menambahkan data");
|
||||
} finally {
|
||||
apbdes.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
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;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.APBDesGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
file: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/landingpage/apbdes/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
apbdes.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
apbdes.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
apbdes.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
apbdes.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/landingpage/apbdes/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "apbdes berhasil dihapus");
|
||||
await apbdes.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus apbdes");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus apbdes");
|
||||
} finally {
|
||||
apbdes.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultapbdesForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
apbdes.edit.loading = true;
|
||||
|
||||
const response = await fetch(`/api/landingpage/apbdes/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
jumlah: data.jumlah,
|
||||
imageId: data.imageId,
|
||||
fileId: data.fileId,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading apbdes:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
} finally {
|
||||
apbdes.edit.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateapbDesaForm.safeParse(apbdes.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
apbdes.edit.loading = true;
|
||||
const response = await fetch(`/api/landingpage/apbdes/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
jumlah: this.form.jumlah,
|
||||
imageId: this.form.imageId,
|
||||
fileId: this.form.fileId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update apbdes");
|
||||
await apbdes.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal mengupdate apbdes");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating apbdes:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal mengupdate apbdes"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
apbdes.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
apbdes.edit.id = "";
|
||||
apbdes.edit.form = { ...defaultapbdesForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default apbdes;
|
||||
@@ -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";
|
||||
@@ -54,20 +55,44 @@ const desaAntikorupsi = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<
|
||||
Prisma.DesaAntiKorupsiGetPayload<{
|
||||
include: {
|
||||
file: true;
|
||||
kategori: true;
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.desaantikorupsi[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
desaAntikorupsi.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
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,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
desaAntikorupsi.findMany.data = res.data.data || [];
|
||||
desaAntikorupsi.findMany.total = res.data.total || 0;
|
||||
desaAntikorupsi.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load media sosial:", res.data?.message);
|
||||
desaAntikorupsi.findMany.data = [];
|
||||
desaAntikorupsi.findMany.total = 0;
|
||||
desaAntikorupsi.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading media sosial:", error);
|
||||
desaAntikorupsi.findMany.data = [];
|
||||
desaAntikorupsi.findMany.total = 0;
|
||||
desaAntikorupsi.findMany.totalPages = 1;
|
||||
} finally {
|
||||
desaAntikorupsi.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -281,14 +306,43 @@ const kategoriDesaAntiKorupsi = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
}> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.kategoridak["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
kategoriDesaAntiKorupsi.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
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 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;
|
||||
} else {
|
||||
console.error("Failed to load media sosial:", res.data?.message);
|
||||
kategoriDesaAntiKorupsi.findMany.data = [];
|
||||
kategoriDesaAntiKorupsi.findMany.total = 0;
|
||||
kategoriDesaAntiKorupsi.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading media sosial:", error);
|
||||
kategoriDesaAntiKorupsi.findMany.data = [];
|
||||
kategoriDesaAntiKorupsi.findMany.total = 0;
|
||||
kategoriDesaAntiKorupsi.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kategoriDesaAntiKorupsi.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -320,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;
|
||||
}
|
||||
|
||||
834
src/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan.ts
Normal file
834
src/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan.ts
Normal file
@@ -0,0 +1,834 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
// Template form responden
|
||||
|
||||
const templateResponden = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
tanggal: z.string().min(1, "Tanggal harus diisi"),
|
||||
jenisKelaminId: z.string().min(1, "Jenis kelamin harus diisi"),
|
||||
ratingId: z.string().min(1, "Rating harus diisi"),
|
||||
kelompokUmurId: z.string().min(1, "Kelompok umur harus diisi"),
|
||||
});
|
||||
|
||||
const defaultFormResponden = {
|
||||
name: "",
|
||||
tanggal: "",
|
||||
jenisKelaminId: "",
|
||||
ratingId: "",
|
||||
kelompokUmurId: "",
|
||||
};
|
||||
|
||||
const responden = proxy({
|
||||
create: {
|
||||
form: { ...defaultFormResponden },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateResponden.safeParse(responden.create.form);
|
||||
if (!cek.success) {
|
||||
const err = cek.error.issues.map((i) => i.message).join("\n");
|
||||
toast.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
responden.create.loading = true;
|
||||
const res = await ApiFetch.api.landingpage.responden["create"].post(
|
||||
responden.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
toast.success("Responden berhasil ditambahkan");
|
||||
await responden.findMany.load();
|
||||
} else {
|
||||
toast.error(res.data?.message ?? "Gagal tambah responden");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal create:", error);
|
||||
toast.error("Terjadi kesalahan saat menambahkan responden");
|
||||
} finally {
|
||||
responden.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => {
|
||||
// Change to arrow function
|
||||
responden.findMany.loading = true; // Use the full path to access the property
|
||||
responden.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.landingpage.responden["findMany"].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
responden.findMany.data = res.data.data || [];
|
||||
responden.findMany.total = res.data.total || 0;
|
||||
responden.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load responden:", res.data?.message);
|
||||
responden.findMany.data = [];
|
||||
responden.findMany.total = 0;
|
||||
responden.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading responden:", error);
|
||||
responden.findMany.data = [];
|
||||
responden.findMany.total = 0;
|
||||
responden.findMany.totalPages = 1;
|
||||
} finally {
|
||||
responden.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.RespondenGetPayload<{
|
||||
include: {
|
||||
jenisKelamin: true;
|
||||
rating: true;
|
||||
kelompokUmur: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/landingpage/responden/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
responden.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
responden.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading responden:", error);
|
||||
responden.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultFormResponden },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
responden.update.loading = true;
|
||||
|
||||
const response = await fetch(`/api/landingpage/responden/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
tanggal: data.tanggal,
|
||||
jenisKelaminId: data.jenisKelaminId,
|
||||
ratingId: data.ratingId,
|
||||
kelompokUmurId: data.kelompokUmurId,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading responden:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
} finally {
|
||||
responden.update.loading = false;
|
||||
}
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
const cek = templateResponden.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await fetch(`/api/landingpage/responden/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
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) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
toast.success("Berhasil update data!");
|
||||
await responden.findMany.load();
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Error update data:", error);
|
||||
toast.error("Gagal update data responden");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
responden.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/landingpage/responden/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "responden berhasil dihapus");
|
||||
await responden.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus responden");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus responden");
|
||||
} finally {
|
||||
responden.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Template form jenis kelamin responden
|
||||
const templateJenisKelaminResponden = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
});
|
||||
|
||||
const defaultFormJenisKelaminResponden = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
const jenisKelaminResponden = proxy({
|
||||
create: {
|
||||
form: { ...defaultFormJenisKelaminResponden },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateJenisKelaminResponden.safeParse(
|
||||
jenisKelaminResponden.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = cek.error.issues.map((i) => i.message).join("\n");
|
||||
toast.error(err);
|
||||
return;
|
||||
}
|
||||
jenisKelaminResponden.create.loading = true;
|
||||
try {
|
||||
jenisKelaminResponden.create.loading = true;
|
||||
const res = await ApiFetch.api.landingpage.jeniskelaminresponden[
|
||||
"create"
|
||||
].post(jenisKelaminResponden.create.form);
|
||||
if (res.status === 200) {
|
||||
toast.success("Jenis kelamin responden berhasil ditambahkan");
|
||||
await jenisKelaminResponden.findMany.load();
|
||||
} else {
|
||||
toast.error(
|
||||
res.data?.message ?? "Gagal tambah jenis kelamin responden"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal create:", error);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat menambahkan jenis kelamin responden"
|
||||
);
|
||||
} finally {
|
||||
jenisKelaminResponden.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => {
|
||||
// Change to arrow function
|
||||
jenisKelaminResponden.findMany.loading = true; // Use the full path to access the property
|
||||
jenisKelaminResponden.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.landingpage.jeniskelaminresponden[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
jenisKelaminResponden.findMany.data = res.data.data || [];
|
||||
jenisKelaminResponden.findMany.total = res.data.total || 0;
|
||||
jenisKelaminResponden.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error(
|
||||
"Failed to load jenis kelamin responden:",
|
||||
res.data?.message
|
||||
);
|
||||
jenisKelaminResponden.findMany.data = [];
|
||||
jenisKelaminResponden.findMany.total = 0;
|
||||
jenisKelaminResponden.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading jenis kelamin responden:", error);
|
||||
jenisKelaminResponden.findMany.data = [];
|
||||
jenisKelaminResponden.findMany.total = 0;
|
||||
jenisKelaminResponden.findMany.totalPages = 1;
|
||||
} finally {
|
||||
jenisKelaminResponden.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.JenisKelaminRespondenGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/landingpage/jeniskelaminresponden/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
jenisKelaminResponden.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
jenisKelaminResponden.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading jenis kelamin responden:", error);
|
||||
jenisKelaminResponden.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultFormJenisKelaminResponden },
|
||||
loading: false,
|
||||
async byId() {
|
||||
// Method implementation if needed
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
const cek = templateJenisKelaminResponden.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/landingpage/jeniskelaminresponden/${id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
);
|
||||
const result = await response.json();
|
||||
if (!response.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
toast.success("Berhasil update data!");
|
||||
await jenisKelaminResponden.findMany.load();
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Error update data:", error);
|
||||
toast.error("Gagal update data jenis kelamin responden");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
jenisKelaminResponden.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/landingpage/jeniskelaminresponden/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "jenis kelamin responden berhasil dihapus"
|
||||
);
|
||||
await jenisKelaminResponden.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus jenis kelamin responden"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus jenis kelamin responden");
|
||||
} finally {
|
||||
jenisKelaminResponden.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Template form pilihan rating responden
|
||||
|
||||
const templatePilihanRatingResponden = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
});
|
||||
|
||||
const defaultFormPilihanRatingResponden = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
const pilihanRatingResponden = proxy({
|
||||
create: {
|
||||
form: { ...defaultFormPilihanRatingResponden },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templatePilihanRatingResponden.safeParse(
|
||||
pilihanRatingResponden.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = cek.error.issues.map((i) => i.message).join("\n");
|
||||
toast.error(err);
|
||||
return;
|
||||
}
|
||||
pilihanRatingResponden.create.loading = true;
|
||||
try {
|
||||
pilihanRatingResponden.create.loading = true;
|
||||
const res = await ApiFetch.api.landingpage.pilihanratingresponden[
|
||||
"create"
|
||||
].post(pilihanRatingResponden.create.form);
|
||||
if (res.status === 200) {
|
||||
toast.success("Jenis kelamin responden berhasil ditambahkan");
|
||||
await pilihanRatingResponden.findMany.load();
|
||||
} else {
|
||||
toast.error(
|
||||
res.data?.message ?? "Gagal tambah jenis kelamin responden"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal create:", error);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat menambahkan jenis kelamin responden"
|
||||
);
|
||||
} finally {
|
||||
pilihanRatingResponden.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => {
|
||||
// Change to arrow function
|
||||
pilihanRatingResponden.findMany.loading = true; // Use the full path to access the property
|
||||
pilihanRatingResponden.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.landingpage.pilihanratingresponden[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pilihanRatingResponden.findMany.data = res.data.data || [];
|
||||
pilihanRatingResponden.findMany.total = res.data.total || 0;
|
||||
pilihanRatingResponden.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error(
|
||||
"Failed to load pilihan rating responden:",
|
||||
res.data?.message
|
||||
);
|
||||
pilihanRatingResponden.findMany.data = [];
|
||||
pilihanRatingResponden.findMany.total = 0;
|
||||
pilihanRatingResponden.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pilihan rating responden:", error);
|
||||
pilihanRatingResponden.findMany.data = [];
|
||||
pilihanRatingResponden.findMany.total = 0;
|
||||
pilihanRatingResponden.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pilihanRatingResponden.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.PilihanRatingRespondenGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/landingpage/pilihanratingresponden/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
pilihanRatingResponden.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
pilihanRatingResponden.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pilihan rating responden:", error);
|
||||
pilihanRatingResponden.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultFormPilihanRatingResponden },
|
||||
loading: false,
|
||||
async byId() {
|
||||
// Method implementation if needed
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
const cek = templatePilihanRatingResponden.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/landingpage/pilihanratingresponden/${id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
);
|
||||
const result = await response.json();
|
||||
if (!response.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
toast.success("Berhasil update data!");
|
||||
await pilihanRatingResponden.findMany.load();
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Error update data:", error);
|
||||
toast.error("Gagal update data pilihan rating responden");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
pilihanRatingResponden.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/landingpage/pilihanratingresponden/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "pilihan rating responden berhasil dihapus"
|
||||
);
|
||||
await pilihanRatingResponden.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus pilihan rating responden"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus pilihan rating responden");
|
||||
} finally {
|
||||
pilihanRatingResponden.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Template form kelompok umur responden
|
||||
|
||||
const templateKelompokUmurResponden = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
});
|
||||
|
||||
const defaultFormKelompokUmurResponden = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
const kelompokUmurResponden = proxy({
|
||||
create: {
|
||||
form: { ...defaultFormKelompokUmurResponden },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateKelompokUmurResponden.safeParse(
|
||||
kelompokUmurResponden.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = cek.error.issues.map((i) => i.message).join("\n");
|
||||
toast.error(err);
|
||||
return;
|
||||
}
|
||||
kelompokUmurResponden.create.loading = true;
|
||||
try {
|
||||
kelompokUmurResponden.create.loading = true;
|
||||
const res = await ApiFetch.api.landingpage.umurresponden["create"].post(
|
||||
kelompokUmurResponden.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
toast.success("Kelompok umur responden berhasil ditambahkan");
|
||||
await kelompokUmurResponden.findMany.load();
|
||||
} else {
|
||||
toast.error(
|
||||
res.data?.message ?? "Gagal tambah kelompok umur responden"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal create:", error);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat menambahkan kelompok umur responden"
|
||||
);
|
||||
} finally {
|
||||
kelompokUmurResponden.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => {
|
||||
// Change to arrow function
|
||||
kelompokUmurResponden.findMany.loading = true; // Use the full path to access the property
|
||||
kelompokUmurResponden.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.landingpage.umurresponden[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kelompokUmurResponden.findMany.data = res.data.data || [];
|
||||
kelompokUmurResponden.findMany.total = res.data.total || 0;
|
||||
kelompokUmurResponden.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error(
|
||||
"Failed to load kelompok umur responden:",
|
||||
res.data?.message
|
||||
);
|
||||
kelompokUmurResponden.findMany.data = [];
|
||||
kelompokUmurResponden.findMany.total = 0;
|
||||
kelompokUmurResponden.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kelompok umur responden:", error);
|
||||
kelompokUmurResponden.findMany.data = [];
|
||||
kelompokUmurResponden.findMany.total = 0;
|
||||
kelompokUmurResponden.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kelompokUmurResponden.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.UmurRespondenGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/landingpage/umurresponden/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kelompokUmurResponden.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
kelompokUmurResponden.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kelompok umur responden:", error);
|
||||
kelompokUmurResponden.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultFormKelompokUmurResponden },
|
||||
loading: false,
|
||||
async byId() {
|
||||
// Method implementation if needed
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
const cek = templateKelompokUmurResponden.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await fetch(`/api/landingpage/umurresponden/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (!response.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
toast.success("Berhasil update data!");
|
||||
await kelompokUmurResponden.findMany.load();
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Error update data:", error);
|
||||
toast.error("Gagal update data kelompok umur responden");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
kelompokUmurResponden.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/landingpage/umurresponden/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "kelompok umur responden berhasil dihapus"
|
||||
);
|
||||
await kelompokUmurResponden.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus kelompok umur responden"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus kelompok umur responden");
|
||||
} finally {
|
||||
kelompokUmurResponden.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const indeksKepuasanState = proxy({
|
||||
responden,
|
||||
kelompokUmurResponden,
|
||||
jenisKelaminResponden,
|
||||
pilihanRatingResponden
|
||||
})
|
||||
|
||||
export default indeksKepuasanState
|
||||
536
src/app/admin/(dashboard)/_state/landing-page/prestasi-desa.ts
Normal file
536
src/app/admin/(dashboard)/_state/landing-page/prestasi-desa.ts
Normal file
@@ -0,0 +1,536 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateprestasiDesaForm = z.object({
|
||||
name: z.string().min(1, "Judul minimal 1 karakter"),
|
||||
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||
imageId: z.string().min(1, "File minimal 1"),
|
||||
kategoriId: z.string().min(1, "Kategori minimal 1 karakter"),
|
||||
});
|
||||
|
||||
const defaultprestasiDesaForm = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
kategoriId: "",
|
||||
};
|
||||
|
||||
const prestasiDesa = proxy({
|
||||
create: {
|
||||
form: { ...defaultprestasiDesaForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateprestasiDesaForm.safeParse(
|
||||
prestasiDesa.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
prestasiDesa.create.loading = true;
|
||||
const res = await ApiFetch.api.landingpage.prestasidesa[
|
||||
"create"
|
||||
].post({
|
||||
...prestasiDesa.create.form,
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
prestasiDesa.findMany.load();
|
||||
return toast.success("Data berhasil ditambahkan");
|
||||
}
|
||||
return toast.error("Gagal menambahkan data");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error("Gagal menambahkan data");
|
||||
} finally {
|
||||
prestasiDesa.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<
|
||||
Prisma.PrestasiDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategori: {
|
||||
select: {
|
||||
id: true;
|
||||
name: true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
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;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.PrestasiDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategori: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/landingpage/prestasidesa/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
prestasiDesa.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
prestasiDesa.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
prestasiDesa.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
prestasiDesa.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/landingpage/prestasidesa/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "prestasi desa berhasil dihapus");
|
||||
await prestasiDesa.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus prestasi desa");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus prestasi desa");
|
||||
} finally {
|
||||
prestasiDesa.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultprestasiDesaForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
prestasiDesa.edit.loading = true;
|
||||
|
||||
const response = await fetch(`/api/landingpage/prestasidesa/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId,
|
||||
kategoriId: data.kategoriId,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading prestasi desa:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
} finally {
|
||||
prestasiDesa.edit.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateprestasiDesaForm.safeParse(
|
||||
prestasiDesa.edit.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
prestasiDesa.edit.loading = true;
|
||||
const response = await fetch(
|
||||
`/api/landingpage/prestasidesa/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
kategoriId: this.form.kategoriId,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update prestasi desa");
|
||||
await prestasiDesa.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal mengupdate prestasi desa"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating prestasi desa:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Gagal mengupdate prestasi desa"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
prestasiDesa.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
prestasiDesa.edit.id = "";
|
||||
prestasiDesa.edit.form = { ...defaultprestasiDesaForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= KATEGORI kegiatan ========================================= //
|
||||
const kategoriPrestasiForm = z.object({
|
||||
name: z.string().min(1, "Nama minimal 1 karakter"),
|
||||
});
|
||||
|
||||
const kategoriPrestasiDefaultForm = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
const kategoriPrestasi = proxy({
|
||||
create: {
|
||||
form: { ...kategoriPrestasiDefaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = kategoriPrestasiForm.safeParse(kategoriPrestasi.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
kategoriPrestasi.create.loading = true;
|
||||
const res = await ApiFetch.api.landingpage.kategoriprestasi[
|
||||
"create"
|
||||
].post(kategoriPrestasi.create.form);
|
||||
if (res.status === 200) {
|
||||
kategoriPrestasi.findMany.load();
|
||||
return toast.success("Data berhasil ditambahkan");
|
||||
}
|
||||
return toast.error("Gagal menambahkan data");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error("Gagal menambahkan data");
|
||||
} finally {
|
||||
kategoriPrestasi.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
}> | null,
|
||||
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;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KategoriPrestasiDesaGetPayload<{
|
||||
omit: { isActive: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/landingpage/kategoriprestasi/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kategoriPrestasi.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
kategoriPrestasi.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
kategoriPrestasi.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
kategoriPrestasi.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/landingpage/kategoriprestasi/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Kategori prestasi berhasil dihapus");
|
||||
await kategoriPrestasi.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus kategori prestasi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus kategori prestasi");
|
||||
} finally {
|
||||
kategoriPrestasi.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...kategoriPrestasiDefaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/landingpage/kategoriprestasi/${id}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori prestasi:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = kategoriPrestasiForm.safeParse(kategoriPrestasi.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
kategoriPrestasi.edit.loading = true;
|
||||
const response = await fetch(
|
||||
`/api/landingpage/kategoriprestasi/${kategoriPrestasi.edit.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: kategoriPrestasi.edit.form.name,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
// Clone the response to avoid 'body already read' error
|
||||
const responseClone = response.clone();
|
||||
|
||||
try {
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(
|
||||
"Update failed with status:",
|
||||
response.status,
|
||||
"Response:",
|
||||
result
|
||||
);
|
||||
throw new Error(
|
||||
result?.message ||
|
||||
`Gagal mengupdate kategori prestasi (${response.status})`
|
||||
);
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
toast.success(
|
||||
result.message || "Berhasil memperbarui kategori prestasi"
|
||||
);
|
||||
await kategoriPrestasi.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal mengupdate kategori prestasi"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// If JSON parsing fails, try to get the response text for better error messages
|
||||
try {
|
||||
const text = await responseClone.text();
|
||||
console.error("Error response text:", text);
|
||||
throw new Error(`Gagal memproses respons dari server: ${text}`);
|
||||
} catch (textError) {
|
||||
console.error("Error parsing response as text:", textError);
|
||||
console.error("Original error:", error);
|
||||
throw new Error("Gagal memproses respons dari server");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating kategori prestasi:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Gagal mengupdate kategori prestasi"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
kategoriPrestasi.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
kategoriPrestasi.edit.id = "";
|
||||
kategoriPrestasi.edit.form = { ...kategoriPrestasiDefaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const prestasiState = proxy({
|
||||
prestasiDesa,
|
||||
kategoriPrestasi,
|
||||
});
|
||||
|
||||
export default prestasiState;
|
||||
@@ -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";
|
||||
@@ -5,10 +6,10 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateProgramInovasi = z.object({
|
||||
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||
description: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
name: z.string().min(1, "Nama minimal 1 karakter"),
|
||||
description: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||
link: z.string().min(3, "Link minimal 3 karakter"),
|
||||
link: z.string().min(1, "Link minimal 1 karakter"),
|
||||
});
|
||||
|
||||
type ProgramInovasiForm = Prisma.ProgramInovasiGetPayload<{
|
||||
@@ -59,15 +60,43 @@ const programInovasi = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.ProgramInovasiGetPayload<{ include: { image: true } }>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.programinovasi[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
programInovasi.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
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
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
programInovasi.findMany.data = res.data.data || [];
|
||||
programInovasi.findMany.total = res.data.total || 0;
|
||||
programInovasi.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load pegawai:", res.data?.message);
|
||||
programInovasi.findMany.data = [];
|
||||
programInovasi.findMany.total = 0;
|
||||
programInovasi.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pegawai:", error);
|
||||
programInovasi.findMany.data = [];
|
||||
programInovasi.findMany.total = 0;
|
||||
programInovasi.findMany.totalPages = 1;
|
||||
} finally {
|
||||
programInovasi.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -453,13 +482,43 @@ const mediaSosial = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.MediaSosialGetPayload<{ include: { image: true } }>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.mediasosial["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
mediaSosial.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
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;
|
||||
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,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
mediaSosial.findMany.data = res.data.data || [];
|
||||
mediaSosial.findMany.total = res.data.total || 0;
|
||||
mediaSosial.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load media sosial:", res.data?.message);
|
||||
mediaSosial.findMany.data = [];
|
||||
mediaSosial.findMany.total = 0;
|
||||
mediaSosial.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading media sosial:", error);
|
||||
mediaSosial.findMany.data = [];
|
||||
mediaSosial.findMany.total = 0;
|
||||
mediaSosial.findMany.totalPages = 1;
|
||||
} finally {
|
||||
mediaSosial.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
261
src/app/admin/(dashboard)/_state/landing-page/sdgs-desa.ts
Normal file
261
src/app/admin/(dashboard)/_state/landing-page/sdgs-desa.ts
Normal file
@@ -0,0 +1,261 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templatesdgsDesaForm = z.object({
|
||||
name: z.string().min(1, "Judul minimal 1 karakter"),
|
||||
jumlah: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||
imageId: z.string().min(1, "File minimal 1"),
|
||||
});
|
||||
|
||||
const defaultsdgsDesaForm = {
|
||||
name: "",
|
||||
jumlah: "",
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
const sdgsDesa = proxy({
|
||||
create: {
|
||||
form: { ...defaultsdgsDesaForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templatesdgsDesaForm.safeParse(
|
||||
sdgsDesa.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
sdgsDesa.create.loading = true;
|
||||
const res = await ApiFetch.api.landingpage.sdgsdesa[
|
||||
"create"
|
||||
].post({
|
||||
...sdgsDesa.create.form,
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
sdgsDesa.findMany.load();
|
||||
return toast.success("Data berhasil ditambahkan");
|
||||
}
|
||||
return toast.error("Gagal menambahkan data");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error("Gagal menambahkan data");
|
||||
} finally {
|
||||
sdgsDesa.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
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,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
sdgsDesa.findMany.data = res.data.data || [];
|
||||
sdgsDesa.findMany.total = res.data.total || 0;
|
||||
sdgsDesa.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load media sosial:", res.data?.message);
|
||||
sdgsDesa.findMany.data = [];
|
||||
sdgsDesa.findMany.total = 0;
|
||||
sdgsDesa.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading media sosial:", error);
|
||||
sdgsDesa.findMany.data = [];
|
||||
sdgsDesa.findMany.total = 0;
|
||||
sdgsDesa.findMany.totalPages = 1;
|
||||
} finally {
|
||||
sdgsDesa.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.SDGSDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/landingpage/sdgsdesa/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
sdgsDesa.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
sdgsDesa.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
sdgsDesa.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
sdgsDesa.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/landingpage/sdgsdesa/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "sdgs desa berhasil dihapus");
|
||||
await sdgsDesa.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus sdgs desa");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus sdgs desa");
|
||||
} finally {
|
||||
sdgsDesa.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultsdgsDesaForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
sdgsDesa.edit.loading = true;
|
||||
|
||||
const response = await fetch(`/api/landingpage/sdgsdesa/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
jumlah: data.jumlah,
|
||||
imageId: data.imageId,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading sdgs desa:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
} finally {
|
||||
sdgsDesa.edit.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templatesdgsDesaForm.safeParse(
|
||||
sdgsDesa.edit.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
sdgsDesa.edit.loading = true;
|
||||
const response = await fetch(
|
||||
`/api/landingpage/sdgsdesa/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
jumlah: this.form.jumlah,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update sdgs desa");
|
||||
await sdgsDesa.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal mengupdate sdgs desa"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating sdgs desa:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Gagal mengupdate sdgs desa"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
sdgsDesa.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
sdgsDesa.edit.id = "";
|
||||
sdgsDesa.edit.form = { ...defaultsdgsDesaForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default sdgsDesa;
|
||||
@@ -56,13 +56,17 @@ const dataLingkunganDesaState = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => {
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
dataLingkunganDesaState.findMany.loading = true; // Use the full path to access the property
|
||||
dataLingkunganDesaState.findMany.page = page;
|
||||
dataLingkunganDesaState.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
const res = await ApiFetch.api.lingkungan.datalingkungandesa["find-many"].get({
|
||||
query: { page, limit },
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.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";
|
||||
@@ -67,10 +68,46 @@ const kegiatanDesa = proxy({
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.lingkungan.kegiatandesa["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
kegiatanDesa.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||
// Change to arrow function
|
||||
kegiatanDesa.findMany.loading = true; // Use the full path to access the property
|
||||
kegiatanDesa.findMany.page = page;
|
||||
kegiatanDesa.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (kategori) query.kategori = kategori;
|
||||
const res = await ApiFetch.api.lingkungan.kegiatandesa[
|
||||
"find-many"
|
||||
].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kegiatanDesa.findMany.data = res.data.data || [];
|
||||
kegiatanDesa.findMany.total = res.data.total || 0;
|
||||
kegiatanDesa.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error(
|
||||
"Failed to load kegiatan desa:",
|
||||
res.data?.message
|
||||
);
|
||||
kegiatanDesa.findMany.data = [];
|
||||
kegiatanDesa.findMany.total = 0;
|
||||
kegiatanDesa.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kegiatan desa:", error);
|
||||
kegiatanDesa.findMany.data = [];
|
||||
kegiatanDesa.findMany.total = 0;
|
||||
kegiatanDesa.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kegiatanDesa.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -244,6 +281,35 @@ const kegiatanDesa = proxy({
|
||||
kegiatanDesa.edit.form = { ...defaultKegiatanDesaForm };
|
||||
},
|
||||
},
|
||||
findFirst: {
|
||||
data: null as Prisma.KegiatanDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategoriKegiatan: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
// findFirst.load()
|
||||
async load(kategori?: string) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await ApiFetch.api.lingkungan.kegiatandesa["find-first"].get({
|
||||
query: kategori ? { kategori } : {},
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
this.data = res.data.data || null;
|
||||
} else {
|
||||
this.data = null;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch kegiatan desa terbaru:", err);
|
||||
this.data = null;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= KATEGORI kegiatan ========================================= //
|
||||
@@ -269,9 +335,7 @@ const kategoriKegiatan = proxy({
|
||||
}
|
||||
try {
|
||||
kategoriKegiatan.create.loading = true;
|
||||
const res = await ApiFetch.api.lingkungan.kategorikegiatan[
|
||||
"create"
|
||||
].post(kategoriKegiatan.create.form);
|
||||
const res = await ApiFetch.api.lingkungan.kategorikegiatan["create"].post(kategoriKegiatan.create.form);
|
||||
if (res.status === 200) {
|
||||
kategoriKegiatan.findMany.load();
|
||||
return toast.success("Data berhasil ditambahkan");
|
||||
@@ -305,9 +369,7 @@ const kategoriKegiatan = proxy({
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/lingkungan/kategorikegiatan/${id}`
|
||||
);
|
||||
const res = await fetch(`/api/lingkungan/kategorikegiatan/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kategoriKegiatan.findUnique.data = data.data ?? null;
|
||||
@@ -367,15 +429,12 @@ const kategoriKegiatan = proxy({
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/lingkungan/kategorikegiatan/${id}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
const response = await fetch(`/api/lingkungan/kategorikegiatan/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
@@ -52,15 +52,19 @@ const pengelolaanSampah = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => {
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
pengelolaanSampah.findMany.loading = true; // Use the full path to access the property
|
||||
pengelolaanSampah.findMany.page = page;
|
||||
pengelolaanSampah.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
const res = await ApiFetch.api.lingkungan.pengelolaansampah[
|
||||
"find-many"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
@@ -265,7 +269,7 @@ const keteranganSampah = proxy({
|
||||
try {
|
||||
keteranganSampah.create.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.lingkungan.pengelolaansampah.keteranganbankterdekat[
|
||||
await ApiFetch.api.lingkungan.keteranganbankterdekat[
|
||||
"create"
|
||||
].post(keteranganSampah.create.form);
|
||||
if (res.status === 200) {
|
||||
@@ -287,14 +291,47 @@ const keteranganSampah = proxy({
|
||||
omit: { isActive: true };
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.lingkungan.pengelolaansampah.keteranganbankterdekat[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
keteranganSampah.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
keteranganSampah.findMany.loading = true; // Use the full path to access the property
|
||||
keteranganSampah.findMany.page = page;
|
||||
keteranganSampah.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
const res = await ApiFetch.api.lingkungan.keteranganbankterdekat[
|
||||
"find-many"
|
||||
].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
keteranganSampah.findMany.data = res.data.data || [];
|
||||
keteranganSampah.findMany.total = res.data.total || 0;
|
||||
keteranganSampah.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error(
|
||||
"Failed to load keterangan bank sampah terdekat:",
|
||||
res.data?.message
|
||||
);
|
||||
keteranganSampah.findMany.data = [];
|
||||
keteranganSampah.findMany.total = 0;
|
||||
keteranganSampah.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading keterangan bank sampah terdekat:", error);
|
||||
keteranganSampah.findMany.data = [];
|
||||
keteranganSampah.findMany.total = 0;
|
||||
keteranganSampah.findMany.totalPages = 1;
|
||||
} finally {
|
||||
keteranganSampah.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KeteranganBankSampahTerdekatGetPayload<{
|
||||
@@ -302,7 +339,7 @@ const keteranganSampah = proxy({
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/lingkungan/pengelolaansampah/keteranganbankterdekat/${id}`);
|
||||
const res = await fetch(`/api/lingkungan/keteranganbankterdekat/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
keteranganSampah.findUnique.data = data.data ?? null;
|
||||
@@ -324,7 +361,7 @@ const keteranganSampah = proxy({
|
||||
try {
|
||||
keteranganSampah.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/lingkungan/pengelolaansampah/keteranganbankterdekat/del/${id}`, {
|
||||
const response = await fetch(`/api/lingkungan/keteranganbankterdekat/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -359,7 +396,7 @@ const keteranganSampah = proxy({
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/lingkungan/pengelolaansampah/keteranganbankterdekat/${id}`, {
|
||||
const response = await fetch(`/api/lingkungan/keteranganbankterdekat/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -404,7 +441,7 @@ const keteranganSampah = proxy({
|
||||
try {
|
||||
keteranganSampah.edit.loading = true;
|
||||
const response = await fetch(
|
||||
`/api/lingkungan/pengelolaansampah/keteranganbankterdekat/${this.id}`,
|
||||
`/api/lingkungan/keteranganbankterdekat/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
|
||||
@@ -56,13 +56,17 @@ const programPenghijauanState = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => {
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
programPenghijauanState.findMany.loading = true; // Use the full path to access the property
|
||||
programPenghijauanState.findMany.page = page;
|
||||
programPenghijauanState.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
const res = await ApiFetch.api.lingkungan.programpenghijauan["find-many"].get({
|
||||
query: { page, limit },
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
|
||||
558
src/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa.ts
Normal file
558
src/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa.ts
Normal file
@@ -0,0 +1,558 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
// ========================================= BEASISWA PENDAFTAR ========================================= //
|
||||
|
||||
const templateBeasiswaPendaftar = z.object({
|
||||
namaLengkap: z.string().min(1, "Nama harus diisi"),
|
||||
nik: z.string().min(1, "NIK harus diisi"),
|
||||
tempatLahir: z.string().min(1, "Tempat lahir harus diisi"),
|
||||
tanggalLahir: z.string().min(1, "Tanggal lahir harus diisi"),
|
||||
jenisKelamin: z.string().min(1, "Jenis kelamin harus diisi"),
|
||||
kewarganegaraan: z.string().min(1, "Kewarganegaraan harus diisi"),
|
||||
agama: z.string().min(1, "Agama harus diisi"),
|
||||
alamatKTP: z.string().min(1, "Alamat KTP harus diisi"),
|
||||
alamatDomisili: z.string().min(1, "Alamat domisili harus diisi"),
|
||||
noHp: z.string().min(1, "No HP harus diisi"),
|
||||
email: z.string().min(1, "Email harus diisi"),
|
||||
statusPernikahan: z.string().min(1, "Status pernikahan harus diisi"),
|
||||
ukuranBaju: z.string().min(1, "Ukuran baju harus diisi"),
|
||||
});
|
||||
|
||||
const defaultBeasiswaPendaftar = {
|
||||
namaLengkap: "",
|
||||
nik: "",
|
||||
tempatLahir: "",
|
||||
tanggalLahir: "",
|
||||
jenisKelamin: "",
|
||||
kewarganegaraan: "",
|
||||
agama: "",
|
||||
alamatKTP: "",
|
||||
alamatDomisili: "",
|
||||
noHp: "",
|
||||
email: "",
|
||||
statusPernikahan: "",
|
||||
ukuranBaju: "",
|
||||
};
|
||||
|
||||
const beasiswaPendaftar = proxy({
|
||||
create: {
|
||||
form: { ...defaultBeasiswaPendaftar },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateBeasiswaPendaftar.safeParse(
|
||||
beasiswaPendaftar.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
beasiswaPendaftar.create.loading = true;
|
||||
const res = await ApiFetch.api.pendidikan.beasiswa.beasiswapendaftar[
|
||||
"create"
|
||||
].post(beasiswaPendaftar.create.form);
|
||||
if (res.status === 200) {
|
||||
beasiswaPendaftar.findMany.load();
|
||||
return toast.success("Data Berhasil Dibuat, Silahkan Menunggu Konfirmasi dari Admin di WhatsApp");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return toast.error("failed create");
|
||||
} finally {
|
||||
beasiswaPendaftar.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as Prisma.BeasiswaPendaftarGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[],
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
beasiswaPendaftar.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
beasiswaPendaftar.findMany.page = page;
|
||||
beasiswaPendaftar.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.pendidikan.beasiswa.beasiswapendaftar["findMany"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
beasiswaPendaftar.findMany.data = res.data.data ?? [];
|
||||
beasiswaPendaftar.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
beasiswaPendaftar.findMany.data = [];
|
||||
beasiswaPendaftar.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch beasiswa pendaftar paginated:", err);
|
||||
beasiswaPendaftar.findMany.data = [];
|
||||
beasiswaPendaftar.findMany.totalPages = 1;
|
||||
} finally {
|
||||
beasiswaPendaftar.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.BeasiswaPendaftarGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/pendidikan/beasiswa/beasiswapendaftar/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
beasiswaPendaftar.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
beasiswaPendaftar.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
beasiswaPendaftar.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async delete(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
beasiswaPendaftar.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/beasiswa/beasiswapendaftar/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Beasiswa berhasil dihapus");
|
||||
await beasiswaPendaftar.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus beasiswa");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus beasiswa");
|
||||
} finally {
|
||||
beasiswaPendaftar.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultBeasiswaPendaftar },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/beasiswa/beasiswapendaftar/${id}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
namaLengkap: data.namaLengkap,
|
||||
nik: data.nik,
|
||||
tempatLahir: data.tempatLahir,
|
||||
tanggalLahir: data.tanggalLahir,
|
||||
jenisKelamin: data.jenisKelamin,
|
||||
kewarganegaraan: data.kewarganegaraan,
|
||||
agama: data.agama,
|
||||
alamatKTP: data.alamatKTP,
|
||||
alamatDomisili: data.alamatDomisili,
|
||||
noHp: data.noHp,
|
||||
email: data.email,
|
||||
statusPernikahan: data.statusPernikahan,
|
||||
ukuranBaju: data.ukuranBaju,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading beasiswa pendaftar:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateBeasiswaPendaftar.safeParse(
|
||||
beasiswaPendaftar.update.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
beasiswaPendaftar.update.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/beasiswa/beasiswapendaftar/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
namaLengkap: this.form.namaLengkap,
|
||||
nik: this.form.nik,
|
||||
tanggalLahir: this.form.tanggalLahir,
|
||||
jenisKelamin: this.form.jenisKelamin,
|
||||
kewarganegaraan: this.form.kewarganegaraan,
|
||||
agama: this.form.agama,
|
||||
alamatKTP: this.form.alamatKTP,
|
||||
alamatDomisili: this.form.alamatDomisili,
|
||||
noHp: this.form.noHp,
|
||||
email: this.form.email,
|
||||
statusPernikahan: this.form.statusPernikahan,
|
||||
ukuranBaju: this.form.ukuranBaju,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update beasiswa pendaftar");
|
||||
await beasiswaPendaftar.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update beasiswa pendaftar");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating beasiswa pendaftar:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update beasiswa pendaftar"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
beasiswaPendaftar.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
beasiswaPendaftar.update.id = "";
|
||||
beasiswaPendaftar.update.form = { ...defaultBeasiswaPendaftar };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= KEUNGGULAN PROGRAM ========================================= //
|
||||
const templateKeunggulanProgram = z.object({
|
||||
judul: z.string().min(1, "Judul harus diisi"),
|
||||
deskripsi: z.string().min(1, "Deskripsi harus diisi"),
|
||||
});
|
||||
|
||||
const defaultKeunggulanProgram = {
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
};
|
||||
|
||||
const keunggulanProgram = proxy({
|
||||
create: {
|
||||
form: { ...defaultKeunggulanProgram },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateKeunggulanProgram.safeParse(
|
||||
keunggulanProgram.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
keunggulanProgram.create.loading = true;
|
||||
const res = await ApiFetch.api.pendidikan.beasiswa.keunggulanprogram[
|
||||
"create"
|
||||
].post(keunggulanProgram.create.form);
|
||||
if (res.status === 200) {
|
||||
keunggulanProgram.findMany.load();
|
||||
return toast.success("Data Berhasil Dibuat, Silahkan Menunggu Konfirmasi dari Admin di WhatsApp");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return toast.error("failed create");
|
||||
} finally {
|
||||
keunggulanProgram.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as Prisma.KeunggulanProgramGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[],
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
keunggulanProgram.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
keunggulanProgram.findMany.page = page;
|
||||
keunggulanProgram.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.pendidikan.beasiswa.keunggulanprogram["findMany"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
keunggulanProgram.findMany.data = res.data.data ?? [];
|
||||
keunggulanProgram.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
keunggulanProgram.findMany.data = [];
|
||||
keunggulanProgram.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch keunggulan program paginated:", err);
|
||||
keunggulanProgram.findMany.data = [];
|
||||
keunggulanProgram.findMany.totalPages = 1;
|
||||
} finally {
|
||||
keunggulanProgram.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KeunggulanProgramGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/pendidikan/beasiswa/keunggulanprogram/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
keunggulanProgram.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
keunggulanProgram.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
keunggulanProgram.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async delete(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
keunggulanProgram.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/beasiswa/keunggulanprogram/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Keunggulan Program berhasil dihapus");
|
||||
await keunggulanProgram.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus keunggulan program");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus keunggulan program");
|
||||
} finally {
|
||||
keunggulanProgram.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultKeunggulanProgram },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/beasiswa/keunggulanprogram/${id}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
judul: data.judul,
|
||||
deskripsi: data.deskripsi,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading keunggulan program:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateKeunggulanProgram.safeParse(
|
||||
keunggulanProgram.update.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
keunggulanProgram.update.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/beasiswa/keunggulanprogram/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
judul: this.form.judul,
|
||||
deskripsi: this.form.deskripsi,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update keunggulan program");
|
||||
await keunggulanProgram.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update keunggulan program");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating keunggulan program:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update keunggulan program"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
keunggulanProgram.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
keunggulanProgram.update.id = "";
|
||||
keunggulanProgram.update.form = { ...defaultKeunggulanProgram };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const beasiswaDesaState = proxy({
|
||||
beasiswaPendaftar,
|
||||
keunggulanProgram
|
||||
});
|
||||
|
||||
export default beasiswaDesaState;
|
||||
@@ -0,0 +1,260 @@
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
// ========================================= TUJUAN PROGRAM ========================================= //
|
||||
|
||||
const templateTujuanProgramForm = z.object({
|
||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
});
|
||||
|
||||
type TujuanProgramForm = Prisma.TujuanBimbinganBelajarDesaGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const stateTujuanProgram = proxy({
|
||||
findById: {
|
||||
data: null as TujuanProgramForm | null,
|
||||
loading: false,
|
||||
initialize() {
|
||||
stateTujuanProgram.findById.data = {
|
||||
id: "",
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
} as TujuanProgramForm;
|
||||
},
|
||||
async load(id: string) {
|
||||
try {
|
||||
stateTujuanProgram.findById.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.bimbinganbelajardesa.tujuanprogram[
|
||||
"find-by-id"
|
||||
].get({
|
||||
query: { id },
|
||||
});
|
||||
if (res.status === 200) {
|
||||
stateTujuanProgram.findById.data = res.data?.data ?? null;
|
||||
} else {
|
||||
toast.error("Gagal mengambil data tujuan program");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengambil data tujuan program");
|
||||
} finally {
|
||||
stateTujuanProgram.findById.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
update: {
|
||||
loading: false,
|
||||
async save(data: TujuanProgramForm) {
|
||||
const cek = templateTujuanProgramForm.safeParse(data);
|
||||
if (!cek.success) {
|
||||
const errors = cek.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
stateTujuanProgram.update.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.bimbinganbelajardesa.tujuanprogram[
|
||||
"update"
|
||||
].post(data);
|
||||
if (res.status === 200) {
|
||||
toast.success("Data tujuan program berhasil diubah");
|
||||
await stateTujuanProgram.findById.load(data.id);
|
||||
} else {
|
||||
toast.error("Gagal mengubah data tujuan program");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengubah data tujuan program");
|
||||
} finally {
|
||||
stateTujuanProgram.update.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= LOKASI DAN JADWAL ========================================= //
|
||||
|
||||
const templateLokasiDanJadwalForm = z.object({
|
||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
});
|
||||
|
||||
type LokasiDanJadwalForm = Prisma.LokasiJadwalBimbinganBelajarDesaGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const lokasiDanJadwalState = proxy({
|
||||
findById: {
|
||||
data: null as LokasiDanJadwalForm | null,
|
||||
loading: false,
|
||||
initialize() {
|
||||
lokasiDanJadwalState.findById.data = {
|
||||
id: "",
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
} as LokasiDanJadwalForm;
|
||||
},
|
||||
async load(id: string) {
|
||||
try {
|
||||
lokasiDanJadwalState.findById.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.bimbinganbelajardesa.lokasidanjadwal[
|
||||
"find-by-id"
|
||||
].get({
|
||||
query: { id },
|
||||
});
|
||||
if (res.status === 200) {
|
||||
lokasiDanJadwalState.findById.data = res.data?.data ?? null;
|
||||
} else {
|
||||
toast.error("Gagal mengambil data lokasi dan jadwal");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengambil data lokasi dan jadwal");
|
||||
} finally {
|
||||
lokasiDanJadwalState.findById.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
loading: false,
|
||||
async save(data: LokasiDanJadwalForm) {
|
||||
const cek = templateLokasiDanJadwalForm.safeParse(data);
|
||||
if (!cek.success) {
|
||||
const errors = cek.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
lokasiDanJadwalState.update.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.bimbinganbelajardesa.lokasidanjadwal[
|
||||
"update"
|
||||
].post(data);
|
||||
if (res.status === 200) {
|
||||
toast.success("Data lokasi dan jadwal berhasil diubah");
|
||||
await lokasiDanJadwalState.findById.load(data.id);
|
||||
} else {
|
||||
toast.error("Gagal mengubah data lokasi dan jadwal");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengubah data lokasi dan jadwal");
|
||||
} finally {
|
||||
lokasiDanJadwalState.update.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= FASILITAS YANG DISEDIAKAN ========================================= //
|
||||
|
||||
const templateFasilitasYangDisediakanForm = z.object({
|
||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
});
|
||||
|
||||
type FasilitasYangDisediakanForm = Prisma.FasilitasBimbinganBelajarDesaGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const fasilitasYangDisediakanState = proxy({
|
||||
findById: {
|
||||
data: null as FasilitasYangDisediakanForm | null,
|
||||
loading: false,
|
||||
initialize() {
|
||||
fasilitasYangDisediakanState.findById.data = {
|
||||
id: "",
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
} as FasilitasYangDisediakanForm;
|
||||
},
|
||||
async load(id: string) {
|
||||
try {
|
||||
fasilitasYangDisediakanState.findById.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.bimbinganbelajardesa.fasilitasyangdisediakan[
|
||||
"find-by-id"
|
||||
].get({
|
||||
query: { id },
|
||||
});
|
||||
if (res.status === 200) {
|
||||
fasilitasYangDisediakanState.findById.data = res.data?.data ?? null;
|
||||
} else {
|
||||
toast.error("Gagal mengambil data fasilitas yang disediakan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengambil data fasilitas yang disediakan");
|
||||
} finally {
|
||||
fasilitasYangDisediakanState.findById.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
loading: false,
|
||||
async save(data: FasilitasYangDisediakanForm) {
|
||||
const cek = templateFasilitasYangDisediakanForm.safeParse(data);
|
||||
if (!cek.success) {
|
||||
const errors = cek.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
fasilitasYangDisediakanState.update.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.bimbinganbelajardesa.fasilitasyangdisediakan[
|
||||
"update"
|
||||
].post(data);
|
||||
if (res.status === 200) {
|
||||
toast.success("Data fasilitas yang disediakan berhasil diubah");
|
||||
await fasilitasYangDisediakanState.findById.load(data.id);
|
||||
} else {
|
||||
toast.error("Gagal mengubah data fasilitas yang disediakan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengubah data fasilitas yang disediakan");
|
||||
} finally {
|
||||
fasilitasYangDisediakanState.update.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const stateBimbinganBelajarDesa = proxy({
|
||||
stateTujuanProgram,
|
||||
lokasiDanJadwalState,
|
||||
fasilitasYangDisediakanState,
|
||||
});
|
||||
|
||||
export default stateBimbinganBelajarDesa;
|
||||
178
src/app/admin/(dashboard)/_state/pendidikan/data-pendidikan.ts
Normal file
178
src/app/admin/(dashboard)/_state/pendidikan/data-pendidikan.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateDataPendidikan = z.object({
|
||||
name: z.string().min(1, "Data nama harus diisi"),
|
||||
jumlah: z.string().min(1, "Data jumlah harus diisi"),
|
||||
});
|
||||
|
||||
type DataPendidikan = Prisma.DataPendidikanGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
name: true;
|
||||
jumlah: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const defaultForm: Omit<DataPendidikan, "id"> & { id?: string } = {
|
||||
name: "",
|
||||
jumlah: "",
|
||||
};
|
||||
|
||||
const dataPendidikan = proxy({
|
||||
create: {
|
||||
form: defaultForm,
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateDataPendidikan.safeParse(dataPendidikan.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
dataPendidikan.create.loading = true;
|
||||
const res = await ApiFetch.api.pendidikan.datapendidikan["create"].post(
|
||||
dataPendidikan.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
dataPendidikan.create.form = {
|
||||
name: "",
|
||||
jumlah: "",
|
||||
};
|
||||
dataPendidikan.findMany.load();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
dataPendidikan.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.DataPendidikanGetPayload<{
|
||||
select: { id: true; name: true; jumlah: true };
|
||||
}>[]
|
||||
| null,
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.pendidikan.datapendidikan[
|
||||
"findMany"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
dataPendidikan.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.DataPendidikanGetPayload<{
|
||||
select: { id: true; name: true; jumlah: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/pendidikan/datapendidikan/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
dataPendidikan.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
dataPendidikan.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading data pendidikan:", error);
|
||||
dataPendidikan.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async byId() {
|
||||
// Method implementation if needed
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
const cek = templateDataPendidikan.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => (v.path as string[]).join("."))
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await fetch(`/api/pendidikan/datapendidikan/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (!response.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
toast.success("Berhasil update data!");
|
||||
await dataPendidikan.findMany.load();
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Error update data data pendidikan:", error);
|
||||
toast.error("Gagal update data data pendidikan");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
dataPendidikan.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/datapendidikan/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Data berhasil dihapus");
|
||||
await dataPendidikan.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete data pendidikan:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus data pendidikan");
|
||||
} finally {
|
||||
dataPendidikan.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
export default dataPendidikan;
|
||||
1160
src/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud.ts
Normal file
1160
src/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,267 @@
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
// ========================================= TUJUAN PENDIDIKAN NON FORMAL ========================================= //
|
||||
|
||||
const templateTujuanPendidikanNonFormalForm = z.object({
|
||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
});
|
||||
|
||||
type TujuanPendidikanNonFormalForm =
|
||||
Prisma.TujuanPendidikanNonFormalGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const stateTujuanPendidikanNonFormal = proxy({
|
||||
findById: {
|
||||
data: null as TujuanPendidikanNonFormalForm | null,
|
||||
loading: false,
|
||||
initialize() {
|
||||
stateTujuanPendidikanNonFormal.findById.data = {
|
||||
id: "",
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
} as TujuanPendidikanNonFormalForm;
|
||||
},
|
||||
async load(id: string) {
|
||||
try {
|
||||
stateTujuanPendidikanNonFormal.findById.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.pendidikannonformal.tujuanpendidikannonformal[
|
||||
"find-by-id"
|
||||
].get({
|
||||
query: { id },
|
||||
});
|
||||
if (res.status === 200) {
|
||||
stateTujuanPendidikanNonFormal.findById.data = res.data?.data ?? null;
|
||||
} else {
|
||||
toast.error("Gagal mengambil data tujuan pendidikan non formal");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat mengambil data tujuan pendidikan non formal"
|
||||
);
|
||||
} finally {
|
||||
stateTujuanPendidikanNonFormal.findById.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
update: {
|
||||
loading: false,
|
||||
async save(data: TujuanPendidikanNonFormalForm) {
|
||||
const cek = templateTujuanPendidikanNonFormalForm.safeParse(data);
|
||||
if (!cek.success) {
|
||||
const errors = cek.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
stateTujuanPendidikanNonFormal.update.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.pendidikannonformal.tujuanpendidikannonformal[
|
||||
"update"
|
||||
].post(data);
|
||||
if (res.status === 200) {
|
||||
toast.success("Data tujuan pendidikan non formal berhasil diubah");
|
||||
await stateTujuanPendidikanNonFormal.findById.load(data.id);
|
||||
} else {
|
||||
toast.error("Gagal mengubah data tujuan pendidikan non formal");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat mengubah data tujuan pendidikan non formal"
|
||||
);
|
||||
} finally {
|
||||
stateTujuanPendidikanNonFormal.update.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= TEMPAT KEGIATAN ========================================= //
|
||||
|
||||
const templateTempatKegiatanForm = z.object({
|
||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
});
|
||||
|
||||
type TempatKegiatanForm = Prisma.TempatKegiatanGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const stateTempatKegiatan = proxy({
|
||||
findById: {
|
||||
data: null as TempatKegiatanForm | null,
|
||||
loading: false,
|
||||
initialize() {
|
||||
stateTempatKegiatan.findById.data = {
|
||||
id: "",
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
} as TempatKegiatanForm;
|
||||
},
|
||||
async load(id: string) {
|
||||
try {
|
||||
stateTempatKegiatan.findById.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.pendidikannonformal.tempatkegiatan[
|
||||
"find-by-id"
|
||||
].get({
|
||||
query: { id },
|
||||
});
|
||||
if (res.status === 200) {
|
||||
stateTempatKegiatan.findById.data = res.data?.data ?? null;
|
||||
} else {
|
||||
toast.error("Gagal mengambil data tempat kegiatan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengambil data tempat kegiatan");
|
||||
} finally {
|
||||
stateTempatKegiatan.findById.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
update: {
|
||||
loading: false,
|
||||
async save(data: TempatKegiatanForm) {
|
||||
const cek = templateTempatKegiatanForm.safeParse(data);
|
||||
if (!cek.success) {
|
||||
const errors = cek.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
stateTempatKegiatan.update.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.pendidikannonformal.tempatkegiatan[
|
||||
"update"
|
||||
].post(data);
|
||||
if (res.status === 200) {
|
||||
toast.success("Data tempat kegiatan berhasil diubah");
|
||||
await stateTempatKegiatan.findById.load(data.id);
|
||||
} else {
|
||||
toast.error("Gagal mengubah data tempat kegiatan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengubah data tempat kegiatan");
|
||||
} finally {
|
||||
stateTempatKegiatan.update.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= JENIS PROGRAM YANG DISELENGGARAKAN ========================================= //
|
||||
|
||||
const templateJenisProgramForm = z.object({
|
||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
});
|
||||
|
||||
type JenisProgramForm = Prisma.JenisProgramYangDiselenggarakanGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const stateJenisProgram = proxy({
|
||||
findById: {
|
||||
data: null as JenisProgramForm | null,
|
||||
loading: false,
|
||||
initialize() {
|
||||
stateJenisProgram.findById.data = {
|
||||
id: "",
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
} as JenisProgramForm;
|
||||
},
|
||||
async load(id: string) {
|
||||
try {
|
||||
stateJenisProgram.findById.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.pendidikannonformal.jenisprogramyangdiselenggarakan[
|
||||
"find-by-id"
|
||||
].get({
|
||||
query: { id },
|
||||
});
|
||||
if (res.status === 200) {
|
||||
stateJenisProgram.findById.data = res.data?.data ?? null;
|
||||
} else {
|
||||
toast.error("Gagal mengambil data jenis program");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengambil data jenis program");
|
||||
} finally {
|
||||
stateJenisProgram.findById.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
update: {
|
||||
loading: false,
|
||||
async save(data: JenisProgramForm) {
|
||||
const cek = templateJenisProgramForm.safeParse(data);
|
||||
if (!cek.success) {
|
||||
const errors = cek.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
stateJenisProgram.update.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.pendidikannonformal.jenisprogramyangdiselenggarakan[
|
||||
"update"
|
||||
].post(data);
|
||||
if (res.status === 200) {
|
||||
toast.success("Data jenis program berhasil diubah");
|
||||
await stateJenisProgram.findById.load(data.id);
|
||||
} else {
|
||||
toast.error("Gagal mengubah data jenis program");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengubah data jenis program");
|
||||
} finally {
|
||||
stateJenisProgram.update.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const pendidikanNonFormalState = proxy({
|
||||
stateTujuanPendidikanNonFormal,
|
||||
stateTempatKegiatan,
|
||||
stateJenisProgram,
|
||||
});
|
||||
|
||||
export default pendidikanNonFormalState;
|
||||
@@ -0,0 +1,522 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateDataPerpustakaan = z.object({
|
||||
judul: z.string().min(1, "Judul harus diisi"),
|
||||
deskripsi: z.string().min(1, "Deskripsi harus diisi"),
|
||||
imageId: z.string().min(1, "Image ID harus diisi"),
|
||||
kategoriId: z.string().min(1, "Kategori ID harus diisi"),
|
||||
});
|
||||
|
||||
const defaultDataPerpustakaan = {
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
kategoriId: "",
|
||||
};
|
||||
|
||||
const dataPerpustakaan = proxy({
|
||||
create: {
|
||||
form: { ...defaultDataPerpustakaan },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateDataPerpustakaan.safeParse(
|
||||
dataPerpustakaan.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
dataPerpustakaan.create.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.perpustakaandigital.dataperpustakaan[
|
||||
"create"
|
||||
].post(dataPerpustakaan.create.form);
|
||||
if (res.status === 200) {
|
||||
dataPerpustakaan.findMany.load();
|
||||
return toast.success("Data Data Perpustakaan Berhasil Dibuat");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return toast.error("failed create");
|
||||
} finally {
|
||||
dataPerpustakaan.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.DataPerpustakaanGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategori: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||
dataPerpustakaan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
dataPerpustakaan.findMany.page = page;
|
||||
dataPerpustakaan.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (kategori) query.kategori = kategori;
|
||||
|
||||
const res = await ApiFetch.api.pendidikan.perpustakaandigital.dataperpustakaan["findMany"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
dataPerpustakaan.findMany.data = res.data.data ?? [];
|
||||
dataPerpustakaan.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
dataPerpustakaan.findMany.data = [];
|
||||
dataPerpustakaan.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch data perpustakaan paginated:", err);
|
||||
dataPerpustakaan.findMany.data = [];
|
||||
dataPerpustakaan.findMany.totalPages = 1;
|
||||
} finally {
|
||||
dataPerpustakaan.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.DataPerpustakaanGetPayload<{
|
||||
include: {
|
||||
kategori: true;
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/pendidikan/perpustakaandigital/dataperpustakaan/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
dataPerpustakaan.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
dataPerpustakaan.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
dataPerpustakaan.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async delete(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
dataPerpustakaan.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/perpustakaandigital/dataperpustakaan/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Data Perpustakaan berhasil dihapus");
|
||||
await dataPerpustakaan.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus Data Perpustakaan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus Data Perpustakaan");
|
||||
} finally {
|
||||
dataPerpustakaan.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultDataPerpustakaan },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/perpustakaandigital/dataperpustakaan/${id}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
judul: data.judul,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId,
|
||||
kategoriId: data.kategoriId,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading perpustakaan digital:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateDataPerpustakaan.safeParse(
|
||||
dataPerpustakaan.update.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
dataPerpustakaan.update.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/perpustakaandigital/dataperpustakaan/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
judul: this.form.judul,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
kategoriId: this.form.kategoriId,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update data perpustakaan digital");
|
||||
await dataPerpustakaan.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal update data perpustakaan digital"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating data perpustakaan digital:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update data perpustakaan digital"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
dataPerpustakaan.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
dataPerpustakaan.update.id = "";
|
||||
dataPerpustakaan.update.form = { ...defaultDataPerpustakaan };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const templateKategoriBuku = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
});
|
||||
|
||||
const defaultKategoriBuku = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
const kategoriBuku = proxy({
|
||||
create: {
|
||||
form: { ...defaultKategoriBuku },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateKategoriBuku.safeParse(kategoriBuku.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kategoriBuku.create.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.perpustakaandigital.kategoribuku[
|
||||
"create"
|
||||
].post(kategoriBuku.create.form);
|
||||
if (res.status === 200) {
|
||||
kategoriBuku.findMany.load();
|
||||
return toast.success("Data Kategori Buku Berhasil Dibuat");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return toast.error("failed create");
|
||||
} finally {
|
||||
kategoriBuku.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as Prisma.KategoriBukuGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[],
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
kategoriBuku.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
kategoriBuku.findMany.page = page;
|
||||
kategoriBuku.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.pendidikan.perpustakaandigital.kategoribuku["findMany"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kategoriBuku.findMany.data = res.data.data ?? [];
|
||||
kategoriBuku.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
kategoriBuku.findMany.data = [];
|
||||
kategoriBuku.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch data kategori buku paginated:", err);
|
||||
kategoriBuku.findMany.data = [];
|
||||
kategoriBuku.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kategoriBuku.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KategoriBukuGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/pendidikan/perpustakaandigital/kategoribuku/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kategoriBuku.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
kategoriBuku.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
kategoriBuku.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async delete(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
kategoriBuku.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/perpustakaandigital/kategoribuku/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "Data Kategori Buku berhasil dihapus"
|
||||
);
|
||||
await kategoriBuku.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus Data Kategori Buku");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus Data Kategori Buku");
|
||||
} finally {
|
||||
kategoriBuku.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultKategoriBuku },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/perpustakaandigital/kategoribuku/${id}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori buku:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateKategoriBuku.safeParse(kategoriBuku.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
kategoriBuku.update.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/pendidikan/perpustakaandigital/kategoribuku/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update data kategori buku");
|
||||
await kategoriBuku.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update data kategori buku");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating data kategori buku:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update data kategori buku"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
kategoriBuku.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
kategoriBuku.update.id = "";
|
||||
kategoriBuku.update.form = { ...defaultKategoriBuku };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const perpustakaanDigitalState = proxy({
|
||||
dataPerpustakaan,
|
||||
kategoriBuku,
|
||||
});
|
||||
|
||||
export default perpustakaanDigitalState;
|
||||
@@ -0,0 +1,181 @@
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
// ========================================= TUJUAN PROGRAM ========================================= //
|
||||
|
||||
const templateTujuanProgramForm = z.object({
|
||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
});
|
||||
|
||||
type TujuanProgramForm = Prisma.TujuanProgramGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const stateTujuanProgram = proxy({
|
||||
findById: {
|
||||
data: null as TujuanProgramForm | null,
|
||||
loading: false,
|
||||
initialize() {
|
||||
stateTujuanProgram.findById.data = {
|
||||
id: "",
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
} as TujuanProgramForm;
|
||||
},
|
||||
async load(id: string) {
|
||||
try {
|
||||
stateTujuanProgram.findById.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.programpendidikananak.tujuanprogram[
|
||||
"find-by-id"
|
||||
].get({
|
||||
query: { id },
|
||||
});
|
||||
if (res.status === 200) {
|
||||
stateTujuanProgram.findById.data = res.data?.data ?? null;
|
||||
} else {
|
||||
toast.error("Gagal mengambil data tujuan program");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengambil data tujuan program");
|
||||
} finally {
|
||||
stateTujuanProgram.findById.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
update: {
|
||||
loading: false,
|
||||
async save(data: TujuanProgramForm) {
|
||||
const cek = templateTujuanProgramForm.safeParse(data);
|
||||
if (!cek.success) {
|
||||
const errors = cek.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
stateTujuanProgram.update.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.programpendidikananak.tujuanprogram[
|
||||
"update"
|
||||
].post(data);
|
||||
if (res.status === 200) {
|
||||
toast.success("Data tujuan program berhasil diubah");
|
||||
await stateTujuanProgram.findById.load(data.id);
|
||||
} else {
|
||||
toast.error("Gagal mengubah data tujuan program");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengubah data tujuan program");
|
||||
} finally {
|
||||
stateTujuanProgram.update.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= PROGRAM UNGGULAN ========================================= //
|
||||
|
||||
const templateProgramUnggulanForm = z.object({
|
||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
});
|
||||
|
||||
type ProgramUnggulanForm = Prisma.ProgramUnggulanGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
const programUnggulanState = proxy({
|
||||
findById: {
|
||||
data: null as ProgramUnggulanForm | null,
|
||||
loading: false,
|
||||
initialize() {
|
||||
programUnggulanState.findById.data = {
|
||||
id: "",
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
} as ProgramUnggulanForm;
|
||||
},
|
||||
async load(id: string) {
|
||||
try {
|
||||
programUnggulanState.findById.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.programpendidikananak.programunggulan[
|
||||
"find-by-id"
|
||||
].get({
|
||||
query: { id },
|
||||
});
|
||||
if (res.status === 200) {
|
||||
programUnggulanState.findById.data = res.data?.data ?? null;
|
||||
} else {
|
||||
toast.error("Gagal mengambil data program pendidikan anak");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat mengambil data program pendidikan anak"
|
||||
);
|
||||
} finally {
|
||||
programUnggulanState.findById.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
loading: false,
|
||||
async save(data: ProgramUnggulanForm) {
|
||||
const cek = templateProgramUnggulanForm.safeParse(data);
|
||||
if (!cek.success) {
|
||||
const errors = cek.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
programUnggulanState.update.loading = true;
|
||||
const res =
|
||||
await ApiFetch.api.pendidikan.programpendidikananak.programunggulan[
|
||||
"update"
|
||||
].post(data);
|
||||
if (res.status === 200) {
|
||||
toast.success("Data program pendidikan anak berhasil diubah");
|
||||
await programUnggulanState.findById.load(data.id);
|
||||
} else {
|
||||
toast.error("Gagal mengubah data program pendidikan anak");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat mengubah data program pendidikan anak"
|
||||
);
|
||||
} finally {
|
||||
programUnggulanState.update.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const stateProgramPendidikanAnak = proxy({
|
||||
stateTujuanProgram,
|
||||
programUnggulanState,
|
||||
});
|
||||
|
||||
export default stateProgramPendidikanAnak;
|
||||
@@ -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;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { z } from "zod";
|
||||
const templateGrafikJenisKelamin = z.object({
|
||||
laki: z.string().min(1, "Data laki-laki harus diisi"),
|
||||
perempuan: z.string().min(1, "Data perempuan harus diisi"),
|
||||
});
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
laki: "",
|
||||
@@ -17,10 +17,12 @@ const defaultForm = {
|
||||
|
||||
const grafikBerdasarkanJenisKelamin = proxy({
|
||||
create: {
|
||||
form: {...defaultForm},
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create(){
|
||||
const cek = templateGrafikJenisKelamin.safeParse(grafikBerdasarkanJenisKelamin.create.form);
|
||||
async create() {
|
||||
const cek = templateGrafikJenisKelamin.safeParse(
|
||||
grafikBerdasarkanJenisKelamin.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = cek.error.issues.map((i) => i.message).join("\n");
|
||||
toast.error(err);
|
||||
@@ -33,14 +35,20 @@ const grafikBerdasarkanJenisKelamin = proxy({
|
||||
"create"
|
||||
].post(grafikBerdasarkanJenisKelamin.create.form);
|
||||
if (res.status === 200) {
|
||||
toast.success("Grafik berdasarkan jenis kelamin berhasil ditambahkan");
|
||||
toast.success(
|
||||
"Grafik berdasarkan jenis kelamin berhasil ditambahkan"
|
||||
);
|
||||
await grafikBerdasarkanJenisKelamin.findMany.load();
|
||||
} else {
|
||||
toast.error(res.data?.message ?? "Gagal tambah grafik berdasarkan jenis kelamin");
|
||||
toast.error(
|
||||
res.data?.message ?? "Gagal tambah grafik berdasarkan jenis kelamin"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal create:", error);
|
||||
toast.error("Terjadi kesalahan saat menambahkan grafik berdasarkan jenis kelamin");
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat menambahkan grafik berdasarkan jenis kelamin"
|
||||
);
|
||||
} finally {
|
||||
grafikBerdasarkanJenisKelamin.create.loading = false;
|
||||
}
|
||||
@@ -52,8 +60,9 @@ const grafikBerdasarkanJenisKelamin = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
grafikBerdasarkanJenisKelamin.findMany.loading = true; // Use the full path to access the property
|
||||
load: async (page = 1, limit = 10) => {
|
||||
// Change to arrow function
|
||||
grafikBerdasarkanJenisKelamin.findMany.loading = true; // Use the full path to access the property
|
||||
grafikBerdasarkanJenisKelamin.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.ppid.grafikberdasarkanjeniskelamin[
|
||||
@@ -61,13 +70,17 @@ const grafikBerdasarkanJenisKelamin = proxy({
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
grafikBerdasarkanJenisKelamin.findMany.data = res.data.data || [];
|
||||
grafikBerdasarkanJenisKelamin.findMany.total = res.data.total || 0;
|
||||
grafikBerdasarkanJenisKelamin.findMany.totalPages = res.data.totalPages || 1;
|
||||
grafikBerdasarkanJenisKelamin.findMany.totalPages =
|
||||
res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load grafik berdasarkan jenis kelamin:", res.data?.message);
|
||||
console.error(
|
||||
"Failed to load grafik berdasarkan jenis kelamin:",
|
||||
res.data?.message
|
||||
);
|
||||
grafikBerdasarkanJenisKelamin.findMany.data = [];
|
||||
grafikBerdasarkanJenisKelamin.findMany.total = 0;
|
||||
grafikBerdasarkanJenisKelamin.findMany.totalPages = 1;
|
||||
@@ -106,7 +119,7 @@ const grafikBerdasarkanJenisKelamin = proxy({
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: {...defaultForm},
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async byId() {
|
||||
// Method implementation if needed
|
||||
@@ -119,20 +132,24 @@ const grafikBerdasarkanJenisKelamin = proxy({
|
||||
}
|
||||
const cek = templateGrafikJenisKelamin.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`;
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/ppid/grafikberdasarkanjeniskelamin/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
`/api/ppid/grafikberdasarkanjeniskelamin/${id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
);
|
||||
const result = await response.json();
|
||||
if (!response.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
@@ -156,29 +173,40 @@ const grafikBerdasarkanJenisKelamin = proxy({
|
||||
try {
|
||||
grafikBerdasarkanJenisKelamin.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/ppid/grafikberdasarkanjeniskelamin/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const response = await fetch(
|
||||
`/api/ppid/grafikberdasarkanjeniskelamin/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Grafik berdasarkan jenis kelamin berhasil dihapus");
|
||||
toast.success(
|
||||
result.message ||
|
||||
"Grafik berdasarkan jenis kelamin berhasil dihapus"
|
||||
);
|
||||
await grafikBerdasarkanJenisKelamin.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus grafik berdasarkan jenis kelamin");
|
||||
toast.error(
|
||||
result?.message ||
|
||||
"Gagal menghapus grafik berdasarkan jenis kelamin"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus grafik berdasarkan jenis kelamin");
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat menghapus grafik berdasarkan jenis kelamin"
|
||||
);
|
||||
} finally {
|
||||
grafikBerdasarkanJenisKelamin.delete.loading = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default grafikBerdasarkanJenisKelamin;
|
||||
|
||||
@@ -1,169 +1,706 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||
})
|
||||
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
imageId: "",
|
||||
name: "",
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
type StrukturPPIDForm = Prisma.StrukturPPIDGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
name: true;
|
||||
imageId: true;
|
||||
image?: {
|
||||
select: {
|
||||
link: true;
|
||||
};
|
||||
};
|
||||
select: {
|
||||
id: true;
|
||||
name: true;
|
||||
imageId: true;
|
||||
image?: {
|
||||
select: {
|
||||
link: true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>;
|
||||
|
||||
const stateStrukturPPID = proxy({
|
||||
struktur: {
|
||||
data: null as StrukturPPIDForm | null,
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
const stateStruktur = proxy({
|
||||
struktur: {
|
||||
data: null as StrukturPPIDForm | null,
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
|
||||
async load(id: string) {
|
||||
if(!id) {
|
||||
toast.warn("ID tidak valid")
|
||||
return null
|
||||
}
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/ppid/strukturppid/${id}`);
|
||||
|
||||
if(!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
try {
|
||||
const response = await fetch(`/api/ppid/strukturppid/${id}`);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if(result.success) {
|
||||
this.data = result.data;
|
||||
return result.data
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal mengambil data struktur")
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Load struktur error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat mengambil data struktur");
|
||||
return null;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.data = null;
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
},
|
||||
|
||||
editStruktur: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
isReadOnly: false,
|
||||
const result = await response.json();
|
||||
|
||||
initialize(strukturData: StrukturPPIDForm) {
|
||||
this.id = strukturData.id;
|
||||
this.isReadOnly = false;
|
||||
this.form = {
|
||||
name: strukturData.name || "",
|
||||
imageId: strukturData.imageId || "",
|
||||
};
|
||||
},
|
||||
|
||||
updateField(field: keyof typeof defaultForm, value: string) {
|
||||
this.form[field] = value;
|
||||
},
|
||||
|
||||
async submit() {
|
||||
const validation = templateForm.safeParse(this.form);
|
||||
|
||||
if (!validation.success) {
|
||||
const errors = validation.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/ppid/strukturppid/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update struktur");
|
||||
await stateStrukturPPID.struktur.load(this.id);
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update struktur");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Update struktur error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat update struktur");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...defaultForm };
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
this.isReadOnly = false;
|
||||
if (result.success) {
|
||||
this.data = result.data;
|
||||
return result.data;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal mengambil data struktur");
|
||||
}
|
||||
},
|
||||
|
||||
async loadForEdit(id: string) {
|
||||
const strukturData = await this.struktur.load(id);
|
||||
if (strukturData) {
|
||||
this.editStruktur.initialize(strukturData);
|
||||
}
|
||||
return strukturData;
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Load struktur error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat mengambil data struktur");
|
||||
return null;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.struktur.reset();
|
||||
this.editStruktur.reset();
|
||||
this.data = null;
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
},
|
||||
},
|
||||
|
||||
editStruktur: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
isReadOnly: false,
|
||||
|
||||
initialize(strukturData: StrukturPPIDForm) {
|
||||
this.id = strukturData.id;
|
||||
this.isReadOnly = false;
|
||||
this.form = {
|
||||
name: strukturData.name || "",
|
||||
imageId: strukturData.imageId || "",
|
||||
};
|
||||
},
|
||||
|
||||
updateField(field: keyof typeof defaultForm, value: string) {
|
||||
this.form[field] = value;
|
||||
},
|
||||
|
||||
async submit() {
|
||||
const validation = templateForm.safeParse(this.form);
|
||||
|
||||
if (!validation.success) {
|
||||
const errors = validation.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/ppid/strukturppid/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update struktur");
|
||||
await stateStruktur.struktur.load(this.id);
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update struktur");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Update struktur error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat update struktur");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...defaultForm };
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
this.isReadOnly = false;
|
||||
},
|
||||
},
|
||||
|
||||
async loadForEdit(id: string) {
|
||||
const strukturData = await this.struktur.load(id);
|
||||
if (strukturData) {
|
||||
this.editStruktur.initialize(strukturData);
|
||||
}
|
||||
})
|
||||
return strukturData;
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.struktur.reset();
|
||||
this.editStruktur.reset();
|
||||
},
|
||||
});
|
||||
|
||||
const templatePosisiOrganisasi = z.object({
|
||||
nama: z.string().min(1, "Nama harus diisi"),
|
||||
deskripsi: z.string().optional(),
|
||||
hierarki: z.number().int().positive("Hierarki harus angka positif"),
|
||||
});
|
||||
|
||||
const posisiOrganisasiDefaultForm = {
|
||||
nama: "",
|
||||
deskripsi: "",
|
||||
hierarki: 0,
|
||||
};
|
||||
|
||||
const posisiOrganisasi = proxy({
|
||||
create: {
|
||||
form: { ...posisiOrganisasiDefaultForm },
|
||||
loading: false,
|
||||
async submit() {
|
||||
const cek = templatePosisiOrganisasi.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = cek.error.issues.map((v) => v.message).join("\n");
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi[
|
||||
"create"
|
||||
].post(this.form);
|
||||
if (res.status === 200) {
|
||||
toast.success("Berhasil menambahkan posisi organisasi");
|
||||
posisiOrganisasi.findMany.load();
|
||||
this.reset();
|
||||
} else {
|
||||
toast.error(res.data?.message || "Gagal menambahkan posisi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Create error:", error);
|
||||
toast.error("Terjadi kesalahan saat menambahkan posisi");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.form = { ...posisiOrganisasiDefaultForm };
|
||||
},
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as Prisma.StrukturOrganisasiPPIDGetPayload<{
|
||||
omit: { isActive: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/ppid/strukturppid/posisiorganisasi/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
posisiOrganisasi.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch posisiOrganisasi:", res.statusText);
|
||||
posisiOrganisasi.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching posisiOrganisasi:", error);
|
||||
posisiOrganisasi.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...posisiOrganisasiDefaultForm },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/ppid/strukturppid/posisiorganisasi/${id}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
nama: data.nama,
|
||||
deskripsi: data.deskripsi,
|
||||
hierarki: data.hierarki,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading posisi organisasi:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templatePosisiOrganisasi.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const response = await fetch(
|
||||
`/api/ppid/strukturppid/posisiorganisasi/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nama: this.form.nama,
|
||||
deskripsi: this.form.deskripsi,
|
||||
hierarki: this.form.hierarki,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update posisi organisasi");
|
||||
await posisiOrganisasi.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal mengupdate posisi organisasi"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating posisi organisasi:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Gagal mengupdate posisi organisasi"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...posisiOrganisasiDefaultForm };
|
||||
},
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: [] as Array<{
|
||||
id: string;
|
||||
nama: string;
|
||||
deskripsi: string | null;
|
||||
hierarki: number;
|
||||
}>,
|
||||
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 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 (err) {
|
||||
console.error("Gagal fetch posisi organisasi paginated:", err);
|
||||
posisiOrganisasi.findMany.data = [];
|
||||
posisiOrganisasi.findMany.totalPages = 1;
|
||||
} finally {
|
||||
posisiOrganisasi.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
posisiOrganisasi.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/ppid/strukturppid/posisiorganisasi/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Posisi organisasi berhasil dihapus");
|
||||
await posisiOrganisasi.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus posisi organisasi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus posisi organisasi");
|
||||
} finally {
|
||||
posisiOrganisasi.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const templatePegawai = z.object({
|
||||
namaLengkap: z.string().min(1, "Nama wajib diisi"),
|
||||
gelarAkademik: z.string().min(1, "Gelar Akademik wajib diisi"),
|
||||
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||
tanggalMasuk: z.string().min(1, "Tanggal masuk wajib diisi"), // ISO format
|
||||
email: z.string().email("Email tidak valid").optional(),
|
||||
telepon: z.string().min(1, "Telepom wajib diisi"),
|
||||
alamat: z.string().min(1, "Alamat wajib diisi"),
|
||||
posisiId: z.string().min(1, "Posisi wajib diisi"),
|
||||
isActive: z.boolean().default(true),
|
||||
});
|
||||
|
||||
const pegawaiDefaultForm = {
|
||||
namaLengkap: "",
|
||||
gelarAkademik: "",
|
||||
imageId: "",
|
||||
tanggalMasuk: "",
|
||||
email: "",
|
||||
telepon: "",
|
||||
alamat: "",
|
||||
posisiId: "",
|
||||
isActive: true,
|
||||
};
|
||||
|
||||
const pegawai = proxy({
|
||||
create: {
|
||||
form: { ...pegawaiDefaultForm },
|
||||
loading: false,
|
||||
async submit() {
|
||||
const cek = templatePegawai.safeParse(pegawai.create.form);
|
||||
if (!cek.success) {
|
||||
const err = cek.error.issues.map((i) => i.message).join("\n");
|
||||
toast.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
pegawai.create.loading = true;
|
||||
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();
|
||||
} else {
|
||||
toast.error(res.data?.message ?? "Gagal tambah pegawai");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal create:", error);
|
||||
toast.error("Terjadi kesalahan saat menambahkan pegawai");
|
||||
} finally {
|
||||
pegawai.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// In struktur-organisasi.ts
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as
|
||||
| (Prisma.PegawaiGetPayload<{
|
||||
include: { posisi: true; image: true };
|
||||
}> & { isActive: boolean })
|
||||
| null,
|
||||
async load(id: string) {
|
||||
const res = await fetch(`/api/ppid/strukturppid/pegawai/${id}`);
|
||||
if (res.ok) {
|
||||
const json = await res.json();
|
||||
pegawai.findUnique.data = json.data
|
||||
? {
|
||||
...json.data,
|
||||
isActive: json.data.isActive ?? json.data.aktif ?? true, // Fallback ke aktif:true jika tidak ada data
|
||||
}
|
||||
: null;
|
||||
} else {
|
||||
pegawai.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
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 json = await res.json();
|
||||
if (res.ok) {
|
||||
toast.success(json.message ?? "Berhasil hapus pegawai");
|
||||
await pegawai.findMany.load();
|
||||
} else {
|
||||
toast.error(json.message ?? "Gagal hapus pegawai");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus");
|
||||
} finally {
|
||||
pegawai.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...pegawaiDefaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
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}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
namaLengkap: data.namaLengkap,
|
||||
gelarAkademik: data.gelarAkademik,
|
||||
imageId: data.imageId,
|
||||
tanggalMasuk: data.tanggalMasuk,
|
||||
email: data.email,
|
||||
telepon: data.telepon,
|
||||
alamat: data.alamat,
|
||||
posisiId: data.posisiId,
|
||||
isActive: data.isActive,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading berita:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async submit() {
|
||||
const cek = templatePegawai.safeParse(pegawai.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
pegawai.edit.loading = true;
|
||||
|
||||
// Format tanggalMasuk to ISO string if it exists
|
||||
const formattedTanggalMasuk = this.form.tanggalMasuk
|
||||
? new Date(this.form.tanggalMasuk).toISOString()
|
||||
: undefined;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/ppid/strukturppid/pegawai/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: this.id,
|
||||
namaLengkap: this.form.namaLengkap,
|
||||
gelarAkademik: this.form.gelarAkademik,
|
||||
imageId: this.form.imageId || null,
|
||||
tanggalMasuk: formattedTanggalMasuk,
|
||||
email: this.form.email,
|
||||
telepon: this.form.telepon,
|
||||
alamat: this.form.alamat,
|
||||
posisiId: this.form.posisiId,
|
||||
isActive: this.form.isActive,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update pegawai");
|
||||
await pegawai.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update pegawai");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating pegawai:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update pegawai"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
pegawai.edit.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
pegawai.edit.id = "";
|
||||
pegawai.edit.form = { ...pegawaiDefaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const stateStrukturPPID = proxy({
|
||||
stateStruktur,
|
||||
posisiOrganisasi,
|
||||
pegawai,
|
||||
});
|
||||
|
||||
export default stateStrukturPPID;
|
||||
|
||||
63
src/app/admin/(dashboard)/_state/state-file-storage.ts
Normal file
63
src/app/admin/(dashboard)/_state/state-file-storage.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { proxy } from "valtio";
|
||||
|
||||
interface FileItem {
|
||||
id: string;
|
||||
name: string;
|
||||
path: string;
|
||||
link: string;
|
||||
mimeType: string;
|
||||
category: string;
|
||||
realName: string;
|
||||
isActive: boolean;
|
||||
createdAt: string | Date;
|
||||
updatedAt: string | Date;
|
||||
deletedAt: string | Date | null;
|
||||
}
|
||||
|
||||
const stateFileStorage = proxy<{
|
||||
list: FileItem[] | null;
|
||||
page: number;
|
||||
limit: number;
|
||||
total: number | undefined;
|
||||
load: (params?: { search?: string }) => Promise<void>;
|
||||
del: (params: { id: string }) => Promise<void>;
|
||||
}>({
|
||||
list: null,
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: undefined,
|
||||
async load(params?: { search?: string }) {
|
||||
const { search = "" } = params ?? {};
|
||||
try {
|
||||
const { data } = await ApiFetch.api.fileStorage.findMany.get({
|
||||
query: {
|
||||
page: this.page,
|
||||
limit: this.limit,
|
||||
search,
|
||||
category: 'image'
|
||||
},
|
||||
});
|
||||
|
||||
if (data?.data) {
|
||||
this.list = data.data as FileItem[];
|
||||
this.total = data.meta?.totalPages;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading files:', error);
|
||||
this.list = [];
|
||||
this.total = 0;
|
||||
}
|
||||
},
|
||||
async del({ id }: { id: string }) {
|
||||
try {
|
||||
await ApiFetch.api.fileStorage.delete({ id });
|
||||
await this.load();
|
||||
} catch (error) {
|
||||
console.error('Error deleting file:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default stateFileStorage;
|
||||
311
src/app/admin/(dashboard)/_state/user/user-state.ts
Normal file
311
src/app/admin/(dashboard)/_state/user/user-state.ts
Normal file
@@ -0,0 +1,311 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { proxy } from "valtio";
|
||||
import { toast } from "react-toastify";
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { z } from "zod";
|
||||
|
||||
// State Valtio
|
||||
const userState = proxy({
|
||||
// Find Many
|
||||
findMany: {
|
||||
data: [] as Prisma.UserGetPayload<{ include: { role: true } }>[],
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
userState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
userState.findMany.page = page;
|
||||
userState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.user["findMany"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
userState.findMany.data = res.data.data ?? [];
|
||||
userState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
userState.findMany.data = [];
|
||||
userState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch user paginated:", err);
|
||||
userState.findMany.data = [];
|
||||
userState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
userState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// Find Unique
|
||||
findUnique: {
|
||||
data: null as Prisma.UserGetPayload<{ include: { role: true } }> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await fetch(`/api/user/findUnique/${id}`);
|
||||
const data = await res.json();
|
||||
if (res.status === 200) {
|
||||
this.data = data.data;
|
||||
} else {
|
||||
toast.error(data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error("Gagal ambil data user");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// Delete (Soft Delete)
|
||||
delete: {
|
||||
loading: false,
|
||||
async submit(id: string) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await fetch(`/api/user/del/${id}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.status === 200) {
|
||||
toast.success("User dinonaktifkan");
|
||||
userState.findMany.load();
|
||||
} else {
|
||||
toast.error(data.message || "Gagal hapus");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error("Gagal hapus user");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const templateRole = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
permissions: z.array(z.string()).min(1, "Permission harus diisi"),
|
||||
});
|
||||
|
||||
const defaultRole = {
|
||||
name: "",
|
||||
permissions: [] as string[],
|
||||
};
|
||||
|
||||
const roleState = proxy({
|
||||
create: {
|
||||
form: { ...defaultRole },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateRole.safeParse(roleState.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
roleState.create.loading = true;
|
||||
const res = await ApiFetch.api.role["create"].post(
|
||||
roleState.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
roleState.findMany.load();
|
||||
return toast.success("Data role Berhasil Dibuat");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return toast.error("failed create");
|
||||
} finally {
|
||||
roleState.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as Prisma.RoleGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[],
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.role["findMany"].get();
|
||||
if (res.status === 200) {
|
||||
roleState.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.RoleGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/role/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
roleState.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
roleState.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
roleState.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async delete(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
roleState.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/role/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Data role berhasil dihapus");
|
||||
await roleState.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus Data role");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus Data role");
|
||||
} finally {
|
||||
roleState.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultRole },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/role/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
permissions: data.permissions,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading role:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateRole.safeParse(roleState.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
roleState.update.loading = true;
|
||||
|
||||
const response = await fetch(`/api/role/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
permissions: this.form.permissions,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update data role");
|
||||
await roleState.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update data role");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating data role:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update data role"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
roleState.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
roleState.update.id = "";
|
||||
roleState.update.form = { ...defaultRole };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const user = proxy({
|
||||
userState,
|
||||
roleState,
|
||||
});
|
||||
|
||||
export default user;
|
||||
@@ -5,30 +5,21 @@ import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core'
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
function LayoutTabs({ children }: { children: React.ReactNode }) {
|
||||
function LayoutTabsBerita({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const tabs = [
|
||||
{
|
||||
label: "Grafik Hasil Kepuasan Masyarakat",
|
||||
value: "grafikhasilkepuasamanmasyarakat",
|
||||
href: "/admin/ppid/ikm-desa-darmasaba/grafik_hasil_kepuasan_masyarakat"
|
||||
label: "List Berita",
|
||||
value: "list_berita",
|
||||
href: "/admin/desa/berita/list-berita"
|
||||
},
|
||||
{
|
||||
label: "Grafik Berdasarkan Jenis Kelamin Responden",
|
||||
value: "grafikberdasarkanjeniskelaminresponden",
|
||||
href: "/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_jenis_kelamin_responden"
|
||||
label: "Kategori Berita",
|
||||
value: "kategori_berita",
|
||||
href: "/admin/desa/berita/kategori-berita"
|
||||
},
|
||||
{
|
||||
label: "Grafik Berdasarkan Pilihan Responden",
|
||||
value: "grafikberdasarkanpilihanresponden",
|
||||
href: "/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_responden"
|
||||
},
|
||||
{
|
||||
label: "Grafik Berdasarkan Umur Responden",
|
||||
value: "grafikberdasarkanumurresponden",
|
||||
href: "/admin/ppid/ikm-desa-darmasaba/grafik_berdasarkan_umur"
|
||||
}
|
||||
|
||||
];
|
||||
const curentTab = tabs.find(tab => tab.href === pathname)
|
||||
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
|
||||
@@ -50,7 +41,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={3}>Indeks Kepuasan Masyarakat (IKM) Desa Darmasaba</Title>
|
||||
<Title order={3}>Gallery</Title>
|
||||
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
|
||||
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
||||
{tabs.map((e, i) => (
|
||||
@@ -69,4 +60,4 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default LayoutTabs;
|
||||
export default LayoutTabsBerita;
|
||||
@@ -1,186 +0,0 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Center, FileInput, Image, Paper, Select, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import CreateEditor from '../../../_com/createEditor';
|
||||
import stateDashboardBerita from '../../../_state/desa/berita';
|
||||
|
||||
export default function CreateBerita() {
|
||||
const beritaState = useProxy(stateDashboardBerita);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const router = useRouter()
|
||||
|
||||
const resetForm = () => {
|
||||
// Reset state di valtio
|
||||
beritaState.berita.create.form = {
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
kategoriBeritaId: "",
|
||||
imageId: "",
|
||||
content: "",
|
||||
};
|
||||
|
||||
// Reset state lokal
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!file) {
|
||||
return toast.warn("Pilih file gambar terlebih dahulu");
|
||||
}
|
||||
|
||||
// Upload gambar dulu
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file,
|
||||
name: file.name,
|
||||
});
|
||||
|
||||
const uploaded = res.data?.data;
|
||||
if (!uploaded?.id) {
|
||||
return toast.error("Gagal upload gambar");
|
||||
}
|
||||
|
||||
// Simpan ID gambar ke form
|
||||
beritaState.berita.create.form.imageId = uploaded.id;
|
||||
|
||||
// Submit data berita
|
||||
await beritaState.berita.create.create();
|
||||
|
||||
// Reset form setelah submit
|
||||
resetForm();
|
||||
router.push("/admin/desa/berita")
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Create Berita</Title>
|
||||
<TextInput
|
||||
value={beritaState.berita.create.form.judul}
|
||||
onChange={(val) => {
|
||||
beritaState.berita.create.form.judul = val.target.value;
|
||||
}}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
<SelectCategory
|
||||
value={beritaState.berita.create.form.kategoriBeritaId}
|
||||
onChange={(val) => {
|
||||
beritaState.berita.create.form.kategoriBeritaId = val?.id || "";
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
value={beritaState.berita.create.form.deskripsi}
|
||||
onChange={(val) => {
|
||||
beritaState.berita.create.form.deskripsi = val.target.value;
|
||||
}}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
|
||||
placeholder="masukkan deskripsi"
|
||||
/>
|
||||
|
||||
<FileInput
|
||||
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>}
|
||||
value={file}
|
||||
onChange={async (e) => {
|
||||
if (!e) return;
|
||||
setFile(e);
|
||||
const base64 = await e.arrayBuffer().then((buf) =>
|
||||
"data:image/png;base64," + Buffer.from(buf).toString("base64")
|
||||
);
|
||||
setPreviewImage(base64);
|
||||
}}
|
||||
/>
|
||||
{previewImage ? (
|
||||
<Image alt="" src={previewImage} w={200} h={200} />
|
||||
) : (
|
||||
<Center w={200} h={200} bg={"gray"}>
|
||||
<IconImageInPicture />
|
||||
</Center>
|
||||
)}
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||
<CreateEditor
|
||||
value={beritaState.berita.create.form.content}
|
||||
onChange={(htmlContent) => {
|
||||
beritaState.berita.create.form.content = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan Berita</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
|
||||
interface SelectCategoryProps {
|
||||
onChange: (value: Prisma.KategoriBeritaGetPayload<{
|
||||
select: {
|
||||
name: true;
|
||||
id: true;
|
||||
};
|
||||
}> | null) => void;
|
||||
value?: string | null;
|
||||
defaultValue?: string | null;
|
||||
}
|
||||
|
||||
function SelectCategory({
|
||||
onChange,
|
||||
value,
|
||||
defaultValue,
|
||||
}: SelectCategoryProps) {
|
||||
const categoryState = useProxy(stateDashboardBerita.category);
|
||||
|
||||
useShallowEffect(() => {
|
||||
categoryState.findMany.load().then(() => {
|
||||
console.log("Kategori berhasil dimuat:", categoryState.findMany.data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!categoryState.findMany.data) {
|
||||
return <Skeleton height={38} />;
|
||||
}
|
||||
|
||||
|
||||
const selectedValue = value || defaultValue;
|
||||
|
||||
return (
|
||||
<Select
|
||||
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
||||
placeholder="Pilih kategori"
|
||||
data={categoryState.findMany.data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={selectedValue || null}
|
||||
onChange={(val: string | null) => {
|
||||
if (val) {
|
||||
const selected = categoryState.findMany.data?.find((item) => item.id === val);
|
||||
if (selected) {
|
||||
onChange(selected);
|
||||
}
|
||||
} else {
|
||||
onChange(null);
|
||||
}
|
||||
}}
|
||||
searchable
|
||||
clearable
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function EditKategoriBerita() {
|
||||
const editState = useProxy(stateDashboardBerita.kategoriBerita)
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [formData, setFormData] = useState({
|
||||
name: editState.update.form.name || '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const loadKategori = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await editState.update.load(id); // akses langsung, bukan dari proxy
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori Berita:", error);
|
||||
toast.error("Gagal memuat data kategori Berita");
|
||||
}
|
||||
};
|
||||
|
||||
loadKategori();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
editState.update.form = {
|
||||
...editState.update.form,
|
||||
name: formData.name,
|
||||
};
|
||||
await editState.update.update();
|
||||
toast.success('Kategori Berita berhasil diperbarui!');
|
||||
router.push('/admin/desa/berita/kategori-berita');
|
||||
} catch (error) {
|
||||
console.error('Error updating kategori Berita:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui kategori Berita');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors["blue-button"]} size={30} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Edit Kategori Berita</Title>
|
||||
<TextInput
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Nama Kategori Berita</Text>}
|
||||
placeholder="masukkan nama kategori Berita"
|
||||
/>
|
||||
|
||||
<Button onClick={handleSubmit}>Simpan</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditKategoriBerita;
|
||||
@@ -0,0 +1,55 @@
|
||||
'use client'
|
||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
|
||||
function CreateKategoriBerita() {
|
||||
const createState = useProxy(stateDashboardBerita.kategoriBerita)
|
||||
const router = useRouter();
|
||||
|
||||
const resetForm = () => {
|
||||
createState.create.form = {
|
||||
name: "",
|
||||
};
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await createState.create.create();
|
||||
resetForm();
|
||||
router.push("/admin/desa/berita/kategori-berita")
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={4}>Create Kategori Berita</Title>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Nama Kategori Berita</Text>}
|
||||
placeholder='Masukkan nama kategori Berita'
|
||||
value={createState.create.form.name}
|
||||
onChange={(val) => {
|
||||
createState.create.form.name = val.target.value;
|
||||
}}
|
||||
/>
|
||||
<Group>
|
||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateKategoriBerita;
|
||||
128
src/app/admin/(dashboard)/desa/berita/kategori-berita/page.tsx
Normal file
128
src/app/admin/(dashboard)/desa/berita/kategori-berita/page.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import stateDashboardBerita from '../../../_state/desa/berita';
|
||||
|
||||
|
||||
|
||||
function KategoriBerita() {
|
||||
const [search, setSearch] = useState('');
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Kategori Berita'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListKategoriBerita search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListKategoriBerita({ search }: { search: string }) {
|
||||
const listDataState = useProxy(stateDashboardBerita.kategoriBerita)
|
||||
const router = useRouter();
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
listDataState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const handleDelete = () => {
|
||||
if (selectedId) {
|
||||
listDataState.delete.delete(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
|
||||
listDataState.findMany.load()
|
||||
}
|
||||
}
|
||||
|
||||
const filteredData = (listDataState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!listDataState.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p="md">
|
||||
<Stack>
|
||||
<JudulList
|
||||
title='List Kategori Berita'
|
||||
href='/admin/desa/berita/kategori-berita/create'
|
||||
/>
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>No</TableTh>
|
||||
<TableTh>Nama</TableTh>
|
||||
<TableTh>Edit</TableTh>
|
||||
<TableTh>Hapus</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item, index) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
<Text truncate="end" fz={"sm"}>{index + 1}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>
|
||||
<Button color='green' onClick={() => router.push(`/admin/pendidikan/perpustakaan-digital/kategori-Berita/${item.id}`)}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button
|
||||
color='red'
|
||||
disabled={listDataState.delete.loading}
|
||||
onClick={() => {
|
||||
setSelectedId(item.id)
|
||||
setModalHapus(true)
|
||||
}}>
|
||||
<IconTrash size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleDelete}
|
||||
text='Apakah anda yakin ingin menghapus kategori Berita ini?'
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default KategoriBerita;
|
||||
13
src/app/admin/(dashboard)/desa/berita/layout.tsx
Normal file
13
src/app/admin/(dashboard)/desa/berita/layout.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
'use client'
|
||||
import React from 'react';
|
||||
import LayoutTabsBerita from './_com/layoutTabs';
|
||||
|
||||
function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<LayoutTabsBerita>
|
||||
{children}
|
||||
</LayoutTabsBerita>
|
||||
);
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@@ -1,30 +1,29 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
"use client";
|
||||
|
||||
import EditEditor from "@/app/admin/(dashboard)/_com/editEditor";
|
||||
import stateDashboardBerita from "@/app/admin/(dashboard)/_state/desa/berita";
|
||||
import colors from "@/con/colors";
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Center,
|
||||
Group,
|
||||
Image,
|
||||
Paper,
|
||||
Select,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
Title
|
||||
} from "@mantine/core";
|
||||
import { IconArrowBack, IconImageInPicture } from "@tabler/icons-react";
|
||||
import { Dropzone } from "@mantine/dropzone";
|
||||
import { IconArrowBack, IconPhoto, IconUpload, IconX } from "@tabler/icons-react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { useProxy } from "valtio/utils";
|
||||
import EditEditor from "@/app/admin/(dashboard)/_com/editEditor";
|
||||
import colors from "@/con/colors";
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { FileInput } from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import stateDashboardBerita from "../../../../_state/desa/berita";
|
||||
|
||||
|
||||
function EditBerita() {
|
||||
const beritaState = useProxy(stateDashboardBerita);
|
||||
@@ -43,6 +42,7 @@ function EditBerita() {
|
||||
|
||||
// Load berita by id saat pertama kali
|
||||
useEffect(() => {
|
||||
beritaState.kategoriBerita.findMany.load()
|
||||
const loadBerita = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
@@ -99,7 +99,7 @@ function EditBerita() {
|
||||
|
||||
await beritaState.berita.edit.update();
|
||||
toast.success("Berita berhasil diperbarui!");
|
||||
router.push("/admin/desa/berita");
|
||||
router.push("/admin/desa/berita/list-berita");
|
||||
} catch (error) {
|
||||
console.error("Error updating berita:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui berita");
|
||||
@@ -130,46 +130,88 @@ function EditBerita() {
|
||||
placeholder="masukkan deskripsi"
|
||||
/>
|
||||
|
||||
<FileInput
|
||||
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar Baru (Opsional)</Text>}
|
||||
value={file}
|
||||
onChange={async (e) => {
|
||||
if (!e) return;
|
||||
setFile(e);
|
||||
const base64 = await e.arrayBuffer().then((buf) =>
|
||||
"data:image/png;base64," + Buffer.from(buf).toString("base64")
|
||||
);
|
||||
setPreviewImage(base64);
|
||||
}}
|
||||
/>
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Gambar</Text>
|
||||
<Box>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0]; // Ambil file pertama
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview
|
||||
}
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2} // Maks 5MB
|
||||
accept={{ 'image/*': [] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
||||
</Dropzone.Idle>
|
||||
|
||||
{previewImage ? (
|
||||
<Image alt="" src={previewImage} w={200} h={200} />
|
||||
) : (
|
||||
<Center w={200} h={200} bg={"gray"}>
|
||||
<IconImageInPicture />
|
||||
</Center>
|
||||
)}
|
||||
<div>
|
||||
<Text size="xl" inline>
|
||||
Drag gambar ke sini atau klik untuk pilih file
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" inline mt={7}>
|
||||
Maksimal 5MB dan harus format gambar
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{/* Tampilkan preview kalau ada */}
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '200px',
|
||||
objectFit: 'contain',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||
<EditEditor
|
||||
value={formData.content}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, content: htmlContent }));
|
||||
beritaState.berita.edit.form.content = htmlContent;
|
||||
}}
|
||||
value={formData.content}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, content: htmlContent }));
|
||||
beritaState.berita.edit.form.content = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<SelectCategory
|
||||
<Select
|
||||
value={formData.kategoriBeritaId}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
kategoriBeritaId: val?.id || ''
|
||||
});
|
||||
}}
|
||||
onChange={(val) => setFormData({ ...formData, kategoriBeritaId: val || "" })}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Kategori</Text>}
|
||||
placeholder='Pilih kategori'
|
||||
data={
|
||||
beritaState.kategoriBerita.findMany.data?.map((v) => ({
|
||||
value: v.id,
|
||||
label: v.name
|
||||
})) || []
|
||||
}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
error={!formData.kategoriBeritaId ? "Pilih kategori" : undefined}
|
||||
/>
|
||||
|
||||
<Button onClick={handleSubmit}>Edit Berita</Button>
|
||||
@@ -179,61 +221,4 @@ function EditBerita() {
|
||||
);
|
||||
}
|
||||
|
||||
interface SelectCategoryProps {
|
||||
onChange: (value: Prisma.KategoriBeritaGetPayload<{
|
||||
select: {
|
||||
name: true;
|
||||
id: true;
|
||||
};
|
||||
}> | null) => void;
|
||||
value?: string | null;
|
||||
defaultValue?: string | null;
|
||||
}
|
||||
|
||||
function SelectCategory({
|
||||
onChange,
|
||||
value,
|
||||
defaultValue,
|
||||
}: SelectCategoryProps) {
|
||||
const categoryState = useProxy(stateDashboardBerita.category);
|
||||
|
||||
useShallowEffect(() => {
|
||||
categoryState.findMany.load().then(() => {
|
||||
console.log("Kategori berhasil dimuat:", categoryState.findMany.data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!categoryState.findMany.data) {
|
||||
return <Skeleton height={38} />;
|
||||
}
|
||||
|
||||
|
||||
const selectedValue = value || defaultValue;
|
||||
|
||||
return (
|
||||
<Select
|
||||
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
||||
placeholder="Pilih kategori"
|
||||
data={categoryState.findMany.data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={selectedValue || null}
|
||||
onChange={(val: string | null) => {
|
||||
if (val) {
|
||||
const selected = categoryState.findMany.data?.find((item) => item.id === val);
|
||||
if (selected) {
|
||||
onChange(selected);
|
||||
}
|
||||
} else {
|
||||
onChange(null);
|
||||
}
|
||||
}}
|
||||
searchable
|
||||
clearable
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditBerita;
|
||||
@@ -8,8 +8,8 @@ import { useParams, useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
|
||||
import colors from '@/con/colors';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import stateDashboardBerita from '../../../_state/desa/berita';
|
||||
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||
|
||||
function DetailBerita() {
|
||||
const beritaState = useProxy(stateDashboardBerita)
|
||||
@@ -28,7 +28,7 @@ function DetailBerita() {
|
||||
beritaState.berita.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/desa/berita")
|
||||
router.push("/admin/desa/berita/list-berita")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ function DetailBerita() {
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (beritaState.berita.findUnique.data) {
|
||||
router.push(`/admin/desa/berita/${beritaState.berita.findUnique.data.id}/edit`);
|
||||
router.push(`/admin/desa/berita/list-berita/${beritaState.berita.findUnique.data.id}/edit`);
|
||||
}
|
||||
}}
|
||||
disabled={!beritaState.berita.findUnique.data}
|
||||
@@ -0,0 +1,187 @@
|
||||
'use client'
|
||||
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
export default function CreateBerita() {
|
||||
const beritaState = useProxy(stateDashboardBerita);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
beritaState.kategoriBerita.findMany.load()
|
||||
}, []);
|
||||
|
||||
const resetForm = () => {
|
||||
// Reset state di valtio
|
||||
beritaState.berita.create.form = {
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
kategoriBeritaId: "",
|
||||
imageId: "",
|
||||
content: "",
|
||||
};
|
||||
|
||||
// Reset state lokal
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!file) {
|
||||
return toast.warn("Pilih file gambar terlebih dahulu");
|
||||
}
|
||||
|
||||
// Upload gambar dulu
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file,
|
||||
name: file.name,
|
||||
});
|
||||
|
||||
const uploaded = res.data?.data;
|
||||
if (!uploaded?.id) {
|
||||
return toast.error("Gagal upload gambar");
|
||||
}
|
||||
|
||||
// Simpan ID gambar ke form
|
||||
beritaState.berita.create.form.imageId = uploaded.id;
|
||||
|
||||
// Submit data berita
|
||||
await beritaState.berita.create.create();
|
||||
|
||||
// Reset form setelah submit
|
||||
resetForm();
|
||||
router.push("/admin/desa/berita/list-berita")
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Create Berita</Title>
|
||||
<TextInput
|
||||
value={beritaState.berita.create.form.judul}
|
||||
onChange={(val) => {
|
||||
beritaState.berita.create.form.judul = val.target.value;
|
||||
}}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
<Select
|
||||
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
||||
placeholder="Pilih kategori"
|
||||
data={beritaState.kategoriBerita.findMany.data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={beritaState.berita.create.form.kategoriBeritaId || null}
|
||||
onChange={(val: string | null) => {
|
||||
if (val) {
|
||||
const selected = beritaState.kategoriBerita.findMany.data?.find((item) => item.id === val);
|
||||
if (selected) {
|
||||
beritaState.berita.create.form.kategoriBeritaId = selected.id;
|
||||
}
|
||||
} else {
|
||||
beritaState.berita.create.form.kategoriBeritaId = "";
|
||||
}
|
||||
}}
|
||||
searchable
|
||||
clearable
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
<TextInput
|
||||
value={beritaState.berita.create.form.deskripsi}
|
||||
onChange={(val) => {
|
||||
beritaState.berita.create.form.deskripsi = val.target.value;
|
||||
}}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
|
||||
placeholder="masukkan deskripsi"
|
||||
/>
|
||||
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Gambar</Text>
|
||||
<Box>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0]; // Ambil file pertama
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview
|
||||
}
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2} // Maks 5MB
|
||||
accept={{ 'image/*': [] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
||||
</Dropzone.Idle>
|
||||
|
||||
<div>
|
||||
<Text size="xl" inline>
|
||||
Drag gambar ke sini atau klik untuk pilih file
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" inline mt={7}>
|
||||
Maksimal 5MB dan harus format gambar
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{/* Tampilkan preview kalau ada */}
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '200px',
|
||||
objectFit: 'contain',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||
<CreateEditor
|
||||
value={beritaState.berita.create.form.content}
|
||||
onChange={(htmlContent) => {
|
||||
beritaState.berita.create.form.content = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan Berita</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -6,8 +6,9 @@ import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/ico
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import stateDashboardBerita from '../../_state/desa/berita';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import stateDashboardBerita from '../../../_state/desa/berita';
|
||||
|
||||
|
||||
|
||||
function Berita() {
|
||||
@@ -38,23 +39,17 @@ function ListBerita({ search }: { search: string }) {
|
||||
} = beritaState.berita.findMany;
|
||||
|
||||
|
||||
// Fetch pertama kali
|
||||
// Fetch data when page or search changes
|
||||
useShallowEffect(() => {
|
||||
load(page, 10); // awal page = 1
|
||||
}, [page]);
|
||||
|
||||
const filteredData = (data || []).filter((item) => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.judul.toLowerCase().includes(keyword) ||
|
||||
item.kategoriBerita?.name.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
|
||||
if (loading || !data) {
|
||||
return <Skeleton h={500} />;
|
||||
}
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors["white-1"]} p={"md"}>
|
||||
@@ -67,7 +62,7 @@ function ListBerita({ search }: { search: string }) {
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button
|
||||
onClick={() => router.push("/admin/desa/berita/create")}
|
||||
onClick={() => router.push("/admin/desa/berita/list-berita/create")}
|
||||
bg={colors["blue-button"]}
|
||||
>
|
||||
<IconCircleDashedPlus size={25} />
|
||||
@@ -107,7 +102,7 @@ function ListBerita({ search }: { search: string }) {
|
||||
<Button
|
||||
bg={"green"}
|
||||
onClick={() =>
|
||||
router.push(`/admin/desa/berita/${item.id}`)
|
||||
router.push(`/admin/desa/berita/list-berita/${item.id}`)
|
||||
}
|
||||
>
|
||||
<IconDeviceImacCog size={25} />
|
||||
@@ -4,8 +4,9 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import stateGallery from '@/app/admin/(dashboard)/_state/desa/gallery';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Center, FileInput, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||
import { Box, Button, Center, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import { IconArrowBack, IconImageInPicture, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
@@ -18,6 +19,11 @@ function EditFoto() {
|
||||
const params = useParams();
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
name: fotoState.update.form.name || '',
|
||||
deskripsi: fotoState.update.form.deskripsi || '',
|
||||
imagesId: fotoState.update.form.imagesId || ''
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const loadFoto = async () => {
|
||||
@@ -26,6 +32,11 @@ function EditFoto() {
|
||||
try {
|
||||
const data = await fotoState.update.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imagesId: data.imageGalleryFoto?.id || ''
|
||||
});
|
||||
if (data?.imageGalleryFoto?.link) {
|
||||
setPreviewImage(data.imageGalleryFoto.link);
|
||||
}
|
||||
@@ -40,6 +51,12 @@ function EditFoto() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
fotoState.update.form = {
|
||||
...fotoState.update.form,
|
||||
name: formData.name,
|
||||
deskripsi: formData.deskripsi,
|
||||
imagesId: formData.imagesId
|
||||
};
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file,
|
||||
@@ -74,30 +91,55 @@ function EditFoto() {
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Judul Foto</Text>}
|
||||
placeholder='Masukkan judul foto'
|
||||
value={fotoState.update.form.name}
|
||||
value={formData.name}
|
||||
onChange={(e) =>
|
||||
(fotoState.update.form.name = e.target.value)
|
||||
(formData.name = e.target.value)
|
||||
}
|
||||
/>
|
||||
<FileInput
|
||||
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>}
|
||||
value={file}
|
||||
onChange={async (e) => {
|
||||
if (!e) return;
|
||||
setFile(e);
|
||||
const base64 = await e.arrayBuffer().then((buf) =>
|
||||
"data:image/png;base64," + Buffer.from(buf).toString("base64")
|
||||
);
|
||||
setPreviewImage(base64);
|
||||
}}
|
||||
/>
|
||||
{previewImage ? (
|
||||
<Image alt="" src={previewImage} w={200} h={200} />
|
||||
) : (
|
||||
<Center w={200} h={200} bg={"gray"}>
|
||||
<IconImageInPicture />
|
||||
</Center>
|
||||
)}
|
||||
<Box>
|
||||
<Text>Upload Foto</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0]; // Ambil file pertama
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview
|
||||
}
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2} // Maks 5MB
|
||||
accept={{ 'image/*': [] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
||||
</Dropzone.Idle>
|
||||
|
||||
<div>
|
||||
<Text size="xl" inline>
|
||||
Drag gambar ke sini atau klik untuk pilih file
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" inline mt={7}>
|
||||
Maksimal 5MB dan harus format gambar
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{previewImage ? (
|
||||
<Image alt="" src={previewImage} w={200} h={200} />
|
||||
) : (
|
||||
<Center w={200} h={200} bg={"gray"}>
|
||||
<IconImageInPicture />
|
||||
</Center>
|
||||
)}
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Foto</Text>
|
||||
<EditEditor
|
||||
|
||||
@@ -3,8 +3,9 @@ import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||
import stateGallery from '@/app/admin/(dashboard)/_state/desa/gallery';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Center, FileInput, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||
import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
@@ -69,25 +70,62 @@ function CreateFoto() {
|
||||
fotoState.create.form.name = val.target.value;
|
||||
}}
|
||||
/>
|
||||
<FileInput
|
||||
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>}
|
||||
value={file}
|
||||
onChange={async (e) => {
|
||||
if (!e) return;
|
||||
setFile(e);
|
||||
const base64 = await e.arrayBuffer().then((buf) =>
|
||||
"data:image/png;base64," + Buffer.from(buf).toString("base64")
|
||||
);
|
||||
setPreviewImage(base64);
|
||||
}}
|
||||
/>
|
||||
{previewImage ? (
|
||||
<Image alt="" src={previewImage} w={200} h={200} />
|
||||
) : (
|
||||
<Center w={200} h={200} bg={"gray"}>
|
||||
<IconImageInPicture />
|
||||
</Center>
|
||||
)}
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Gambar</Text>
|
||||
<Box>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0]; // Ambil file pertama
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview
|
||||
}
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2} // Maks 5MB
|
||||
accept={{ 'image/*': [] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
||||
</Dropzone.Idle>
|
||||
|
||||
<div>
|
||||
<Text size="xl" inline>
|
||||
Drag gambar ke sini atau klik untuk pilih file
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" inline mt={7}>
|
||||
Maksimal 5MB dan harus format gambar
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{/* Tampilkan preview kalau ada */}
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '200px',
|
||||
objectFit: 'contain',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Foto</Text>
|
||||
<CreateEditor
|
||||
|
||||
@@ -1,93 +1,124 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import JudulListTab from '../../../_com/judulListTab';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import stateGallery from '../../../_state/desa/gallery';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import { useState } from 'react';
|
||||
"use client";
|
||||
import colors from "@/con/colors";
|
||||
import stateFileStorage from "@/state/state-list-image";
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Flex,
|
||||
Group,
|
||||
Image,
|
||||
Pagination,
|
||||
Paper,
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title
|
||||
} from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import { IconSearch, IconTrash, IconX } from "@tabler/icons-react";
|
||||
import { motion } from "framer-motion";
|
||||
import toast from "react-simple-toasts";
|
||||
import { useSnapshot } from "valtio";
|
||||
|
||||
function Foto() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Posisi Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListFoto search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListFoto({ search }: { search: string }) {
|
||||
const fotoState = useProxy(stateGallery.foto)
|
||||
const router = useRouter();
|
||||
export default function ListImage() {
|
||||
const { list, total } = useSnapshot(stateFileStorage);
|
||||
|
||||
useShallowEffect(() => {
|
||||
fotoState.findMany.load()
|
||||
}, [])
|
||||
stateFileStorage.load();
|
||||
}, []);
|
||||
|
||||
const filteredData = (fotoState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!fotoState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
let timeOut: NodeJS.Timer;
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulListTab
|
||||
title='List Foto'
|
||||
href='/admin/desa/gallery/foto/create'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={16} />}
|
||||
<Stack p={"lg"}>
|
||||
<Flex justify="space-between">
|
||||
<Title order={3}>List Foto</Title>
|
||||
<TextInput
|
||||
radius={"lg"}
|
||||
leftSection={<IconSearch />}
|
||||
rightSection={
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
onClick={() => {
|
||||
stateFileStorage.load();
|
||||
}}
|
||||
>
|
||||
<IconX />
|
||||
</ActionIcon>
|
||||
}
|
||||
placeholder="Pencarian"
|
||||
onChange={(e) => {
|
||||
if (timeOut) clearTimeout(timeOut);
|
||||
timeOut = setTimeout(() => {
|
||||
stateFileStorage.load({ search: e.target.value });
|
||||
}, 200);
|
||||
}}
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Judul Foto</TableTh>
|
||||
<TableTh>Tanggal Foto</TableTh>
|
||||
<TableTh>Deskripsi Foto</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>{new Date(item.createdAt).toDateString()}</TableTd>
|
||||
<TableTd>
|
||||
<Text dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/desa/gallery/foto/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Flex>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<SimpleGrid
|
||||
cols={{
|
||||
base: 3,
|
||||
md: 5,
|
||||
lg: 10,
|
||||
}}
|
||||
>
|
||||
{list &&
|
||||
list.map((v, k) => {
|
||||
return (
|
||||
<Paper key={k} shadow="sm">
|
||||
<Stack pos={"relative"} gap={0} justify="space-between">
|
||||
<motion.div
|
||||
onClick={() => {
|
||||
// copy to clipboard
|
||||
navigator.clipboard.writeText(v.url);
|
||||
toast("Berhasil disalin");
|
||||
}}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.8 }}
|
||||
>
|
||||
<Image
|
||||
h={100}
|
||||
src={v.url + "?size=100"}
|
||||
alt={v.name}
|
||||
fit="cover"
|
||||
loading="lazy"
|
||||
style={{
|
||||
objectFit: "cover",
|
||||
objectPosition: "center",
|
||||
}}
|
||||
/>
|
||||
</motion.div>
|
||||
<Box p={"md"} h={54}>
|
||||
<Text lineClamp={2} fz={"xs"}>
|
||||
{v.name}
|
||||
</Text>
|
||||
</Box>
|
||||
<Group justify="end">
|
||||
<IconTrash
|
||||
color="red"
|
||||
onClick={() => {
|
||||
stateFileStorage.del({ name: v.name }).finally(() => {
|
||||
toast("Berhasil dihapus");
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
})}
|
||||
</SimpleGrid>
|
||||
</Paper>
|
||||
</Box>
|
||||
{total && (
|
||||
<Pagination
|
||||
total={total}
|
||||
onChange={(e) => {
|
||||
stateFileStorage.page = e;
|
||||
stateFileStorage.load();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Foto;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import LayoutTabsGallery from "../../ppid/_com/layoutTabsGallery"
|
||||
import LayoutTabsGallery from "./lib/layoutTabs"
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
|
||||
62
src/app/admin/(dashboard)/desa/gallery/lib/layoutTabs.tsx
Normal file
62
src/app/admin/(dashboard)/desa/gallery/lib/layoutTabs.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
/* 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 LayoutTabsGallery({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const tabs = [
|
||||
{
|
||||
label: "Foto",
|
||||
value: "foto",
|
||||
href: "/admin/desa/gallery/foto"
|
||||
},
|
||||
{
|
||||
label: "Video",
|
||||
value: "video",
|
||||
href: "/admin/desa/gallery/video"
|
||||
},
|
||||
];
|
||||
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}>Gallery</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 LayoutTabsGallery;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user