Compare commits
37 Commits
nico/14-au
...
nico/12-se
| Author | SHA1 | Date | |
|---|---|---|---|
| a5d841bb6b | |||
| 6a7bd386ae | |||
| a9d98895bb | |||
| 75475dc62e | |||
| b39800a475 | |||
| 797713ef49 | |||
| 8817b937b1 | |||
| 2adf60f9eb | |||
| 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 |
2
.gitignore
vendored
@@ -48,3 +48,5 @@ next-env.d.ts
|
|||||||
|
|
||||||
.env.*
|
.env.*
|
||||||
|
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
|
|||||||
15
package.json
@@ -3,11 +3,9 @@
|
|||||||
"version": "0.1.5",
|
"version": "0.1.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "bun --bun next dev",
|
||||||
"build": "next build",
|
"build": "bun --bun next build",
|
||||||
"start": "next start",
|
"start": "bun --bun next start"
|
||||||
"lint": "next lint",
|
|
||||||
"prisma:seed": "bun run prisma/seed.ts"
|
|
||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"seed": "bun run prisma/seed.ts"
|
"seed": "bun run prisma/seed.ts"
|
||||||
@@ -57,6 +55,8 @@
|
|||||||
"form-data": "^4.0.2",
|
"form-data": "^4.0.2",
|
||||||
"framer-motion": "^12.23.5",
|
"framer-motion": "^12.23.5",
|
||||||
"get-port": "^7.1.0",
|
"get-port": "^7.1.0",
|
||||||
|
"iron-session": "^8.0.4",
|
||||||
|
"jose": "^6.1.0",
|
||||||
"jotai": "^2.12.3",
|
"jotai": "^2.12.3",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"motion": "^12.4.1",
|
"motion": "^12.4.1",
|
||||||
"nanoid": "^5.1.5",
|
"nanoid": "^5.1.5",
|
||||||
"next": "15.1.6",
|
"next": "^15.5.2",
|
||||||
"next-view-transitions": "^0.3.4",
|
"next-view-transitions": "^0.3.4",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"p-limit": "^6.2.0",
|
"p-limit": "^6.2.0",
|
||||||
@@ -73,15 +73,18 @@
|
|||||||
"prisma": "^6.3.1",
|
"prisma": "^6.3.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
|
"react-international-phone": "^4.6.0",
|
||||||
"react-leaflet": "^5.0.0",
|
"react-leaflet": "^5.0.0",
|
||||||
"react-simple-toasts": "^6.1.0",
|
"react-simple-toasts": "^6.1.0",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
"readdirp": "^4.1.1",
|
"readdirp": "^4.1.1",
|
||||||
"recharts": "^2.15.3",
|
"recharts": "^2.15.3",
|
||||||
|
"sharp": "^0.34.3",
|
||||||
"swr": "^2.3.2",
|
"swr": "^2.3.2",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"valtio": "^2.1.3",
|
"valtio": "^2.1.3",
|
||||||
|
"zlib": "^1.0.5",
|
||||||
"zod": "^3.24.3"
|
"zod": "^3.24.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
137
prisma/data/file-storage.json
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "cmff0rr4z0002vn0twp333m2",
|
||||||
|
"name": "S6RIjFaPvdQm3oq4rM4X9-desktop.webp",
|
||||||
|
"realName": "bares.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/S6RIjFaPvdQm3oq4rM4X9-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff0tnf00003vn0t3kgzi0u0",
|
||||||
|
"name": "_pVNEmThU5ICGa8gv3gh_-desktop.webp",
|
||||||
|
"realName": "bicara-darma.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/_pVNEmThU5ICGa8gv3gh_-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff0uykf0004vn0trmmxpgfh",
|
||||||
|
"name": "bv6rdKvjxkkjUSGLQ0lvB-desktop.webp",
|
||||||
|
"realName": "daves.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/bv6rdKvjxkkjUSGLQ0lvB-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff0z34f0005vn0tjtvq519p",
|
||||||
|
"name": "Z4hWaV04CvoE20MjccQsV-desktop.webp",
|
||||||
|
"realName": "mangan.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/Z4hWaV04CvoE20MjccQsV-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff38cyq000bvn0t9f01cz3f",
|
||||||
|
"name": "LvLAtOqWojx4sn6NjJWB9-desktop.webp",
|
||||||
|
"realName": "gelah-melah.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/LvLAtOqWojx4sn6NjJWB9-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff0zqvd0007vn0tv6o5hjcq",
|
||||||
|
"name": "gR2mcvAQVgJ2-rM5coYJj-desktop.webp",
|
||||||
|
"realName": "inovasi-desa-darmasaba.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/gR2mcvAQVgJ2-rM5coYJj-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff1013m0008vn0th7t0d64d",
|
||||||
|
"name": "JpL-9F8-IGztMn8E2ce02-desktop.webp",
|
||||||
|
"realName": "pdkt.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/JpL-9F8-IGztMn8E2ce02-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff10cwq0009vn0tse8dzu3j",
|
||||||
|
"name": "bxAk4AsGbJTC705_IVdes-desktop.webp",
|
||||||
|
"realName": "sajjiana-dharma-raksaka.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/bxAk4AsGbJTC705_IVdes-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff2w5ly000avn0telhct71k",
|
||||||
|
"name": "Vbj_osnMJUkGEQGDTLwV--desktop.webp",
|
||||||
|
"realName": "perbekel.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/Vbj_osnMJUkGEQGDTLwV--desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff3joae0000vn6h8sgs0ilg",
|
||||||
|
"name": "7hox9spUxj56hY_EBYLnj-desktop.webp",
|
||||||
|
"realName": "youtube.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/7hox9spUxj56hY_EBYLnj-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff3ll130001vn6hkhls3f5y",
|
||||||
|
"name": "ChihV7_1eS-AGtSg9UwMv-desktop.webp",
|
||||||
|
"realName": "gmail.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/ChihV7_1eS-AGtSg9UwMv-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff3mtat0002vn6hs8vyyhdd",
|
||||||
|
"name": "z8v9ZREwOJHKGIRYauROt-desktop.webp",
|
||||||
|
"realName": "facebook.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/z8v9ZREwOJHKGIRYauROt-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff3nv180003vn6h5jvedidq",
|
||||||
|
"name": "BLjMxTKoCNE31uOURR3IU-desktop.webp",
|
||||||
|
"realName": "telephone-call.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/BLjMxTKoCNE31uOURR3IU-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff3oouh0004vn6hd94brzv9",
|
||||||
|
"name": "hkJYAeTNWK_vYaYS20w3I-desktop.webp",
|
||||||
|
"realName": "instagram.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/hkJYAeTNWK_vYaYS20w3I-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmff3q12g0005vn6h5ojov2qa",
|
||||||
|
"name": "6XEoZ9SFu59COpil03Gya-desktop.webp",
|
||||||
|
"realName": "tiktok.png",
|
||||||
|
"path": "uploads/images",
|
||||||
|
"mimeType": "image/webp",
|
||||||
|
"link": "/api/fileStorage/findUnique/6XEoZ9SFu59COpil03Gya-desktop.webp",
|
||||||
|
"category": "image"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -3,126 +3,108 @@
|
|||||||
"id": "cmds9h9ko000pvnberdjnx64b",
|
"id": "cmds9h9ko000pvnberdjnx64b",
|
||||||
"name": "1.1 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PERENCANAAN, PELAKSANAAN, PENATAUSAHAAN DAN PERTANGGUNG JAWABAN APBDES BESERTA IMPLEMENTASINYA",
|
"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>",
|
"deskripsi": "<p>ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PERENCANAAN, PELAKSANAAN, PENATAUSAHAAN DAN PERTANGGUNG JAWABAN APBDES BESERTA IMPLEMENTASINYA</p>",
|
||||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh",
|
"kategoriId": "cmds9es2o000ivnbe1o0swrvh"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmds9sjmz000svnbesv2133of",
|
"id": "cmds9sjmz000svnbesv2133of",
|
||||||
"name": "1.2 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP MENGENAI MEKANISME EVALUASI KINERJA PERANGKAT DESA",
|
"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>",
|
"deskripsi": "<p>ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP MENGENAI MEKANISME EVALUASI KINERJA PERANGKAT DESA</p>",
|
||||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh",
|
"kategoriId": "cmds9es2o000ivnbe1o0swrvh"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmds9tcpi000vvnbev3ebtlnt",
|
"id": "cmds9tcpi000vvnbev3ebtlnt",
|
||||||
"name": "1.3 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PENGENDALIAN GRATIFIKASI, SUAP DAN KONFLIK KEPENTINGAN",
|
"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>",
|
"deskripsi": "<p>ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PENGENDALIAN GRATIFIKASI, SUAP DAN KONFLIK KEPENTINGAN</p>",
|
||||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh",
|
"kategoriId": "cmds9es2o000ivnbe1o0swrvh"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmds9twvj000yvnbep0pq8dzf",
|
"id": "cmds9twvj000yvnbep0pq8dzf",
|
||||||
"name": "1.4 PERJANJIAN KERJA SAMA ANTARA PELAKSANA KEGIATAN ANGGARAN DENGAN PIHAK PENYEDIA, DAN TELAH MELALUI PROSES PENGADAAN BARANG/JASA DI DESA",
|
"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>",
|
"deskripsi": "<p>PERJANJIAN KERJA SAMA ANTARA PELAKSANA KEGIATAN ANGGARAN DENGAN PIHAK PENYEDIA, DAN TELAH MELALUI PROSES PENGADAAN BARANG/JASA DI DESA</p>",
|
||||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh",
|
"kategoriId": "cmds9es2o000ivnbe1o0swrvh"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmds9ugap0011vnbe118yv871",
|
"id": "cmds9ugap0011vnbe118yv871",
|
||||||
"name": "1.5 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PAKTA INTEGRITAS DAN SEJENISNYA",
|
"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>",
|
"deskripsi": "<p>ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PAKTA INTEGRITAS DAN SEJENISNYA</p>",
|
||||||
"kategoriId": "cmds9es2o000ivnbe1o0swrvh",
|
"kategoriId": "cmds9es2o000ivnbe1o0swrvh"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsa35310014vnbe6qy6l1rz",
|
"id": "cmdsa35310014vnbe6qy6l1rz",
|
||||||
"name": "2.1 ADANYA KEGIATAN PENGAWASAN DAN EVALUASI KINERJA PERANGKAT DESA",
|
"name": "2.1 ADANYA KEGIATAN PENGAWASAN DAN EVALUASI KINERJA PERANGKAT DESA",
|
||||||
"deskripsi": "<p>ADANYA KEGIATAN PENGAWASAN DAN EVALUASI KINERJA PERANGKAT DESA</p>",
|
"deskripsi": "<p>ADANYA KEGIATAN PENGAWASAN DAN EVALUASI KINERJA PERANGKAT DESA</p>",
|
||||||
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm",
|
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsa46590017vnbepp3noso1",
|
"id": "cmdsa46590017vnbepp3noso1",
|
||||||
"name": "2.2 ADANYA TINDAK LANJUT HASIL PEMBINAAN, PETUNJUK, ARAH, PENGAWASAN, DAN PEMERIKSAAN DARI PEMERINTAH PUSAT/DAERAH",
|
"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>",
|
"deskripsi": "<p>ADANYA TINDAK LANJUT HASIL PEMBINAAN, PETUNJUK, ARAH, PENGAWASAN, DAN PEMERIKSAAN DARI PEMERINTAH PUSAT/DAERAH</p>",
|
||||||
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm",
|
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsa5m7z001avnbe4cvfrcz0",
|
"id": "cmdsa5m7z001avnbe4cvfrcz0",
|
||||||
"name": "2.3 TIDAK ADANYA APARATUR DESA DALAM 3(TIGA) TAHUN TERAKHIR YANG TERJERAT TINDAKAN PIDANA KORUPSI",
|
"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>",
|
"deskripsi": "<p>TIDAK ADANYA APARATUR DESA DALAM 3(TIGA) TAHUN TERAKHIR YANG TERJERAT TINDAKAN PIDANA KORUPSI</p>",
|
||||||
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm",
|
"kategoriId": "cmds9f2ua000jvnbeksu1sfwm"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsa8q5q001dvnbemch8j24x",
|
"id": "cmdsa8q5q001dvnbemch8j24x",
|
||||||
"name": "3.1 ADANYA LAYANAN PENGADUAN BAGI MASYARAKAT",
|
"name": "3.1 ADANYA LAYANAN PENGADUAN BAGI MASYARAKAT",
|
||||||
"deskripsi": "<p>ADANYA LAYANAN PENGADUAN BAGI MASYARAKAT</p>",
|
"deskripsi": "<p>ADANYA LAYANAN PENGADUAN BAGI MASYARAKAT</p>",
|
||||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl",
|
"kategoriId": "cmds9fr73000kvnbe6w281dcl"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsa9lbi001gvnbequn2ba7m",
|
"id": "cmdsa9lbi001gvnbequn2ba7m",
|
||||||
"name": "3.2 ADANYA SURVEY KEPUASAN MASYARAKAT (SKM) TERHADAP LAYANAN PEMERINTAH DESA",
|
"name": "3.2 ADANYA SURVEY KEPUASAN MASYARAKAT (SKM) TERHADAP LAYANAN PEMERINTAH DESA",
|
||||||
"deskripsi": "<p>ADANYA SURVEY KEPUASAN MASYARAKAT (SKM) TERHADAP LAYANAN PEMERINTAH DESA</p>",
|
"deskripsi": "<p>ADANYA SURVEY KEPUASAN MASYARAKAT (SKM) TERHADAP LAYANAN PEMERINTAH DESA</p>",
|
||||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl",
|
"kategoriId": "cmds9fr73000kvnbe6w281dcl"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsaa7aq001jvnbeizh04e67",
|
"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",
|
"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>",
|
"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",
|
"kategoriId": "cmds9fr73000kvnbe6w281dcl"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsaaw8d001mvnbek3tfefrk",
|
"id": "cmdsaaw8d001mvnbek3tfefrk",
|
||||||
"name": "3.4 ADANYA MEDIA INFORMASI TENTANG APBDES DI BALAI DESA DAN/ATAU TEMPAT LAIN YANG MUDAH DIAKSES OLEH MASYARAKAT",
|
"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>",
|
"deskripsi": "<p>ADANYA MEDIA INFORMASI TENTANG APBDES DI BALAI DESA DAN/ATAU TEMPAT LAIN YANG MUDAH DIAKSES OLEH MASYARAKAT</p>",
|
||||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl",
|
"kategoriId": "cmds9fr73000kvnbe6w281dcl"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsabhif001pvnbepm06hry6",
|
"id": "cmdsabhif001pvnbepm06hry6",
|
||||||
"name": "3.5 ADANYA MAKLUMAT PELAYANAN",
|
"name": "3.5 ADANYA MAKLUMAT PELAYANAN",
|
||||||
"deskripsi": "<p>ADANYA MAKLUMAT PELAYANAN</p>",
|
"deskripsi": "<p>ADANYA MAKLUMAT PELAYANAN</p>",
|
||||||
"kategoriId": "cmds9fr73000kvnbe6w281dcl",
|
"kategoriId": "cmds9fr73000kvnbe6w281dcl"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsag40b001svnbe7krq9khc",
|
"id": "cmdsag40b001svnbe7krq9khc",
|
||||||
"name": "4.1 ADANYA PARTISIPASI DAN KETERLIBATAN MASYARAKAT DALAM PENYUSUNAN RKP DESA",
|
"name": "4.1 ADANYA PARTISIPASI DAN KETERLIBATAN MASYARAKAT DALAM PENYUSUNAN RKP DESA",
|
||||||
"deskripsi": "<p>ADANYA PARTISIPASI DAN KETERLIBATAN MASYARAKAT DALAM PENYUSUNAN RKP DESA</p>",
|
"deskripsi": "<p>ADANYA PARTISIPASI DAN KETERLIBATAN MASYARAKAT DALAM PENYUSUNAN RKP DESA</p>",
|
||||||
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv",
|
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsagkaf001vvnbejo26w8sa",
|
"id": "cmdsagkaf001vvnbejo26w8sa",
|
||||||
"name": "4.2 ADANYA KESADARAN MASYARAKAT DALAM MENCEGAH TERJADINYA PRAKTIK GRATIFIKASI, SUAP DAN KONFLIK KEPENTINGAN",
|
"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>",
|
"deskripsi": "<p>ADANYA KESADARAN MASYARAKAT DALAM MENCEGAH TERJADINYA PRAKTIK GRATIFIKASI, SUAP DAN KONFLIK KEPENTINGAN</p>",
|
||||||
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv",
|
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsah4qe001yvnbeiy3mwrvb",
|
"id": "cmdsah4qe001yvnbeiy3mwrvb",
|
||||||
"name": "4.3 ADANYA KETERLIBATAN LEMBAGA KEMASYARAKATAN DALAM PELAKSANAAN PEMBANGUNAN DESA",
|
"name": "4.3 ADANYA KETERLIBATAN LEMBAGA KEMASYARAKATAN DALAM PELAKSANAAN PEMBANGUNAN DESA",
|
||||||
"deskripsi": "<p>ADANYA KETERLIBATAN LEMBAGA KEMASYARAKATAN DALAM PELAKSANAAN PEMBANGUNAN DESA</p>",
|
"deskripsi": "<p>ADANYA KETERLIBATAN LEMBAGA KEMASYARAKATAN DALAM PELAKSANAAN PEMBANGUNAN DESA</p>",
|
||||||
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv",
|
"kategoriId": "cmds9g5ow000lvnbel3rkkwrv"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsak5vn0021vnbemg86aab4",
|
"id": "cmdsak5vn0021vnbemg86aab4",
|
||||||
"name": "5.1 ADANYA BUDAYA LOKAL/HUKUM ADAT YANG MENDORONG UPAYA PENCEGAHAN TINDAK PIDANA KORUPSI",
|
"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>",
|
"deskripsi": "<p>ADANYA BUDAYA LOKAL/HUKUM ADAT YANG MENDORONG UPAYA PENCEGAHAN TINDAK PIDANA KORUPSI</p>",
|
||||||
"kategoriId": "cmds9govb000mvnbesq8b4y99",
|
"kategoriId": "cmds9govb000mvnbesq8b4y99"
|
||||||
"fileId": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdsalc800024vnbezgulhgrb",
|
"id": "cmdsalc800024vnbezgulhgrb",
|
||||||
"name": "5.2 ADANYA TOKOH MASYARAKAT, TOKOH AGAMA, TOKOH ADAT, TOKOH PEMUDA, DAN KAUM PEREMPUAN YANG MENDORONG UPAYA PENCEGAHAN TINDAK PIDANA KORUPSI",
|
"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>",
|
"deskripsi": "<p>ADANYA TOKOH MASYARAKAT, TOKOH AGAMA, TOKOH ADAT, TOKOH PEMUDA, DAN KAUM PEREMPUAN YANG MENDORONG UPAYA PENCEGAHAN TINDAK PIDANA KORUPSI</p>",
|
||||||
"kategoriId": "cmds9govb000mvnbesq8b4y99",
|
"kategoriId": "cmds9govb000mvnbesq8b4y99"
|
||||||
"fileId": ""
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -2,31 +2,37 @@
|
|||||||
{
|
{
|
||||||
"id": "cmds8w2q60002vnbe6i8qhkuo",
|
"id": "cmds8w2q60002vnbe6i8qhkuo",
|
||||||
"name": "Telephone Desa Darmasaba",
|
"name": "Telephone Desa Darmasaba",
|
||||||
"iconUrl": "081239580000"
|
"iconUrl": "081239580000",
|
||||||
|
"imageId": "cmff3nv180003vn6h5jvedidq"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmds8z7u20005vnbegyyvnbk0",
|
"id": "cmds8z7u20005vnbegyyvnbk0",
|
||||||
"name": "Email Desa Darmasaba",
|
"name": "Email Desa Darmasaba",
|
||||||
"iconUrl": "desadarmasaba@badungkab.go.id"
|
"iconUrl": "desadarmasaba@badungkab.go.id",
|
||||||
|
"imageId": "cmff3ll130001vn6hkhls3f5y"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmds9023u0008vnbe3oxmhwyf",
|
"id": "cmds9023u0008vnbe3oxmhwyf",
|
||||||
"name": "Desa Darmasaba",
|
"name": "Desa Darmasaba",
|
||||||
"iconUrl": "https://www.youtube.com/channel/UCtPw9WOQO7d2HIKzKgel4Xg"
|
"iconUrl": "https://www.youtube.com/channel/UCtPw9WOQO7d2HIKzKgel4Xg",
|
||||||
|
"imageId": "cmff3joae0000vn6h8sgs0ilg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmds90oul000bvnbe2bqkptoi",
|
"id": "cmds90oul000bvnbe2bqkptoi",
|
||||||
"name": "Pemerintah Desa Darmasaba",
|
"name": "Pemerintah Desa Darmasaba",
|
||||||
"iconUrl": "https://www.facebook.com/DarmasabaDesaku"
|
"iconUrl": "https://www.facebook.com/DarmasabaDesaku",
|
||||||
|
"imageId": "cmff3mtat0002vn6hs8vyyhdd"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmds91i4e000evnbe8gtf1gub",
|
"id": "cmds91i4e000evnbe8gtf1gub",
|
||||||
"name": "ddarmasaba",
|
"name": "ddarmasaba",
|
||||||
"iconUrl": "https://www.instagram.com/ddarmasaba/"
|
"iconUrl": "https://www.instagram.com/ddarmasaba/",
|
||||||
|
"imageId": "cmff3oouh0004vn6hd94brzv9"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmds92de5000hvnbemlu6sq5x",
|
"id": "cmds92de5000hvnbemlu6sq5x",
|
||||||
"name": "desa.darmasaba",
|
"name": "desa.darmasaba",
|
||||||
"iconUrl": "https://www.tiktok.com/@desa.darmasaba?is_from_webapp=1&sender_device=pc"
|
"iconUrl": "https://www.tiktok.com/@desa.darmasaba?is_from_webapp=1&sender_device=pc",
|
||||||
|
"imageId": "cmff3q12g0005vn6h5ojov2qa"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
{
|
{
|
||||||
"id": "edit",
|
"id": "edit",
|
||||||
"name": "I.B Surya Prabhawa Manuaba, S.H., M.H.",
|
"name": "I.B Surya Prabhawa Manuaba, S.H., M.H.",
|
||||||
"position": "Perbekel Darmasaba periode 2021-2027"
|
"position": "Perbekel Darmasaba periode 2021-2027",
|
||||||
|
"imageId": "cmff2w5ly000avn0telhct71k"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,50 +1,51 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"id": "cmdr7039z0002vn5rttctt9hn",
|
|
||||||
"name": "Davest",
|
|
||||||
"description": "Darmasaba Village Festval",
|
|
||||||
"link": "https://darmasaba.desa.id/berita/55862-rakor-davest-2024"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "cmdr755pf0005vn5rp8tyuubw",
|
"id": "cmdr755pf0005vn5rp8tyuubw",
|
||||||
"name": "Dmangan",
|
"name": "Dmangan",
|
||||||
"description": "Darmasaba Aman Pangan",
|
"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"
|
"link": "https://darmasaba.desa.id/berita/61452-kader-d-mangan-berhasil-meraih-prestasi-dalam-ajang-lomba-banjar-bali-quis-bbq-tahun-2024",
|
||||||
|
"imageId" : "cmff0z34f0005vn0tjtvq519p"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdr76nqk0008vn5rdddvcxnr",
|
"id": "cmdr76nqk0008vn5rdddvcxnr",
|
||||||
"name": "Bicara Darmasaba",
|
"name": "Bicara Darmasaba",
|
||||||
"description": "Bicara Darmasaba",
|
"description": "Bicara Darmasaba",
|
||||||
"link": "https://darmasaba.desa.id/berita/42506-bicara-darmasaba"
|
"link": "https://darmasaba.desa.id/berita/42506-bicara-darmasaba",
|
||||||
|
"imageId" : "cmff0tnf00003vn0t3kgzi0u0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdr77vbw000bvn5rvpmoq31s",
|
"id": "cmdr77vbw000bvn5rvpmoq31s",
|
||||||
"name": "Bares",
|
"name": "Bares",
|
||||||
"description": "Darmasaba Recycling Stock/Exchange",
|
"description": "Darmasaba Recycling Stock/Exchange",
|
||||||
"link": "http://darmasaba.desa.id/berita/56722-bares"
|
"link": "http://darmasaba.desa.id/berita/56722-bares",
|
||||||
|
"imageId" : "cmff0rr4z0002vn0twp333m2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdr7bxtp000evn5rmy85wihx",
|
"id": "cmdr7bxtp000evn5rmy85wihx",
|
||||||
"name": "Sajjana Dharma Raksaka",
|
"name": "Sajjana Dharma Raksaka",
|
||||||
"description": "Sajjana Dharma Raksaka",
|
"description": "Sajjana Dharma Raksaka",
|
||||||
"link": "https://ppid.badungkab.go.id/storage/dokumen/5RS9dldGkrgzMQq6bKdZsqsVRHI8gffWv4PGfb3r.pdf"
|
"link": "https://ppid.badungkab.go.id/storage/dokumen/5RS9dldGkrgzMQq6bKdZsqsVRHI8gffWv4PGfb3r.pdf",
|
||||||
|
"imageId" : "cmff10cwq0009vn0tse8dzu3j"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdr7dlnk000hvn5r9lur3z35",
|
"id": "cmdr7dlnk000hvn5r9lur3z35",
|
||||||
"name": "PDKT",
|
"name": "PDKT",
|
||||||
"description": "Perangkat Desa Kuat Teknologi",
|
"description": "Perangkat Desa Kuat Teknologi",
|
||||||
"link": "https://darmasaba.desa.id/berita/53752-p-d-k-t"
|
"link": "https://darmasaba.desa.id/berita/53752-p-d-k-t",
|
||||||
|
"imageId" : "cmff1013m0008vn0th7t0d64d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdr7ftob000mvn5rfhgdtg8v",
|
"id": "cmdr7ftob000mvn5rfhgdtg8v",
|
||||||
"name": "GM",
|
"name": "GM",
|
||||||
"description": "Galah Melah",
|
"description": "Galah Melah",
|
||||||
"link": "https://darmasaba.desa.id/berita/52880-galah-melah"
|
"link": "https://darmasaba.desa.id/berita/52880-galah-melah",
|
||||||
|
"imageId" : "cmff38cyq000bvn0t9f01cz3f"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "cmdr7glue000pvn5r6onzslju",
|
"id": "cmdr7glue000pvn5r6onzslju",
|
||||||
"name": "Inovasi Desa Darmasaba",
|
"name": "Inovasi Desa Darmasaba",
|
||||||
"description": "Inovasi Desa Darmasaba",
|
"description": "Inovasi Desa Darmasaba",
|
||||||
"link": "https://darmasaba.desa.id/produk-lokal-desa"
|
"link": "https://darmasaba.desa.id/produk-lokal-desa",
|
||||||
|
"imageId" : "cmff0zqvd0007vn0tv6o5hjcq"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": "1",
|
"id": "cmeppcwzk0000vn5exmudcipd",
|
||||||
"jenisInformasi": "Peraturan Desa",
|
"jenisInformasi": "Potensi Desa",
|
||||||
"deskripsi": "Dokumen yang berisi kebijakan dan regulasi 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": "15 Januari 2024"
|
"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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "cmdpm429r0000vnndkcwslt0h",
|
|
||||||
"name": "warga"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
29
prisma/data/user/roles.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"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": "2",
|
||||||
|
"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": "3",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
32
prisma/data/user/users.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"nama": "Admin Desa",
|
||||||
|
"nomor": "089647037426",
|
||||||
|
"roleId": "1",
|
||||||
|
"isActive": true,
|
||||||
|
"lastLogin": "2025-08-31T10:00:00.000Z",
|
||||||
|
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||||
|
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"nama": "Admin Kesehatan",
|
||||||
|
"nomor": "082339004198",
|
||||||
|
"roleId": "2",
|
||||||
|
"isActive": true,
|
||||||
|
"lastLogin": null,
|
||||||
|
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||||
|
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"nama": "Admin Sekolah",
|
||||||
|
"nomor": "085237157222",
|
||||||
|
"roleId": "3",
|
||||||
|
"isActive": true,
|
||||||
|
"lastLogin": null,
|
||||||
|
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||||
|
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -85,7 +85,6 @@ model FileStorage {
|
|||||||
KontakItem KontakItem[]
|
KontakItem KontakItem[]
|
||||||
Pegawai Pegawai[]
|
Pegawai Pegawai[]
|
||||||
DesaDigital DesaDigital[]
|
DesaDigital DesaDigital[]
|
||||||
KolaborasiInovasi KolaborasiInovasi[]
|
|
||||||
InfoTekno InfoTekno[]
|
InfoTekno InfoTekno[]
|
||||||
PengaduanMasyarakat PengaduanMasyarakat[]
|
PengaduanMasyarakat PengaduanMasyarakat[]
|
||||||
KegiatanDesa KegiatanDesa[]
|
KegiatanDesa KegiatanDesa[]
|
||||||
@@ -93,13 +92,15 @@ model FileStorage {
|
|||||||
PejabatDesa PejabatDesa[]
|
PejabatDesa PejabatDesa[]
|
||||||
MediaSosial MediaSosial[]
|
MediaSosial MediaSosial[]
|
||||||
DesaAntiKorupsi DesaAntiKorupsi[]
|
DesaAntiKorupsi DesaAntiKorupsi[]
|
||||||
SDGSDesa SDGSDesa[]
|
SDGSDesa SdgsDesa[]
|
||||||
APBDesImage APBDes[] @relation("APBDesImage")
|
APBDesImage APBDes[] @relation("APBDesImage")
|
||||||
APBDesFile APBDes[] @relation("APBDesFile")
|
APBDesFile APBDes[] @relation("APBDesFile")
|
||||||
PrestasiDesa PrestasiDesa[]
|
PrestasiDesa PrestasiDesa[]
|
||||||
DataPerpustakaan DataPerpustakaan[]
|
DataPerpustakaan DataPerpustakaan[]
|
||||||
PegawaiPPID PegawaiPPID[]
|
PegawaiPPID PegawaiPPID[]
|
||||||
PerbekelDariMasaKeMasa PerbekelDariMasaKeMasa[]
|
PerbekelDariMasaKeMasa PerbekelDariMasaKeMasa[]
|
||||||
|
|
||||||
|
MitraKolaborasi MitraKolaborasi[]
|
||||||
}
|
}
|
||||||
|
|
||||||
//========================================= MENU LANDING PAGE ========================================= //
|
//========================================= MENU LANDING PAGE ========================================= //
|
||||||
@@ -167,7 +168,7 @@ model KategoriDesaAntiKorupsi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//========================================= SDGS Desa ========================================= //
|
//========================================= SDGS Desa ========================================= //
|
||||||
model SDGSDesa {
|
model SdgsDesa {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String @unique
|
name String @unique
|
||||||
jumlah String
|
jumlah String
|
||||||
@@ -201,8 +202,8 @@ model PrestasiDesa {
|
|||||||
deskripsi String @db.Text
|
deskripsi String @db.Text
|
||||||
kategori KategoriPrestasiDesa @relation(fields: [kategoriId], references: [id])
|
kategori KategoriPrestasiDesa @relation(fields: [kategoriId], references: [id])
|
||||||
kategoriId String
|
kategoriId String
|
||||||
image FileStorage @relation(fields: [imageId], references: [id])
|
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||||
imageId String
|
imageId String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
@@ -223,7 +224,7 @@ model KategoriPrestasiDesa {
|
|||||||
model Responden {
|
model Responden {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String @unique
|
name String @unique
|
||||||
tanggal DateTime // misal: 2025-05-01
|
tanggal DateTime @db.Date // misal: 2025-05-01
|
||||||
jenisKelamin JenisKelaminResponden @relation(fields: [jenisKelaminId], references: [id])
|
jenisKelamin JenisKelaminResponden @relation(fields: [jenisKelaminId], references: [id])
|
||||||
jenisKelaminId String
|
jenisKelaminId String
|
||||||
rating PilihanRatingResponden @relation(fields: [ratingId], references: [id])
|
rating PilihanRatingResponden @relation(fields: [ratingId], references: [id])
|
||||||
@@ -292,6 +293,9 @@ model PosisiOrganisasiPPID {
|
|||||||
pegawai PegawaiPPID[]
|
pegawai PegawaiPPID[]
|
||||||
strukturOrganisasi StrukturPPID[] // Relasi balik
|
strukturOrganisasi StrukturPPID[] // Relasi balik
|
||||||
parentId String?
|
parentId String?
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id])
|
parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id])
|
||||||
children PosisiOrganisasiPPID[] @relation("Parent")
|
children PosisiOrganisasiPPID[] @relation("Parent")
|
||||||
}
|
}
|
||||||
@@ -951,13 +955,16 @@ model Kematian {
|
|||||||
|
|
||||||
// ========================================= GRAFIK KEPUASAN ========================================= //
|
// ========================================= GRAFIK KEPUASAN ========================================= //
|
||||||
model GrafikKepuasan {
|
model GrafikKepuasan {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
label String
|
nama String
|
||||||
jumlah String
|
tanggal DateTime
|
||||||
createdAt DateTime @default(now())
|
jenisKelamin String
|
||||||
updatedAt DateTime @updatedAt
|
alamat String
|
||||||
deletedAt DateTime @default(now())
|
penyakit String
|
||||||
isActive Boolean @default(true)
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================= ARTIKEL KESEHATAN ========================================= //
|
// ========================================= ARTIKEL KESEHATAN ========================================= //
|
||||||
@@ -1209,25 +1216,40 @@ model LayananPolsek {
|
|||||||
|
|
||||||
// ========================================= KONTAK DARURAT ========================================= //
|
// ========================================= KONTAK DARURAT ========================================= //
|
||||||
model KontakDaruratKeamanan {
|
model KontakDaruratKeamanan {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
nama String // contoh: "Layanan Darurat", "Fasilitas Kesehatan"
|
nama String
|
||||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||||
imageId String?
|
imageId String?
|
||||||
kontakItems KontakItem[]
|
kategori KontakItem @relation(fields: [kategoriId], references: [id])
|
||||||
createdAt DateTime @default(now())
|
kategoriId String
|
||||||
updatedAt DateTime @updatedAt
|
kontakItems KontakDaruratToItem[]
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime?
|
||||||
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
model KontakItem {
|
model KontakItem {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
nama String // contoh: "Polisi", "Ambulans", "Puskesmas Darmasaba"
|
nama String
|
||||||
nomorTelepon String
|
nomorTelepon String
|
||||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||||
imageId String?
|
imageId String?
|
||||||
kategori KontakDaruratKeamanan @relation(fields: [kategoriId], references: [id])
|
createdAt DateTime @default(now())
|
||||||
kategoriId String
|
updatedAt DateTime @updatedAt
|
||||||
createdAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
isActive Boolean @default(true)
|
||||||
|
KontakDaruratToItem KontakDaruratToItem[]
|
||||||
|
KontakDaruratKeamanan KontakDaruratKeamanan[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model KontakDaruratToItem {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
kontakDaruratId String
|
||||||
|
kontakItemId String
|
||||||
|
kontakDarurat KontakDaruratKeamanan @relation(fields: [kontakDaruratId], references: [id])
|
||||||
|
kontakItem KontakItem @relation(fields: [kontakItemId], references: [id])
|
||||||
|
createdAt DateTime @default(now())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================= PENCEGAHAN KRIMINALITAS ========================================= //
|
// ========================================= PENCEGAHAN KRIMINALITAS ========================================= //
|
||||||
@@ -1544,7 +1566,7 @@ model DataDemografiPekerjaan {
|
|||||||
model DetailDataPengangguran {
|
model DetailDataPengangguran {
|
||||||
id String @id @default(uuid()) @db.Uuid
|
id String @id @default(uuid()) @db.Uuid
|
||||||
month String @db.VarChar(20)
|
month String @db.VarChar(20)
|
||||||
year Int
|
year DateTime
|
||||||
totalUnemployment Int
|
totalUnemployment Int
|
||||||
educatedUnemployment Int
|
educatedUnemployment Int
|
||||||
uneducatedUnemployment Int
|
uneducatedUnemployment Int
|
||||||
@@ -1632,18 +1654,27 @@ model ProgramKreatif {
|
|||||||
|
|
||||||
// ========================================= KOLABORASI INOVASI ========================================= //
|
// ========================================= KOLABORASI INOVASI ========================================= //
|
||||||
model KolaborasiInovasi {
|
model KolaborasiInovasi {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
tahun Int
|
tahun Int
|
||||||
slug String @db.Text //deskripsi singkat
|
slug String @db.Text //deskripsi singkat
|
||||||
deskripsi String @db.Text //deskripsi panjang
|
deskripsi String @db.Text //deskripsi panjang
|
||||||
kolaborator String
|
kolaborator String
|
||||||
image FileStorage @relation(fields: [imageId], references: [id])
|
createdAt DateTime @default(now())
|
||||||
imageId String
|
updatedAt DateTime @updatedAt
|
||||||
createdAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
isActive Boolean @default(true)
|
||||||
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 ========================================= //
|
// ========================================= INFO TEKHNOLOGI TEPAT GUNA ========================================= //
|
||||||
@@ -2087,26 +2118,66 @@ model KategoriBuku {
|
|||||||
DataPerpustakaan DataPerpustakaan[]
|
DataPerpustakaan DataPerpustakaan[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================= USER ========================================= //
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
nama String
|
username String
|
||||||
email String @unique
|
nomor String @unique
|
||||||
password String
|
role Role @relation(fields: [roleId], references: [id])
|
||||||
role Role @relation(fields: [roleId], references: [id])
|
roleId String @default("1")
|
||||||
roleId String
|
instansi String?
|
||||||
isActive Boolean @default(true)
|
UserSession UserSession? // Nama instansi (Puskesmas, Sekolah, dll)
|
||||||
createdAt DateTime @default(now())
|
isActive Boolean @default(true)
|
||||||
updatedAt DateTime @updatedAt
|
lastLogin DateTime?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime?
|
||||||
}
|
}
|
||||||
|
|
||||||
model Role {
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
model KodeOtp {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
isActive Boolean @default(true)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
nomor String
|
||||||
isActive Boolean @default(true)
|
otp Int
|
||||||
User User[]
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
model UserSession {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
token String
|
||||||
|
expires DateTime?
|
||||||
|
active Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @default(now()) @updatedAt
|
||||||
|
User User @relation(fields: [userId], references: [id])
|
||||||
|
userId String @unique
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================= DATA PENDIDIKAN ========================================= //
|
// ========================================= DATA PENDIDIKAN ========================================= //
|
||||||
|
|||||||
445
prisma/seed.ts
@@ -1,68 +1,160 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
import profilePejabatDesa from "./data/landing-page/profile/profile.json";
|
import profilePejabatDesa from "./data/landing-page/profile/profile.json";
|
||||||
import penghargaan from "./data/landing-page/penghargaan/penghargaan.json";
|
|
||||||
import programInovasi from "./data/landing-page/profile/programInovasi.json";
|
import programInovasi from "./data/landing-page/profile/programInovasi.json";
|
||||||
import mediaSosial from "./data/landing-page/profile/mediaSosial.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 sdgsDesa from "./data/landing-page/sdgs-desa/sdgs-desa.json";
|
||||||
import apbdes from "./data/landing-page/apbdes/apbdes.json";
|
import apbdes from "./data/landing-page/apbdes/apbdes.json";
|
||||||
import pelayananSuratKeterangan from "./data/desa/layanan/pelayananSuratKeterangan.json";
|
import kategoriPrestasiDesa from "./data/landing-page/prestasi-desa/kategori-prestasi.json";
|
||||||
import categoryPengumuman from "./data/category-pengumuman.json";
|
import prestasiDesa from "./data/landing-page/prestasi-desa/prestasi-desa.json";
|
||||||
import kategoriBerita from "./data/kategori-berita.json";
|
import penghargaan from "./data/landing-page/penghargaan/penghargaan.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 profilePPID from "./data/ppid/profile-ppid/profilePPid.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 visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json";
|
||||||
|
import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json";
|
||||||
import jenisKelamin from "./data/ppid/ikm/jenis-kelamin/jenis-kelamin.json";
|
import 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 pilihanRatingResponden from "./data/ppid/ikm/pilihan-rating-responden/rating-responden.json";
|
||||||
import umurResponden from "./data/ppid/ikm/umur-responden/umur-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 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 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 lambangDesa from "./data/desa/profile/lambang_desa.json";
|
||||||
import maskotDesa from "./data/desa/profile/maskot_desa.json";
|
import maskotDesa from "./data/desa/profile/maskot_desa.json";
|
||||||
import profilPerbekel from "./data/desa/profile/profil_perbekel.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 kategoriProduk from "./data/ekonomi/pasar-desa/kategori-produk.json";
|
||||||
import hubunganOrganisasi from "./data/ekonomi/struktur-organisasi/hubungan-organisasi.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 pegawai from "./data/ekonomi/struktur-organisasi/pegawai.json";
|
||||||
import detailDataPengangguran from "./data/ekonomi/jumlah-pengangguran/detail-data-pengangguran.json";
|
import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi.json";
|
||||||
import tujuanEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan.json";
|
import kategoriBerita from "./data/kategori-berita.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 contohEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/contoh-kegiatan-di-desa-darmasaba.json";
|
||||||
import nilaiKonservasiAdat from "./data/lingkungan/konservasi-adat-bali/nilai-konservasi-adat.json";
|
import materiEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan.json";
|
||||||
|
import tujuanEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan.json";
|
||||||
import bentukKonservasiBerdasarkanAdat from "./data/lingkungan/konservasi-adat-bali/bentuk-konservasi.json";
|
import bentukKonservasiBerdasarkanAdat from "./data/lingkungan/konservasi-adat-bali/bentuk-konservasi.json";
|
||||||
import filosofiTriHita from "./data/lingkungan/konservasi-adat-bali/filosofi-tri-hita.json";
|
import filosofiTriHita from "./data/lingkungan/konservasi-adat-bali/filosofi-tri-hita.json";
|
||||||
import tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-program.json";
|
import nilaiKonservasiAdat from "./data/lingkungan/konservasi-adat-bali/nilai-konservasi-adat.json";
|
||||||
|
import caraMemperolehInformasi from "./data/list-caraMemperolehInformasi.json";
|
||||||
|
import caraMemperolehSalinanInformasi from "./data/list-caraMemperolehSalinanInformasi.json";
|
||||||
|
import jenisInformasiDiminta from "./data/list-jenisInfromasi.json";
|
||||||
|
import potensi from "./data/list-potensi.json";
|
||||||
|
import fasilitasBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan.json";
|
||||||
|
import lokasiJadwalBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal.json";
|
||||||
|
import tujuanBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/tujuan-bimbingan-belajar-desa.json";
|
||||||
|
import jenisProgramYangDiselenggarakan from "./data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json";
|
||||||
|
import tempatKegiatan from "./data/pendidikan/pendidikan-non-formal/tempat-kegiatan.json";
|
||||||
import tujuanProgram2 from "./data/pendidikan/pendidikan-non-formal/tujuan-program2.json";
|
import tujuanProgram2 from "./data/pendidikan/pendidikan-non-formal/tujuan-program2.json";
|
||||||
import programUnggulan from "./data/pendidikan/program-pendidikan-anak/program-unggulan.json";
|
import programUnggulan from "./data/pendidikan/program-pendidikan-anak/program-unggulan.json";
|
||||||
import tujuanBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/tujuan-bimbingan-belajar-desa.json";
|
import tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-program.json";
|
||||||
import lokasiJadwalBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal.json";
|
import roles from "./data/user/roles.json";
|
||||||
import fasilitasBimbinganBelajarDesa from "./data/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan.json";
|
import users from "./data/user/users.json";
|
||||||
import tempatKegiatan from "./data/pendidikan/pendidikan-non-formal/tempat-kegiatan.json";
|
import fileStorage from "./data/file-storage.json";
|
||||||
import jenisProgramYangDiselenggarakan from "./data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json";
|
|
||||||
import posisiOrganisasiPPID from "./data/ppid/struktur-ppid/posisi-organisasi-PPID.json";
|
|
||||||
import pegawaiPPID from "./data/ppid/struktur-ppid/pegawai-PPID.json";
|
|
||||||
import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSaktiDesa.json";
|
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
// =========== USER & ROLE ===========
|
||||||
|
// In your seed.ts
|
||||||
|
// =========== ROLES ===========
|
||||||
|
console.log("🔄 Seeding roles...");
|
||||||
|
for (const r of roles) {
|
||||||
|
await prisma.role.upsert({
|
||||||
|
where: { id: r.id },
|
||||||
|
update: {
|
||||||
|
name: r.name,
|
||||||
|
description: r.description,
|
||||||
|
permissions: r.permissions,
|
||||||
|
isActive: r.isActive,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: r.id,
|
||||||
|
name: r.name,
|
||||||
|
description: r.description,
|
||||||
|
permissions: r.permissions,
|
||||||
|
isActive: r.isActive,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("✅ Roles seeded");
|
||||||
|
|
||||||
|
// =========== USERS ===========
|
||||||
|
console.log("🔄 Seeding users...");
|
||||||
|
for (const u of users) {
|
||||||
|
// First verify the role exists
|
||||||
|
const roleExists = await prisma.role.findUnique({
|
||||||
|
where: { id: u.roleId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!roleExists) {
|
||||||
|
console.error(`❌ Role with id ${u.roleId} not found for user ${u.nama}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.user.upsert({
|
||||||
|
where: { id: u.id },
|
||||||
|
update: {
|
||||||
|
username: u.nama,
|
||||||
|
nomor: u.nomor,
|
||||||
|
roleId: u.roleId,
|
||||||
|
isActive: u.isActive,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: u.id,
|
||||||
|
username: u.nama,
|
||||||
|
nomor: u.nomor,
|
||||||
|
roleId: u.roleId,
|
||||||
|
isActive: u.isActive,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("✅ Users seeded");
|
||||||
|
|
||||||
|
// =========== FILE STORAGE ===========
|
||||||
|
console.log("🔄 Seeding file storage...");
|
||||||
|
for (const f of fileStorage) {
|
||||||
|
await prisma.fileStorage.upsert({
|
||||||
|
where: { id: f.id },
|
||||||
|
update: {
|
||||||
|
name: f.name,
|
||||||
|
realName: f.realName,
|
||||||
|
path: f.path,
|
||||||
|
mimeType: f.mimeType,
|
||||||
|
link: f.link,
|
||||||
|
category: f.category,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: f.id,
|
||||||
|
name: f.name,
|
||||||
|
realName: f.realName,
|
||||||
|
path: f.path,
|
||||||
|
mimeType: f.mimeType,
|
||||||
|
link: f.link,
|
||||||
|
category: f.category,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("✅ File storage seeded");
|
||||||
// =========== LANDING PAGE ===========
|
// =========== LANDING PAGE ===========
|
||||||
// =========== PROFILE ===========
|
// =========== SUBMENU PROFILE ===========
|
||||||
|
// =========== PROFILE PEJABAT DESA ===========
|
||||||
for (const p of profilePejabatDesa) {
|
for (const p of profilePejabatDesa) {
|
||||||
await prisma.pejabatDesa.upsert({
|
await prisma.pejabatDesa.upsert({
|
||||||
where: { id: p.id },
|
where: { id: p.id },
|
||||||
update: {
|
update: {
|
||||||
name: p.name,
|
name: p.name,
|
||||||
position: p.position,
|
position: p.position,
|
||||||
|
imageId: p.imageId,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
id: p.id,
|
id: p.id,
|
||||||
name: p.name,
|
name: p.name,
|
||||||
position: p.position,
|
position: p.position,
|
||||||
|
imageId: p.imageId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -72,18 +164,35 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
|
|
||||||
// =========== PROGRAM INOVASI ===========
|
// =========== PROGRAM INOVASI ===========
|
||||||
for (const p of programInovasi) {
|
for (const p of programInovasi) {
|
||||||
|
let imageId: string | null = null;
|
||||||
|
|
||||||
|
if (p.imageId) {
|
||||||
|
const imageExists = await prisma.fileStorage.findUnique({
|
||||||
|
where: { id: p.imageId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (imageExists) {
|
||||||
|
imageId = p.imageId;
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`⚠️ imageId ${p.imageId} tidak ditemukan untuk ProgramInovasi ${p.name}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
await prisma.programInovasi.upsert({
|
await prisma.programInovasi.upsert({
|
||||||
where: { id: p.id },
|
where: { id: p.id },
|
||||||
update: {
|
update: {
|
||||||
name: p.name,
|
name: p.name,
|
||||||
description: p.description,
|
description: p.description,
|
||||||
link: p.link,
|
link: p.link,
|
||||||
|
imageId: p.imageId,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
id: p.id,
|
id: p.id,
|
||||||
name: p.name,
|
name: p.name,
|
||||||
description: p.description,
|
description: p.description,
|
||||||
link: p.link,
|
link: p.link,
|
||||||
|
imageId: p.imageId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -96,16 +205,102 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
update: {
|
update: {
|
||||||
name: p.name,
|
name: p.name,
|
||||||
iconUrl: p.iconUrl,
|
iconUrl: p.iconUrl,
|
||||||
|
imageId: p.imageId,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
id: p.id,
|
id: p.id,
|
||||||
name: p.name,
|
name: p.name,
|
||||||
iconUrl: p.iconUrl,
|
iconUrl: p.iconUrl,
|
||||||
|
imageId: p.imageId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log("media sosial success ...");
|
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 ===========
|
// =========== PENGHARGAAN ===========
|
||||||
for (const p of penghargaan) {
|
for (const p of penghargaan) {
|
||||||
await prisma.penghargaan.upsert({
|
await prisma.penghargaan.upsert({
|
||||||
@@ -160,30 +355,10 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
}
|
}
|
||||||
console.log("pelayanan surat keterangan success ...");
|
console.log("pelayanan surat keterangan success ...");
|
||||||
|
|
||||||
// =========== LAYANAN ===========
|
|
||||||
for (const l of layanan) {
|
|
||||||
await prisma.layanan.upsert({
|
|
||||||
where: {
|
|
||||||
name: l.name,
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
name: l.name,
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
name: l.name,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("layanan success ...");
|
|
||||||
|
|
||||||
// =========== SDGSDesa ===========
|
// =========== SDGSDesa ===========
|
||||||
for (const l of sdgsDesa) {
|
for (const l of sdgsDesa) {
|
||||||
await prisma.sDGSDesa.upsert({
|
await prisma.sdgsDesa.upsert({
|
||||||
where: {
|
where: { id: l.id },
|
||||||
name: l.name,
|
|
||||||
jumlah: l.jumlah,
|
|
||||||
},
|
|
||||||
update: {
|
update: {
|
||||||
name: l.name,
|
name: l.name,
|
||||||
jumlah: l.jumlah,
|
jumlah: l.jumlah,
|
||||||
@@ -217,6 +392,9 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
|
|
||||||
console.log("sdgs desa success ...");
|
console.log("sdgs desa success ...");
|
||||||
|
|
||||||
|
// =========== MENU DESA ===========
|
||||||
|
// =========== SUBMENU PROFILE ===========
|
||||||
|
// =========== SEJARAH DESA ===========
|
||||||
for (const l of sejarahDesa) {
|
for (const l of sejarahDesa) {
|
||||||
await prisma.sejarahDesa.upsert({
|
await prisma.sejarahDesa.upsert({
|
||||||
where: {
|
where: {
|
||||||
@@ -236,6 +414,7 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
|
|
||||||
console.log("sejarah desa success ...");
|
console.log("sejarah desa success ...");
|
||||||
|
|
||||||
|
// =========== MASKOT DESA ===========
|
||||||
for (const l of maskotDesa) {
|
for (const l of maskotDesa) {
|
||||||
await prisma.maskotDesa.upsert({
|
await prisma.maskotDesa.upsert({
|
||||||
where: {
|
where: {
|
||||||
@@ -255,6 +434,7 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
|
|
||||||
console.log("maskot desa success ...");
|
console.log("maskot desa success ...");
|
||||||
|
|
||||||
|
// =========== LAMBANG DESA ===========
|
||||||
for (const l of lambangDesa) {
|
for (const l of lambangDesa) {
|
||||||
await prisma.lambangDesa.upsert({
|
await prisma.lambangDesa.upsert({
|
||||||
where: {
|
where: {
|
||||||
@@ -274,6 +454,7 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
|
|
||||||
console.log("lambang desa success ...");
|
console.log("lambang desa success ...");
|
||||||
|
|
||||||
|
// =========== PROFIL PERBEKEL ===========
|
||||||
for (const c of profilPerbekel) {
|
for (const c of profilPerbekel) {
|
||||||
await prisma.profilPerbekel.upsert({
|
await prisma.profilPerbekel.upsert({
|
||||||
where: { id: c.id },
|
where: { id: c.id },
|
||||||
@@ -298,6 +479,7 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
"✅ profilePerbekel seeded without imageId (editable later via UI)"
|
"✅ profilePerbekel seeded without imageId (editable later via UI)"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// =========== VISI MISI DESA ===========
|
||||||
for (const l of visiMisiDesa) {
|
for (const l of visiMisiDesa) {
|
||||||
await prisma.visiMisiDesa.upsert({
|
await prisma.visiMisiDesa.upsert({
|
||||||
where: {
|
where: {
|
||||||
@@ -317,6 +499,35 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
|
|
||||||
console.log("visi misi desa success ...");
|
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();
|
const flattenedPosisi = posisiOrganisasiPPID.flat();
|
||||||
|
|
||||||
// ✅ Urutkan berdasarkan hierarki
|
// ✅ Urutkan berdasarkan hierarki
|
||||||
@@ -341,9 +552,9 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
create: p,
|
create: p,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log("✅ Posisi organisasi berhasil");
|
console.log("posisi organisasi berhasil");
|
||||||
|
|
||||||
// 2. Seed Pegawai
|
// =========== PEGAWAI PPID ===========
|
||||||
const flattenedPegawai = pegawaiPPID.flat();
|
const flattenedPegawai = pegawaiPPID.flat();
|
||||||
for (const p of flattenedPegawai) {
|
for (const p of flattenedPegawai) {
|
||||||
await prisma.pegawaiPPID.upsert({
|
await prisma.pegawaiPPID.upsert({
|
||||||
@@ -352,7 +563,70 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
create: p,
|
create: p,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log("✅ Pegawai berhasil");
|
console.log("pegawai berhasil");
|
||||||
|
|
||||||
|
// =========== SUBMENU VISI MISI PPID ===========
|
||||||
|
|
||||||
|
for (const v of visiMisiPPID) {
|
||||||
|
await prisma.visiMisiPPID.upsert({
|
||||||
|
where: {
|
||||||
|
id: v.id,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
misi: v.misi,
|
||||||
|
visi: v.visi,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: v.id,
|
||||||
|
misi: v.misi,
|
||||||
|
visi: v.visi,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("visi misi PPID success ...");
|
||||||
|
|
||||||
|
// =========== SUBMENU DASAR HUKUM PPID ===========
|
||||||
|
for (const v of dasarHukumPPID) {
|
||||||
|
await prisma.dasarHukumPPID.upsert({
|
||||||
|
where: {
|
||||||
|
id: v.id,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
judul: v.judul,
|
||||||
|
content: v.content,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: v.id,
|
||||||
|
judul: v.judul,
|
||||||
|
content: v.content,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("dasar hukum PPID success ...");
|
||||||
|
|
||||||
|
// =========== SUBMENU DAFTAR INFORMASI PUBLIK PPID ===========
|
||||||
|
for (const v of daftarInformasiPublik) {
|
||||||
|
// Convert string date to Date object
|
||||||
|
const tanggal = new Date(v.tanggal);
|
||||||
|
|
||||||
|
await prisma.daftarInformasiPublik.upsert({
|
||||||
|
where: {
|
||||||
|
id: v.id,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
jenisInformasi: v.jenisInformasi,
|
||||||
|
deskripsi: v.deskripsi,
|
||||||
|
tanggal: tanggal,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: v.id,
|
||||||
|
jenisInformasi: v.jenisInformasi,
|
||||||
|
deskripsi: v.deskripsi,
|
||||||
|
tanggal: tanggal,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("daftar informasi publik PPID success ...");
|
||||||
|
|
||||||
for (const l of pelayananPerizinanBerusaha) {
|
for (const l of pelayananPerizinanBerusaha) {
|
||||||
await prisma.pelayananPerizinanBerusaha.upsert({
|
await prisma.pelayananPerizinanBerusaha.upsert({
|
||||||
@@ -486,48 +760,6 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
}
|
}
|
||||||
console.log("cara memperoleh salinan informasi success ...");
|
console.log("cara memperoleh salinan informasi success ...");
|
||||||
|
|
||||||
for (const c of profilePPID) {
|
|
||||||
await prisma.profilePPID.upsert({
|
|
||||||
where: { id: c.id },
|
|
||||||
update: {
|
|
||||||
name: c.name,
|
|
||||||
biodata: c.biodata,
|
|
||||||
riwayat: c.riwayat,
|
|
||||||
pengalaman: c.pengalaman,
|
|
||||||
unggulan: c.unggulan,
|
|
||||||
// imageId tidak di-update
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
id: c.id,
|
|
||||||
name: c.name,
|
|
||||||
biodata: c.biodata,
|
|
||||||
riwayat: c.riwayat,
|
|
||||||
pengalaman: c.pengalaman,
|
|
||||||
unggulan: c.unggulan,
|
|
||||||
// imageId tidak di-create
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log("✅ profilePPID seeded without imageId (editable later via UI)");
|
|
||||||
|
|
||||||
for (const v of visiMisiPPID) {
|
|
||||||
await prisma.visiMisiPPID.upsert({
|
|
||||||
where: {
|
|
||||||
id: v.id,
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
misi: v.misi,
|
|
||||||
visi: v.visi,
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
id: v.id,
|
|
||||||
misi: v.misi,
|
|
||||||
visi: v.visi,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log("visi misi PPID success ...");
|
|
||||||
|
|
||||||
for (const j of jenisKelamin) {
|
for (const j of jenisKelamin) {
|
||||||
await prisma.jenisKelaminResponden.upsert({
|
await prisma.jenisKelaminResponden.upsert({
|
||||||
where: {
|
where: {
|
||||||
@@ -576,24 +808,6 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
}
|
}
|
||||||
console.log("umur responden success ...");
|
console.log("umur responden success ...");
|
||||||
|
|
||||||
for (const v of dasarHukumPPID) {
|
|
||||||
await prisma.dasarHukumPPID.upsert({
|
|
||||||
where: {
|
|
||||||
id: v.id,
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
judul: v.judul,
|
|
||||||
content: v.content,
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
id: v.id,
|
|
||||||
judul: v.judul,
|
|
||||||
content: v.content,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log("dasar hukum PPID success ...");
|
|
||||||
|
|
||||||
for (const k of kategoriProduk) {
|
for (const k of kategoriProduk) {
|
||||||
await prisma.kategoriProduk.upsert({
|
await prisma.kategoriProduk.upsert({
|
||||||
where: {
|
where: {
|
||||||
@@ -681,9 +895,12 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
console.log("hubungan organisasi success ...");
|
console.log("hubungan organisasi success ...");
|
||||||
|
|
||||||
for (const d of detailDataPengangguran) {
|
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({
|
await prisma.detailDataPengangguran.upsert({
|
||||||
where: {
|
where: {
|
||||||
month_year: { month: d.month, year: d.year },
|
month_year: { month: d.month, year: yearAsDate },
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
totalUnemployment: d.totalUnemployment,
|
totalUnemployment: d.totalUnemployment,
|
||||||
@@ -693,7 +910,7 @@ import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSak
|
|||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
month: d.month,
|
month: d.month,
|
||||||
year: d.year,
|
year: yearAsDate,
|
||||||
totalUnemployment: d.totalUnemployment,
|
totalUnemployment: d.totalUnemployment,
|
||||||
educatedUnemployment: d.educatedUnemployment,
|
educatedUnemployment: d.educatedUnemployment,
|
||||||
uneducatedUnemployment: d.uneducatedUnemployment,
|
uneducatedUnemployment: d.uneducatedUnemployment,
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
BIN
public/chatbot-removebg-preview.png
Normal file
|
After Width: | Height: | Size: 303 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 275 KiB |
@@ -23,6 +23,7 @@ export default function SpashScreen() {
|
|||||||
<Paper p={"md"} miw={320}>
|
<Paper p={"md"} miw={320}>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Image
|
<Image
|
||||||
|
loading="lazy"
|
||||||
src={images["darmasaba-icon"]}
|
src={images["darmasaba-icon"]}
|
||||||
alt="darmasaba"
|
alt="darmasaba"
|
||||||
w={100}
|
w={100}
|
||||||
|
|||||||
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -74,18 +74,18 @@ const berita = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
search: "",
|
search: "",
|
||||||
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||||
berita.findMany.loading = true; // ✅ Akses langsung via nama path
|
berita.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
berita.findMany.page = page;
|
berita.findMany.page = page;
|
||||||
berita.findMany.search = search;
|
berita.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const query: any = { page, limit };
|
const query: any = { page, limit };
|
||||||
if (search) query.search = search;
|
if (search) query.search = search;
|
||||||
if (kategori) query.kategori = kategori;
|
if (kategori) query.kategori = kategori;
|
||||||
|
|
||||||
const res = await ApiFetch.api.desa.berita["find-many"].get({ query });
|
const res = await ApiFetch.api.desa.berita["find-many"].get({ query });
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
berita.findMany.data = res.data.data ?? [];
|
berita.findMany.data = res.data.data ?? [];
|
||||||
berita.findMany.totalPages = res.data.totalPages ?? 1;
|
berita.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
@@ -368,11 +368,37 @@ const kategoriBerita = proxy({
|
|||||||
isActive: true;
|
isActive: true;
|
||||||
};
|
};
|
||||||
}>[],
|
}>[],
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
const res = await ApiFetch.api.desa.kategoriberita["findMany"].get();
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
if (res.status === 200) {
|
kategoriBerita.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
kategoriBerita.findMany.data = res.data?.data ?? [];
|
kategoriBerita.findMany.page = page;
|
||||||
|
kategoriBerita.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.desa.kategoriberita[
|
||||||
|
"findMany"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
kategoriBerita.findMany.data = res.data.data ?? [];
|
||||||
|
kategoriBerita.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
kategoriBerita.findMany.data = [];
|
||||||
|
kategoriBerita.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch kategori berita paginated:", err);
|
||||||
|
kategoriBerita.findMany.data = [];
|
||||||
|
kategoriBerita.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
kategoriBerita.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ const templateTelunjukSaktiDesaForm = z.object({
|
|||||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const templatePelayananPerizinanBerusaha = z.object({
|
const templatePelayananPerizinanBerusaha = z.object({
|
||||||
name: z.string().min(3, "Nama minimal 3 karakter"),
|
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||||
@@ -72,7 +71,6 @@ const pelayananPendudukNonPermanenForm = {
|
|||||||
deskripsi: "",
|
deskripsi: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const suratKeterangan = proxy({
|
const suratKeterangan = proxy({
|
||||||
create: {
|
create: {
|
||||||
form: { ...suratKeteranganForm },
|
form: { ...suratKeteranganForm },
|
||||||
@@ -113,16 +111,21 @@ const suratKeterangan = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
search: "",
|
||||||
suratKeterangan.findMany.loading = true; // Use the full path to access the property
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
// Change to arrow function
|
||||||
|
suratKeterangan.findMany.loading = true; // Use the full path to access the property
|
||||||
suratKeterangan.findMany.page = page;
|
suratKeterangan.findMany.page = page;
|
||||||
|
suratKeterangan.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
const res = await ApiFetch.api.desa.layanan.pelayanansuratketerangan[
|
const res = await ApiFetch.api.desa.layanan.pelayanansuratketerangan[
|
||||||
"find-many"
|
"find-many"
|
||||||
].get({
|
].get({
|
||||||
query: { page, limit },
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
suratKeterangan.findMany.data = res.data.data || [];
|
suratKeterangan.findMany.data = res.data.data || [];
|
||||||
suratKeterangan.findMany.total = res.data.total || 0;
|
suratKeterangan.findMany.total = res.data.total || 0;
|
||||||
@@ -341,28 +344,34 @@ const pelayananTelunjukSaktiDesa = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
search: "",
|
||||||
pelayananTelunjukSaktiDesa.findMany.loading = true; // Use the full path to access the property
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
// Change to arrow function
|
||||||
|
pelayananTelunjukSaktiDesa.findMany.loading = true; // Use the full path to access the property
|
||||||
pelayananTelunjukSaktiDesa.findMany.page = page;
|
pelayananTelunjukSaktiDesa.findMany.page = page;
|
||||||
|
pelayananTelunjukSaktiDesa.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
const res = await ApiFetch.api.desa.layanan.pelayanantelunjuksaktidesa[
|
const res = await ApiFetch.api.desa.layanan.pelayanantelunjuksaktidesa[
|
||||||
"find-many"
|
"find-many"
|
||||||
].get({
|
].get({
|
||||||
query: { page, limit },
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
pelayananTelunjukSaktiDesa.findMany.data = res.data.data || [];
|
pelayananTelunjukSaktiDesa.findMany.data = res.data.data || [];
|
||||||
pelayananTelunjukSaktiDesa.findMany.total = res.data.total || 0;
|
pelayananTelunjukSaktiDesa.findMany.total = res.data.total || 0;
|
||||||
pelayananTelunjukSaktiDesa.findMany.totalPages = res.data.totalPages || 1;
|
pelayananTelunjukSaktiDesa.findMany.totalPages =
|
||||||
|
res.data.totalPages || 1;
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to load telunjuk sakti desa:", res.data?.message);
|
console.error("Failed to load surat keterangan:", res.data?.message);
|
||||||
pelayananTelunjukSaktiDesa.findMany.data = [];
|
pelayananTelunjukSaktiDesa.findMany.data = [];
|
||||||
pelayananTelunjukSaktiDesa.findMany.total = 0;
|
suratKeterangan.findMany.total = 0;
|
||||||
pelayananTelunjukSaktiDesa.findMany.totalPages = 1;
|
suratKeterangan.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading telunjuk sakti desa:", error);
|
console.error("Error loading surat keterangan:", error);
|
||||||
pelayananTelunjukSaktiDesa.findMany.data = [];
|
pelayananTelunjukSaktiDesa.findMany.data = [];
|
||||||
pelayananTelunjukSaktiDesa.findMany.total = 0;
|
pelayananTelunjukSaktiDesa.findMany.total = 0;
|
||||||
pelayananTelunjukSaktiDesa.findMany.totalPages = 1;
|
pelayananTelunjukSaktiDesa.findMany.totalPages = 1;
|
||||||
@@ -410,7 +419,9 @@ const pelayananTelunjukSaktiDesa = proxy({
|
|||||||
);
|
);
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
toast.success(result.message || "Telunjuk Sakti Desa berhasil dihapus");
|
toast.success(
|
||||||
|
result.message || "Telunjuk Sakti Desa berhasil dihapus"
|
||||||
|
);
|
||||||
await pelayananTelunjukSaktiDesa.findMany.load(); // refresh list
|
await pelayananTelunjukSaktiDesa.findMany.load(); // refresh list
|
||||||
} else {
|
} else {
|
||||||
toast.error(result.message || "Gagal menghapus telunjuk sakti desa");
|
toast.error(result.message || "Gagal menghapus telunjuk sakti desa");
|
||||||
@@ -501,7 +512,9 @@ const pelayananTelunjukSaktiDesa = proxy({
|
|||||||
}
|
}
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
toast.success(result.message || "Telunjuk Sakti Desa berhasil diupdate");
|
toast.success(
|
||||||
|
result.message || "Telunjuk Sakti Desa berhasil diupdate"
|
||||||
|
);
|
||||||
await pelayananTelunjukSaktiDesa.findMany.load(); // refresh list
|
await pelayananTelunjukSaktiDesa.findMany.load(); // refresh list
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -522,7 +535,7 @@ const pelayananTelunjukSaktiDesa = proxy({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const pelayananPerizinanBerusaha = proxy({
|
const pelayananPerizinanBerusaha = proxy({
|
||||||
findById: {
|
findById: {
|
||||||
@@ -596,9 +609,7 @@ const pelayananPerizinanBerusaha = proxy({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching pelayanan perizinan berusaha:", error);
|
console.error("Error fetching pelayanan perizinan berusaha:", error);
|
||||||
toast.error(
|
toast.error(
|
||||||
error instanceof Error
|
error instanceof Error ? error.message : "Gagal memuat data"
|
||||||
? error.message
|
|
||||||
: "Gagal memuat data"
|
|
||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -713,9 +724,7 @@ const pelayananPendudukNonPermanen = proxy({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching pelayanan penduduk non permanen:", error);
|
console.error("Error fetching pelayanan penduduk non permanen:", error);
|
||||||
toast.error(
|
toast.error(
|
||||||
error instanceof Error
|
error instanceof Error ? error.message : "Gagal memuat data"
|
||||||
? error.message
|
|
||||||
: "Gagal memuat data"
|
|
||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,16 +56,21 @@ const penghargaanState = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
search: "",
|
||||||
penghargaanState.findMany.loading = true; // Use the full path to access the property
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
// Change to arrow function
|
||||||
|
penghargaanState.findMany.loading = true; // Use the full path to access the property
|
||||||
penghargaanState.findMany.page = page;
|
penghargaanState.findMany.page = page;
|
||||||
|
penghargaanState.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
const res = await ApiFetch.api.desa.penghargaan[
|
const res = await ApiFetch.api.desa.penghargaan[
|
||||||
"find-many"
|
"find-many"
|
||||||
].get({
|
].get({
|
||||||
query: { page, limit },
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
penghargaanState.findMany.data = res.data.data || [];
|
penghargaanState.findMany.data = res.data.data || [];
|
||||||
penghargaanState.findMany.total = res.data.total || 0;
|
penghargaanState.findMany.total = res.data.total || 0;
|
||||||
|
|||||||
@@ -55,11 +55,39 @@ const category = proxy({
|
|||||||
pengumumans: number;
|
pengumumans: number;
|
||||||
};
|
};
|
||||||
})[],
|
})[],
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
const res = await ApiFetch.api.desa.kategoripengumuman["findMany"].get();
|
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||||
if (res.status === 200) {
|
category.findMany.loading = true; // Use the full path to access the property
|
||||||
category.findMany.data = res.data?.data ?? [];
|
category.findMany.page = page;
|
||||||
|
category.findMany.search = search;
|
||||||
|
try {
|
||||||
|
const res = await ApiFetch.api.desa.kategoripengumuman[
|
||||||
|
"findMany"
|
||||||
|
].get({
|
||||||
|
query: { page, limit },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
category.findMany.data = res.data.data || [];
|
||||||
|
category.findMany.total = res.data.total || 0;
|
||||||
|
category.findMany.totalPages = res.data.totalPages || 1;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to load potensi desa:", res.data?.message);
|
||||||
|
category.findMany.data = [];
|
||||||
|
category.findMany.total = 0;
|
||||||
|
category.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading potensi desa:", error);
|
||||||
|
category.findMany.data = [];
|
||||||
|
category.findMany.total = 0;
|
||||||
|
category.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
category.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -56,9 +56,11 @@ const potensiDesa = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||||
potensiDesa.findMany.loading = true; // Use the full path to access the property
|
potensiDesa.findMany.loading = true; // Use the full path to access the property
|
||||||
potensiDesa.findMany.page = page;
|
potensiDesa.findMany.page = page;
|
||||||
|
potensiDesa.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
const res = await ApiFetch.api.desa.potensi[
|
const res = await ApiFetch.api.desa.potensi[
|
||||||
"find-many"
|
"find-many"
|
||||||
@@ -298,11 +300,34 @@ const kategoriPotensi = proxy({
|
|||||||
isActive: true;
|
isActive: true;
|
||||||
};
|
};
|
||||||
}>[],
|
}>[],
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
const res = await ApiFetch.api.desa.kategoripotensi["findMany"].get();
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
if (res.status === 200) {
|
kategoriPotensi.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
kategoriPotensi.findMany.data = res.data?.data ?? [];
|
kategoriPotensi.findMany.page = page;
|
||||||
|
kategoriPotensi.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.desa.kategoripotensi["findMany"].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
kategoriPotensi.findMany.data = res.data.data ?? [];
|
||||||
|
kategoriPotensi.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
kategoriPotensi.findMany.data = [];
|
||||||
|
kategoriPotensi.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch kategori potensi paginated:", err);
|
||||||
|
kategoriPotensi.findMany.data = [];
|
||||||
|
kategoriPotensi.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
kategoriPotensi.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -61,13 +62,39 @@ const lowonganKerjaState = proxy({
|
|||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.LowonganPekerjaanGetPayload<{
|
| Prisma.LowonganPekerjaanGetPayload<{
|
||||||
omit: { isActive: true };
|
omit: {
|
||||||
|
isActive: true;
|
||||||
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.ekonomi.lowongankerja["find-many"].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
loading: false,
|
||||||
lowonganKerjaState.findMany.data = res.data?.data ?? [];
|
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 ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -53,22 +54,47 @@ const pasarDesa = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as Array<
|
data: null as
|
||||||
Prisma.PasarDesaGetPayload<{
|
| Prisma.PasarDesaGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
image: true;
|
image: true;
|
||||||
KategoriToPasar: {
|
KategoriToPasar: {
|
||||||
include: {
|
include: {
|
||||||
kategori: true;
|
kategori: true;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
}>[]
|
||||||
}>
|
| null,
|
||||||
> | null,
|
page: 1,
|
||||||
async load() {
|
totalPages: 1,
|
||||||
const res = await ApiFetch.api.ekonomi.pasardesa["find-many"].get();
|
loading: false,
|
||||||
if (res.status === 200) {
|
search: "",
|
||||||
pasarDesa.findMany.data = res.data?.data ?? [];
|
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: {
|
findMany: {
|
||||||
data: null as Array<{
|
data: null as
|
||||||
id: string;
|
| Prisma.KategoriProdukGetPayload<{
|
||||||
nama: string;
|
omit: {
|
||||||
}> | null,
|
isActive: true;
|
||||||
async load() {
|
};
|
||||||
const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get();
|
}>[]
|
||||||
if (res.status === 200) {
|
| null,
|
||||||
kategoriProduk.findMany.data = res.data?.data ?? [];
|
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 ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -11,8 +12,7 @@ const templateForm = z.object({
|
|||||||
statistik: z.object({
|
statistik: z.object({
|
||||||
tahun: z.string().min(1, "Tahun minimal 1 karakter"),
|
tahun: z.string().min(1, "Tahun minimal 1 karakter"),
|
||||||
jumlah: z.string().min(1, "Jumlah minimal 1 karakter"),
|
jumlah: z.string().min(1, "Jumlah minimal 1 karakter"),
|
||||||
})
|
}),
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultForm = {
|
const defaultForm = {
|
||||||
@@ -21,8 +21,8 @@ const defaultForm = {
|
|||||||
ikonUrl: "",
|
ikonUrl: "",
|
||||||
statistik: {
|
statistik: {
|
||||||
tahun: "",
|
tahun: "",
|
||||||
jumlah: ""
|
jumlah: "",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const programKemiskinanState = proxy({
|
const programKemiskinanState = proxy({
|
||||||
@@ -64,12 +64,35 @@ const programKemiskinanState = proxy({
|
|||||||
};
|
};
|
||||||
}>[],
|
}>[],
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.ekonomi.programkemiskinan[
|
totalPages: 1,
|
||||||
"find-many"
|
search: "",
|
||||||
].get();
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
if (res.status === 200) {
|
programKemiskinanState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
programKemiskinanState.findMany.data = res.data?.data ?? [];
|
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 ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -55,10 +56,34 @@ const desaDigitalState = proxy({
|
|||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.inovasi.desadigital["find-many"].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
loading: false,
|
||||||
desaDigitalState.findMany.data = res.data?.data ?? [];
|
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 ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -55,10 +56,34 @@ const infoTeknoState = proxy({
|
|||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.inovasi.infotekno["find-many"].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
loading: false,
|
||||||
infoTeknoState.findMany.data = res.data?.data ?? [];
|
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";
|
import { z } from "zod";
|
||||||
|
|
||||||
const templateForm = z.object({
|
const templateForm = z.object({
|
||||||
name: z.string().min(1, "Nama minimal 1 karakter"),
|
name: z.string().min(1, "Nama kolaborasi inovasi harus diisi"),
|
||||||
tahun: z.number().min(4, "Tahun minimal 4 karakter"),
|
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, "Deskripsi singkat minimal 1 karakter"),
|
slug: z.string().min(1, "Slug harus dihasilkan otomatis"),
|
||||||
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
deskripsi: z.string().min(1, "Deskripsi harus diisi"),
|
||||||
kolaborator: z.string().min(1, "Kolaborator minimal 1 karakter"),
|
kolaborator: z.string().min(1, "Kolaborator harus diisi"),
|
||||||
imageId: z.string().min(1, "Image ID minimal 1 karakter"),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const defaultForm = {
|
const defaultForm = {
|
||||||
@@ -20,7 +19,6 @@ const defaultForm = {
|
|||||||
slug: "",
|
slug: "",
|
||||||
deskripsi: "",
|
deskripsi: "",
|
||||||
kolaborator: "",
|
kolaborator: "",
|
||||||
imageId: "",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const kolaborasiInovasiState = proxy({
|
const kolaborasiInovasiState = proxy({
|
||||||
@@ -28,27 +26,37 @@ const kolaborasiInovasiState = proxy({
|
|||||||
form: { ...defaultForm },
|
form: { ...defaultForm },
|
||||||
loading: false,
|
loading: false,
|
||||||
async create() {
|
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 {
|
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;
|
kolaborasiInovasiState.create.loading = true;
|
||||||
|
|
||||||
const res = await ApiFetch.api.inovasi.kolaborasiinovasi["create"].post(
|
const res = await ApiFetch.api.inovasi.kolaborasiinovasi["create"].post(
|
||||||
kolaborasiInovasiState.create.form
|
kolaborasiInovasiState.create.form
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
kolaborasiInovasiState.findMany.load();
|
await kolaborasiInovasiState.findMany.load();
|
||||||
return toast.success("success create");
|
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) {
|
} 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 {
|
} finally {
|
||||||
kolaborasiInovasiState.create.loading = false;
|
kolaborasiInovasiState.create.loading = false;
|
||||||
}
|
}
|
||||||
@@ -60,13 +68,21 @@ const kolaborasiInovasiState = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => {
|
search: "",
|
||||||
// Change to arrow function
|
year: "",
|
||||||
kolaborasiInovasiState.findMany.loading = true; // Use the full path to access the property
|
load: async (page = 1, limit = 10, search = "", year?: string) => {
|
||||||
|
kolaborasiInovasiState.findMany.loading = true;
|
||||||
kolaborasiInovasiState.findMany.page = page;
|
kolaborasiInovasiState.findMany.page = page;
|
||||||
|
kolaborasiInovasiState.findMany.search = search;
|
||||||
|
kolaborasiInovasiState.findMany.year = year || "";
|
||||||
|
|
||||||
try {
|
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({
|
const res = await ApiFetch.api.inovasi.kolaborasiinovasi["find-many"].get({
|
||||||
query: { page, limit },
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
@@ -124,7 +140,6 @@ const kolaborasiInovasiState = proxy({
|
|||||||
slug: data.slug,
|
slug: data.slug,
|
||||||
deskripsi: data.deskripsi,
|
deskripsi: data.deskripsi,
|
||||||
kolaborator: data.kolaborator,
|
kolaborator: data.kolaborator,
|
||||||
imageId: data.imageId,
|
|
||||||
};
|
};
|
||||||
return data;
|
return data;
|
||||||
} else {
|
} else {
|
||||||
@@ -179,7 +194,7 @@ const kolaborasiInovasiState = proxy({
|
|||||||
},
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.KolaborasiInovasiGetPayload<{
|
data: null as Prisma.KolaborasiInovasiGetPayload<{
|
||||||
include: { image: true };
|
omit: { isActive: true };
|
||||||
}> | null,
|
}> | null,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
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 ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -53,15 +54,39 @@ const keamananLingkunganState = proxy({
|
|||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.KeamananLingkunganGetPayload<{
|
| Prisma.KeamananLingkunganGetPayload<{
|
||||||
include: { image: true };
|
include: {
|
||||||
|
image: true;
|
||||||
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.keamanan.keamananlingkungan[
|
totalPages: 1,
|
||||||
"find-many"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
keamananLingkunganState.findMany.data = res.data?.data ?? [];
|
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 ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -7,25 +8,13 @@ import { z } from "zod";
|
|||||||
const templateForm = z.object({
|
const templateForm = z.object({
|
||||||
nama: z.string().min(1, "Nama minimal 1 karakter"),
|
nama: z.string().min(1, "Nama minimal 1 karakter"),
|
||||||
imageId: z.string().nonempty(),
|
imageId: z.string().nonempty(),
|
||||||
kontakItems: z.array(
|
kategoriId: z.array(z.string()).min(1, "Minimal pilih satu kategori"),
|
||||||
z.object({
|
|
||||||
nama: z.string().min(1, "Nama minimal 1 karakter"),
|
|
||||||
nomorTelepon: z.string().min(1, "Nomor Telepon minimal 1 karakter"),
|
|
||||||
imageId: z.string().nonempty(),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultForm = {
|
const defaultForm = {
|
||||||
nama: "",
|
nama: "",
|
||||||
imageId: "",
|
imageId: "",
|
||||||
kontakItems: [
|
kategoriId: [] as string[],
|
||||||
{
|
|
||||||
nama: "",
|
|
||||||
nomorTelepon: "",
|
|
||||||
imageId: "",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const kontakDaruratKeamananState = proxy({
|
const kontakDaruratKeamananState = proxy({
|
||||||
@@ -61,20 +50,50 @@ const kontakDaruratKeamananState = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as Array<
|
||||||
| Prisma.KontakDaruratKeamananGetPayload<{
|
Prisma.KontakDaruratKeamananGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
kontakItems: true;
|
kategori: true;
|
||||||
image: true;
|
image: true;
|
||||||
|
kontakItems: {
|
||||||
|
include: {
|
||||||
|
kontakItem: true;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}>[]
|
};
|
||||||
| null,
|
}>
|
||||||
async load() {
|
> | null,
|
||||||
const res = await ApiFetch.api.keamanan.kontakdaruratkeamanan[
|
page: 1,
|
||||||
"find-many"
|
totalPages: 1,
|
||||||
].get();
|
loading: false,
|
||||||
if (res.status === 200) {
|
search: "",
|
||||||
kontakDaruratKeamananState.findMany.data = res.data?.data ?? [];
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
kontakDaruratKeamananState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
kontakDaruratKeamananState.findMany.page = page;
|
||||||
|
kontakDaruratKeamananState.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.keamanan.kontakdaruratkeamanan[
|
||||||
|
"findMany"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
kontakDaruratKeamananState.findMany.data = res.data.data ?? [];
|
||||||
|
kontakDaruratKeamananState.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
kontakDaruratKeamananState.findMany.data = [];
|
||||||
|
kontakDaruratKeamananState.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch kontak darurat paginated:", err);
|
||||||
|
kontakDaruratKeamananState.findMany.data = [];
|
||||||
|
kontakDaruratKeamananState.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
kontakDaruratKeamananState.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -83,10 +102,15 @@ const kontakDaruratKeamananState = proxy({
|
|||||||
include: {
|
include: {
|
||||||
kontakItems: {
|
kontakItems: {
|
||||||
include: {
|
include: {
|
||||||
image: true;
|
kontakItem: {
|
||||||
|
include: {
|
||||||
|
image: true;
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
image: true;
|
image: true;
|
||||||
|
kategori: true;
|
||||||
};
|
};
|
||||||
}> | null,
|
}> | null,
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -168,14 +192,8 @@ const kontakDaruratKeamananState = proxy({
|
|||||||
this.id = data.id;
|
this.id = data.id;
|
||||||
this.form = {
|
this.form = {
|
||||||
nama: data.nama,
|
nama: data.nama,
|
||||||
imageId: data.imageId,
|
imageId: data.imageId || '',
|
||||||
kontakItems: [
|
kategoriId: data.kontakItems?.map((item: any) => item.kontakItemId) || []
|
||||||
{
|
|
||||||
nama: data.kontakItems.nama,
|
|
||||||
nomorTelepon: data.kontakItems.nomorTelepon,
|
|
||||||
imageId: data.kontakItems.imageId,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
return data;
|
return data;
|
||||||
} else {
|
} else {
|
||||||
@@ -213,13 +231,7 @@ const kontakDaruratKeamananState = proxy({
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
nama: this.form.nama,
|
nama: this.form.nama,
|
||||||
imageId: this.form.imageId,
|
imageId: this.form.imageId,
|
||||||
kontakItems: [
|
kategoriId: this.form.kategoriId,
|
||||||
{
|
|
||||||
nama: this.form.kontakItems[0].nama,
|
|
||||||
nomorTelepon: this.form.kontakItems[0].nomorTelepon,
|
|
||||||
imageId: this.form.kontakItems[0].imageId,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -256,4 +268,257 @@ const kontakDaruratKeamananState = proxy({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default kontakDaruratKeamananState;
|
const templateFormItem = z.object({
|
||||||
|
nama: z.string().min(1, "Nama minimal 1 karakter"),
|
||||||
|
nomorTelepon: z.string().min(1, "Nomor Telepon minimal 1 karakter"),
|
||||||
|
imageId: z.string().nonempty(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultFormItem = {
|
||||||
|
nama: "",
|
||||||
|
nomorTelepon: "",
|
||||||
|
imageId: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const kontakDaruratItem = proxy({
|
||||||
|
create: {
|
||||||
|
form: { ...defaultFormItem },
|
||||||
|
loading: false,
|
||||||
|
async create() {
|
||||||
|
const cek = templateFormItem.safeParse(
|
||||||
|
kontakDaruratItem.create.form
|
||||||
|
);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
kontakDaruratItem.create.loading = true;
|
||||||
|
const res = await ApiFetch.api.keamanan.kontakitem[
|
||||||
|
"create"
|
||||||
|
].post(kontakDaruratItem.create.form);
|
||||||
|
if (res.status === 200) {
|
||||||
|
kontakDaruratItem.findMany.load();
|
||||||
|
return toast.success("success create");
|
||||||
|
}
|
||||||
|
console.log(res);
|
||||||
|
return toast.error("failed create");
|
||||||
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
} finally {
|
||||||
|
kontakDaruratItem.create.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findMany: {
|
||||||
|
data: null as Array<
|
||||||
|
Prisma.KontakItemGetPayload<{
|
||||||
|
include: {
|
||||||
|
image: true;
|
||||||
|
};
|
||||||
|
}>
|
||||||
|
> | null,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
loading: false,
|
||||||
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
kontakDaruratItem.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
kontakDaruratItem.findMany.page = page;
|
||||||
|
kontakDaruratItem.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.keamanan.kontakitem[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
kontakDaruratItem.findMany.data = res.data.data ?? [];
|
||||||
|
kontakDaruratItem.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
kontakDaruratItem.findMany.data = [];
|
||||||
|
kontakDaruratItem.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch kontak darurat paginated:", err);
|
||||||
|
kontakDaruratItem.findMany.data = [];
|
||||||
|
kontakDaruratItem.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
kontakDaruratItem.findMany.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.KontakItemGetPayload<{
|
||||||
|
include: {
|
||||||
|
kategori: true;
|
||||||
|
image: true;
|
||||||
|
};
|
||||||
|
}> | null,
|
||||||
|
loading: false,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/keamanan/kontakitem/${id}`);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
kontakDaruratItem.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch data", res.status, res.statusText);
|
||||||
|
kontakDaruratItem.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
kontakDaruratItem.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
try {
|
||||||
|
kontakDaruratItem.delete.loading = true;
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/keamanan/kontakitem/del/${id}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(result.message || "Kontak item berhasil dihapus");
|
||||||
|
await kontakDaruratItem.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(result?.message || "Gagal menghapus kontak item");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus kontak item");
|
||||||
|
} finally {
|
||||||
|
kontakDaruratItem.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
id: "",
|
||||||
|
form: { ...defaultFormItem },
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
async load(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/keamanan/kontakitem/${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,
|
||||||
|
nomorTelepon: data.nomorTelepon,
|
||||||
|
imageId: data.imageId,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw new Error(result?.message || "Gagal memuat data");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading kontak darurat:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error ? error.message : "Gagal memuat data"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
const cek = templateFormItem.safeParse(
|
||||||
|
kontakDaruratItem.update.form
|
||||||
|
);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
kontakDaruratItem.update.loading = true;
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/keamanan/kontakitem/${this.id}`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
nama: this.form.nama,
|
||||||
|
nomorTelepon: this.form.nomorTelepon,
|
||||||
|
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 kontak item");
|
||||||
|
await kontakDaruratItem.findMany.load(); // refresh list
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || "Gagal mengupdate kontak item");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating kontak item:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Gagal mengupdate kontak item"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
kontakDaruratItem.update.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
kontakDaruratItem.update.id = "";
|
||||||
|
kontakDaruratItem.update.form = { ...defaultFormItem };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const kontakDarurat = proxy({
|
||||||
|
kontakDaruratKeamananState,
|
||||||
|
kontakDaruratItem,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default kontakDarurat;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -88,12 +89,37 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.keamanan.pencegahankriminalitas[
|
totalPages: 1,
|
||||||
"find-many"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
pencegahanKriminalitasState.findMany.data = res.data?.data ?? [];
|
pencegahanKriminalitasState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
pencegahanKriminalitasState.findMany.page = page;
|
||||||
|
pencegahanKriminalitasState.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.keamanan.pencegahankriminalitas[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
pencegahanKriminalitasState.findMany.data = res.data.data ?? [];
|
||||||
|
pencegahanKriminalitasState.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
pencegahanKriminalitasState.findMany.data = [];
|
||||||
|
pencegahanKriminalitasState.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch pencegahan kriminalitas paginated:", err);
|
||||||
|
pencegahanKriminalitasState.findMany.data = [];
|
||||||
|
pencegahanKriminalitasState.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
pencegahanKriminalitasState.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -311,4 +337,4 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
export default pencegahanKriminalitasState;
|
export default pencegahanKriminalitasState;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -63,13 +64,41 @@ const polsekTerdekatState = proxy({
|
|||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.PolsekTerdekatGetPayload<{
|
| Prisma.PolsekTerdekatGetPayload<{
|
||||||
include: { layananPolsek: true };
|
include: {
|
||||||
|
layananPolsek: true;
|
||||||
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.keamanan.polsekterdekat["find-many"].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
loading: false,
|
||||||
polsekTerdekatState.findMany.data = res.data?.data ?? [];
|
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 };
|
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;
|
export default polsekTerdekatState;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -53,15 +54,39 @@ const tipsKeamananState = proxy({
|
|||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.MenuTipsKeamananGetPayload<{
|
| Prisma.MenuTipsKeamananGetPayload<{
|
||||||
include: { image: true };
|
include: {
|
||||||
|
image: true;
|
||||||
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.keamanan.menutipskeamanan[
|
totalPages: 1,
|
||||||
"find-many"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
tipsKeamananState.findMany.data = res.data?.data ?? [];
|
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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -115,27 +115,38 @@ const artikelKesehatanState = proxy({
|
|||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
artikelKesehatanState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
artikelKesehatanState.findMany.page = page;
|
||||||
|
artikelKesehatanState.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.loading = true;
|
const query: any = { page, limit };
|
||||||
const res = await (ApiFetch.api.kesehatan as any)["artikel-kesehatan"][
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.kesehatan["artikel-kesehatan"][
|
||||||
"find-many"
|
"find-many"
|
||||||
].get();
|
].get({ query });
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
this.data = res.data?.data ?? [];
|
artikelKesehatanState.findMany.data =
|
||||||
|
res.data.data ?? [];
|
||||||
|
artikelKesehatanState.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
} else {
|
} else {
|
||||||
toast.error("Gagal memuat data artikel kesehatan");
|
artikelKesehatanState.findMany.data = [];
|
||||||
|
artikelKesehatanState.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error("Terjadi error saat load data");
|
console.error("Gagal fetch artikel kesehatan paginated:", err);
|
||||||
console.error("LOAD ERROR:", err);
|
artikelKesehatanState.findMany.data = [];
|
||||||
throw err;
|
artikelKesehatanState.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
artikelKesehatanState.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -280,12 +291,9 @@ const artikelKesehatanState = proxy({
|
|||||||
async byId(id: string) {
|
async byId(id: string) {
|
||||||
try {
|
try {
|
||||||
artikelKesehatanState.delete.loading = true;
|
artikelKesehatanState.delete.loading = true;
|
||||||
const res = await fetch(
|
const res = await fetch(`/api/kesehatan/artikel-kesehatan/del/${id}`, {
|
||||||
`/api/kesehatan/artikel-kesehatan/del/${id}`,
|
method: "DELETE",
|
||||||
{
|
});
|
||||||
method: "DELETE",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
if (res.ok && result.success) {
|
if (res.ok && result.success) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { toast } from "react-toastify";
|
|||||||
import { proxy } from "valtio";
|
import { proxy } from "valtio";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
//fasilitas kesehatan aja
|
||||||
// Validasi form
|
// Validasi form
|
||||||
const templateForm = z.object({
|
const templateForm = z.object({
|
||||||
name: z.string().min(1, "Nama harus diisi"),
|
name: z.string().min(1, "Nama harus diisi"),
|
||||||
@@ -61,7 +62,7 @@ const defaultForm = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const fasilitasKesehatanState = proxy({
|
const fasilitasKesehatan = proxy({
|
||||||
create: {
|
create: {
|
||||||
form: { ...defaultForm },
|
form: { ...defaultForm },
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -86,7 +87,7 @@ const fasilitasKesehatanState = proxy({
|
|||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
toast.success("Berhasil menambahkan fasilitas kesehatan");
|
toast.success("Berhasil menambahkan fasilitas kesehatan");
|
||||||
this.resetForm();
|
this.resetForm();
|
||||||
await fasilitasKesehatanState.findMany.load();
|
await fasilitasKesehatan.findMany.load();
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -102,7 +103,6 @@ const fasilitasKesehatanState = proxy({
|
|||||||
this.form = { ...defaultForm };
|
this.form = { ...defaultForm };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.FasilitasKesehatanGetPayload<{
|
| Prisma.FasilitasKesehatanGetPayload<{
|
||||||
@@ -116,27 +116,38 @@ const fasilitasKesehatanState = proxy({
|
|||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
fasilitasKesehatanState.fasilitasKesehatan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
fasilitasKesehatanState.fasilitasKesehatan.findMany.page = page;
|
||||||
|
fasilitasKesehatanState.fasilitasKesehatan.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.loading = true;
|
const query: any = { page, limit };
|
||||||
const res = await (ApiFetch.api.kesehatan as any)[
|
if (search) query.search = search;
|
||||||
"fasilitas-kesehatan"
|
|
||||||
]["find-many"].get();
|
|
||||||
|
|
||||||
if (res.status === 200) {
|
const res = await ApiFetch.api.kesehatan["fasilitas-kesehatan"][
|
||||||
this.data = res.data?.data ?? [];
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
fasilitasKesehatanState.fasilitasKesehatan.findMany.data =
|
||||||
|
res.data.data ?? [];
|
||||||
|
fasilitasKesehatanState.fasilitasKesehatan.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
} else {
|
} else {
|
||||||
toast.error("Gagal memuat data fasilitas kesehatan");
|
fasilitasKesehatanState.fasilitasKesehatan.findMany.data = [];
|
||||||
|
fasilitasKesehatanState.fasilitasKesehatan.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error("Terjadi error saat load data");
|
console.error("Gagal fetch fasilitas kesehatan paginated:", err);
|
||||||
console.error("LOAD ERROR:", err);
|
fasilitasKesehatanState.fasilitasKesehatan.findMany.data = [];
|
||||||
throw err;
|
fasilitasKesehatanState.fasilitasKesehatan.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
fasilitasKesehatanState.fasilitasKesehatan.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -156,7 +167,7 @@ const fasilitasKesehatanState = proxy({
|
|||||||
const res = await fetch(`/api/kesehatan/fasilitas-kesehatan/${id}`);
|
const res = await fetch(`/api/kesehatan/fasilitas-kesehatan/${id}`);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
fasilitasKesehatanState.findUnique.data = data.data ?? null;
|
fasilitasKesehatan.findUnique.data = data.data ?? null;
|
||||||
} else {
|
} else {
|
||||||
toast.error("Gagal load data fasilitas kesehatan");
|
toast.error("Gagal load data fasilitas kesehatan");
|
||||||
}
|
}
|
||||||
@@ -176,8 +187,8 @@ const fasilitasKesehatanState = proxy({
|
|||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
const data = result.data;
|
const data = result.data;
|
||||||
|
|
||||||
fasilitasKesehatanState.edit.id = data.id;
|
fasilitasKesehatan.edit.id = data.id;
|
||||||
fasilitasKesehatanState.edit.form = {
|
fasilitasKesehatan.edit.form = {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
informasiUmum: {
|
informasiUmum: {
|
||||||
fasilitas: data.informasiumum.fasilitas,
|
fasilitas: data.informasiumum.fasilitas,
|
||||||
@@ -205,7 +216,7 @@ const fasilitasKesehatanState = proxy({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
async submit() {
|
async submit() {
|
||||||
const cek = templateForm.safeParse(fasilitasKesehatanState.edit.form);
|
const cek = templateForm.safeParse(fasilitasKesehatan.edit.form);
|
||||||
if (!cek.success) {
|
if (!cek.success) {
|
||||||
const errMsg = cek.error.issues
|
const errMsg = cek.error.issues
|
||||||
.map((v) => `${v.path.join(".")}: ${v.message}`)
|
.map((v) => `${v.path.join(".")}: ${v.message}`)
|
||||||
@@ -215,42 +226,38 @@ const fasilitasKesehatanState = proxy({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fasilitasKesehatanState.edit.loading = true;
|
fasilitasKesehatan.edit.loading = true;
|
||||||
const payload = {
|
const payload = {
|
||||||
name: fasilitasKesehatanState.edit.form.name,
|
name: fasilitasKesehatan.edit.form.name,
|
||||||
informasiUmum: {
|
informasiUmum: {
|
||||||
fasilitas:
|
fasilitas: fasilitasKesehatan.edit.form.informasiUmum.fasilitas,
|
||||||
fasilitasKesehatanState.edit.form.informasiUmum.fasilitas,
|
alamat: fasilitasKesehatan.edit.form.informasiUmum.alamat,
|
||||||
alamat: fasilitasKesehatanState.edit.form.informasiUmum.alamat,
|
|
||||||
jamOperasional:
|
jamOperasional:
|
||||||
fasilitasKesehatanState.edit.form.informasiUmum.jamOperasional,
|
fasilitasKesehatan.edit.form.informasiUmum.jamOperasional,
|
||||||
},
|
},
|
||||||
layananUnggulan: {
|
layananUnggulan: {
|
||||||
content: fasilitasKesehatanState.edit.form.layananUnggulan.content,
|
content: fasilitasKesehatan.edit.form.layananUnggulan.content,
|
||||||
},
|
},
|
||||||
dokterdanTenagaMedis: {
|
dokterdanTenagaMedis: {
|
||||||
name: fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.name,
|
name: fasilitasKesehatan.edit.form.dokterdanTenagaMedis.name,
|
||||||
specialist:
|
specialist:
|
||||||
fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.specialist,
|
fasilitasKesehatan.edit.form.dokterdanTenagaMedis.specialist,
|
||||||
jadwal:
|
jadwal: fasilitasKesehatan.edit.form.dokterdanTenagaMedis.jadwal,
|
||||||
fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.jadwal,
|
|
||||||
},
|
},
|
||||||
fasilitasPendukung: {
|
fasilitasPendukung: {
|
||||||
content:
|
content: fasilitasKesehatan.edit.form.fasilitasPendukung.content,
|
||||||
fasilitasKesehatanState.edit.form.fasilitasPendukung.content,
|
|
||||||
},
|
},
|
||||||
prosedurPendaftaran: {
|
prosedurPendaftaran: {
|
||||||
content:
|
content: fasilitasKesehatan.edit.form.prosedurPendaftaran.content,
|
||||||
fasilitasKesehatanState.edit.form.prosedurPendaftaran.content,
|
|
||||||
},
|
},
|
||||||
tarifDanLayanan: {
|
tarifDanLayanan: {
|
||||||
layanan: fasilitasKesehatanState.edit.form.tarifDanLayanan.layanan,
|
layanan: fasilitasKesehatan.edit.form.tarifDanLayanan.layanan,
|
||||||
tarif: fasilitasKesehatanState.edit.form.tarifDanLayanan.tarif,
|
tarif: fasilitasKesehatan.edit.form.tarifDanLayanan.tarif,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`/api/kesehatan/fasilitas-kesehatan/${fasilitasKesehatanState.edit.id}`,
|
`/api/kesehatan/fasilitas-kesehatan/${fasilitasKesehatan.edit.id}`,
|
||||||
{
|
{
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@@ -264,7 +271,7 @@ const fasilitasKesehatanState = proxy({
|
|||||||
}
|
}
|
||||||
|
|
||||||
toast.success("Berhasil update fasilitas kesehatan");
|
toast.success("Berhasil update fasilitas kesehatan");
|
||||||
await fasilitasKesehatanState.findMany.load();
|
await fasilitasKesehatan.findMany.load();
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error(
|
toast.error(
|
||||||
@@ -272,37 +279,297 @@ const fasilitasKesehatanState = proxy({
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
fasilitasKesehatanState.edit.loading = false;
|
fasilitasKesehatan.edit.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetForm() {
|
resetForm() {
|
||||||
fasilitasKesehatanState.edit.id = "";
|
fasilitasKesehatan.edit.id = "";
|
||||||
fasilitasKesehatanState.edit.form = { ...defaultForm };
|
fasilitasKesehatan.edit.form = { ...defaultForm };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
loading: false,
|
loading: false,
|
||||||
async byId(id: string){
|
async byId(id: string) {
|
||||||
try {
|
try {
|
||||||
fasilitasKesehatanState.delete.loading = true;
|
fasilitasKesehatan.delete.loading = true;
|
||||||
const res = await fetch(`/api/kesehatan/fasilitas-kesehatan/del/${id}`, {
|
const res = await fetch(
|
||||||
method: "DELETE",
|
`/api/kesehatan/fasilitas-kesehatan/del/${id}`,
|
||||||
});
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
if (res.ok && result.success) {
|
if (res.ok && result.success) {
|
||||||
toast.success("Fasilitas kesehatan berhasil dihapus");
|
toast.success("Fasilitas kesehatan berhasil dihapus");
|
||||||
await fasilitasKesehatanState.findMany.load();
|
await fasilitasKesehatan.findMany.load();
|
||||||
} else {
|
} else {
|
||||||
toast.error(result.message || "Gagal menghapus");
|
toast.error(result.message || "Gagal menghapus");
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
toast.error("Terjadi kesalahan saat menghapus");
|
toast.error("Terjadi kesalahan saat menghapus");
|
||||||
} finally {
|
} 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;
|
export default fasilitasKesehatanState;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -5,20 +6,19 @@ import { proxy } from "valtio";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const templateGrafikKepuasan = z.object({
|
const templateGrafikKepuasan = z.object({
|
||||||
label: z.string().min(2, "Label harus diisi"),
|
nama: z.string().min(2, "Nama harus diisi"),
|
||||||
jumlah: z.string().min(1, "Jumlah 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<{
|
const defaultForm = {
|
||||||
select: {
|
nama: "",
|
||||||
label: true;
|
tanggal: "",
|
||||||
jumlah: true;
|
jenisKelamin: "",
|
||||||
};
|
alamat: "",
|
||||||
}>;
|
penyakit: "",
|
||||||
|
|
||||||
const defaultForm: GrafikKepuasan = {
|
|
||||||
label: "",
|
|
||||||
jumlah: ""
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const grafikkepuasan = proxy({
|
const grafikkepuasan = proxy({
|
||||||
@@ -36,16 +36,15 @@ const grafikkepuasan = proxy({
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
grafikkepuasan.create.loading = true;
|
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) {
|
if (res.status === 200) {
|
||||||
const id = res.data?.data?.id;
|
const id = res.data?.data;
|
||||||
if (id) {
|
if (id) {
|
||||||
toast.success("Success create");
|
toast.success("Success create");
|
||||||
grafikkepuasan.create.form = {
|
grafikkepuasan.create.form = { ...defaultForm };
|
||||||
label: "",
|
|
||||||
jumlah: "",
|
|
||||||
};
|
|
||||||
grafikkepuasan.findMany.load();
|
grafikkepuasan.findMany.load();
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -62,21 +61,49 @@ const grafikkepuasan = proxy({
|
|||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.GrafikKepuasanGetPayload<{ omit: { isActive: true } }>[]
|
| Prisma.GrafikKepuasanGetPayload<{
|
||||||
|
omit: {
|
||||||
|
isActive: true;
|
||||||
|
};
|
||||||
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.kesehatan.grafikkepuasan[
|
totalPages: 1,
|
||||||
"find-many"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
grafikkepuasan.findMany.data = res.data?.data ?? [];
|
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: {
|
findUnique: {
|
||||||
data: null as Prisma.GrafikKepuasanGetPayload<{
|
data: null as Prisma.GrafikKepuasanGetPayload<{
|
||||||
omit: { isActive: true }
|
omit: { isActive: true };
|
||||||
}> | null,
|
}> | null,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/kesehatan/grafikkepuasan/${id}`);
|
const res = await fetch(`/api/kesehatan/grafikkepuasan/${id}`);
|
||||||
@@ -95,88 +122,137 @@ const grafikkepuasan = proxy({
|
|||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
id: "",
|
id: "",
|
||||||
form: {...defaultForm},
|
form: { ...defaultForm },
|
||||||
loading: false,
|
loading: false,
|
||||||
async byId() {
|
async load(id: string) {
|
||||||
},
|
if (!id) {
|
||||||
async submit() {
|
toast.warn("ID tidak valid");
|
||||||
const id = this.id;
|
return null;
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
if (!response.ok) {
|
||||||
await grafikkepuasan.findMany.load();
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
return result.data;
|
const result = await response.json();
|
||||||
} 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 response = await fetch(`/api/kesehatan/grafikkepuasan/del/${id}`, {
|
if (result?.success) {
|
||||||
method: "DELETE",
|
const data = result.data;
|
||||||
headers: {
|
this.id = data.id;
|
||||||
"Content-Type": "application/json",
|
this.form = {
|
||||||
},
|
nama: data.nama,
|
||||||
});
|
tanggal: data.tanggal,
|
||||||
|
jenisKelamin: data.jenisKelamin,
|
||||||
const result = await response.json();
|
alamat: data.alamat,
|
||||||
|
penyakit: data.penyakit,
|
||||||
if (response.ok && result?.success) {
|
};
|
||||||
toast.success(
|
return data; // Return the loaded data
|
||||||
result.message || "Grafik kepuasan berhasil dihapus"
|
} else {
|
||||||
);
|
throw new Error(result?.message || "Gagal memuat data");
|
||||||
await grafikkepuasan.findMany.load(); // refresh list
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
|
console.error("Error loading grafik kepuasan:", error);
|
||||||
toast.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);
|
async submit() {
|
||||||
toast.error("Terjadi kesalahan saat menghapus grafik kepuasan");
|
const id = this.id;
|
||||||
} finally {
|
if (!id) {
|
||||||
grafikkepuasan.delete.loading = false;
|
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;
|
export default grafikkepuasan;
|
||||||
|
|||||||
@@ -120,27 +120,36 @@ const jadwalkegiatanState = proxy({
|
|||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
jadwalkegiatanState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
jadwalkegiatanState.findMany.page = page;
|
||||||
|
jadwalkegiatanState.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.loading = true;
|
const query: any = { page, limit };
|
||||||
const res = await (ApiFetch.api.kesehatan as any)[
|
if (search) query.search = search;
|
||||||
"jadwal-kegiatan"
|
|
||||||
]["find-many"].get();
|
|
||||||
|
|
||||||
if (res.status === 200) {
|
const res = await ApiFetch.api.kesehatan["jadwal-kegiatan"][
|
||||||
this.data = res.data?.data ?? [];
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
jadwalkegiatanState.findMany.data = res.data.data ?? [];
|
||||||
|
jadwalkegiatanState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
} else {
|
} else {
|
||||||
toast.error("Gagal memuat data jadwal kegiatan");
|
jadwalkegiatanState.findMany.data = [];
|
||||||
|
jadwalkegiatanState.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error("Terjadi error saat load data");
|
console.error("Gagal fetch jadwal kegiatan paginated:", err);
|
||||||
console.error("LOAD ERROR:", err);
|
jadwalkegiatanState.findMany.data = [];
|
||||||
throw err;
|
jadwalkegiatanState.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
jadwalkegiatanState.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -227,29 +236,42 @@ const jadwalkegiatanState = proxy({
|
|||||||
content: jadwalkegiatanState.edit.form.content,
|
content: jadwalkegiatanState.edit.form.content,
|
||||||
informasiJadwalKegiatan: {
|
informasiJadwalKegiatan: {
|
||||||
name: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.name,
|
name: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.name,
|
||||||
tanggal: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.tanggal,
|
tanggal:
|
||||||
|
jadwalkegiatanState.edit.form.informasiJadwalKegiatan.tanggal,
|
||||||
waktu: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.waktu,
|
waktu: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.waktu,
|
||||||
lokasi: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.lokasi,
|
lokasi:
|
||||||
|
jadwalkegiatanState.edit.form.informasiJadwalKegiatan.lokasi,
|
||||||
},
|
},
|
||||||
layananJadwalKegiatan: {
|
layananJadwalKegiatan: {
|
||||||
content: jadwalkegiatanState.edit.form.layananJadwalKegiatan.content,
|
content:
|
||||||
|
jadwalkegiatanState.edit.form.layananJadwalKegiatan.content,
|
||||||
},
|
},
|
||||||
deskripsiJadwalKegiatan: {
|
deskripsiJadwalKegiatan: {
|
||||||
deskripsi: jadwalkegiatanState.edit.form.deskripsiJadwalKegiatan.deskripsi,
|
deskripsi:
|
||||||
|
jadwalkegiatanState.edit.form.deskripsiJadwalKegiatan.deskripsi,
|
||||||
},
|
},
|
||||||
syaratKetentuanJadwalKegiatan: {
|
syaratKetentuanJadwalKegiatan: {
|
||||||
content: jadwalkegiatanState.edit.form.syaratKetentuanJadwalKegiatan.content,
|
content:
|
||||||
|
jadwalkegiatanState.edit.form.syaratKetentuanJadwalKegiatan
|
||||||
|
.content,
|
||||||
},
|
},
|
||||||
dokumenJadwalKegiatan: {
|
dokumenJadwalKegiatan: {
|
||||||
content: jadwalkegiatanState.edit.form.dokumenJadwalKegiatan.content,
|
content:
|
||||||
|
jadwalkegiatanState.edit.form.dokumenJadwalKegiatan.content,
|
||||||
},
|
},
|
||||||
pendaftaranJadwalKegiatan: {
|
pendaftaranJadwalKegiatan: {
|
||||||
name: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.name,
|
name: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.name,
|
||||||
tanggal: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.tanggal,
|
tanggal:
|
||||||
namaOrangtua: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.namaOrangtua,
|
jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.tanggal,
|
||||||
nomor: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.nomor,
|
namaOrangtua:
|
||||||
alamat: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.alamat,
|
jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan
|
||||||
catatan: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.catatan,
|
.namaOrangtua,
|
||||||
|
nomor:
|
||||||
|
jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.nomor,
|
||||||
|
alamat:
|
||||||
|
jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.alamat,
|
||||||
|
catatan:
|
||||||
|
jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.catatan,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -286,7 +308,7 @@ const jadwalkegiatanState = proxy({
|
|||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
loading: false,
|
loading: false,
|
||||||
async byId(id: string){
|
async byId(id: string) {
|
||||||
try {
|
try {
|
||||||
jadwalkegiatanState.delete.loading = true;
|
jadwalkegiatanState.delete.loading = true;
|
||||||
const res = await fetch(`/api/kesehatan/jadwal-kegiatan/del/${id}`, {
|
const res = await fetch(`/api/kesehatan/jadwal-kegiatan/del/${id}`, {
|
||||||
@@ -305,7 +327,7 @@ const jadwalkegiatanState = proxy({
|
|||||||
} finally {
|
} finally {
|
||||||
jadwalkegiatanState.delete.loading = false;
|
jadwalkegiatanState.delete.loading = false;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -20,17 +21,41 @@ const defaultForm = {
|
|||||||
|
|
||||||
const infoWabahPenyakit = proxy({
|
const infoWabahPenyakit = proxy({
|
||||||
findMany: {
|
findMany: {
|
||||||
data: [] as Prisma.InfoWabahPenyakitGetPayload<{
|
data: null as
|
||||||
include: {
|
| Prisma.InfoWabahPenyakitGetPayload<{
|
||||||
image: true;
|
include: {
|
||||||
};
|
image: true;
|
||||||
}>[],
|
};
|
||||||
async load() {
|
}>[]
|
||||||
const res = await ApiFetch.api.kesehatan.infowabahpenyakit[
|
| null,
|
||||||
"find-many"
|
page: 1,
|
||||||
].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
loading: false,
|
||||||
infoWabahPenyakit.findMany.data = res.data?.data ?? [];
|
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 ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -5,204 +6,241 @@ import { proxy } from "valtio";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const templateForm = z.object({
|
const templateForm = z.object({
|
||||||
name: z.string().min(3, "Judul minimal 3 karakter"),
|
name: z.string().min(3, "Judul minimal 3 karakter"),
|
||||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||||
imageId: z.string().nonempty(),
|
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 };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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 ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -17,21 +18,45 @@ const defaultForm = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const penangananDarurat = proxy({
|
const penangananDarurat = proxy({
|
||||||
findMany: {
|
findMany: {
|
||||||
data: [] as Prisma.PenangananDaruratGetPayload<{
|
data: null as
|
||||||
include: {
|
| Prisma.PenangananDaruratGetPayload<{
|
||||||
image: true;
|
include: {
|
||||||
};
|
image: true;
|
||||||
}>[],
|
};
|
||||||
async load() {
|
}>[]
|
||||||
const res = await ApiFetch.api.kesehatan.penanganandarurat[
|
| null,
|
||||||
"find-many"
|
page: 1,
|
||||||
].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
loading: false,
|
||||||
penangananDarurat.findMany.data = res.data?.data ?? [];
|
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:{
|
create:{
|
||||||
form: {...defaultForm},
|
form: {...defaultForm},
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -20,17 +21,43 @@ const defaultForm = {
|
|||||||
|
|
||||||
const programKesehatan = proxy({
|
const programKesehatan = proxy({
|
||||||
findMany: {
|
findMany: {
|
||||||
data: [] as Prisma.ProgramKesehatanGetPayload<{
|
data: null as
|
||||||
include: {
|
| Prisma.ProgramKesehatanGetPayload<{
|
||||||
image: true;
|
include: {
|
||||||
};
|
image: true;
|
||||||
}>[],
|
};
|
||||||
async load() {
|
}>[]
|
||||||
const res = await ApiFetch.api.kesehatan.programkesehatan[
|
| null,
|
||||||
"find-many"
|
page: 1,
|
||||||
].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
loading: false,
|
||||||
programKesehatan.findMany.data = res.data?.data ?? [];
|
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 {
|
try {
|
||||||
programKesehatan.delete.loading = true;
|
programKesehatan.delete.loading = true;
|
||||||
|
|
||||||
const response = await fetch(`/api/kesehatan/programkesehatan/del/${id}`, {
|
const response = await fetch(
|
||||||
method: "DELETE",
|
`/api/kesehatan/programkesehatan/del/${id}`,
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
method: "DELETE",
|
||||||
},
|
headers: {
|
||||||
});
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (response.ok && result?.success) {
|
if (response.ok && result?.success) {
|
||||||
@@ -156,57 +186,70 @@ const programKesehatan = proxy({
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching program kesehatan:", 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;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async update() {
|
async update() {
|
||||||
const cek = templateForm.safeParse(programKesehatan.edit.form);
|
const cek = templateForm.safeParse(programKesehatan.edit.form);
|
||||||
if (!cek.success) {
|
if (!cek.success) {
|
||||||
const err = `[${cek.error.issues
|
const err = `[${cek.error.issues
|
||||||
.map((v) => `${v.path.join(".")}`)
|
.map((v) => `${v.path.join(".")}`)
|
||||||
.join("\n")}] required`;
|
.join("\n")}] required`;
|
||||||
return toast.error(err);
|
return toast.error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
programKesehatan.edit.loading = true;
|
programKesehatan.edit.loading = true;
|
||||||
const response = await fetch(`/api/kesehatan/programkesehatan/${this.id}`, {
|
const response = await fetch(
|
||||||
method: "PUT",
|
`/api/kesehatan/programkesehatan/${this.id}`,
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
method: "PUT",
|
||||||
},
|
headers: {
|
||||||
body: JSON.stringify({
|
"Content-Type": "application/json",
|
||||||
name: this.form.name,
|
},
|
||||||
deskripsiSingkat: this.form.deskripsiSingkat,
|
body: JSON.stringify({
|
||||||
deskripsi: this.form.deskripsi,
|
name: this.form.name,
|
||||||
imageId: this.form.imageId,
|
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}`);
|
);
|
||||||
}
|
if (!response.ok) {
|
||||||
const result = await response.json();
|
const errorData = await response.json().catch(() => ({}));
|
||||||
if (result.success) {
|
throw new Error(
|
||||||
toast.success(result.message || "Program kesehatan berhasil diupdate");
|
errorData.message || `HTTP error! status: ${response.status}`
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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() {
|
reset() {
|
||||||
programKesehatan.edit.id = "";
|
programKesehatan.edit.id = "";
|
||||||
programKesehatan.edit.form = { ...defaultForm };
|
programKesehatan.edit.form = { ...defaultForm };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -163,13 +164,43 @@ const puskesmasState = proxy({
|
|||||||
},
|
},
|
||||||
|
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as Prisma.PuskesmasGetPayload<{
|
data: null as
|
||||||
include: { image: true; jam: true; kontak: true };
|
| Prisma.PuskesmasGetPayload<{
|
||||||
}>[] | null,
|
include: {
|
||||||
async load() {
|
image: true;
|
||||||
const res = await ApiFetch.api.kesehatan.puskesmas["find-many"].get();
|
jam: true;
|
||||||
if (res.status === 200) {
|
kontak: true;
|
||||||
puskesmasState.findMany.data = res.data?.data ?? [];
|
};
|
||||||
|
}>[]
|
||||||
|
| 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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -50,18 +51,50 @@ const apbdes = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as Array<
|
data: null as
|
||||||
Prisma.APBDesGetPayload<{
|
| Prisma.APBDesGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
image: true;
|
image: true;
|
||||||
file: true;
|
file: true;
|
||||||
};
|
};
|
||||||
}>
|
}>[]
|
||||||
> | null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.landingpage.apbdes["find-many"].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
total: 0,
|
||||||
apbdes.findMany.data = res.data?.data ?? [];
|
loading: false,
|
||||||
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||||
|
apbdes.findMany.loading = true; // Use the full path to access the property
|
||||||
|
apbdes.findMany.page = page;
|
||||||
|
apbdes.findMany.search = search;
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.landingpage.apbdes[
|
||||||
|
"findMany"
|
||||||
|
].get({
|
||||||
|
query
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
apbdes.findMany.data = res.data.data || [];
|
||||||
|
apbdes.findMany.total = res.data.total || 0;
|
||||||
|
apbdes.findMany.totalPages = res.data.totalPages || 1;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to load pegawai:", res.data?.message);
|
||||||
|
apbdes.findMany.data = [];
|
||||||
|
apbdes.findMany.total = 0;
|
||||||
|
apbdes.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading pegawai:", error);
|
||||||
|
apbdes.findMany.data = [];
|
||||||
|
apbdes.findMany.total = 0;
|
||||||
|
apbdes.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
apbdes.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -60,16 +60,22 @@ const desaAntikorupsi = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
search: "",
|
||||||
desaAntikorupsi.findMany.loading = true; // Use the full path to access the property
|
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.page = page;
|
||||||
|
desaAntikorupsi.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res = await ApiFetch.api.landingpage.desaantikorupsi[
|
const res = await ApiFetch.api.landingpage.desaantikorupsi[
|
||||||
"findMany"
|
"findMany"
|
||||||
].get({
|
].get({
|
||||||
query: { page, limit },
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
desaAntikorupsi.findMany.data = res.data.data || [];
|
desaAntikorupsi.findMany.data = res.data.data || [];
|
||||||
desaAntikorupsi.findMany.total = res.data.total || 0;
|
desaAntikorupsi.findMany.total = res.data.total || 0;
|
||||||
@@ -305,20 +311,25 @@ const kategoriDesaAntiKorupsi = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
search: "",
|
||||||
kategoriDesaAntiKorupsi.findMany.loading = true; // Use the full path to access the property
|
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.page = page;
|
||||||
|
kategoriDesaAntiKorupsi.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
const res = await ApiFetch.api.landingpage.kategoridak[
|
const query: any = { page, limit };
|
||||||
"findMany"
|
if (search) query.search = search;
|
||||||
].get({
|
|
||||||
query: { page, limit },
|
const res = await ApiFetch.api.landingpage.kategoridak["findMany"].get({
|
||||||
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
kategoriDesaAntiKorupsi.findMany.data = res.data.data || [];
|
kategoriDesaAntiKorupsi.findMany.data = res.data.data || [];
|
||||||
kategoriDesaAntiKorupsi.findMany.total = res.data.total || 0;
|
kategoriDesaAntiKorupsi.findMany.total = res.data.total || 0;
|
||||||
kategoriDesaAntiKorupsi.findMany.totalPages = res.data.totalPages || 1;
|
kategoriDesaAntiKorupsi.findMany.totalPages =
|
||||||
|
res.data.totalPages || 1;
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to load media sosial:", res.data?.message);
|
console.error("Failed to load media sosial:", res.data?.message);
|
||||||
kategoriDesaAntiKorupsi.findMany.data = [];
|
kategoriDesaAntiKorupsi.findMany.data = [];
|
||||||
@@ -363,27 +374,30 @@ const kategoriDesaAntiKorupsi = proxy({
|
|||||||
try {
|
try {
|
||||||
kategoriDesaAntiKorupsi.delete.loading = true;
|
kategoriDesaAntiKorupsi.delete.loading = true;
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(`/api/landingpage/kategoridak/del/${id}`, {
|
||||||
`/api/landingpage/kategoridak/del/${id}`,
|
method: "DELETE",
|
||||||
{
|
headers: {
|
||||||
method: "DELETE",
|
"Content-Type": "application/json",
|
||||||
headers: {
|
},
|
||||||
"Content-Type": "application/json",
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (response.ok && result?.success) {
|
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
|
await kategoriDesaAntiKorupsi.findMany.load(); // refresh list
|
||||||
} else {
|
} else {
|
||||||
toast.error(result?.message || "Gagal menghapus kategori desa anti korupsi");
|
toast.error(
|
||||||
|
result?.message || "Gagal menghapus kategori desa anti korupsi"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Gagal delete:", 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 {
|
} finally {
|
||||||
kategoriDesaAntiKorupsi.delete.loading = false;
|
kategoriDesaAntiKorupsi.delete.loading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,7 +181,13 @@ const responden = proxy({
|
|||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(this.form),
|
body: JSON.stringify({
|
||||||
|
name: this.form.name,
|
||||||
|
tanggal: this.form.tanggal,
|
||||||
|
jenisKelaminId: this.form.jenisKelaminId,
|
||||||
|
ratingId: this.form.ratingId,
|
||||||
|
kelompokUmurId: this.form.kelompokUmurId,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (!response.ok || !result?.success) {
|
if (!response.ok || !result?.success) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -58,16 +59,43 @@ const prestasiDesa = proxy({
|
|||||||
Prisma.PrestasiDesaGetPayload<{
|
Prisma.PrestasiDesaGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
image: true;
|
image: true;
|
||||||
kategori: true;
|
kategori: {
|
||||||
|
select: {
|
||||||
|
id: true;
|
||||||
|
name: true;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}>
|
}>
|
||||||
> | null,
|
> | null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.landingpage.prestasidesa[
|
totalPages: 1,
|
||||||
"find-many"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
prestasiDesa.findMany.data = res.data?.data ?? [];
|
prestasiDesa.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
prestasiDesa.findMany.page = page;
|
||||||
|
prestasiDesa.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.landingpage.prestasidesa["find-many"].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
prestasiDesa.findMany.data = res.data.data ?? [];
|
||||||
|
prestasiDesa.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
prestasiDesa.findMany.data = [];
|
||||||
|
prestasiDesa.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch prestasi desa paginated:", err);
|
||||||
|
prestasiDesa.findMany.data = [];
|
||||||
|
prestasiDesa.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
prestasiDesa.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -283,12 +311,34 @@ const kategoriPrestasi = proxy({
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
}> | null,
|
}> | null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.landingpage.kategoriprestasi[
|
totalPages: 1,
|
||||||
"find-many"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
kategoriPrestasi.findMany.data = res.data?.data ?? [];
|
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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,7 +23,12 @@ type ProgramInovasiForm = Prisma.ProgramInovasiGetPayload<{
|
|||||||
|
|
||||||
const programInovasi = proxy({
|
const programInovasi = proxy({
|
||||||
create: {
|
create: {
|
||||||
form: {} as ProgramInovasiForm,
|
form: {
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
imageId: "",
|
||||||
|
link: ""
|
||||||
|
} as ProgramInovasiForm,
|
||||||
loading: false,
|
loading: false,
|
||||||
async create() {
|
async create() {
|
||||||
// Ensure all required fields are non-null
|
// Ensure all required fields are non-null
|
||||||
@@ -65,14 +70,19 @@ const programInovasi = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||||
programInovasi.findMany.loading = true; // Use the full path to access the property
|
programInovasi.findMany.loading = true; // Use the full path to access the property
|
||||||
programInovasi.findMany.page = page;
|
programInovasi.findMany.page = page;
|
||||||
|
programInovasi.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res = await ApiFetch.api.landingpage.programinovasi[
|
const res = await ApiFetch.api.landingpage.programinovasi[
|
||||||
"findMany"
|
"findMany"
|
||||||
].get({
|
].get({
|
||||||
query: { page, limit },
|
query
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
@@ -482,14 +492,19 @@ const mediaSosial = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||||
mediaSosial.findMany.loading = true; // Use the full path to access the property
|
mediaSosial.findMany.loading = true; // Use the full path to access the property
|
||||||
mediaSosial.findMany.page = page;
|
mediaSosial.findMany.page = page;
|
||||||
try {
|
mediaSosial.findMany.search = search;
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res = await ApiFetch.api.landingpage.mediasosial[
|
const res = await ApiFetch.api.landingpage.mediasosial[
|
||||||
"findMany"
|
"findMany"
|
||||||
].get({
|
].get({
|
||||||
query: { page, limit },
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
|||||||
@@ -58,16 +58,21 @@ const sdgsDesa = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||||
sdgsDesa.findMany.loading = true; // Use the full path to access the property
|
sdgsDesa.findMany.loading = true; // Use the full path to access the property
|
||||||
sdgsDesa.findMany.page = page;
|
sdgsDesa.findMany.page = page;
|
||||||
|
sdgsDesa.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res = await ApiFetch.api.landingpage.sdgsdesa[
|
const res = await ApiFetch.api.landingpage.sdgsdesa[
|
||||||
"findMany"
|
"findMany"
|
||||||
].get({
|
].get({
|
||||||
query: { page, limit },
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
sdgsDesa.findMany.data = res.data.data || [];
|
sdgsDesa.findMany.data = res.data.data || [];
|
||||||
sdgsDesa.findMany.total = res.data.total || 0;
|
sdgsDesa.findMany.total = res.data.total || 0;
|
||||||
@@ -89,7 +94,7 @@ const sdgsDesa = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.SDGSDesaGetPayload<{
|
data: null as Prisma.SdgsDesaGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
image: true;
|
image: true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -56,13 +56,17 @@ const dataLingkunganDesaState = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => {
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
// Change to arrow function
|
// Change to arrow function
|
||||||
dataLingkunganDesaState.findMany.loading = true; // Use the full path to access the property
|
dataLingkunganDesaState.findMany.loading = true; // Use the full path to access the property
|
||||||
dataLingkunganDesaState.findMany.page = page;
|
dataLingkunganDesaState.findMany.page = page;
|
||||||
|
dataLingkunganDesaState.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
const res = await ApiFetch.api.lingkungan.datalingkungandesa["find-many"].get({
|
const res = await ApiFetch.api.lingkungan.datalingkungandesa["find-many"].get({
|
||||||
query: { page, limit },
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
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 ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -67,10 +68,46 @@ const kegiatanDesa = proxy({
|
|||||||
};
|
};
|
||||||
}>
|
}>
|
||||||
> | null,
|
> | null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.lingkungan.kegiatandesa["find-many"].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
total: 0,
|
||||||
kegiatanDesa.findMany.data = res.data?.data ?? [];
|
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 };
|
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 ========================================= //
|
// ========================================= KATEGORI kegiatan ========================================= //
|
||||||
@@ -269,9 +335,7 @@ const kategoriKegiatan = proxy({
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
kategoriKegiatan.create.loading = true;
|
kategoriKegiatan.create.loading = true;
|
||||||
const res = await ApiFetch.api.lingkungan.kategorikegiatan[
|
const res = await ApiFetch.api.lingkungan.kategorikegiatan["create"].post(kategoriKegiatan.create.form);
|
||||||
"create"
|
|
||||||
].post(kategoriKegiatan.create.form);
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
kategoriKegiatan.findMany.load();
|
kategoriKegiatan.findMany.load();
|
||||||
return toast.success("Data berhasil ditambahkan");
|
return toast.success("Data berhasil ditambahkan");
|
||||||
@@ -305,9 +369,7 @@ const kategoriKegiatan = proxy({
|
|||||||
}> | null,
|
}> | null,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(`/api/lingkungan/kategorikegiatan/${id}`);
|
||||||
`/api/lingkungan/kategorikegiatan/${id}`
|
|
||||||
);
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
kategoriKegiatan.findUnique.data = data.data ?? null;
|
kategoriKegiatan.findUnique.data = data.data ?? null;
|
||||||
@@ -367,15 +429,12 @@ const kategoriKegiatan = proxy({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(`/api/lingkungan/kategorikegiatan/${id}`, {
|
||||||
`/api/lingkungan/kategorikegiatan/${id}`,
|
method: "GET",
|
||||||
{
|
headers: {
|
||||||
method: "GET",
|
"Content-Type": "application/json",
|
||||||
headers: {
|
},
|
||||||
"Content-Type": "application/json",
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,15 +52,19 @@ const pengelolaanSampah = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => {
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
// Change to arrow function
|
// Change to arrow function
|
||||||
pengelolaanSampah.findMany.loading = true; // Use the full path to access the property
|
pengelolaanSampah.findMany.loading = true; // Use the full path to access the property
|
||||||
pengelolaanSampah.findMany.page = page;
|
pengelolaanSampah.findMany.page = page;
|
||||||
|
pengelolaanSampah.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
const res = await ApiFetch.api.lingkungan.pengelolaansampah[
|
const res = await ApiFetch.api.lingkungan.pengelolaansampah[
|
||||||
"find-many"
|
"find-many"
|
||||||
].get({
|
].get({
|
||||||
query: { page, limit },
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
@@ -265,7 +269,7 @@ const keteranganSampah = proxy({
|
|||||||
try {
|
try {
|
||||||
keteranganSampah.create.loading = true;
|
keteranganSampah.create.loading = true;
|
||||||
const res =
|
const res =
|
||||||
await ApiFetch.api.lingkungan.pengelolaansampah.keteranganbankterdekat[
|
await ApiFetch.api.lingkungan.keteranganbankterdekat[
|
||||||
"create"
|
"create"
|
||||||
].post(keteranganSampah.create.form);
|
].post(keteranganSampah.create.form);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
@@ -287,14 +291,47 @@ const keteranganSampah = proxy({
|
|||||||
omit: { isActive: true };
|
omit: { isActive: true };
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.lingkungan.pengelolaansampah.keteranganbankterdekat[
|
totalPages: 1,
|
||||||
"find-many"
|
total: 0,
|
||||||
].get();
|
loading: false,
|
||||||
if (res.status === 200) {
|
search: "",
|
||||||
keteranganSampah.findMany.data = res.data?.data ?? [];
|
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: {
|
findUnique: {
|
||||||
data: null as Prisma.KeteranganBankSampahTerdekatGetPayload<{
|
data: null as Prisma.KeteranganBankSampahTerdekatGetPayload<{
|
||||||
@@ -302,7 +339,7 @@ const keteranganSampah = proxy({
|
|||||||
}> | null,
|
}> | null,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/lingkungan/pengelolaansampah/keteranganbankterdekat/${id}`);
|
const res = await fetch(`/api/lingkungan/keteranganbankterdekat/${id}`);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
keteranganSampah.findUnique.data = data.data ?? null;
|
keteranganSampah.findUnique.data = data.data ?? null;
|
||||||
@@ -324,7 +361,7 @@ const keteranganSampah = proxy({
|
|||||||
try {
|
try {
|
||||||
keteranganSampah.delete.loading = true;
|
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",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -359,7 +396,7 @@ const keteranganSampah = proxy({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/lingkungan/pengelolaansampah/keteranganbankterdekat/${id}`, {
|
const response = await fetch(`/api/lingkungan/keteranganbankterdekat/${id}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -404,7 +441,7 @@ const keteranganSampah = proxy({
|
|||||||
try {
|
try {
|
||||||
keteranganSampah.edit.loading = true;
|
keteranganSampah.edit.loading = true;
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/lingkungan/pengelolaansampah/keteranganbankterdekat/${this.id}`,
|
`/api/lingkungan/keteranganbankterdekat/${this.id}`,
|
||||||
{
|
{
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -56,13 +56,17 @@ const programPenghijauanState = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => {
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
// Change to arrow function
|
// Change to arrow function
|
||||||
programPenghijauanState.findMany.loading = true; // Use the full path to access the property
|
programPenghijauanState.findMany.loading = true; // Use the full path to access the property
|
||||||
programPenghijauanState.findMany.page = page;
|
programPenghijauanState.findMany.page = page;
|
||||||
|
programPenghijauanState.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
const res = await ApiFetch.api.lingkungan.programpenghijauan["find-many"].get({
|
const res = await ApiFetch.api.lingkungan.programpenghijauan["find-many"].get({
|
||||||
query: { page, limit },
|
query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { proxy } from "valtio";
|
import { proxy } from "valtio";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// ========================================= BEASISWA PENDAFTAR ========================================= //
|
||||||
|
|
||||||
const templateBeasiswaPendaftar = z.object({
|
const templateBeasiswaPendaftar = z.object({
|
||||||
namaLengkap: z.string().min(1, "Nama harus diisi"),
|
namaLengkap: z.string().min(1, "Nama harus diisi"),
|
||||||
nik: z.string().min(1, "NIK harus diisi"),
|
nik: z.string().min(1, "NIK harus diisi"),
|
||||||
@@ -76,13 +79,34 @@ const beasiswaPendaftar = proxy({
|
|||||||
isActive: true;
|
isActive: true;
|
||||||
};
|
};
|
||||||
}>[],
|
}>[],
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
const res = await ApiFetch.api.pendidikan.beasiswa.beasiswapendaftar[
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
"findMany"
|
beasiswaPendaftar.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
].get();
|
beasiswaPendaftar.findMany.page = page;
|
||||||
if (res.status === 200) {
|
beasiswaPendaftar.findMany.search = search;
|
||||||
beasiswaPendaftar.findMany.data = res.data?.data ?? [];
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -275,8 +299,260 @@ const beasiswaPendaftar = proxy({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ========================================= 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({
|
const beasiswaDesaState = proxy({
|
||||||
beasiswaPendaftar,
|
beasiswaPendaftar,
|
||||||
|
keunggulanProgram
|
||||||
});
|
});
|
||||||
|
|
||||||
export default beasiswaDesaState;
|
export default beasiswaDesaState;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -51,13 +52,46 @@ const jenjangPendidikan = proxy({
|
|||||||
id: string;
|
id: string;
|
||||||
nama: string;
|
nama: string;
|
||||||
}> | null,
|
}> | null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res =
|
totalPages: 1,
|
||||||
await ApiFetch.api.pendidikan.infosekolahpaud.jenjangpendidikan[
|
total: 0,
|
||||||
"find-many"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
jenjangPendidikan.findMany.data = res.data?.data ?? [];
|
// Change to arrow function
|
||||||
|
jenjangPendidikan.findMany.loading = true; // Use the full path to access the property
|
||||||
|
jenjangPendidikan.findMany.page = page;
|
||||||
|
jenjangPendidikan.findMany.search = search;
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
const res =
|
||||||
|
await ApiFetch.api.pendidikan.infosekolahpaud.jenjangpendidikan[
|
||||||
|
"find-many"
|
||||||
|
].get({
|
||||||
|
query,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
jenjangPendidikan.findMany.data = res.data.data || [];
|
||||||
|
jenjangPendidikan.findMany.total = res.data.total || 0;
|
||||||
|
jenjangPendidikan.findMany.totalPages = res.data.totalPages || 1;
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Failed to load jenjang pendidikan:",
|
||||||
|
res.data?.message
|
||||||
|
);
|
||||||
|
jenjangPendidikan.findMany.data = [];
|
||||||
|
jenjangPendidikan.findMany.total = 0;
|
||||||
|
jenjangPendidikan.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading jenjang pendidikan:", error);
|
||||||
|
jenjangPendidikan.findMany.data = [];
|
||||||
|
jenjangPendidikan.findMany.total = 0;
|
||||||
|
jenjangPendidikan.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
jenjangPendidikan.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -299,18 +333,64 @@ const lembagaPendidikan = proxy({
|
|||||||
Prisma.LembagaGetPayload<{
|
Prisma.LembagaGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
jenjangPendidikan: true;
|
jenjangPendidikan: true;
|
||||||
siswa: true;
|
|
||||||
pengajar: true;
|
|
||||||
};
|
};
|
||||||
}>
|
}> & {
|
||||||
|
siswa?: [];
|
||||||
|
pengajar?: [];
|
||||||
|
}
|
||||||
> | null,
|
> | null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res =
|
totalPages: 1,
|
||||||
await ApiFetch.api.pendidikan.infosekolahpaud.lembagapendidikan[
|
total: 0,
|
||||||
"find-many"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "", jenjangPendidikan = "") => {
|
||||||
lembagaPendidikan.findMany.data = res.data?.data ?? [];
|
lembagaPendidikan.findMany.loading = true;
|
||||||
|
lembagaPendidikan.findMany.page = page;
|
||||||
|
lembagaPendidikan.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = {
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
...(search && { search }),
|
||||||
|
...(jenjangPendidikan && { jenjangPendidikanId: jenjangPendidikan })
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Fetching lembaga with query:', query);
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.pendidikan.infosekolahpaud.lembagapendidikan["find-many"].get({ query });
|
||||||
|
|
||||||
|
console.log('API Response:', res);
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
const data = Array.isArray(res.data.data) ? res.data.data : [];
|
||||||
|
const total = typeof res.data.total === 'number' ? res.data.total : 0;
|
||||||
|
const totalPages = typeof res.data.totalPages === 'number' ? res.data.totalPages : 1;
|
||||||
|
|
||||||
|
lembagaPendidikan.findMany.data = data;
|
||||||
|
lembagaPendidikan.findMany.total = total;
|
||||||
|
lembagaPendidikan.findMany.totalPages = totalPages;
|
||||||
|
|
||||||
|
console.log('Successfully loaded lembaga data:', {
|
||||||
|
count: data.length,
|
||||||
|
total,
|
||||||
|
totalPages
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Failed to load lembaga pendidikan:",
|
||||||
|
res.data?.message || 'No error message provided'
|
||||||
|
);
|
||||||
|
throw new Error(res.data?.message || 'Failed to load lembaga pendidikan');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading lembaga pendidikan:", error);
|
||||||
|
lembagaPendidikan.findMany.data = [];
|
||||||
|
lembagaPendidikan.findMany.total = 0;
|
||||||
|
lembagaPendidikan.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
lembagaPendidikan.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -554,16 +634,55 @@ const siswa = proxy({
|
|||||||
data: null as Array<
|
data: null as Array<
|
||||||
Prisma.SiswaGetPayload<{
|
Prisma.SiswaGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
lembaga: true;
|
lembaga: {
|
||||||
|
include: {
|
||||||
|
jenjangPendidikan: true;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}>
|
}>
|
||||||
> | null,
|
> | null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.pendidikan.infosekolahpaud.siswa[
|
totalPages: 1,
|
||||||
"find-many"
|
total: 0,
|
||||||
].get();
|
loading: false,
|
||||||
if (res.status === 200) {
|
search: "",
|
||||||
siswa.findMany.data = res.data?.data ?? [];
|
jenjangPendidikan: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "", jenjangPendidikan = "") => {
|
||||||
|
siswa.findMany.loading = true;
|
||||||
|
siswa.findMany.page = page;
|
||||||
|
siswa.findMany.search = search;
|
||||||
|
siswa.findMany.jenjangPendidikan = jenjangPendidikan;
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
if (jenjangPendidikan) query.jenjangPendidikanName = jenjangPendidikan;
|
||||||
|
const res = await ApiFetch.api.pendidikan.infosekolahpaud.siswa[
|
||||||
|
"find-many"
|
||||||
|
].get({
|
||||||
|
query,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
siswa.findMany.data = res.data.data || [];
|
||||||
|
siswa.findMany.total = res.data.total || 0;
|
||||||
|
siswa.findMany.totalPages = res.data.totalPages || 1;
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Failed to load siswa:",
|
||||||
|
res.data?.message
|
||||||
|
);
|
||||||
|
siswa.findMany.data = [];
|
||||||
|
siswa.findMany.total = 0;
|
||||||
|
siswa.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading siswa:", error);
|
||||||
|
siswa.findMany.data = [];
|
||||||
|
siswa.findMany.total = 0;
|
||||||
|
siswa.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
siswa.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -794,16 +913,56 @@ const pengajar = proxy({
|
|||||||
data: null as Array<
|
data: null as Array<
|
||||||
Prisma.PengajarGetPayload<{
|
Prisma.PengajarGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
lembaga: true;
|
lembaga: {
|
||||||
|
include: {
|
||||||
|
jenjangPendidikan: true
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}>
|
}>
|
||||||
> | null,
|
> | null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.pendidikan.infosekolahpaud.pengajar[
|
totalPages: 1,
|
||||||
"find-many"
|
total: 0,
|
||||||
].get();
|
loading: false,
|
||||||
if (res.status === 200) {
|
search: "",
|
||||||
pengajar.findMany.data = res.data?.data ?? [];
|
jenjangPendidikan: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "", jenjangPendidikan = "") => {
|
||||||
|
// Change to arrow function
|
||||||
|
pengajar.findMany.loading = true; // Use the full path to access the property
|
||||||
|
pengajar.findMany.page = page;
|
||||||
|
pengajar.findMany.search = search;
|
||||||
|
pengajar.findMany.jenjangPendidikan = jenjangPendidikan;
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
if (jenjangPendidikan) query.jenjangPendidikanId = jenjangPendidikan;
|
||||||
|
const res = await ApiFetch.api.pendidikan.infosekolahpaud.pengajar[
|
||||||
|
"find-many"
|
||||||
|
].get({
|
||||||
|
query,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
pengajar.findMany.data = res.data.data || [];
|
||||||
|
pengajar.findMany.total = res.data.total || 0;
|
||||||
|
pengajar.findMany.totalPages = res.data.totalPages || 1;
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Failed to load pengajar:",
|
||||||
|
res.data?.message
|
||||||
|
);
|
||||||
|
pengajar.findMany.data = [];
|
||||||
|
pengajar.findMany.total = 0;
|
||||||
|
pengajar.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading pengajar:", error);
|
||||||
|
pengajar.findMany.data = [];
|
||||||
|
pengajar.findMany.total = 0;
|
||||||
|
pengajar.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
pengajar.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -815,7 +974,9 @@ const pengajar = proxy({
|
|||||||
}> | null,
|
}> | null,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/pendidikan/infosekolahpaud/pengajar/${id}`);
|
const res = await fetch(
|
||||||
|
`/api/pendidikan/infosekolahpaud/pengajar/${id}`
|
||||||
|
);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
pengajar.findUnique.data = data.data ?? null;
|
pengajar.findUnique.data = data.data ?? null;
|
||||||
@@ -948,7 +1109,8 @@ const pengajar = proxy({
|
|||||||
result
|
result
|
||||||
);
|
);
|
||||||
throw new Error(
|
throw new Error(
|
||||||
result?.message || `Gagal mengupdate pengajar (${response.status})`
|
result?.message ||
|
||||||
|
`Gagal mengupdate pengajar (${response.status})`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -54,23 +55,46 @@ const dataPerpustakaan = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: [] as Prisma.DataPerpustakaanGetPayload<{
|
data: null as
|
||||||
include: {
|
| Prisma.DataPerpustakaanGetPayload<{
|
||||||
kategori: true;
|
include: {
|
||||||
image: true;
|
image: true;
|
||||||
};
|
kategori: true;
|
||||||
}>[],
|
};
|
||||||
loading: false,
|
}>[]
|
||||||
async load() {
|
| null,
|
||||||
const res =
|
page: 1,
|
||||||
await ApiFetch.api.pendidikan.perpustakaandigital.dataperpustakaan[
|
totalPages: 1,
|
||||||
"findMany"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||||
dataPerpustakaan.findMany.data = res.data?.data ?? [];
|
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: {
|
findUnique: {
|
||||||
data: null as Prisma.DataPerpustakaanGetPayload<{
|
data: null as Prisma.DataPerpustakaanGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
@@ -293,14 +317,34 @@ const kategoriBuku = proxy({
|
|||||||
isActive: true;
|
isActive: true;
|
||||||
};
|
};
|
||||||
}>[],
|
}>[],
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
const res =
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
await ApiFetch.api.pendidikan.perpustakaandigital.kategoribuku[
|
kategoriBuku.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
"findMany"
|
kategoriBuku.findMany.page = page;
|
||||||
].get();
|
kategoriBuku.findMany.search = search;
|
||||||
if (res.status === 200) {
|
|
||||||
kategoriBuku.findMany.data = res.data?.data ?? [];
|
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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -49,35 +49,38 @@ const daftarInformasiPublik = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as any[] | null,
|
data: null as
|
||||||
|
| Prisma.DaftarInformasiPublikGetPayload<{
|
||||||
|
omit: {
|
||||||
|
isActive: true;
|
||||||
|
};
|
||||||
|
}>[]
|
||||||
|
| null,
|
||||||
page: 1,
|
page: 1,
|
||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
search: "",
|
||||||
daftarInformasiPublik.findMany.loading = true; // Use the full path to access the property
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
daftarInformasiPublik.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
daftarInformasiPublik.findMany.page = page;
|
daftarInformasiPublik.findMany.page = page;
|
||||||
try {
|
daftarInformasiPublik.findMany.search = search;
|
||||||
const res = await ApiFetch.api.ppid.daftarinformasipublik[
|
|
||||||
"find-many"
|
|
||||||
].get({
|
|
||||||
query: { page, limit },
|
|
||||||
});
|
|
||||||
|
|
||||||
|
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) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
daftarInformasiPublik.findMany.data = res.data.data || [];
|
daftarInformasiPublik.findMany.data = res.data.data ?? [];
|
||||||
daftarInformasiPublik.findMany.total = res.data.total || 0;
|
daftarInformasiPublik.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
daftarInformasiPublik.findMany.totalPages = res.data.totalPages || 1;
|
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to load daftar informasi publik:", res.data?.message);
|
|
||||||
daftarInformasiPublik.findMany.data = [];
|
daftarInformasiPublik.findMany.data = [];
|
||||||
daftarInformasiPublik.findMany.total = 0;
|
|
||||||
daftarInformasiPublik.findMany.totalPages = 1;
|
daftarInformasiPublik.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("Error loading daftar informasi publik:", error);
|
console.error("Gagal fetch daftar informasi publik paginated:", err);
|
||||||
daftarInformasiPublik.findMany.data = [];
|
daftarInformasiPublik.findMany.data = [];
|
||||||
daftarInformasiPublik.findMany.total = 0;
|
|
||||||
daftarInformasiPublik.findMany.totalPages = 1;
|
daftarInformasiPublik.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
daftarInformasiPublik.findMany.loading = false;
|
daftarInformasiPublik.findMany.loading = false;
|
||||||
|
|||||||
@@ -348,18 +348,34 @@ const posisiOrganisasi = proxy({
|
|||||||
deskripsi: string | null;
|
deskripsi: string | null;
|
||||||
hierarki: number;
|
hierarki: number;
|
||||||
}>,
|
}>,
|
||||||
async load() {
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
loading: false,
|
||||||
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
posisiOrganisasi.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
posisiOrganisasi.findMany.page = page;
|
||||||
|
posisiOrganisasi.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi[
|
const query: any = { page, limit };
|
||||||
"find-many"
|
if (search) query.search = search;
|
||||||
].get();
|
|
||||||
if (res.status === 200) {
|
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi["find-many"].get({ query });
|
||||||
// The API now returns the id field, so we can use it directly
|
|
||||||
this.data = res.data?.data ?? [];
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
posisiOrganisasi.findMany.data = res.data.data ?? [];
|
||||||
|
posisiOrganisasi.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
posisiOrganisasi.findMany.data = [];
|
||||||
|
posisiOrganisasi.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("Find many error:", error);
|
console.error("Gagal fetch posisi organisasi paginated:", err);
|
||||||
this.data = [];
|
posisiOrganisasi.findMany.data = [];
|
||||||
|
posisiOrganisasi.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
posisiOrganisasi.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -438,9 +454,9 @@ const pegawai = proxy({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
pegawai.create.loading = true;
|
pegawai.create.loading = true;
|
||||||
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
|
const res = await ApiFetch.api.ppid.strukturppid.pegawai["create"].post(
|
||||||
"create"
|
pegawai.create.form
|
||||||
].post(pegawai.create.form);
|
);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
toast.success("Pegawai berhasil ditambahkan");
|
toast.success("Pegawai berhasil ditambahkan");
|
||||||
await pegawai.findMany.load();
|
await pegawai.findMany.load();
|
||||||
@@ -457,42 +473,55 @@ const pegawai = proxy({
|
|||||||
},
|
},
|
||||||
|
|
||||||
// In struktur-organisasi.ts
|
// In struktur-organisasi.ts
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as any[] | null,
|
data: null as
|
||||||
page: 1,
|
| Prisma.PegawaiPPIDGetPayload<{
|
||||||
totalPages: 1,
|
include: {
|
||||||
total: 0,
|
image: true;
|
||||||
loading: false,
|
posisi: true;
|
||||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
};
|
||||||
pegawai.findMany.loading = true; // Use the full path to access the property
|
}>[]
|
||||||
pegawai.findMany.page = page;
|
| null,
|
||||||
try {
|
page: 1,
|
||||||
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
|
totalPages: 1,
|
||||||
"find-many"
|
total: 0,
|
||||||
].get({
|
loading: false,
|
||||||
query: { page, limit },
|
search: "",
|
||||||
});
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
// Change to arrow function
|
||||||
|
pegawai.findMany.loading = true; // Use the full path to access the property
|
||||||
|
pegawai.findMany.page = page;
|
||||||
|
pegawai.findMany.search = search;
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
|
||||||
pegawai.findMany.data = res.data.data || [];
|
"find-many"
|
||||||
pegawai.findMany.total = res.data.total || 0;
|
].get({
|
||||||
pegawai.findMany.totalPages = res.data.totalPages || 1;
|
query,
|
||||||
} else {
|
});
|
||||||
console.error("Failed to load pegawai:", res.data?.message);
|
|
||||||
|
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.data = [];
|
||||||
pegawai.findMany.total = 0;
|
pegawai.findMany.total = 0;
|
||||||
pegawai.findMany.totalPages = 1;
|
pegawai.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
pegawai.findMany.loading = false;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
},
|
||||||
console.error("Error loading pegawai:", error);
|
|
||||||
pegawai.findMany.data = [];
|
|
||||||
pegawai.findMany.total = 0;
|
|
||||||
pegawai.findMany.totalPages = 1;
|
|
||||||
} finally {
|
|
||||||
pegawai.findMany.loading = false;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as
|
data: null as
|
||||||
| (Prisma.PegawaiGetPayload<{
|
| (Prisma.PegawaiGetPayload<{
|
||||||
@@ -521,12 +550,9 @@ findMany: {
|
|||||||
if (!id) return toast.warn("ID tidak valid");
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
try {
|
try {
|
||||||
pegawai.delete.loading = true;
|
pegawai.delete.loading = true;
|
||||||
const res = await fetch(
|
const res = await fetch(`/api/ppid/strukturppid/pegawai/del/${id}`, {
|
||||||
`/api/ppid/strukturppid/pegawai/del/${id}`,
|
method: "DELETE",
|
||||||
{
|
});
|
||||||
method: "DELETE",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
toast.success(json.message ?? "Berhasil hapus pegawai");
|
toast.success(json.message ?? "Berhasil hapus pegawai");
|
||||||
@@ -555,15 +581,12 @@ findMany: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(`/api/ppid/strukturppid/pegawai/${id}`, {
|
||||||
`/api/ppid/strukturppid/pegawai/${id}`,
|
method: "GET",
|
||||||
{
|
headers: {
|
||||||
method: "GET",
|
"Content-Type": "application/json",
|
||||||
headers: {
|
},
|
||||||
"Content-Type": "application/json",
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
@@ -677,7 +700,7 @@ findMany: {
|
|||||||
const stateStrukturPPID = proxy({
|
const stateStrukturPPID = proxy({
|
||||||
stateStruktur,
|
stateStruktur,
|
||||||
posisiOrganisasi,
|
posisiOrganisasi,
|
||||||
pegawai
|
pegawai,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default stateStrukturPPID;
|
export default stateStrukturPPID;
|
||||||
|
|||||||
@@ -1,124 +1,43 @@
|
|||||||
import { proxy } from 'valtio'
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { toast } from 'react-toastify'
|
import { proxy } from "valtio";
|
||||||
import ApiFetch from '@/lib/api-fetch'
|
import { toast } from "react-toastify";
|
||||||
import { Prisma } from '@prisma/client'
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { z } from 'zod'
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
// 1. Validasi Zod
|
// State Valtio
|
||||||
const userSchema = z.object({
|
|
||||||
nama: z.string().min(1, 'Nama harus diisi'),
|
|
||||||
email: z.string().email('Email tidak valid'),
|
|
||||||
password: z.string().min(6, 'Password minimal 6 karakter'),
|
|
||||||
roleId: z.string().optional(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const defaultForm = { nama: '', email: '', password: '', roleId: '' }
|
|
||||||
|
|
||||||
// 2. State Valtio
|
|
||||||
const userState = proxy({
|
const userState = proxy({
|
||||||
// Register
|
|
||||||
register: {
|
|
||||||
form: { ...defaultForm },
|
|
||||||
loading: false,
|
|
||||||
async submit() {
|
|
||||||
const valid = userSchema.omit({ roleId: true }).safeParse(userState.register.form)
|
|
||||||
if (!valid.success) {
|
|
||||||
const err = valid.error.issues.map(i => i.message).join(', ')
|
|
||||||
return toast.error(err)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
userState.register.loading = true
|
|
||||||
const res = await ApiFetch.api.user.register.post(userState.register.form)
|
|
||||||
if (res.status === 200) {
|
|
||||||
toast.success('Registrasi berhasil, silakan login')
|
|
||||||
userState.register.form = { ...defaultForm } // reset
|
|
||||||
} else {
|
|
||||||
toast.error(res.data?.message || 'Gagal registrasi')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
toast.error('Terjadi kesalahan saat registrasi')
|
|
||||||
} finally {
|
|
||||||
userState.register.loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Login
|
|
||||||
login: {
|
|
||||||
form: { email: '', password: '' },
|
|
||||||
loading: false,
|
|
||||||
async submit() {
|
|
||||||
try {
|
|
||||||
userState.login.loading = true
|
|
||||||
const res = await ApiFetch.api.user.login.post(userState.login.form)
|
|
||||||
if (res.status === 200) {
|
|
||||||
toast.success('Login berhasil')
|
|
||||||
const token = res.data?.data?.token
|
|
||||||
if (typeof token === 'string') {
|
|
||||||
localStorage.setItem('token', token)
|
|
||||||
// Optional: simpan user role untuk otorisasi
|
|
||||||
const user = res.data?.data?.user
|
|
||||||
localStorage.setItem('user', JSON.stringify(user))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
toast.error(res.data?.message || 'Login gagal')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
toast.error('Terjadi kesalahan saat login')
|
|
||||||
} finally {
|
|
||||||
userState.login.loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// CRUD User (untuk admin)
|
|
||||||
create: {
|
|
||||||
form: { ...defaultForm },
|
|
||||||
loading: false,
|
|
||||||
async create(isAdmin = false) {
|
|
||||||
const valid = userSchema.safeParse(userState.create.form)
|
|
||||||
if (!valid.success) {
|
|
||||||
const err = valid.error.issues.map(i => i.message).join(', ')
|
|
||||||
return toast.error(err)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
userState.create.loading = true
|
|
||||||
const endpoint = isAdmin ? 'create' : 'register'
|
|
||||||
const res = await ApiFetch.api.user[endpoint].post(userState.create.form)
|
|
||||||
if (res.status === 200) {
|
|
||||||
toast.success('User berhasil dibuat')
|
|
||||||
userState.findMany.load() // refresh list
|
|
||||||
userState.create.form = { ...defaultForm } // reset form
|
|
||||||
} else {
|
|
||||||
toast.error(res.data?.message || 'Gagal membuat user')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
toast.error('Gagal membuat user')
|
|
||||||
} finally {
|
|
||||||
userState.create.loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Find Many
|
// Find Many
|
||||||
findMany: {
|
findMany: {
|
||||||
data: [] as Prisma.UserGetPayload<{ include: { role: true } }>[],
|
data: [] as Prisma.UserGetPayload<{ include: { role: true } }>[],
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
this.loading = true
|
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 {
|
try {
|
||||||
const res = await ApiFetch.api.user.findMany.get()
|
const query: any = { page, limit };
|
||||||
if (res.status === 200) {
|
if (search) query.search = search;
|
||||||
this.data = res.data?.data || []
|
|
||||||
|
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 (e) {
|
} catch (err) {
|
||||||
console.error(e)
|
console.error("Gagal fetch user paginated:", err);
|
||||||
toast.error('Gagal muat data user')
|
userState.findMany.data = [];
|
||||||
|
userState.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
userState.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -128,71 +47,20 @@ const userState = proxy({
|
|||||||
data: null as Prisma.UserGetPayload<{ include: { role: true } }> | null,
|
data: null as Prisma.UserGetPayload<{ include: { role: true } }> | null,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
this.loading = true
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/user/findUnique/${id}`)
|
const res = await fetch(`/api/user/findUnique/${id}`);
|
||||||
const data = await res.json()
|
const data = await res.json();
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
this.data = data.data
|
this.data = data.data;
|
||||||
} else {
|
} else {
|
||||||
toast.error(data.message)
|
toast.error(data.message);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
toast.error('Gagal ambil data user')
|
toast.error("Gagal ambil data user");
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false;
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Update
|
|
||||||
update: {
|
|
||||||
id: '',
|
|
||||||
form: { ...defaultForm },
|
|
||||||
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) {
|
|
||||||
const user = data.data
|
|
||||||
this.id = user.id
|
|
||||||
this.form = {
|
|
||||||
nama: user.nama,
|
|
||||||
email: user.email,
|
|
||||||
password: '', // jangan kirim password lama
|
|
||||||
roleId: user.roleId,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
toast.error('Gagal muat data')
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async submit() {
|
|
||||||
this.loading = true
|
|
||||||
try {
|
|
||||||
const res = await fetch(`/api/user/update/${this.id}`, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(this.form),
|
|
||||||
})
|
|
||||||
const data = await res.json()
|
|
||||||
if (res.status === 200) {
|
|
||||||
toast.success('Update berhasil')
|
|
||||||
userState.findMany.load()
|
|
||||||
} else {
|
|
||||||
toast.error(data.message || 'Gagal update')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
toast.error('Gagal update user')
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -201,35 +69,63 @@ const userState = proxy({
|
|||||||
delete: {
|
delete: {
|
||||||
loading: false,
|
loading: false,
|
||||||
async submit(id: string) {
|
async submit(id: string) {
|
||||||
this.loading = true
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/user/del/${id}`, {
|
const res = await fetch(`/api/user/del/${id}`, {
|
||||||
method: 'PUT',
|
method: "PUT",
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { "Content-Type": "application/json" },
|
||||||
})
|
});
|
||||||
const data = await res.json()
|
const data = await res.json();
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
toast.success('User dinonaktifkan')
|
toast.success("User dinonaktifkan");
|
||||||
userState.findMany.load()
|
userState.findMany.load();
|
||||||
} else {
|
} else {
|
||||||
toast.error(data.message || 'Gagal hapus')
|
toast.error(data.message || "Gagal hapus");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
toast.error('Gagal hapus user')
|
toast.error("Gagal hapus user");
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
updateActive: {
|
||||||
|
loading: false,
|
||||||
|
async submit(id: string, isActive: boolean) {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/user/updt`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ id, isActive }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.status === 200 && data.success) {
|
||||||
|
toast.success(data.message);
|
||||||
|
userState.findMany.load(userState.findMany.page, 10, userState.findMany.search);
|
||||||
|
} else {
|
||||||
|
toast.error(data.message || "Gagal update status user");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
toast.error("Gagal update status user");
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const templateRole = z.object({
|
const templateRole = z.object({
|
||||||
name: z.string().min(1, "Nama harus diisi"),
|
name: z.string().min(1, "Nama harus diisi"),
|
||||||
|
permissions: z.array(z.string()).min(1, "Permission harus diisi"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultRole = {
|
const defaultRole = {
|
||||||
name: "",
|
name: "",
|
||||||
|
permissions: [] as string[],
|
||||||
};
|
};
|
||||||
|
|
||||||
const roleState = proxy({
|
const roleState = proxy({
|
||||||
@@ -247,10 +143,9 @@ const roleState = proxy({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
roleState.create.loading = true;
|
roleState.create.loading = true;
|
||||||
const res =
|
const res = await ApiFetch.api.role["create"].post(
|
||||||
await ApiFetch.api.role[
|
roleState.create.form
|
||||||
"create"
|
);
|
||||||
].post(roleState.create.form);
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
roleState.findMany.load();
|
roleState.findMany.load();
|
||||||
return toast.success("Data role Berhasil Dibuat");
|
return toast.success("Data role Berhasil Dibuat");
|
||||||
@@ -273,10 +168,7 @@ const roleState = proxy({
|
|||||||
}>[],
|
}>[],
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
async load() {
|
||||||
const res =
|
const res = await ApiFetch.api.role["findMany"].get();
|
||||||
await ApiFetch.api.role[
|
|
||||||
"findMany"
|
|
||||||
].get();
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
roleState.findMany.data = res.data?.data ?? [];
|
roleState.findMany.data = res.data?.data ?? [];
|
||||||
}
|
}
|
||||||
@@ -291,9 +183,7 @@ const roleState = proxy({
|
|||||||
loading: false,
|
loading: false,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(`/api/role/${id}`);
|
||||||
`/api/role/${id}`
|
|
||||||
);
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
roleState.findUnique.data = data.data ?? null;
|
roleState.findUnique.data = data.data ?? null;
|
||||||
@@ -315,22 +205,17 @@ const roleState = proxy({
|
|||||||
try {
|
try {
|
||||||
roleState.delete.loading = true;
|
roleState.delete.loading = true;
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(`/api/role/del/${id}`, {
|
||||||
`/api/role/del/${id}`,
|
method: "DELETE",
|
||||||
{
|
headers: {
|
||||||
method: "DELETE",
|
"Content-Type": "application/json",
|
||||||
headers: {
|
},
|
||||||
"Content-Type": "application/json",
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (response.ok && result?.success) {
|
if (response.ok && result?.success) {
|
||||||
toast.success(
|
toast.success(result.message || "Data role berhasil dihapus");
|
||||||
result.message || "Data role berhasil dihapus"
|
|
||||||
);
|
|
||||||
await roleState.findMany.load(); // refresh list
|
await roleState.findMany.load(); // refresh list
|
||||||
} else {
|
} else {
|
||||||
toast.error(result?.message || "Gagal menghapus Data role");
|
toast.error(result?.message || "Gagal menghapus Data role");
|
||||||
@@ -354,15 +239,12 @@ const roleState = proxy({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(`/api/role/${id}`, {
|
||||||
`/api/role/${id}`,
|
method: "GET",
|
||||||
{
|
headers: {
|
||||||
method: "GET",
|
"Content-Type": "application/json",
|
||||||
headers: {
|
},
|
||||||
"Content-Type": "application/json",
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
@@ -374,6 +256,7 @@ const roleState = proxy({
|
|||||||
this.id = data.id;
|
this.id = data.id;
|
||||||
this.form = {
|
this.form = {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
permissions: data.permissions,
|
||||||
};
|
};
|
||||||
return data; // Return the loaded data
|
return data; // Return the loaded data
|
||||||
} else {
|
} else {
|
||||||
@@ -400,18 +283,16 @@ const roleState = proxy({
|
|||||||
try {
|
try {
|
||||||
roleState.update.loading = true;
|
roleState.update.loading = true;
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(`/api/role/${this.id}`, {
|
||||||
`/api/role/${this.id}`,
|
method: "PUT",
|
||||||
{
|
headers: {
|
||||||
method: "PUT",
|
"Content-Type": "application/json",
|
||||||
headers: {
|
},
|
||||||
"Content-Type": "application/json",
|
body: JSON.stringify({
|
||||||
},
|
name: this.form.name,
|
||||||
body: JSON.stringify({
|
permissions: this.form.permissions,
|
||||||
name: this.form.name,
|
}),
|
||||||
}),
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
@@ -451,6 +332,6 @@ const roleState = proxy({
|
|||||||
const user = proxy({
|
const user = proxy({
|
||||||
userState,
|
userState,
|
||||||
roleState,
|
roleState,
|
||||||
})
|
});
|
||||||
|
|
||||||
export default user
|
export default user;
|
||||||
|
|||||||
111
src/app/admin/(dashboard)/auth/login-admin/page.tsx
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
'use client'
|
||||||
|
import { apiFetchLogin } from '@/app/admin/auth/_lib/api_fetch_auth';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Center, Flex, Image, Paper, Stack, Text, Title } from '@mantine/core';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { PhoneInput } from "react-international-phone";
|
||||||
|
import "react-international-phone/style.css";
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Login() {
|
||||||
|
const router = useRouter()
|
||||||
|
const [phone, setPhone] = useState("")
|
||||||
|
const [isError, setError] = useState(false)
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
async function onLogin() {
|
||||||
|
const nomor = phone.substring(1);
|
||||||
|
if (nomor.length <= 4) return setError(true)
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiFetchLogin({ nomor: nomor })
|
||||||
|
if (response && response.success) {
|
||||||
|
localStorage.setItem("hipmi_auth_code_id", response.kodeId);
|
||||||
|
toast.success(response.message);
|
||||||
|
router.push("/validasi", { scroll: false });
|
||||||
|
} else {
|
||||||
|
setLoading(false);
|
||||||
|
toast.error(response?.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setLoading(false)
|
||||||
|
console.log("Error Login", error)
|
||||||
|
toast.error("Terjadi kesalahan saat login")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack pos={"relative"} bg={colors.Bg}>
|
||||||
|
<Box px={{ base: 'md', md: 100 }} pb={50}>
|
||||||
|
<Stack align='center' justify='center' h={"100vh"}>
|
||||||
|
<Paper p={'xl'} radius={'md'} bg={colors['white-trans-1']}>
|
||||||
|
<Stack align='center' gap={"lg"}>
|
||||||
|
<Box>
|
||||||
|
<Title ta={"center"} order={2} fw={'bold'} c={colors['blue-button']}>
|
||||||
|
Login
|
||||||
|
</Title>
|
||||||
|
<Center>
|
||||||
|
<Image src={"/darmasaba-icon.png"} alt="" w={80} />
|
||||||
|
</Center>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
{/* <Box mb={10}>
|
||||||
|
<Text c={colors['blue-button']} ta={"center"} fz={"sm"} fw={'bold'}>Masuk Untuk Akses Admin</Text>
|
||||||
|
<TextInput
|
||||||
|
label='Username'
|
||||||
|
placeholder='Username'
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Box> */}
|
||||||
|
<PhoneInput
|
||||||
|
countrySelectorStyleProps={{
|
||||||
|
buttonStyle: {
|
||||||
|
backgroundColor: colors['blue-button'],
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
inputStyle={{ width: "100%"}}
|
||||||
|
defaultCountry="id"
|
||||||
|
onChange={(val) => {
|
||||||
|
setPhone(val);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{isError ? (
|
||||||
|
toast.error("Masukan nomor telepon anda")
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
<Box py={20} >
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
radius={'xl'}
|
||||||
|
onClick={onLogin}
|
||||||
|
loading={loading ? true : false}
|
||||||
|
>Masuk
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Flex justify={'center'} align={'center'}>
|
||||||
|
<Text>Belum punya akun? </Text>
|
||||||
|
<Button variant='transparent' component={Link} href={'/registrasi'}>
|
||||||
|
<Text c={colors['blue-button']} fw={'bold'}>Registrasi</Text>
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Login;
|
||||||
121
src/app/admin/(dashboard)/auth/registrasi-admin/page.tsx
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||||
|
'use client'
|
||||||
|
import { apiFetchRegister } from '@/app/admin/auth/_lib/api_fetch_auth';
|
||||||
|
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Center, Checkbox, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { PhoneInput } from "react-international-phone";
|
||||||
|
import "react-international-phone/style.css";
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
function Registrasi() {
|
||||||
|
const [phone, setPhone] = useState("")
|
||||||
|
const router = useRouter()
|
||||||
|
const [value, setValue] = useState("")
|
||||||
|
const [isValue, setIsValue] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
async function onRegistarsi() {
|
||||||
|
if (value.length < 5) {
|
||||||
|
toast.error("Username minimal 5 karakter!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.includes(" ")) {
|
||||||
|
toast.error("Username tidak boleh ada spasi!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!phone) {
|
||||||
|
toast.error("Nomor telepon wajib diisi!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const respone = await apiFetchRegister({ nomor: phone, username: value });
|
||||||
|
|
||||||
|
if (respone.success) {
|
||||||
|
router.push("/login", { scroll: false });
|
||||||
|
toast.success(respone.message);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
setLoading(false);
|
||||||
|
toast.error(respone.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setLoading(false);
|
||||||
|
console.log("Error Registrasi", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Stack pos={"relative"} bg={colors.Bg} gap={"22"} py={"xl"} h={"100vh"}>
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<BackButton />
|
||||||
|
</Box>
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Stack justify='center' align='center' h={"80vh"}>
|
||||||
|
<Paper p={'xl'} radius={'md'} bg={colors['white-trans-1']}>
|
||||||
|
<Stack align='center'>
|
||||||
|
<Title order={2} fw={'bold'} c={colors['blue-button']}>
|
||||||
|
Registrasi
|
||||||
|
</Title>
|
||||||
|
<Center>
|
||||||
|
<Image src={"/darmasaba-icon.png"} alt="" w={80} />
|
||||||
|
</Center>
|
||||||
|
<Box>
|
||||||
|
<TextInput placeholder='Username'
|
||||||
|
label='Username'
|
||||||
|
maxLength={50}
|
||||||
|
|
||||||
|
error={
|
||||||
|
value.length > 0 && value.length < 5
|
||||||
|
? "Minimal 5 karakter !"
|
||||||
|
: value.includes(" ")
|
||||||
|
? "Tidak boleh ada spasi"
|
||||||
|
: isValue
|
||||||
|
? "Masukan username anda"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
onChange={(val) => {
|
||||||
|
val.currentTarget.value.length > 0 ? setIsValue(false) : "";
|
||||||
|
setValue(val.currentTarget.value);
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
|
||||||
|
/>
|
||||||
|
<Box py={10}>
|
||||||
|
<Text fz={"sm"} >Nomor Telepon</Text>
|
||||||
|
<PhoneInput
|
||||||
|
countrySelectorStyleProps={{
|
||||||
|
buttonStyle: {
|
||||||
|
backgroundColor: colors['blue-button'],
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
inputStyle={{ width: "100%" }}
|
||||||
|
defaultCountry="id"
|
||||||
|
onChange={(val) => {
|
||||||
|
setPhone(val);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box pb={10}>
|
||||||
|
<Checkbox
|
||||||
|
label="Saya menyetujui syarat dan ketentuan yang berlaku"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box pb={20} >
|
||||||
|
<Button fullWidth bg={colors['blue-button']} radius={'xl'} onClick={onRegistarsi} loading={loading ? true : false}>Daftar</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Registrasi;
|
||||||
38
src/app/admin/(dashboard)/auth/validasi-admin/page.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Paper, PinInput, Stack, Text, Title } from '@mantine/core';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
function Validasi() {
|
||||||
|
const router = useRouter()
|
||||||
|
return (
|
||||||
|
<Stack pos={"relative"} bg={colors.Bg}>
|
||||||
|
<Box px={{ base: 'md', md: 100 }} pb={50}>
|
||||||
|
<Stack align='center' justify='center' h={"100vh"}>
|
||||||
|
<Paper p={'xl'} radius={'md'} bg={colors['white-trans-1']}>
|
||||||
|
<Stack align='center' gap={"lg"}>
|
||||||
|
<Box>
|
||||||
|
<Title ta={"center"} order={2} fw={'bold'} c={colors['blue-button']}>
|
||||||
|
Kode Verifikasi
|
||||||
|
</Title>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Text c={colors['blue-button']} ta={"center"} fz={"sm"} fw={'bold'}>Masukkan Kode Verifikasi</Text>
|
||||||
|
<PinInput type={/^[0-9]*$/} inputType="tel" inputMode="numeric" />
|
||||||
|
</Box>
|
||||||
|
<Box py={20} >
|
||||||
|
<Button onClick={() => router.push("/admin/landing-page/profile/program-inovasi")}>
|
||||||
|
Page
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Validasi;
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { IconFileText, IconBuildingStore, IconSparkles, IconUsers } from '@tabler/icons-react';
|
||||||
|
|
||||||
function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
|
function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -12,26 +13,35 @@ function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
|
|||||||
{
|
{
|
||||||
label: "Pelayanan Surat Keterangan",
|
label: "Pelayanan Surat Keterangan",
|
||||||
value: "pelayanansuratketerangan",
|
value: "pelayanansuratketerangan",
|
||||||
href: "/admin/desa/layanan/pelayanan_surat_keterangan"
|
href: "/admin/desa/layanan/pelayanan_surat_keterangan",
|
||||||
|
icon: <IconFileText size={18} stroke={1.8} />,
|
||||||
|
tooltip: "Layanan terkait surat keterangan resmi desa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Pelayanan Perizinan Berusaha",
|
label: "Pelayanan Perizinan Berusaha",
|
||||||
value: "pelayananperizinanusaha",
|
value: "pelayananperizinanusaha",
|
||||||
href: "/admin/desa/layanan/pelayanan_perizinan_berusaha"
|
href: "/admin/desa/layanan/pelayanan_perizinan_berusaha",
|
||||||
|
icon: <IconBuildingStore size={18} stroke={1.8} />,
|
||||||
|
tooltip: "Layanan untuk izin usaha masyarakat"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Pelayanan Telunjuk Sakti Desa",
|
label: "Pelayanan Telunjuk Sakti Desa",
|
||||||
value: "pelayanantelunjuksaktidesa",
|
value: "pelayanantelunjuksaktidesa",
|
||||||
href: "/admin/desa/layanan/pelayanan_telunjuk_sakti_desa"
|
href: "/admin/desa/layanan/pelayanan_telunjuk_sakti_desa",
|
||||||
|
icon: <IconSparkles size={18} stroke={1.8} />,
|
||||||
|
tooltip: "Layanan inovasi khusus desa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Pelayanan Penduduk Non-Permanent",
|
label: "Pelayanan Penduduk Non-Permanent",
|
||||||
value: "pelayanantelunjuknonpermanent",
|
value: "pelayanannonpermanent",
|
||||||
href: "/admin/desa/layanan/pelayanan_penduduk_non_permanent"
|
href: "/admin/desa/layanan/pelayanan_penduduk_non_permanent",
|
||||||
|
icon: <IconUsers size={18} stroke={1.8} />,
|
||||||
|
tooltip: "Pendataan penduduk non-permanent"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const curentTab = tabs.find(tab => tab.href === pathname)
|
|
||||||
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
|
const currentTab = tabs.find(tab => tab.href === pathname)
|
||||||
|
const [activeTab, setActiveTab] = useState<string | null>(currentTab?.value || tabs[0].value);
|
||||||
|
|
||||||
const handleTabChange = (value: string | null) => {
|
const handleTabChange = (value: string | null) => {
|
||||||
const tab = tabs.find(t => t.value === value)
|
const tab = tabs.find(t => t.value === value)
|
||||||
@@ -49,24 +59,65 @@ function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
|
|||||||
}, [pathname])
|
}, [pathname])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack gap="lg">
|
||||||
<Title order={3}>Layanan</Title>
|
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Layanan</Title>
|
||||||
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
|
<Tabs
|
||||||
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
color={colors['blue-button']}
|
||||||
{tabs.map((e, i) => (
|
variant='pills'
|
||||||
<TabsTab key={i} value={e.value}>{e.label}</TabsTab>
|
value={activeTab}
|
||||||
|
onChange={handleTabChange}
|
||||||
|
radius="lg"
|
||||||
|
keepMounted={false}
|
||||||
|
>
|
||||||
|
<TabsList
|
||||||
|
p="sm"
|
||||||
|
style={{
|
||||||
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((tab, i) => (
|
||||||
|
<Tooltip
|
||||||
|
key={i}
|
||||||
|
label={tab.tooltip}
|
||||||
|
position="bottom"
|
||||||
|
withArrow
|
||||||
|
transitionProps={{ transition: 'pop', duration: 200 }}
|
||||||
|
>
|
||||||
|
<TabsTab
|
||||||
|
value={tab.value}
|
||||||
|
leftSection={tab.icon}
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
transition: "all 0.2s ease",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
{tabs.map((e, i) => (
|
|
||||||
<TabsPanel key={i} value={e.value}>
|
{tabs.map((tab, i) => (
|
||||||
{/* Konten dummy, bisa diganti tergantung routing */}
|
<TabsPanel
|
||||||
<></>
|
key={i}
|
||||||
|
value={tab.value}
|
||||||
|
style={{
|
||||||
|
padding: "1.5rem",
|
||||||
|
background: "linear-gradient(180deg, #ffffff, #f5f6fa)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
boxShadow: "0 4px 16px rgba(0,0,0,0.05)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Konten dummy, bisa diganti sesuai routing */}
|
||||||
|
<>{children}</>
|
||||||
</TabsPanel>
|
</TabsPanel>
|
||||||
))}
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
{children}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LayoutTabsLayanan;
|
export default LayoutTabsLayanan;
|
||||||
|
|||||||
@@ -1,63 +1,110 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { IconNews, IconCategory } from '@tabler/icons-react';
|
||||||
|
|
||||||
function LayoutTabsBerita({ children }: { children: React.ReactNode }) {
|
function LayoutTabsBerita({ children }: { children: React.ReactNode }) {
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
const pathname = usePathname()
|
const pathname = usePathname();
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
label: "List Berita",
|
label: "List Berita",
|
||||||
value: "list_berita",
|
value: "list_berita",
|
||||||
href: "/admin/desa/berita/list-berita"
|
href: "/admin/desa/berita/list-berita",
|
||||||
|
icon: <IconNews size={18} stroke={1.8} />,
|
||||||
|
tooltip: "Lihat dan kelola semua berita desa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Kategori Berita",
|
label: "Kategori Berita",
|
||||||
value: "kategori_berita",
|
value: "kategori_berita",
|
||||||
href: "/admin/desa/berita/kategori-berita"
|
href: "/admin/desa/berita/kategori-berita",
|
||||||
|
icon: <IconCategory size={18} stroke={1.8} />,
|
||||||
|
tooltip: "Kelola kategori berita desa"
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
const curentTab = tabs.find(tab => tab.href === pathname)
|
|
||||||
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
|
const currentTab = tabs.find(tab => tab.href === pathname);
|
||||||
|
const [activeTab, setActiveTab] = useState<string | null>(currentTab?.value || tabs[0].value);
|
||||||
|
|
||||||
const handleTabChange = (value: string | null) => {
|
const handleTabChange = (value: string | null) => {
|
||||||
const tab = tabs.find(t => t.value === value)
|
const tab = tabs.find(t => t.value === value);
|
||||||
if (tab) {
|
if (tab) {
|
||||||
router.push(tab.href)
|
router.push(tab.href);
|
||||||
}
|
}
|
||||||
setActiveTab(value)
|
setActiveTab(value);
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const match = tabs.find(tab => tab.href === pathname)
|
const match = tabs.find(tab => tab.href === pathname);
|
||||||
if (match) {
|
if (match) {
|
||||||
setActiveTab(match.value)
|
setActiveTab(match.value);
|
||||||
}
|
}
|
||||||
}, [pathname])
|
}, [pathname]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack gap="lg">
|
||||||
<Title order={3}>Gallery</Title>
|
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Berita Desa</Title>
|
||||||
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
|
<Tabs
|
||||||
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
color={colors['blue-button']}
|
||||||
{tabs.map((e, i) => (
|
variant="pills"
|
||||||
<TabsTab key={i} value={e.value}>{e.label}</TabsTab>
|
value={activeTab}
|
||||||
|
onChange={handleTabChange}
|
||||||
|
radius="lg"
|
||||||
|
keepMounted={false}
|
||||||
|
>
|
||||||
|
<TabsList
|
||||||
|
p="sm"
|
||||||
|
style={{
|
||||||
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((tab, i) => (
|
||||||
|
<Tooltip
|
||||||
|
key={i}
|
||||||
|
label={tab.tooltip}
|
||||||
|
position="bottom"
|
||||||
|
withArrow
|
||||||
|
transitionProps={{ transition: 'pop', duration: 200 }}
|
||||||
|
>
|
||||||
|
<TabsTab
|
||||||
|
value={tab.value}
|
||||||
|
leftSection={tab.icon}
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
transition: "all 0.2s ease",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
{tabs.map((e, i) => (
|
|
||||||
<TabsPanel key={i} value={e.value}>
|
{tabs.map((tab, i) => (
|
||||||
{/* Konten dummy, bisa diganti tergantung routing */}
|
<TabsPanel
|
||||||
<></>
|
key={i}
|
||||||
|
value={tab.value}
|
||||||
|
style={{
|
||||||
|
padding: "1.5rem",
|
||||||
|
background: "linear-gradient(180deg, #ffffff, #f5f6fa)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
boxShadow: "0 4px 16px rgba(0,0,0,0.05)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Konten dummy, bisa diganti sesuai routing */}
|
||||||
|
<>{children}</>
|
||||||
</TabsPanel>
|
</TabsPanel>
|
||||||
))}
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
{children}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LayoutTabsBerita;
|
export default LayoutTabsBerita;
|
||||||
|
|||||||
@@ -2,7 +2,16 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
Tooltip
|
||||||
|
} from '@mantine/core';
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
@@ -10,67 +19,102 @@ import { toast } from 'react-toastify';
|
|||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
function EditKategoriBerita() {
|
function EditKategoriBerita() {
|
||||||
const editState = useProxy(stateDashboardBerita.kategoriBerita)
|
const editState = useProxy(stateDashboardBerita.kategoriBerita);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: editState.update.form.name || '',
|
name: editState.update.form.name || '',
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadKategori = async () => {
|
const loadKategori = async () => {
|
||||||
const id = params?.id as string;
|
const id = params?.id as string;
|
||||||
if (!id) return;
|
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 {
|
try {
|
||||||
editState.update.form = {
|
const data = await editState.update.load(id);
|
||||||
...editState.update.form,
|
if (data) {
|
||||||
name: formData.name,
|
setFormData({
|
||||||
};
|
name: data.name || '',
|
||||||
await editState.update.update();
|
});
|
||||||
toast.success('Kategori Berita berhasil diperbarui!');
|
}
|
||||||
router.push('/admin/desa/berita/kategori-berita');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating kategori Berita:', error);
|
console.error('Error loading kategori Berita:', error);
|
||||||
toast.error('Terjadi kesalahan saat memperbarui kategori Berita');
|
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 (
|
return (
|
||||||
<Box>
|
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||||
<Box mb={10}>
|
{/* Back Button + Title */}
|
||||||
<Button variant="subtle" onClick={() => router.back()}>
|
<Group mb="md">
|
||||||
<IconArrowBack color={colors["blue-button"]} size={30} />
|
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||||
</Button>
|
<Button
|
||||||
</Box>
|
variant="subtle"
|
||||||
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
|
onClick={() => router.back()}
|
||||||
<Stack gap={"xs"}>
|
p="xs"
|
||||||
<Title order={3}>Edit Kategori Berita</Title>
|
radius="md"
|
||||||
|
>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Title order={4} ml="sm" c="dark">
|
||||||
|
Edit Kategori Berita
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Form Wrapper */}
|
||||||
|
<Paper
|
||||||
|
w={{ base: '100%', md: '50%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{ border: '1px solid #e0e0e0' }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
<TextInput
|
<TextInput
|
||||||
|
label="Nama Kategori Berita"
|
||||||
|
placeholder="Masukkan nama kategori berita"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Nama Kategori Berita</Text>}
|
required
|
||||||
placeholder="masukkan nama kategori Berita"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button onClick={handleSubmit}>Simpan</Button>
|
<Group justify="right">
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||||
|
color: '#fff',
|
||||||
|
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,50 +1,87 @@
|
|||||||
'use client'
|
'use client';
|
||||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
} from '@mantine/core';
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function CreateKategoriBerita() {
|
function CreateKategoriBerita() {
|
||||||
const createState = useProxy(stateDashboardBerita.kategoriBerita)
|
const createState = useProxy(stateDashboardBerita.kategoriBerita);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
createState.create.form = {
|
createState.create.form = {
|
||||||
name: "",
|
name: '',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
await createState.create.create();
|
await createState.create.create();
|
||||||
resetForm();
|
resetForm();
|
||||||
router.push("/admin/desa/berita/kategori-berita")
|
router.push('/admin/desa/berita/kategori-berita');
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||||
<Box mb={10}>
|
{/* Header dengan back button */}
|
||||||
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
<Group mb="md">
|
||||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||||
</Button>
|
<Button
|
||||||
</Box>
|
variant="subtle"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
p="xs"
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Title order={4} ml="sm" c="dark">
|
||||||
|
Tambah Kategori Berita
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
{/* Form utama */}
|
||||||
<Stack gap={"xs"}>
|
<Paper
|
||||||
<Title order={4}>Create Kategori Berita</Title>
|
w={{ base: '100%', md: '50%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{ border: '1px solid #e0e0e0' }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Nama Kategori Berita</Text>}
|
label={<Text fw="bold" fz="sm">Nama Kategori Berita</Text>}
|
||||||
placeholder='Masukkan nama kategori Berita'
|
placeholder="Masukkan nama kategori berita"
|
||||||
value={createState.create.form.name}
|
value={createState.create.form.name || ''}
|
||||||
onChange={(val) => {
|
onChange={(e) => (createState.create.form.name = e.target.value)}
|
||||||
createState.create.form.name = val.target.value;
|
required
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Group>
|
|
||||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>Submit</Button>
|
<Group justify="right">
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||||
|
color: '#fff',
|
||||||
|
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -1,25 +1,40 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
import {
|
||||||
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
|
Box,
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Group,
|
||||||
|
Pagination,
|
||||||
|
Paper,
|
||||||
|
Skeleton,
|
||||||
|
Stack,
|
||||||
|
Table,
|
||||||
|
TableTbody,
|
||||||
|
TableTd,
|
||||||
|
TableTh,
|
||||||
|
TableThead,
|
||||||
|
TableTr,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
Tooltip
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import HeaderSearch from '../../../_com/header';
|
import HeaderSearch from '../../../_com/header';
|
||||||
import JudulList from '../../../_com/judulList';
|
|
||||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
import stateDashboardBerita from '../../../_state/desa/berita';
|
import stateDashboardBerita from '../../../_state/desa/berita';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function KategoriBerita() {
|
function KategoriBerita() {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<HeaderSearch
|
<HeaderSearch
|
||||||
title='Kategori Berita'
|
title="Kategori Berita"
|
||||||
placeholder='pencarian'
|
placeholder="Cari nama kategori berita..."
|
||||||
searchIcon={<IconSearch size={20} />}
|
searchIcon={<IconSearch size={20} />}
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||||
@@ -30,99 +45,155 @@ function KategoriBerita() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ListKategoriBerita({ search }: { search: string }) {
|
function ListKategoriBerita({ search }: { search: string }) {
|
||||||
const listDataState = useProxy(stateDashboardBerita.kategoriBerita)
|
const listDataState = useProxy(stateDashboardBerita.kategoriBerita);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [modalHapus, setModalHapus] = useState(false)
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
load,
|
||||||
|
page,
|
||||||
|
totalPages,
|
||||||
|
} = listDataState.findMany;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
listDataState.findMany.load()
|
load(page, 10, search);
|
||||||
}, [])
|
}, [page, search]);
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
listDataState.delete.delete(selectedId)
|
listDataState.delete.delete(selectedId);
|
||||||
setModalHapus(false)
|
setModalHapus(false);
|
||||||
setSelectedId(null)
|
setSelectedId(null);
|
||||||
|
load(page, 10, search);
|
||||||
listDataState.findMany.load()
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const filteredData = (listDataState.findMany.data || []).filter(item => {
|
const filteredData = data || [];
|
||||||
const keyword = search.toLowerCase();
|
|
||||||
return (
|
|
||||||
item.name.toLowerCase().includes(keyword)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!listDataState.findMany.data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
<Skeleton h={500} />
|
<Skeleton height={600} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper bg={colors['white-1']} p="md">
|
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||||
<Stack>
|
<Group justify="space-between" mb="md">
|
||||||
<JudulList
|
<Title order={4}>Daftar Kategori Berita</Title>
|
||||||
title='List Kategori Berita'
|
<Tooltip label="Tambah Kategori Berita" withArrow>
|
||||||
href='/admin/desa/berita/kategori-berita/create'
|
<Button
|
||||||
/>
|
leftSection={<IconPlus size={18} />}
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
color="blue"
|
||||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
variant="light"
|
||||||
<TableThead>
|
onClick={() =>
|
||||||
<TableTr>
|
router.push('/admin/desa/berita/kategori-berita/create')
|
||||||
<TableTh>No</TableTh>
|
}
|
||||||
<TableTh>Nama</TableTh>
|
>
|
||||||
<TableTh>Edit</TableTh>
|
Tambah Baru
|
||||||
<TableTh>Hapus</TableTh>
|
</Button>
|
||||||
</TableTr>
|
</Tooltip>
|
||||||
</TableThead>
|
</Group>
|
||||||
<TableTbody>
|
|
||||||
{filteredData.map((item, index) => (
|
<Box style={{ overflowX: 'auto' }}>
|
||||||
|
<Table highlightOnHover>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh style={{ width: '10%' }}>No</TableTh>
|
||||||
|
<TableTh style={{ width: '50%' }}>Nama</TableTh>
|
||||||
|
<TableTh style={{ width: '20%' }}>Edit</TableTh>
|
||||||
|
<TableTh style={{ width: '20%' }}>Hapus</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item, index) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Box w={100}>
|
<Text fz="sm">{index + 1}</Text>
|
||||||
<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>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Text fw={500} truncate="end" lineClamp={1}>
|
||||||
color='red'
|
{item.name}
|
||||||
disabled={listDataState.delete.loading}
|
</Text>
|
||||||
onClick={() => {
|
</TableTd>
|
||||||
setSelectedId(item.id)
|
<TableTd>
|
||||||
setModalHapus(true)
|
<Tooltip label="Edit Kategori Berita" withArrow>
|
||||||
}}>
|
<Button
|
||||||
<IconTrash size={20} />
|
variant="light"
|
||||||
</Button>
|
color="green"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/desa/berita/kategori-berita/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconEdit size={18} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Tooltip label="Hapus Kategori Berita" withArrow>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="red"
|
||||||
|
disabled={listDataState.delete.loading}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(item.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconTrash size={18} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
))}
|
))
|
||||||
</TableTbody>
|
) : (
|
||||||
</Table>
|
<TableTr>
|
||||||
</Box>
|
<TableTd colSpan={4}>
|
||||||
</Stack>
|
<Center py={20}>
|
||||||
|
<Text color="dimmed">
|
||||||
|
Tidak ada data kategori berita yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
)}
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
<Center>
|
||||||
|
<Pagination
|
||||||
|
value={page}
|
||||||
|
onChange={(newPage) => {
|
||||||
|
load(newPage, 10, search);
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
}}
|
||||||
|
total={totalPages}
|
||||||
|
mt="md"
|
||||||
|
mb="md"
|
||||||
|
color="blue"
|
||||||
|
radius="md"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
|
||||||
{/* Modal Konfirmasi Hapus */}
|
{/* Modal Konfirmasi Hapus */}
|
||||||
<ModalKonfirmasiHapus
|
<ModalKonfirmasiHapus
|
||||||
opened={modalHapus}
|
opened={modalHapus}
|
||||||
onClose={() => setModalHapus(false)}
|
onClose={() => setModalHapus(false)}
|
||||||
onConfirm={handleDelete}
|
onConfirm={handleDelete}
|
||||||
text='Apakah anda yakin ingin menghapus kategori Berita ini?'
|
text="Apakah anda yakin ingin menghapus kategori berita ini?"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default KategoriBerita;
|
export default KategoriBerita;
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Title
|
Title,
|
||||||
|
Tooltip,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { Dropzone } from "@mantine/dropzone";
|
import { Dropzone } from "@mantine/dropzone";
|
||||||
import { IconArrowBack, IconPhoto, IconUpload, IconX } from "@tabler/icons-react";
|
import { IconArrowBack, IconPhoto, IconUpload, IconX } from "@tabler/icons-react";
|
||||||
@@ -24,7 +25,6 @@ import { useEffect, useState } from "react";
|
|||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { useProxy } from "valtio/utils";
|
import { useProxy } from "valtio/utils";
|
||||||
|
|
||||||
|
|
||||||
function EditBerita() {
|
function EditBerita() {
|
||||||
const beritaState = useProxy(stateDashboardBerita);
|
const beritaState = useProxy(stateDashboardBerita);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -33,29 +33,29 @@ function EditBerita() {
|
|||||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||||
const [file, setFile] = useState<File | null>(null);
|
const [file, setFile] = useState<File | null>(null);
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
judul: beritaState.berita.edit.form.judul || '',
|
judul: beritaState.berita.edit.form.judul || "",
|
||||||
deskripsi: beritaState.berita.edit.form.deskripsi || '',
|
deskripsi: beritaState.berita.edit.form.deskripsi || "",
|
||||||
kategoriBeritaId: beritaState.berita.edit.form.kategoriBeritaId || '',
|
kategoriBeritaId: beritaState.berita.edit.form.kategoriBeritaId || "",
|
||||||
content: beritaState.berita.edit.form.content || '',
|
content: beritaState.berita.edit.form.content || "",
|
||||||
imageId: beritaState.berita.edit.form.imageId || ''
|
imageId: beritaState.berita.edit.form.imageId || "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load berita by id saat pertama kali
|
// Load berita by id saat pertama kali
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
beritaState.kategoriBerita.findMany.load()
|
beritaState.kategoriBerita.findMany.load();
|
||||||
const loadBerita = async () => {
|
const loadBerita = async () => {
|
||||||
const id = params?.id as string;
|
const id = params?.id as string;
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await stateDashboardBerita.berita.edit.load(id); // akses langsung, bukan dari proxy
|
const data = await stateDashboardBerita.berita.edit.load(id);
|
||||||
if (data) {
|
if (data) {
|
||||||
setFormData({
|
setFormData({
|
||||||
judul: data.judul || '',
|
judul: data.judul || "",
|
||||||
deskripsi: data.deskripsi || '',
|
deskripsi: data.deskripsi || "",
|
||||||
kategoriBeritaId: data.kategoriBeritaId || '',
|
kategoriBeritaId: data.kategoriBeritaId || "",
|
||||||
content: data.content || '',
|
content: data.content || "",
|
||||||
imageId: data.imageId || '',
|
imageId: data.imageId || "",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data?.image?.link) {
|
if (data?.image?.link) {
|
||||||
@@ -69,31 +69,26 @@ function EditBerita() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loadBerita();
|
loadBerita();
|
||||||
}, [params?.id]); // ✅ hapus beritaState dari dependency
|
}, [params?.id]);
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Update global state with form data
|
|
||||||
beritaState.berita.edit.form = {
|
beritaState.berita.edit.form = {
|
||||||
...beritaState.berita.edit.form,
|
...beritaState.berita.edit.form,
|
||||||
judul: formData.judul,
|
...formData,
|
||||||
deskripsi: formData.deskripsi,
|
|
||||||
content: formData.content,
|
|
||||||
kategoriBeritaId: formData.kategoriBeritaId || '',
|
|
||||||
imageId: formData.imageId // Keep existing imageId if not changed
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Jika ada file baru, upload
|
|
||||||
if (file) {
|
if (file) {
|
||||||
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
const res = await ApiFetch.api.fileStorage.create.post({
|
||||||
|
file,
|
||||||
|
name: file.name,
|
||||||
|
});
|
||||||
const uploaded = res.data?.data;
|
const uploaded = res.data?.data;
|
||||||
|
|
||||||
if (!uploaded?.id) {
|
if (!uploaded?.id) {
|
||||||
return toast.error("Gagal upload gambar");
|
return toast.error("Gagal upload gambar");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update imageId in global state
|
|
||||||
beritaState.berita.edit.form.imageId = uploaded.id;
|
beritaState.berita.edit.form.imageId = uploaded.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,87 +102,111 @@ function EditBerita() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box px={{ base: "sm", md: "lg" }} py="md">
|
||||||
<Box mb={10}>
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()}>
|
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||||
<IconArrowBack color={colors["blue-button"]} size={30} />
|
<Button
|
||||||
</Button>
|
variant="subtle"
|
||||||
</Box>
|
onClick={() => router.back()}
|
||||||
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
|
p="xs"
|
||||||
<Stack gap={"xs"}>
|
radius="md"
|
||||||
<Title order={3}>Edit Berita</Title>
|
>
|
||||||
|
<IconArrowBack color={colors["blue-button"]} size={24} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Title order={4} ml="sm" c="dark">
|
||||||
|
Edit Berita
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Paper
|
||||||
|
w={{ base: "100%", md: "50%" }}
|
||||||
|
bg={colors["white-1"]}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{ border: "1px solid #e0e0e0" }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
<TextInput
|
<TextInput
|
||||||
|
label="Judul"
|
||||||
|
placeholder="Masukkan judul"
|
||||||
value={formData.judul}
|
value={formData.judul}
|
||||||
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
|
onChange={(e) =>
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
setFormData({ ...formData, judul: e.target.value })
|
||||||
placeholder="masukkan judul"
|
}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
|
label="Deskripsi"
|
||||||
|
placeholder="Masukkan deskripsi"
|
||||||
value={formData.deskripsi}
|
value={formData.deskripsi}
|
||||||
onChange={(e) => setFormData({ ...formData, deskripsi: e.target.value })}
|
onChange={(e) =>
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
|
setFormData({ ...formData, deskripsi: e.target.value })
|
||||||
placeholder="masukkan deskripsi"
|
}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz={"md"} fw={"bold"}>Gambar</Text>
|
<Text fw="bold" fz="sm" mb={6}>
|
||||||
<Box>
|
Gambar Berita
|
||||||
<Dropzone
|
</Text>
|
||||||
onDrop={(files) => {
|
<Dropzone
|
||||||
const selectedFile = files[0]; // Ambil file pertama
|
onDrop={(files) => {
|
||||||
if (selectedFile) {
|
const selectedFile = files[0];
|
||||||
setFile(selectedFile);
|
if (selectedFile) {
|
||||||
setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview
|
setFile(selectedFile);
|
||||||
}
|
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||||
}}
|
}
|
||||||
onReject={() => toast.error('File tidak valid.')}
|
}}
|
||||||
maxSize={5 * 1024 ** 2} // Maks 5MB
|
onReject={() => toast.error("File tidak valid, gunakan format gambar")}
|
||||||
accept={{ 'image/*': [] }}
|
maxSize={5 * 1024 ** 2}
|
||||||
>
|
accept={{ "image/*": [] }}
|
||||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
radius="md"
|
||||||
<Dropzone.Accept>
|
p="xl"
|
||||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
>
|
||||||
</Dropzone.Accept>
|
<Group justify="center" gap="xl" mih={180}>
|
||||||
<Dropzone.Reject>
|
<Dropzone.Accept>
|
||||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
<IconUpload size={48} color={colors["blue-button"]} stroke={1.5} />
|
||||||
</Dropzone.Reject>
|
</Dropzone.Accept>
|
||||||
<Dropzone.Idle>
|
<Dropzone.Reject>
|
||||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
<IconX size={48} color="red" stroke={1.5} />
|
||||||
</Dropzone.Idle>
|
</Dropzone.Reject>
|
||||||
|
<Dropzone.Idle>
|
||||||
|
<IconPhoto size={48} color="#868e96" stroke={1.5} />
|
||||||
|
</Dropzone.Idle>
|
||||||
|
<Stack gap="xs" align="center">
|
||||||
|
<Text size="md" fw={500}>
|
||||||
|
Seret gambar atau klik untuk memilih file
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" c="dimmed">
|
||||||
|
Maksimal 5MB, format gambar wajib
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Group>
|
||||||
|
</Dropzone>
|
||||||
|
|
||||||
<div>
|
{previewImage && (
|
||||||
<Text size="xl" inline>
|
<Box mt="sm" style={{ display: "flex", justifyContent: "center" }}>
|
||||||
Drag gambar ke sini atau klik untuk pilih file
|
<Image
|
||||||
</Text>
|
src={previewImage}
|
||||||
<Text size="sm" c="dimmed" inline mt={7}>
|
alt="Preview Gambar"
|
||||||
Maksimal 5MB dan harus format gambar
|
radius="md"
|
||||||
</Text>
|
style={{
|
||||||
</div>
|
maxHeight: 220,
|
||||||
</Group>
|
objectFit: "contain",
|
||||||
</Dropzone>
|
border: `1px solid ${colors["blue-button"]}`,
|
||||||
|
}}
|
||||||
{/* Tampilkan preview kalau ada */}
|
/>
|
||||||
{previewImage && (
|
</Box>
|
||||||
<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>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
<Text fz="sm" fw="bold">
|
||||||
|
Konten
|
||||||
|
</Text>
|
||||||
<EditEditor
|
<EditEditor
|
||||||
value={formData.content}
|
value={formData.content}
|
||||||
onChange={(htmlContent) => {
|
onChange={(htmlContent) => {
|
||||||
@@ -199,13 +218,15 @@ function EditBerita() {
|
|||||||
|
|
||||||
<Select
|
<Select
|
||||||
value={formData.kategoriBeritaId}
|
value={formData.kategoriBeritaId}
|
||||||
onChange={(val) => setFormData({ ...formData, kategoriBeritaId: val || "" })}
|
onChange={(val) =>
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Kategori</Text>}
|
setFormData({ ...formData, kategoriBeritaId: val || "" })
|
||||||
placeholder='Pilih kategori'
|
}
|
||||||
|
label="Kategori"
|
||||||
|
placeholder="Pilih kategori"
|
||||||
data={
|
data={
|
||||||
beritaState.kategoriBerita.findMany.data?.map((v) => ({
|
beritaState.kategoriBerita.findMany.data?.map((v) => ({
|
||||||
value: v.id,
|
value: v.id,
|
||||||
label: v.name
|
label: v.name,
|
||||||
})) || []
|
})) || []
|
||||||
}
|
}
|
||||||
clearable
|
clearable
|
||||||
@@ -214,7 +235,20 @@ function EditBerita() {
|
|||||||
error={!formData.kategoriBeritaId ? "Pilih kategori" : undefined}
|
error={!formData.kategoriBeritaId ? "Pilih kategori" : undefined}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button onClick={handleSubmit}>Edit Berita</Button>
|
<Group justify="right">
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${colors["blue-button"]}, #4facfe)`,
|
||||||
|
color: "#fff",
|
||||||
|
boxShadow: "0 4px 15px rgba(79, 172, 254, 0.4)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import { Box, Button, Group, Image, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core';
|
||||||
import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
@@ -12,107 +11,146 @@ import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirma
|
|||||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||||
|
|
||||||
function DetailBerita() {
|
function DetailBerita() {
|
||||||
const beritaState = useProxy(stateDashboardBerita)
|
const beritaState = useProxy(stateDashboardBerita);
|
||||||
const [modalHapus, setModalHapus] = useState(false)
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
const params = useParams()
|
const params = useParams();
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
beritaState.berita.findUnique.load(params?.id as string)
|
beritaState.berita.findUnique.load(params?.id as string);
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
const handleHapus = () => {
|
const handleHapus = () => {
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
beritaState.berita.delete.byId(selectedId)
|
beritaState.berita.delete.byId(selectedId);
|
||||||
setModalHapus(false)
|
setModalHapus(false);
|
||||||
setSelectedId(null)
|
setSelectedId(null);
|
||||||
router.push("/admin/desa/berita/list-berita")
|
router.push("/admin/desa/berita/list-berita");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (!beritaState.berita.findUnique.data) {
|
if (!beritaState.berita.findUnique.data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
<Skeleton h={40} />
|
<Skeleton height={500} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = beritaState.berita.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box py={10}>
|
||||||
<Box mb={10}>
|
{/* Tombol Back */}
|
||||||
<Button variant="subtle" onClick={() => router.back()}>
|
<Button
|
||||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
variant="subtle"
|
||||||
</Button>
|
onClick={() => router.back()}
|
||||||
</Box>
|
leftSection={<IconArrowBack size={24} color={colors['blue-button']} />}
|
||||||
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
|
mb={15}
|
||||||
<Stack>
|
>
|
||||||
<Text fz={"xl"} fw={"bold"}>Detail Berita</Text>
|
Kembali
|
||||||
{beritaState.berita.findUnique.data ? (
|
</Button>
|
||||||
<Paper key={beritaState.berita.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
{/* Detail Berita */}
|
||||||
<Box>
|
<Paper
|
||||||
<Text fw={"bold"} fz={"lg"}>Kategori</Text>
|
withBorder
|
||||||
<Text fz={"lg"}>{beritaState.berita.findUnique.data?.kategoriBerita?.name}</Text>
|
w={{ base: "100%", md: "70%" }}
|
||||||
</Box>
|
bg={colors['white-1']}
|
||||||
<Box>
|
p="lg"
|
||||||
<Text fw={"bold"} fz={"lg"}>Judul</Text>
|
radius="md"
|
||||||
<Text fz={"lg"}>{beritaState.berita.findUnique.data?.judul}</Text>
|
shadow="sm"
|
||||||
</Box>
|
>
|
||||||
<Box>
|
<Stack gap="md">
|
||||||
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
|
<Text fz="2xl" fw="bold" c={colors['blue-button']}>
|
||||||
<Text fz={"lg"} >{beritaState.berita.findUnique.data?.deskripsi}</Text>
|
Detail Berita
|
||||||
</Box>
|
</Text>
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
|
<Paper bg="#ECEEF8" p="md" radius="md" shadow="xs">
|
||||||
<Image w={{ base: 150, md: 150, lg: 150 }} src={beritaState.berita.findUnique.data?.image?.link} alt="gambar" />
|
<Stack gap="sm">
|
||||||
</Box>
|
<Box>
|
||||||
<Box>
|
<Text fz="lg" fw="bold">Kategori</Text>
|
||||||
<Text fw={"bold"} fz={"lg"}>Konten</Text>
|
<Text fz="md" c="dimmed">{data.kategoriBerita?.name || '-'}</Text>
|
||||||
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: beritaState.berita.findUnique.data?.content }} />
|
</Box>
|
||||||
</Box>
|
|
||||||
<Flex gap={"xs"} mt={10}>
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Judul</Text>
|
||||||
|
<Text fz="md" c="dimmed">{data.judul || '-'}</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Deskripsi</Text>
|
||||||
|
<Text fz="md" c="dimmed">{data.deskripsi || '-'}</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Gambar</Text>
|
||||||
|
{data.image?.link ? (
|
||||||
|
<Image
|
||||||
|
src={data.image.link}
|
||||||
|
alt={data.judul || 'Gambar Berita'}
|
||||||
|
w={200}
|
||||||
|
h={200}
|
||||||
|
radius="md"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Text fz="sm" c="dimmed">Tidak ada gambar</Text>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Konten</Text>
|
||||||
|
<Text
|
||||||
|
fz="md"
|
||||||
|
c="dimmed"
|
||||||
|
dangerouslySetInnerHTML={{ __html: data.content || '-' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Action Button */}
|
||||||
|
<Group gap="sm">
|
||||||
|
<Tooltip label="Hapus Berita" withArrow position="top">
|
||||||
<Button
|
<Button
|
||||||
|
color="red"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (beritaState.berita.findUnique.data) {
|
setSelectedId(data.id);
|
||||||
setSelectedId(beritaState.berita.findUnique.data.id);
|
setModalHapus(true);
|
||||||
setModalHapus(true);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
disabled={beritaState.berita.delete.loading || !beritaState.berita.findUnique.data}
|
variant="light"
|
||||||
color={"red"}
|
radius="md"
|
||||||
|
size="md"
|
||||||
>
|
>
|
||||||
<IconX size={20} />
|
<IconTrash size={20} />
|
||||||
</Button>
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip label="Edit Berita" withArrow position="top">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
color="green"
|
||||||
if (beritaState.berita.findUnique.data) {
|
onClick={() => router.push(`/admin/desa/berita/list-berita/${data.id}/edit`)}
|
||||||
router.push(`/admin/desa/berita/list-berita/${beritaState.berita.findUnique.data.id}/edit`);
|
variant="light"
|
||||||
}
|
radius="md"
|
||||||
}}
|
size="md"
|
||||||
disabled={!beritaState.berita.findUnique.data}
|
|
||||||
color={"green"}
|
|
||||||
>
|
>
|
||||||
<IconEdit size={20} />
|
<IconEdit size={20} />
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Tooltip>
|
||||||
</Stack>
|
</Group>
|
||||||
</Paper>
|
</Stack>
|
||||||
) : null}
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Modal Konfirmasi Hapus */}
|
{/* Modal Hapus */}
|
||||||
<ModalKonfirmasiHapus
|
<ModalKonfirmasiHapus
|
||||||
opened={modalHapus}
|
opened={modalHapus}
|
||||||
onClose={() => setModalHapus(false)}
|
onClose={() => setModalHapus(false)}
|
||||||
onConfirm={handleHapus}
|
onConfirm={handleHapus}
|
||||||
text='Apakah anda yakin ingin menghapus berita ini?'
|
text="Apakah Anda yakin ingin menghapus berita ini?"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DetailBerita;
|
export default DetailBerita;
|
||||||
|
|||||||