diff --git a/prisma/data/ekonomi/demografi-pekerjaan/demografi-pekerjaan.json b/prisma/data/ekonomi/demografi-pekerjaan/demografi-pekerjaan.json
new file mode 100644
index 00000000..344868c1
--- /dev/null
+++ b/prisma/data/ekonomi/demografi-pekerjaan/demografi-pekerjaan.json
@@ -0,0 +1,56 @@
+[
+ {
+ "id": "cmkf3kv0b0004vnys6hh7ugj2",
+ "pekerjaan": "Petani/Pekebun",
+ "lakiLaki": 180,
+ "perempuan": 120
+ },
+ {
+ "id": "cmkf3kv0b0004vnys6hh8vhk3",
+ "pekerjaan": "Perajin Industri",
+ "lakiLaki": 95,
+ "perempuan": 140
+ },
+ {
+ "id": "cmkf3kv0b0004vnys6hh9wil4",
+ "pekerjaan": "Pedagang/UMKM",
+ "lakiLaki": 130,
+ "perempuan": 170
+ },
+ {
+ "id": "cmkf3kv0b0004vnys6hh0xjm5",
+ "pekerjaan": "Karyawan Swasta",
+ "lakiLaki": 260,
+ "perempuan": 310
+ },
+ {
+ "id": "cmkf3kv0b0004vnys6hh1ykn6",
+ "pekerjaan": "PNS/TNI/Polri",
+ "lakiLaki": 85,
+ "perempuan": 75
+ },
+ {
+ "id": "cmkf3kv0b0004vnys6hh2zlo7",
+ "pekerjaan": "Buruh Harian Lepas",
+ "lakiLaki": 140,
+ "perempuan": 90
+ },
+ {
+ "id": "cmkf3kv0b0004vnys6hh3amp8",
+ "pekerjaan": "Wiraswasta",
+ "lakiLaki": 165,
+ "perempuan": 110
+ },
+ {
+ "id": "cmkf3kv0b0004vnys6hh4bnq9",
+ "pekerjaan": "Pelajar/Mahasiswa",
+ "lakiLaki": 220,
+ "perempuan": 240
+ },
+ {
+ "id": "cmkf3kv0b0004vnys6hh5cor0",
+ "pekerjaan": "Belum/Tidak Bekerja",
+ "lakiLaki": 70,
+ "perempuan": 95
+ }
+]
diff --git a/prisma/data/ekonomi/lowongan-kerja-lokal/lowongan-kerja-lokal.json b/prisma/data/ekonomi/lowongan-kerja-lokal/lowongan-kerja-lokal.json
new file mode 100644
index 00000000..4747aaa9
--- /dev/null
+++ b/prisma/data/ekonomi/lowongan-kerja-lokal/lowongan-kerja-lokal.json
@@ -0,0 +1,57 @@
+[
+ {
+ "id": "3b3e817b-c136-4488-ac79-9a7d408939a2",
+ "posisi": "Lowongan TPS3R Pudak Mesari",
+ "namaPerusahaan": "TPS3R Pudak Mesari Desa Darmasaba",
+ "lokasi": "Desa Darmasaba, Abiansemal, Kabupaten Badung, Bali",
+ "tipePekerjaan": "Freelance",
+ "gaji": "1.500.000",
+ "deskripsi": "Menjalankan tugas di TPS3R Pudak Mesari.",
+ "kualifikasi": "Usia 18-30 tahun, SMA/SMK minimal",
+ "notelp": "089647037426"
+ },
+ {
+ "id": "3b3e817b-c136-4488-bd80-9a7d408939a2",
+ "posisi": "Marketing Executive",
+ "namaPerusahaan": "PT Mitra Krida Mandiri (Dealer Honda MKM Darmasaba)",
+ "lokasi": "Jalan Raya Darmasaba No.169, Abiansemal, Badung, Bali",
+ "tipePekerjaan": "Full Time",
+ "gaji": "2.500.000",
+ "deskripsi": "Menjalankan tugas pemasaran dan penjualan produk Honda di area Darmasaba.",
+ "kualifikasi": "Usia 18-30 tahun, SMA/SMK minimal, memiliki sepeda motor Honda dan smartphone.",
+ "notelp": "081296001047"
+ },
+ {
+ "id": "3b3e817b-c136-4488-ce91-9a7d408939a2",
+ "posisi": "Kasir",
+ "namaPerusahaan": "GOGO DARMASABA",
+ "lokasi": "Jl. Raya Darmasaba, Darmasaba, Kec. Abiansemal, Kabupaten Badung, Bali",
+ "tipePekerjaan": "Full Time",
+ "gaji": "2.500.000",
+ "deskripsi": "Melakukan pelayanan kasir dan administrasi pelanggan di restoran/food service.",
+ "kualifikasi": "Wanita, 18-30 tahun, SMA/SMK minimal pengalaman 1-3 tahun sebagai kasir atau Customer Service.",
+ "notelp": "089647037426"
+ },
+ {
+ "id": "3b3e817b-c136-4488-df02-9a7d408939a2",
+ "posisi": "Kasir / Teknisi Handphone",
+ "namaPerusahaan": "Jaya Cell Darmasaba",
+ "lokasi": "Jl. Raya Darmasaba, Darmasaba, Abiansemal, Badung, Bali",
+ "tipePekerjaan": "Full Time",
+ "gaji": "2.000.000",
+ "deskripsi": "Melakukan pelayanan kasir serta teknisi ponsel termasuk troubleshooting dan perbaikan.",
+ "kualifikasi": "Tidak disebutkan pengalaman khusus, memiliki KTP dan keinginan kuat untuk bekerja.",
+ "notelp": "089647037426"
+ },
+ {
+ "id": "3b3e817b-c136-4488-eg13-9a7d408939a2",
+ "posisi": "Guru Les (Pengajar Anak)",
+ "namaPerusahaan": "Bimba AIUEO Darmasaba",
+ "lokasi": "Darmasaba, Kabupaten Badung, Bali",
+ "tipePekerjaan": "Full Time",
+ "gaji": "2.000.000",
+ "deskripsi": "Mengajar calistung dan perkembangan dasar anak usia 3-6 tahun.",
+ "kualifikasi": "Minimal SMA/SMK, komunikasi baik, berinteraksi dengan anak-anak.",
+ "notelp": "089647037426"
+ }
+]
diff --git a/prisma/data/ekonomi/pasar-desa/kategori-produk.json b/prisma/data/ekonomi/pasar-desa/kategori-produk.json
index c8c9b59e..7093047e 100644
--- a/prisma/data/ekonomi/pasar-desa/kategori-produk.json
+++ b/prisma/data/ekonomi/pasar-desa/kategori-produk.json
@@ -6,5 +6,17 @@
{
"id": "5c06chf7-123f-6hfe-0663-5e9h76e55060",
"nama": "Minuman"
+ },
+ {
+ "id": "5c06chf7-123f-7igd-0663-5e9h76e55060",
+ "nama": "Sembako"
+ },
+ {
+ "id": "5c06chf7-123f-8jhe-0663-5e9h76e55060",
+ "nama": "Sayur Mayur"
+ },
+ {
+ "id": "5c06chf7-123f-9kif-0663-5e9h76e55060",
+ "nama": "Protein Hewani"
}
]
diff --git a/prisma/data/ekonomi/pasar-desa/kategori-to-pasar.json b/prisma/data/ekonomi/pasar-desa/kategori-to-pasar.json
new file mode 100644
index 00000000..0ac0fae2
--- /dev/null
+++ b/prisma/data/ekonomi/pasar-desa/kategori-to-pasar.json
@@ -0,0 +1,42 @@
+[
+ {
+ "id": "f6b52033-5016-45d9-b0fd-b9d4b6c4729b",
+ "kategoriId": "5c06chf7-123f-9kif-0663-5e9h76e55060",
+ "pasarDesaId": "d62660a2-ac6b-428a-acf6-58cc837ef789"
+ },
+ {
+ "id": "d2ef373c-043c-44b5-adde-6a25a54199d3",
+ "kategoriId": "5c06chf7-123f-7igd-0663-5e9h76e55060",
+ "pasarDesaId": "d62660a2-ac6b-428a-acf6-58cc837ef789"
+ },
+ {
+ "id": "ad427752-fea0-4ef3-a312-5961eefd5ee3",
+ "kategoriId": "4b95bge6-012e-5ged-9552-4d8g65d44959",
+ "pasarDesaId": "d62660a2-ac6b-428a-acf6-58cc837ef789"
+ },
+ {
+ "id": "bd00ab59-7ac8-4d40-94de-a86bb0eb4557",
+ "kategoriId": "5c06chf7-123f-8jhe-0663-5e9h76e55060",
+ "pasarDesaId": "24c6b992-49da-4c6e-aebb-72cf89f75438"
+ },
+ {
+ "id": "b7d311a2-a23a-499d-a339-823c5e30849a",
+ "kategoriId": "5c06chf7-123f-7igd-0663-5e9h76e55060",
+ "pasarDesaId": "24c6b992-49da-4c6e-aebb-72cf89f75438"
+ },
+ {
+ "id": "50ccc6c9-92c1-4d86-9585-85d48d35f640",
+ "kategoriId": "4b95bge6-012e-5ged-9552-4d8g65d44959",
+ "pasarDesaId": "24c6b992-49da-4c6e-aebb-72cf89f75438"
+ },
+ {
+ "id": "3b27f795-1d1d-4655-90f9-b779a009094e",
+ "kategoriId": "4b95bge6-012e-5ged-9552-4d8g65d44959",
+ "pasarDesaId": "6dea2257-b710-4cd2-8d94-9b6737e658d8"
+ },
+ {
+ "id": "d45873c5-5948-40f9-a88d-aa0861132bae",
+ "kategoriId": "4b95bge6-012e-5ged-9552-4d8g65d44959",
+ "pasarDesaId": "1b7a17ea-83f7-4e73-a94d-96e2b4a623f2"
+ }
+]
diff --git a/prisma/data/ekonomi/pasar-desa/pasar-desa.json b/prisma/data/ekonomi/pasar-desa/pasar-desa.json
new file mode 100644
index 00000000..956975d7
--- /dev/null
+++ b/prisma/data/ekonomi/pasar-desa/pasar-desa.json
@@ -0,0 +1,46 @@
+[
+ {
+ "id": "1b7a17ea-83f7-4e73-a94d-96e2b4a623f2",
+ "nama": "Warung Pasar Darmasaba",
+ "harga": 30000,
+ "imageId": "cmkew56ls0000vnysrnzr9ttx",
+ "rating": 4.3,
+ "alamatUsaha": "Br. Baler Pasar, Desa Darmasaba, Kec. Abiansemal",
+ "kontak": "081234567890",
+ "deskripsi": "Warung tradisional yang menjual kebutuhan pokok harian seperti sembako, jajanan pasar, dan minuman.",
+ "kategoriProdukId": "5c06chf7-123f-7igd-0663-5e9h76e55060"
+ },
+ {
+ "id": "6dea2257-b710-4cd2-8d94-9b6737e658d8",
+ "nama": "Jajanan Pasar Bu Made",
+ "imageId": "cmkewaa2s0001vnysvvs9tu56",
+ "harga": 5000,
+ "rating": 4.6,
+ "alamatUsaha": "Jl. Raya Darmasaba, dekat Banjar Baler Pasar",
+ "kontak": "082145678901",
+ "deskripsi": "Menjual berbagai jajanan pasar tradisional Bali seperti laklak, klepon, dan pisang rai.",
+ "kategoriProdukId": "4b95bge6-012e-5ged-9552-4d8g65d44959"
+ },
+ {
+ "id": "24c6b992-49da-4c6e-aebb-72cf89f75438",
+ "nama": "Sayur Segar Pak Wayan",
+ "imageId": "cmkewcvfq0002vnys6985nm90",
+ "harga": 20000,
+ "rating": 4.4,
+ "alamatUsaha": "Area Pasar Desa Darmasaba",
+ "kontak": "087865432109",
+ "deskripsi": "Lapak sayur segar yang menyediakan sayuran lokal hasil petani sekitar Desa Darmasaba.",
+ "kategoriProdukId": "5c06chf7-123f-8jhe-0663-5e9h76e55060"
+ },
+ {
+ "id": "d62660a2-ac6b-428a-acf6-58cc837ef789",
+ "nama": "Ayam & Daging Segar Darmasaba",
+ "imageId": "cmkewf4u90003vnys87en35nj",
+ "harga": 80000,
+ "rating": 4.2,
+ "alamatUsaha": "Br. Baler Pasar, Desa Darmasaba",
+ "kontak": "081998877665",
+ "deskripsi": "Menjual ayam potong dan daging segar untuk kebutuhan rumah tangga dan upacara adat.",
+ "kategoriProdukId": "5c06chf7-123f-9kif-0663-5e9h76e55060"
+ }
+]
diff --git a/prisma/data/ekonomi/program-kemiskinan/program-kemiskinan.json b/prisma/data/ekonomi/program-kemiskinan/program-kemiskinan.json
new file mode 100644
index 00000000..3348f08a
--- /dev/null
+++ b/prisma/data/ekonomi/program-kemiskinan/program-kemiskinan.json
@@ -0,0 +1,23 @@
+[
+ {
+ "id": "dd92a029-cd7d-4b60-8a3b-dd88e61fe715",
+ "nama": "BLT-DD (Bantuan Langsung Tunai Dana Desa)",
+ "icon": "bantuan",
+ "deskripsi": "
Program pemberian Bantuan Langsung Tunai yang dibiayai dari Dana Desa untuk meringankan beban ekonomi keluarga miskin/prasejahtera di Desa Darmasaba.
",
+ "statistikId": "d59481a3-ff7f-4e52-cd5c-89e143eeb869"
+ },
+ {
+ "id": "dd92a029-cd7d-4b60-9b4c-dd88e61fe715",
+ "nama": "Penguatan Ketahanan Pangan",
+ "icon": "air",
+ "deskripsi": "Kegiatan pemberdayaan masyarakat dalam ketahanan pangan untuk mendukung ketersediaan pangan keluarga kurang mampu dan meningkatkan kemampuan produksi pangan lokal.
",
+ "statistikId": "d59481a3-ff7f-4e52-de6d-89e143eeb869"
+ },
+ {
+ "id": "dd92a029-cd7d-4b60-0c5d-dd88e61fe715",
+ "nama": "Peningkatan IKM berbasis E-commerce",
+ "icon": "ekonomi",
+ "deskripsi": "Program peningkatan keterampilan usaha mikro kecil (IKM) termasuk pelatihan branding, pengemasan, dan promosi digital untuk memperkuat ekonomi rumah tangga melalui pemasaran online.
",
+ "statistikId": "d59481a3-ff7f-4e52-df7e-89e143eeb869"
+ }
+]
diff --git a/prisma/data/ekonomi/program-kemiskinan/statistik-kemiskinan.json b/prisma/data/ekonomi/program-kemiskinan/statistik-kemiskinan.json
new file mode 100644
index 00000000..7fec063f
--- /dev/null
+++ b/prisma/data/ekonomi/program-kemiskinan/statistik-kemiskinan.json
@@ -0,0 +1,17 @@
+[
+ {
+ "id": "d59481a3-ff7f-4e52-cd5c-89e143eeb869",
+ "tahun": 2023,
+ "jumlah": 20
+ },
+ {
+ "id": "d59481a3-ff7f-4e52-de6d-89e143eeb869",
+ "tahun": 2024,
+ "jumlah": 30
+ },
+ {
+ "id": "d59481a3-ff7f-4e52-df7e-89e143eeb869",
+ "tahun": 2025,
+ "jumlah": 20
+ }
+]
diff --git a/prisma/data/ekonomi/sektor-unggulan/sektor-unggulan.json b/prisma/data/ekonomi/sektor-unggulan/sektor-unggulan.json
new file mode 100644
index 00000000..fca71eb1
--- /dev/null
+++ b/prisma/data/ekonomi/sektor-unggulan/sektor-unggulan.json
@@ -0,0 +1,44 @@
+[
+ {
+ "id": "053999e8-e5c4-4a50-b587-0e0ce15aba1a",
+ "name": "Pertanian",
+ "description": "Sektor pertanian meliputi kegiatan bercocok tanam padi, palawija, dan tanaman lain di subak yang menjadi basis mata pencaharian warga",
+ "value": 90
+ },
+ {
+ "id": "8e0d2f2d-512d-4c05-8880-b6e7d144a34d",
+ "name": "UMKM Kecil",
+ "description": "Usaha Mikro Kecil Menengah termasuk IKM berbasis pengolahan pangan dan kuliner yang tumbuh di desa sebagai penggerak ekonomi lokal",
+ "value": 45
+ },
+ {
+ "id": "0378b10a-f0e3-421c-9272-225d931179ce",
+ "name": "Peternakan",
+ "description": "Peternakan ikan lele dan mata pencaharian lain yang mendukung ketahanan pangan dan ekonomi masyarakat desa",
+ "value": 30
+ },
+ {
+ "id": "4fa28680-8014-4c46-9dd0-1aa910630fd3",
+ "name": "BUMDes",
+ "description": "BUMDes Pudak Mesari sebagai lembaga usaha desa yang mengembangkan potensi lokal dan layanan ekonomi",
+ "value": 20
+ },
+ {
+ "id": "669464b2-dd7e-44be-b609-97a9b844df8b",
+ "name": "Kawasan Kuliner",
+ "description": "Potensi kawasan kuliner desa yang menjadi daya tarik ekonomi dan pariwisata kecil di daerah Darmasaba",
+ "value": 15
+ },
+ {
+ "id": "ef65e122-84ce-4483-93e9-c1a8bcee9b79",
+ "name": "Pariwisata",
+ "description": "Potensi wisata lokal seperti Jogging Track, taman dan water park yang memberikan nilai tambah ekonomi masyarakat",
+ "value": 35
+ },
+ {
+ "id": "08443c84-8ca9-4690-b900-e5e3e753cc97",
+ "name": "Kerajinan Genteng",
+ "description": "Kerajinan genteng press di Desa Adat Tegal yang merupakan usaha kerajinan lokal dengan kontribusi ekonomi",
+ "value": 25
+ }
+]
diff --git a/prisma/data/file-storage.json b/prisma/data/file-storage.json
index 34fe16f1..b3549ca4 100644
--- a/prisma/data/file-storage.json
+++ b/prisma/data/file-storage.json
@@ -999,7 +999,7 @@
"link": "/api/fileStorage/findUnique/M9QlgVKIEfCdY3g4F_tRZ-desktop.webp",
"category": "image"
},
- {
+ {
"id": "cmkayz2h8001cvn6yrb7uptjs",
"name": "Gi8EX3pBmT719AfzXirDS-desktop.webp",
"realName": "pd1.jpg",
@@ -1115,5 +1115,59 @@
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/DyX82oztXbHfu6HEvbrpt-desktop.webp",
"category": "image"
+ },
+ {
+ "id": "cmkc2tcn30000vnt9esmx8kyb",
+ "name": "K0wY911212dinYA3AFB_f-desktop.webp",
+ "realName": "keamananl-1.jpg",
+ "path": "uploads/images",
+ "mimeType": "image/webp",
+ "link": "/api/fileStorage/findUnique/K0wY911212dinYA3AFB_f-desktop.webp",
+ "category": "image"
+ },
+ {
+ "id": "cmkc2xm1z0003vnt98682dv0a",
+ "name": "x0_-siY2V8IehBzo4_uph-desktop.webp",
+ "realName": "keamananl-2.jpg",
+ "path": "uploads/images",
+ "mimeType": "image/webp",
+ "link": "/api/fileStorage/findUnique/x0_-siY2V8IehBzo4_uph-desktop.webp",
+ "category": "image"
+ },
+ {
+ "id": "cmkc36q2j0006vnt9g87h5it4",
+ "name": "TXknK9CSRSxwvM2hPW6BO-desktop.webp",
+ "realName": "pecalang.jpeg",
+ "path": "uploads/images",
+ "mimeType": "image/webp",
+ "link": "/api/fileStorage/findUnique/TXknK9CSRSxwvM2hPW6BO-desktop.webp",
+ "category": "image"
+ },
+ {
+ "id": "cmkccs50d0000vn2mfuk0d9dw",
+ "name": "U7rePDZq5E59z-Eo9tLBe-desktop.webp",
+ "realName": "tips-keamanan-1.jpg",
+ "path": "uploads/images",
+ "mimeType": "image/webp",
+ "link": "/api/fileStorage/findUnique/U7rePDZq5E59z-Eo9tLBe-desktop.webp",
+ "category": "image"
+ },
+ {
+ "id": "cmkccyh7t0003vn2mjdrqtuu0",
+ "name": "TTur8BttDlAS9UgZVe3M8-desktop.webp",
+ "realName": "tipskaman.jpg",
+ "path": "uploads/images",
+ "mimeType": "image/webp",
+ "link": "/api/fileStorage/findUnique/TTur8BttDlAS9UgZVe3M8-desktop.webp",
+ "category": "image"
+ },
+ {
+ "id": "cmkdu8kb20002vn4lihwo4k86",
+ "name": "6DQbAvn0St-xHdPGW3vpY-desktop.webp",
+ "realName": "Jamban4.jpg",
+ "path": "uploads/images",
+ "mimeType": "image/webp",
+ "link": "/api/fileStorage/findUnique/6DQbAvn0St-xHdPGW3vpY-desktop.webp",
+ "category": "image"
}
]
diff --git a/prisma/data/keamanan/keamanan-lingkungan/keamanan-lingkungan.json b/prisma/data/keamanan/keamanan-lingkungan/keamanan-lingkungan.json
new file mode 100644
index 00000000..788349d9
--- /dev/null
+++ b/prisma/data/keamanan/keamanan-lingkungan/keamanan-lingkungan.json
@@ -0,0 +1,20 @@
+[
+ {
+ "id": "cmkc2tcs00002vnt9c0ssj05n",
+ "name": "Sosialisasi dan Pembinaan Keamanan Lingkungan Desa Darmasaba",
+ "deskripsi": "Pemerintah Desa Darmasaba melaksanakan Sosialisasi dan Pembinaan tentang keamanan dan ketertiban lingkungan kepada warga Perumahan Darmasaba Permai di Wantilan Perum Darmasaba Permai, Desa Darmasaba. Kegiatan ini melibatkan Perbekel Darmasaba, Bhabinkamtibmas, Babinsa, anggota BPD, LPM Desa, KBD dan KBA untuk mengajak warga berperan aktif dalam menjaga keamanan lingkungan, serta mendukung pemasangan lampu penerangan jalan guna mencegah kriminalitas dan kecelakaan di wilayah lingkungan.
",
+ "imageId": "cmkc2tcn30000vnt9esmx8kyb"
+ },
+ {
+ "id": "cmkc2xmdh0005vnt9ri6f4nk8",
+ "name": "Sinergi Aparat dan Masyarakat untuk Keamanan Lingkungan",
+ "deskripsi": "Desa Darmasaba bersama aparat seperti Polres Badung dan elemen masyarakat berkomitmen menjalin sinergi untuk menciptakan keamanan dan ketertiban lingkungan yang kondusif, memperkuat kepedulian serta tindakan nyata dalam menjaga situasi kamtibmas desa.
",
+ "imageId": "cmkc2xm1z0003vnt98682dv0a"
+ },
+ {
+ "id": "cmkc36qbl0008vnt9odvekex6",
+ "name": "Peran Sistem Keamanan Lingkungan (Siskamling) dan Pecalang di Bali",
+ "deskripsi": "Sistem keamanan lingkungan (Siskamling) di Bali termasuk di Desa Darmasaba melibatkan kolaborasi antara pemerintah desa, satlinmas, dan pecalang sebagai pranata adat Bali. Sinergi ini penting untuk menjaga ketertiban masyarakat serta harmoni sosial berdasarkan kearifan lokal seperti Tri Hita Karana, meskipun perlu pembinaan dan koordinasi terus menerus dari desa dan aparat terkait.
",
+ "imageId": "cmkc36q2j0006vnt9g87h5it4"
+ }
+]
diff --git a/prisma/data/keamanan/kontak-darurat-keamanan/kontak-darurat-keamanan.json b/prisma/data/keamanan/kontak-darurat-keamanan/kontak-darurat-keamanan.json
new file mode 100644
index 00000000..989d7ab9
--- /dev/null
+++ b/prisma/data/keamanan/kontak-darurat-keamanan/kontak-darurat-keamanan.json
@@ -0,0 +1,20 @@
+[
+ {
+ "id": "keamanan-polisi",
+ "nama": "Kepolisian",
+ "icon": "keamanan",
+ "kategoriId": "item-polisi"
+ },
+ {
+ "id": "keamanan-damkar",
+ "nama": "Pemadam Kebakaran",
+ "icon": "pemadam",
+ "kategoriId": "item-damkar"
+ },
+ {
+ "id": "keamanan-sar",
+ "nama": "SAR & Evakuasi",
+ "icon": "sar",
+ "kategoriId": "item-sar"
+ }
+]
diff --git a/prisma/data/keamanan/kontak-darurat-keamanan/kontakDaruratToItem.json b/prisma/data/keamanan/kontak-darurat-keamanan/kontakDaruratToItem.json
new file mode 100644
index 00000000..b3e18d3b
--- /dev/null
+++ b/prisma/data/keamanan/kontak-darurat-keamanan/kontakDaruratToItem.json
@@ -0,0 +1,17 @@
+[
+ {
+ "id": "map-polisi-1",
+ "kontakDaruratId": "keamanan-polisi",
+ "kontakItemId": "item-polsek-darmasaba"
+ },
+ {
+ "id": "map-polisi-2",
+ "kontakDaruratId": "keamanan-polisi",
+ "kontakItemId": "item-polres-badung"
+ },
+ {
+ "id": "map-damkar-1",
+ "kontakDaruratId": "keamanan-damkar",
+ "kontakItemId": "item-damkar-badung"
+ }
+]
diff --git a/prisma/data/keamanan/kontak-darurat-keamanan/kontakItem.json b/prisma/data/keamanan/kontak-darurat-keamanan/kontakItem.json
new file mode 100644
index 00000000..1c00152d
--- /dev/null
+++ b/prisma/data/keamanan/kontak-darurat-keamanan/kontakItem.json
@@ -0,0 +1,45 @@
+[
+ {
+ "id": "item-polisi",
+ "nama": "Polisi",
+ "nomorTelepon": "110",
+ "icon": "keamanan"
+ },
+ {
+ "id": "item-damkar",
+ "nama": "Pemadam Kebakaran",
+ "nomorTelepon": "113",
+ "icon": "pemadam"
+ },
+ {
+ "id": "item-sar",
+ "nama": "BASARNAS",
+ "nomorTelepon": "115",
+ "icon": "sar"
+ },
+
+ {
+ "id": "item-polsek-darmasaba",
+ "nama": "Polsek Darmasaba",
+ "nomorTelepon": "0361123456",
+ "icon": "bangunan"
+ },
+ {
+ "id": "item-polres-badung",
+ "nama": "Polres Badung",
+ "nomorTelepon": "0361123999",
+ "icon": "bangunan"
+ },
+ {
+ "id": "item-damkar-badung",
+ "nama": "Damkar Kabupaten Badung",
+ "nomorTelepon": "0361900113",
+ "icon": "pemadam"
+ },
+ {
+ "id": "item-bpbd-badung",
+ "nama": "BPBD Badung",
+ "nomorTelepon": "0361900113",
+ "icon": "sar"
+ }
+]
diff --git a/prisma/data/keamanan/laporan-publik/laporan-publik.json b/prisma/data/keamanan/laporan-publik/laporan-publik.json
new file mode 100644
index 00000000..2cb9f817
--- /dev/null
+++ b/prisma/data/keamanan/laporan-publik/laporan-publik.json
@@ -0,0 +1,16 @@
+[
+ {
+ "id": "cmkdt14my0000vn4lrvfv6jxr",
+ "judul": "LAPORAN REALISASI APBDES SEMESTER I TAHUN ANGGARAN 2025",
+ "lokasi": "Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung",
+ "tanggalWaktu": "2025-08-04T08:58:55.080Z",
+ "kronologi": "Pemerintah Desa Darmasaba menyampaikan realisasi pendapatan dan belanja desa hingga semester I tahun 2025 yang mencakup berbagai sumber pendapatan dan rincian belanja desa.
"
+ },
+ {
+ "id": "cmkdt14my0000vn4lrvfv7kys",
+ "judul": "Aksi Penanganan Maraknya Pembuangan Sampah Liar di Wilayah Desa Darmasaba",
+ "lokasi": "Desa Darmasaba, Kabupaten Badung",
+ "tanggalWaktu": "2025-11-24T08:58:55.080Z",
+ "kronologi": "Tim mendatangi rumah pihak yang diduga melakukan pembuangan sampah liar, melakukan pendataan dan penelusuran, serta koordinasi lintas wilayah untuk memastikan penanganan yang tepat.
"
+ }
+]
diff --git a/prisma/data/keamanan/laporan-publik/penanganan-laporan.json b/prisma/data/keamanan/laporan-publik/penanganan-laporan.json
new file mode 100644
index 00000000..170c9527
--- /dev/null
+++ b/prisma/data/keamanan/laporan-publik/penanganan-laporan.json
@@ -0,0 +1,12 @@
+[
+ {
+ "id": "cmkdt41lx0001vn4lrlcqz735",
+ "laporanId": "cmkdt14my0000vn4lrvfv6jxr",
+ "deskripsi": "Laporan ini disampaikan sebagai bentuk komitmen transparansi pengelolaan keuangan desa dan dapat dimonitor oleh masyarakat.
"
+ },
+ {
+ "id": "cmkdt41lx0002vn4lrlcqz846",
+ "laporanId": "cmkdt14my0000vn4lrvfv7kys",
+ "deskripsi": "Pemerintah Desa bersama Penyidik Lingkungan Hidup melakukan investigasi lapangan terhadap laporan masyarakat mengenai aksi pembuangan sampah liar, serta melakukan koordinasi untuk penindakan sesuai ketentuan.
"
+ }
+]
\ No newline at end of file
diff --git a/prisma/data/keamanan/pencegahan-kriminalitas/pencegahan-kriminalitas.json b/prisma/data/keamanan/pencegahan-kriminalitas/pencegahan-kriminalitas.json
new file mode 100644
index 00000000..8854bbd1
--- /dev/null
+++ b/prisma/data/keamanan/pencegahan-kriminalitas/pencegahan-kriminalitas.json
@@ -0,0 +1,23 @@
+[
+ {
+ "id": "cmh48mn850003qq091pvs7rf1",
+ "judul": "Maling Motor di Darmasaba, Residivis Begal Didor",
+ "deskripsi": "Maling Motor di Darmasaba, Residivis Begal Didor David Andiansyah kembali terancam dibui lantaran melakukan aksi pencurian.
",
+ "deskripsiSingkat": "Maling Motor di Darmasaba, Residivis Begal Didor David Andiansyah kembali terancam dibui lantaran melakukan aksi pencurian. Mantan narapidana kasus begal ini diciduk polisi usai mencuri sepeda motor di garase rumah, kawasan Banjar Cabe, Desa Darmasaba, Abiansemal, Badung, Kamis (24/6). Kasi Humas Polres Badung Iptu Ketut Sudana menerangkan, peristiwa tersebut bermula ketika korban Ni Made Desniari berkunjung ke rumah tetangganya Putu Juliati. Saat itu korban memarkirkan sepeda motor Honda Beat miliknya di garase (TKP), tetapi kunci motornya masih nyantol di motor. Ketika korban kembali sekitar pukul 13.00, didapati motornya telah raib. Atas kejadian ini wanita asli Banjar Cabe melapor ke polisi.
",
+ "linkVideo": "https://www.youtube.com/embed/2rxK5A-KoeY"
+ },
+ {
+ "id": "cmh59no850003qq091pvs7rf1",
+ "judul": "Integrasi Digital & Akuntabilitas Desa (Pencegahan Penyalahgunaan Wewenang)",
+ "deskripsi": "Video ini membahas bagaimana integrasi digital seperti Simpades, Siskeudes, dan program Jaga Desa memperkuat akuntabilitas pemerintahan desa Darmasaba. Program tersebut mendukung transparansi dan pencegahan tindak pidana administrasi maupun penyalahgunaan wewenang karena tata kelola data desa yang baik membantu mencegah korupsi atau kecurangan dalam pengelolaan dana dan layanan desa.
",
+ "deskripsiSingkat": "Integrasi digital meningkatkan transparansi dan mencegah penyalahgunaan wewenang di pemerintahan desa.
",
+ "linkVideo": "https://www.youtube.com/embed/l7NIqjA2b_k"
+ },
+ {
+ "id": "cmh60op850003qq091pvs7rf1",
+ "judul": "PROFIL DESA DARMASABA",
+ "deskripsiSingkat": "PROFIL DESA DARMASABA Desa Darmasaba adalah permata di ujung selatan Kecamatan Abiansemal, Kabupaten Badung",
+ "deskripsi": "PROFIL DESA DARMASABA Desa Darmasaba adalah permata di ujung selatan Kecamatan Abiansemal, Kabupaten Badung, yang dikenal dengan kekayaan budaya, inovasi desa, dan potensi ekonomi yang terus berkembang. Berbatasan langsung dengan Kota Denpasar, Desa Darmasaba memiliki luas wilayah 567 hektar dengan jumlah penduduk 10.141 jiwa (per akhir Desember 2024). Video ini menampilkan potensi dan keunggulan Desa Darmasaba: 🏞️ Keindahan alam dan kawasan persawahan yang dikelola empat subak aktif. 🍽️ Keberhasilan UMKM dan kuliner lokal, termasuk produk unggulan ACK yang merambah pasar nasional. 🌱 Inovasi ketahanan pangan, pengolahan sampah ramah lingkungan (TPS3R Pudak Mesari, CINTA Darmasaba), hingga Graha Sari Boga dengan program makan bergizi gratis. 🎭 Pelestarian seni, budaya, dan tradisi, termasuk maskot Sekar Pudak dan tradisi Ngerebeg. 🏆 Prestasi desa tingkat nasional dan internasional. 📱 Transformasi digital dengan aplikasi Darmasaba Digital App, talkshow Bicara Darmasaba, hingga perpustakaan digital Pustaka Ananta Loka. Dengan motto \\\"Menggali Warisan, Merangkai Inovasi\\\", Desa Darmasaba menghadirkan 13 inovasi unggulan di bidang pemerintahan, kewilayahan, UMKM, dan kemasyarakatan. Desa ini membuktikan bahwa kolaborasi masyarakat dan pemerintah mampu mewujudkan desa yang mandiri, berdaya saing, dan berkelanjutan. 🎥 Produksi: Tim Media Kreatif Desa Darmasaba 🤝 Dukungan: Seluruh Lembaga & Elemen Masyarakat Desa Darmasaba
",
+ "linkVideo": "https://www.youtube.com/watch?v=9eCnhJvdv6A"
+ }
+]
diff --git a/prisma/data/keamanan/polsek-terdekat/layanan-polsek.json b/prisma/data/keamanan/polsek-terdekat/layanan-polsek.json
new file mode 100644
index 00000000..0d64f8c7
--- /dev/null
+++ b/prisma/data/keamanan/polsek-terdekat/layanan-polsek.json
@@ -0,0 +1,14 @@
+[
+ {
+ "id": "7ded635b-bf4a-4c1b-b6fa-2f13f4dfc478",
+ "nama": "Layanan Administrasi & Surat-Menyurat"
+ },
+ {
+ "id": "b5af284c-6aa1-4442-935f-869d78eb7ecf",
+ "nama": "Penanganan Laporan & Pengaduan"
+ },
+ {
+ "id": "56b37d7f-d717-4e33-b05d-ea22b5f7af91",
+ "nama": "Perlindungan & Pengayoman Masyarakat"
+ }
+]
diff --git a/prisma/data/keamanan/polsek-terdekat/layanan-to-polsek.json b/prisma/data/keamanan/polsek-terdekat/layanan-to-polsek.json
new file mode 100644
index 00000000..299631cf
--- /dev/null
+++ b/prisma/data/keamanan/polsek-terdekat/layanan-to-polsek.json
@@ -0,0 +1,62 @@
+[
+ {
+ "id": "a50d52f2-e70f-4f29-9133-e294c40d14d3",
+ "layananId": "7ded635b-bf4a-4c1b-b6fa-2f13f4dfc478",
+ "polsekTerdekatId": "b92ee048-fb7f-44e9-aa5b-822e6cd0085d"
+ },
+ {
+ "id": "012454f8-f5d7-41c0-9dce-2754c0356523",
+ "layananId": "b5af284c-6aa1-4442-935f-869d78eb7ecf",
+ "polsekTerdekatId": "b92ee048-fb7f-44e9-aa5b-822e6cd0085d"
+ },
+ {
+ "id": "50736038-4ba6-47f8-8399-35b73b389f12",
+ "layananId": "56b37d7f-d717-4e33-b05d-ea22b5f7af91",
+ "polsekTerdekatId": "b92ee048-fb7f-44e9-aa5b-822e6cd0085d"
+ },
+ {
+ "id": "e2dc3487-1f62-4f63-9a12-49ac30da3372",
+ "layananId": "7ded635b-bf4a-4c1b-b6fa-2f13f4dfc478",
+ "polsekTerdekatId": "de769c40-10d4-4fbc-a5ef-12f2ce53a0a2"
+ },
+ {
+ "id": "47fe1f9c-4072-4203-90f9-3294d1369ac5",
+ "layananId": "b5af284c-6aa1-4442-935f-869d78eb7ecf",
+ "polsekTerdekatId": "de769c40-10d4-4fbc-a5ef-12f2ce53a0a2"
+ },
+ {
+ "id": "2cc1ba81-6b62-42ff-af21-09f8165a2dd0",
+ "layananId": "56b37d7f-d717-4e33-b05d-ea22b5f7af91",
+ "polsekTerdekatId": "de769c40-10d4-4fbc-a5ef-12f2ce53a0a2"
+ },
+ {
+ "id": "3ca2ce42-2e7d-4750-87b7-f1f52ed141de",
+ "layananId": "7ded635b-bf4a-4c1b-b6fa-2f13f4dfc478",
+ "polsekTerdekatId": "9a3fff54-8854-4929-b9b5-b5b2751011ea"
+ },
+ {
+ "id": "90472bca-cf3d-47ca-92e5-db43c4c7e579",
+ "layananId": "b5af284c-6aa1-4442-935f-869d78eb7ecf",
+ "polsekTerdekatId": "9a3fff54-8854-4929-b9b5-b5b2751011ea"
+ },
+ {
+ "id": "41cfc1fe-a193-446d-b574-64b1124c6f55",
+ "layananId": "56b37d7f-d717-4e33-b05d-ea22b5f7af91",
+ "polsekTerdekatId": "9a3fff54-8854-4929-b9b5-b5b2751011ea"
+ },
+ {
+ "id": "f35443e1-6aca-416d-8c55-00e3f4a0f5f9",
+ "layananId": "7ded635b-bf4a-4c1b-b6fa-2f13f4dfc478",
+ "polsekTerdekatId": "c2d272e1-737d-44f5-bd85-ae268cb06cbf"
+ },
+ {
+ "id": "e09797f6-82e5-4b77-946e-319eee431c8f",
+ "layananId": "b5af284c-6aa1-4442-935f-869d78eb7ecf",
+ "polsekTerdekatId": "c2d272e1-737d-44f5-bd85-ae268cb06cbf"
+ },
+ {
+ "id": "bd9b1f27-cd1b-4e23-b162-3a757745f78a",
+ "layananId": "56b37d7f-d717-4e33-b05d-ea22b5f7af91",
+ "polsekTerdekatId": "c2d272e1-737d-44f5-bd85-ae268cb06cbf"
+ }
+]
diff --git a/prisma/data/keamanan/polsek-terdekat/polsek-terdekat.json b/prisma/data/keamanan/polsek-terdekat/polsek-terdekat.json
new file mode 100644
index 00000000..97a41792
--- /dev/null
+++ b/prisma/data/keamanan/polsek-terdekat/polsek-terdekat.json
@@ -0,0 +1,54 @@
+[
+ {
+ "id": "b92ee048-fb7f-44e9-aa5b-822e6cd0085d",
+ "nama": "Kantor Polisi Abian Semal",
+ "jarakKeDesa": "9,6 Km",
+ "alamat": "ABIAN SEMAL POLICE STAT, JL. Pasar, Blahkiuh, Kec. Abiansemal, Kabupaten Badung, Bali 80352",
+ "nomorTelepon": "0361813972",
+ "jamOperasional": "Buka 24 Jam",
+ "embedMapUrl": "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d63127.118683990586!2d115.16592643905703!3d-8.553143643198624!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2dd23ceb4f6e5363%3A0xa353662f070f47d8!2sAbian%20Semal%20Police%20Station!5e0!3m2!1sid!2sid!4v1768376981008!5m2!1sid!2sid",
+ "namaTempatMaps": "Abian Semal Police Station",
+ "alamatMaps": "ABIAN SEMAL POLICE STAT, JL. Pasar, Blahkiuh, Kec. Abiansemal, Kabupaten Badung, Bali 80352",
+ "linkPetunjukArah": "https://maps.app.goo.gl/GhHVNQqffNrXSMFX7",
+ "layananPolsekId": "7ded635b-bf4a-4c1b-b6fa-2f13f4dfc478"
+ },
+ {
+ "id": "de769c40-10d4-4fbc-a5ef-12f2ce53a0a2",
+ "nama": "Polres Badung",
+ "jarakKeDesa": "5,8 Km",
+ "alamat": "Jl. Kebo Iwa No.1, Mengwitani, Kec. Mengwi, Kabupaten Badung, Bali 80351",
+ "nomorTelepon": "0361829949",
+ "jamOperasional": "Buka 24 Jam",
+ "embedMapUrl": "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d15780.907469872707!2d115.17829950197888!3d-8.574172113520685!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2dd238cade1488c3%3A0x918ba5cac3ef00b7!2sPolres%20Badung!5e0!3m2!1sid!2sid!4v1768377100998!5m2!1sid!2sid",
+ "namaTempatMaps": "Polres Badung",
+ "alamatMaps": "Jl. Kebo Iwa No.1, Mengwitani, Kec. Mengwi, Kabupaten Badung, Bali 80351",
+ "linkPetunjukArah": "https://maps.app.goo.gl/7yQQod4PFhpqqe7Z8",
+ "layananPolsekId": "7ded635b-bf4a-4c1b-b6fa-2f13f4dfc478"
+ },
+ {
+ "id": "9a3fff54-8854-4929-b9b5-b5b2751011ea",
+ "nama": "Polsek Mengwi",
+ "jarakKeDesa": "9,7 Km",
+ "alamat": "Jl. I Gusti Ngurah Rai No.110, Werdi Bhuwana, Kec. Mengwi, Kabupaten Badung, Bali 80351",
+ "nomorTelepon": "0361411270",
+ "jamOperasional": "Buka 24 Jam",
+ "embedMapUrl": "https://www.google.com/maps/embed?pb=!1m14!1m8!1m3!1d63126.100310757916!2d115.1716545!3d-8.5592871!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2dd23b9ddc3980af%3A0xa1f4f195483537b!2sPolsek%20Mengwi!5e0!3m2!1sid!2sid!4v1768377317955!5m2!1sid!2sid",
+ "namaTempatMaps": "Polsek Mengwi",
+ "alamatMaps": "Jl. I Gusti Ngurah Rai No.110, Werdi Bhuwana, Kec. Mengwi, Kabupaten Badung, Bali 80351",
+ "linkPetunjukArah": "https://maps.app.goo.gl/cJD44NSUdpA7Ly2m6",
+ "layananPolsekId": "7ded635b-bf4a-4c1b-b6fa-2f13f4dfc478"
+ },
+ {
+ "id": "c2d272e1-737d-44f5-bd85-ae268cb06cbf",
+ "nama": "Pos Polisi Ahmad Yani",
+ "jarakKeDesa": "7 Km",
+ "alamat": "Jl. Ahmad Yani Utara No.5, Peguyangan, Kec. Denpasar Utara, Kota Denpasar, Bali 80239",
+ "nomorTelepon": "-",
+ "jamOperasional": "-",
+ "embedMapUrl": "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d31558.650325984465!2d115.18791122296605!3d-8.612190901728288!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2dd23f635f1186b1%3A0xcfdde508d897fb04!2sPos%20Polisi%20Simpang%20Ahmad%20Yani!5e0!3m2!1sid!2sid!4v1768377470154!5m2!1sid!2sid",
+ "namaTempatMaps": "Pos Polisi Simpang Ahmad Yani",
+ "alamatMaps": "Jl. Ahmad Yani Utara No.5, Peguyangan, Kec. Denpasar Utara, Kota Denpasar, Bali 80239",
+ "linkPetunjukArah": "https://maps.app.goo.gl/D8HGs4mSAQqJm9KRA",
+ "layananPolsekId": "7ded635b-bf4a-4c1b-b6fa-2f13f4dfc478"
+ }
+]
diff --git a/prisma/data/keamanan/tips-keamanan/tips-keamanan.json b/prisma/data/keamanan/tips-keamanan/tips-keamanan.json
new file mode 100644
index 00000000..2eff01d6
--- /dev/null
+++ b/prisma/data/keamanan/tips-keamanan/tips-keamanan.json
@@ -0,0 +1,14 @@
+[
+ {
+ "id": "cmh48wo9c0006qq09txnxusql",
+ "judul": "Keamanan Rumah",
+ "deskripsi": "
Pastikan pintu dan jendela selalu terkunci saat meninggalkan rumah
Pasang lampu penerangan di halaman dan area sekitar rumah untuk mencegah tindak kejahatan.
Jangan mudah memberikan akses masuk ke orang yang tidak dikenal.
",
+ "imageId": "cmkccs50d0000vn2mfuk0d9dw"
+ },
+ {
+ "id": "cmh48wo9c1117rr10txnxusql",
+ "judul": "Keamanan Lingkungan Tanggungjawab Bersama",
+ "deskripsi": "Pemerintah Desa Darmasaba melaksanakan sosialisasi dan pembinaan tentang keamanan dan ketertiban lingkungan kepada warga Perumahan Darmasaba Permai. Warga diajak berperan aktif dalam menjaga keamanan lingkungan serta mendukung penyediaan lampu penerangan jalan untuk mencegah tindak kriminal dan kecelakaan. Bhabinkamtibmas dan Babinsa turut memberikan materi keamanan dan ketertiban kepada warga, menekankan pentingnya partisipasi masyarakat dalam menjaga keamanan desa.
",
+ "imageId": "cmkccyh7t0003vn2mjdrqtuu0"
+ }
+]
\ No newline at end of file
diff --git a/prisma/data/kesehatan/program-kesehatan/program-kesehatan.json b/prisma/data/kesehatan/program-kesehatan/program-kesehatan.json
index 8234f506..82fe65b5 100644
--- a/prisma/data/kesehatan/program-kesehatan/program-kesehatan.json
+++ b/prisma/data/kesehatan/program-kesehatan/program-kesehatan.json
@@ -40,5 +40,12 @@
"deskripsiSingkat": "Program pembinaan dan pengembangan kapasitas kader Posyandu.
",
"deskripsi": "Pemdes Darmasaba melaksanakan kegiatan Outbound Posyandu untuk meningkatkan kapasitas dan wawasan Kader Posyandu se-Desa Darmasaba sebagai bagian dari upaya peningkatan kualitas pelayanan kesehatan dasar di masyarakat.
",
"imageId": "cmkaykipf0019vn6yknjno3k1"
+ },
+ {
+ "id": "cmkdu8ki10004vn4lpbxm2zqo",
+ "name": "PEMBANGUNAN JAMBAN BAGI MASYARAKAT",
+ "deskripsiSingkat": "Program pengadaan jamban bagi Masyarakat ini diharapkan menjadi stimulus agar masyarakat peduli terhadap lingkungan sehat sehingga Badung Open Defection Free atau terbebas dari buang air besar di tempat terbuka dapat terwujud.
",
+ "deskripsi": "Desa Darmasaba sebagai desa yang berkomitmen selalu selaras dengan pembangunan Pemerintah Kabupaten Badung pada tahun anggaran 2023 ini turut ambil bagian dalam menyukseskan program Bupati Badung I Nyoman Giri Prasta, S.Sos dalam bidang kesehatan sanitasi masyarakat. Program pengadaan jamban bagi Masyarakat ini diharapkan menjadi stimulus agar masyarakat peduli terhadap lingkungan sehat sehingga Badung Open Defection Free atau terbebas dari buang air besar di tempat terbuka dapat terwujud.
Pemberian bantuan jamban ini dilaksanakan di 11 banjar dengan menyasar 22 keluarga yang memang belum memiliki jamban yang sumber dananya sepenuhnya dari APBDes Darmasaba T. A. 2023. Pembangunan Jamban bagi Masyarakat ini juga menjadi bukti komitmen Pemerintah Desa Darmasaba dalam melaksanakan salah satu visi mewujudkan masyarakat yang sejahtera dan berbudaya untuk menjaga lingkungan yang bersih dan sehat.
",
+ "imageId": "cmkdu8kb20002vn4lihwo4k86"
}
]
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index dcdcf5e1..c04e56e5 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -607,7 +607,7 @@ model Berita {
id String @id @default(cuid())
judul String
deskripsi String
- image FileStorage? @relation(fields: [imageId], references: [id])
+ image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
content String @db.Text
createdAt DateTime @default(now())
@@ -1113,17 +1113,17 @@ model DoctorSign {
// ========================================= POSYANDU ========================================= //
model Posyandu {
- id String @id @default(cuid())
+ id String @id @default(cuid())
name String
nomor String
deskripsi String
jadwalPelayanan String
image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- deletedAt DateTime @default(now())
- isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime @default(now())
+ isActive Boolean @default(true)
}
// ========================================= PUSKESMAS ========================================= //
@@ -1133,12 +1133,12 @@ model Puskesmas {
alamat String
jam JamOperasional @relation(fields: [jamId], references: [id])
jamId String
- image FileStorage? @relation(fields: [imageId], references: [id])
+ image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
kontak KontakPuskesmas @relation(fields: [kontakId], references: [id])
kontakId String
createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
+ updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
@@ -1170,57 +1170,57 @@ model KontakPuskesmas {
// ========================================= PROGRAM KESSEHATAN ========================================= //
model ProgramKesehatan {
- id String @id @default(cuid())
+ id String @id @default(cuid())
name String
deskripsiSingkat String
deskripsi String
image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- deletedAt DateTime @default(now())
- isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime @default(now())
+ isActive Boolean @default(true)
}
// ========================================= PENANGANAN DARURAT ========================================= //
model PenangananDarurat {
- id String @id @default(cuid())
+ id String @id @default(cuid())
name String
deskripsi String
image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- deletedAt DateTime @default(now())
- isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime @default(now())
+ isActive Boolean @default(true)
}
// ========================================= KONTAK DARURAT ========================================= //
model KontakDarurat {
- id String @id @default(cuid())
+ id String @id @default(cuid())
name String
deskripsi String
image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
whatsapp String
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- deletedAt DateTime @default(now())
- isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime @default(now())
+ isActive Boolean @default(true)
}
// ========================================= INFO WABAH PENYAKIT ========================================= //
model InfoWabahPenyakit {
- id String @id @default(cuid())
+ id String @id @default(cuid())
name String
deskripsiSingkat String
deskripsiLengkap String
image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- deletedAt DateTime @default(now())
- isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime @default(now())
+ isActive Boolean @default(true)
}
// ========================================= MENU KEAMANAN ========================================= //
@@ -1239,7 +1239,7 @@ model KeamananLingkungan {
// ========================================= POLSEK TERDEKAT ========================================= //
model PolsekTerdekat {
- id String @id @default(uuid())
+ id String @id @default(uuid())
nama String
jarakKeDesa String
alamat String
@@ -1249,22 +1249,36 @@ model PolsekTerdekat {
namaTempatMaps String
alamatMaps String
linkPetunjukArah String
- layananPolsek LayananPolsek @relation(fields: [layananPolsekId], references: [id])
+ layananPolsek LayananPolsek @relation(fields: [layananPolsekId], references: [id])
layananPolsekId String
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- deletedAt DateTime @default(now())
- isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime @default(now())
+ isActive Boolean @default(true)
+ LayananToPolsek LayananToPolsek[]
}
model LayananPolsek {
- id String @id @default(uuid())
- nama String // contoh: "Pelayanan SKCK", "Laporan Kriminal"
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- deletedAt DateTime?
- isActive Boolean @default(true)
- PolsekTerdekat PolsekTerdekat[]
+ id String @id @default(uuid())
+ nama String // contoh: "Pelayanan SKCK", "Laporan Kriminal"
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime?
+ isActive Boolean @default(true)
+ PolsekTerdekat PolsekTerdekat[]
+ LayananToPolsek LayananToPolsek[]
+}
+
+model LayananToPolsek {
+ id String @id @default(uuid())
+ layanan LayananPolsek @relation(fields: [layananId], references: [id])
+ layananId String
+ polsekTerdekat PolsekTerdekat @relation(fields: [polsekTerdekatId], references: [id])
+ polsekTerdekatId String
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime @default(now())
+ isActive Boolean @default(true)
}
// ========================================= KONTAK DARURAT ========================================= //
@@ -1377,6 +1391,7 @@ model PasarDesa {
rating Float
alamatUsaha String
kontak String
+ deskripsi String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
diff --git a/prisma/seed.ts b/prisma/seed.ts
index e9026cd7..a0f55415 100644
--- a/prisma/seed.ts
+++ b/prisma/seed.ts
@@ -34,17 +34,35 @@ import perbekelDariMasaKeMasa from "./data/desa/profile/profile-perbekel-lalu.js
import sejarahDesa from "./data/desa/profile/sejarah_desa.json";
import visiMisiDesa from "./data/desa/profile/visi_misi_desa.json";
import detailDataPengangguran from "./data/ekonomi/jumlah-pengangguran/detail-data-pengangguran.json";
-import kategoriProduk from "./data/ekonomi/pasar-desa/kategori-produk.json";
import pegawai from "./data/ekonomi/struktur-organisasi/pegawai-bumdes.json";
import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json";
import posyandu from "./data/kesehatan/posyandu/posyandu.json";
-import kontakPuskesmas from "./data/kesehatan/puskesmas/kontak-puskesmas/kontak.json"
-import jamPuskesmas from "./data/kesehatan/puskesmas/jam-puskesmas/jam.json"
+import kontakPuskesmas from "./data/kesehatan/puskesmas/kontak-puskesmas/kontak.json";
+import jamPuskesmas from "./data/kesehatan/puskesmas/jam-puskesmas/jam.json";
import puskesmas from "./data/kesehatan/puskesmas/puskesmas.json";
-import programKesehatan from "./data/kesehatan/program-kesehatan/program-kesehatan.json"
-import penangananDarurat from "./data/kesehatan/penanganan-darurat/penganan-darurat.json"
-import kontakDarurat from "./data/kesehatan/kontak-darurat/kontak-darurat.json"
-import infoWabahPenyakit from "./data/kesehatan/infowabahpenyakit/infowabahpenyakit.json"
+import programKesehatan from "./data/kesehatan/program-kesehatan/program-kesehatan.json";
+import penangananDarurat from "./data/kesehatan/penanganan-darurat/penganan-darurat.json";
+import kontakDarurat from "./data/kesehatan/kontak-darurat/kontak-darurat.json";
+import infoWabahPenyakit from "./data/kesehatan/infowabahpenyakit/infowabahpenyakit.json";
+import keamananLingkungan from "./data/keamanan/keamanan-lingkungan/keamanan-lingkungan.json";
+import kontakDaruratKeamanan from "./data/keamanan/kontak-darurat-keamanan/kontak-darurat-keamanan.json";
+import kontakItem from "./data/keamanan/kontak-darurat-keamanan/kontakItem.json";
+import kontakDaruratToItem from "./data/keamanan/kontak-darurat-keamanan/kontakDaruratToItem.json";
+import pencegahanKriminalitas from "./data/keamanan/pencegahan-kriminalitas/pencegahan-kriminalitas.json";
+import tipsKeamanan from "./data/keamanan/tips-keamanan/tips-keamanan.json";
+import polsekTerdekat from "./data/keamanan/polsek-terdekat/polsek-terdekat.json";
+import layananPolsek from "./data/keamanan/polsek-terdekat/layanan-polsek.json";
+import layananToPolsek from "./data/keamanan/polsek-terdekat/layanan-to-polsek.json";
+import penangananLaporan from "./data/keamanan/laporan-publik/penanganan-laporan.json";
+import laporanPublik from "./data/keamanan/laporan-publik/laporan-publik.json";
+import pasarDesa from "./data/ekonomi/pasar-desa/pasar-desa.json";
+import kategoriProduk from "./data/ekonomi/pasar-desa/kategori-produk.json";
+import kategoriToPasar from "./data/ekonomi/pasar-desa/kategori-to-pasar.json";
+import lowonganKerjaLokal from "./data/ekonomi/lowongan-kerja-lokal/lowongan-kerja-lokal.json";
+import demografiPekerjaan from "./data/ekonomi/demografi-pekerjaan/demografi-pekerjaan.json";
+import sektorUnggulanDesa from "./data/ekonomi/sektor-unggulan/sektor-unggulan.json";
+import programKemiskinan from "./data/ekonomi/program-kemiskinan/program-kemiskinan.json";
+import statistikKemiskinan from "./data/ekonomi/program-kemiskinan/statistik-kemiskinan.json";
import berita from "./data/desa/berita/berita.json";
import kategoriBerita from "./data/desa/berita/kategori-berita.json";
import contohEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/contoh-kegiatan-di-desa-darmasaba.json";
@@ -1239,22 +1257,6 @@ import resolveImageById from "./resolveImageByName";
}
console.log("penghargaan success ...");
- for (const k of kategoriProduk) {
- await prisma.kategoriProduk.upsert({
- where: {
- id: k.id,
- },
- update: {
- nama: k.nama,
- },
- create: {
- id: k.id,
- nama: k.nama,
- },
- });
- }
- console.log("kategori produk success ...");
-
const flattenedPosisiBumdes = posisiOrganisasi.flat();
// ====================== MENU KESEHATAN ========================
@@ -1496,7 +1498,7 @@ import resolveImageById from "./resolveImageByName";
}
console.log("penanganan darurat success ...");
- // ==================== SUBMENU KONTAK DARURAT =========================
+ // ==================== SUBMENU KONTAK DARURAT =========================
console.log("🔄 Seeding Kontak Darurat...");
for (const p of kontakDarurat) {
const existing = await prisma.kontakDarurat.findUnique({
@@ -1544,7 +1546,7 @@ import resolveImageById from "./resolveImageByName";
}
console.log("kontak darurat success ...");
- // ==================== SUBMENU INFO WABAH PENYAKIT =========================
+ // ==================== SUBMENU INFO WABAH PENYAKIT =========================
console.log("🔄 Seeding Info Wabah Penyakit...");
for (const p of infoWabahPenyakit) {
const existing = await prisma.infoWabahPenyakit.findUnique({
@@ -1590,9 +1592,406 @@ import resolveImageById from "./resolveImageByName";
},
});
}
- console.log("kontak darurat success ...");
+ console.log("info wabah penyakit success ...");
- // ✅ Urutkan berdasarkan hierarki
+ // ====================== MENU KEAMANAN ========================
+ // ==================== SUBMENU KEAMANAN LINGKUNGAN ============
+ console.log("🔄 Seeding Keamanan Lingkungan...");
+ for (const k of keamananLingkungan) {
+ const existing = await prisma.keamananLingkungan.findUnique({
+ where: { id: k.id },
+ select: { imageId: true },
+ });
+
+ let imageId = existing?.imageId; // Pertahankan existing
+
+ // Kalau belum ada imageId, cari berdasarkan name/realName
+ if (!imageId && k.imageId) {
+ // ✅ Cari langsung berdasarkan ID yang ada di p.imageId
+ const fileRecord = await prisma.fileStorage.findUnique({
+ where: { id: k.imageId },
+ select: { id: true, name: true },
+ });
+
+ if (fileRecord) {
+ imageId = fileRecord.id;
+ console.log(
+ `✅ Found file by ID: ${fileRecord.name} (${fileRecord.id})`
+ );
+ } else {
+ console.warn(`⚠️ File with ID ${k.imageId} not found for ${k.name}`);
+ imageId = null;
+ }
+ }
+
+ await prisma.keamananLingkungan.upsert({
+ where: { id: k.id },
+ update: {
+ name: k.name,
+ deskripsi: k.deskripsi,
+ imageId,
+ },
+ create: {
+ id: k.id,
+ name: k.name,
+ deskripsi: k.deskripsi,
+ imageId,
+ },
+ });
+ }
+ console.log("info wabah penyakit success ...");
+
+ // ==================== SUBMENU POLSEK TERDEKAT ================
+ console.log("🔄 Seeding Polsek Terdekat...");
+ for (const k of polsekTerdekat) {
+ await prisma.polsekTerdekat.upsert({
+ where: {
+ id: k.id,
+ },
+ update: {
+ nama: k.nama,
+ jarakKeDesa: k.jarakKeDesa,
+ alamat: k.alamat,
+ nomorTelepon: k.nomorTelepon,
+ jamOperasional: k.jamOperasional,
+ embedMapUrl: k.embedMapUrl,
+ namaTempatMaps: k.namaTempatMaps,
+ alamatMaps: k.alamatMaps,
+ linkPetunjukArah: k.linkPetunjukArah,
+ layananPolsekId: k.layananPolsekId,
+ },
+ create: {
+ id: k.id,
+ nama: k.nama,
+ jarakKeDesa: k.jarakKeDesa,
+ alamat: k.alamat,
+ nomorTelepon: k.nomorTelepon,
+ jamOperasional: k.jamOperasional,
+ embedMapUrl: k.embedMapUrl,
+ namaTempatMaps: k.namaTempatMaps,
+ alamatMaps: k.alamatMaps,
+ linkPetunjukArah: k.linkPetunjukArah,
+ layananPolsekId: k.layananPolsekId,
+ },
+ });
+ }
+
+ console.log("polsek terdekat success ...");
+
+ console.log("🔄 Seeding Layanan Polsek...");
+ for (const k of layananPolsek) {
+ await prisma.layananPolsek.upsert({
+ where: {
+ id: k.id,
+ },
+ update: {
+ nama: k.nama,
+ },
+ create: {
+ id: k.id,
+ nama: k.nama,
+ },
+ });
+ }
+
+ console.log("layanan polsek success ...");
+
+ console.log("🔄 Seeding Layanan To Polsek...");
+ for (const k of layananToPolsek) {
+ await prisma.layananToPolsek.upsert({
+ where: {
+ id: k.id,
+ },
+ update: {
+ layananId: k.layananId,
+ polsekTerdekatId: k.polsekTerdekatId,
+ },
+ create: {
+ id: k.id,
+ layananId: k.layananId,
+ polsekTerdekatId: k.polsekTerdekatId,
+ },
+ });
+ }
+
+ console.log("layanan to polsek success ...");
+
+ // ==================== SUBMENU KONTAK DARURAT =================
+
+ console.log("🔄 Seeding Kontak Item...");
+ for (const e of kontakItem) {
+ await prisma.kontakItem.upsert({
+ where: {
+ id: e.id,
+ },
+ update: {
+ nama: e.nama,
+ icon: e.icon,
+ nomorTelepon: e.nomorTelepon,
+ },
+ create: {
+ id: e.id, // ✅ WAJIB
+ nama: e.nama,
+ icon: e.icon,
+ nomorTelepon: e.nomorTelepon,
+ },
+ });
+ }
+ console.log("✅ Kontak Item seeded successfully");
+
+ console.log("🔄 Seeding Kontak Darurat Keamanan...");
+ for (const d of kontakDaruratKeamanan) {
+ await prisma.kontakDaruratKeamanan.upsert({
+ where: {
+ id: d.id,
+ },
+ update: {
+ nama: d.nama,
+ icon: d.icon,
+ kategoriId: d.kategoriId,
+ },
+ create: {
+ id: d.id,
+ nama: d.nama,
+ icon: d.icon,
+ kategoriId: d.kategoriId,
+ },
+ });
+ }
+
+ console.log("✅ Kontak Darurat Keamanan seeded successfully");
+
+ console.log("🔄 Seeding Kontak Darurat To Item...");
+ for (const f of kontakDaruratToItem) {
+ // ✅ Validasi foreign keys
+ const kontakDaruratExists = await prisma.kontakDaruratKeamanan.findUnique({
+ where: { id: f.kontakDaruratId },
+ });
+
+ const kontakItemExists = await prisma.kontakItem.findUnique({
+ where: { id: f.kontakItemId },
+ });
+
+ if (!kontakDaruratExists) {
+ console.warn(
+ `⚠️ KontakDarurat ${f.kontakDaruratId} not found, skipping...`
+ );
+ continue;
+ }
+
+ if (!kontakItemExists) {
+ console.warn(`⚠️ KontakItem ${f.kontakItemId} not found, skipping...`);
+ continue;
+ }
+
+ await prisma.kontakDaruratToItem.upsert({
+ where: { id: f.id },
+ update: {
+ kontakDaruratId: f.kontakDaruratId,
+ kontakItemId: f.kontakItemId,
+ },
+ create: {
+ id: f.id,
+ kontakDaruratId: f.kontakDaruratId,
+ kontakItemId: f.kontakItemId,
+ },
+ });
+ }
+ console.log("✅ Kontak Darurat To Item seeded successfully");
+
+ // ==================== SUBMENU PENCEGAHAN KRIMINALITAS ========
+ console.log("🔄 Seeding Pencegahan Kriminalitas...");
+ for (const d of pencegahanKriminalitas) {
+ await prisma.pencegahanKriminalitas.upsert({
+ where: {
+ id: d.id,
+ },
+ update: {
+ judul: d.judul,
+ deskripsi: d.deskripsi,
+ deskripsiSingkat: d.deskripsiSingkat,
+ linkVideo: d.linkVideo,
+ },
+ create: {
+ id: d.id,
+ judul: d.judul,
+ deskripsi: d.deskripsi,
+ deskripsiSingkat: d.deskripsiSingkat,
+ linkVideo: d.linkVideo,
+ },
+ });
+ }
+
+ console.log("✅ Pencegahan Kriminalitas seeded successfully");
+ // ==================== SUBMENU LAPORAN PUBLIK =================
+ console.log("🔄 Seeding Laporan Publik...");
+ for (const l of laporanPublik) {
+ await prisma.laporanPublik.upsert({
+ where: {
+ id: l.id,
+ },
+ update: {
+ judul: l.judul,
+ lokasi: l.lokasi,
+ tanggalWaktu: l.tanggalWaktu,
+ kronologi: l.kronologi,
+ },
+ create: {
+ id: l.id,
+ judul: l.judul,
+ lokasi: l.lokasi,
+ tanggalWaktu: l.tanggalWaktu,
+ kronologi: l.kronologi,
+ },
+ });
+ }
+
+ console.log("laporan publik success ...");
+
+ console.log("🔄 Seeding Penanganan Laporan...");
+ for (const l of penangananLaporan) {
+ await prisma.penangananLaporanPublik.upsert({
+ where: {
+ id: l.id,
+ },
+ update: {
+ deskripsi: l.deskripsi,
+ laporanId: l.laporanId,
+ },
+ create: {
+ id: l.id,
+ deskripsi: l.deskripsi,
+ laporanId: l.laporanId,
+ },
+ });
+ }
+
+ console.log("penanganan laporan success ...");
+
+ // ==================== SUBMENU TIPS KEAMANAN ==================
+ console.log("🔄 Seeding Tips Keamanan...");
+ for (const t of tipsKeamanan) {
+ await prisma.menuTipsKeamanan.upsert({
+ where: {
+ id: t.id,
+ },
+ update: {
+ judul: t.judul,
+ deskripsi: t.deskripsi,
+ imageId: t.imageId,
+ },
+ create: {
+ id: t.id,
+ judul: t.judul,
+ deskripsi: t.deskripsi,
+ imageId: t.imageId,
+ },
+ });
+ }
+
+ console.log("✅ Tips Keamanan seeded successfully");
+
+ // ====================== MENU EKONOMI ========================
+ // ==================== SUBMENU PASAR DESA ====================
+ console.log("🔄 Seeding Kategori Produk...");
+ for (const k of kategoriProduk) {
+ await prisma.kategoriProduk.upsert({
+ where: {
+ id: k.id,
+ },
+ update: {
+ nama: k.nama,
+ },
+ create: {
+ id: k.id,
+ nama: k.nama,
+ },
+ });
+ }
+ console.log("✅ Kategori Produk seeded successfully");
+
+ console.log("🔄 Seeding Pasar Desa...");
+ for (const p of pasarDesa) {
+ await prisma.pasarDesa.upsert({
+ where: {
+ id: p.id,
+ },
+ update: {
+ nama: p.nama,
+ imageId: p.imageId,
+ harga: p.harga,
+ rating: p.rating,
+ alamatUsaha: p.alamatUsaha,
+ kontak: p.kontak,
+ deskripsi: p.deskripsi,
+ kategoriProdukId: p.kategoriProdukId,
+ },
+ create: {
+ id: p.id,
+ nama: p.nama,
+ imageId: p.imageId,
+ harga: p.harga,
+ rating: p.rating,
+ alamatUsaha: p.alamatUsaha,
+ kontak: p.kontak,
+ deskripsi: p.deskripsi,
+ kategoriProdukId: p.kategoriProdukId,
+ },
+ });
+ }
+ console.log("✅ Pasar Desa seeded successfully");
+
+ console.log("🔄 Seeding Kategori To Pasar...");
+ for (const p of kategoriToPasar) {
+ await prisma.kategoriToPasar.upsert({
+ where: {
+ id: p.id,
+ },
+ update: {
+ kategoriId: p.kategoriId,
+ pasarDesaId: p.pasarDesaId,
+ },
+ create: {
+ id: p.id,
+ kategoriId: p.kategoriId,
+ pasarDesaId: p.pasarDesaId,
+ },
+ });
+ }
+
+ // ==================== SUBMENU LOWONGAN KERJA LOKAL ==========
+ console.log("🔄 Seeding Lowongan Kerja Lokal...");
+ for (const k of lowonganKerjaLokal) {
+ await prisma.lowonganPekerjaan.upsert({
+ where: {
+ id: k.id,
+ },
+ update: {
+ posisi: k.posisi,
+ namaPerusahaan: k.namaPerusahaan,
+ lokasi: k.lokasi,
+ tipePekerjaan: k.tipePekerjaan,
+ gaji: k.gaji,
+ deskripsi: k.deskripsi,
+ kualifikasi: k.kualifikasi,
+ notelp: k.notelp,
+ },
+ create: {
+ id: k.id,
+ posisi: k.posisi,
+ namaPerusahaan: k.namaPerusahaan,
+ lokasi: k.lokasi,
+ tipePekerjaan: k.tipePekerjaan,
+ gaji: k.gaji,
+ deskripsi: k.deskripsi,
+ kualifikasi: k.kualifikasi,
+ notelp: k.notelp,
+ },
+ });
+ }
+ console.log("✅ Lowongan Kerja Lokal seeded successfully");
+
+ // ==================== SUBMENU STRUKTUR ORGANISASI DAN SK PENGURUS BUMDES ==========
const sortedPosisiBumdes = flattenedPosisiBumdes.sort(
(a, b) => a.hierarki - b.hierarki
);
@@ -1648,6 +2047,9 @@ import resolveImageById from "./resolveImageByName";
}
console.log("pegawai success ...");
+ // ==================== SUBMENU PENDAPATAN ASLI DESA ==========
+
+ // ==================== SUBMENU JUMLAH PENGANGGURAN ==========
for (const d of detailDataPengangguran) {
await prisma.detailDataPengangguran.upsert({
where: {
@@ -1671,6 +2073,96 @@ import resolveImageById from "./resolveImageByName";
}
console.log("📊 detailDataPengangguran success ...");
+ // ==================== SUBMENU PENDUDUK USIA KERJA ==========
+
+ // ==================== SUBMENU PENDUDUK MISKIN =============
+
+ // ==================== SUBMENU PROGRAM KEMISKINAN =============
+ for (const k of statistikKemiskinan) {
+ await prisma.statistikKemiskinan.upsert({
+ where: {
+ id: k.id,
+ },
+ update: {
+ tahun: k.tahun,
+ jumlah: k.jumlah,
+ },
+ create: {
+ id: k.id,
+ tahun: k.tahun,
+ jumlah: k.jumlah,
+ },
+ });
+ }
+ console.log("📊 Statistik Kemiskinan seeded successfully");
+
+ console.log("🔄 Seeding Program Kemiskinan...");
+ for (const k of programKemiskinan) {
+ await prisma.programKemiskinan.upsert({
+ where: {
+ id: k.id,
+ },
+ update: {
+ nama: k.nama,
+ deskripsi: k.deskripsi,
+ icon: k.icon,
+ statistikId: k.statistikId,
+ },
+ create: {
+ id: k.id,
+ nama: k.nama,
+ deskripsi: k.deskripsi,
+ icon: k.icon,
+ statistikId: k.statistikId,
+ },
+ });
+ }
+ console.log("✅ Program Kemiskinan seeded successfully");
+
+ // ==================== SUBMENU SEKTOR UNGGULAN DESA =============
+ console.log("🔄 Seeding Sektor Unggulan Desa...");
+ for (const k of sektorUnggulanDesa) {
+ await prisma.sektorUnggulanDesa.upsert({
+ where: {
+ id: k.id,
+ },
+ update: {
+ name: k.name,
+ description: k.description,
+ value: k.value,
+ },
+ create: {
+ id: k.id,
+ name: k.name,
+ description: k.description,
+ value: k.value,
+ },
+ });
+ }
+ console.log("✅ Sektor Unggulan Desa seeded successfully");
+
+ // ==================== SUBMENU DEMOGRAFI PEKERJAAN =============
+ console.log("🔄 Seeding Demografi Pekerjaan...");
+ for (const k of demografiPekerjaan) {
+ await prisma.dataDemografiPekerjaan.upsert({
+ where: {
+ id: k.id,
+ },
+ update: {
+ pekerjaan: k.pekerjaan,
+ lakiLaki: k.lakiLaki,
+ perempuan: k.perempuan,
+ },
+ create: {
+ id: k.id,
+ pekerjaan: k.pekerjaan,
+ lakiLaki: k.lakiLaki,
+ perempuan: k.perempuan,
+ },
+ });
+ }
+ console.log("✅ Demografi Pekerjaan seeded successfully");
+
// =========== KATEGORI GOTONG ROYONG ===========
// Add IDs to the kategoriKegiatan data
const kategoriKegiatan = kategoriKegiatanData.map((k, index) => ({
diff --git a/prisma/seed_assets.ts b/prisma/seed_assets.ts
index a0922c67..133614fd 100644
--- a/prisma/seed_assets.ts
+++ b/prisma/seed_assets.ts
@@ -45,7 +45,7 @@ export default async function seedAssets() {
// 1. Download zip
const url =
- "https://cld-dkr-makuro-seafile.wibudev.com/f/1d6104cdf2b849f89533/?dl=1";
+ "https://cld-dkr-makuro-seafile.wibudev.com/f/88293b915cf34b939819/?dl=1";
const res = await fetchWithRetry(url, 3, 20000);
// Validasi content-type
diff --git a/src/app/admin/(dashboard)/_com/iconMap.tsx b/src/app/admin/(dashboard)/_com/iconMap.tsx
index de88a3dc..fc058736 100644
--- a/src/app/admin/(dashboard)/_com/iconMap.tsx
+++ b/src/app/admin/(dashboard)/_com/iconMap.tsx
@@ -27,8 +27,25 @@ import {
IconFiretruck,
IconBuilding,
IconAlertTriangle,
+
+ // ===== Tambahan =====
+ IconLifebuoy,
+ IconRun,
+ IconShield,
+ IconPhoneCall,
+ IconFirstAidKit,
+ IconStethoscope,
+ IconBuildingCommunity,
+ IconFileText,
+ IconInfoCircle,
+ IconMessageReport,
+ IconUsers,
+ IconQuestionMark,
} from '@tabler/icons-react'
+/* =======================
+ Icon Keys (DB Safe)
+======================= */
export type IconKey =
| 'ekowisata'
| 'kompetisi'
@@ -50,14 +67,33 @@ export type IconKey =
| 'pelatihan'
| 'subsidi'
| 'layananKesehatan'
+
+ // ===== Keamanan & Darurat =====
| 'polisi'
| 'ambulans'
| 'pemadam'
- | 'rumahSakit'
- | 'bangunan'
| 'darurat'
+ | 'sar'
+ | 'evakuasi'
+ | 'keamanan'
+ | 'teleponDarurat'
+ // ===== Kesehatan =====
+ | 'rumahSakit'
+ | 'puskesmas'
+ | 'klinik'
+ // ===== Pemerintahan =====
+ | 'bangunan'
+ | 'kantorDesa'
+ | 'administrasi'
+ | 'informasi'
+ | 'pengaduan'
+ | 'layananPublik'
+
+/* =======================
+ Icon Map
+======================= */
const iconMap: Record> = {
ekowisata: IconLeaf,
kompetisi: IconTrophy,
@@ -79,22 +115,45 @@ const iconMap: Record> = {
pelatihan: IconSchool,
subsidi: IconShoppingCart,
layananKesehatan: IconHospital,
+
+ // ===== Keamanan & Darurat =====
polisi: IconShieldFilled,
ambulans: IconAmbulance,
pemadam: IconFiretruck,
+ darurat: IconAlertTriangle,
+ sar: IconLifebuoy,
+ evakuasi: IconRun,
+ keamanan: IconShield,
+ teleponDarurat: IconPhoneCall,
+
+ // ===== Kesehatan =====
rumahSakit: IconHospital,
+ puskesmas: IconFirstAidKit,
+ klinik: IconStethoscope,
+
+ // ===== Pemerintahan =====
bangunan: IconBuilding,
- darurat: IconAlertTriangle
+ kantorDesa: IconBuildingCommunity,
+ administrasi: IconFileText,
+ informasi: IconInfoCircle,
+ pengaduan: IconMessageReport,
+ layananPublik: IconUsers,
}
+/* =======================
+ Icon Mapper Component
+======================= */
type Props = {
name: IconKey
size?: number
color?: string
}
-export const IconMapper: React.FC = ({ name, size = 24, color }) => {
- const IconComponent = iconMap[name]
- if (!IconComponent) return null
+export const IconMapper: React.FC = ({
+ name,
+ size = 24,
+ color,
+}) => {
+ const IconComponent = iconMap[name] ?? IconQuestionMark
return
}
diff --git a/src/app/admin/(dashboard)/_com/selectIcon.tsx b/src/app/admin/(dashboard)/_com/selectIcon.tsx
index 88e17af8..4d814917 100644
--- a/src/app/admin/(dashboard)/_com/selectIcon.tsx
+++ b/src/app/admin/(dashboard)/_com/selectIcon.tsx
@@ -6,27 +6,38 @@ import {
IconAlertTriangle,
IconAmbulance,
IconBuilding,
+ IconBuildingCommunity,
IconCash,
IconChartLine,
IconChristmasTreeFilled,
IconClipboardTextFilled,
IconDroplet,
+ IconFileText,
IconFiretruck,
+ IconFirstAidKit,
IconHome,
IconHomeEco,
IconHospital,
+ IconInfoCircle,
IconLeaf,
+ IconLifebuoy,
+ IconMessageReport,
+ IconPhoneCall,
IconRecycle,
+ IconRun,
IconScale,
IconSchool,
+ IconShield,
IconShieldFilled,
IconShoppingCart,
+ IconStethoscope,
IconTent,
IconTrashFilled,
IconTree,
IconTrendingUp,
IconTrophy,
IconTruckFilled,
+ IconUsers,
} from '@tabler/icons-react';
import { useEffect, useState } from 'react';
@@ -51,15 +62,32 @@ const iconMap = {
pelatihan: { label: 'Pelatihan', icon: IconSchool },
subsidi: { label: 'Subsidi', icon: IconShoppingCart },
layananKesehatan: { label: 'Layanan Kesehatan', icon: IconHospital },
+
+ // ===== Keamanan & Darurat =====
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 },
+ pemadam: { label: 'Pemadam Kebakaran', icon: IconFiretruck },
darurat: { label: 'Darurat', icon: IconAlertTriangle },
+ sar: { label: 'SAR / Basarnas', icon: IconLifebuoy },
+ evakuasi: { label: 'Evakuasi', icon: IconRun },
+ keamanan: { label: 'Keamanan', icon: IconShield },
+ teleponDarurat: { label: 'Telepon Darurat', icon: IconPhoneCall },
+ // ===== Kesehatan =====
+ rumahSakit: { label: 'Rumah Sakit', icon: IconHospital },
+ puskesmas: { label: 'Puskesmas', icon: IconFirstAidKit },
+ klinik: { label: 'Klinik', icon: IconStethoscope },
+
+ // ===== Pemerintahan & Fasilitas =====
+ bangunan: { label: 'Bangunan', icon: IconBuilding },
+ kantorDesa: { label: 'Kantor Desa', icon: IconBuildingCommunity },
+ administrasi: { label: 'Administrasi', icon: IconFileText },
+ informasi: { label: 'Informasi', icon: IconInfoCircle },
+ pengaduan: { label: 'Pengaduan', icon: IconMessageReport },
+ layananPublik: { label: 'Layanan Publik', icon: IconUsers },
};
+
type IconKey = keyof typeof iconMap;
const iconList = Object.entries(iconMap).map(([value, data]) => ({
diff --git a/src/app/admin/(dashboard)/_com/selectIconEdit.tsx b/src/app/admin/(dashboard)/_com/selectIconEdit.tsx
index c221672f..ff7ba141 100644
--- a/src/app/admin/(dashboard)/_com/selectIconEdit.tsx
+++ b/src/app/admin/(dashboard)/_com/selectIconEdit.tsx
@@ -26,6 +26,17 @@ import {
IconTruckFilled,
IconBuilding,
IconAlertTriangle,
+ IconBuildingCommunity,
+ IconFileText,
+ IconFirstAidKit,
+ IconInfoCircle,
+ IconLifebuoy,
+ IconMessageReport,
+ IconPhoneCall,
+ IconRun,
+ IconShield,
+ IconStethoscope,
+ IconUsers,
} from '@tabler/icons-react';
const iconMap = {
@@ -49,12 +60,29 @@ const iconMap = {
pelatihan: { label: 'Pelatihan', icon: IconSchool },
subsidi: { label: 'Subsidi', icon: IconShoppingCart },
layananKesehatan: { label: 'Layanan Kesehatan', icon: IconHospital },
+
+ // ===== Keamanan & Darurat =====
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 },
+ pemadam: { label: 'Pemadam Kebakaran', icon: IconFiretruck },
darurat: { label: 'Darurat', icon: IconAlertTriangle },
+ sar: { label: 'SAR / Basarnas', icon: IconLifebuoy },
+ evakuasi: { label: 'Evakuasi', icon: IconRun },
+ keamanan: { label: 'Keamanan', icon: IconShield },
+ teleponDarurat: { label: 'Telepon Darurat', icon: IconPhoneCall },
+
+ // ===== Kesehatan =====
+ rumahSakit: { label: 'Rumah Sakit', icon: IconHospital },
+ puskesmas: { label: 'Puskesmas', icon: IconFirstAidKit },
+ klinik: { label: 'Klinik', icon: IconStethoscope },
+
+ // ===== Pemerintahan & Fasilitas =====
+ bangunan: { label: 'Bangunan', icon: IconBuilding },
+ kantorDesa: { label: 'Kantor Desa', icon: IconBuildingCommunity },
+ administrasi: { label: 'Administrasi', icon: IconFileText },
+ informasi: { label: 'Informasi', icon: IconInfoCircle },
+ pengaduan: { label: 'Pengaduan', icon: IconMessageReport },
+ layananPublik: { label: 'Layanan Publik', icon: IconUsers },
};
export type IconKey = keyof typeof iconMap;
diff --git a/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts b/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts
index c25a99ff..4d64fda8 100644
--- a/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts
+++ b/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts
@@ -13,6 +13,7 @@ const templatePasarDesaForm = z.object({
rating: z.number().min(1, "Rating minimal 1"),
kategoriId: z.array(z.string()).min(1, "Minimal pilih satu kategori"),
kontak: z.string().min(1, "Kontak wajib diisi"),
+ deskripsi: z.string().min(1, "Deskripsi wajib diisi"),
});
const defaultPasarDesaForm = {
@@ -23,6 +24,7 @@ const defaultPasarDesaForm = {
rating: 0,
kategoriId: [] as string[],
kontak: "",
+ deskripsi: ""
};
const pasarDesa = proxy({
@@ -191,6 +193,7 @@ const pasarDesa = proxy({
rating: data.rating,
kategoriId: data.kategoriId,
kontak: data.kontak,
+ deskripsi: data.deskripsi
};
return data;
} else {
@@ -229,6 +232,7 @@ const pasarDesa = proxy({
rating: this.form.rating,
kategoriId: this.form.kategoriId,
kontak: this.form.kontak,
+ deskripsi: this.form.deskripsi
}),
});
if (!response.ok) {
diff --git a/src/app/admin/(dashboard)/_state/keamanan/polsek-terdekat.ts b/src/app/admin/(dashboard)/_state/keamanan/polsek-terdekat.ts
index ad0c696f..03415edb 100644
--- a/src/app/admin/(dashboard)/_state/keamanan/polsek-terdekat.ts
+++ b/src/app/admin/(dashboard)/_state/keamanan/polsek-terdekat.ts
@@ -15,7 +15,7 @@ const templateForm = z.object({
namaTempatMaps: z.string().min(1, "Nama Tempat Maps minimal 1 karakter"),
alamatMaps: z.string().min(1, "Alamat Maps minimal 1 karakter"),
linkPetunjukArah: z.string().min(1, "Link Petunjuk Arah minimal 1 karakter"),
- layananPolsekId: z.string().min(1, "Layanan Polsek Id minimal 1 karakter"),
+ layananPolsekId: z.array(z.string()).min(1, "Pilih minimal 1 layanan polsek"),
});
const defaultForm = {
@@ -28,7 +28,7 @@ const defaultForm = {
namaTempatMaps: "",
alamatMaps: "",
linkPetunjukArah: "",
- layananPolsekId: "",
+ layananPolsekId: [] as string[],
};
const polsekTerdekatState = proxy({
@@ -66,6 +66,11 @@ const polsekTerdekatState = proxy({
| Prisma.PolsekTerdekatGetPayload<{
include: {
layananPolsek: true;
+ LayananToPolsek: {
+ include: {
+ layanan: true;
+ }
+ }
};
}>[]
| null,
@@ -104,7 +109,14 @@ const polsekTerdekatState = proxy({
},
findUnique: {
data: null as Prisma.PolsekTerdekatGetPayload<{
- include: { layananPolsek: true };
+ include: {
+ layananPolsek: true;
+ LayananToPolsek: {
+ include: {
+ layanan: true;
+ }
+ }
+ };
}> | null,
async load(id: string) {
try {
@@ -117,7 +129,7 @@ const polsekTerdekatState = proxy({
polsekTerdekatState.findUnique.data = null;
}
} catch (error) {
- console.error("Error fetching data:", error);
+ console.error("Gagal fetch detail polsek terdekat:", error);
polsekTerdekatState.findUnique.data = null;
}
},
@@ -273,10 +285,13 @@ const polsekTerdekatState = proxy({
};
}> | null,
loading: false,
- load: async () => { // Changed to arrow function
+ load: async () => {
+ // Changed to arrow function
polsekTerdekatState.findFirst.loading = true;
try {
- const res = await ApiFetch.api.keamanan.polsekterdekat["find-first"].get();
+ const res = await ApiFetch.api.keamanan.polsekterdekat[
+ "find-first"
+ ].get();
if (res.status === 200 && res.data?.success) {
polsekTerdekatState.findFirst.data = res.data.data || null;
} else {
@@ -287,8 +302,284 @@ const polsekTerdekatState = proxy({
} finally {
polsekTerdekatState.findFirst.loading = false;
}
- }
- }
+ },
+ },
});
-export default polsekTerdekatState;
+const templateFormLayananPolsek = z.object({
+ nama: z.string().min(1, "Nama harus diisi"),
+});
+
+const defaultFormLayananPolsek = {
+ nama: "",
+};
+
+const layananPolsek = proxy({
+ create: {
+ form: { ...defaultFormLayananPolsek },
+ loading: false,
+ async create() {
+ const cek = templateFormLayananPolsek.safeParse(
+ layananPolsek.create.form
+ );
+ if (!cek.success) {
+ const err = `[${cek.error.issues
+ .map((v) => `${v.path.join(".")}`)
+ .join("\n")}] required`;
+ return toast.error(err);
+ }
+
+ try {
+ layananPolsek.create.loading = true;
+ const res = await ApiFetch.api.keamanan["layananpolsek"][
+ "create"
+ ].post(layananPolsek.create.form);
+ if (res.status === 200) {
+ layananPolsek.findManyAll.load();
+ return toast.success("Data Kategori Berita Berhasil Dibuat");
+ }
+ console.log(res);
+ return toast.error("failed create");
+ } catch (error) {
+ console.log(error);
+ return toast.error("failed create");
+ } finally {
+ layananPolsek.create.loading = false;
+ }
+ },
+ },
+ findMany: {
+ data: null as
+ | Prisma.LayananPolsekGetPayload<{
+ omit: {
+ isActive: true;
+ };
+ }>[]
+ | null,
+ page: 1,
+ totalPages: 1,
+ loading: false,
+ search: "",
+ load: async (page = 1, limit = 10, search = "") => {
+ layananPolsek.findMany.loading = true; // ✅ Akses langsung via nama path
+ layananPolsek.findMany.page = page;
+ layananPolsek.findMany.search = search;
+
+ try {
+ const query: any = { page, limit };
+ if (search) query.search = search;
+
+ const res = await ApiFetch.api.keamanan["layananpolsek"]["findMany"].get({ query });
+
+ if (res.status === 200 && res.data?.success) {
+ layananPolsek.findMany.data = res.data.data ?? [];
+ layananPolsek.findMany.totalPages = res.data.totalPages ?? 1;
+ } else {
+ layananPolsek.findMany.data = [];
+ layananPolsek.findMany.totalPages = 1;
+ }
+ } catch (err) {
+ console.error("Gagal fetch layanan polsek paginated:", err);
+ layananPolsek.findMany.data = [];
+ layananPolsek.findMany.totalPages = 1;
+ } finally {
+ layananPolsek.findMany.loading = false;
+ }
+ },
+ },
+ findManyAll: {
+ data: [] as Prisma.LayananPolsekGetPayload<{ omit: { isActive: true } }>[],
+ loading: false,
+
+ async load() {
+ this.loading = true;
+ try {
+ const res = await ApiFetch.api.keamanan["layananpolsek"][
+ "findManyAll"
+ ].get();
+
+ if (res.status === 200 && res.data?.success) {
+ this.data = (res.data.data ?? []).map((item: any) => ({
+ id: String(item.id),
+ nama: String(item.nama || ""),
+ createdAt: item.createdAt ? new Date(item.createdAt) : new Date(),
+ updatedAt: item.updatedAt ? new Date(item.updatedAt) : new Date(),
+ deletedAt: item.deletedAt ? new Date(item.deletedAt) : null,
+ }));
+ } else {
+ this.data = [];
+ }
+ } catch (error) {
+ console.error("Gagal fetch layanan polsek:", error);
+ this.data = [];
+ } finally {
+ this.loading = false;
+ }
+ },
+ },
+ findUnique: {
+ data: null as Prisma.LayananPolsekGetPayload<{
+ omit: {
+ isActive: true;
+ };
+ }> | null,
+ loading: false,
+ async load(id: string) {
+ try {
+ const res = await fetch(`/api/keamanan/layananpolsek/${id}`);
+ if (res.ok) {
+ const data = await res.json();
+ layananPolsek.findUnique.data = data.data ?? null;
+ } else {
+ console.error("Failed to fetch data", res.status, res.statusText);
+ layananPolsek.findUnique.data = null;
+ }
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ layananPolsek.findUnique.data = null;
+ }
+ },
+ },
+ delete: {
+ loading: false,
+ async delete(id: string) {
+ if (!id) return toast.warn("ID tidak valid");
+
+ try {
+ layananPolsek.delete.loading = true;
+
+ const response = await fetch(`/api/keamanan/layananpolsek/del/${id}`, {
+ method: "DELETE",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+
+ const result = await response.json();
+
+ if (response.ok && result?.success) {
+ toast.success(
+ result.message || "Data layanan polsek berhasil dihapus"
+ );
+ await layananPolsek.findManyAll.load(); // refresh list
+ } else {
+ toast.error(result?.message || "Gagal menghapus Data layanan polsek");
+ }
+ } catch (error) {
+ console.error("Gagal delete:", error);
+ toast.error("Terjadi kesalahan saat menghapus Data layanan polsek");
+ } finally {
+ layananPolsek.delete.loading = false;
+ }
+ },
+ },
+ update: {
+ id: "",
+ form: { ...defaultFormLayananPolsek },
+ loading: false,
+ async load(id: string) {
+ if (!id) {
+ toast.warn("ID tidak valid");
+ return null;
+ }
+
+ try {
+ const response = await fetch(`/api/keamanan/layananpolsek/${id}`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const result = await response.json();
+
+ if (result?.success) {
+ const data = result.data;
+ this.id = data.id;
+ this.form = {
+ nama: data.nama,
+ };
+ return data; // Return the loaded data
+ } else {
+ throw new Error(result?.message || "Gagal memuat data");
+ }
+ } catch (error) {
+ console.error("Error loading layanan polsek:", error);
+ toast.error(
+ error instanceof Error ? error.message : "Gagal memuat data"
+ );
+ return null;
+ }
+ },
+ async update() {
+ const cek = templateFormLayananPolsek.safeParse(
+ layananPolsek.update.form
+ );
+ if (!cek.success) {
+ const err = `[${cek.error.issues
+ .map((v) => `${v.path.join(".")}`)
+ .join("\n")}] required`;
+ toast.error(err);
+ return false;
+ }
+
+ try {
+ layananPolsek.update.loading = true;
+
+ const response = await fetch(
+ `/api/keamanan/layananpolsek/${this.id}`,
+ {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ nama: this.form.nama,
+ }),
+ }
+ );
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}));
+ throw new Error(
+ errorData.message || `HTTP error! status: ${response.status}`
+ );
+ }
+
+ const result = await response.json();
+
+ if (result.success) {
+ toast.success("Berhasil update data layanan polsek");
+ await layananPolsek.findManyAll.load(); // refresh list
+ return true;
+ } else {
+ throw new Error(result.message || "Gagal update data layanan polsek");
+ }
+ } catch (error) {
+ console.error("Error updating data layanan polsek:", error);
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : "Terjadi kesalahan saat update data layanan polsek"
+ );
+ return false;
+ } finally {
+ layananPolsek.update.loading = false;
+ }
+ },
+ reset() {
+ layananPolsek.update.id = "";
+ layananPolsek.update.form = { ...defaultFormLayananPolsek };
+ },
+ },
+});
+
+const statePolsekTerdekat = proxy({
+ polsekTerdekatState,
+ layananPolsek,
+});
+
+export default statePolsekTerdekat;
diff --git a/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/edit/page.tsx
index f1116ae9..f656a13f 100644
--- a/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/edit/page.tsx
@@ -1,6 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client';
+import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import pasarDesaState from '@/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
@@ -33,6 +34,7 @@ type FormData = {
rating: number;
kategoriId: string[];
kontak: string;
+ deskripsi: string;
};
function EditPasarDesa() {
@@ -51,6 +53,7 @@ function EditPasarDesa() {
rating: 0,
kategoriId: [],
kontak: '',
+ deskripsi: ''
});
const [originalData, setOriginalData] = useState({
@@ -62,6 +65,7 @@ function EditPasarDesa() {
rating: 0,
kategoriId: [],
kontak: '',
+ deskripsi: ''
});
// load data awal
@@ -83,6 +87,7 @@ function EditPasarDesa() {
rating: data.rating || 0,
kategoriId: data.KategoriToPasar?.map((k: any) => k.kategoriId) || [],
kontak: data.kontak || '',
+ deskripsi: data.deskripsi || ''
});
setOriginalData({
nama: data.nama || '',
@@ -93,6 +98,7 @@ function EditPasarDesa() {
rating: data.rating || 0,
kategoriId: data.KategoriToPasar?.map((k: any) => k.kategoriId) || [],
kontak: data.kontak || '',
+ deskripsi: data.deskripsi || ''
});
if (data.image?.link) setPreviewImage(data.image.link);
}
@@ -120,12 +126,13 @@ function EditPasarDesa() {
rating: originalData.rating,
kategoriId: (originalData as any)?.KategoriToPasar?.map((k: any) => k.kategoriId) || [],
kontak: originalData.kontak,
+ deskripsi: originalData.deskripsi
});
setPreviewImage(originalData.imageUrl || null);
setFile(null);
toast.info("Form dikembalikan ke data awal");
};
-
+
const handleSubmit = async () => {
try {
@@ -157,7 +164,7 @@ function EditPasarDesa() {
};
return (
-
+
router.back()} p="xs" radius="md">
@@ -316,6 +323,19 @@ function EditPasarDesa() {
error={!formData.kategoriId.length ? 'Pilih minimal satu kategori' : undefined}
/>
+ {/* Input Deskripsi */}
+
+
+ Deskripsi
+
+
+ setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
+ }
+ />
+
+
+
{/* Header dengan tombol kembali */}
router.back()} p="xs" radius="md">
@@ -234,6 +236,18 @@ export default function CreatePasarDesa() {
}
/>
+
+
+ Deskripsi Produk
+
+ {
+ statePasar.pasarDesa.create.form.deskripsi = val;
+ }}
+ />
+
+
{/* Tombol Submit */}
([]);
- const [modalOpen, setModalOpen] = useState(false);
- const [modalUpdateOpen, setModalUpdateOpen] = useState(false);
- const [namaLayananBaru, setNamaLayananBaru] = useState("");
- const [selectedLayananId, setSelectedLayananId] = useState(
- null
- );
- const [namaLayananUpdate, setNamaLayananUpdate] = useState("");
- const [isSubmitting, setIsSubmitting] = useState(false);
- const [formData, setFormData] = useState({
- nama: "",
- jarakKeDesa: "",
- alamat: "",
- nomorTelepon: "",
- jamOperasional: "",
- embedMapUrl: "",
- namaTempatMaps: "",
- alamatMaps: "",
- linkPetunjukArah: "",
- layananPolsekId: "",
- });
-
- const [originalData, setOriginalData] = useState({
- nama: "",
- jarakKeDesa: "",
- alamat: "",
- nomorTelepon: "",
- jamOperasional: "",
- embedMapUrl: "",
- namaTempatMaps: "",
- alamatMaps: "",
- linkPetunjukArah: "",
- layananPolsekId: "",
- });
-
- // load data untuk form edit
- useEffect(() => {
- const loadPolsekTerdekat = async () => {
- const id = params?.id as string;
- if (!id) return;
-
- try {
- const data = await polsekState.edit.load(id);
- if (data) {
- setFormData({
- nama: data.nama || "",
- jarakKeDesa: data.jarakKeDesa || "",
- alamat: data.alamat || "",
- nomorTelepon: data.nomorTelepon || "",
- jamOperasional: data.jamOperasional || "",
- embedMapUrl: data.embedMapUrl || "",
- namaTempatMaps: data.namaTempatMaps || "",
- alamatMaps: data.alamatMaps || "",
- linkPetunjukArah: data.linkPetunjukArah || "",
- layananPolsekId: data.layananPolsekId || "",
- });
-
- setOriginalData({
- nama: data.nama || "",
- jarakKeDesa: data.jarakKeDesa || "",
- alamat: data.alamat || "",
- nomorTelepon: data.nomorTelepon || "",
- jamOperasional: data.jamOperasional || "",
- embedMapUrl: data.embedMapUrl || "",
- namaTempatMaps: data.namaTempatMaps || "",
- alamatMaps: data.alamatMaps || "",
- linkPetunjukArah: data.linkPetunjukArah || "",
- layananPolsekId: data.layananPolsekId || "",
- });
- }
- } catch (error) {
- console.error("Error loading polsek terdekat:", error);
- toast.error("Gagal memuat data polsek terdekat");
- }
- };
-
- loadPolsekTerdekat();
- }, [params?.id]);
-
- const fetchLayanan = async () => {
- try {
- const res = await fetch("/api/keamanan/layanan-polsek/find-many");
- const data = await res.json();
-
- if (data.success) {
- const options = data.data.map((item: any) => ({
- value: item.id,
- label: item.nama,
- }));
- setLayananOptions(options);
- }
- } catch {
- toast.error("Gagal memuat layanan polsek");
- }
- };
-
- const handleTambahLayanan = async () => {
- if (!namaLayananBaru.trim())
- return toast.warn("Nama layanan tidak boleh kosong");
-
- try {
- const res = await fetch("/api/keamanan/layanan-polsek/create", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ nama: namaLayananBaru }),
- });
- const data = await res.json();
-
- if (data.success) {
- const newLayanan = {
- value: data.data.id,
- label: data.data.nama,
- };
- setLayananOptions((prev) => [...prev, newLayanan]);
- await fetchLayanan();
- polsekState.create.form.layananPolsekId = data.data.id;
- toast.success("Layanan baru ditambahkan!");
- setModalOpen(false);
- setNamaLayananBaru("");
- } else {
- toast.error(data.message || "Gagal menambah layanan");
- }
- } catch {
- toast.error("Error menambah layanan");
- }
- };
-
- const handleUpdateLayanan = async (id: string, namaBaru: string) => {
- if (!namaBaru.trim())
- return toast.warn("Nama layanan tidak boleh kosong");
-
- try {
- const res = await fetch(`/api/keamanan/layanan-polsek/update/${id}`, {
- method: "PUT",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ nama: namaBaru }),
- });
- const data = await res.json();
-
- if (data.success) {
- await fetchLayanan();
- toast.success("Layanan berhasil diupdate!");
- setModalUpdateOpen(false);
- setNamaLayananUpdate("");
- } else {
- toast.error(data.message || "Gagal mengupdate layanan");
- }
- } catch {
- toast.error("Error mengupdate layanan");
- }
- };
-
- const handleDeleteLayanan = async (id: string) => {
- const confirmDelete = confirm("Yakin ingin menghapus layanan ini?");
- if (!confirmDelete) return;
-
- try {
- const res = await fetch(`/api/keamanan/layanan-polsek/del/${id}`, {
- method: "DELETE",
- });
- const data = await res.json();
-
- if (data.success) {
- await fetchLayanan();
- setLayananOptions((prev) =>
- prev.filter((layanan) => layanan.value !== id)
- );
- toast.success("Layanan berhasil dihapus!");
- } else {
- toast.error(data.message || "Gagal menghapus layanan");
- }
- } catch {
- toast.error("Error menghapus layanan");
- }
- };
-
- useEffect(() => {
- fetchLayanan();
- }, []);
-
- const handleChange = (field: string, value: string) => {
- setFormData(prev => ({ ...prev, [field]: value }));
- };
-
- const handleResetForm = () => {
- setFormData({
- nama: originalData.nama,
- jarakKeDesa: originalData.jarakKeDesa,
- alamat: originalData.alamat,
- nomorTelepon: originalData.nomorTelepon,
- jamOperasional: originalData.jamOperasional,
- embedMapUrl: originalData.embedMapUrl,
- namaTempatMaps: originalData.namaTempatMaps,
- alamatMaps: originalData.alamatMaps,
- linkPetunjukArah: originalData.linkPetunjukArah,
- layananPolsekId: originalData.layananPolsekId,
- });
- toast.info("Form dikembalikan ke data awal");
- };
-
- const handleSubmit = async () => {
- try {
- setIsSubmitting(true);
- polsekState.edit.form = { ...formData }; // update global state hanya di sini
- await polsekState.edit.update();
- toast.success("Polsek terdekat berhasil diperbarui!");
- router.push("/admin/keamanan/polsek-terdekat");
- } catch (error) {
- console.error("Error updating polsek terdekat:", error);
- toast.error("Gagal memperbarui data polsek terdekat");
- } finally {
- setIsSubmitting(false);
- }
- };
-
- return (
-
- {/* Modal Tambah */}
- setModalOpen(false)}
- title="Tambah Layanan Polsek"
- centered
- >
-
- setNamaLayananBaru(e.currentTarget.value)}
- />
- Simpan
-
-
-
- {/* Modal Update */}
- setModalUpdateOpen(false)}
- title="Update Layanan Polsek"
- centered
- >
-
- setNamaLayananUpdate(e.currentTarget.value)}
- />
- {
- if (!selectedLayananId)
- return toast.warn("ID layanan tidak ditemukan");
- handleUpdateLayanan(selectedLayananId, namaLayananUpdate);
- }}
- >
- Simpan
-
-
-
-
- {/* Header */}
-
- router.back()}
- p="xs"
- radius="md"
- >
-
-
-
- Edit Polsek Terdekat
-
-
-
- {/* Form utama */}
-
-
- {/* Input fields */}
- handleChange("nama", e.currentTarget.value)}
- label="Nama Polsek Terdekat"
- placeholder="Masukkan nama Polsek Terdekat"
- required
- />
- handleChange("jarakKeDesa", e.currentTarget.value)}
- label="Jarak Polsek Terdekat"
- />
- handleChange("alamat", e.currentTarget.value)}
- label="Alamat Polsek Terdekat"
- />
- handleChange("nomorTelepon", e.currentTarget.value)}
- label="Nomor Telepon"
- />
- handleChange("jamOperasional", e.currentTarget.value)}
- label="Jam Operasional"
- />
- handleChange("embedMapUrl", e.currentTarget.value)}
- label="Embed Map URL"
- />
- handleChange("namaTempatMaps", e.currentTarget.value)}
- label="Nama Tempat Maps"
- />
- handleChange("alamatMaps", e.currentTarget.value)}
- label="Alamat Maps"
- />
- handleChange("linkPetunjukArah", e.currentTarget.value)}
- label="Link Petunjuk Arah"
- />
-
- handleChange("layananPolsekId", val || "")}
- />
- setModalOpen(true)}
- >
- + Tambah Layanan Baru
-
-
- {/* List layanan */}
-
- Daftar Layanan Polsek
-
- {layananOptions.map((item) => (
-
-
- {item.label}
-
- {
- setSelectedLayananId(item.value);
- setNamaLayananUpdate(item.label);
- setModalUpdateOpen(true);
- }}
- >
- Edit
-
- handleDeleteLayanan(item.value)}
- >
- Hapus
-
-
-
-
- ))}
-
- {/* Submit */}
-
- {/* Tombol Batal */}
-
- Batal
-
-
- {/* Tombol Simpan */}
-
- {isSubmitting ? : 'Simpan'}
-
-
-
-
-
- );
-}
-
-export default EditPolsekTerdekat;
diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/_com/layoutPolsek.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/_com/layoutPolsek.tsx
new file mode 100644
index 00000000..f415c771
--- /dev/null
+++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/_com/layoutPolsek.tsx
@@ -0,0 +1,150 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+'use client'
+import colors from '@/con/colors';
+import { Box, ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
+import { IconBuilding, IconTool } from '@tabler/icons-react';
+import { usePathname, useRouter } from 'next/navigation';
+import React, { useEffect, useState } from 'react';
+
+function LayoutPolsek({ children }: { children: React.ReactNode }) {
+ const router = useRouter();
+ const pathname = usePathname();
+
+ const tabs = [
+ {
+ label: "Daftar Polsek Terdekat",
+ value: "daftar-polsek-terdekat",
+ href: "/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat",
+ icon:
+ },
+ {
+ label: "Layanan Polsek",
+ value: "layanan-polsek",
+ href: "/admin/keamanan/polsek-terdekat/layanan-polsek",
+ icon:
+ }
+ ];
+
+ const currentTab = tabs.find(tab => tab.href === pathname);
+ const [activeTab, setActiveTab] = useState(currentTab?.value || tabs[0].value);
+
+ const handleTabChange = (value: string | null) => {
+ const tab = tabs.find(t => t.value === value);
+ if (tab) {
+ router.push(tab.href);
+ }
+ setActiveTab(value);
+ };
+
+ useEffect(() => {
+ const match = tabs.find(tab => tab.href === pathname);
+ if (match) {
+ setActiveTab(match.value);
+ }
+ }, [pathname]);
+
+ return (
+
+
+ Polsek Terdekat
+
+
+ {/* ✅ Scroll horizontal wrapper */}
+
+
+
+ {tabs.map((tab, i) => (
+
+ {tab.label}
+
+ ))}
+
+
+
+
+
+
+
+
+ {tabs.map((tab, i) => (
+
+ {tab.label}
+
+ ))}
+
+
+
+
+ {tabs.map((tab, i) => (
+
+ {children}
+
+ ))}
+
+
+ );
+}
+
+export default LayoutPolsek;
diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/daftar-polsek-terdekat/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/daftar-polsek-terdekat/[id]/edit/page.tsx
new file mode 100644
index 00000000..1c2b79d4
--- /dev/null
+++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/daftar-polsek-terdekat/[id]/edit/page.tsx
@@ -0,0 +1,279 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+"use client";
+
+import statePolsekTerdekat from "@/app/admin/(dashboard)/_state/keamanan/polsek-terdekat";
+import colors from "@/con/colors";
+import {
+ Box,
+ Button,
+ Group,
+ Loader,
+ MultiSelect,
+ Paper,
+ Stack,
+ TextInput,
+ Title
+} from "@mantine/core";
+import { IconArrowBack } from "@tabler/icons-react";
+import { useParams, useRouter } from "next/navigation";
+import { useEffect, useState } from "react";
+import { toast } from "react-toastify";
+import { useProxy } from "valtio/utils";
+
+
+type FormData = {
+ nama: string;
+ jarakKeDesa: string;
+ alamat: string;
+ nomorTelepon: string;
+ jamOperasional: string;
+ embedMapUrl: string;
+ namaTempatMaps: string;
+ alamatMaps: string;
+ linkPetunjukArah: string;
+ layananPolsekId: string[];
+};
+
+function EditPolsekTerdekat() {
+ const polsekState = useProxy(statePolsekTerdekat.polsekTerdekatState);
+ const params = useParams();
+ const router = useRouter();
+
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [formData, setFormData] = useState({
+ nama: "",
+ jarakKeDesa: "",
+ alamat: "",
+ nomorTelepon: "",
+ jamOperasional: "",
+ embedMapUrl: "",
+ namaTempatMaps: "",
+ alamatMaps: "",
+ linkPetunjukArah: "",
+ layananPolsekId: []
+ });
+
+ const [originalData, setOriginalData] = useState({
+ nama: "",
+ jarakKeDesa: "",
+ alamat: "",
+ nomorTelepon: "",
+ jamOperasional: "",
+ embedMapUrl: "",
+ namaTempatMaps: "",
+ alamatMaps: "",
+ linkPetunjukArah: "",
+ layananPolsekId: []
+ });
+
+ useEffect(() => {
+ statePolsekTerdekat.layananPolsek.findManyAll.load();
+ }, []);
+
+ // load data untuk form edit
+ useEffect(() => {
+ const loadPolsekTerdekat = async () => {
+ const id = params?.id as string;
+ if (!id) return;
+
+ try {
+ const data = await polsekState.edit.load(id);
+ if (data) {
+ setFormData({
+ nama: data.nama || "",
+ jarakKeDesa: data.jarakKeDesa || "",
+ alamat: data.alamat || "",
+ nomorTelepon: data.nomorTelepon || "",
+ jamOperasional: data.jamOperasional || "",
+ embedMapUrl: data.embedMapUrl || "",
+ namaTempatMaps: data.namaTempatMaps || "",
+ alamatMaps: data.alamatMaps || "",
+ linkPetunjukArah: data.linkPetunjukArah || "",
+ layananPolsekId: data.LayananToPolsek?.map((l: any) => l.layananId) || [],
+ });
+
+ setOriginalData({
+ nama: data.nama || "",
+ jarakKeDesa: data.jarakKeDesa || "",
+ alamat: data.alamat || "",
+ nomorTelepon: data.nomorTelepon || "",
+ jamOperasional: data.jamOperasional || "",
+ embedMapUrl: data.embedMapUrl || "",
+ namaTempatMaps: data.namaTempatMaps || "",
+ alamatMaps: data.alamatMaps || "",
+ linkPetunjukArah: data.linkPetunjukArah || "",
+ layananPolsekId: data.LayananToPolsek?.map((l: any) => l.layananId) || [],
+ });
+ }
+ } catch (error) {
+ console.error("Error loading polsek terdekat:", error);
+ toast.error("Gagal memuat data polsek terdekat");
+ }
+ };
+
+ loadPolsekTerdekat();
+ }, [params?.id]);
+
+
+ const handleChange = (key: keyof FormData, value: any) => {
+ setFormData((prev) => ({ ...prev, [key]: value }));
+ };
+
+ const handleResetForm = () => {
+ setFormData({
+ nama: originalData.nama,
+ jarakKeDesa: originalData.jarakKeDesa,
+ alamat: originalData.alamat,
+ nomorTelepon: originalData.nomorTelepon,
+ jamOperasional: originalData.jamOperasional,
+ embedMapUrl: originalData.embedMapUrl,
+ namaTempatMaps: originalData.namaTempatMaps,
+ alamatMaps: originalData.alamatMaps,
+ linkPetunjukArah: originalData.linkPetunjukArah,
+ layananPolsekId: (originalData as any)?.LayananToPolsek?.map((l: any) => l.layananId) || [],
+ });
+ toast.info("Form dikembalikan ke data awal");
+ };
+
+ const handleSubmit = async () => {
+ try {
+ setIsSubmitting(true);
+ await polsekState.edit.update();
+ toast.success("Polsek terdekat berhasil diperbarui!");
+ router.push("/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat");
+ } catch (error) {
+ console.error("Error updating polsek terdekat:", error);
+ toast.error("Gagal memperbarui data polsek terdekat");
+ } finally {
+ setIsSubmitting(false);
+ }
+};
+
+ return (
+
+ {/* Header */}
+
+ router.back()}
+ p="xs"
+ radius="md"
+ >
+
+
+
+ Edit Polsek Terdekat
+
+
+
+ {/* Form utama */}
+
+
+ {/* Input fields */}
+ handleChange("nama", e.currentTarget.value)}
+ label="Nama Polsek Terdekat"
+ placeholder="Masukkan nama Polsek Terdekat"
+ required
+ />
+ handleChange("jarakKeDesa", e.currentTarget.value)}
+ label="Jarak Polsek Terdekat"
+ />
+ handleChange("alamat", e.currentTarget.value)}
+ label="Alamat Polsek Terdekat"
+ />
+ handleChange("nomorTelepon", e.currentTarget.value)}
+ label="Nomor Telepon"
+ />
+ handleChange("jamOperasional", e.currentTarget.value)}
+ label="Jam Operasional"
+ />
+ handleChange("embedMapUrl", e.currentTarget.value)}
+ label="Embed Map URL"
+ />
+ handleChange("namaTempatMaps", e.currentTarget.value)}
+ label="Nama Tempat Maps"
+ />
+ handleChange("alamatMaps", e.currentTarget.value)}
+ label="Alamat Maps"
+ />
+ handleChange("linkPetunjukArah", e.currentTarget.value)}
+ label="Link Petunjuk Arah"
+ />
+
+ handleChange('layananPolsekId', val)}
+ data={
+ statePolsekTerdekat.layananPolsek.findManyAll.data?.map((v) => ({
+ value: v.id,
+ label: v.nama,
+ })) || []
+ }
+ clearable
+ searchable
+ required
+ error={!formData.layananPolsekId.length ? 'Pilih minimal satu layanan polsek' : undefined}
+ />
+
+ {/* Submit */}
+
+ {/* Tombol Batal */}
+
+ Batal
+
+
+ {/* Tombol Simpan */}
+
+ {isSubmitting ? : 'Simpan'}
+
+
+
+
+
+ );
+}
+
+export default EditPolsekTerdekat;
diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/daftar-polsek-terdekat/[id]/page.tsx
similarity index 87%
rename from src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/page.tsx
rename to src/app/admin/(dashboard)/keamanan/polsek-terdekat/daftar-polsek-terdekat/[id]/page.tsx
index 71f18cc2..5d8ac062 100644
--- a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/daftar-polsek-terdekat/[id]/page.tsx
@@ -6,12 +6,12 @@ import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
-import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
-import polsekTerdekat from '../../../_state/keamanan/polsek-terdekat';
+import { ModalKonfirmasiHapus } from '../../../../_com/modalKonfirmasiHapus';
+import statePolsekTerdekat from '../../../../_state/keamanan/polsek-terdekat';
function DetailPolsekTerdekat() {
const router = useRouter();
- const polsekState = useProxy(polsekTerdekat);
+ const polsekState = useProxy(statePolsekTerdekat.polsekTerdekatState);
const [selectedId, setSelectedId] = useState(null);
const [modalHapus, setModalHapus] = useState(false);
const params = useParams();
@@ -25,7 +25,7 @@ function DetailPolsekTerdekat() {
polsekState.delete.byId(selectedId);
setModalHapus(false);
setSelectedId(null);
- router.push("/admin/keamanan/polsek-terdekat");
+ router.push("/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat");
}
};
@@ -40,7 +40,7 @@ function DetailPolsekTerdekat() {
const data = polsekState.findUnique.data;
return (
-
+
{/* Tombol Back */}
- {/* Layanan Polsek */}
+
Layanan Polsek
- {data?.layananPolsek?.nama || "-"}
+
+ {data.LayananToPolsek && data.LayananToPolsek.length > 0 ? (
+ data.LayananToPolsek.map((layanan) => (
+
+ • {layanan.layanan.nama}
+
+ ))
+ ) : (
+ Tidak ada layanan polsek
+ )}
+
{/* Aksi */}
@@ -172,7 +182,7 @@ function DetailPolsekTerdekat() {
router.push(`/admin/keamanan/polsek-terdekat/${data.id}/edit`)}
+ onClick={() => router.push(`/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat/${data.id}/edit`)}
variant="light"
radius="md"
size="md"
diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/create/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/daftar-polsek-terdekat/create/page.tsx
similarity index 61%
rename from src/app/admin/(dashboard)/keamanan/polsek-terdekat/create/page.tsx
rename to src/app/admin/(dashboard)/keamanan/polsek-terdekat/daftar-polsek-terdekat/create/page.tsx
index e57bbd2e..b3b63080 100644
--- a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/create/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/daftar-polsek-terdekat/create/page.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import colors from '@/con/colors';
import {
@@ -6,9 +5,8 @@ import {
Button,
Group,
Loader,
- Modal,
+ MultiSelect,
Paper,
- Select,
Stack,
Text,
TextInput,
@@ -19,16 +17,17 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-import polsekTerdekat from '../../../_state/keamanan/polsek-terdekat';
+import statePolsekTerdekat from '../../../../_state/keamanan/polsek-terdekat';
function CreatePolsekTerdekat() {
- const polsekState = useProxy(polsekTerdekat);
+ const polsekState = useProxy(statePolsekTerdekat.polsekTerdekatState);
const router = useRouter();
- const [layananOptions, setLayananOptions] = useState<{ value: string; label: string }[]>([]);
- const [modalOpen, setModalOpen] = useState(false);
- const [namaLayananBaru, setNamaLayananBaru] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
+ useEffect(() => {
+ statePolsekTerdekat.layananPolsek.findManyAll.load();
+ }, []);
+
const resetForm = () => {
polsekState.create.form = {
nama: "",
@@ -40,44 +39,44 @@ function CreatePolsekTerdekat() {
namaTempatMaps: "",
alamatMaps: "",
linkPetunjukArah: "",
- layananPolsekId: "",
+ layananPolsekId: [],
};
};
const isValidGoogleMapsEmbed = (url: string): boolean => {
- try {
- const u = new URL(url);
- return (
- u.hostname === 'www.google.com' &&
- u.pathname === '/maps/embed' &&
- u.searchParams.has('pb')
- );
- } catch {
- return false;
- }
-};
+ try {
+ const u = new URL(url);
+ return (
+ u.hostname === 'www.google.com' &&
+ u.pathname === '/maps/embed' &&
+ u.searchParams.has('pb')
+ );
+ } catch {
+ return false;
+ }
+ };
const handleSubmit = async () => {
- const { embedMapUrl } = polsekState.create.form;
+ const { embedMapUrl } = polsekState.create.form;
- // ✅ Validasi Google Maps Embed URL (jika diisi)
- if (embedMapUrl && !isValidGoogleMapsEmbed(embedMapUrl)) {
- toast.error("URL embed peta tidak valid. Harap paste iframe dari Google Maps.");
- return;
- }
+ // ✅ Validasi Google Maps Embed URL (jika diisi)
+ if (embedMapUrl && !isValidGoogleMapsEmbed(embedMapUrl)) {
+ toast.error("URL embed peta tidak valid. Harap paste iframe dari Google Maps.");
+ return;
+ }
- try {
- setIsSubmitting(true);
- await polsekState.create.create();
- resetForm();
- router.push("/admin/keamanan/polsek-terdekat");
- } catch (error) {
- console.error(error);
- toast.error("Gagal menambah polsek terdekat");
- } finally {
- setIsSubmitting(false);
- }
-};
+ try {
+ setIsSubmitting(true);
+ await polsekState.create.create();
+ resetForm();
+ router.push("/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat");
+ } catch (error) {
+ console.error(error);
+ toast.error("Gagal menambah polsek terdekat");
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
const extractEmbedUrl = (input: string): string => {
// Jika sudah berupa URL embed yang valid
@@ -96,77 +95,8 @@ function CreatePolsekTerdekat() {
return input.trim();
};
- const fetchLayanan = async () => {
- try {
- const res = await fetch("/api/keamanan/layanan-polsek/find-many");
- const data = await res.json();
-
- if (data.success) {
- const options = data.data.map((item: any) => ({
- value: item.id,
- label: item.nama,
- }));
- setLayananOptions(options);
- }
- } catch {
- toast.error("Gagal memuat layanan polsek");
- }
- };
-
- const handleTambahLayanan = async () => {
- if (!namaLayananBaru.trim()) return toast.warn("Nama layanan tidak boleh kosong");
-
- try {
- const res = await fetch("/api/keamanan/layanan-polsek/create", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ nama: namaLayananBaru }),
- });
- const data = await res.json();
-
- if (data.success) {
- const newLayanan = {
- value: data.data.id,
- label: data.data.nama,
- };
- setLayananOptions((prev) => [...prev, newLayanan]);
- await fetchLayanan();
- polsekState.create.form.layananPolsekId = data.data.id;
- toast.success("Layanan baru ditambahkan!");
- setModalOpen(false);
- setNamaLayananBaru("");
- } else {
- toast.error(data.message || "Gagal menambah layanan");
- }
- } catch {
- toast.error("Error menambah layanan");
- }
- };
-
- useEffect(() => {
- fetchLayanan();
- }, []);
-
return (
-
- {/* Modal Tambah Layanan */}
- setModalOpen(false)}
- title="Tambah Layanan Polsek"
- centered
- >
-
- setNamaLayananBaru(e.currentTarget.value)}
- />
- Simpan
-
-
-
+
{/* Header */}
Link Petunjuk Arah}
placeholder="Masukkan link petunjuk arah"
/>
- {
- if (val) {
- const selected = layananOptions.find(
- (item) => item.value === val
- );
- if (selected) {
- polsekState.create.form.layananPolsekId = selected.value;
- }
- } else {
- polsekState.create.form.layananPolsekId = '';
- }
+ placeholder="Pilih layanan polsek (bisa lebih dari satu)"
+ data={statePolsekTerdekat.layananPolsek.findManyAll.data?.map((v) => ({
+ value: v.id,
+ label: v.nama,
+ })) || []}
+ value={polsekState.create.form.layananPolsekId}
+ onChange={(val) => {
+ polsekState.create.form.layananPolsekId = val;
}}
searchable
clearable
- nothingFoundMessage="Tidak ditemukan"
+ nothingFoundMessage="Tidak ada layanan ditemukan"
required
+ error={polsekState.create.form.layananPolsekId?.length === 0 ? "Pilih minimal 1 layanan polsek" : undefined}
/>
- setModalOpen(true)}
- >
- + Tambah Layanan Baru
-
-
{/* Tombol Submit */}
}
color="blue"
variant="light"
- onClick={() => router.push('/admin/keamanan/polsek-terdekat/create')}
+ onClick={() => router.push('/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat/create')}
>
Tambah Baru
@@ -145,7 +146,7 @@ function ListPolsekTerdekat({ search }: { search: string }) {
router.push(`/admin/keamanan/polsek-terdekat/${item.id}`)}
+ onClick={() => router.push(`/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat/${item.id}`)}
w="100%"
>
@@ -207,7 +208,7 @@ function ListPolsekTerdekat({ search }: { search: string }) {
variant="light"
color="blue"
fullWidth
- onClick={() => router.push(`/admin/keamanan/polsek-terdekat/${item.id}`)}
+ onClick={() => router.push(`/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat/${item.id}`)}
>
diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layanan-polsek/[id]/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layanan-polsek/[id]/page.tsx
new file mode 100644
index 00000000..04e64bd2
--- /dev/null
+++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layanan-polsek/[id]/page.tsx
@@ -0,0 +1,161 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+'use client'
+
+import statePolsekTerdekat from '@/app/admin/(dashboard)/_state/keamanan/polsek-terdekat';
+import colors from '@/con/colors';
+import {
+ Box,
+ Button,
+ Group,
+ Loader,
+ Paper,
+ Stack,
+ TextInput,
+ Title
+} from '@mantine/core';
+import { IconArrowBack } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useEffect, useState } from 'react';
+import { toast } from 'react-toastify';
+import { useProxy } from 'valtio/utils';
+
+function EditLayananPolsek() {
+ const editState = useProxy(statePolsekTerdekat.layananPolsek);
+ const router = useRouter();
+ const params = useParams();
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const [originalData, setOriginalData] = useState({
+ nama: '',
+ });
+
+ const [formData, setFormData] = useState({
+ nama: '',
+ });
+
+ useEffect(() => {
+ const loadLayananPolsek = async () => {
+ const id = params?.id as string;
+ if (!id) return;
+
+ try {
+ const data = await editState.update.load(id);
+ if (data) {
+ setFormData({
+ nama: data.nama || '',
+ });
+ setOriginalData({
+ nama: data.nama || '',
+ });
+ }
+ } catch (error) {
+ console.error('Error loading layanan polsek:', error);
+ toast.error('Gagal memuat data layanan polsek');
+ }
+ };
+
+ loadLayananPolsek();
+ }, [params?.id]);
+
+ const handleChange = (e: React.ChangeEvent) => {
+ setFormData((prev) => ({
+ ...prev,
+ [e.target.name]: e.target.value,
+ }));
+ };
+
+ const handleResetForm = () => {
+ setFormData({
+ nama: originalData.nama,
+ });
+ toast.info('Form dikembalikan ke data awal');
+ };
+
+ const handleSubmit = async () => {
+ try {
+ setIsSubmitting(true);
+ // update global state hanya saat submit
+ editState.update.form = {
+ ...editState.update.form,
+ nama: formData.nama,
+ };
+
+ await editState.update.update();
+ toast.success('Layanan Polsek berhasil diperbarui!');
+ router.push('/admin/keamanan/polsek-terdekat/layanan-polsek');
+ } catch (error) {
+ console.error('Error updating layanan polsek:', error);
+ toast.error('Terjadi kesalahan saat memperbarui layanan polsek');
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ return (
+
+ {/* Back Button + Title */}
+
+ router.back()}
+ p="xs"
+ radius="md"
+ >
+
+
+
+ Edit Layanan Polsek
+
+
+
+ {/* Form Wrapper */}
+
+
+
+
+
+
+ Batal
+
+
+ {/* Tombol Simpan */}
+
+ {isSubmitting ? : 'Simpan'}
+
+
+
+
+
+ );
+}
+
+export default EditLayananPolsek;
diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layanan-polsek/create/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layanan-polsek/create/page.tsx
new file mode 100644
index 00000000..378ba46e
--- /dev/null
+++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layanan-polsek/create/page.tsx
@@ -0,0 +1,111 @@
+'use client';
+import statePolsekTerdekat from '@/app/admin/(dashboard)/_state/keamanan/polsek-terdekat';
+import colors from '@/con/colors';
+import {
+ Box,
+ Button,
+ Group,
+ Loader,
+ Paper,
+ Stack,
+ TextInput,
+ Title
+} from '@mantine/core';
+import { IconArrowBack } from '@tabler/icons-react';
+import { useRouter } from 'next/navigation';
+import { useState } from 'react';
+import { toast } from 'react-toastify';
+import { useProxy } from 'valtio/utils';
+
+function CreateLayananPolsek() {
+ const createState = useProxy(statePolsekTerdekat.layananPolsek);
+ const router = useRouter();
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const resetForm = () => {
+ createState.create.form = {
+ nama: '',
+ };
+ };
+
+ const handleSubmit = async () => {
+ setIsSubmitting(true);
+ try {
+ await createState.create.create();
+ resetForm();
+ router.push('/admin/keamanan/polsek-terdekat/layanan-polsek');
+ } catch (error) {
+ console.error('Error creating layanan polsek:', error);
+ toast.error('Gagal menambahkan layanan polsek');
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ return (
+
+ {/* Header dengan back button */}
+
+ router.back()}
+ p="xs"
+ radius="md"
+ >
+
+
+
+ Tambah Layanan Polsek
+
+
+
+ {/* Form utama */}
+
+
+ (createState.create.form.nama = e.target.value)}
+ required
+ />
+
+
+
+ Reset
+
+
+ {/* Tombol Simpan */}
+
+ {isSubmitting ? : 'Simpan'}
+
+
+
+
+
+ );
+}
+
+export default CreateLayananPolsek;
diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layanan-polsek/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layanan-polsek/page.tsx
new file mode 100644
index 00000000..df663acb
--- /dev/null
+++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layanan-polsek/page.tsx
@@ -0,0 +1,265 @@
+'use client'
+import colors from '@/con/colors';
+import {
+ Box,
+ Button,
+ Center,
+ Group,
+ Pagination,
+ Paper,
+ Skeleton,
+ Stack,
+ Table,
+ TableTbody,
+ TableTd,
+ TableTh,
+ TableThead,
+ TableTr,
+ Text,
+ Title,
+} from '@mantine/core';
+import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
+import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
+import { useRouter } from 'next/navigation';
+import { useState } from 'react';
+import { useProxy } from 'valtio/utils';
+import HeaderSearch from '../../../_com/header';
+import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
+import statePolsekTerdekat from '../../../_state/keamanan/polsek-terdekat';
+
+
+function LayananPolsek() {
+ const [search, setSearch] = useState("");
+
+ return (
+
+ }
+ value={search}
+ onChange={(e) => setSearch(e.currentTarget.value)}
+ />
+
+
+
+ );
+}
+
+function ListLayananPolsek({ search }: { search: string }) {
+ const layananState = useProxy(statePolsekTerdekat.layananPolsek);
+ const [modalHapus, setModalHapus] = useState(false);
+ const [selectedId, setSelectedId] = useState(null);
+ const router = useRouter();
+ const [debouncedSearch] = useDebouncedValue(search, 1000);
+
+ const {
+ data,
+ page,
+ totalPages,
+ loading,
+ load,
+ } = layananState.findMany;
+
+ useShallowEffect(() => {
+ load(page, 10, debouncedSearch);
+ }, [page, debouncedSearch]);
+
+ const handleDelete = () => {
+ if (selectedId) {
+ layananState.delete.delete(selectedId);
+ setModalHapus(false);
+ setSelectedId(null);
+ load(page, 10, search);
+ }
+ };
+
+ const filteredData = data || [];
+
+ if (loading || !data) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+ Daftar Layanan Polsek
+
+ }
+ color="blue"
+ variant="light"
+ onClick={() => router.push('/admin/keamanan/polsek-terdekat/layanan-polsek/create')}
+ >
+ Tambah Baru
+
+
+
+ {/* Desktop Table */}
+
+
+
+
+
+
+ Nama Layanan Polsek
+
+
+
+ Edit
+
+
+ Hapus
+
+
+
+
+ {filteredData.length > 0 ? (
+ filteredData.map((item) => (
+
+
+
+ {item.nama}
+
+
+
+
+ router.push(
+ `/admin/keamanan/polsek-terdekat/layanan-polsek/${item.id}`
+ )
+ }
+ size="compact-sm"
+ >
+
+
+
+
+ {
+ setSelectedId(item.id);
+ setModalHapus(true);
+ }}
+ size="compact-sm"
+ >
+
+
+
+
+ ))
+ ) : (
+
+
+
+
+ Tidak ada data Polsek yang cocok
+
+
+
+
+ )}
+
+
+
+
+ {/* Mobile Cards */}
+
+
+ {filteredData.length > 0 ? (
+ filteredData.map((item) => (
+
+
+
+
+ Nama Layanan Polsek
+
+
+ {item.nama}
+
+
+
+
+ router.push(
+ `/admin/keamanan/polsek-terdekat/layanan-polsek/${item.id}`
+ )
+ }
+ >
+
+
+ {
+ setSelectedId(item.id);
+ setModalHapus(true);
+ }}
+ >
+
+
+
+
+
+ ))
+ ) : (
+
+
+ Tidak ada data Layanan Polsek yang cocok
+
+
+ )}
+
+
+
+
+ {/* Pagination */}
+
+ {
+ load(newPage, 10, search);
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ }}
+ total={totalPages}
+ mt={{ base: 'lg', md: 'xl' }}
+ mb={{ base: 'lg', md: 'xl' }}
+ color="blue"
+ radius="md"
+ />
+
+
+ {/* Modal Konfirmasi Hapus */}
+ setModalHapus(false)}
+ onConfirm={handleDelete}
+ text="Apakah anda yakin ingin menghapus layanan polsek ini?"
+ />
+
+ );
+}
+
+export default LayananPolsek;
\ No newline at end of file
diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layout.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layout.tsx
new file mode 100644
index 00000000..5792a343
--- /dev/null
+++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/layout.tsx
@@ -0,0 +1,34 @@
+'use client'
+import React from 'react';
+import LayoutPolsek from './_com/layoutPolsek';
+import { usePathname } from 'next/navigation';
+import { Box } from '@mantine/core';
+
+function Layout({ children }: { children: React.ReactNode }) {
+ const pathname = usePathname();
+
+ // Contoh path:
+ // - /darmasaba/desa/berita/semua → panjang 5 → list
+ // - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
+ // - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
+
+ const segments = pathname.split('/').filter(Boolean);
+ const isDetailPage = segments.length >= 5;
+
+ if (isDetailPage) {
+ // Tampilkan tanpa tab menu
+ return (
+
+ {children}
+
+ );
+ }
+
+ return (
+
+ {children}
+
+ );
+}
+
+export default Layout;
diff --git a/src/app/admin/_com/list_PageAdmin.tsx b/src/app/admin/_com/list_PageAdmin.tsx
index bd810e00..132fbbab 100644
--- a/src/app/admin/_com/list_PageAdmin.tsx
+++ b/src/app/admin/_com/list_PageAdmin.tsx
@@ -14,11 +14,6 @@ export const devBar = [
name: "Desa Anti Korupsi",
path: "/admin/landing-page/desa-anti-korupsi/list-desa-anti-korupsi"
},
- // {
- // id: "Landing_Page_3",
- // name: "Indeks Kepuasan Masyarakat",
- // path: "/admin/landing-page/indeks-kepuasan-masyarakat/grafik-kepuasan-masyarakat"
- // },
{
id: "Landing_Page_3",
name: "SDGs",
@@ -182,7 +177,7 @@ export const devBar = [
{
id: "Keamanan_2",
name: "Polsek Terdekat",
- path: "/admin/keamanan/polsek-terdekat"
+ path: "/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat"
},
{
id: "Keamanan_3",
@@ -418,11 +413,6 @@ export const navBar = [
name: "Desa Anti Korupsi",
path: "/admin/landing-page/desa-anti-korupsi/list-desa-anti-korupsi"
},
- // {
- // id: "Landing_Page_3",
- // name: "Indeks Kepuasan Masyarakat",
- // path: "/admin/landing-page/indeks-kepuasan-masyarakat/grafik-kepuasan-masyarakat"
- // },
{
id: "Landing_Page_3",
name: "SDGs",
@@ -586,7 +576,7 @@ export const navBar = [
{
id: "Keamanan_2",
name: "Polsek Terdekat",
- path: "/admin/keamanan/polsek-terdekat"
+ path: "/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat"
},
{
id: "Keamanan_3",
@@ -822,11 +812,6 @@ export const role1 = [
name: "Desa Anti Korupsi",
path: "/admin/landing-page/desa-anti-korupsi/list-desa-anti-korupsi"
},
- // {
- // id: "Landing_Page_3",
- // name: "Indeks Kepuasan Masyarakat",
- // path: "/admin/landing-page/indeks-kepuasan-masyarakat/grafik-kepuasan-masyarakat"
- // },
{
id: "Landing_Page_3",
name: "SDGs",
@@ -948,7 +933,7 @@ export const role1 = [
{
id: "Keamanan_2",
name: "Polsek Terdekat",
- path: "/admin/keamanan/polsek-terdekat"
+ path: "/admin/keamanan/polsek-terdekat/daftar-polsek-terdekat"
},
{
id: "Keamanan_3",
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/create.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/create.ts
index 42d01da5..62ad27b1 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/create.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/create.ts
@@ -9,6 +9,7 @@ type FormCreate = {
rating: number;
kategoriId: string[];
kontak: string;
+ deskripsi: string;
// Array of KategoriProduk IDs
};
@@ -31,7 +32,8 @@ export default async function pasarDesaCreate(context: Context) {
imageId: body.imageId,
rating: Number(body.rating),
kategoriProdukId: body.kategoriId[0],
- kontak: body.kontak
+ kontak: body.kontak,
+ deskripsi: body.deskripsi,
// Use the first category as the main one
},
});
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/index.ts
index 6731abb2..5d310992 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/index.ts
@@ -38,6 +38,7 @@ const PasarDesa = new Elysia({
rating: t.Number(),
kategoriId: t.Array(t.String()),
kontak: t.String(),
+ deskripsi: t.String(),
}),
}
)
@@ -81,6 +82,7 @@ const PasarDesa = new Elysia({
rating: t.Number(),
kategoriId: t.Array(t.String()),
kontak: t.String(),
+ deskripsi: t.String(),
}),
}
);
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/updt.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/updt.ts
index 7acac5cd..98d80704 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/updt.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/updt.ts
@@ -10,6 +10,7 @@ type FormUpdate = {
rating: number;
kategoriId: string[]; // Array of KategoriProduk IDs
kontak: string;
+ deskripsi: string;
};
export default async function pasarDesaUpdate(context: Context) {
@@ -32,7 +33,8 @@ export default async function pasarDesaUpdate(context: Context) {
alamatUsaha: body.alamatUsaha,
imageId: body.imageId,
rating: Number(body.rating),
- kontak: body.kontak
+ kontak: body.kontak,
+ deskripsi: body.deskripsi
},
});
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/index.ts b/src/app/api/[[...slugs]]/_lib/keamanan/index.ts
index c0ce3d65..723134bb 100644
--- a/src/app/api/[[...slugs]]/_lib/keamanan/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/index.ts
@@ -4,9 +4,10 @@ import PolsekTerdekat from "./polsek-terdekat";
import PencegahanKriminalitas from "./pencegahan-kriminalitas";
import MenuTipsKeamanan from "./tips-keamanan";
import LaporanPublik from "./laporan-publik";
-import LayananPolsek from "./layanan-polsek";
+
import KontakDaruratKeamanan from "./kontak-darurat-keamanan";
import KontakItem from "./kontak-darurat-keamanan/kontak-item";
+import LayananPolsek from "./polsek-terdekat/layanan-polsek";
const Keamanan = new Elysia({ prefix: "/api/keamanan", tags: ["Keamanan"] })
.use(KeamananLingkungan)
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/create.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/create.ts
deleted file mode 100644
index 452dd869..00000000
--- a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/create.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import prisma from "@/lib/prisma";
-import { Context } from "elysia";
-
-export default async function layananPolsekCreate(context: Context) {
- const body = context.body as { nama: string };
-
- if (!body.nama) {
- return {
- success: false,
- message: "Nama is required",
- };
- }
-
- const layanan = await prisma.layananPolsek.create({
- data: {
- nama: body.nama,
- deletedAt: null, // pastikan ini ditambahkan kalau field-mu optional
- },
- });
-
- return {
- success: true,
- message: "Sukses menambahkan layanan polsek",
- data: layanan,
- };
-}
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/del.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/del.ts
deleted file mode 100644
index 63669de5..00000000
--- a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/del.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import prisma from "@/lib/prisma";
-import { Context } from "elysia";
-
-export const layananPolsekDelete = async (context: Context) => {
- const id = context.params.id;
- if (!id) {
- return {
- success: false,
- message: "ID is required",
- }
- }
-
- const layanan = await prisma.layananPolsek.delete({
- where: {
- id: id,
- },
- })
-
- if(!layanan) {
- return {
- success: false,
- message: "Layanan polsek tidak ditemukan",
- }
- }
-
- return {
- success: true,
- message: "Sukses Menghapus layanan polsek",
- data: layanan,
- }
-}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findMany.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findMany.ts
deleted file mode 100644
index a5028d6e..00000000
--- a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findMany.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-import prisma from "@/lib/prisma";
-
- async function layananPolsekFindMany() {
- const data = await prisma.layananPolsek.findMany();
- return {
- success: true,
- data: data.map((item: any) => {
- return {
- id: item.id,
- nama: item.nama,
- }
- }),
- };
-}
-
-export default layananPolsekFindMany
-
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findUnique.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findUnique.ts
deleted file mode 100644
index 343e2aae..00000000
--- a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findUnique.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import prisma from "@/lib/prisma";
-
-export default async function layananPolsekFindUnique(request: Request){
- const url = new URL(request.url);
- const pathSegments = url.pathname.split('/');
- const id = pathSegments[pathSegments.length - 1];
-
- if(!id){
- return Response.json({
- success: false,
- message: "ID tidak boleh kosong",
- }, { status: 400 });
- }
-
- try {
- if (typeof id !== 'string') {
- return Response.json({
- success: false,
- message: "ID tidak valid",
- }, { status: 400 });
- }
-
- const data = await prisma.layananPolsek.findUnique({
- where: { id },
- });
-
- if (!data) {
- return Response.json({
- success: false,
- message: "Layanan polsek tidak ditemukan",
- }, { status: 404 });
- }
-
- return Response.json({
- success: true,
- message: "Success fetch layanan polsek by ID",
- data,
- }, { status: 200 });
- } catch (e) {
- console.error("Find by ID error:", e);
- return Response.json({
- success: false,
- message: "Gagal mengambil layanan polsek: " + (e instanceof Error ? e.message : 'Unknown error'),
- }, {
- status: 500,
- });
- }
-}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/updt.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/updt.ts
deleted file mode 100644
index b6eab8d0..00000000
--- a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/updt.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import prisma from "@/lib/prisma";
-import { Context } from "elysia";
-
-
-export default async function layananPolsekUpdate(context: Context) {
- const body = await context.request.json()
-
- if (!body.nama) {
- return {
- success: false,
- message: "Nama is required",
- }
- }
-
- const layanan = await prisma.layananPolsek.update({
- where: {
- id: body.id,
- },
- data: {
- nama: body.nama,
- },
- })
-
- return {
- success: true,
- message: "Success update layanan polsek",
- data: layanan,
- }
-}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/create.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/create.ts
index 71047362..e1b412fd 100644
--- a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/create.ts
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/create.ts
@@ -1,44 +1,84 @@
import prisma from "@/lib/prisma";
-import { Prisma } from "@prisma/client";
import { Context } from "elysia";
-type FormCreate = Prisma.PolsekTerdekatGetPayload<{
- select: {
- nama: true;
- jarakKeDesa: true;
- alamat: true;
- nomorTelepon: true;
- jamOperasional: true;
- embedMapUrl: true;
- namaTempatMaps: true;
- alamatMaps: true;
- linkPetunjukArah: true;
- layananPolsekId: true;
- };
-}>;
+type FormCreate = {
+ nama: string;
+ jarakKeDesa: string;
+ alamat: string;
+ nomorTelepon: string;
+ jamOperasional: string;
+ embedMapUrl: string;
+ namaTempatMaps: string;
+ alamatMaps: string;
+ linkPetunjukArah: string;
+ layananPolsekId: string[];
+};
+
async function polsekTerdekatCreate(context: Context) {
const body = context.body as FormCreate;
- await prisma.polsekTerdekat.create({
+ // Validate that layananPolsekId is provided and not empty
+ if (!body.layananPolsekId || body.layananPolsekId.length === 0) {
+ return {
+ success: false,
+ message: "Pilih minimal 1 layanan polsek",
+ };
+ }
+
+ try {
+ // Start a transaction to ensure data consistency
+ const result = await prisma.$transaction(async (prisma) => {
+ // 1. Create PasarDesa with the first kategoriId as the main category
+ const polsekTerdekat = await prisma.polsekTerdekat.create({
data: {
- nama: body.nama,
- jarakKeDesa: body.jarakKeDesa,
- alamat: body.alamat,
- nomorTelepon: body.nomorTelepon,
- jamOperasional: body.jamOperasional,
- embedMapUrl: body.embedMapUrl,
- namaTempatMaps: body.namaTempatMaps,
- alamatMaps: body.alamatMaps,
- linkPetunjukArah: body.linkPetunjukArah,
- layananPolsekId: body.layananPolsekId,
+ nama: body.nama,
+ jarakKeDesa: body.jarakKeDesa,
+ alamat: body.alamat,
+ nomorTelepon: body.nomorTelepon,
+ jamOperasional: body.jamOperasional,
+ embedMapUrl: body.embedMapUrl,
+ namaTempatMaps: body.namaTempatMaps,
+ alamatMaps: body.alamatMaps,
+ linkPetunjukArah: body.linkPetunjukArah,
+ layananPolsekId: body.layananPolsekId[0],
+ // Use the first category as the main one
},
+ });
+
+ // 2. Create category relationships in KategoriToPasar for all categories
+ await prisma.layananToPolsek.createMany({
+ data: body.layananPolsekId.map((layananId) => ({
+ polsekTerdekatId: polsekTerdekat.id,
+ layananId: layananId,
+ })),
+ });
+
+ // 3. Get the complete data with relationships
+ return await prisma.polsekTerdekat.findUnique({
+ where: { id: polsekTerdekat.id },
+ include: {
+ layananPolsek: true,
+ LayananToPolsek: {
+ include: {
+ layanan: true
+ },
+ },
+ },
+ });
});
+
return {
- success: true,
- message: "Sukses menambahkan polsek terdekat",
- data: {
- ...body,
- },
+ success: true,
+ message: "Sukses menambahkan pasar desa",
+ data: result,
};
+ } catch (error) {
+ console.error("Error creating polsek terdekat:", error);
+ return {
+ success: false,
+ message: "Gagal menambahkan polsek terdekat"
+ };
+ }
}
+
export default polsekTerdekatCreate;
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts
index 42fc6c92..73febce0 100644
--- a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts
@@ -2,38 +2,28 @@ import prisma from "@/lib/prisma";
import { Context } from "elysia";
const polsekTerdekatDelete = async (context: Context) => {
- const id = context.params?.id as string;
+ const { params } = context;
+ const id = params?.id as string;
- if (!id) {
- return {
- status: 400,
- body: "ID tidak diberikan",
- };
- }
+ if (!id) {
+ throw new Error("ID tidak ditemukan dalam parameter");
+ }
- const polsekTerdekat = await prisma.polsekTerdekat.findUnique({
- where: { id },
- include: {
- layananPolsek: true,
- }
- });
+ // 1. Hapus relasi dari pivot
+ await prisma.layananToPolsek.deleteMany({
+ where: { polsekTerdekatId: id },
+ });
- if (!polsekTerdekat) {
- return {
- status: 404,
- body: "Polsek terdekat tidak ditemukan",
- };
- }
+ // 2. Hapus pasar desa utama
+ const deleted = await prisma.polsekTerdekat.delete({
+ where: { id },
+ });
- await prisma.polsekTerdekat.delete({
- where: { id },
- });
-
- return {
- success: true,
- status: 200,
- message: "Polsek terdekat berhasil dihapus",
- };
+ return {
+ success: true,
+ message: "Berhasil menghapus polsek terdekat",
+ data: deleted,
+ };
};
-export default polsekTerdekatDelete;
\ No newline at end of file
+export default polsekTerdekatDelete;
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findMany.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findMany.ts
index 36fed3ed..9de415bb 100644
--- a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findMany.ts
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findMany.ts
@@ -28,6 +28,11 @@ async function polsekTerdekatFindMany(context: Context) {
where,
include: {
layananPolsek: true,
+ LayananToPolsek: {
+ include: {
+ layanan: true,
+ }
+ }
},
skip,
take: limit,
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findUnique.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findUnique.ts
index bdf6b92b..90d63000 100644
--- a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findUnique.ts
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findUnique.ts
@@ -1,51 +1,32 @@
import prisma from "@/lib/prisma";
+import { Context } from "elysia";
-export default async function polsekTerdekatFindUnique(request: Request){
- const url = new URL(request.url);
- const pathSegments = url.pathname.split('/');
- const id = pathSegments[pathSegments.length - 1];
+export default async function polsekTerdekatFindUnique(context: Context) {
+ const { params } = context;
+ const id = params?.id as string;
- if(!id){
- return Response.json({
- success: false,
- message: "ID tidak boleh kosong",
- }, { status: 400 });
- }
+ if (!id) {
+ throw new Error("ID tidak ditemukan dalam parameter");
+ }
- try {
- if (typeof id !== 'string') {
- return Response.json({
- success: false,
- message: "ID tidak valid",
- }, { status: 400 });
- }
+ const data = await prisma.polsekTerdekat.findUnique({
+ where: { id },
+ include: {
+ LayananToPolsek: {
+ include: {
+ layanan: true
+ },
+ },
+ },
+ });
- const data = await prisma.polsekTerdekat.findUnique({
- where: { id },
- include: {
- layananPolsek: true,
- },
- });
+ if (!data) {
+ throw new Error("Polsek terdekat tidak ditemukan");
+ }
- if (!data) {
- return Response.json({
- success: false,
- message: "Polsek terdekat tidak ditemukan",
- }, { status: 404 });
- }
-
- return Response.json({
- success: true,
- message: "Success fetch polsek terdekat by ID",
- data,
- }, { status: 200 });
- } catch (e) {
- console.error("Find by ID error:", e);
- return Response.json({
- success: false,
- message: "Gagal mengambil polsek terdekat: " + (e instanceof Error ? e.message : 'Unknown error'),
- }, {
- status: 500,
- });
- }
-}
\ No newline at end of file
+ return {
+ success: true,
+ message: "Data polsek terdekat ditemukan",
+ data,
+ };
+}
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts
index f87a7baa..f2602f1f 100644
--- a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts
@@ -6,13 +6,18 @@ import polsekTerdekatFindUnique from "./findUnique";
import polsekTerdekatUpdate from "./updt";
import polsekTerdekatFindFirst from "./findFirst";
-
const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/Polsek Terdekat"] })
.get("/find-many", polsekTerdekatFindMany)
-.get("/:id", async (context) => {
- const response = await polsekTerdekatFindUnique(new Request(context.request));
- return response;
-})
+.get("/:id",
+ async (context) => {
+ return await polsekTerdekatFindUnique(context);
+ },
+ {
+ params: t.Object({
+ id: t.String(),
+ }),
+ }
+ )
.get("/find-first", polsekTerdekatFindFirst)
.post("/create", polsekTerdekatCreate, {
body: t.Object({
@@ -25,7 +30,7 @@ const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/
namaTempatMaps: t.String(),
alamatMaps: t.String(),
linkPetunjukArah: t.String(),
- layananPolsekId: t.String(),
+ layananPolsekId: t.Array(t.String()),
}),
})
.delete("/del/:id", polsekTerdekatDelete)
@@ -44,7 +49,7 @@ const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/
namaTempatMaps: t.String(),
alamatMaps: t.String(),
linkPetunjukArah: t.String(),
- layananPolsekId: t.String(),
+ layananPolsekId: t.Array(t.String()),
}),
})
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/create.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/create.ts
new file mode 100644
index 00000000..6a7b03ef
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/create.ts
@@ -0,0 +1,26 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+type FormCreate = {
+ nama: string;
+}
+
+export default async function layananPolsekCreate(context: Context) {
+ const body = (await context.body) as FormCreate;
+
+ try {
+ const result = await prisma.layananPolsek.create({
+ data: {
+ nama: body.nama,
+ },
+ });
+ return {
+ success: true,
+ message: "Berhasil membuat kategori layanan polsek",
+ data: result,
+ };
+ } catch (error) {
+ console.error("Error creating kategori layanan polsek:", error);
+ throw new Error("Gagal membuat kategori layanan polsek: " + (error as Error).message);
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/del.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/del.ts
new file mode 100644
index 00000000..7e974888
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/del.ts
@@ -0,0 +1,16 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+export default async function layananPolsekDelete(context: Context) {
+ const id = context.params.id as string;
+
+ await prisma.layananPolsek.delete({
+ where: { id },
+ });
+
+ return {
+ status: 200,
+ success: true,
+ message: "Sukses Menghapus kategori layanan polsek",
+ };
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/findMany.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/findMany.ts
new file mode 100644
index 00000000..9c8354e3
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/findMany.ts
@@ -0,0 +1,53 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+// /api/berita/findManyPaginated.ts
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+async function layananPolsekFindMany(context: Context) {
+ // Ambil parameter dari query
+ const page = Number(context.query.page) || 1;
+ const limit = Number(context.query.limit) || 10;
+ const search = (context.query.search as string) || '';
+ const skip = (page - 1) * limit;
+
+ // Buat where clause
+ const where: any = { isActive: true };
+
+ // Tambahkan pencarian (jika ada)
+ if (search) {
+ where.OR = [
+ { nama: { contains: search, mode: 'insensitive' } },
+ ];
+ }
+
+ try {
+ // Ambil data dan total count secara paralel
+ const [data, total] = await Promise.all([
+ prisma.layananPolsek.findMany({
+ where,
+ skip,
+ take: limit,
+ orderBy: { createdAt: 'desc' },
+ }),
+ prisma.layananPolsek.count({ where }),
+ ]);
+
+ return {
+ success: true,
+ message: "Berhasil ambil layanan polsek dengan pagination",
+ data,
+ page,
+ limit,
+ total,
+ totalPages: Math.ceil(total / limit),
+ };
+ } catch (e) {
+ console.error("Error di findMany paginated:", e);
+ return {
+ success: false,
+ message: "Gagal mengambil data layanan polsek",
+ };
+ }
+}
+
+export default layananPolsekFindMany;
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/findManyAll.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/findManyAll.ts
new file mode 100644
index 00000000..adfcb9d8
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/findManyAll.ts
@@ -0,0 +1,41 @@
+import prisma from "@/lib/prisma";
+
+async function layananPolsekFindManyAll() {
+ try {
+ const data = await prisma.layananPolsek.findMany({
+ where: {
+ isActive: true,
+ },
+ select: {
+ id: true,
+ nama: true,
+ createdAt: true,
+ updatedAt: true,
+ deletedAt: true,
+ },
+ orderBy: {
+ createdAt: "asc",
+ },
+ });
+
+ return {
+ success: true,
+ message: "Berhasil ambil data layanan polsek",
+ data: data.map(item => ({
+ ...item,
+ // Ensure all required fields are present and properly typed
+ createdAt: new Date(item.createdAt),
+ updatedAt: new Date(item.updatedAt),
+ deletedAt: item.deletedAt ? new Date(item.deletedAt) : null,
+ })),
+ };
+ } catch (error) {
+ console.error("Error layananPolsek.findMany:", error);
+ return {
+ success: false,
+ message: "Gagal mengambil data layanan polsek",
+ };
+ }
+}
+
+export default layananPolsekFindManyAll;
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/findUnique.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/findUnique.ts
new file mode 100644
index 00000000..2ee5f1c3
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/findUnique.ts
@@ -0,0 +1,46 @@
+import prisma from "@/lib/prisma";
+
+export default async function layananPolsekFindUnique(request: Request) {
+ const url = new URL(request.url);
+ const pathSegments = url.pathname.split('/');
+ const id = pathSegments[pathSegments.length - 1];
+
+ if (!id) {
+ return {
+ success: false,
+ message: "ID is required",
+ }
+ }
+
+ try {
+ if (typeof id !== 'string') {
+ return {
+ success: false,
+ message: "ID is required",
+ }
+ }
+
+ const data = await prisma.layananPolsek.findUnique({
+ where: { id },
+ });
+
+ if (!data) {
+ return {
+ success: false,
+ message: "Data not found",
+ }
+ }
+
+ return {
+ success: true,
+ message: "Success get layanan polsek",
+ data,
+ }
+ } catch (error) {
+ console.error("Find by ID error:", error);
+ return {
+ success: false,
+ message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'),
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/index.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/index.ts
similarity index 56%
rename from src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/index.ts
rename to src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/index.ts
index efbd270c..081dacb2 100644
--- a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/index.ts
@@ -1,32 +1,34 @@
-import Elysia from "elysia";
+import Elysia, { t } from "elysia";
import layananPolsekCreate from "./create";
-import layananPolsekFindMany from "./findMany";
-import { t } from "elysia";
-import layananPolsekUpdate from "./updt";
+import layananPolsekDelete from "./del";
+import layananPolsekFindManyAll from "./findManyAll";
import layananPolsekFindUnique from "./findUnique";
-import { layananPolsekDelete } from "./del";
+import layananPolsekUpdate from "./updt";
+import layananPolsekFindMany from "./findMany";
const LayananPolsek = new Elysia({
- prefix: "/layanan-polsek",
- tags: ["Keamanan/Polsek Terdekat/Layanan Polsek"],
+ prefix: "/layananpolsek",
+ tags: ["layanan polsek"],
})
- .get("/find-many", layananPolsekFindMany)
+
+ .post("/create", layananPolsekCreate, {
+ body: t.Object({
+ nama: t.String(),
+ }),
+ })
+.get("/findMany", layananPolsekFindMany)
+ .get("/findManyAll", layananPolsekFindManyAll)
.get("/:id", async (context) => {
const response = await layananPolsekFindUnique(
new Request(context.request)
);
return response;
})
- .delete("/del/:id", layananPolsekDelete)
- .post("/create", layananPolsekCreate, {
- body: t.Object({
- nama: t.String(),
- }),
- })
.put("/:id", layananPolsekUpdate, {
body: t.Object({
nama: t.String(),
}),
- });
+ })
+ .delete("/del/:id", layananPolsekDelete);
-export default LayananPolsek;
+export default LayananPolsek;
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/updt.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/updt.ts
new file mode 100644
index 00000000..8e67f7fe
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/layanan-polsek/updt.ts
@@ -0,0 +1,28 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+type FormUpdate = {
+ nama: string;
+}
+
+export default async function layananPolsekUpdate(context: Context) {
+ const body = (await context.body) as FormUpdate;
+ const id = context.params.id as string;
+
+ try {
+ const result = await prisma.layananPolsek.update({
+ where: { id },
+ data: {
+ nama: body.nama,
+ },
+ });
+ return {
+ success: true,
+ message: "Berhasil mengupdate layanan polsek",
+ data: result,
+ };
+ } catch (error) {
+ console.error("Error updating layanan polsek:", error);
+ throw new Error("Gagal mengupdate layanan polsek: " + (error as Error).message);
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/updt.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/updt.ts
index b629cd2a..164b2c2a 100644
--- a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/updt.ts
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/updt.ts
@@ -1,94 +1,89 @@
import prisma from "@/lib/prisma";
-import { Prisma } from "@prisma/client";
import { Context } from "elysia";
-type FormUpdate = Prisma.PolsekTerdekatGetPayload<{
- select: {
- nama: true;
- jarakKeDesa: true;
- alamat: true;
- nomorTelepon: true;
- jamOperasional: true;
- embedMapUrl: true;
- namaTempatMaps: true;
- alamatMaps: true;
- linkPetunjukArah: true;
- layananPolsekId: true;
- };
-}>;
+type FormUpdate = {
+ nama: string;
+ jarakKeDesa: string;
+ alamat: string;
+ nomorTelepon: string;
+ jamOperasional: string;
+ embedMapUrl: string;
+ namaTempatMaps: string;
+ alamatMaps: string;
+ linkPetunjukArah: string;
+ layananPolsekId: string[];
+};
+
export default async function polsekTerdekatUpdate(context: Context) {
+ const id = context.params?.id;
+ const body = context.body as FormUpdate;
+
+ if (!id) {
+ return {
+ success: false,
+ message: "ID tidak diberikan",
+ };
+ }
+
+ // Validate that layananPolsekId is provided and not empty
+ if (!body.layananPolsekId || body.layananPolsekId.length === 0) {
+ return {
+ success: false,
+ message: "Pilih minimal 1 layanan polsek",
+ };
+ }
+
+ // Since the relationship is one-to-many, we'll use the first ID
+ const layananPolsekId = body.layananPolsekId[0];
+
try {
- const id = context.params?.id;
- const body = (await context.body) as Omit;
-
- const { nama, jarakKeDesa, alamat, nomorTelepon, jamOperasional, embedMapUrl, namaTempatMaps, alamatMaps, linkPetunjukArah, layananPolsekId } = body;
-
- if (!id) {
- return new Response(JSON.stringify({
- success: false,
- message: "ID tidak diberikan",
- }), {
- status: 400,
- headers: {
- "Content-Type": "application/json",
- },
- });
- }
+ // First check if the record exists
const existing = await prisma.polsekTerdekat.findUnique({
where: { id },
- include: {
- layananPolsek: true,
- }
});
if (!existing) {
- return new Response(JSON.stringify({
+ return {
success: false,
message: "Polsek terdekat tidak ditemukan",
- }), {
- status: 404,
- headers: {
- "Content-Type": "application/json",
- },
- });
+ };
}
+ // Update the PolsekTerdekat record with the relationship
const updated = await prisma.polsekTerdekat.update({
where: { id },
data: {
- nama,
- jarakKeDesa,
- alamat,
- nomorTelepon,
- jamOperasional,
- embedMapUrl,
- namaTempatMaps,
- alamatMaps,
- linkPetunjukArah,
- layananPolsekId,
+ nama: body.nama,
+ jarakKeDesa: body.jarakKeDesa,
+ alamat: body.alamat,
+ nomorTelepon: body.nomorTelepon,
+ jamOperasional: body.jamOperasional,
+ embedMapUrl: body.embedMapUrl,
+ namaTempatMaps: body.namaTempatMaps,
+ alamatMaps: body.alamatMaps,
+ linkPetunjukArah: body.linkPetunjukArah,
+ layananPolsek: {
+ connect: { id: layananPolsekId }
+ }
},
+ include: {
+ layananPolsek: true
+ }
});
- return new Response(JSON.stringify({
+ return {
success: true,
- message: "Success update polsek terdekat",
- data: updated,
- }), {
- status: 200,
- headers: {
- "Content-Type": "application/json",
+ message: "Data polsek terdekat berhasil diperbarui",
+ data: {
+ ...updated,
+ layananPolsekId: [layananPolsekId] // Keep it as array for consistency
},
- });
+ };
} catch (error) {
console.error("Error updating polsek terdekat:", error);
- return new Response(JSON.stringify({
+ return {
success: false,
- message: "Terjadi kesalahan saat mengupdate polsek terdekat",
- }), {
- status: 500,
- headers: {
- "Content-Type": "application/json",
- },
- });
+ message: "Gagal memperbarui data polsek terdekat"
+ };
}
}
\ No newline at end of file
diff --git a/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx b/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx
index 732d9db0..c63d34be 100644
--- a/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx
@@ -83,7 +83,9 @@ function Page() {
-
+
+
+
{v.posisi}
@@ -95,16 +97,29 @@ function Page() {
-
-
-
+
+
+
+
+
+
{v.lokasi}
+
-
+
+
+
Full Time
diff --git a/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/page.tsx b/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/page.tsx
index 9b25fc65..b9a8a997 100644
--- a/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/page.tsx
@@ -1,16 +1,16 @@
'use client'
/* eslint-disable react-hooks/exhaustive-deps */
-import polsekTerdekatState from '@/app/admin/(dashboard)/_state/keamanan/polsek-terdekat';
+import statePolsekTerdekat from '@/app/admin/(dashboard)/_state/keamanan/polsek-terdekat';
import colors from '@/con/colors';
import { Badge, Box, Button, Center, Flex, Paper, SimpleGrid, Skeleton, Stack, Text, Title } from '@mantine/core';
import { IconArrowDown, IconClock, IconNavigation, IconPhone, IconPin } from '@tabler/icons-react';
+import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
-import { useRouter } from 'next/navigation';
function Page() {
- const state = useProxy(polsekTerdekatState.findFirst);
+ const state = useProxy(statePolsekTerdekat.polsekTerdekatState.findFirst);
const router = useRouter();
const { data, loading, load } = state;
diff --git a/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/semua-polsek/page.tsx b/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/semua-polsek/page.tsx
index 9d8f758e..56e3cda8 100644
--- a/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/semua-polsek/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/semua-polsek/page.tsx
@@ -1,17 +1,16 @@
'use client'
-import polsekTerdekatState from '@/app/admin/(dashboard)/_state/keamanan/polsek-terdekat';
+import statePolsekTerdekat from '@/app/admin/(dashboard)/_state/keamanan/polsek-terdekat';
import colors from '@/con/colors';
import { Box, Button, Center, Grid, GridCol, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
-import { useShallowEffect } from '@mantine/hooks';
+import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconNavigation, IconSearch } from '@tabler/icons-react';
-import React, { useState } from 'react';
+import { useRouter } from 'next/navigation';
+import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../../desa/layanan/_com/BackButto';
-import { useRouter } from 'next/navigation';
-import { useDebouncedValue } from '@mantine/hooks';
function Page() {
- const state = useProxy(polsekTerdekatState);
+ const state = useProxy(statePolsekTerdekat.polsekTerdekatState);
const [search, setSearch] = useState('');
const [debouncedSearch] = useDebouncedValue(search, 1000);
const router = useRouter();