Compare commits
14 Commits
nico/4-9-2
...
nico/22-se
| Author | SHA1 | Date | |
|---|---|---|---|
| b5c044df6e | |||
| 0fc47c28ff | |||
| 8e25c91e85 | |||
| 068d8b1077 | |||
| 9f72e94557 | |||
| 79ad39fc55 | |||
| 39e1e7b575 | |||
| 4ceea5203f | |||
| a5d841bb6b | |||
| 6a7bd386ae | |||
| a9d98895bb | |||
| 75475dc62e | |||
| b39800a475 | |||
| 797713ef49 |
2
.gitignore
vendored
@@ -48,3 +48,5 @@ next-env.d.ts
|
|||||||
|
|
||||||
.env.*
|
.env.*
|
||||||
|
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
|
|||||||
11
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"
|
||||||
@@ -75,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": {
|
||||||
|
|||||||
@@ -1,51 +1,99 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"month": "Jan",
|
"month": "Jan",
|
||||||
"year": 2025,
|
"year": 2025,
|
||||||
"totalUnemployment": 160,
|
"totalUnemployment": 160,
|
||||||
"educatedUnemployment": 95,
|
"educatedUnemployment": 95,
|
||||||
"uneducatedUnemployment": 65,
|
"uneducatedUnemployment": 65,
|
||||||
"percentageChange": null
|
"percentageChange": 0.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"month": "Feb",
|
"month": "Feb",
|
||||||
"year": 2025,
|
"year": 2025,
|
||||||
"totalUnemployment": 155,
|
"totalUnemployment": 158,
|
||||||
"educatedUnemployment": 90,
|
"educatedUnemployment": 93,
|
||||||
"uneducatedUnemployment": 65,
|
"uneducatedUnemployment": 65,
|
||||||
"percentageChange": -3.1
|
"percentageChange": -1.25
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"month": "Mar",
|
"month": "Mar",
|
||||||
"year": 2025,
|
"year": 2025,
|
||||||
"totalUnemployment": 150,
|
"totalUnemployment": 155,
|
||||||
"educatedUnemployment": 88,
|
"educatedUnemployment": 91,
|
||||||
"uneducatedUnemployment": 62,
|
"uneducatedUnemployment": 64,
|
||||||
"percentageChange": -3.2
|
"percentageChange": -1.90
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"month": "Apr",
|
"month": "Apr",
|
||||||
"year": 2025,
|
"year": 2025,
|
||||||
"totalUnemployment": 148,
|
"totalUnemployment": 152,
|
||||||
"educatedUnemployment": 85,
|
"educatedUnemployment": 89,
|
||||||
"uneducatedUnemployment": 63,
|
"uneducatedUnemployment": 63,
|
||||||
"percentageChange": -1.3
|
"percentageChange": -1.94
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"month": "Mei",
|
"month": "Mei",
|
||||||
"year": 2025,
|
"year": 2025,
|
||||||
"totalUnemployment": 145,
|
"totalUnemployment": 150,
|
||||||
"educatedUnemployment": 82,
|
"educatedUnemployment": 88,
|
||||||
"uneducatedUnemployment": 63,
|
"uneducatedUnemployment": 62,
|
||||||
"percentageChange": -2.0
|
"percentageChange": -1.32
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"month": "Jun",
|
"month": "Jun",
|
||||||
"year": 2025,
|
"year": 2025,
|
||||||
"totalUnemployment": 140,
|
"totalUnemployment": 148,
|
||||||
"educatedUnemployment": 80,
|
"educatedUnemployment": 87,
|
||||||
"uneducatedUnemployment": 60,
|
"uneducatedUnemployment": 61,
|
||||||
"percentageChange": -3.4
|
"percentageChange": -1.33
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
|
"month": "Jul",
|
||||||
|
"year": 2025,
|
||||||
|
"totalUnemployment": 145,
|
||||||
|
"educatedUnemployment": 85,
|
||||||
|
"uneducatedUnemployment": 60,
|
||||||
|
"percentageChange": -2.03
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "Agu",
|
||||||
|
"year": 2025,
|
||||||
|
"totalUnemployment": 142,
|
||||||
|
"educatedUnemployment": 84,
|
||||||
|
"uneducatedUnemployment": 58,
|
||||||
|
"percentageChange": -2.07
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "Sep",
|
||||||
|
"year": 2025,
|
||||||
|
"totalUnemployment": 140,
|
||||||
|
"educatedUnemployment": 83,
|
||||||
|
"uneducatedUnemployment": 57,
|
||||||
|
"percentageChange": -1.41
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "Okt",
|
||||||
|
"year": 2025,
|
||||||
|
"totalUnemployment": 138,
|
||||||
|
"educatedUnemployment": 82,
|
||||||
|
"uneducatedUnemployment": 56,
|
||||||
|
"percentageChange": -1.43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "Nov",
|
||||||
|
"year": 2025,
|
||||||
|
"totalUnemployment": 135,
|
||||||
|
"educatedUnemployment": 80,
|
||||||
|
"uneducatedUnemployment": 55,
|
||||||
|
"percentageChange": -2.17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "Des",
|
||||||
|
"year": 2025,
|
||||||
|
"totalUnemployment": 132,
|
||||||
|
"educatedUnemployment": 78,
|
||||||
|
"uneducatedUnemployment": 54,
|
||||||
|
"percentageChange": -2.22
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
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"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -81,8 +81,6 @@ model FileStorage {
|
|||||||
PelayananSuratKeteranganImage PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage")
|
PelayananSuratKeteranganImage PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage")
|
||||||
PelayananSuratKeteranganImage2 PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage2")
|
PelayananSuratKeteranganImage2 PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage2")
|
||||||
PasarDesa PasarDesa[]
|
PasarDesa PasarDesa[]
|
||||||
KontakDaruratKeamanan KontakDaruratKeamanan[]
|
|
||||||
KontakItem KontakItem[]
|
|
||||||
Pegawai Pegawai[]
|
Pegawai Pegawai[]
|
||||||
DesaDigital DesaDigital[]
|
DesaDigital DesaDigital[]
|
||||||
InfoTekno InfoTekno[]
|
InfoTekno InfoTekno[]
|
||||||
@@ -92,7 +90,7 @@ 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[]
|
||||||
@@ -101,6 +99,8 @@ model FileStorage {
|
|||||||
PerbekelDariMasaKeMasa PerbekelDariMasaKeMasa[]
|
PerbekelDariMasaKeMasa PerbekelDariMasaKeMasa[]
|
||||||
|
|
||||||
MitraKolaborasi MitraKolaborasi[]
|
MitraKolaborasi MitraKolaborasi[]
|
||||||
|
|
||||||
|
ArtikelKesehatan ArtikelKesehatan[]
|
||||||
}
|
}
|
||||||
|
|
||||||
//========================================= MENU LANDING PAGE ========================================= //
|
//========================================= MENU LANDING PAGE ========================================= //
|
||||||
@@ -168,9 +168,9 @@ 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
|
||||||
jumlah String
|
jumlah String
|
||||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||||
imageId String?
|
imageId String?
|
||||||
@@ -183,7 +183,7 @@ model SDGSDesa {
|
|||||||
//========================================= APBDes ========================================= //
|
//========================================= APBDes ========================================= //
|
||||||
model APBDes {
|
model APBDes {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String @unique
|
name String
|
||||||
jumlah String
|
jumlah String
|
||||||
image FileStorage? @relation("APBDesImage", fields: [imageId], references: [id])
|
image FileStorage? @relation("APBDesImage", fields: [imageId], references: [id])
|
||||||
imageId String?
|
imageId String?
|
||||||
@@ -198,7 +198,7 @@ model APBDes {
|
|||||||
//========================================= PRESTASI DESA ========================================= //
|
//========================================= PRESTASI DESA ========================================= //
|
||||||
model PrestasiDesa {
|
model PrestasiDesa {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String @unique
|
name String
|
||||||
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
|
||||||
@@ -972,8 +972,10 @@ model ArtikelKesehatan {
|
|||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
title String
|
title String
|
||||||
content String
|
content String
|
||||||
introduction Introduction @relation(fields: [introductionId], references: [id])
|
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||||
|
imageId String?
|
||||||
introductionId String
|
introductionId String
|
||||||
|
introduction Introduction @relation(fields: [introductionId], references: [id])
|
||||||
symptom Symptom @relation(fields: [symptomId], references: [id])
|
symptom Symptom @relation(fields: [symptomId], references: [id])
|
||||||
symptomId String
|
symptomId String
|
||||||
prevention Prevention @relation(fields: [preventionId], references: [id])
|
prevention Prevention @relation(fields: [preventionId], references: [id])
|
||||||
@@ -1216,71 +1218,51 @@ 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])
|
icon String
|
||||||
imageId String?
|
kategori KontakItem @relation(fields: [kategoriId], references: [id])
|
||||||
kontakItems KontakItem[]
|
kategoriId String
|
||||||
createdAt DateTime @default(now())
|
kontakItems KontakDaruratToItem[]
|
||||||
updatedAt DateTime @updatedAt
|
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])
|
icon String
|
||||||
imageId String?
|
createdAt DateTime @default(now())
|
||||||
kategori KontakDaruratKeamanan @relation(fields: [kategoriId], references: [id])
|
updatedAt DateTime @updatedAt
|
||||||
kategoriId String
|
deletedAt DateTime @default(now())
|
||||||
createdAt DateTime @default(now())
|
isActive Boolean @default(true)
|
||||||
updatedAt DateTime @updatedAt
|
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 ========================================= //
|
||||||
model PencegahanKriminalitas {
|
model PencegahanKriminalitas {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
programKeamanan ProgramKeamanan @relation(fields: [programKeamananId], references: [id])
|
judul String
|
||||||
programKeamananId String
|
deskripsi String
|
||||||
tipsKeamanan TipsKeamanan @relation(fields: [tipsKeamananId], references: [id])
|
deskripsiSingkat String
|
||||||
tipsKeamananId String
|
linkVideo String
|
||||||
videoKeamanan VideoKeamanan @relation(fields: [videoKeamananId], references: [id])
|
createdAt DateTime @default(now())
|
||||||
videoKeamananId 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 ProgramKeamanan {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
nama String // contoh: "Ronda Malam"
|
|
||||||
deskripsi String? // jika mau tambahkan info detail
|
|
||||||
slug String @unique
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
PencegahanKriminalitas PencegahanKriminalitas[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model TipsKeamanan {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
judul String
|
|
||||||
konten String
|
|
||||||
slug String
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
PencegahanKriminalitas PencegahanKriminalitas[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model VideoKeamanan {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
judul String
|
|
||||||
deskripsi String?
|
|
||||||
videoUrl String // link youtube atau embed url
|
|
||||||
slug String
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
PencegahanKriminalitas PencegahanKriminalitas[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================= LAPORAN PUBLIK ========================================= //
|
// ========================================= LAPORAN PUBLIK ========================================= //
|
||||||
@@ -1289,11 +1271,13 @@ model LaporanPublik {
|
|||||||
judul String
|
judul String
|
||||||
lokasi String
|
lokasi String
|
||||||
tanggalWaktu DateTime
|
tanggalWaktu DateTime
|
||||||
status StatusLaporan
|
status StatusLaporan @default(Proses)
|
||||||
penanganan PenangananLaporanPublik[]
|
penanganan PenangananLaporanPublik[]
|
||||||
kronologi String? // Optional, bisa diisi detail kronologi
|
kronologi String? // Optional, bisa diisi detail kronologi
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
model PenangananLaporanPublik {
|
model PenangananLaporanPublik {
|
||||||
@@ -1401,6 +1385,9 @@ model PosisiOrganisasi {
|
|||||||
pegawai Pegawai[]
|
pegawai Pegawai[]
|
||||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||||
StrukturOrganisasiPPID StrukturOrganisasiPPID[]
|
StrukturOrganisasiPPID StrukturOrganisasiPPID[]
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
@@map("posisi_organisasi")
|
@@map("posisi_organisasi")
|
||||||
}
|
}
|
||||||
@@ -1469,7 +1456,7 @@ model ProgramKemiskinan {
|
|||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
nama String
|
nama String
|
||||||
deskripsi String
|
deskripsi String
|
||||||
ikonUrl String?
|
icon String
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
statistikId String? @unique
|
statistikId String? @unique
|
||||||
statistik StatistikKemiskinan? @relation(fields: [statistikId], references: [id])
|
statistik StatistikKemiskinan? @relation(fields: [statistikId], references: [id])
|
||||||
@@ -1551,7 +1538,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 DateTime
|
year Int
|
||||||
totalUnemployment Int
|
totalUnemployment Int
|
||||||
educatedUnemployment Int
|
educatedUnemployment Int
|
||||||
uneducatedUnemployment Int
|
uneducatedUnemployment Int
|
||||||
@@ -2106,18 +2093,18 @@ model KategoriBuku {
|
|||||||
// ========================================= USER ========================================= //
|
// ========================================= USER ========================================= //
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
username String
|
username String
|
||||||
nomor String @unique
|
nomor String @unique
|
||||||
role Role @relation(fields: [roleId], references: [id])
|
role Role @relation(fields: [roleId], references: [id])
|
||||||
roleId String @default("1")
|
roleId String @default("1")
|
||||||
instansi String?
|
instansi String?
|
||||||
UserSession UserSession? // Nama instansi (Puskesmas, Sekolah, dll)
|
UserSession UserSession? // Nama instansi (Puskesmas, Sekolah, dll)
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
lastLogin DateTime?
|
lastLogin DateTime?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime?
|
deletedAt DateTime?
|
||||||
}
|
}
|
||||||
|
|
||||||
model Role {
|
model Role {
|
||||||
|
|||||||
216
prisma/seed.ts
@@ -1,3 +1,4 @@
|
|||||||
|
/* 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 programInovasi from "./data/landing-page/profile/programInovasi.json";
|
import programInovasi from "./data/landing-page/profile/programInovasi.json";
|
||||||
@@ -15,7 +16,7 @@ import posisiOrganisasiPPID from "./data/ppid/struktur-ppid/posisi-organisasi-PP
|
|||||||
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 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 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 categoryPengumuman from "./data/category-pengumuman.json";
|
||||||
@@ -54,63 +55,90 @@ import programUnggulan from "./data/pendidikan/program-pendidikan-anak/program-u
|
|||||||
import tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-program.json";
|
import tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-program.json";
|
||||||
import roles from "./data/user/roles.json";
|
import roles from "./data/user/roles.json";
|
||||||
import users from "./data/user/users.json";
|
import users from "./data/user/users.json";
|
||||||
|
import fileStorage from "./data/file-storage.json";
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
// =========== USER & ROLE ===========
|
// =========== USER & ROLE ===========
|
||||||
// In your seed.ts
|
// In your seed.ts
|
||||||
// =========== ROLES ===========
|
// =========== ROLES ===========
|
||||||
console.log("🔄 Seeding roles...");
|
console.log("🔄 Seeding roles...");
|
||||||
for (const r of roles) {
|
for (const r of roles) {
|
||||||
await prisma.role.upsert({
|
await prisma.role.upsert({
|
||||||
where: { id: r.id },
|
where: { id: r.id },
|
||||||
update: {
|
update: {
|
||||||
name: r.name,
|
name: r.name,
|
||||||
description: r.description,
|
description: r.description,
|
||||||
permissions: r.permissions,
|
permissions: r.permissions,
|
||||||
isActive: r.isActive,
|
isActive: r.isActive,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
id: r.id,
|
id: r.id,
|
||||||
name: r.name,
|
name: r.name,
|
||||||
description: r.description,
|
description: r.description,
|
||||||
permissions: r.permissions,
|
permissions: r.permissions,
|
||||||
isActive: r.isActive,
|
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;
|
|
||||||
}
|
}
|
||||||
|
console.log("✅ Roles seeded");
|
||||||
|
|
||||||
await prisma.user.upsert({
|
// =========== USERS ===========
|
||||||
where: { id: u.id },
|
console.log("🔄 Seeding users...");
|
||||||
update: {
|
for (const u of users) {
|
||||||
username: u.nama,
|
// First verify the role exists
|
||||||
nomor: u.nomor,
|
const roleExists = await prisma.role.findUnique({
|
||||||
roleId: u.roleId,
|
where: { id: u.roleId },
|
||||||
isActive: u.isActive,
|
});
|
||||||
},
|
|
||||||
create: {
|
if (!roleExists) {
|
||||||
id: u.id,
|
console.error(`❌ Role with id ${u.roleId} not found for user ${u.nama}`);
|
||||||
username: u.nama,
|
continue;
|
||||||
nomor: u.nomor,
|
}
|
||||||
roleId: u.roleId,
|
|
||||||
isActive: u.isActive,
|
await prisma.user.upsert({
|
||||||
},
|
where: { id: u.id },
|
||||||
});
|
update: {
|
||||||
}
|
username: u.nama,
|
||||||
console.log("✅ Users seeded");
|
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 ===========
|
||||||
// =========== SUBMENU PROFILE ===========
|
// =========== SUBMENU PROFILE ===========
|
||||||
// =========== PROFILE PEJABAT DESA ===========
|
// =========== PROFILE PEJABAT DESA ===========
|
||||||
@@ -120,11 +148,13 @@ console.log("✅ Users seeded");
|
|||||||
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,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -134,18 +164,35 @@ console.log("✅ Users seeded");
|
|||||||
|
|
||||||
// =========== 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,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -158,11 +205,13 @@ console.log("✅ Users seeded");
|
|||||||
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,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -308,11 +357,8 @@ console.log("✅ Users seeded");
|
|||||||
|
|
||||||
// =========== 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,
|
||||||
@@ -330,8 +376,7 @@ console.log("✅ Users seeded");
|
|||||||
for (const l of apbdes) {
|
for (const l of apbdes) {
|
||||||
await prisma.aPBDes.upsert({
|
await prisma.aPBDes.upsert({
|
||||||
where: {
|
where: {
|
||||||
name: l.name,
|
id: l.id,
|
||||||
jumlah: l.jumlah,
|
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
name: l.name,
|
name: l.name,
|
||||||
@@ -558,29 +603,29 @@ console.log("✅ Users seeded");
|
|||||||
}
|
}
|
||||||
console.log("dasar hukum PPID success ...");
|
console.log("dasar hukum PPID success ...");
|
||||||
|
|
||||||
// =========== SUBMENU DAFTAR INFORMASI PUBLIK PPID ===========
|
// =========== SUBMENU DAFTAR INFORMASI PUBLIK PPID ===========
|
||||||
for (const v of daftarInformasiPublik) {
|
for (const v of daftarInformasiPublik) {
|
||||||
// Convert string date to Date object
|
// Convert string date to Date object
|
||||||
const tanggal = new Date(v.tanggal);
|
const tanggal = new Date(v.tanggal);
|
||||||
|
|
||||||
await prisma.daftarInformasiPublik.upsert({
|
await prisma.daftarInformasiPublik.upsert({
|
||||||
where: {
|
where: {
|
||||||
id: v.id,
|
id: v.id,
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
jenisInformasi: v.jenisInformasi,
|
jenisInformasi: v.jenisInformasi,
|
||||||
deskripsi: v.deskripsi,
|
deskripsi: v.deskripsi,
|
||||||
tanggal: tanggal,
|
tanggal: tanggal,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
id: v.id,
|
id: v.id,
|
||||||
jenisInformasi: v.jenisInformasi,
|
jenisInformasi: v.jenisInformasi,
|
||||||
deskripsi: v.deskripsi,
|
deskripsi: v.deskripsi,
|
||||||
tanggal: tanggal,
|
tanggal: tanggal,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log("daftar informasi publik PPID success ...");
|
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({
|
||||||
@@ -849,12 +894,9 @@ console.log("✅ Users seeded");
|
|||||||
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: yearAsDate },
|
month_year: { month: d.month, year: d.year },
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
totalUnemployment: d.totalUnemployment,
|
totalUnemployment: d.totalUnemployment,
|
||||||
@@ -864,7 +906,7 @@ console.log("✅ Users seeded");
|
|||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
month: d.month,
|
month: d.month,
|
||||||
year: yearAsDate,
|
year: d.year,
|
||||||
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 After Width: | Height: | Size: 266 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}
|
||||||
|
|||||||
100
src/app/admin/(dashboard)/_com/iconMap.tsx
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
IconLeaf,
|
||||||
|
IconTrophy,
|
||||||
|
IconTent,
|
||||||
|
IconChartLine,
|
||||||
|
IconRecycle,
|
||||||
|
IconTruck,
|
||||||
|
IconScale,
|
||||||
|
IconClipboard,
|
||||||
|
IconTrash,
|
||||||
|
IconHomeEco,
|
||||||
|
IconChristmasTreeFilled,
|
||||||
|
IconTrendingUp,
|
||||||
|
IconShieldFilled,
|
||||||
|
IconHome,
|
||||||
|
IconTree,
|
||||||
|
IconDroplet,
|
||||||
|
IconCash,
|
||||||
|
IconSchool,
|
||||||
|
IconShoppingCart,
|
||||||
|
IconHospital,
|
||||||
|
IconAmbulance,
|
||||||
|
IconFiretruck,
|
||||||
|
IconBuilding,
|
||||||
|
IconAlertTriangle,
|
||||||
|
} from '@tabler/icons-react'
|
||||||
|
|
||||||
|
export type IconKey =
|
||||||
|
| 'ekowisata'
|
||||||
|
| 'kompetisi'
|
||||||
|
| 'wisata'
|
||||||
|
| 'ekonomi'
|
||||||
|
| 'sampah'
|
||||||
|
| 'truck'
|
||||||
|
| 'scale'
|
||||||
|
| 'clipboard'
|
||||||
|
| 'trash'
|
||||||
|
| 'lingkunganSehat'
|
||||||
|
| 'sumberOksigen'
|
||||||
|
| 'ekonomiBerkelanjutan'
|
||||||
|
| 'mencegahBencana'
|
||||||
|
| 'rumah'
|
||||||
|
| 'pohon'
|
||||||
|
| 'air'
|
||||||
|
| 'bantuan'
|
||||||
|
| 'pelatihan'
|
||||||
|
| 'subsidi'
|
||||||
|
| 'layananKesehatan'
|
||||||
|
| 'polisi'
|
||||||
|
| 'ambulans'
|
||||||
|
| 'pemadam'
|
||||||
|
| 'rumahSakit'
|
||||||
|
| 'bangunan'
|
||||||
|
| 'darurat'
|
||||||
|
|
||||||
|
|
||||||
|
const iconMap: Record<IconKey, React.FC<any>> = {
|
||||||
|
ekowisata: IconLeaf,
|
||||||
|
kompetisi: IconTrophy,
|
||||||
|
wisata: IconTent,
|
||||||
|
ekonomi: IconChartLine,
|
||||||
|
sampah: IconRecycle,
|
||||||
|
truck: IconTruck,
|
||||||
|
scale: IconScale,
|
||||||
|
clipboard: IconClipboard,
|
||||||
|
trash: IconTrash,
|
||||||
|
lingkunganSehat: IconHomeEco,
|
||||||
|
sumberOksigen: IconChristmasTreeFilled,
|
||||||
|
ekonomiBerkelanjutan: IconTrendingUp,
|
||||||
|
mencegahBencana: IconShieldFilled,
|
||||||
|
rumah: IconHome,
|
||||||
|
pohon: IconTree,
|
||||||
|
air: IconDroplet,
|
||||||
|
bantuan: IconCash,
|
||||||
|
pelatihan: IconSchool,
|
||||||
|
subsidi: IconShoppingCart,
|
||||||
|
layananKesehatan: IconHospital,
|
||||||
|
polisi: IconShieldFilled,
|
||||||
|
ambulans: IconAmbulance,
|
||||||
|
pemadam: IconFiretruck,
|
||||||
|
rumahSakit: IconHospital,
|
||||||
|
bangunan: IconBuilding,
|
||||||
|
darurat: IconAlertTriangle
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
name: IconKey
|
||||||
|
size?: number
|
||||||
|
color?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IconMapper: React.FC<Props> = ({ name, size = 24, color }) => {
|
||||||
|
const IconComponent = iconMap[name]
|
||||||
|
if (!IconComponent) return null
|
||||||
|
return <IconComponent size={size} color={color} />
|
||||||
|
}
|
||||||
@@ -3,16 +3,24 @@
|
|||||||
|
|
||||||
import { Box, rem, Select } from '@mantine/core';
|
import { Box, rem, Select } from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
|
IconAlertTriangle,
|
||||||
|
IconAmbulance,
|
||||||
|
IconBuilding,
|
||||||
|
IconCash,
|
||||||
IconChartLine,
|
IconChartLine,
|
||||||
IconChristmasTreeFilled,
|
IconChristmasTreeFilled,
|
||||||
IconClipboardTextFilled,
|
IconClipboardTextFilled,
|
||||||
IconDroplet,
|
IconDroplet,
|
||||||
|
IconFiretruck,
|
||||||
IconHome,
|
IconHome,
|
||||||
IconHomeEco,
|
IconHomeEco,
|
||||||
|
IconHospital,
|
||||||
IconLeaf,
|
IconLeaf,
|
||||||
IconRecycle,
|
IconRecycle,
|
||||||
IconScale,
|
IconScale,
|
||||||
|
IconSchool,
|
||||||
IconShieldFilled,
|
IconShieldFilled,
|
||||||
|
IconShoppingCart,
|
||||||
IconTent,
|
IconTent,
|
||||||
IconTrashFilled,
|
IconTrashFilled,
|
||||||
IconTree,
|
IconTree,
|
||||||
@@ -32,13 +40,23 @@ const iconMap = {
|
|||||||
scale: { label: 'Scale', icon: IconScale },
|
scale: { label: 'Scale', icon: IconScale },
|
||||||
clipboard: { label: 'Clipboard', icon: IconClipboardTextFilled },
|
clipboard: { label: 'Clipboard', icon: IconClipboardTextFilled },
|
||||||
trash: { label: 'Trash', icon: IconTrashFilled },
|
trash: { label: 'Trash', icon: IconTrashFilled },
|
||||||
lingkunganSehat: {label: 'Lingkungan Sehat', icon: IconHomeEco},
|
lingkunganSehat: { label: 'Lingkungan Sehat', icon: IconHomeEco },
|
||||||
sumberOksigen: {label: 'Sumber Oksigen', icon: IconChristmasTreeFilled},
|
sumberOksigen: { label: 'Sumber Oksigen', icon: IconChristmasTreeFilled },
|
||||||
ekonomiBerkelanjutan: {label: 'Ekonomi Berkelanjutan', icon: IconTrendingUp},
|
ekonomiBerkelanjutan: { label: 'Ekonomi Berkelanjutan', icon: IconTrendingUp },
|
||||||
mencegahBencana: {label: 'Mencegah Bencana', icon: IconShieldFilled},
|
mencegahBencana: { label: 'Mencegah Bencana', icon: IconShieldFilled },
|
||||||
rumah: {label: 'Rumah', icon: IconHome},
|
rumah: { label: 'Rumah', icon: IconHome },
|
||||||
pohon: {label: 'Pohon', icon: IconTree},
|
pohon: { label: 'Pohon', icon: IconTree },
|
||||||
air: {label: 'Air', icon: IconDroplet}
|
air: { label: 'Air', icon: IconDroplet },
|
||||||
|
bantuan: { label: 'Bantuan', icon: IconCash },
|
||||||
|
pelatihan: { label: 'Pelatihan', icon: IconSchool },
|
||||||
|
subsidi: { label: 'Subsidi', icon: IconShoppingCart },
|
||||||
|
layananKesehatan: { label: 'Layanan Kesehatan', icon: IconHospital },
|
||||||
|
polisi: { label: 'Polisi', icon: IconShieldFilled },
|
||||||
|
ambulans: { label: 'Ambulans', icon: IconAmbulance },
|
||||||
|
pemadam: { label: 'Pemadam', icon: IconFiretruck },
|
||||||
|
rumahSakit: { label: 'Rumah Sakit', icon: IconHospital },
|
||||||
|
bangunan: { label: 'Bangunan', icon: IconBuilding },
|
||||||
|
darurat: { label: 'Darurat', icon: IconAlertTriangle },
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,22 +2,30 @@
|
|||||||
|
|
||||||
import { Box, rem, Select } from '@mantine/core';
|
import { Box, rem, Select } from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
|
IconAmbulance,
|
||||||
|
IconCash,
|
||||||
IconChartLine,
|
IconChartLine,
|
||||||
IconChristmasTreeFilled,
|
IconChristmasTreeFilled,
|
||||||
IconClipboardTextFilled,
|
IconClipboardTextFilled,
|
||||||
IconDroplet,
|
IconDroplet,
|
||||||
|
IconFiretruck,
|
||||||
IconHome,
|
IconHome,
|
||||||
IconHomeEco,
|
IconHomeEco,
|
||||||
|
IconHospital,
|
||||||
IconLeaf,
|
IconLeaf,
|
||||||
IconRecycle,
|
IconRecycle,
|
||||||
IconScale,
|
IconScale,
|
||||||
|
IconSchool,
|
||||||
IconShieldFilled,
|
IconShieldFilled,
|
||||||
|
IconShoppingCart,
|
||||||
IconTent,
|
IconTent,
|
||||||
IconTrashFilled,
|
IconTrashFilled,
|
||||||
IconTree,
|
IconTree,
|
||||||
IconTrendingUp,
|
IconTrendingUp,
|
||||||
IconTrophy,
|
IconTrophy,
|
||||||
IconTruckFilled,
|
IconTruckFilled,
|
||||||
|
IconBuilding,
|
||||||
|
IconAlertTriangle
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
|
|
||||||
const iconMap = {
|
const iconMap = {
|
||||||
@@ -36,7 +44,17 @@ const iconMap = {
|
|||||||
mencegahBencana: {label: 'Mencegah Bencana', icon: IconShieldFilled},
|
mencegahBencana: {label: 'Mencegah Bencana', icon: IconShieldFilled},
|
||||||
rumah: {label: 'Rumah', icon: IconHome},
|
rumah: {label: 'Rumah', icon: IconHome},
|
||||||
pohon: {label: 'Pohon', icon: IconTree},
|
pohon: {label: 'Pohon', icon: IconTree},
|
||||||
air: {label: 'Air', icon: IconDroplet}
|
air: {label: 'Air', icon: IconDroplet},
|
||||||
|
bantuan: {label: 'Bantuan', icon: IconCash},
|
||||||
|
pelatihan: {label: 'Pelatihan', icon: IconSchool},
|
||||||
|
subsidi: {label: 'Subsidi', icon: IconShoppingCart},
|
||||||
|
layananKesehatan: {label: 'Layanan Kesehatan', icon: IconHospital},
|
||||||
|
polisi: {label: 'Polisi', icon: IconShieldFilled},
|
||||||
|
ambulans: {label: 'Ambulans', icon: IconAmbulance},
|
||||||
|
pemadam: {label: 'Pemadam', icon: IconFiretruck},
|
||||||
|
rumahSakit: {label: 'Rumah Sakit', icon: IconHospital},
|
||||||
|
bangunan: {label: 'Bangunan', icon: IconBuilding},
|
||||||
|
darurat: {label: 'Darurat', icon: IconAlertTriangle},
|
||||||
};
|
};
|
||||||
|
|
||||||
type IconKey = keyof typeof iconMap;
|
type IconKey = keyof typeof iconMap;
|
||||||
|
|||||||
@@ -7,9 +7,13 @@ import { z } from "zod";
|
|||||||
|
|
||||||
const templateApbDesa = z.object({
|
const templateApbDesa = z.object({
|
||||||
tahun: z.number().min(4, "Tahun minimal 4 karakter"),
|
tahun: z.number().min(4, "Tahun minimal 4 karakter"),
|
||||||
pembiayaanIds: z.array(z.string().uuid()).nonempty("Pilih minimal 1 pembiayaan"),
|
pembiayaanIds: z
|
||||||
|
.array(z.string().uuid())
|
||||||
|
.nonempty("Pilih minimal 1 pembiayaan"),
|
||||||
belanjaIds: z.array(z.string().uuid()).nonempty("Pilih minimal 1 belanja"),
|
belanjaIds: z.array(z.string().uuid()).nonempty("Pilih minimal 1 belanja"),
|
||||||
pendapatanIds: z.array(z.string().uuid()).nonempty("Pilih minimal 1 pendapatan"),
|
pendapatanIds: z
|
||||||
|
.array(z.string().uuid())
|
||||||
|
.nonempty("Pilih minimal 1 pendapatan"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const ApbDesaDefaultForm = {
|
const ApbDesaDefaultForm = {
|
||||||
@@ -54,32 +58,46 @@ const ApbDesa = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.ApbDesaGetPayload<{
|
| Prisma.ApbDesaGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
pendapatan: true;
|
pendapatan: true;
|
||||||
belanja: true;
|
belanja: true;
|
||||||
pembiayaan: true;
|
pembiayaan: true;
|
||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
ApbDesa.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
ApbDesa.findMany.page = page;
|
||||||
|
ApbDesa.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.loading = true;
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.apbdesa[
|
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.apbdesa[
|
||||||
"find-many"
|
"find-many"
|
||||||
].get();
|
].get({ query });
|
||||||
if (res.status === 200) {
|
|
||||||
this.data = res.data?.data ?? [];
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
ApbDesa.findMany.data = res.data.data ?? [];
|
||||||
|
ApbDesa.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
} else {
|
} else {
|
||||||
toast.error(res.data?.message || "Gagal mengambil APB Desa");
|
ApbDesa.findMany.data = [];
|
||||||
|
ApbDesa.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("Find many error:", error);
|
console.error("Gagal fetch APB Desa paginated:", err);
|
||||||
toast.error("Gagal mengambil APB Desa");
|
ApbDesa.findMany.data = [];
|
||||||
|
ApbDesa.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
ApbDesa.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -106,13 +124,13 @@ const ApbDesa = proxy({
|
|||||||
throw new Error("Gagal mengambil APB Desa");
|
throw new Error("Gagal mengambil APB Desa");
|
||||||
}
|
}
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(result.message || "Gagal memuat APB Desa");
|
throw new Error(result.message || "Gagal memuat APB Desa");
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = result.data;
|
const data = result.data;
|
||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.form = {
|
this.form = {
|
||||||
tahun: data.tahun || 0,
|
tahun: data.tahun || 0,
|
||||||
@@ -120,7 +138,7 @@ const ApbDesa = proxy({
|
|||||||
belanjaIds: data.belanja?.map((b: any) => b.id) || [],
|
belanjaIds: data.belanja?.map((b: any) => b.id) || [],
|
||||||
pembiayaanIds: data.pembiayaan?.map((p: any) => p.id) || [],
|
pembiayaanIds: data.pembiayaan?.map((p: any) => p.id) || [],
|
||||||
};
|
};
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading APB Desa:", error);
|
console.error("Error loading APB Desa:", error);
|
||||||
@@ -189,7 +207,7 @@ const ApbDesa = proxy({
|
|||||||
data: null as Prisma.ApbDesaGetPayload<{
|
data: null as Prisma.ApbDesaGetPayload<{
|
||||||
include: { pendapatan: true; belanja: true; pembiayaan: true };
|
include: { pendapatan: true; belanja: true; pembiayaan: true };
|
||||||
}> | null,
|
}> | null,
|
||||||
|
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@@ -199,11 +217,11 @@ const ApbDesa = proxy({
|
|||||||
throw new Error("Gagal mengambil detail APB Desa");
|
throw new Error("Gagal mengambil detail APB Desa");
|
||||||
}
|
}
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(result.message || "Gagal mengambil data");
|
throw new Error(result.message || "Gagal mengambil data");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data = result.data; // ✅ fix utama di sini
|
this.data = result.data; // ✅ fix utama di sini
|
||||||
return result.data;
|
return result.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -264,34 +282,32 @@ const pendapatan = proxy({
|
|||||||
data: null as any[] | null,
|
data: null as any[] | null,
|
||||||
page: 1,
|
page: 1,
|
||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => {
|
search: "",
|
||||||
// Change to arrow function
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
pendapatan.findMany.loading = true; // Use the full path to access the property
|
pendapatan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
pendapatan.findMany.page = page;
|
pendapatan.findMany.page = page;
|
||||||
|
pendapatan.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res =
|
const query: any = { page, limit };
|
||||||
await ApiFetch.api.ekonomi.pendapatanaslidesa.pendapatanasli[
|
if (search) query.search = search;
|
||||||
"find-many"
|
|
||||||
].get({
|
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.pendapatanasli[
|
||||||
query: { page, limit },
|
"find-many"
|
||||||
});
|
].get({ query });
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
pendapatan.findMany.data = res.data.data || [];
|
pendapatan.findMany.data = res.data.data ?? [];
|
||||||
pendapatan.findMany.total = res.data.total || 0;
|
pendapatan.findMany.totalPages =
|
||||||
pendapatan.findMany.totalPages = res.data.totalPages || 1;
|
res.data.totalPages ?? 1;
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to load pendapatan:", res.data?.message);
|
|
||||||
pendapatan.findMany.data = [];
|
pendapatan.findMany.data = [];
|
||||||
pendapatan.findMany.total = 0;
|
|
||||||
pendapatan.findMany.totalPages = 1;
|
pendapatan.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("Error loading pendapatan:", error);
|
console.error("Gagal fetch pendapatan asli desa paginated:", err);
|
||||||
pendapatan.findMany.data = [];
|
pendapatan.findMany.data = [];
|
||||||
pendapatan.findMany.total = 0;
|
|
||||||
pendapatan.findMany.totalPages = 1;
|
pendapatan.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
pendapatan.findMany.loading = false;
|
pendapatan.findMany.loading = false;
|
||||||
@@ -308,12 +324,15 @@ const pendapatan = proxy({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/pendapatanasli/${id}`, {
|
const response = await fetch(
|
||||||
method: "GET",
|
`/api/ekonomi/pendapatanaslidesa/pendapatanasli/${id}`,
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
method: "GET",
|
||||||
},
|
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}`);
|
||||||
}
|
}
|
||||||
@@ -349,16 +368,19 @@ const pendapatan = proxy({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
pendapatan.update.loading = true;
|
pendapatan.update.loading = true;
|
||||||
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/pendapatanasli/${this.id}`, {
|
const response = await fetch(
|
||||||
method: "PUT",
|
`/api/ekonomi/pendapatanaslidesa/pendapatanasli/${this.id}`,
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
method: "PUT",
|
||||||
},
|
headers: {
|
||||||
body: JSON.stringify({
|
"Content-Type": "application/json",
|
||||||
name: this.form.name,
|
},
|
||||||
value: this.form.value,
|
body: JSON.stringify({
|
||||||
}),
|
name: this.form.name,
|
||||||
});
|
value: this.form.value,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -495,23 +517,37 @@ const belanja = proxy({
|
|||||||
name: string;
|
name: string;
|
||||||
value: number;
|
value: number;
|
||||||
}>,
|
}>,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
belanja.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
belanja.findMany.page = page;
|
||||||
|
belanja.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.loading = true;
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.belanja[
|
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.belanja[
|
||||||
"find-many"
|
"find-many"
|
||||||
].get();
|
].get({ query });
|
||||||
if (res.status === 200) {
|
|
||||||
this.data = res.data?.data ?? [];
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
belanja.findMany.data = res.data.data ?? [];
|
||||||
|
belanja.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
} else {
|
} else {
|
||||||
toast.error(res.data?.message || "Gagal mengambil Belanja");
|
belanja.findMany.data = [];
|
||||||
|
belanja.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("Find many error:", error);
|
console.error("Gagal fetch Belanja paginated:", err);
|
||||||
toast.error("Gagal mengambil Belanja");
|
belanja.findMany.data = [];
|
||||||
|
belanja.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
belanja.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -525,12 +561,15 @@ const belanja = proxy({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/belanja/${id}`, {
|
const response = await fetch(
|
||||||
method: "GET",
|
`/api/ekonomi/pendapatanaslidesa/belanja/${id}`,
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
method: "GET",
|
||||||
},
|
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}`);
|
||||||
}
|
}
|
||||||
@@ -566,16 +605,19 @@ const belanja = proxy({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
belanja.update.loading = true;
|
belanja.update.loading = true;
|
||||||
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/belanja/${this.id}`, {
|
const response = await fetch(
|
||||||
method: "PUT",
|
`/api/ekonomi/pendapatanaslidesa/belanja/${this.id}`,
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
method: "PUT",
|
||||||
},
|
headers: {
|
||||||
body: JSON.stringify({
|
"Content-Type": "application/json",
|
||||||
name: this.form.name,
|
},
|
||||||
value: this.form.value,
|
body: JSON.stringify({
|
||||||
}),
|
name: this.form.name,
|
||||||
});
|
value: this.form.value,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -710,23 +752,37 @@ const pembiayaan = proxy({
|
|||||||
name: string;
|
name: string;
|
||||||
value: number;
|
value: number;
|
||||||
}>,
|
}>,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
pembiayaan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
pembiayaan.findMany.page = page;
|
||||||
|
pembiayaan.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.loading = true;
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.pembiayaan[
|
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.pembiayaan[
|
||||||
"find-many"
|
"find-many"
|
||||||
].get();
|
].get({ query });
|
||||||
if (res.status === 200) {
|
|
||||||
this.data = res.data?.data ?? [];
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
pembiayaan.findMany.data = res.data.data ?? [];
|
||||||
|
pembiayaan.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
} else {
|
} else {
|
||||||
toast.error(res.data?.message || "Gagal mengambil Pembiayaan");
|
pembiayaan.findMany.data = [];
|
||||||
|
pembiayaan.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("Find many error:", error);
|
console.error("Gagal fetch Pembiayaan paginated:", err);
|
||||||
toast.error("Gagal mengambil Pembiayaan");
|
pembiayaan.findMany.data = [];
|
||||||
|
pembiayaan.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
pembiayaan.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -740,12 +796,15 @@ const pembiayaan = proxy({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/pembiayaan/${id}`, {
|
const response = await fetch(
|
||||||
method: "GET",
|
`/api/ekonomi/pendapatanaslidesa/pembiayaan/${id}`,
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
method: "GET",
|
||||||
},
|
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}`);
|
||||||
}
|
}
|
||||||
@@ -781,16 +840,19 @@ const pembiayaan = proxy({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
pembiayaan.update.loading = true;
|
pembiayaan.update.loading = true;
|
||||||
const response = await fetch(`/api/ekonomi/pendapatanaslidesa/pembiayaan/${this.id}`, {
|
const response = await fetch(
|
||||||
method: "PUT",
|
`/api/ekonomi/pendapatanaslidesa/pembiayaan/${this.id}`,
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
method: "PUT",
|
||||||
},
|
headers: {
|
||||||
body: JSON.stringify({
|
"Content-Type": "application/json",
|
||||||
name: this.form.name,
|
},
|
||||||
value: this.form.value,
|
body: JSON.stringify({
|
||||||
}),
|
name: this.form.name,
|
||||||
});
|
value: this.form.value,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -71,12 +72,37 @@ const demografiPekerjaan = proxy({
|
|||||||
omit: { isActive: true };
|
omit: { isActive: true };
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.ekonomi.demografipekerjaan[
|
totalPages: 1,
|
||||||
"find-many"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
demografiPekerjaan.findMany.data = res.data?.data ?? [];
|
demografiPekerjaan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
demografiPekerjaan.findMany.page = page;
|
||||||
|
demografiPekerjaan.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.ekonomi.demografipekerjaan[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
demografiPekerjaan.findMany.data = res.data.data ?? [];
|
||||||
|
demografiPekerjaan.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
demografiPekerjaan.findMany.data = [];
|
||||||
|
demografiPekerjaan.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch demografi pekerjaan paginated:", err);
|
||||||
|
demografiPekerjaan.findMany.data = [];
|
||||||
|
demografiPekerjaan.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
demografiPekerjaan.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -194,4 +220,4 @@ const demografiPekerjaan = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
export default demografiPekerjaan
|
export default demografiPekerjaan;
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -69,16 +70,37 @@ const jumlahPendudukMiskin = proxy({
|
|||||||
select: { id: true; year: true; totalPoorPopulation: true };
|
select: { id: true; year: true; totalPoorPopulation: true };
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
loading: false,
|
page: 1,
|
||||||
async load() {
|
totalPages: 1,
|
||||||
const res = await ApiFetch.api.ekonomi.jumlahpendudukmiskin[
|
loading: false,
|
||||||
"find-many"
|
search: "",
|
||||||
].get();
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
if (res.status === 200) {
|
jumlahPendudukMiskin.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
jumlahPendudukMiskin.findMany.data = res.data?.data ?? [];
|
jumlahPendudukMiskin.findMany.page = page;
|
||||||
}
|
jumlahPendudukMiskin.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.ekonomi.jumlahpendudukmiskin["find-many"].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
jumlahPendudukMiskin.findMany.data = res.data.data ?? [];
|
||||||
|
jumlahPendudukMiskin.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
jumlahPendudukMiskin.findMany.data = [];
|
||||||
|
jumlahPendudukMiskin.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch jumlah penduduk miskin paginated:", err);
|
||||||
|
jumlahPendudukMiskin.findMany.data = [];
|
||||||
|
jumlahPendudukMiskin.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
jumlahPendudukMiskin.findMany.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.GrafikJumlahPendudukMiskinGetPayload<{
|
data: null as Prisma.GrafikJumlahPendudukMiskinGetPayload<{
|
||||||
select: { id: true; year: true; totalPoorPopulation: true };
|
select: { id: true; year: true; totalPoorPopulation: true };
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ const templateJumlahPengngguran = z.object({
|
|||||||
uneducatedUnemployment: z
|
uneducatedUnemployment: z
|
||||||
.number()
|
.number()
|
||||||
.min(1, "Pengangguran tidak pendidikan harus diisi"),
|
.min(1, "Pengangguran tidak pendidikan harus diisi"),
|
||||||
percentageChange: z.number().min(0, "Persentase perubahan harus diisi"),
|
percentageChange: z.number({ invalid_type_error: "Persentase perubahan harus angka" }),
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type JumlahPengangguran = {
|
type JumlahPengangguran = {
|
||||||
@@ -29,7 +30,7 @@ type JumlahPengangguran = {
|
|||||||
|
|
||||||
const jumlahPengangguranForm: JumlahPengangguran = {
|
const jumlahPengangguranForm: JumlahPengangguran = {
|
||||||
month: "",
|
month: "",
|
||||||
year: 0,
|
year: new Date().getFullYear(), // Default to current year
|
||||||
totalUnemployment: 0,
|
totalUnemployment: 0,
|
||||||
educatedUnemployment: 0,
|
educatedUnemployment: 0,
|
||||||
uneducatedUnemployment: 0,
|
uneducatedUnemployment: 0,
|
||||||
@@ -60,13 +61,21 @@ const jumlahPengangguran = proxy({
|
|||||||
form: jumlahPengangguranForm,
|
form: jumlahPengangguranForm,
|
||||||
loading: false,
|
loading: false,
|
||||||
async create() {
|
async create() {
|
||||||
const cek = templateJumlahPengngguran.safeParse(
|
// Ensure all number fields are actual numbers
|
||||||
jumlahPengangguran.create.form
|
const formData = {
|
||||||
);
|
...jumlahPengangguran.create.form,
|
||||||
|
year: Number(jumlahPengangguran.create.form.year) || new Date().getFullYear(),
|
||||||
|
totalUnemployment: Number(jumlahPengangguran.create.form.totalUnemployment) || 0,
|
||||||
|
educatedUnemployment: Number(jumlahPengangguran.create.form.educatedUnemployment) || 0,
|
||||||
|
uneducatedUnemployment: Number(jumlahPengangguran.create.form.uneducatedUnemployment) || 0,
|
||||||
|
percentageChange: Number(jumlahPengangguran.create.form.percentageChange) || 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const cek = templateJumlahPengngguran.safeParse(formData);
|
||||||
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(".")} (${v.message})`)
|
||||||
.join("\n")}] required`;
|
.join("\n")}]`;
|
||||||
toast.error(err);
|
toast.error(err);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -78,7 +87,7 @@ const jumlahPengangguran = proxy({
|
|||||||
].post(jumlahPengangguran.create.form);
|
].post(jumlahPengangguran.create.form);
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
const id = res.data?.data?.id;
|
const id = res.data?.id;
|
||||||
if (id) {
|
if (id) {
|
||||||
toast.success("Success create");
|
toast.success("Success create");
|
||||||
jumlahPengangguran.create.form = { ...jumlahPengangguranForm };
|
jumlahPengangguran.create.form = { ...jumlahPengangguranForm };
|
||||||
@@ -103,16 +112,40 @@ const jumlahPengangguran = proxy({
|
|||||||
omit: { isActive: true };
|
omit: { isActive: true };
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res =
|
totalPages: 1,
|
||||||
await ApiFetch.api.ekonomi.jumlahpengangguran.detaildatapengangguran[
|
loading: false,
|
||||||
"find-many"
|
search: "",
|
||||||
].get();
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
if (res.status === 200) {
|
jumlahPengangguran.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
jumlahPengangguran.findMany.data = res.data?.data ?? [];
|
jumlahPengangguran.findMany.page = page;
|
||||||
}
|
jumlahPengangguran.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.ekonomi.jumlahpengangguran.detaildatapengangguran[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
jumlahPengangguran.findMany.data = res.data.data ?? [];
|
||||||
|
jumlahPengangguran.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
jumlahPengangguran.findMany.data = [];
|
||||||
|
jumlahPengangguran.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch jumlah pengangguran paginated:", err);
|
||||||
|
jumlahPengangguran.findMany.data = [];
|
||||||
|
jumlahPengangguran.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
jumlahPengangguran.findMany.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.DetailDataPengangguranGetPayload<{
|
data: null as Prisma.DetailDataPengangguranGetPayload<{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ 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"),
|
||||||
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||||
ikonUrl: z.string().optional(),
|
icon: z.string().min(1, "Icon minimal 1 karakter"),
|
||||||
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"),
|
||||||
@@ -18,7 +18,7 @@ const templateForm = z.object({
|
|||||||
const defaultForm = {
|
const defaultForm = {
|
||||||
nama: "",
|
nama: "",
|
||||||
deskripsi: "",
|
deskripsi: "",
|
||||||
ikonUrl: "",
|
icon: "",
|
||||||
statistik: {
|
statistik: {
|
||||||
tahun: "",
|
tahun: "",
|
||||||
jumlah: "",
|
jumlah: "",
|
||||||
@@ -148,7 +148,7 @@ const programKemiskinanState = proxy({
|
|||||||
this.form = {
|
this.form = {
|
||||||
nama: data.nama,
|
nama: data.nama,
|
||||||
deskripsi: data.deskripsi,
|
deskripsi: data.deskripsi,
|
||||||
ikonUrl: data.ikonUrl || "",
|
icon: data.icon,
|
||||||
statistik: {
|
statistik: {
|
||||||
tahun: data.statistik.tahun,
|
tahun: data.statistik.tahun,
|
||||||
jumlah: data.statistik.jumlah,
|
jumlah: data.statistik.jumlah,
|
||||||
@@ -189,7 +189,7 @@ const programKemiskinanState = proxy({
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
nama: this.form.nama,
|
nama: this.form.nama,
|
||||||
deskripsi: this.form.deskripsi,
|
deskripsi: this.form.deskripsi,
|
||||||
ikonUrl: this.form.ikonUrl,
|
icon: this.form.icon,
|
||||||
statistik: {
|
statistik: {
|
||||||
tahun: this.form.statistik.tahun,
|
tahun: this.form.statistik.tahun,
|
||||||
jumlah: this.form.statistik.jumlah,
|
jumlah: this.form.statistik.jumlah,
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -76,13 +77,37 @@ const grafikSektorUnggulan = proxy({
|
|||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
search: "",
|
||||||
const res = await ApiFetch.api.ekonomi.sektourunggulandesa[
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
"find-many"
|
grafikSektorUnggulan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
].get();
|
grafikSektorUnggulan.findMany.page = page;
|
||||||
if (res.status === 200) {
|
grafikSektorUnggulan.findMany.search = search;
|
||||||
grafikSektorUnggulan.findMany.data = res.data?.data ?? [];
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.ekonomi.sektourunggulandesa[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
grafikSektorUnggulan.findMany.data = res.data.data ?? [];
|
||||||
|
grafikSektorUnggulan.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
grafikSektorUnggulan.findMany.data = [];
|
||||||
|
grafikSektorUnggulan.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch sektor unggulan desa paginated:", err);
|
||||||
|
grafikSektorUnggulan.findMany.data = [];
|
||||||
|
grafikSektorUnggulan.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
grafikSektorUnggulan.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -161,18 +161,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.ekonomi["struktur-organisasi"][
|
const query: any = { page, limit };
|
||||||
"posisi-organisasi"
|
if (search) query.search = search;
|
||||||
]["find-many"].get();
|
|
||||||
if (res.status === 200) {
|
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -75,16 +76,37 @@ const grafikBerdasarkanUsiaKerjaNganggur = proxy({
|
|||||||
omit: { isActive: true };
|
omit: { isActive: true };
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
loading: false,
|
page: 1,
|
||||||
async load() {
|
totalPages: 1,
|
||||||
const res = await ApiFetch.api.ekonomi.grafikusiakerjayangmenganggur[
|
loading: false,
|
||||||
"find-many"
|
search: "",
|
||||||
].get();
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
if (res.status === 200) {
|
grafikBerdasarkanUsiaKerjaNganggur.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
grafikBerdasarkanUsiaKerjaNganggur.findMany.data = res.data?.data ?? [];
|
grafikBerdasarkanUsiaKerjaNganggur.findMany.page = page;
|
||||||
}
|
grafikBerdasarkanUsiaKerjaNganggur.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.ekonomi.grafikusiakerjayangmenganggur["find-many"].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
grafikBerdasarkanUsiaKerjaNganggur.findMany.data = res.data.data ?? [];
|
||||||
|
grafikBerdasarkanUsiaKerjaNganggur.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
grafikBerdasarkanUsiaKerjaNganggur.findMany.data = [];
|
||||||
|
grafikBerdasarkanUsiaKerjaNganggur.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch grafik berdasarkan usia kerja yang menganggur paginated:", err);
|
||||||
|
grafikBerdasarkanUsiaKerjaNganggur.findMany.data = [];
|
||||||
|
grafikBerdasarkanUsiaKerjaNganggur.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
grafikBerdasarkanUsiaKerjaNganggur.findMany.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.GrafikMenganggurBerdasarkanUsiaGetPayload<{
|
data: null as Prisma.GrafikMenganggurBerdasarkanUsiaGetPayload<{
|
||||||
omit: { isActive: true };
|
omit: { isActive: true };
|
||||||
@@ -259,15 +281,36 @@ const grafikBerdasarkanPendidikan = proxy({
|
|||||||
omit: { isActive: true };
|
omit: { isActive: true };
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
loading: false,
|
page: 1,
|
||||||
async load() {
|
totalPages: 1,
|
||||||
const res = await ApiFetch.api.ekonomi.grafikmenganggurberdasarkanpendidikan[
|
loading: false,
|
||||||
"find-many"
|
search: "",
|
||||||
].get();
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
if (res.status === 200) {
|
grafikBerdasarkanPendidikan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
grafikBerdasarkanPendidikan.findMany.data = res.data?.data ?? [];
|
grafikBerdasarkanPendidikan.findMany.page = page;
|
||||||
}
|
grafikBerdasarkanPendidikan.findMany.search = search;
|
||||||
},
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.ekonomi.grafikmenganggurberdasarkanpendidikan["find-many"].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
grafikBerdasarkanPendidikan.findMany.data = res.data.data ?? [];
|
||||||
|
grafikBerdasarkanPendidikan.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
grafikBerdasarkanPendidikan.findMany.data = [];
|
||||||
|
grafikBerdasarkanPendidikan.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch grafik berdasarkan pendidikan paginated:", err);
|
||||||
|
grafikBerdasarkanPendidikan.findMany.data = [];
|
||||||
|
grafikBerdasarkanPendidikan.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
grafikBerdasarkanPendidikan.findMany.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.GrafikMenganggurBerdasarkanPendidikanGetPayload<{
|
data: null as Prisma.GrafikMenganggurBerdasarkanPendidikanGetPayload<{
|
||||||
|
|||||||
@@ -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,10 +62,37 @@ const ajukanIdeInovatifState = proxy({
|
|||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.inovasi.ajukanideinovatif["find-many"].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
loading: false,
|
||||||
ajukanIdeInovatifState.findMany.data = res.data?.data ?? [];
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
ajukanIdeInovatifState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
ajukanIdeInovatifState.findMany.page = page;
|
||||||
|
ajukanIdeInovatifState.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res =
|
||||||
|
await ApiFetch.api.inovasi.ajukanideinovatif[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
ajukanIdeInovatifState.findMany.data = res.data.data ?? [];
|
||||||
|
ajukanIdeInovatifState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
ajukanIdeInovatifState.findMany.data = [];
|
||||||
|
ajukanIdeInovatifState.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch ajukan ide inovatif paginated:", err);
|
||||||
|
ajukanIdeInovatifState.findMany.data = [];
|
||||||
|
ajukanIdeInovatifState.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
ajukanIdeInovatifState.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -97,16 +125,21 @@ const ajukanIdeInovatifState = proxy({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
ajukanIdeInovatifState.delete.loading = true;
|
ajukanIdeInovatifState.delete.loading = true;
|
||||||
const response = await fetch(`/api/inovasi/ajukanideinovatif/del/${id}`, {
|
const response = await fetch(
|
||||||
method: "DELETE",
|
`/api/inovasi/ajukanideinovatif/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) {
|
if (response.ok) {
|
||||||
toast.success(result.message || "Ajukan Ide Inovatif berhasil dihapus");
|
toast.success(
|
||||||
|
result.message || "Ajukan Ide Inovatif berhasil dihapus"
|
||||||
|
);
|
||||||
await ajukanIdeInovatifState.findMany.load();
|
await ajukanIdeInovatifState.findMany.load();
|
||||||
} else {
|
} else {
|
||||||
toast.error(result?.message || "Gagal menghapus ajukan ide inovatif");
|
toast.error(result?.message || "Gagal menghapus ajukan ide inovatif");
|
||||||
|
|||||||
@@ -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,19 +55,20 @@ const administrasiOnline = proxy({
|
|||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as Array<
|
data: null as Array<
|
||||||
Prisma.AdministrasiOnlineGetPayload<{
|
Prisma.AdministrasiOnlineGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
jenisLayanan: true;
|
jenisLayanan: true;
|
||||||
};
|
};
|
||||||
}>
|
}>
|
||||||
> | null,
|
> | null,
|
||||||
page: 1,
|
page: 1,
|
||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
search: "",
|
||||||
async load(page = 1, limit = 10) {
|
async load(page = 1, limit = 10, search = "") {
|
||||||
administrasiOnline.findMany.loading = true;
|
administrasiOnline.findMany.loading = true;
|
||||||
administrasiOnline.findMany.page = page;
|
administrasiOnline.findMany.page = page;
|
||||||
|
administrasiOnline.findMany.search = search;
|
||||||
try {
|
try {
|
||||||
const res =
|
const res =
|
||||||
await ApiFetch.api.inovasi.layananonlinedesa.administrasionline[
|
await ApiFetch.api.inovasi.layananonlinedesa.administrasionline[
|
||||||
@@ -75,6 +77,7 @@ const administrasiOnline = proxy({
|
|||||||
query: {
|
query: {
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
|
search,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,10 +94,10 @@ const administrasiOnline = proxy({
|
|||||||
},
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.AdministrasiOnlineGetPayload<{
|
data: null as Prisma.AdministrasiOnlineGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
jenisLayanan: true;
|
jenisLayanan: true;
|
||||||
};
|
};
|
||||||
}> | null,
|
}> | null,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
@@ -199,13 +202,37 @@ const jenisLayanan = proxy({
|
|||||||
nama: string;
|
nama: string;
|
||||||
deskripsi: string;
|
deskripsi: string;
|
||||||
}> | null,
|
}> | null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res =
|
totalPages: 1,
|
||||||
await ApiFetch.api.inovasi.layananonlinedesa.administrasionline.jenislayanan[
|
loading: false,
|
||||||
"find-many"
|
search: "",
|
||||||
].get();
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
if (res.status === 200) {
|
jenisLayanan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
jenisLayanan.findMany.data = res.data?.data ?? [];
|
jenisLayanan.findMany.page = page;
|
||||||
|
jenisLayanan.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res =
|
||||||
|
await ApiFetch.api.inovasi.layananonlinedesa.administrasionline.jenislayanan[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
jenisLayanan.findMany.data = res.data.data ?? [];
|
||||||
|
jenisLayanan.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
jenisLayanan.findMany.data = [];
|
||||||
|
jenisLayanan.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch jenis layanan paginated:", err);
|
||||||
|
jenisLayanan.findMany.data = [];
|
||||||
|
jenisLayanan.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
jenisLayanan.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -403,7 +430,9 @@ const templatePengaduanMasyarakatForm = z.object({
|
|||||||
nik: z.string().min(1, "NIK minimal 1 karakter"),
|
nik: z.string().min(1, "NIK minimal 1 karakter"),
|
||||||
judulPengaduan: z.string().min(1, "Judul pengaduan minimal 1 karakter"),
|
judulPengaduan: z.string().min(1, "Judul pengaduan minimal 1 karakter"),
|
||||||
lokasiKejadian: z.string().min(1, "Lokasi kejadian minimal 1 karakter"),
|
lokasiKejadian: z.string().min(1, "Lokasi kejadian minimal 1 karakter"),
|
||||||
deskripsiPengaduan: z.string().min(1, "Deskripsi pengaduan minimal 1 karakter"),
|
deskripsiPengaduan: z
|
||||||
|
.string()
|
||||||
|
.min(1, "Deskripsi pengaduan minimal 1 karakter"),
|
||||||
jenisPengaduanId: z.string().min(1, "Jenis pengaduan minimal 1 karakter"),
|
jenisPengaduanId: z.string().min(1, "Jenis pengaduan minimal 1 karakter"),
|
||||||
imageId: z.string().min(1, "Image minimal 1 karakter"),
|
imageId: z.string().min(1, "Image minimal 1 karakter"),
|
||||||
});
|
});
|
||||||
@@ -455,37 +484,42 @@ const pengaduanMasyarakat = proxy({
|
|||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as Array<
|
data: null as Array<
|
||||||
Prisma.PengaduanMasyarakatGetPayload<{
|
Prisma.PengaduanMasyarakatGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
jenisPengaduan: true;
|
jenisPengaduan: true;
|
||||||
image: true;
|
image: true;
|
||||||
};
|
};
|
||||||
}>
|
}>
|
||||||
> | null,
|
> | null,
|
||||||
page: 1,
|
page: 1,
|
||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
search: "",
|
||||||
async load(page = 1, limit = 10) {
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
pengaduanMasyarakat.findMany.loading = true;
|
pengaduanMasyarakat.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
pengaduanMasyarakat.findMany.page = page;
|
pengaduanMasyarakat.findMany.page = page;
|
||||||
|
pengaduanMasyarakat.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res =
|
const res =
|
||||||
await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat[
|
await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat[
|
||||||
"find-many"
|
"find-many"
|
||||||
].get({
|
].get({ query });
|
||||||
query: {
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
pengaduanMasyarakat.findMany.data = res.data.data ?? [];
|
pengaduanMasyarakat.findMany.data = res.data.data ?? [];
|
||||||
pengaduanMasyarakat.findMany.totalPages = res.data.totalPages ?? 1;
|
pengaduanMasyarakat.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
pengaduanMasyarakat.findMany.data = [];
|
||||||
|
pengaduanMasyarakat.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal fetch pengaduan masyarakat paginated:", err);
|
console.error("Gagal fetch pengaduan masyarakat paginated:", err);
|
||||||
|
pengaduanMasyarakat.findMany.data = [];
|
||||||
|
pengaduanMasyarakat.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
pengaduanMasyarakat.findMany.loading = false;
|
pengaduanMasyarakat.findMany.loading = false;
|
||||||
}
|
}
|
||||||
@@ -493,11 +527,11 @@ const pengaduanMasyarakat = proxy({
|
|||||||
},
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.PengaduanMasyarakatGetPayload<{
|
data: null as Prisma.PengaduanMasyarakatGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
jenisPengaduan: true;
|
jenisPengaduan: true;
|
||||||
image: true;
|
image: true;
|
||||||
};
|
};
|
||||||
}> | null,
|
}> | null,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
@@ -507,7 +541,10 @@ const pengaduanMasyarakat = proxy({
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
pengaduanMasyarakat.findUnique.data = data.data ?? null;
|
pengaduanMasyarakat.findUnique.data = data.data ?? null;
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to fetch pengaduan masyarakat:", res.statusText);
|
console.error(
|
||||||
|
"Failed to fetch pengaduan masyarakat:",
|
||||||
|
res.statusText
|
||||||
|
);
|
||||||
pengaduanMasyarakat.findUnique.data = null;
|
pengaduanMasyarakat.findUnique.data = null;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -542,7 +579,9 @@ const pengaduanMasyarakat = proxy({
|
|||||||
);
|
);
|
||||||
await pengaduanMasyarakat.findMany.load(); // refresh list
|
await pengaduanMasyarakat.findMany.load(); // refresh list
|
||||||
} else {
|
} else {
|
||||||
toast.error(result?.message || "Gagal menghapus pengaduan masyarakat");
|
toast.error(
|
||||||
|
result?.message || "Gagal menghapus pengaduan masyarakat"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Gagal delete:", error);
|
console.error("Gagal delete:", error);
|
||||||
@@ -567,7 +606,9 @@ const jenisPengaduan = proxy({
|
|||||||
form: { ...defaultJenisPengaduanForm },
|
form: { ...defaultJenisPengaduanForm },
|
||||||
loading: false,
|
loading: false,
|
||||||
async create() {
|
async create() {
|
||||||
const cek = templateJenisPengaduanForm.safeParse(jenisPengaduan.create.form);
|
const cek = templateJenisPengaduanForm.safeParse(
|
||||||
|
jenisPengaduan.create.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(".")}`)
|
||||||
@@ -598,13 +639,37 @@ const jenisPengaduan = proxy({
|
|||||||
id: string;
|
id: string;
|
||||||
nama: string;
|
nama: string;
|
||||||
}> | null,
|
}> | null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res =
|
totalPages: 1,
|
||||||
await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat.jenispengaduan[
|
loading: false,
|
||||||
"find-many"
|
search: "",
|
||||||
].get();
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
if (res.status === 200) {
|
jenisPengaduan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
jenisPengaduan.findMany.data = res.data?.data ?? [];
|
jenisPengaduan.findMany.page = page;
|
||||||
|
jenisPengaduan.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res =
|
||||||
|
await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat.jenispengaduan[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
jenisPengaduan.findMany.data = res.data.data ?? [];
|
||||||
|
jenisPengaduan.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
jenisPengaduan.findMany.data = [];
|
||||||
|
jenisPengaduan.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch jenis pengaduan paginated:", err);
|
||||||
|
jenisPengaduan.findMany.data = [];
|
||||||
|
jenisPengaduan.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
jenisPengaduan.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -693,7 +758,7 @@ const jenisPengaduan = proxy({
|
|||||||
const data = result.data;
|
const data = result.data;
|
||||||
this.id = data.id;
|
this.id = data.id;
|
||||||
this.form = {
|
this.form = {
|
||||||
nama: data.nama
|
nama: data.nama,
|
||||||
};
|
};
|
||||||
return data;
|
return data;
|
||||||
} else {
|
} else {
|
||||||
@@ -709,7 +774,9 @@ const jenisPengaduan = proxy({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async update() {
|
async update() {
|
||||||
const cek = templateJenisPengaduanForm.safeParse(jenisPengaduan.edit.form);
|
const cek = templateJenisPengaduanForm.safeParse(
|
||||||
|
jenisPengaduan.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(".")}`)
|
||||||
@@ -759,7 +826,9 @@ const jenisPengaduan = proxy({
|
|||||||
await jenisPengaduan.findMany.load(); // refresh list
|
await jenisPengaduan.findMany.load(); // refresh list
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(result.message || "Gagal mengupdate jenis pengaduan");
|
throw new Error(
|
||||||
|
result.message || "Gagal mengupdate jenis pengaduan"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If JSON parsing fails, try to get the response text for better error messages
|
// If JSON parsing fails, try to get the response text for better error messages
|
||||||
@@ -792,7 +861,6 @@ const jenisPengaduan = proxy({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const layananonlineDesa = proxy({
|
const layananonlineDesa = proxy({
|
||||||
administrasiOnline,
|
administrasiOnline,
|
||||||
jenisLayanan,
|
jenisLayanan,
|
||||||
|
|||||||
@@ -54,34 +54,32 @@ const programKreatifState = proxy({
|
|||||||
data: null as any[] | null,
|
data: null as any[] | null,
|
||||||
page: 1,
|
page: 1,
|
||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
total: 0,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
load: async (page = 1, limit = 10) => {
|
search: "",
|
||||||
// Change to arrow function
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
programKreatifState.findMany.loading = true; // Use the full path to access the property
|
programKreatifState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
programKreatifState.findMany.page = page;
|
programKreatifState.findMany.page = page;
|
||||||
|
programKreatifState.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await ApiFetch.api.inovasi.programkreatif["find-many"].get({
|
const query: any = { page, limit };
|
||||||
query: { page, limit },
|
if (search) query.search = search;
|
||||||
});
|
|
||||||
|
const res = await ApiFetch.api.inovasi.programkreatif[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
programKreatifState.findMany.data = res.data.data || [];
|
programKreatifState.findMany.data = res.data.data ?? [];
|
||||||
programKreatifState.findMany.total = res.data.total || 0;
|
programKreatifState.findMany.totalPages =
|
||||||
programKreatifState.findMany.totalPages = res.data.totalPages || 1;
|
res.data.totalPages ?? 1;
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
|
||||||
"Failed to load grafik berdasarkan jenis kelamin:",
|
|
||||||
res.data?.message
|
|
||||||
);
|
|
||||||
programKreatifState.findMany.data = [];
|
programKreatifState.findMany.data = [];
|
||||||
programKreatifState.findMany.total = 0;
|
|
||||||
programKreatifState.findMany.totalPages = 1;
|
programKreatifState.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("Error loading grafik berdasarkan jenis kelamin:", error);
|
console.error("Gagal fetch program kreatif paginated:", err);
|
||||||
programKreatifState.findMany.data = [];
|
programKreatifState.findMany.data = [];
|
||||||
programKreatifState.findMany.total = 0;
|
|
||||||
programKreatifState.findMany.totalPages = 1;
|
programKreatifState.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
programKreatifState.findMany.loading = false;
|
programKreatifState.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";
|
||||||
@@ -6,26 +7,14 @@ 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(),
|
icon: 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: "",
|
icon: "",
|
||||||
kontakItems: [
|
kategoriId: [] as string[],
|
||||||
{
|
|
||||||
nama: "",
|
|
||||||
nomorTelepon: "",
|
|
||||||
imageId: "",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const kontakDaruratKeamananState = proxy({
|
const kontakDaruratKeamananState = proxy({
|
||||||
@@ -61,20 +50,49 @@ const kontakDaruratKeamananState = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as Array<
|
||||||
| Prisma.KontakDaruratKeamananGetPayload<{
|
Prisma.KontakDaruratKeamananGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
kontakItems: true;
|
kategori: 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 +101,10 @@ const kontakDaruratKeamananState = proxy({
|
|||||||
include: {
|
include: {
|
||||||
kontakItems: {
|
kontakItems: {
|
||||||
include: {
|
include: {
|
||||||
image: true;
|
kontakItem: true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
image: true;
|
kategori: true;
|
||||||
};
|
};
|
||||||
}> | null,
|
}> | null,
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -168,14 +186,9 @@ const kontakDaruratKeamananState = proxy({
|
|||||||
this.id = data.id;
|
this.id = data.id;
|
||||||
this.form = {
|
this.form = {
|
||||||
nama: data.nama,
|
nama: data.nama,
|
||||||
imageId: data.imageId,
|
icon: data.icon || "",
|
||||||
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 {
|
||||||
@@ -212,14 +225,8 @@ const kontakDaruratKeamananState = proxy({
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
nama: this.form.nama,
|
nama: this.form.nama,
|
||||||
imageId: this.form.imageId,
|
icon: this.form.icon,
|
||||||
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 +263,242 @@ 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"),
|
||||||
|
icon: z.string().nonempty(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultFormItem = {
|
||||||
|
nama: "",
|
||||||
|
nomorTelepon: "",
|
||||||
|
icon: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
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<{
|
||||||
|
omit: {
|
||||||
|
isActive: 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<{
|
||||||
|
omit: {
|
||||||
|
isActive: 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,
|
||||||
|
icon: data.icon,
|
||||||
|
};
|
||||||
|
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,
|
||||||
|
icon: this.form.icon,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
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";
|
||||||
@@ -10,12 +11,24 @@ const templateForm = z.object({
|
|||||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||||
lokasi: z.string().min(3, "Lokasi minimal 3 karakter"),
|
lokasi: z.string().min(3, "Lokasi minimal 3 karakter"),
|
||||||
tanggalWaktu: z.string().min(3, "Tanggal Waktu minimal 3 karakter"),
|
tanggalWaktu: z.string().min(3, "Tanggal Waktu minimal 3 karakter"),
|
||||||
status: z.enum(["Selesai", "Proses", "Gagal"]),
|
|
||||||
penanganan: z.string(),
|
|
||||||
kronologi: z.string().optional(),
|
kronologi: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface FormData {
|
interface FormData {
|
||||||
|
judul: string;
|
||||||
|
lokasi: string;
|
||||||
|
tanggalWaktu: string;
|
||||||
|
kronologi: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultForm: FormData = {
|
||||||
|
judul: "",
|
||||||
|
lokasi: "",
|
||||||
|
tanggalWaktu: new Date().toISOString(),
|
||||||
|
kronologi: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
interface FormEditData {
|
||||||
judul: string;
|
judul: string;
|
||||||
lokasi: string;
|
lokasi: string;
|
||||||
tanggalWaktu: string;
|
tanggalWaktu: string;
|
||||||
@@ -24,15 +37,16 @@ interface FormData {
|
|||||||
kronologi: string;
|
kronologi: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultForm: FormData = {
|
const editForm: FormEditData = {
|
||||||
judul: "",
|
judul: "",
|
||||||
lokasi: "",
|
lokasi: "",
|
||||||
tanggalWaktu: new Date().toISOString(),
|
tanggalWaktu: new Date().toISOString(),
|
||||||
|
kronologi: "",
|
||||||
status: "Proses",
|
status: "Proses",
|
||||||
penanganan: "",
|
penanganan: "",
|
||||||
kronologi: "",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const laporanPublikState = proxy({
|
const laporanPublikState = proxy({
|
||||||
create: {
|
create: {
|
||||||
form: { ...defaultForm },
|
form: { ...defaultForm },
|
||||||
@@ -97,13 +111,37 @@ const laporanPublikState = proxy({
|
|||||||
include: { penanganan: true };
|
include: { penanganan: true };
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.keamanan.laporanpublik["find-many"].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
loading: false,
|
||||||
laporanPublikState.findMany.data = res.data?.data ?? [];
|
search: "",
|
||||||
}
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
laporanPublikState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
laporanPublikState.findMany.page = page;
|
||||||
|
laporanPublikState.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.keamanan.laporanpublik["find-many"].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
laporanPublikState.findMany.data = res.data.data ?? [];
|
||||||
|
laporanPublikState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
laporanPublikState.findMany.data = [];
|
||||||
|
laporanPublikState.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch laporan publik paginated:", err);
|
||||||
|
laporanPublikState.findMany.data = [];
|
||||||
|
laporanPublikState.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
laporanPublikState.findMany.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.LaporanPublikGetPayload<{
|
data: null as Prisma.LaporanPublikGetPayload<{
|
||||||
include: { penanganan: true };
|
include: { penanganan: true };
|
||||||
@@ -160,7 +198,7 @@ const laporanPublikState = proxy({
|
|||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
id: "",
|
id: "",
|
||||||
form: { ...defaultForm },
|
form: { ...editForm },
|
||||||
loading: false,
|
loading: false,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
@@ -266,7 +304,7 @@ const laporanPublikState = proxy({
|
|||||||
},
|
},
|
||||||
reset() {
|
reset() {
|
||||||
laporanPublikState.edit.id = "";
|
laporanPublikState.edit.id = "";
|
||||||
laporanPublikState.edit.form = { ...defaultForm };
|
laporanPublikState.edit.form = { ...editForm };
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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,45 +6,17 @@ import { proxy } from "valtio";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const templateForm = z.object({
|
const templateForm = z.object({
|
||||||
pencegahanKriminalitas: z.object({
|
judul: z.string().min(1, "Judul minimal 1 karakter"),
|
||||||
programKeamanan: z.object({
|
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||||
nama: z.string().min(1, "Nama minimal 1 karakter"),
|
deskripsiSingkat: z.string().min(1, "Deskripsi singkat minimal 1 karakter"),
|
||||||
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
linkVideo: z.string().min(1, "Link video minimal 1 karakter"),
|
||||||
slug: z.string().min(1, "Slug minimal 1 karakter"),
|
|
||||||
}),
|
|
||||||
tipsKeamanan: z.object({
|
|
||||||
judul: z.string().min(1, "Judul minimal 1 karakter"),
|
|
||||||
konten: z.string().min(1, "Konten minimal 1 karakter"),
|
|
||||||
slug: z.string().min(1, "Slug minimal 1 karakter"),
|
|
||||||
}),
|
|
||||||
videoKeamanan: z.object({
|
|
||||||
judul: z.string().min(1, "Judul minimal 1 karakter"),
|
|
||||||
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
|
||||||
videoUrl: z.string().min(1, "Video URL minimal 1 karakter"),
|
|
||||||
slug: z.string().min(1, "Slug minimal 1 karakter"),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultForm = {
|
const defaultForm = {
|
||||||
pencegahanKriminalitas: {
|
judul: "",
|
||||||
programKeamanan: {
|
deskripsi: "",
|
||||||
nama: "",
|
deskripsiSingkat: "",
|
||||||
deskripsi: "",
|
linkVideo: "",
|
||||||
slug: "",
|
|
||||||
},
|
|
||||||
tipsKeamanan: {
|
|
||||||
judul: "",
|
|
||||||
konten: "",
|
|
||||||
slug: "",
|
|
||||||
},
|
|
||||||
videoKeamanan: {
|
|
||||||
judul: "",
|
|
||||||
deskripsi: "",
|
|
||||||
videoUrl: "",
|
|
||||||
slug: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const pencegahanKriminalitasState = proxy({
|
const pencegahanKriminalitasState = proxy({
|
||||||
@@ -64,7 +37,7 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
pencegahanKriminalitasState.create.loading = true;
|
pencegahanKriminalitasState.create.loading = true;
|
||||||
const res = await ApiFetch.api.keamanan.pencegahankriminalitas[
|
const res = await ApiFetch.api.keamanan.pencegahankriminalitas[
|
||||||
"create"
|
"create"
|
||||||
].post(pencegahanKriminalitasState.create.form.pencegahanKriminalitas);
|
].post(pencegahanKriminalitasState.create.form);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
pencegahanKriminalitasState.findMany.load();
|
pencegahanKriminalitasState.findMany.load();
|
||||||
return toast.success("success create");
|
return toast.success("success create");
|
||||||
@@ -81,29 +54,46 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.PencegahanKriminalitasGetPayload<{
|
| Prisma.PencegahanKriminalitasGetPayload<{
|
||||||
include: {
|
omit: { isActive: true };
|
||||||
programKeamanan: true;
|
|
||||||
tipsKeamanan: true;
|
|
||||||
videoKeamanan: true;
|
|
||||||
};
|
|
||||||
}>[]
|
}>[]
|
||||||
| 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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.PencegahanKriminalitasGetPayload<{
|
data: null as Prisma.PencegahanKriminalitasGetPayload<{
|
||||||
include: {
|
omit: { isActive: true };
|
||||||
programKeamanan: true;
|
|
||||||
tipsKeamanan: true;
|
|
||||||
videoKeamanan: true;
|
|
||||||
};
|
|
||||||
}> | null,
|
}> | null,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
@@ -122,6 +112,30 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
findFirst: {
|
||||||
|
data: null as Prisma.PencegahanKriminalitasGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}> | null,
|
||||||
|
loading: false,
|
||||||
|
// findFirst.load()
|
||||||
|
async load() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const res = await ApiFetch.api.keamanan.pencegahankriminalitas["find-first"].get();
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
this.data = res.data.data || null;
|
||||||
|
} else {
|
||||||
|
this.data = null;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch pencegahan kriminalitas terbaru:", err);
|
||||||
|
this.data = null;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
delete: {
|
delete: {
|
||||||
loading: false,
|
loading: false,
|
||||||
async byId(id: string) {
|
async byId(id: string) {
|
||||||
@@ -187,24 +201,10 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
const data = result.data;
|
const data = result.data;
|
||||||
pencegahanKriminalitasState.update.id = data.id;
|
pencegahanKriminalitasState.update.id = data.id;
|
||||||
pencegahanKriminalitasState.update.form = {
|
pencegahanKriminalitasState.update.form = {
|
||||||
pencegahanKriminalitas: {
|
judul: data.judul,
|
||||||
programKeamanan: {
|
deskripsi: data.deskripsi,
|
||||||
nama: data.programKeamanan.nama,
|
deskripsiSingkat: data.deskripsiSingkat,
|
||||||
deskripsi: data.programKeamanan.deskripsi,
|
linkVideo: data.linkVideo,
|
||||||
slug: data.programKeamanan.slug,
|
|
||||||
},
|
|
||||||
tipsKeamanan: {
|
|
||||||
judul: data.tipsKeamanan.judul,
|
|
||||||
konten: data.tipsKeamanan.konten,
|
|
||||||
slug: data.tipsKeamanan.slug,
|
|
||||||
},
|
|
||||||
videoKeamanan: {
|
|
||||||
judul: data.videoKeamanan.judul,
|
|
||||||
deskripsi: data.videoKeamanan.deskripsi,
|
|
||||||
videoUrl: data.videoKeamanan.videoUrl,
|
|
||||||
slug: data.videoKeamanan.slug,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
return data;
|
return data;
|
||||||
} else {
|
} else {
|
||||||
@@ -240,40 +240,11 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
pencegahanKriminalitas: {
|
judul: pencegahanKriminalitasState.update.form.judul,
|
||||||
programKeamanan: {
|
deskripsi: pencegahanKriminalitasState.update.form.deskripsi,
|
||||||
nama: pencegahanKriminalitasState.update.form
|
deskripsiSingkat:
|
||||||
.pencegahanKriminalitas.programKeamanan.nama,
|
pencegahanKriminalitasState.update.form.deskripsiSingkat,
|
||||||
deskripsi:
|
linkVideo: pencegahanKriminalitasState.update.form.linkVideo,
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.programKeamanan.deskripsi,
|
|
||||||
slug: pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.programKeamanan.slug,
|
|
||||||
},
|
|
||||||
tipsKeamanan: {
|
|
||||||
judul:
|
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.tipsKeamanan.judul,
|
|
||||||
konten:
|
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.tipsKeamanan.konten,
|
|
||||||
slug: pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.tipsKeamanan.slug,
|
|
||||||
},
|
|
||||||
videoKeamanan: {
|
|
||||||
judul:
|
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.videoKeamanan.judul,
|
|
||||||
deskripsi:
|
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.videoKeamanan.deskripsi,
|
|
||||||
videoUrl:
|
|
||||||
pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.videoKeamanan.videoUrl,
|
|
||||||
slug: pencegahanKriminalitasState.update.form
|
|
||||||
.pencegahanKriminalitas.videoKeamanan.slug,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -311,4 +282,4 @@ const pencegahanKriminalitasState = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
export default pencegahanKriminalitasState;
|
export default pencegahanKriminalitasState;
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ const templateForm = z.object({
|
|||||||
doctorSign: z.object({
|
doctorSign: z.object({
|
||||||
content: z.string().min(1, "Content harus diisi"),
|
content: z.string().min(1, "Content harus diisi"),
|
||||||
}),
|
}),
|
||||||
|
imageId: z.string().min(1, "Image ID harus diisi"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultForm = {
|
const defaultForm = {
|
||||||
title: "",
|
title: "",
|
||||||
content: "",
|
content: "",
|
||||||
|
imageId: "",
|
||||||
introduction: {
|
introduction: {
|
||||||
content: "",
|
content: "",
|
||||||
},
|
},
|
||||||
@@ -59,6 +61,7 @@ const defaultForm = {
|
|||||||
doctorSign: {
|
doctorSign: {
|
||||||
content: "",
|
content: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const artikelKesehatanState = proxy({
|
const artikelKesehatanState = proxy({
|
||||||
@@ -112,30 +115,42 @@ const artikelKesehatanState = proxy({
|
|||||||
firstaid: true;
|
firstaid: true;
|
||||||
mythvsfact: true;
|
mythvsfact: true;
|
||||||
doctorsign: true;
|
doctorsign: true;
|
||||||
|
image: true;
|
||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| 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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -148,6 +163,7 @@ const artikelKesehatanState = proxy({
|
|||||||
firstaid: true;
|
firstaid: true;
|
||||||
mythvsfact: true;
|
mythvsfact: true;
|
||||||
doctorsign: true;
|
doctorsign: true;
|
||||||
|
image: true;
|
||||||
};
|
};
|
||||||
}> | null,
|
}> | null,
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -202,6 +218,7 @@ const artikelKesehatanState = proxy({
|
|||||||
doctorSign: {
|
doctorSign: {
|
||||||
content: data.doctorsign.content,
|
content: data.doctorsign.content,
|
||||||
},
|
},
|
||||||
|
imageId: data.imageId,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async submit() {
|
async submit() {
|
||||||
@@ -242,6 +259,7 @@ const artikelKesehatanState = proxy({
|
|||||||
doctorSign: {
|
doctorSign: {
|
||||||
content: artikelKesehatanState.edit.form.doctorSign.content,
|
content: artikelKesehatanState.edit.form.doctorSign.content,
|
||||||
},
|
},
|
||||||
|
imageId: artikelKesehatanState.edit.form.imageId,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
@@ -280,12 +298,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) {
|
||||||
|
|||||||
@@ -116,27 +116,38 @@ const fasilitasKesehatan = 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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -558,7 +569,7 @@ const dokter = proxy({
|
|||||||
|
|
||||||
const fasilitasKesehatanState = proxy({
|
const fasilitasKesehatanState = proxy({
|
||||||
fasilitasKesehatan,
|
fasilitasKesehatan,
|
||||||
dokter
|
dokter,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default fasilitasKesehatanState;
|
export default fasilitasKesehatanState;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,290 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { proxy } from "valtio";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const templateForm = z.object({
|
||||||
|
name: z.string().min(1, "Name minimal 1 karakter"),
|
||||||
|
tanggal: z.string().min(1, "Tanggal minimal 1 karakter"),
|
||||||
|
namaOrangtua: z.string().min(1, "Nama Orangtua minimal 1 karakter"),
|
||||||
|
nomor: z.string().min(1, "Nomor minimal 1 karakter"),
|
||||||
|
alamat: z.string().min(1, "Alamat minimal 1 karakter"),
|
||||||
|
catatan: z.string().min(1, "Catatan minimal 1 karakter"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultForm = {
|
||||||
|
name: "",
|
||||||
|
tanggal: "",
|
||||||
|
namaOrangtua: "",
|
||||||
|
nomor: "",
|
||||||
|
alamat: "",
|
||||||
|
catatan: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const pendaftaranJadwalKegiatanState = proxy({
|
||||||
|
create: {
|
||||||
|
form: { ...defaultForm },
|
||||||
|
loading: false,
|
||||||
|
async submit() {
|
||||||
|
const cek = templateForm.safeParse(this.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const errMsg = cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}: ${v.message}`)
|
||||||
|
.join("\n");
|
||||||
|
toast.error(errMsg);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loading = true;
|
||||||
|
const payload = { ...this.form };
|
||||||
|
|
||||||
|
const res = await (ApiFetch.api.kesehatan as any)[
|
||||||
|
"pendaftaran-jadwal-kegiatan"
|
||||||
|
].create.post(payload);
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
toast.success("Berhasil menambahkan jadwal kegiatan");
|
||||||
|
this.resetForm();
|
||||||
|
await pendaftaranJadwalKegiatanState.findMany.load();
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
const msg = err?.message || "Terjadi kesalahan saat mengirim data";
|
||||||
|
toast.error(msg);
|
||||||
|
console.error("SUBMIT ERROR:", err);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetForm() {
|
||||||
|
this.form = { ...defaultForm };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findMany: {
|
||||||
|
data: null as
|
||||||
|
| Prisma.PendaftaranJadwalKegiatanGetPayload<{
|
||||||
|
omit: {
|
||||||
|
isActive: true;
|
||||||
|
};
|
||||||
|
}>[]
|
||||||
|
| null,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
loading: false,
|
||||||
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
pendaftaranJadwalKegiatanState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
pendaftaranJadwalKegiatanState.findMany.page = page;
|
||||||
|
pendaftaranJadwalKegiatanState.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.kesehatan["pendaftaran-jadwal-kegiatan"][
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
pendaftaranJadwalKegiatanState.findMany.data = res.data.data ?? [];
|
||||||
|
pendaftaranJadwalKegiatanState.findMany.totalPages =
|
||||||
|
res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
pendaftaranJadwalKegiatanState.findMany.data = [];
|
||||||
|
pendaftaranJadwalKegiatanState.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
"Gagal fetch pendaftaran jadwal kegiatan paginated:",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
pendaftaranJadwalKegiatanState.findMany.data = [];
|
||||||
|
pendaftaranJadwalKegiatanState.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
pendaftaranJadwalKegiatanState.findMany.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.PendaftaranJadwalKegiatanGetPayload<{
|
||||||
|
omit: {
|
||||||
|
isActive: true;
|
||||||
|
};
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/kesehatan/pendaftaran-jadwal-kegiatan/${id}`
|
||||||
|
);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
pendaftaranJadwalKegiatanState.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch data", res.status, res.statusText);
|
||||||
|
pendaftaranJadwalKegiatanState.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
pendaftaranJadwalKegiatanState.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
|
try {
|
||||||
|
pendaftaranJadwalKegiatanState.delete.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/kesehatan/pendaftaran-jadwal-kegiatan/del/${id}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(
|
||||||
|
result.message || "Pendaftaran jadwal kegiatan berhasil dihapus"
|
||||||
|
);
|
||||||
|
await pendaftaranJadwalKegiatanState.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(
|
||||||
|
result?.message || "Gagal menghapus pendaftaran jadwal kegiatan"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error(
|
||||||
|
"Terjadi kesalahan saat menghapus pendaftaran jadwal kegiatan"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
pendaftaranJadwalKegiatanState.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/pendaftaran-jadwal-kegiatan/${id}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result?.success) {
|
||||||
|
const data = result.data;
|
||||||
|
this.id = data.id;
|
||||||
|
this.form = {
|
||||||
|
name: data.name,
|
||||||
|
tanggal: data.tanggal,
|
||||||
|
namaOrangtua: data.namaOrangtua,
|
||||||
|
nomor: data.nomor,
|
||||||
|
alamat: data.alamat,
|
||||||
|
catatan: data.catatan,
|
||||||
|
};
|
||||||
|
return data; // Return the loaded data
|
||||||
|
} else {
|
||||||
|
throw new Error(result?.message || "Gagal memuat data");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading pendaftaran jadwal kegiatan:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error ? error.message : "Gagal memuat data"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
const cek = templateForm.safeParse(pendaftaranJadwalKegiatanState.edit.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
toast.error(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pendaftaranJadwalKegiatanState.edit.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/kesehatan/pendaftaran-jadwal-kegiatan/${this.id}`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: this.form.name,
|
||||||
|
tanggal: this.form.tanggal,
|
||||||
|
namaOrangtua: this.form.namaOrangtua,
|
||||||
|
nomor: this.form.nomor,
|
||||||
|
alamat: this.form.alamat,
|
||||||
|
catatan: this.form.catatan,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
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 pendaftaran jadwal kegiatan");
|
||||||
|
await pendaftaranJadwalKegiatanState.findMany.load(); // refresh list
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || "Gagal update pendaftaran jadwal kegiatan");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating pendaftaran jadwal kegiatan:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Terjadi kesalahan saat update pendaftaran jadwal kegiatan"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
pendaftaranJadwalKegiatanState.edit.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
pendaftaranJadwalKegiatanState.edit.id = "";
|
||||||
|
pendaftaranJadwalKegiatanState.edit.form = { ...defaultForm };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default pendaftaranJadwalKegiatanState;
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const sdgsDesa = proxy({
|
|||||||
].get({
|
].get({
|
||||||
query,
|
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;
|
||||||
@@ -94,7 +94,7 @@ const sdgsDesa = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.SDGSDesaGetPayload<{
|
data: null as Prisma.SdgsDesaGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
image: true;
|
image: true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -354,14 +354,39 @@ const kategoriKegiatan = proxy({
|
|||||||
id: string;
|
id: string;
|
||||||
nama: string;
|
nama: string;
|
||||||
}> | null,
|
}> | null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.lingkungan.kategorikegiatan[
|
totalPages: 1,
|
||||||
"find-many"
|
loading: false,
|
||||||
].get();
|
search: "",
|
||||||
if (res.status === 200) {
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
kategoriKegiatan.findMany.data = res.data?.data ?? [];
|
kategoriKegiatan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
}
|
kategoriKegiatan.findMany.page = page;
|
||||||
},
|
kategoriKegiatan.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res =
|
||||||
|
await ApiFetch.api.lingkungan.kategorikegiatan[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
kategoriKegiatan.findMany.data = res.data.data ?? [];
|
||||||
|
kategoriKegiatan.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
kategoriKegiatan.findMany.data = [];
|
||||||
|
kategoriKegiatan.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch kategori kegiatan paginated:", err);
|
||||||
|
kategoriKegiatan.findMany.data = [];
|
||||||
|
kategoriKegiatan.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
kategoriKegiatan.findMany.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.KategoriKegiatanGetPayload<{
|
data: null as Prisma.KategoriKegiatanGetPayload<{
|
||||||
|
|||||||
@@ -352,17 +352,19 @@ const posisiOrganisasi = proxy({
|
|||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
search: "",
|
search: "",
|
||||||
load: async (page = 1, limit = 10, search = "") => {
|
load: async (page = 1, limit?: number, search = "") => {
|
||||||
posisiOrganisasi.findMany.loading = true; // ✅ Akses langsung via nama path
|
const appliedLimit = limit ?? 10;
|
||||||
posisiOrganisasi.findMany.page = page;
|
posisiOrganisasi.findMany.page = page;
|
||||||
posisiOrganisasi.findMany.search = search;
|
posisiOrganisasi.findMany.search = search;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const query: any = { page, limit };
|
const query: any = { page, limit: appliedLimit };
|
||||||
if (search) query.search = search;
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi["find-many"].get({ query });
|
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi[
|
||||||
|
"find-many"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
posisiOrganisasi.findMany.data = res.data.data ?? [];
|
posisiOrganisasi.findMany.data = res.data.data ?? [];
|
||||||
posisiOrganisasi.findMany.totalPages = res.data.totalPages ?? 1;
|
posisiOrganisasi.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
|||||||
@@ -90,6 +90,32 @@ const userState = proxy({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
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({
|
||||||
|
|||||||
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 loading="lazy" 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 loading="lazy" 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,7 +1,7 @@
|
|||||||
/* 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, Tooltip } from '@mantine/core';
|
import { ScrollArea, 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';
|
import { IconFileText, IconBuildingStore, IconSparkles, IconUsers } from '@tabler/icons-react';
|
||||||
@@ -62,43 +62,50 @@ function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
|
|||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Layanan</Title>
|
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Layanan</Title>
|
||||||
<Tabs
|
<Tabs
|
||||||
color={colors['blue-button']}
|
color={colors["blue-button"]}
|
||||||
variant='pills'
|
variant="pills"
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
<TabsList
|
{/* ✅ Scroll horizontal wrapper */}
|
||||||
p="sm"
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
style={{
|
<TabsList
|
||||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
p="sm"
|
||||||
borderRadius: "1rem",
|
style={{
|
||||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
}}
|
borderRadius: "1rem",
|
||||||
>
|
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||||
{tabs.map((tab, i) => (
|
display: "flex",
|
||||||
<Tooltip
|
flexWrap: "nowrap",
|
||||||
key={i}
|
gap: "0.5rem",
|
||||||
label={tab.tooltip}
|
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||||
position="bottom"
|
}}
|
||||||
withArrow
|
>
|
||||||
transitionProps={{ transition: 'pop', duration: 200 }}
|
{tabs.map((tab, i) => (
|
||||||
>
|
<Tooltip
|
||||||
<TabsTab
|
key={i}
|
||||||
value={tab.value}
|
label={tab.tooltip}
|
||||||
leftSection={tab.icon}
|
position="bottom"
|
||||||
style={{
|
withArrow
|
||||||
fontWeight: 600,
|
transitionProps={{ transition: 'pop', duration: 200 }}
|
||||||
fontSize: "0.9rem",
|
|
||||||
transition: "all 0.2s ease",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{tab.label}
|
<TabsTab
|
||||||
</TabsTab>
|
value={tab.value}
|
||||||
</Tooltip>
|
leftSection={tab.icon}
|
||||||
))}
|
style={{
|
||||||
</TabsList>
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
transition: "all 0.2s ease",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel
|
<TabsPanel
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* 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, Tooltip } from '@mantine/core';
|
import { ScrollArea, 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';
|
import { IconNews, IconCategory } from '@tabler/icons-react';
|
||||||
@@ -49,43 +49,50 @@ function LayoutTabsBerita({ children }: { children: React.ReactNode }) {
|
|||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Berita Desa</Title>
|
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Berita Desa</Title>
|
||||||
<Tabs
|
<Tabs
|
||||||
color={colors['blue-button']}
|
color={colors["blue-button"]}
|
||||||
variant="pills"
|
variant="pills"
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
<TabsList
|
{/* ✅ Scroll horizontal wrapper */}
|
||||||
p="sm"
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
style={{
|
<TabsList
|
||||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
p="sm"
|
||||||
borderRadius: "1rem",
|
style={{
|
||||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
}}
|
borderRadius: "1rem",
|
||||||
>
|
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||||
{tabs.map((tab, i) => (
|
display: "flex",
|
||||||
<Tooltip
|
flexWrap: "nowrap",
|
||||||
key={i}
|
gap: "0.5rem",
|
||||||
label={tab.tooltip}
|
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||||
position="bottom"
|
}}
|
||||||
withArrow
|
>
|
||||||
transitionProps={{ transition: 'pop', duration: 200 }}
|
{tabs.map((tab, i) => (
|
||||||
>
|
<Tooltip
|
||||||
<TabsTab
|
key={i}
|
||||||
value={tab.value}
|
label={tab.tooltip}
|
||||||
leftSection={tab.icon}
|
position="bottom"
|
||||||
style={{
|
withArrow
|
||||||
fontWeight: 600,
|
transitionProps={{ transition: 'pop', duration: 200 }}
|
||||||
fontSize: "0.9rem",
|
|
||||||
transition: "all 0.2s ease",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{tab.label}
|
<TabsTab
|
||||||
</TabsTab>
|
value={tab.value}
|
||||||
</Tooltip>
|
leftSection={tab.icon}
|
||||||
))}
|
style={{
|
||||||
</TabsList>
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
transition: "all 0.2s ease",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel
|
<TabsPanel
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ function EditBerita() {
|
|||||||
objectFit: "contain",
|
objectFit: "contain",
|
||||||
border: `1px solid ${colors["blue-button"]}`,
|
border: `1px solid ${colors["blue-button"]}`,
|
||||||
}}
|
}}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ function DetailBerita() {
|
|||||||
h={200}
|
h={200}
|
||||||
radius="md"
|
radius="md"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
|
loading='lazy'
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Text fz="sm" c="dimmed">Tidak ada gambar</Text>
|
<Text fz="sm" c="dimmed">Tidak ada gambar</Text>
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ export default function CreateBerita() {
|
|||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
border: '1px solid #ddd',
|
border: '1px solid #ddd',
|
||||||
}}
|
}}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -96,9 +96,11 @@ function ListBerita({ search }: { search: string }) {
|
|||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd style={{ width: '30%' }}>
|
<TableTd style={{ width: '30%' }}>
|
||||||
<Text fw={500} truncate="end" lineClamp={1}>
|
<Box w={200}>
|
||||||
{item.judul}
|
<Text fw={500} truncate="end" lineClamp={1}>
|
||||||
</Text>
|
{item.judul}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ width: '20%' }}>
|
<TableTd style={{ width: '20%' }}>
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed">
|
||||||
@@ -112,7 +114,7 @@ function ListBerita({ search }: { search: string }) {
|
|||||||
style={{ borderRadius: 8, overflow: 'hidden' }}
|
style={{ borderRadius: 8, overflow: 'hidden' }}
|
||||||
>
|
>
|
||||||
{item.image?.link ? (
|
{item.image?.link ? (
|
||||||
<Image src={item.image.link} alt="gambar" fit="cover" />
|
<Image loading='lazy' src={item.image.link} alt="gambar" fit="cover" />
|
||||||
) : (
|
) : (
|
||||||
<Box bg={colors['blue-button']} w="100%" h="100%" />
|
<Box bg={colors['blue-button']} w="100%" h="100%" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export default function ListImage() {
|
|||||||
radius="md"
|
radius="md"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
stateFileStorage
|
stateFileStorage
|
||||||
.del({ name: v.name })
|
.del({ id: v.id })
|
||||||
.finally(() => toast("Foto berhasil dihapus"));
|
.finally(() => toast("Foto berhasil dihapus"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -131,6 +131,7 @@ export default function ListImage() {
|
|||||||
h={120}
|
h={120}
|
||||||
fit="contain"
|
fit="contain"
|
||||||
opacity={0.7}
|
opacity={0.7}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<Text c="dimmed" ta="center">
|
<Text c="dimmed" ta="center">
|
||||||
Belum ada foto yang tersedia
|
Belum ada foto yang tersedia
|
||||||
@@ -143,14 +144,15 @@ export default function ListImage() {
|
|||||||
<Flex justify="center">
|
<Flex justify="center">
|
||||||
<Pagination
|
<Pagination
|
||||||
total={total}
|
total={total}
|
||||||
|
value={stateFileStorage.page} // Changed from page to value
|
||||||
size="md"
|
size="md"
|
||||||
radius="md"
|
radius="md"
|
||||||
withEdges
|
withEdges
|
||||||
onChange={(page) => {
|
onChange={(page) => {
|
||||||
stateFileStorage.page = page;
|
stateFileStorage.load({ page });
|
||||||
stateFileStorage.load();
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* 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, Tooltip } from '@mantine/core';
|
import { ScrollArea, 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 { IconPhoto, IconVideo } from '@tabler/icons-react';
|
import { IconPhoto, IconVideo } from '@tabler/icons-react';
|
||||||
@@ -48,43 +48,50 @@ function LayoutTabsGallery({ children }: { children: React.ReactNode }) {
|
|||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Gallery</Title>
|
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Gallery</Title>
|
||||||
<Tabs
|
<Tabs
|
||||||
color={colors['blue-button']}
|
color={colors["blue-button"]}
|
||||||
variant='pills'
|
variant="pills"
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
<TabsList
|
{/* ✅ Scroll horizontal wrapper */}
|
||||||
p="sm"
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
style={{
|
<TabsList
|
||||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
p="sm"
|
||||||
borderRadius: "1rem",
|
style={{
|
||||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
}}
|
borderRadius: "1rem",
|
||||||
>
|
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||||
{tabs.map((tab, i) => (
|
display: "flex",
|
||||||
<Tooltip
|
flexWrap: "nowrap",
|
||||||
key={i}
|
gap: "0.5rem",
|
||||||
label={tab.tooltip}
|
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||||
position="bottom"
|
}}
|
||||||
withArrow
|
>
|
||||||
transitionProps={{ transition: 'pop', duration: 200 }}
|
{tabs.map((tab, i) => (
|
||||||
>
|
<Tooltip
|
||||||
<TabsTab
|
key={i}
|
||||||
value={tab.value}
|
label={tab.tooltip}
|
||||||
leftSection={tab.icon}
|
position="bottom"
|
||||||
style={{
|
withArrow
|
||||||
fontWeight: 600,
|
transitionProps={{ transition: 'pop', duration: 200 }}
|
||||||
fontSize: "0.9rem",
|
|
||||||
transition: "all 0.2s ease",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{tab.label}
|
<TabsTab
|
||||||
</TabsTab>
|
value={tab.value}
|
||||||
</Tooltip>
|
leftSection={tab.icon}
|
||||||
))}
|
style={{
|
||||||
</TabsList>
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
transition: "all 0.2s ease",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel
|
<TabsPanel
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ function EditSuratKeterangan() {
|
|||||||
{/* Upload Gambar 1 */}
|
{/* Upload Gambar 1 */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold" fz="sm" mb={6}>
|
<Text fw="bold" fz="sm" mb={6}>
|
||||||
Gambar 1
|
Gambar Konten Pelayanan
|
||||||
</Text>
|
</Text>
|
||||||
<Dropzone
|
<Dropzone
|
||||||
onDrop={(files) => {
|
onDrop={(files) => {
|
||||||
@@ -188,6 +188,7 @@ function EditSuratKeterangan() {
|
|||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
border: `1px solid ${colors['blue-button']}`,
|
border: `1px solid ${colors['blue-button']}`,
|
||||||
}}
|
}}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@@ -196,7 +197,7 @@ function EditSuratKeterangan() {
|
|||||||
{/* Upload Gambar 2 */}
|
{/* Upload Gambar 2 */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold" fz="sm" mb={6}>
|
<Text fw="bold" fz="sm" mb={6}>
|
||||||
Gambar 2
|
Gambar Alur Pelayanan Surat
|
||||||
</Text>
|
</Text>
|
||||||
<Dropzone
|
<Dropzone
|
||||||
onDrop={(files) => {
|
onDrop={(files) => {
|
||||||
@@ -244,6 +245,7 @@ function EditSuratKeterangan() {
|
|||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
border: `1px solid ${colors['blue-button']}`,
|
border: `1px solid ${colors['blue-button']}`,
|
||||||
}}
|
}}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ function DetailSuratKeterangan() {
|
|||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">
|
<Text fz="lg" fw="bold">
|
||||||
Gambar
|
Gambar Konten Pelayanan
|
||||||
</Text>
|
</Text>
|
||||||
{data?.image?.link ? (
|
{data?.image?.link ? (
|
||||||
<Image
|
<Image
|
||||||
@@ -110,6 +110,7 @@ function DetailSuratKeterangan() {
|
|||||||
h={200}
|
h={200}
|
||||||
radius="md"
|
radius="md"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed">
|
||||||
@@ -120,7 +121,7 @@ function DetailSuratKeterangan() {
|
|||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">
|
<Text fz="lg" fw="bold">
|
||||||
Gambar 2
|
Gambar Alur Pelayanan Surat
|
||||||
</Text>
|
</Text>
|
||||||
{data?.image2?.link ? (
|
{data?.image2?.link ? (
|
||||||
<Image
|
<Image
|
||||||
@@ -130,6 +131,7 @@ function DetailSuratKeterangan() {
|
|||||||
h={200}
|
h={200}
|
||||||
radius="md"
|
radius="md"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed">
|
||||||
|
|||||||
@@ -126,10 +126,10 @@ function CreateSuratKeterangan() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Gambar Utama */}
|
{/* Gambar Konten Pelayanan */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold" fz="sm" mb={6}>
|
<Text fw="bold" fz="sm" mb={6}>
|
||||||
Gambar Utama
|
Gambar Konten Pelayanan
|
||||||
</Text>
|
</Text>
|
||||||
<Dropzone
|
<Dropzone
|
||||||
onDrop={(files) => {
|
onDrop={(files) => {
|
||||||
@@ -170,15 +170,16 @@ function CreateSuratKeterangan() {
|
|||||||
alt="Preview Gambar Utama"
|
alt="Preview Gambar Utama"
|
||||||
radius="md"
|
radius="md"
|
||||||
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Gambar Tambahan */}
|
{/* Gambar Alur Pelayanan Surat */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold" fz="sm" mb={6}>
|
<Text fw="bold" fz="sm" mb={6}>
|
||||||
Gambar Tambahan (Opsional)
|
Gambar Alur Pelayanan Surat
|
||||||
</Text>
|
</Text>
|
||||||
<Dropzone
|
<Dropzone
|
||||||
onDrop={(files) => {
|
onDrop={(files) => {
|
||||||
@@ -219,6 +220,7 @@ function CreateSuratKeterangan() {
|
|||||||
alt="Preview Gambar Tambahan"
|
alt="Preview Gambar Tambahan"
|
||||||
radius="md"
|
radius="md"
|
||||||
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
Title,
|
Title,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
@@ -124,14 +124,16 @@ function ListSuratKeterangan({ search }: { search: string }) {
|
|||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ width: '15%' }}>
|
<TableTd style={{ width: '15%' }}>
|
||||||
<Button
|
<Button
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
variant="light"
|
variant="light"
|
||||||
color="blue"
|
color="blue"
|
||||||
|
leftSection={<IconDeviceImacCog size={16} />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push(`/admin/desa/layanan/pelayanan_surat_keterangan/${item.id}`)
|
router.push(`/admin/desa/layanan/pelayanan_surat_keterangan/${item.id}`)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<IconDeviceImac size={20} />
|
Detail
|
||||||
<Text ml={5}>Detail</Text>
|
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ function EditPenghargaan() {
|
|||||||
alt="Preview Gambar"
|
alt="Preview Gambar"
|
||||||
radius="md"
|
radius="md"
|
||||||
style={{ maxHeight: 220, objectFit: 'contain', border: `1px solid ${colors['blue-button']}` }}
|
style={{ maxHeight: 220, objectFit: 'contain', border: `1px solid ${colors['blue-button']}` }}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ function DetailPenghargaan() {
|
|||||||
h={200}
|
h={200}
|
||||||
radius="md"
|
radius="md"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed">
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ function CreatePenghargaan() {
|
|||||||
alt="Preview Gambar"
|
alt="Preview Gambar"
|
||||||
radius="md"
|
radius="md"
|
||||||
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
Title,
|
Title,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImacCog, IconPlus, IconSearch } 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';
|
||||||
@@ -113,14 +113,16 @@ function ListPenghargaan({ search }: { search: string }) {
|
|||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
size="xs"
|
||||||
color="blue"
|
radius="md"
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
leftSection={<IconDeviceImacCog size={16} />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push(`/admin/desa/penghargaan/${item.id}`)
|
router.push(`/admin/desa/penghargaan/${item.id}`)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<IconDeviceImac size={20} />
|
Detail
|
||||||
<Text ml={5}>Detail</Text>
|
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* 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, Tooltip } from '@mantine/core';
|
import { ScrollArea, 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 { IconListDetails, IconCategory } from '@tabler/icons-react';
|
import { IconListDetails, IconCategory } from '@tabler/icons-react';
|
||||||
@@ -48,37 +48,44 @@ function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
|
|||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Pengumuman</Title>
|
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Pengumuman</Title>
|
||||||
<Tabs
|
<Tabs
|
||||||
color={colors['blue-button']}
|
color={colors["blue-button"]}
|
||||||
variant='pills'
|
variant="pills"
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
<TabsList
|
{/* ✅ Scroll horizontal wrapper */}
|
||||||
p="sm"
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
style={{
|
<TabsList
|
||||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
p="sm"
|
||||||
borderRadius: "1rem",
|
style={{
|
||||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
}}
|
borderRadius: "1rem",
|
||||||
>
|
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||||
{tabs.map((tab, i) => (
|
display: "flex",
|
||||||
<Tooltip key={i} label={tab.tooltip} position="bottom" withArrow transitionProps={{ transition: 'pop', duration: 200 }}>
|
flexWrap: "nowrap",
|
||||||
<TabsTab
|
gap: "0.5rem",
|
||||||
value={tab.value}
|
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||||
leftSection={tab.icon}
|
}}
|
||||||
style={{
|
>
|
||||||
fontWeight: 600,
|
{tabs.map((tab, i) => (
|
||||||
fontSize: "0.9rem",
|
<Tooltip key={i} label={tab.tooltip} position="bottom" withArrow transitionProps={{ transition: 'pop', duration: 200 }}>
|
||||||
transition: "all 0.2s ease",
|
<TabsTab
|
||||||
}}
|
value={tab.value}
|
||||||
>
|
leftSection={tab.icon}
|
||||||
{tab.label}
|
style={{
|
||||||
</TabsTab>
|
fontWeight: 600,
|
||||||
</Tooltip>
|
fontSize: "0.9rem",
|
||||||
))}
|
transition: "all 0.2s ease",
|
||||||
</TabsList>
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel
|
<TabsPanel
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* 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, Tooltip } from '@mantine/core';
|
import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
|
||||||
import { IconCategory, IconListCheck } from '@tabler/icons-react';
|
import { IconCategory, IconListCheck } from '@tabler/icons-react';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
@@ -48,37 +48,44 @@ function LayoutTabsPotensi({ children }: { children: React.ReactNode }) {
|
|||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Potensi</Title>
|
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Potensi</Title>
|
||||||
<Tabs
|
<Tabs
|
||||||
color={colors['blue-button']}
|
color={colors["blue-button"]}
|
||||||
variant='pills'
|
variant="pills"
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
<TabsList
|
{/* ✅ Scroll horizontal wrapper */}
|
||||||
p="sm"
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
style={{
|
<TabsList
|
||||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
p="sm"
|
||||||
borderRadius: "1rem",
|
style={{
|
||||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
}}
|
borderRadius: "1rem",
|
||||||
>
|
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||||
{tabs.map((tab, i) => (
|
display: "flex",
|
||||||
<Tooltip key={i} label={tab.tooltip} position="bottom" withArrow transitionProps={{ transition: 'pop', duration: 200 }}>
|
flexWrap: "nowrap",
|
||||||
<TabsTab
|
gap: "0.5rem",
|
||||||
value={tab.value}
|
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||||
leftSection={tab.icon}
|
}}
|
||||||
style={{
|
>
|
||||||
fontWeight: 600,
|
{tabs.map((tab, i) => (
|
||||||
fontSize: "0.9rem",
|
<Tooltip key={i} label={tab.tooltip} position="bottom" withArrow transitionProps={{ transition: 'pop', duration: 200 }}>
|
||||||
transition: "all 0.2s ease",
|
<TabsTab
|
||||||
}}
|
value={tab.value}
|
||||||
>
|
leftSection={tab.icon}
|
||||||
{tab.label}
|
style={{
|
||||||
</TabsTab>
|
fontWeight: 600,
|
||||||
</Tooltip>
|
fontSize: "0.9rem",
|
||||||
))}
|
transition: "all 0.2s ease",
|
||||||
</TabsList>
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel
|
<TabsPanel
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ function EditPotensi() {
|
|||||||
objectFit: "contain",
|
objectFit: "contain",
|
||||||
border: `1px solid ${colors["blue-button"]}`,
|
border: `1px solid ${colors["blue-button"]}`,
|
||||||
}}
|
}}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ export default function DetailPotensi() {
|
|||||||
h={200}
|
h={200}
|
||||||
radius="md"
|
radius="md"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
|
loading='lazy'
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Text fz="sm" c="dimmed">Tidak ada gambar</Text>
|
<Text fz="sm" c="dimmed">Tidak ada gambar</Text>
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ function CreatePotensi() {
|
|||||||
alt="Preview Gambar"
|
alt="Preview Gambar"
|
||||||
radius="md"
|
radius="md"
|
||||||
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
||||||
|
loading='lazy'
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@@ -195,7 +196,7 @@ function CreatePotensi() {
|
|||||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Simpan Potensi
|
Simpan
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -120,12 +120,14 @@ function ListPotensi({ search }: { search: string }) {
|
|||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
variant="light"
|
variant="light"
|
||||||
color="blue"
|
color="blue"
|
||||||
|
leftSection={<IconDeviceImacCog size={16} />}
|
||||||
onClick={() => router.push(`/admin/desa/potensi/list-potensi/${item.id}`)}
|
onClick={() => router.push(`/admin/desa/potensi/list-potensi/${item.id}`)}
|
||||||
>
|
>
|
||||||
<IconDeviceImacCog size={20} />
|
Detail
|
||||||
<Text ml={5}>Detail</Text>
|
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* 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, Tooltip } from '@mantine/core';
|
import { ScrollArea, 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 { IconUser, IconUsers, IconCalendar } from '@tabler/icons-react';
|
import { IconUser, IconUsers, IconCalendar } from '@tabler/icons-react';
|
||||||
@@ -55,56 +55,63 @@ function LayoutTabsDetail({ children }: { children: React.ReactNode }) {
|
|||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Profile Desa</Title>
|
<Title order={3} fw={700} style={{ color: "#1A1B1E" }}>Profile Desa</Title>
|
||||||
<Tabs
|
<Tabs
|
||||||
color={colors['blue-button']}
|
color={colors["blue-button"]}
|
||||||
variant='pills'
|
variant="pills"
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
<TabsList
|
{/* ✅ Scroll horizontal wrapper */}
|
||||||
p="sm"
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
style={{
|
<TabsList
|
||||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
p="sm"
|
||||||
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>
|
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
|
||||||
<TabsPanel
|
|
||||||
key={i}
|
|
||||||
value={tab.value}
|
|
||||||
style={{
|
style={{
|
||||||
padding: "1.5rem",
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
background: "linear-gradient(180deg, #ffffff, #f5f6fa)",
|
|
||||||
borderRadius: "1rem",
|
borderRadius: "1rem",
|
||||||
boxShadow: "0 4px 16px rgba(0,0,0,0.05)",
|
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
gap: "0.5rem",
|
||||||
|
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Konten dummy, bisa diganti sesuai routing */}
|
{tabs.map((tab, i) => (
|
||||||
<>{children}</>
|
<Tooltip key={i} label={tab.tooltip} position="bottom" withArrow transitionProps={{ transition: 'pop', duration: 200 }}>
|
||||||
</TabsPanel>
|
<TabsTab
|
||||||
))}
|
value={tab.value}
|
||||||
</Tabs>
|
leftSection={tab.icon}
|
||||||
</Stack>
|
style={{
|
||||||
);
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
transition: "all 0.2s ease",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
|
{tabs.map((tab, i) => (
|
||||||
|
<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>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LayoutTabsDetail;
|
export default LayoutTabsDetail;
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ function Page() {
|
|||||||
height={180}
|
height={180}
|
||||||
fit="cover"
|
fit="cover"
|
||||||
radius="sm"
|
radius="sm"
|
||||||
|
loading='lazy'
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={`Label Gambar ${index + 1}`}
|
label={`Label Gambar ${index + 1}`}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
|
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
|
||||||
<Stack gap={0}>
|
<Stack gap={0}>
|
||||||
<Center>
|
<Center>
|
||||||
<Image src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
|
<Image loading='lazy' src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
|
||||||
</Center>
|
</Center>
|
||||||
<Paper
|
<Paper
|
||||||
bg={colors['blue-button']}
|
bg={colors['blue-button']}
|
||||||
@@ -102,7 +102,7 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
|
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
|
||||||
<Stack gap={0}>
|
<Stack gap={0}>
|
||||||
<Center>
|
<Center>
|
||||||
<Image src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
|
<Image loading='lazy' src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
|
||||||
</Center>
|
</Center>
|
||||||
<Paper
|
<Paper
|
||||||
bg={colors['blue-button']}
|
bg={colors['blue-button']}
|
||||||
@@ -152,7 +152,7 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
|
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
|
||||||
<Stack gap={0}>
|
<Stack gap={0}>
|
||||||
<Center>
|
<Center>
|
||||||
<Image src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
|
<Image loading='lazy' src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
|
||||||
</Center>
|
</Center>
|
||||||
<Paper
|
<Paper
|
||||||
bg={colors['blue-button']}
|
bg={colors['blue-button']}
|
||||||
@@ -199,7 +199,7 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
|
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
|
||||||
<Stack gap={0}>
|
<Stack gap={0}>
|
||||||
<Center>
|
<Center>
|
||||||
<Image src="/pudak-icon.png" w={{ base: 150, md: 250 }} alt="Maskot Desa" />
|
<Image loading='lazy' src="/pudak-icon.png" w={{ base: 150, md: 250 }} alt="Maskot Desa" />
|
||||||
</Center>
|
</Center>
|
||||||
<Paper
|
<Paper
|
||||||
bg={colors['blue-button']}
|
bg={colors['blue-button']}
|
||||||
@@ -228,6 +228,7 @@ function Page() {
|
|||||||
fit="cover"
|
fit="cover"
|
||||||
radius="md"
|
radius="md"
|
||||||
style={{ border: '1px solid #ccc' }}
|
style={{ border: '1px solid #ccc' }}
|
||||||
|
loading='lazy'
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
<Text ta="center" mt="xs" fw="bold">{img.label}</Text>
|
<Text ta="center" mt="xs" fw="bold">{img.label}</Text>
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ function EditPerbekelDariMasaKeMasa() {
|
|||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
border: `1px solid ${colors['blue-button']}`,
|
border: `1px solid ${colors['blue-button']}`,
|
||||||
}}
|
}}
|
||||||
|
loading='lazy'
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ function DetailPerbekelDariMasa() {
|
|||||||
h={150}
|
h={150}
|
||||||
radius="md"
|
radius="md"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
|
loading='lazy'
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Text fz="sm" c="dimmed">Tidak ada gambar</Text>
|
<Text fz="sm" c="dimmed">Tidak ada gambar</Text>
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ function CreatePerbekelDariMasaKeMasa() {
|
|||||||
alt="Preview Gambar"
|
alt="Preview Gambar"
|
||||||
radius="md"
|
radius="md"
|
||||||
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
||||||
|
loading='lazy'
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core';
|
import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
@@ -82,12 +82,14 @@ function ListPerbekelDariMasaKeMasa({ search }: { search: string }) {
|
|||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
variant="light"
|
variant="light"
|
||||||
color="blue"
|
color="blue"
|
||||||
|
leftSection={<IconDeviceImacCog size={16} />}
|
||||||
onClick={() => router.push(`/admin/desa/profile/profile-perbekel-dari-masa-ke-masa/${item.id}`)}
|
onClick={() => router.push(`/admin/desa/profile/profile-perbekel-dari-masa-ke-masa/${item.id}`)}
|
||||||
>
|
>
|
||||||
<IconDeviceImac size={20} />
|
Detail
|
||||||
<Text ml={5}>Detail</Text>
|
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
|
|||||||