Compare commits
11 Commits
nico/9-sep
...
nico/24-se
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e4a7a1c0a | |||
| b5c044df6e | |||
| 0fc47c28ff | |||
| 8e25c91e85 | |||
| 068d8b1077 | |||
| 9f72e94557 | |||
| 79ad39fc55 | |||
| 39e1e7b575 | |||
| 4ceea5203f | |||
| a5d841bb6b | |||
| 6a7bd386ae |
@@ -80,9 +80,11 @@
|
|||||||
"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
|
||||||
@@ -672,17 +672,18 @@ model GalleryVideo {
|
|||||||
|
|
||||||
// ========================================= LAYANAN DESA ========================================= //
|
// ========================================= LAYANAN DESA ========================================= //
|
||||||
model PelayananSuratKeterangan {
|
model PelayananSuratKeterangan {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
deskripsi String @db.Text
|
deskripsi String @db.Text
|
||||||
image FileStorage? @relation("PelayananSuratKeteranganImage", fields: [imageId], references: [id])
|
image FileStorage? @relation("PelayananSuratKeteranganImage", fields: [imageId], references: [id])
|
||||||
imageId String?
|
imageId String?
|
||||||
image2 FileStorage? @relation("PelayananSuratKeteranganImage2", fields: [image2Id], references: [id])
|
image2 FileStorage? @relation("PelayananSuratKeteranganImage2", fields: [image2Id], references: [id])
|
||||||
image2Id String?
|
image2Id String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
|
AjukanPermohonan AjukanPermohonan[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model PelayananTelunjukSaktiDesa {
|
model PelayananTelunjukSaktiDesa {
|
||||||
@@ -717,6 +718,20 @@ model PelayananPendudukNonPermanen {
|
|||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model AjukanPermohonan {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
nama String
|
||||||
|
nik String
|
||||||
|
alamat String
|
||||||
|
nomorKk String
|
||||||
|
kategori PelayananSuratKeterangan @relation(fields: [kategoriId], references: [id])
|
||||||
|
kategoriId String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================= PENGHARGAAN ========================================= //
|
// ========================================= PENGHARGAAN ========================================= //
|
||||||
model Penghargaan {
|
model Penghargaan {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
@@ -972,8 +987,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 +1233,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 +1286,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 +1400,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 +1471,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 +1553,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 +2108,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: 195 KiB After Width: | Height: | Size: 47 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;
|
||||||
|
|||||||
@@ -71,6 +71,22 @@ const pelayananPendudukNonPermanenForm = {
|
|||||||
deskripsi: "",
|
deskripsi: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const templateAjukanForm = z.object({
|
||||||
|
nama: z.string().min(1).max(5000),
|
||||||
|
nik: z.string().min(1).max(5000),
|
||||||
|
alamat: z.string().min(1).max(5000),
|
||||||
|
nomorKk: z.string().min(1).max(5000),
|
||||||
|
kategoriId: z.string().min(1).max(5000),
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultAjukanForm = {
|
||||||
|
nama: "",
|
||||||
|
nik: "",
|
||||||
|
alamat: "",
|
||||||
|
nomorKk: "",
|
||||||
|
kategoriId: "",
|
||||||
|
};
|
||||||
|
|
||||||
const suratKeterangan = proxy({
|
const suratKeterangan = proxy({
|
||||||
create: {
|
create: {
|
||||||
form: { ...suratKeteranganForm },
|
form: { ...suratKeteranganForm },
|
||||||
@@ -146,6 +162,30 @@ const suratKeterangan = proxy({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
findManyAll: {
|
||||||
|
data: null as Prisma.PelayananSuratKeteranganGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}>[] | null,
|
||||||
|
loading: false,
|
||||||
|
load: async () => {
|
||||||
|
suratKeterangan.findManyAll.loading = true;
|
||||||
|
try {
|
||||||
|
const res = await ApiFetch.api.desa.layanan.pelayanansuratketerangan["findManyAll"].get();
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
suratKeterangan.findManyAll.data = res.data.data || [];
|
||||||
|
} else {
|
||||||
|
suratKeterangan.findManyAll.data = [];
|
||||||
|
console.error("Failed to load surat keterangan all:", res.data?.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading surat keterangan all:", error);
|
||||||
|
suratKeterangan.findManyAll.data = [];
|
||||||
|
} finally {
|
||||||
|
suratKeterangan.findManyAll.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as Prisma.PelayananSuratKeteranganGetPayload<{
|
data: null as Prisma.PelayananSuratKeteranganGetPayload<{
|
||||||
include: {
|
include: {
|
||||||
@@ -769,11 +809,250 @@ const pelayananPendudukNonPermanen = proxy({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ajukanPermohonan = proxy({
|
||||||
|
create: {
|
||||||
|
form: { ...defaultAjukanForm },
|
||||||
|
loading: false,
|
||||||
|
async create() {
|
||||||
|
const cek = templateAjukanForm.safeParse(
|
||||||
|
ajukanPermohonan.create.form
|
||||||
|
);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ajukanPermohonan.create.loading = true;
|
||||||
|
const res = await ApiFetch.api.desa.ajukanpermohonan[
|
||||||
|
"create"
|
||||||
|
].post(ajukanPermohonan.create.form);
|
||||||
|
if (res.status === 200) {
|
||||||
|
ajukanPermohonan.findMany.load();
|
||||||
|
return toast.success("Ajukan permohonan berhasil disimpan!");
|
||||||
|
}
|
||||||
|
return toast.error("Gagal menyimpan ajukan permohonan");
|
||||||
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
} finally {
|
||||||
|
ajukanPermohonan.create.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetForm() {
|
||||||
|
ajukanPermohonan.create.form = { ...defaultAjukanForm };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findMany: {
|
||||||
|
data: null as Prisma.AjukanPermohonanGetPayload<{
|
||||||
|
include: {
|
||||||
|
kategori: true;
|
||||||
|
};
|
||||||
|
}>[] | null,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
// Change to arrow function
|
||||||
|
ajukanPermohonan.findMany.loading = true; // Use the full path to access the property
|
||||||
|
ajukanPermohonan.findMany.page = page;
|
||||||
|
ajukanPermohonan.findMany.search = search;
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
const res = await ApiFetch.api.desa.ajukanpermohonan[
|
||||||
|
"findMany"
|
||||||
|
].get({
|
||||||
|
query,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
ajukanPermohonan.findMany.data = res.data.data || [];
|
||||||
|
ajukanPermohonan.findMany.total = res.data.total || 0;
|
||||||
|
ajukanPermohonan.findMany.totalPages = res.data.totalPages || 1;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to load ajukan permohonan:", res.data?.message);
|
||||||
|
ajukanPermohonan.findMany.data = [];
|
||||||
|
ajukanPermohonan.findMany.total = 0;
|
||||||
|
ajukanPermohonan.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading ajukan permohonan:", error);
|
||||||
|
ajukanPermohonan.findMany.data = [];
|
||||||
|
ajukanPermohonan.findMany.total = 0;
|
||||||
|
ajukanPermohonan.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
ajukanPermohonan.findMany.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.AjukanPermohonanGetPayload<{
|
||||||
|
include: {
|
||||||
|
kategori: true;
|
||||||
|
}
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/desa/ajukanpermohonan/${id}`
|
||||||
|
);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
ajukanPermohonan.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch ajukan permohonan:", res.statusText);
|
||||||
|
ajukanPermohonan.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching ajukan permohonan:", error);
|
||||||
|
ajukanPermohonan.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
try {
|
||||||
|
ajukanPermohonan.delete.loading = true;
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/desa/ajukanpermohonan/del/${id}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const result = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
toast.success(result.message || "Ajukan permohonan berhasil dihapus");
|
||||||
|
await ajukanPermohonan.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(result.message || "Gagal menghapus ajukan permohonan");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus ajukan permohonan");
|
||||||
|
} finally {
|
||||||
|
ajukanPermohonan.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
id: "",
|
||||||
|
form: { ...defaultAjukanForm },
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
async load(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/desa/ajukanpermohonan/${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,
|
||||||
|
nik: data.nik,
|
||||||
|
alamat: data.alamat,
|
||||||
|
nomorKk: data.nomorKk,
|
||||||
|
kategoriId: data.kategoriId,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || "Gagal memuat data");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching ajukan permohonan:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error ? error.message : "Gagal memuat data"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async update() {
|
||||||
|
const cek = templateAjukanForm.safeParse(
|
||||||
|
ajukanPermohonan.edit.form
|
||||||
|
);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ajukanPermohonan.edit.loading = true;
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/desa/ajukanpermohonan/${this.id}`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
nama: this.form.nama,
|
||||||
|
nik: this.form.nik,
|
||||||
|
alamat: this.form.alamat,
|
||||||
|
nomorKk: this.form.nomorKk,
|
||||||
|
kategoriId: this.form.kategoriId,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(
|
||||||
|
errorData.message || `HTTP error! status: ${response.status}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.success) {
|
||||||
|
toast.success(result.message || "Ajukan permohonan berhasil diupdate");
|
||||||
|
await ajukanPermohonan.findMany.load(); // refresh list
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
result.message || "Gagal mengupdate ajukan permohonan"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating ajukan permohonan:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Terjadi kesalahan saat update ajukan permohonan"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
ajukanPermohonan.edit.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const stateLayananDesa = proxy({
|
const stateLayananDesa = proxy({
|
||||||
suratKeterangan,
|
suratKeterangan,
|
||||||
pelayananPerizinanBerusaha,
|
pelayananPerizinanBerusaha,
|
||||||
pelayananTelunjukSaktiDesa,
|
pelayananTelunjukSaktiDesa,
|
||||||
pelayananPendudukNonPermanen,
|
pelayananPendudukNonPermanen,
|
||||||
|
ajukanPermohonan,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default stateLayananDesa;
|
export default stateLayananDesa;
|
||||||
|
|||||||
@@ -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,6 +115,7 @@ const artikelKesehatanState = proxy({
|
|||||||
firstaid: true;
|
firstaid: true;
|
||||||
mythvsfact: true;
|
mythvsfact: true;
|
||||||
doctorsign: true;
|
doctorsign: true;
|
||||||
|
image: true;
|
||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
@@ -159,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,
|
||||||
@@ -213,6 +218,7 @@ const artikelKesehatanState = proxy({
|
|||||||
doctorSign: {
|
doctorSign: {
|
||||||
content: data.doctorsign.content,
|
content: data.doctorsign.content,
|
||||||
},
|
},
|
||||||
|
imageId: data.imageId,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async submit() {
|
async submit() {
|
||||||
@@ -253,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(
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ function Login() {
|
|||||||
Login
|
Login
|
||||||
</Title>
|
</Title>
|
||||||
<Center>
|
<Center>
|
||||||
<Image src={"/darmasaba-icon.png"} alt="" w={80} />
|
<Image loading="lazy" src={"/darmasaba-icon.png"} alt="" w={80} />
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ function Registrasi() {
|
|||||||
Registrasi
|
Registrasi
|
||||||
</Title>
|
</Title>
|
||||||
<Center>
|
<Center>
|
||||||
<Image src={"/darmasaba-icon.png"} alt="" w={80} />
|
<Image loading="lazy" src={"/darmasaba-icon.png"} alt="" w={80} />
|
||||||
</Center>
|
</Center>
|
||||||
<Box>
|
<Box>
|
||||||
<TextInput placeholder='Username'
|
<TextInput placeholder='Username'
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, 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, IconUsersPlus } from '@tabler/icons-react';
|
||||||
|
|
||||||
function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
|
function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -37,6 +37,13 @@ function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
|
|||||||
href: "/admin/desa/layanan/pelayanan_penduduk_non_permanent",
|
href: "/admin/desa/layanan/pelayanan_penduduk_non_permanent",
|
||||||
icon: <IconUsers size={18} stroke={1.8} />,
|
icon: <IconUsers size={18} stroke={1.8} />,
|
||||||
tooltip: "Pendataan penduduk non-permanent"
|
tooltip: "Pendataan penduduk non-permanent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Ajukan Permohonan",
|
||||||
|
value: "ajukanpermohonan",
|
||||||
|
href: "/admin/desa/layanan/ajukan_permohonan",
|
||||||
|
icon: <IconUsersPlus size={18} stroke={1.8} />,
|
||||||
|
tooltip: "Ajukan permohonan"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -62,43 +69,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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
Group,
|
Group,
|
||||||
Image,
|
|
||||||
Pagination,
|
Pagination,
|
||||||
Paper,
|
Paper,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
@@ -18,7 +17,7 @@ import {
|
|||||||
TableTr,
|
TableTr,
|
||||||
Text,
|
Text,
|
||||||
Title,
|
Title,
|
||||||
Tooltip,
|
Tooltip
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||||
@@ -87,7 +86,6 @@ function ListBerita({ search }: { search: string }) {
|
|||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ width: '30%' }}>Judul</TableTh>
|
<TableTh style={{ width: '30%' }}>Judul</TableTh>
|
||||||
<TableTh style={{ width: '20%' }}>Kategori</TableTh>
|
<TableTh style={{ width: '20%' }}>Kategori</TableTh>
|
||||||
<TableTh style={{ width: '25%' }}>Gambar</TableTh>
|
|
||||||
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
|
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
@@ -96,28 +94,17 @@ 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={150}>
|
||||||
{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">
|
||||||
{item.kategoriBerita?.name || '-'}
|
{item.kategoriBerita?.name || '-'}
|
||||||
</Text>
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ width: '25%' }}>
|
|
||||||
<Box
|
|
||||||
w={80}
|
|
||||||
h={80}
|
|
||||||
style={{ borderRadius: 8, overflow: 'hidden' }}
|
|
||||||
>
|
|
||||||
{item.image?.link ? (
|
|
||||||
<Image src={item.image.link} alt="gambar" fit="cover" />
|
|
||||||
) : (
|
|
||||||
<Box bg={colors['blue-button']} w="100%" h="100%" />
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</TableTd>
|
|
||||||
<TableTd style={{ width: '15%' }}>
|
<TableTd style={{ width: '15%' }}>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
'use client'
|
||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Paper,
|
||||||
|
Select,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
Tooltip
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
function EditAjukanPermohonan() {
|
||||||
|
const router = useRouter();
|
||||||
|
const params = useParams();
|
||||||
|
const stateAjukan = useProxy(stateLayananDesa.ajukanPermohonan);
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
nama: stateAjukan.edit.form.nama,
|
||||||
|
nik: stateAjukan.edit.form.nik,
|
||||||
|
alamat: stateAjukan.edit.form.alamat,
|
||||||
|
nomorKk: stateAjukan.edit.form.nomorKk,
|
||||||
|
kategoriId: stateAjukan.edit.form.kategoriId,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
stateLayananDesa.suratKeterangan.findManyAll.load();
|
||||||
|
const loadAjukan = async () => {
|
||||||
|
const id = params?.id as string;
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await stateAjukan.edit.load(id);
|
||||||
|
if (data) {
|
||||||
|
setFormData({
|
||||||
|
nama: data.nama || '',
|
||||||
|
nik: data.nik || '',
|
||||||
|
alamat: data.alamat || '',
|
||||||
|
nomorKk: data.nomorKk || '',
|
||||||
|
kategoriId: data.kategoriId || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading ajukan:', error);
|
||||||
|
toast.error('Gagal memuat data ajukan');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadAjukan();
|
||||||
|
}, [params?.id]);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
stateAjukan.edit.form = {
|
||||||
|
...stateAjukan.edit.form,
|
||||||
|
...formData,
|
||||||
|
};
|
||||||
|
toast.success('Ajukan berhasil diperbarui!');
|
||||||
|
router.push('/admin/desa/layanan/ajukan_permohonan');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating ajukan:', error);
|
||||||
|
toast.error('Terjadi kesalahan saat memperbarui ajukan');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||||
|
{/* Back Button */}
|
||||||
|
<Group mb="md">
|
||||||
|
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Title order={4} ml="sm" c="dark">
|
||||||
|
Edit Ajukan Permohonan
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Paper
|
||||||
|
w={{ base: '100%', md: '50%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{ border: '1px solid #e0e0e0' }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
|
<TextInput
|
||||||
|
label="Nama"
|
||||||
|
placeholder="Masukkan nama"
|
||||||
|
value={formData.nama}
|
||||||
|
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
type="number"
|
||||||
|
label="NIK"
|
||||||
|
placeholder="Masukkan NIK"
|
||||||
|
value={formData.nik}
|
||||||
|
onChange={(e) => setFormData({ ...formData, nik: e.target.value })}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
label="Alamat"
|
||||||
|
placeholder="Masukkan alamat"
|
||||||
|
value={formData.alamat}
|
||||||
|
onChange={(e) => setFormData({ ...formData, alamat: e.target.value })}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
type="number"
|
||||||
|
label="Nomor KK"
|
||||||
|
placeholder="Masukkan nomor KK"
|
||||||
|
value={formData.nomorKk}
|
||||||
|
onChange={(e) => setFormData({ ...formData, nomorKk: e.target.value })}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
label="Kategori"
|
||||||
|
placeholder="Pilih kategori"
|
||||||
|
data={stateLayananDesa.suratKeterangan.findManyAll.data?.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}))}
|
||||||
|
value={formData.kategoriId || null}
|
||||||
|
onChange={(val: string | null) => {
|
||||||
|
if (val) {
|
||||||
|
const selected = stateLayananDesa.suratKeterangan.findMany.data?.find(
|
||||||
|
(item) => item.id === val
|
||||||
|
);
|
||||||
|
if (selected) {
|
||||||
|
stateAjukan.edit.form.kategoriId = selected.id;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stateAjukan.edit.form.kategoriId = '';
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
searchable
|
||||||
|
clearable
|
||||||
|
nothingFoundMessage="Tidak ditemukan"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Group justify="right">
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||||
|
color: '#fff',
|
||||||
|
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditAjukanPermohonan;
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
'use client'
|
||||||
|
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||||
|
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Paper,
|
||||||
|
Skeleton,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Tooltip
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
function DetailAjukanPermohonan() {
|
||||||
|
const ajukanPermohonanState = useProxy(stateLayananDesa.ajukanPermohonan);
|
||||||
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
|
const params = useParams();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
ajukanPermohonanState.findUnique.load(params?.id as string);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleHapus = () => {
|
||||||
|
if (selectedId) {
|
||||||
|
ajukanPermohonanState.delete.byId(selectedId);
|
||||||
|
setModalHapus(false);
|
||||||
|
setSelectedId(null);
|
||||||
|
router.push('/admin/desa/layanan/ajukan_permohonan');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!ajukanPermohonanState.findUnique.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
<Skeleton height={500} radius="md" />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ajukanPermohonanState.findUnique.data;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
{/* Tombol Kembali */}
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
leftSection={<IconArrowBack size={24} color={colors['blue-button']} />}
|
||||||
|
mb={15}
|
||||||
|
>
|
||||||
|
Kembali
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Paper
|
||||||
|
withBorder
|
||||||
|
w={{ base: '100%', md: '60%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
|
<Text fz="2xl" fw="bold" c={colors['blue-button']}>
|
||||||
|
Detail Surat Keterangan
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Paper bg="#ECEEF8" p="md" radius="md" shadow="xs">
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">
|
||||||
|
Nama
|
||||||
|
</Text>
|
||||||
|
<Text fz="md" c="dimmed">
|
||||||
|
{data?.nama || '-'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">
|
||||||
|
NIK
|
||||||
|
</Text>
|
||||||
|
<Text fz="md" c="dimmed">
|
||||||
|
{data?.nik || '-'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">
|
||||||
|
Alamat
|
||||||
|
</Text>
|
||||||
|
<Text fz="md" c="dimmed">
|
||||||
|
{data?.alamat || '-'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">
|
||||||
|
Nomor KK
|
||||||
|
</Text>
|
||||||
|
<Text fz="md" c="dimmed">
|
||||||
|
{data?.nomorKk || '-'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">
|
||||||
|
Kategori
|
||||||
|
</Text>
|
||||||
|
<Text fz="md" c="dimmed">
|
||||||
|
{data?.kategori.name || '-'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Group gap="sm">
|
||||||
|
<Tooltip label="Hapus Surat" withArrow position="top">
|
||||||
|
<Button
|
||||||
|
color="red"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(data.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}}
|
||||||
|
variant="light"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
disabled={ajukanPermohonanState.delete.loading}
|
||||||
|
>
|
||||||
|
<IconTrash size={20} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip label="Edit Surat" withArrow position="top">
|
||||||
|
<Button
|
||||||
|
color="green"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/desa/layanan/ajukan_permohonan/${data.id}/edit`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
variant="light"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Konfirmasi Hapus */}
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah Anda yakin ingin menghapus ajukan permohonan ini?"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailAjukanPermohonan;
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Pagination,
|
||||||
|
Paper,
|
||||||
|
Skeleton,
|
||||||
|
Stack,
|
||||||
|
Table,
|
||||||
|
TableTbody,
|
||||||
|
TableTd,
|
||||||
|
TableTh,
|
||||||
|
TableThead,
|
||||||
|
TableTr,
|
||||||
|
Text,
|
||||||
|
Title
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import HeaderSearch from '../../../_com/header';
|
||||||
|
import stateLayananDesa from '../../../_state/desa/layananDesa';
|
||||||
|
|
||||||
|
function AjukanPermohonan() {
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<HeaderSearch
|
||||||
|
title='Pelayanan Ajukan Permohonan'
|
||||||
|
placeholder='Cari nama atau deskripsi...'
|
||||||
|
searchIcon={<IconSearch size={20} />}
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||||
|
/>
|
||||||
|
<ListAjukanPermohonan search={search} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ListAjukanPermohonan({ search }: { search: string }) {
|
||||||
|
const AjukanPermohonanState = useProxy(stateLayananDesa.ajukanPermohonan);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
page,
|
||||||
|
totalPages,
|
||||||
|
loading,
|
||||||
|
load,
|
||||||
|
} = AjukanPermohonanState.findMany;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
load(page, 10, search);
|
||||||
|
}, [page, search]);
|
||||||
|
|
||||||
|
// Loading state
|
||||||
|
if (loading || !data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
<Skeleton height={600} radius="md" />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper withBorder bg={colors['white-1']} p={'lg'} shadow="md" radius="md">
|
||||||
|
<Title order={4}>List Ajukan Permohonan</Title>
|
||||||
|
<Box style={{ overflowX: "auto" }}>
|
||||||
|
<Table highlightOnHover>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh style={{ width: '30%' }}>Nama</TableTh>
|
||||||
|
<TableTh style={{ width: '45%' }}>Alamat</TableTh>
|
||||||
|
<TableTh style={{ width: '15%' }}>NIK</TableTh>
|
||||||
|
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
{data.length > 0 ? (
|
||||||
|
data.map((item) => (
|
||||||
|
<TableTr key={item.id}>
|
||||||
|
<TableTd style={{ width: '30%' }}>
|
||||||
|
<Box w={200}>
|
||||||
|
<Text fw={500} truncate="end" lineClamp={1}>
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd style={{ width: '45%' }}>
|
||||||
|
<Box w={200}>
|
||||||
|
<Text fw={500} truncate="end" lineClamp={1}>
|
||||||
|
{item.alamat}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd style={{ width: '45%' }}>
|
||||||
|
<Box w={200}>
|
||||||
|
<Text fw={500} truncate="end" lineClamp={1}>
|
||||||
|
{item.nik}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd style={{ width: '15%' }}>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
leftSection={<IconDeviceImacCog size={16} />}
|
||||||
|
onClick={() =>
|
||||||
|
router.push(`/admin/desa/layanan/ajukan_permohonan/${item.id}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableTr>
|
||||||
|
<TableTd colSpan={3}>
|
||||||
|
<Center py={20}>
|
||||||
|
<Text color="dimmed">Tidak ada data ajukan permohonan yang cocok</Text>
|
||||||
|
</Center>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
)}
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
<Center>
|
||||||
|
<Pagination
|
||||||
|
value={page}
|
||||||
|
onChange={(newPage) => {
|
||||||
|
load(newPage, 10, search);
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
}}
|
||||||
|
total={totalPages}
|
||||||
|
mt="md"
|
||||||
|
mb="md"
|
||||||
|
color="blue"
|
||||||
|
radius="md"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AjukanPermohonan;
|
||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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">
|
||||||
@@ -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">
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ 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>
|
||||||
)}
|
)}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -94,9 +94,11 @@ function ListPengumuman({ search }: { search: string }) {
|
|||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fw={500} truncate="end" lineClamp={1}>
|
<Box w={150}>
|
||||||
{item.judul}
|
<Text fw={500} truncate="end" lineClamp={1}>
|
||||||
</Text>
|
{item.judul}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed">
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 { Box, Button, Center, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip, Pagination } from '@mantine/core';
|
import { Box, Button, Center, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip, Pagination, Group } from '@mantine/core';
|
||||||
import { IconEdit, IconSearch, IconTrash, IconPlus } from '@tabler/icons-react';
|
import { IconEdit, IconSearch, IconTrash, IconPlus } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
@@ -60,7 +60,7 @@ function ListKategoriPotensi({ search }: { search: string }) {
|
|||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Box style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 15 }}>
|
<Group justify="space-between">
|
||||||
<Title order={4}>List Kategori Potensi</Title>
|
<Title order={4}>List Kategori Potensi</Title>
|
||||||
<Tooltip label="Tambah Kategori Potensi" withArrow>
|
<Tooltip label="Tambah Kategori Potensi" withArrow>
|
||||||
<Button
|
<Button
|
||||||
@@ -72,7 +72,7 @@ function ListKategoriPotensi({ search }: { search: string }) {
|
|||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Group>
|
||||||
|
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
<Box style={{ overflowX: 'auto' }}>
|
||||||
<Table highlightOnHover striped withRowBorders style={{ minWidth: '700px' }}>
|
<Table highlightOnHover striped withRowBorders style={{ minWidth: '700px' }}>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||