Compare commits
99 Commits
nico/17-se
...
nico/12-ja
| Author | SHA1 | Date | |
|---|---|---|---|
| 903dc74cca | |||
| 503da91ce6 | |||
| daaed8089b | |||
| f436aa2ef0 | |||
| 50bc54ceca | |||
| f0f201c853 | |||
| 29065cb3e2 | |||
| bf20cd55e8 | |||
| af60bcd6fc | |||
| dc8793e3ae | |||
| c8484357cb | |||
| 342e9bbc65 | |||
| f6f77d9e35 | |||
| a00481152c | |||
| 242ea86f77 | |||
| 99c2c9c6d7 | |||
| ac2fc1a705 | |||
| 9dbe172165 | |||
| cc318d4d54 | |||
| dcb8017594 | |||
| ec3ad12531 | |||
| dad44c0537 | |||
| 867dce42f0 | |||
| 7bb17ddf22 | |||
| a4069d3cba | |||
| ffe5e6dd9f | |||
| dcf195f54f | |||
| c03a6b3aed | |||
| 1bb9f239db | |||
| a213ff7d37 | |||
| 0018bdc251 | |||
| 83fb39a957 | |||
| 7238692dd0 | |||
| 8b50139d79 | |||
| 066180fc0e | |||
| 67f29aabef | |||
| dbf7c34228 | |||
| 036fc86fed | |||
| 2cecec733e | |||
| c64a2e5457 | |||
| 757911d7dd | |||
| 54232e4465 | |||
| 29a9a59bca | |||
| 2fb3666e57 | |||
| e30b27f7a4 | |||
| e941ed3893 | |||
| ace5aff1b6 | |||
| 716db0adca | |||
| a291bdfb51 | |||
| 0dff8f3254 | |||
| 78b8aa74cd | |||
| a0537810e8 | |||
| b3c169a2d4 | |||
| 2608a5ffdd | |||
| 6c32f3ebdb | |||
| 0feeb4de93 | |||
| 9622eb5a9a | |||
| 417a8937f5 | |||
| db8909b9ed | |||
| f66a46f645 | |||
| fb57698dc9 | |||
| d128313e71 | |||
| 7b4bb1e58e | |||
| 0befe6a3f2 | |||
| a6663bbcee | |||
| ed371bd0d9 | |||
| f82c7b86e0 | |||
| b5d6585cd5 | |||
| aa98359ef7 | |||
| 0ff0d5234a | |||
| 827c1c191a | |||
| fb596f9033 | |||
| 9055b40769 | |||
| bbf13c1cf7 | |||
| 75bf0652b1 | |||
| 0b574406e2 | |||
| ccf39bc778 | |||
| 3c21f7742c | |||
| a158241c0b | |||
| 80c5dc6361 | |||
| 8ad38fc907 | |||
| d601b2fee3 | |||
| cee0957e07 | |||
| 5c66eccf23 | |||
| f7fd9be255 | |||
| 8a6d8ed8db | |||
| 63054cedf0 | |||
| c2f1ab8179 | |||
| 295d6f7d63 | |||
| dbd56a1493 | |||
| 2a26db6e17 | |||
| 33fc472472 | |||
| d8fa56d923 | |||
| cac146471a | |||
| 3e4a7a1c0a | |||
| b5c044df6e | |||
| 0fc47c28ff | |||
| 8e25c91e85 | |||
| 068d8b1077 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -41,6 +41,9 @@ next-env.d.ts
|
||||
# uploads
|
||||
/uploads
|
||||
|
||||
# download
|
||||
/download
|
||||
|
||||
# cache
|
||||
/cache
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
experimental: {},
|
||||
allowedDevOrigins: [
|
||||
"http://192.168.1.82:3000", // buat akses dari HP/device lain
|
||||
"http://localhost:3000", // akses lokal
|
||||
],
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
|
||||
23
package.json
23
package.json
@@ -3,9 +3,9 @@
|
||||
"version": "0.1.5",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "bun --bun next dev",
|
||||
"build": "bun --bun next build",
|
||||
"start": "bun --bun next start"
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "bun run prisma/seed.ts"
|
||||
@@ -19,6 +19,7 @@
|
||||
"@elysiajs/static": "^1.3.0",
|
||||
"@elysiajs/stream": "^1.1.0",
|
||||
"@elysiajs/swagger": "^1.2.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@mantine/carousel": "^7.16.2",
|
||||
"@mantine/charts": "^7.17.1",
|
||||
"@mantine/core": "^7.17.4",
|
||||
@@ -26,6 +27,7 @@
|
||||
"@mantine/dropzone": "^8.1.1",
|
||||
"@mantine/form": "^8.1.0",
|
||||
"@mantine/hooks": "^7.17.4",
|
||||
"@mantine/modals": "^8.3.6",
|
||||
"@mantine/tiptap": "^7.17.4",
|
||||
"@paljs/types": "^8.1.0",
|
||||
"@prisma/client": "^6.3.1",
|
||||
@@ -39,19 +41,27 @@
|
||||
"@tiptap/pm": "^2.11.7",
|
||||
"@tiptap/react": "^2.11.7",
|
||||
"@tiptap/starter-kit": "^2.11.7",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/bun": "^1.2.2",
|
||||
"@types/leaflet": "^1.9.20",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/nodemailer": "^7.0.2",
|
||||
"add": "^2.0.6",
|
||||
"adm-zip": "^0.5.16",
|
||||
"animate.css": "^4.1.1",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"bun": "^1.2.2",
|
||||
"chart.js": "^4.4.8",
|
||||
"classnames": "^2.5.1",
|
||||
"colors": "^1.4.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"dotenv": "^17.2.3",
|
||||
"elysia": "^1.3.5",
|
||||
"embla-carousel-autoplay": "^8.5.2",
|
||||
"embla-carousel-react": "^7.1.0",
|
||||
"embla-carousel": "^8.6.0",
|
||||
"embla-carousel-autoplay": "^8.6.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"extract-zip": "^2.0.1",
|
||||
"form-data": "^4.0.2",
|
||||
"framer-motion": "^12.23.5",
|
||||
"get-port": "^7.1.0",
|
||||
@@ -67,17 +77,20 @@
|
||||
"next": "^15.5.2",
|
||||
"next-view-transitions": "^0.3.4",
|
||||
"node-fetch": "^3.3.2",
|
||||
"nodemailer": "^7.0.10",
|
||||
"p-limit": "^6.2.0",
|
||||
"primeicons": "^7.0.0",
|
||||
"primereact": "^10.9.6",
|
||||
"prisma": "^6.3.1",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-exif-orientation-img": "^0.1.5",
|
||||
"react-international-phone": "^4.6.0",
|
||||
"react-leaflet": "^5.0.0",
|
||||
"react-simple-toasts": "^6.1.0",
|
||||
"react-toastify": "^11.0.5",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"react-zoom-pan-pinch": "^3.7.0",
|
||||
"readdirp": "^4.1.1",
|
||||
"recharts": "^2.15.3",
|
||||
"sharp": "^0.34.3",
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'postcss-preset-mantine': {},
|
||||
'postcss-simple-vars': {
|
||||
variables: {
|
||||
'mantine-breakpoint-xs': '36em',
|
||||
'mantine-breakpoint-sm': '48em',
|
||||
'mantine-breakpoint-md': '62em',
|
||||
'mantine-breakpoint-lg': '75em',
|
||||
'mantine-breakpoint-xl': '88em',
|
||||
},
|
||||
plugins: {
|
||||
'postcss-preset-mantine': {},
|
||||
'postcss-simple-vars': {
|
||||
variables: {
|
||||
/* Mobile first */
|
||||
'mantine-breakpoint-xs': '30em', // 480px → mobile kecil–normal
|
||||
'mantine-breakpoint-sm': '48em', // 768px → tablet / mobile landscape
|
||||
'mantine-breakpoint-md': '64em', // 1024px → laptop & desktop kecil
|
||||
'mantine-breakpoint-lg': '80em', // 1280px → desktop standar
|
||||
'mantine-breakpoint-xl': '90em', // 1440px+ → desktop besar
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
[
|
||||
{ "name": "Sosial & Kesehatan" },
|
||||
{ "name": "Ekonomi & UMKM" },
|
||||
{ "name": "Pendidikan & Kepemudaan" },
|
||||
{ "name": "Lingkungan & Bencana" },
|
||||
{ "name": "Adat & Budaya" },
|
||||
{ "name": "Digitalisasi Desa" }
|
||||
]
|
||||
146
prisma/data/desa/berita/berita.json
Normal file
146
prisma/data/desa/berita/berita.json
Normal file
@@ -0,0 +1,146 @@
|
||||
[
|
||||
{
|
||||
"id": "cmk6ae8rz00003b6r06x7hsqi",
|
||||
"judul": "TP. Posyandu Bali Gelar Aksi Sosial ‘Membina dan Berbagi’ di Desa Darmasaba",
|
||||
"deskripsi": "<p>Kegiatan pembinaan dan bantuan kepada kader Posyandu Desa Darmasaba oleh TP Posyandu Provinsi Bali.</p>",
|
||||
"content": "<p>Sebanyak 50 kader posyandu mendapatkan pembinaan dan sembako sebagai dukungan terhadap peran Posyandu dalam pemberdayaan masyarakat. Kegiatan ini menunjukkan peran strategis posyandu dalam layanan publik desa.</p>",
|
||||
"kategoriBeritaId": "cmk69tghy000vvnv8xeouenv5",
|
||||
"imageId": "cmk6bk2va0003vnadn9w0mxc4"
|
||||
},
|
||||
{
|
||||
"id": "cmk6af7vf00013b6rj2br4nv8",
|
||||
"judul": "Desa Darmasaba Gelar Temu Sadar Hukum, Bahas Isu KDRT",
|
||||
"deskripsi": "<p>Temu Sadar Hukum untuk meningkatkan kesadaran hukum warga Desa Darmasaba.</p>",
|
||||
"content": "<p>Kegiatan ini membahas isu kekerasan dalam rumah tangga dengan narasumber dari Kanwil Kemenkum Bali, menjadi forum penting dalam membangun masyarakat yang melek hukum.</p>",
|
||||
"kategoriBeritaId": "cmk69tghy000vvnv8xeouenv5",
|
||||
"imageId": "cmk6bo3290006vnadzhgalacy"
|
||||
},
|
||||
{
|
||||
"id": "cmk6afo0g00033b6rjc2pae67",
|
||||
"judul": "Bicara Darmasaba Bahas Berbagai Persoalan, Dorong Warga Sampaikan Aspirasi",
|
||||
"deskripsi": "<p>Ruang dialog terbuka di Desa Darmasaba untuk membahas persoalan sampah dan partisipasi publik.</p>",
|
||||
"content": "<p>Forum dialog ini membantu pemerintahan desa menyusun kebijakan tepat sasaran, terutama terkait permasalahan lingkungan dan aspirasi warga.</p>",
|
||||
"kategoriBeritaId": "cmk69tghy000vvnv8xeouenv5",
|
||||
"imageId": "cmk6bptid0009vnad6my1w4s1"
|
||||
},
|
||||
{
|
||||
"id": "cmk6ag56y00053b6rj9481z6m",
|
||||
"judul": "Bicara Darmasaba Bahas ‘Sampah Kita, Tanggung Jawab Siapa?’",
|
||||
"deskripsi": "<p>Diskusi terbuka antara pemerintah desa dan warga mengenai isu sampah.</p>",
|
||||
"content": "<p>Acara ini mendukung perumusan kebijakan desa yang sesuai kebutuhan warga dan meningkatkan partisipasi dalam pembangunan lingkungan.</p>",
|
||||
"kategoriBeritaId": "cmk69tghx000tvnv8g2d206wv",
|
||||
"imageId": "cmk6c86dc000cvnadlh54q7xr"
|
||||
},
|
||||
{
|
||||
"id": "cmk6agzxx00073b6rr3vhxcsj",
|
||||
"judul": "Penutupan KKN-PMM Periode II Universitas Warmadewa di Desa Darmasaba",
|
||||
"deskripsi": "<p>Penutupan program KKN-PMM yang berjalan dengan berbagai kegiatan pemberdayaan masyarakat.</p>",
|
||||
"content": "<p>Kegiatan KKN meliputi edukasi kesehatan, pengelolaan sampah, literasi keuangan, dan upaya ekonomi lokal sebagai bagian dari pembangunan desa berkelanjutan.</p>",
|
||||
"kategoriBeritaId": "cmk69tghx000tvnv8g2d206wv",
|
||||
"imageId": "cmk6cbbtw000fvnadk862le38"
|
||||
},
|
||||
{
|
||||
"id": "cmk6agzxx00073b6rr3vhxasj",
|
||||
"judul": "Desa Darmasaba Siap Kelola Sampah Mandiri, Anggarkan Rp1,5 Miliar",
|
||||
"deskripsi": "<p>Desa Darmasaba mengalokasikan anggaran untuk pengelolaan sampah berbasis komunitas.</p>",
|
||||
"content": "<p>Pengelolaan sampah mandiri melalui TPS3R, kader penyuluh, dan inovasi CINtA menjadi strategi desa dalam penanganan sampah sesuai kebijakan provinsi dan desa.</p>",
|
||||
"kategoriBeritaId": "cmk69tghx000tvnv8g2d206wv",
|
||||
"imageId": "cmk6cdmjp000ivnadeejerm59"
|
||||
},
|
||||
{
|
||||
"id": "cmk6agzxx00073b6rr3vhxbsj",
|
||||
"judul": "Sekda Adi Arnawa Buka Darmasaba Village Festival II",
|
||||
"deskripsi": "<p>Pembukaan festival desa untuk mendorong UMKM dan ekonomi lokal.</p>",
|
||||
"content": "<p>Kegiatan ini menampilkan berbagai UMKM yang membantu meningkatkan pendapatan masyarakat setempat.</p>",
|
||||
"kategoriBeritaId": "cmk69tghx000uvnv847ppcxqh",
|
||||
"imageId": "cmk6crib4000lvnadqtrhnb14"
|
||||
},
|
||||
{
|
||||
"id": "cmk6agzxx00073b6rr3vhxdsj",
|
||||
"judul": "Membangun Desa Berkelanjutan Melalui Ekowisata dan Kuliner di Darmasaba",
|
||||
"deskripsi": "<p>Program inovatif untuk memperkuat ekonomi lokal melalui ekowisata dan kuliner.</p>",
|
||||
"content": "<p>Kegiatan mencakup pembangunan green house, edukasi pemasaran digital, literasi bahasa Inggris, dan pengembangan potensi kuliner desa.</p>",
|
||||
"kategoriBeritaId": "cmk69tghx000uvnv847ppcxqh",
|
||||
"imageId": "cmk6czfag000ovnad7kjz36lj"
|
||||
},
|
||||
{
|
||||
"id": "cmk6agzxx00073b6rr3vhxesj",
|
||||
"judul": "Inovasi Desa Darmasaba Lanjutkan Perjuangan ke Tingkat Nasional",
|
||||
"deskripsi": "<p>Desa Darmasaba meraih penghargaan juara dalam evaluasi perkembangan desa untuk mendukung perekonomian dan pemerintahan lokal.</p>",
|
||||
"content": "<p>Prestasi desa ditandai dengan keberhasilan dalam lomba evaluasi perkembangan desa di tingkat provinsi dan kabupaten, yang berdampak positif pada ekonomi desa.</p>",
|
||||
"kategoriBeritaId": "cmk69tghx000uvnv847ppcxqh",
|
||||
"imageId": "cmk6d3zow000rvnades3btqrh"
|
||||
},
|
||||
{
|
||||
"id": "cmk6agzxx00073b6rr3vhxfsj",
|
||||
"judul": "Desa Darmasaba Kembali Ukir Prestasi Internasional BAJRA",
|
||||
"deskripsi": "<p>Prestasi desa dalam forum internasional mengenai penanggulangan rabies.</p>",
|
||||
"content": "<p>Partisipasi dalam konferensi Rabies in Borneo menunjukkan kolaborasi lintas sektor dan penggunaan data dalam pelayanan publik desa.</p>",
|
||||
"kategoriBeritaId": "cmk69tght000svnv8ok5rid2v",
|
||||
"imageId": "cmk6hgl8r000uvnadwchcqigp"
|
||||
},
|
||||
{
|
||||
"id": "cmk6aih8f00093b6rqw63yp1z",
|
||||
"judul": "Cegah Penyebaran Rabies, Darmasaba Keluarkan Larangan Membuang Hewan",
|
||||
"deskripsi": "<p>Pemasangan spanduk larangan buang hewan sebagai langkah proteksi kesehatan masyarakat.</p>",
|
||||
"content": "<p>Pemerintah desa bersama Tim Bajra aktif dalam kampanye dan regulasi untuk mencegah rabies di tingkat desa.</p>",
|
||||
"kategoriBeritaId": "cmk69tght000svnv8ok5rid2v",
|
||||
"imageId": "cmk6hh34d000xvnadr74cs014"
|
||||
},
|
||||
{
|
||||
"id": "cmk6aih8f00093b6rqw63yp2z",
|
||||
"judul": "TP. Posyandu Bali dan Pemerintah Desa Kolaborasi Tingkatkan Pelayanan",
|
||||
"deskripsi": "<p>Kolaborasi pemerintahan desa dengan TP Posyandu untuk meningkatkan layanan masyarakat.</p>",
|
||||
"content": "<p>Kegiatan ini menunjukkan peran pemerintahan desa dalam mendukung layanan kesehatan dan pemberdayaan masyarakat.</p>",
|
||||
"kategoriBeritaId": "cmk69tght000svnv8ok5rid2v",
|
||||
"imageId": "cmk6hhl5p0010vnadx09hrg3n"
|
||||
},
|
||||
{
|
||||
"id": "cmk6aih8f00093b6rqw63yp3z",
|
||||
"judul": "Membangun Desa Berkelanjutan Melalui Inovasi Pengelolaan Sampah",
|
||||
"deskripsi": "<p>Inisiatif pengelolaan sampah desa sebagai bagian dari inovasi teknologi lokal.</p>",
|
||||
"content": "<p>Penerapan metode pengelolaan sampah dan biopori menunjukkan upaya Desa Darmasaba dalam menggunakan solusi teknologi sederhana untuk masalah lingkungan.</p>",
|
||||
"kategoriBeritaId": "cmk69tghz000xvnv8kxzzt24h",
|
||||
"imageId": "cmk6hz5640013vnadwdmfzyoa"
|
||||
},
|
||||
{
|
||||
"id": "cmk6aih8f00093b6rqw63yp4z",
|
||||
"judul": "Inovasi BAJRA Integrasikan Pelaporan Cepat Berbasis Data",
|
||||
"deskripsi": "<p>Program BAJRA menerapkan mekanisme pelaporan cepat berbasis data untuk penanggulangan rabies.</p>",
|
||||
"content": "<p>Penggunaan teknologi informasi dalam pelaporan kasus rabies membantu respons cepat pemerintahan desa dan komunitas.</p>",
|
||||
"kategoriBeritaId": "cmk69tghz000xvnv8kxzzt24h",
|
||||
"imageId": "cmk6hzlrg0016vnaduf57rp2i"
|
||||
},
|
||||
{
|
||||
"id": "cmk6aih8f00093b6rqw63yp5z",
|
||||
"judul": "Digitalisasi Desa Darmasaba",
|
||||
"deskripsi": "<p>Digitalisasi Desa Darmasaba Bersama PT. Bali Interaktif Perkasa.</p>",
|
||||
"content": "<p>Digitalisasi Desa Darmasaba Bersama PT. Bali Interaktif Perkasa<br><br>Dalam rangka mendukung transformasi digital dan inovasi desa, Desa Darmasaba bekerja sama dengan PT. Bali Interaktif Perkasa melaksanakan kegiatan Digitalisasi Desa.<br><br>Program ini bertujuan untuk memperkuat kapasitas desa dalam pemanfaatan teknologi informasi dan komunikasi, sehingga pelayanan publik menjadi lebih efektif, transparan, dan cepat. Masyarakat juga diberikan pemahaman terkait pemanfaatan platform digital untuk kegiatan administrasi, komunikasi, dan pengembangan potensi desa.<br><br>Kegiatan digitalisasi ini menjadi bagian dari komitmen Desa Darmasaba untuk mewujudkan desa cerdas (smart village) yang mampu bersaing dan beradaptasi di era digital, sekaligus meningkatkan inovasi dan pemberdayaan masyarakat.<br><br>Dengan kolaborasi ini, Desa Darmasaba menegaskan tekadnya untuk terus berinovasi, menghadirkan kemudahan bagi masyarakat, dan memperkuat tata kelola desa berbasis teknologi modern.<br><br>? Digitalisasi hari ini, kemajuan desa esok!<br><br>#DesaDarmasaba #DigitalisasiDesa #DesaCerdas #InovasiDesa #TransformasiDigital<br>#PemdesDarmasaba<br>#PerbekelDarmasaba<br>#DesaDarmasaba<br> #KitaDarmasaba<br> #DarmasabaBisa<br> <br> @kostergubernurbali<br> @giri.prasta<br> @iwayanadiarnawa<br> @gus.bota<br> @puturasniathiadiarnawa<br> @yunita_oktarini<br> @surya.suamba<br> @budhi.argawakba<br> @pemkabbadung<br> @ppidbadung<br> @dinaspmddukcapilprovbali<br> @surya_prabhawa<br> @kecamatanabiansemal<br> @dpmdbadungkab<br> @pemprov_bali<br> @prokompimbadung<br> @seputar_darmasaba</p>",
|
||||
"kategoriBeritaId": "cmk69tghz000xvnv8kxzzt24h",
|
||||
"imageId": "cmk6hzz1i0019vnadwdymqlrf"
|
||||
},
|
||||
{
|
||||
"id": "cmk6aih8f00093b6rqw63yp6z",
|
||||
"judul": "Festival Desa Tingkatkan Kreativitas Digital UMKM",
|
||||
"deskripsi": "<p>Festival Darmasaba Village Festival II melibatkan promosi digital produk UMKM.</p>",
|
||||
"content": "<p>Promosi dan dokumentasi digital menjadi bagian dari strategi pemasaran UMKM dalam festival desa.</p>",
|
||||
"kategoriBeritaId": "cmk69tghy000wvnv8umg2vloa",
|
||||
"imageId": "cmk6i925x001cvnadm3y2m04b"
|
||||
},
|
||||
{
|
||||
"id": "cmk6aih8f00093b6rqw63yp7z",
|
||||
"judul": "Sekda Adi Arnawa dan Pementasan Seni Tradisional di Festival Darmasaba",
|
||||
"deskripsi": "<p>Pementasan seni tradisional menjadi bagian dari Darmasaba Village Festival II.</p>",
|
||||
"content": "<p>Kegiatan ini mengangkat warisan budaya lokal melalui pertunjukan dan lomba di festival desa.</p>",
|
||||
"kategoriBeritaId": "cmk69tghy000wvnv8umg2vloa",
|
||||
"imageId": "cmk6i9cc7001fvnadn9xnl8xq"
|
||||
},
|
||||
{
|
||||
"id": "cmk6aih8f00093b6rqw63yp8z",
|
||||
"judul": "Dialog Publik Tingkatkan Partisipasi Budaya Lokal",
|
||||
"deskripsi": "<p>Forum dialog desa mengangkat tema partisipasi masyarakat dalam kegiatan budaya lokal.</p>",
|
||||
"content": "<p>Diskusi ini memperkuat peran budaya dalam pembangunan desa melalui keterlibatan warga dalam kegiatan adat dan sosial.</p>",
|
||||
"kategoriBeritaId": "cmk69tghy000wvnv8umg2vloa",
|
||||
"imageId": "cmk6i9qff001ivnadgrrghebr"
|
||||
}
|
||||
]
|
||||
8
prisma/data/desa/berita/kategori-berita.json
Normal file
8
prisma/data/desa/berita/kategori-berita.json
Normal file
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{ "id": "cmk69tght000svnv8ok5rid2v", "name": "Pemerintahan" },
|
||||
{ "id": "cmk69tghx000tvnv8g2d206wv", "name": "Pembangunan" },
|
||||
{ "id": "cmk69tghx000uvnv847ppcxqh", "name": "Ekonomi" },
|
||||
{ "id": "cmk69tghy000vvnv8xeouenv5", "name": "Sosial" },
|
||||
{ "id": "cmk69tghy000wvnv8umg2vloa", "name": "Budaya" },
|
||||
{ "id": "cmk69tghz000xvnv8kxzzt24h", "name": "Teknologi" }
|
||||
]
|
||||
20
prisma/data/desa/gallery/video/video.json
Normal file
20
prisma/data/desa/gallery/video/video.json
Normal file
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"id": "cmk6kvn6b0000vn6qzg5z6qa6",
|
||||
"judul": "TAHAP PENILAIAN VERIFIKASI LAPANGAN AJANG MANGUPURA AWARD TAHUN 2025",
|
||||
"deskripsi": "<p>TAHAP PENILAIAN VERIFIKASI LAPANGAN AJANG MANGUPURA AWARD Senin, 29 September 2025 – Pemerintah Desa Darmasaba mengikuti tahap penilaian verifikasi lapangan dalam rangkaian Ajang Mangupura Award Tahun 2025 pada kategori Pemerintah Desa. Ajang bergengsi ini merupakan bentuk apresiasi Pemerintah Kabupaten Badung kepada desa-desa yang berprestasi dalam tata kelola aset, keuangan, arsip, tata kelola sumber daya manusia, pelayanan publik dan persampahan, inovasi, sinergitas, dan akuntabilitas. Proses verifikasi lapangan diawali di Kantor Perbekel Darmasaba dengan pemeriksaan langsung kepada masing-masing pengampu indikator, kemudian dilanjutkan dengan kunjungan ke BUMDes Pudak Mesari serta TPS 3R Pudak Mesari sebagai bentuk evaluasi nyata terhadap kinerja dan program desa. Kegiatan ini menjadi langkah penting dalam menilai implementasi tata kelola pemerintahan desa yang transparan, inovatif, dan berkelanjutan. Melalui tahapan ini, Pemerintah Desa Darmasaba berharap dapat terus menghadirkan pelayanan terbaik bagi masyarakat, mengembangkan inovasi desa, serta memperkuat sinergi antara pemerintah, desa adat, dan masyarakat dalam mewujudkan pembangunan daerah yang maju, berdaya saing, dan berkelanjutan. #MangupuraAward2025 #Darmasaba #DesaBerprestasi #PemerintahDesa #Badung #Abiansemal #PemdesDarmasaba #PerbekelDarmasaba #DesaDarmasaba #KitaDarmasaba #DarmasabaBisa</p>",
|
||||
"linkVideo": "https://www.youtube.com/watch?v=e2tSRnNkYDE"
|
||||
},
|
||||
{
|
||||
"id": "cmk6kvn6b0000vn6qzg5z6qb7",
|
||||
"judul": "Vaksinasi Rabies di Desa Darmasaba",
|
||||
"deskripsi": "<p>Vaksinasi Rabies di Desa Darmasaba Selasa, 7 Oktober 2025 Pemerintah Desa Darmasaba melalui Tim Bajra Desa Darmasaba, bekerja sama dengan Dinas Pertanian dan Pangan Kabupaten Badung Bidang Kesehatan Hewan, menyelenggarakan kegiatan Vaksinasi Rabies bagi hewan penular rabies (HPR) yang meliputi anjing, kucing, dan kera di wilayah Desa Darmasaba. Kegiatan ini dilaksanakan sebagai langkah preventif untuk menekan penyebaran virus rabies sekaligus memberikan perlindungan kesehatan bagi hewan peliharaan maupun masyarakat. Melalui program vaksinasi ini, diharapkan Desa Darmasaba dapat terbebas dari ancaman rabies dan semakin meningkatkan kesadaran masyarakat akan pentingnya menjaga kesehatan hewan peliharaan. Pemerintah Desa Darmasaba mengimbau seluruh warga untuk memastikan hewan kesayangan mendapatkan vaksinasi sesuai jadwal yang telah ditentukan. Informasi lengkap mengenai jadwal vaksinasi dapat dilihat pada pengumuman yang tertera. Selain itu, demi kelancaran proses vaksinasi, diharapkan pemilik hewan dapat mengikat atau mengandangkan hewan peliharaannya saat proses vaksinasi berlangsung. Dengan adanya kolaborasi antara pemerintah desa, dinas terkait, dan partisipasi aktif masyarakat, kegiatan ini diharapkan mampu memberikan manfaat nyata serta menciptakan lingkungan Desa Darmasaba yang lebih sehat, aman, dan terbebas dari rabies. #VaksinasiRabies #KesehatanHewan #BebasRabies #Badung #Abiansemal #TimBajraDarmasaba #PemdesDarmasaba #PerbekelDarmasaba #DesaDarmasaba #KitaDarmasaba #DarmasabaBisa</p>",
|
||||
"linkVideo": "https://www.youtube.com/watch?v=bc99y94FBx8"
|
||||
},
|
||||
{
|
||||
"id": "cmk6kvn6b0000vn6qzg5z6qc8",
|
||||
"judul": "Musyawarah Perencanaan Pembangunan Desa Darmasaba Tahun 2026",
|
||||
"deskripsi":"<p>Musyawarah Perencanaan Pembangunan Desa Darmasaba Tahun 2026 Pemerintah Desa Darmasaba menyelenggarakan Musyawarah Perencanaan Pembangunan Desa (Musrenbangdes)dalam rangka penyusunan Rencana Kerja Pemerintah Desa (RKP Desa) Tahun 2026. Musrenbangdes ini merupakan forum resmi yang mempertemukan perwakilan dari DPMD Kab. Badung, Kecamatan Abiansemal, Pemerintah Desa, Lembaga Desa, Tokoh Masyarakat, serta perwakilan unsur lainnya untuk bersama-sama merumuskan arah pembangunan desa di tahun mendatang. Melalui kegiatan ini, seluruh peserta diberikan ruang untuk menyampaikan usulan, gagasan, serta masukan yang berkaitan dengan prioritas pembangunan, baik di bidang infrastruktur, pemberdayaan masyarakat, ekonomi, sosial, maupun pelestarian adat dan budaya. Proses musyawarah ini menjadi bagian penting dalam mewujudkan pembangunan desa yang partisipatif, transparan, dan berorientasi pada kebutuhan masyarakat. Dengan terselenggaranya Musrenbangdes ini, Pemerintah Desa Darmasaba berharap RKP Desa Tahun 2026 dapat disusun secara komprehensif, berkelanjutan, dan selaras dengan visi pembangunan daerah. Selain itu, kegiatan ini juga menegaskan komitmen Pemerintah Desa Darmasaba untuk terus menghadirkan pembangunan yang inklusif, berkeadilan, dan bermanfaat bagi seluruh lapisan masyarakat. #Musrenbangdes2026 #RKPDarmasaba2026 #PemdesDarmasaba #PerbekelDarmasaba #DesaDarmasaba #KitaDarmasaba #DarmasabaBisa</p>",
|
||||
"linkVideo": "https://www.youtube.com/watch?v=7pirwEmyP-4"
|
||||
}
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"id": "edit",
|
||||
"name": "Pelayanan Penduduk Non-Permanent",
|
||||
"deskripsi": "<p>Surat Keterangan Penduduk Non-Permanent adalah dokumen yang dikeluarkan oleh pihak berwenang untuk memberikan keterangan bahwa seseorang atau kelompok orang memiliki status penduduk non-permanent di suatu wilayah. Dokumen ini biasanya digunakan untuk keperluan administratif atau legal, seperti mendapatkan akses ke layanan kesehatan, pendidikan, atau pelayanan publik lainnya.</p>"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"id": "edit",
|
||||
"name": "Pelayanan Perizinan Berusaha Berbasis Risiko Melalui Sistem ONLINE SINGLE SUBMISSION (OSS)",
|
||||
"deskripsi": "<p>Penyelenggaraan Perizinan Berusaha Berbasis Risiko melalui Sistem Online Single Submission (OSS) merupakan pelaksanaan Undang-Undang Nomor 11 Tahun 2020 Tentang Cipta Kerja. OSS Berbasis Risiko wajib digunakan oleh Pelaku Usaha, Kementerian/Lembaga, Pemerintah Daerah, Administrator Kawasan Ekonomi Khusus (KEK), dan Badan Pengusahaan Kawasan Perdagangan Bebas Pelabuhan Bebas (KPBPB).Berdasarkan Peraturan Pemerintah Nomor 5 Tahun 2021 terdapat 1.702 kegiatan usaha yang terdiri atas 1.349 Klasifikasi Baku Lapangan Usaha Indonesia (KBLI) yang sudah diimplementasikan dalam Sistem OSS Berbasis Risiko.</p>",
|
||||
"link" : "https://oss.go.id/"
|
||||
|
||||
@@ -1,57 +1,79 @@
|
||||
[
|
||||
{
|
||||
"id" : "cmdxyb9zi0010vniiaeyi55ui",
|
||||
"name" : "Surat Keterangan Beda Biodata Diri",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Fotocopy dokumen bersangkutan yang terdapat perbedaan biodata diri misal : Sertifikat Tanah/Ijazah/Polis Asuransi dan lainnya.</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxycqz40014vniidftrixvf",
|
||||
"name" : "Surat Keterangan Yatim Piatu",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau KIA atau Kartu Keluarga</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdwx3wph0003vnr74us2t7h7",
|
||||
"name" : "Surat Keterangan Domisili Organisasi",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen:</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy Surat Keterangan Terdaftar (SKT) organisasi atau Pengukuhan Kelompok</p></li><li><p>Jika Pengajuan baru pembuatan SKT maka melengkapi Susunan Pengurus lengkap dengan Kop Organisasi</p></li><li><p>Tanggal berdiri/Tahun berdiri/Sejak kapan berdirinya organisasi</p></li></ul><p>Alur Pelayanan:</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxxv3i80004vniidg1mrucc",
|
||||
"name" : "Surat Keterangan Penghasilan",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP orang tua atau Fotocopy Kartu keluarga</p></li><li><p>Membuat Surat Pernyataan Penghasilan bermaterai (disertai jumlah penghasilan)</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxxwp070008vnii9jbdcto7",
|
||||
"name" : "Surat Keterangan Tidak Mampu",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP/KIA atau Kartu Keluarga</p></li><li><p>Fotocopy Kartu Indonesia Pintar/Kartu Perlindungan Sosial/Terdaftar dalam DTKS</p></li><li><p>Jika tidak memiliki Kartu tersebut diatas diwajibkan membuat Surat Pernyataan Tidak Mampu</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxxyfkl000cvnii1bxinnfi",
|
||||
"name" : "Surat Keterangan Kelahiran",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy Surat lahir dari dokter/bidan (jika ada)</p></li><li><p>Fotocopy Kartu Keluarga</p></li><li><p>Fotocopy KTP 2 orang saksi</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxy23pl000gvniihsg38aq4",
|
||||
"name" : "Surat Keterangan Usaha",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Foto Lokasi dan Kegiatan Usaha di cetak dalam selembar kertas (diparaf dan stempel oleh Kelian Banjar Dinas)</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxy4mgt000kvniib1nemjem",
|
||||
"name" : "Surat Keterangan Kematian",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Surat Kematian dari rumah sakit atau dokter (jika ada)</p></li><li><p>tanggal kematian</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxy61a1000ovniif4ytb9hs",
|
||||
"name" : "Surat Keterangan Tempat Usaha",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Foto Lokasi dan Kegiatan Usaha di cetak dalam selembar kertas (diparaf dan stempel oleh Kelian Banjar Dinas)</p></li><li><p>Surat Perjanjian Sewa/Kontrak atau Kwintansi Pembayaran Sewa 3 bulan terakhir bagi yang mengontrak tempat usaha, apabila tempat usaha milik sendiri lampiri dengan dokumen kepemilikan tempat usaha (dapat berupa fotocopy sppt atau Fotocopy Sertipikat Hak Milik)</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxy754q000svniiiz8oqyo0",
|
||||
"name" : "Surat Keterangan Belum Kawin",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Khusus bagi yang berstatus duda atau janda melampirkan fotocopy akta cerai atau dokumen pendukung lainnya</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
},
|
||||
{
|
||||
"id" : "cmdxy8pi2000wvnii48fc1sxd",
|
||||
"name" : "Surat Keterangan Kelakuan Baik",
|
||||
"deskripsi" : "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li></ul><p>Alur Pelayanan :</p>"
|
||||
}
|
||||
]
|
||||
{
|
||||
"id": "cmdxyb9zi0010vniiaeyi55ui",
|
||||
"name": "Surat Keterangan Beda Biodata Diri",
|
||||
"deskripsi": "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Fotocopy dokumen bersangkutan yang terdapat perbedaan biodata diri misal : Sertifikat Tanah/Ijazah/Polis Asuransi dan lainnya.</p></li></ul><p>Alur Pelayanan :</p>",
|
||||
"image": "cmk6n68k80005vn6qtdbshoqi",
|
||||
"image2": "cmk6n68vw0006vn6qv65lrdw8"
|
||||
},
|
||||
{
|
||||
"id": "cmdxycqz40014vniidftrixvf",
|
||||
"name": "Surat Keterangan Yatim Piatu",
|
||||
"deskripsi": "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau KIA atau Kartu Keluarga</p></li></ul><p>Alur Pelayanan :</p>",
|
||||
"image": "cmk6n8rwv0007vn6q312rawvt",
|
||||
"image2": "cmk6n8s9b0008vn6qwd02eyjf"
|
||||
},
|
||||
{
|
||||
"id": "cmdwx3wph0003vnr74us2t7h7",
|
||||
"name": "Surat Keterangan Domisili Organisasi",
|
||||
"deskripsi": "<p>Persyaratan Dokumen:</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy Surat Keterangan Terdaftar (SKT) organisasi atau Pengukuhan Kelompok</p></li><li><p>Jika Pengajuan baru pembuatan SKT maka melengkapi Susunan Pengurus lengkap dengan Kop Organisasi</p></li><li><p>Tanggal berdiri/Tahun berdiri/Sejak kapan berdirinya organisasi</p></li></ul><p>Alur Pelayanan:</p>",
|
||||
"image": "cmk6n9ti90009vn6qt48wbklu",
|
||||
"image2": "cmk6n9tvx000avn6qwkguha7w"
|
||||
},
|
||||
{
|
||||
"id": "cmdxxv3i80004vniidg1mrucc",
|
||||
"name": "Surat Keterangan Penghasilan",
|
||||
"deskripsi": "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP orang tua atau Fotocopy Kartu keluarga</p></li><li><p>Membuat Surat Pernyataan Penghasilan bermaterai (disertai jumlah penghasilan)</p></li></ul><p>Alur Pelayanan :</p>",
|
||||
"image": "cmk6nb69n000bvn6qx67n06ee",
|
||||
"image2": "cmk6nb6nq000cvn6q75730cr3"
|
||||
},
|
||||
{
|
||||
"id": "cmdxxwp070008vnii9jbdcto7",
|
||||
"name": "Surat Keterangan Tidak Mampu",
|
||||
"deskripsi": "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP/KIA atau Kartu Keluarga</p></li><li><p>Fotocopy Kartu Indonesia Pintar/Kartu Perlindungan Sosial/Terdaftar dalam DTKS</p></li><li><p>Jika tidak memiliki Kartu tersebut diatas diwajibkan membuat Surat Pernyataan Tidak Mampu</p></li></ul><p>Alur Pelayanan :</p>",
|
||||
"image": "cmk6nc438000dvn6q723r7phi",
|
||||
"image2": "cmk6nc4aa000evn6qwl5t53h4"
|
||||
},
|
||||
{
|
||||
"id": "cmdxxyfkl000cvnii1bxinnfi",
|
||||
"name": "Surat Keterangan Kelahiran",
|
||||
"deskripsi": "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy Surat lahir dari dokter/bidan (jika ada)</p></li><li><p>Fotocopy Kartu Keluarga</p></li><li><p>Fotocopy KTP 2 orang saksi</p></li></ul><p>Alur Pelayanan :</p>",
|
||||
"image": "cmk6ndw2z000fvn6q3b9wxswd",
|
||||
"image2": "cmk6ndwbf000gvn6qusd4mkhq"
|
||||
},
|
||||
{
|
||||
"id": "cmdxy23pl000gvniihsg38aq4",
|
||||
"name": "Surat Keterangan Usaha",
|
||||
"deskripsi": "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Foto Lokasi dan Kegiatan Usaha di cetak dalam selembar kertas (diparaf dan stempel oleh Kelian Banjar Dinas)</p></li></ul><p>Alur Pelayanan :</p>",
|
||||
"image": "cmk6nf4pu000hvn6qszs6xyp5",
|
||||
"image2": "cmk6nf53c000ivn6qawvoxndb"
|
||||
},
|
||||
{
|
||||
"id": "cmdxy4mgt000kvniib1nemjem",
|
||||
"name": "Surat Keterangan Kematian",
|
||||
"deskripsi": "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Surat Kematian dari rumah sakit atau dokter (jika ada)</p></li><li><p>tanggal kematian</p></li></ul><p>Alur Pelayanan :</p>",
|
||||
"image": "cmk6ng6yk000jvn6q0ditzufj",
|
||||
"image2": "cmk6ng77b000kvn6q51j2s4q7"
|
||||
},
|
||||
{
|
||||
"id": "cmdxy61a1000ovniif4ytb9hs",
|
||||
"name": "Surat Keterangan Tempat Usaha",
|
||||
"deskripsi": "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Foto Lokasi dan Kegiatan Usaha di cetak dalam selembar kertas (diparaf dan stempel oleh Kelian Banjar Dinas)</p></li><li><p>Surat Perjanjian Sewa/Kontrak atau Kwintansi Pembayaran Sewa 3 bulan terakhir bagi yang mengontrak tempat usaha, apabila tempat usaha milik sendiri lampiri dengan dokumen kepemilikan tempat usaha (dapat berupa fotocopy sppt atau Fotocopy Sertipikat Hak Milik)</p></li></ul><p>Alur Pelayanan :</p>",
|
||||
"image": "cmk6nh7ez000lvn6qjfkqeufn",
|
||||
"image2": "cmk6nh7pq000mvn6qor967hjh"
|
||||
},
|
||||
{
|
||||
"id": "cmdxy754q000svniiiz8oqyo0",
|
||||
"name": "Surat Keterangan Belum Kawin",
|
||||
"deskripsi": "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li><li><p>Khusus bagi yang berstatus duda atau janda melampirkan fotocopy akta cerai atau dokumen pendukung lainnya</p></li></ul><p>Alur Pelayanan :</p>",
|
||||
"image": "cmk6n37tp0003vn6qqyvelh5s",
|
||||
"image2": "cmk6n388d0004vn6qby5fkuz8"
|
||||
},
|
||||
{
|
||||
"id": "cmdxy8pi2000wvnii48fc1sxd",
|
||||
"name": "Surat Keterangan Kelakuan Baik",
|
||||
"deskripsi": "<p>Persyaratan Dokumen :</p><ul><li><p>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</p></li><li><p>Fotocopy KTP atau Kartu Keluarga</p></li></ul><p>Alur Pelayanan :</p>",
|
||||
"image": "cmk6mvhwh0001vn6qa6ddzqfm",
|
||||
"image2": "cmk6mvi8c0002vn6q9vfyv6yt"
|
||||
}
|
||||
]
|
||||
|
||||
30
prisma/data/desa/penghargaan/penghargaan.json
Normal file
30
prisma/data/desa/penghargaan/penghargaan.json
Normal file
@@ -0,0 +1,30 @@
|
||||
[
|
||||
{
|
||||
"id": "cmkal9dd70002vnexhvn4j2fv",
|
||||
"name": "Perbekel Darmasaba Terima Penghargaan pada Indonesia Alternative Dispute Resolution Awards 2025",
|
||||
"juara": "Penghargaan IADRA 2025",
|
||||
"deskripsi": "<p>Perbekel Darmasaba, Ida Bagus Surya Prabhawa Manuaba menerima penghargaan pada Indonesia Alternative Dispute Resolution Awards 2025 atas kontribusi dalam penyelesaian sengketa secara damai dan berkeadilan.</p>",
|
||||
"imageId": "cmkal9d2p0000vnexf8flpunj"
|
||||
},
|
||||
{
|
||||
"id": "cmkalfbux0005vnexj8zplube",
|
||||
"name": "Penghargaan Bhawana Sewaka Nugraha kepada Perbekel Darmasaba",
|
||||
"juara": "Penghargaan Bhawana Sewaka Nugraha",
|
||||
"deskripsi": "<p>Penghargaan Bhawana Sewaka Nugraha diberikan kepada Perbekel Desa Darmasaba atas dedikasi dan komitmen dalam menjaga kelestarian lingkungan serta menginspirasi masyarakat untuk menciptakan desa yang hijau, bersih, dan berkelanjutan.</p>",
|
||||
"imageId": "cmkalfbsn0003vnex3bbsx5ja"
|
||||
},
|
||||
{
|
||||
"id": "cmkalg6hk0008vnexmsqwd3n7",
|
||||
"name": "Desa Darmasaba Raih Juara 1 Lomba Desa Tingkat Provinsi Bali Tahun 2025",
|
||||
"juara": "Juara 1 Lomba Desa Provinsi Bali 2025",
|
||||
"deskripsi": "<p>Desa Darmasaba meraih juara 1 dalam Lomba Desa Tingkat Provinsi Bali Tahun 2025 melalui sinergi elemen desa serta inovasi dalam pelayanan publik dan pemberdayaan masyarakat.</p>",
|
||||
"imageId": "cmkalg6fh0006vnexid4z9vlg"
|
||||
},
|
||||
{
|
||||
"id": "cmkalgucu000bvnexw2blt1v3",
|
||||
"name": "Pemerintah Desa Darmasaba Raih Peringkat IV dalam Ajang Mangupura Award Tahun 2025",
|
||||
"juara": "Peringkat IV Mangupura Award 2025",
|
||||
"deskripsi": "<p>Desa Darmasaba mendapatkan penghargaan Peringkat IV pada Mangupura Award 2025 dari Pemerintah Kabupaten Badung atas tata kelola pemerintahan, inovasi layanan, dan pemberdayaan masyarakat.</p>",
|
||||
"imageId": "cmkalguac0009vnex5dpixivn"
|
||||
}
|
||||
]
|
||||
8
prisma/data/desa/pengumuman/kategori-pengumuman.json
Normal file
8
prisma/data/desa/pengumuman/kategori-pengumuman.json
Normal file
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{ "id": "cmk69tghx111tvnv8g2d206wv", "name": "Digitalisasi Desa" },
|
||||
{ "id": "cmk69tghx111uvnv847ppcxqh", "name": "Adat & Budaya" },
|
||||
{ "id": "cmk69tght111svnv8ok5rid2v", "name": "Lingkungan & Bencana" },
|
||||
{ "id": "cmk69tghy111wvnv8umg2vloa", "name": "Pendidikan & Kepemudaan" },
|
||||
{ "id": "cmk69tghy111vvnv8xeouenv5", "name": "Ekonomi & UMKM" },
|
||||
{ "id": "cmk69tghz111xvnv8kxzzt24h", "name": "Sosial & Kesehatan" }
|
||||
]
|
||||
37
prisma/data/desa/pengumuman/pengumuman.json
Normal file
37
prisma/data/desa/pengumuman/pengumuman.json
Normal file
@@ -0,0 +1,37 @@
|
||||
[
|
||||
{
|
||||
"id": "cmk6ae8rz00003b6r06x7hsqz",
|
||||
"judul": "Pengumuman Lelang Pengadaan Pick UpDump TPS3R Pudak Mesari",
|
||||
"deskripsi": "<p>Pengumuman lelang pengadaan alat Pick UpDump untuk TPS3R Pudak Mesari Desa Darmasaba.</p>",
|
||||
"content": "<p>Desa Darmasaba membuka lelang pengadaan Pick UpDump untuk mendukung kegiatan pengelolaan sampah di TPS3R Pudak Mesari.</p>",
|
||||
"categoryPengumumanId": "cmk69tghy111vvnv8xeouenv5"
|
||||
},
|
||||
{
|
||||
"id": "cmk6af7vf00013b6rj2br4nv7",
|
||||
"judul": "Pengumuman Lelang Pembangunan Taman BR. Darmasaba",
|
||||
"deskripsi": "<p>Pengumuman lelang pembangunan taman di Banjar BR. Darmasaba.</p>",
|
||||
"content": "<p>Desa Darmasaba mengumumkan pelaksanaan lelang pembangunan taman sebagai bagian dari upaya memperindah ruang publik desa.</p>",
|
||||
"categoryPengumumanId": "cmk69tghy111vvnv8xeouenv5"
|
||||
},
|
||||
{
|
||||
"id": "cmk6afo0g00033b6rjc2pae69",
|
||||
"judul": "Pengumuman Lelang Penataan Lapangan Desa Darmasaba Tahun Anggaran 2024",
|
||||
"deskripsi": "<p>Pengumuman lelang penataan lapangan Desa Darmasaba untuk tahun anggaran 2024.</p>",
|
||||
"content": "<p>Lelang penataan lapangan desa dilaksanakan untuk menunjang kegiatan desa dan fasilitas masyarakat di Darmasaba.</p>",
|
||||
"categoryPengumumanId": "cmk69tghy111vvnv8xeouenv5"
|
||||
},
|
||||
{
|
||||
"id": "cmk6ag56y00053b6rj9481z5l",
|
||||
"judul": "Pengumuman Lelang Penataan Landscape Lapangan Desa",
|
||||
"deskripsi": "<p>Pengumuman lelang penataan landscape lapangan Desa Darmasaba.</p>",
|
||||
"content": "<p>Desa Darmasaba menyelenggarakan lelang untuk kegiatan penataan landscape lapangan desa dalam rangka peningkatan fasilitas umum.</p>",
|
||||
"categoryPengumumanId": "cmk69tghx111tvnv8g2d206wv"
|
||||
},
|
||||
{
|
||||
"id": "cmk6agzxx00073b6rr3vhxcti",
|
||||
"judul": "Penutupan KKN-PMM Periode II Universitas Warmadewa di Desa Darmasaba",
|
||||
"deskripsi": "<p>Penutupan program KKN-PMM yang berjalan dengan berbagai kegiatan pemberdayaan masyarakat.</p>",
|
||||
"content": "<p>Kegiatan KKN meliputi edukasi kesehatan, pengelolaan sampah, literasi keuangan, dan upaya ekonomi lokal sebagai bagian dari pembangunan desa berkelanjutan.</p>",
|
||||
"categoryPengumumanId": "cmk69tghx111tvnv8g2d206wv"
|
||||
}
|
||||
]
|
||||
14
prisma/data/desa/potensi/kategori-potensi.json
Normal file
14
prisma/data/desa/potensi/kategori-potensi.json
Normal file
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": "cmk3pmwq10008vn9bqdquv153",
|
||||
"nama": "Wisata"
|
||||
},
|
||||
{
|
||||
"id": "cmk3s1ks6000ivn9bcrv960ko",
|
||||
"nama": "Ekonomi"
|
||||
},
|
||||
{
|
||||
"id": "cmk3s1r0m000jvn9b8tlhogwn",
|
||||
"nama": "Lingkungan"
|
||||
}
|
||||
]
|
||||
@@ -1,74 +1,90 @@
|
||||
[
|
||||
{
|
||||
"id": "cmdyamai40004vnw3sdjbvn48",
|
||||
"name": "TPS3R Pudak Mesari",
|
||||
"deskripsi": "TPS 3R Pudak Mesari Darmasaba layak mendapat penghargaan demikian apresiasi dari Delterra Sosial Indonesia nie Semeton Darmasaba!, Hal tersebut dikarenakan walaupun baru berdiri namun TPS 3R kebanggaan Desa Darmasaba tersebut sudah berjalan dengan sangat baik.",
|
||||
"content": "<p>TPS3R Pudak Mesari adalah Tempat Pengolahan Sampah dengan konsep Reduce, Reuse, dan Recycle (TPS3R) yang berlokasi di Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung, Bali. Fasilitas ini berperan penting dalam pengelolaan sampah berbasis masyarakat, dengan tujuan mengurangi volume sampah yang masuk ke Tempat Pembuangan Akhir (TPA) dan meningkatkan kesadaran warga tentang pentingnya pengelolaan sampah yang berkelanjutan.</p><p>Potensi Desa melalui TPS3R Pudak Mesari:</p><ol><li><p><strong>Peningkatan Kesehatan Lingkungan:</strong></p><p>Dengan pengelolaan sampah yang efektif, desa dapat menjaga kebersihan lingkungan, mengurangi risiko penyakit, dan menciptakan suasana yang lebih nyaman bagi warga.</p></li><li><p><strong>Pemberdayaan Ekonomi Masyarakat:</strong></p><p>TPS3R membuka peluang usaha bagi warga melalui pemilahan dan pengolahan sampah, seperti produksi kompos dari sampah organik dan kerajinan tangan dari sampah anorganik yang dapat meningkatkan pendapatan masyarakat.</p></li><li><p><strong>Edukasi dan Kesadaran Lingkungan:</strong></p><p>Fasilitas ini dapat menjadi pusat edukasi bagi masyarakat tentang pentingnya pengelolaan sampah, mendorong partisipasi aktif dalam menjaga kelestarian lingkungan.</p></li><li><p><strong>Pengembangan Pariwisata Berkelanjutan:</strong></p><p>Dengan lingkungan yang bersih dan asri, Desa Darmasaba memiliki potensi untuk menarik wisatawan yang tertarik pada ekowisata dan budaya lokal, sehingga meningkatkan perekonomian desa.</p></li></ol>"
|
||||
},
|
||||
{
|
||||
"id": "cmdyb7h440003vngjapbc84f7",
|
||||
"name": "Bumdes Pudak Mesari",
|
||||
"deskripsi": "Bumdes Pudak Mesari sangat membantu warga desa Darmasaba dalam mengelola dan membangun sebuah desa yang lebih baik lagi",
|
||||
"content": "<p>Badan Usaha Milik Desa (BUMDes) Pudak Mesari adalah lembaga ekonomi desa yang berperan penting dalam pengembangan potensi dan kesejahteraan masyarakat Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung, Bali. BUMDes ini berfungsi sebagai motor penggerak perekonomian desa melalui berbagai unit usaha yang dikelola secara profesional.</p><p>Potensi dan Peran BUMDes Pudak Mesari:</p><ol><li><p><strong>Pengembangan Usaha Mikro dan Kecil:</strong></p><p>BUMDes Pudak Mesari menyediakan layanan bagi pelaku usaha mikro dan kecil di desa, seperti penyediaan konsumsi dan snack kotak untuk berbagai acara.</p></li><li><p><strong>Pengelolaan Sampah Berbasis Masyarakat:</strong></p><p>Melalui kolaborasi dengan komunitas pemuda peduli lingkungan, BUMDes Pudak Mesari aktif dalam pengelolaan sampah berbasis masyarakat.</p></li><li><p><strong>Peningkatan Kapasitas dan Transparansi:</strong></p><p>Untuk memastikan pengelolaan yang akuntabel, BUMDes Pudak Mesari rutin mengadakan rapat koordinasi dan pendampingan penyusunan laporan pertanggungjawaban.</p></li><li><p><strong>Kolaborasi Internasional:</strong></p><p>Desa Darmasaba, melalui BUMDes Pudak Mesari, menerima kunjungan dari tim Osaki Jepang untuk memperkuat pengelolaan sampah dan lingkungan.</p></li></ol><p>Dengan berbagai inisiatif tersebut, BUMDes Pudak Mesari menunjukkan perannya sebagai pilar utama dalam pengembangan ekonomi dan kesejahteraan masyarakat Desa Darmasaba, sekaligus menjaga kelestarian lingkungan melalui program-program inovatif dan kolaboratif.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdybb53i0007vngjet38spn8",
|
||||
"id": "cmk3rbc7s000evn9bvb8p0xk1",
|
||||
"name": "Taman Beji Cengana",
|
||||
"deskripsi": "Tirta Klebutan di Pura Taman Beji Cengana di Desa Adat Darmasaba, Badung, selain dipercaya nunas Taksu serta pembersihan diri. Tersemat juga asal usul cerita ditemukannya Tirta Klebutan yang tepat berada di pinggir Tukad Cengana tersebut.",
|
||||
"content": "<p>Taman Beji Cengana, terletak di Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung, Bali, adalah situs suci yang memiliki nilai spiritual dan sejarah yang tinggi. Tempat ini dikenal sebagai lokasi untuk ritual pembersihan diri (melukat) dan peribadatan oleh umat Hindu Bali. Keberadaan mata air suci (Tirta Klebutan) di Taman Beji Cengana dipercaya memberikan berkah dan penyucian bagi mereka yang datang untuk berdoa dan melakukan ritual.</p><p>Potensi Desa melalui Taman Beji Cengana:</p><ol><li><p><strong>Pengembangan Pariwisata Spiritual:</strong></p><p>Taman Beji Cengana memiliki potensi besar sebagai destinasi wisata spiritual. Wisatawan yang mencari pengalaman spiritual dan ketenangan batin dapat tertarik untuk mengunjungi tempat ini, mengikuti ritual melukat, dan merasakan suasana sakral yang ditawarkan.</p></li><li><p><strong>Pelestarian Budaya dan Tradisi:</strong></p><p>Dengan mempromosikan Taman Beji Cengana sebagai pusat kegiatan budaya dan ritual tradisional, desa dapat memastikan bahwa warisan budaya dan tradisi lokal tetap lestari.</p></li><li><p><strong>Pendidikan dan Penelitian:</strong></p><p>Taman Beji Cengana dapat dijadikan sebagai pusat pendidikan dan penelitian bagi akademisi, peneliti, dan pelajar yang tertarik mempelajari budaya, agama, dan sejarah Bali.</p></li><li><p><strong>Pengembangan Ekonomi Kreatif:</strong></p><p>Dengan meningkatnya jumlah pengunjung ke Taman Beji Cengana, peluang bagi pengembangan ekonomi kreatif juga terbuka lebar. Masyarakat lokal dapat mengembangkan produk kerajinan tangan, kuliner khas, dan suvenir yang mencerminkan budaya dan tradisi desa.</p></li><li><p><strong>Konservasi Lingkungan:</strong></p><p>Sebagai situs suci dengan mata air alami, Taman Beji Cengana memiliki peran penting dalam konservasi lingkungan. Upaya menjaga kebersihan dan kelestarian mata air serta lingkungan sekitarnya dapat menjadi contoh praktik konservasi yang baik.</p></li></ol><p>Dengan memanfaatkan potensi yang dimiliki Taman Beji Cengana, Desa Darmasaba dapat mengembangkan sektor pariwisata, budaya, pendidikan, ekonomi, dan lingkungan secara berkelanjutan, yang pada gilirannya akan meningkatkan kesejahteraan masyarakat dan pelestarian warisan budaya.</p>"
|
||||
"deskripsi": "<p>Taman Beji Cengana adalah situs keagamaan dan budaya tradisional Bali</p>",
|
||||
"content": "<p>Taman Beji Cengana adalah situs keagamaan dan budaya tradisional Bali yang memadukan unsur spiritual, ritual pembersihan (melukat), serta nilai budaya lokal yang berpadu dengan suasana alam yang tenang dan harmonis.</p>",
|
||||
"kategoriId": "cmk3pmwq10008vn9bqdquv153",
|
||||
"imageId": "cmk3rbc53000cvn9bjsywrj18"
|
||||
},
|
||||
{
|
||||
"id": "cmdybckcz000avngjfzpy60uk",
|
||||
"id": "cmk3q0sez000bvn9b3iyyct5m",
|
||||
"name": "Gumuh Sari Water Park",
|
||||
"deskripsi": "Gumuh Sari Rekreasi atau waterpark, tempat wisata yang asyik dan seru untuk kamu sekeluarga! Tempat liburan di Bali memang seakan nggak ada habisnya. Selalu ada aja destinasi wisata seru yang bisa jadi wishlist. Ada banyak banget tempat wisata yang kamu kunjungi di Bali, mulai dari wisata alam, wisata modern, sampai wisata air.",
|
||||
"content": "<p>Gumuh Sari Waterpark, terletak di Jl. Tegal Gumuh No. 9, Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung, Bali, adalah destinasi rekreasi yang menawarkan berbagai fasilitas untuk pengunjung dari segala usia. Taman rekreasi ini tidak hanya menyediakan wahana air yang menyenangkan, tetapi juga fasilitas olahraga dan kuliner, menjadikannya tempat ideal untuk rekreasi keluarga dan komunitas.</p><p>Potensi Desa melalui Gumuh Sari Waterpark:</p><ol><li><p><strong>Pengembangan Pariwisata Lokal:</strong></p><p>Dengan adanya destinasi seperti Gumuh Sari Waterpark, Desa Darmasaba dapat menarik lebih banyak wisatawan lokal maupun mancanegara. Kehadiran pengunjung ini berpotensi meningkatkan pendapatan desa dan membuka peluang usaha baru bagi masyarakat setempat.</p></li><li><p><strong>Peningkatan Ekonomi Masyarakat:</strong></p><p>Fasilitas seperti restoran dan pusat olahraga di dalam kompleks waterpark memberikan peluang bagi warga lokal untuk terlibat dalam sektor jasa dan perdagangan. Hal ini dapat menciptakan lapangan pekerjaan dan mendukung pertumbuhan ekonomi desa.</p></li><li><p><strong>Pengembangan Fasilitas Olahraga dan Kesehatan:</strong></p><p>Dengan adanya pusat futsal dan gym, Gumuh Sari Waterpark mendorong masyarakat untuk berpartisipasi dalam kegiatan olahraga, yang dapat meningkatkan kesehatan dan kesejahteraan warga.</p></li><li><p><strong>Pemberdayaan Komunitas Melalui Event dan Acara:</strong></p><p>Waterpark ini sering menjadi tuan rumah berbagai acara komunitas, seperti pesta busa dan bola, yang dapat mempererat hubungan antarwarga dan menciptakan lingkungan yang harmonis.</p></li><li><p><strong>Peningkatan Infrastruktur dan Aksesibilitas:</strong></p><p>Dengan meningkatnya jumlah pengunjung, infrastruktur desa seperti jalan, transportasi, dan layanan umum lainnya akan berkembang untuk memenuhi kebutuhan tersebut, yang pada gilirannya meningkatkan kualitas hidup masyarakat setempat.</p></li></ol><p>Melalui pengelolaan dan pengembangan yang tepat, Gumuh Sari Waterpark dapat menjadi motor penggerak bagi kemajuan Desa Darmasaba, meningkatkan kesejahteraan masyarakat, dan menjadikan desa ini sebagai destinasi wisata yang dikenal luas.</p>"
|
||||
"deskripsi": "<p>Gumuh Sari merupakan tempat rekreasi nyaman di kawasan badung yang dapat dinikmati oleh semua umur dengan berbagai fasilitas hiburan, seperti Water Park, Pusat Kebugaran, Lapangan Futsal dan Restaurant.</p>",
|
||||
"content": "<p>Gumuh Sari merupakan tempat rekreasi nyaman di kawasan badung yang dapat dinikmati oleh semua umur dengan berbagai fasilitas hiburan, seperti Water Park, Pusat Kebugaran, Lapangan Futsal dan Restaurant.<br><br><strong>Buka setiap hari</strong><br>Senin-Jumat: pukul 10.00-18.00 WITA (Weekday)<br>Sabtu-Minggu: pukul 09.00-18.00 WITA (Weekend)<br><br><strong>Wahana:</strong><br>- Kolam Renang Anak<br>- Kolam Renang Dewasa<br>- Kolam Renang Olympic<br>- Kids Playground Indoor<br>- Ayunan<br>- ATV<br><br><strong>Fasilitas:</strong><br>- Tempat ibadah<br>- Area parkir<br>- Gazebo<br>- Restoran dan kantin<br>- WiFi gratis<br>- Kamar mandi dan ruang bilas<br>- Ruang loker<br><br><strong>Kategori Tiket:</strong><br>Senin-Sabtu<br>- Dewasa 35.000<br>- Anak-anak (3-12 tahun) 20.000<br><br>Minggu & Libur Nasional<br>- Dewasa 40.000<br>- Anak-anak (3-12 tahun) 25.000<br><br>Hari Raya<br>- Dewasa 45.000<br>- Anak-anak (3-12 tahun) 35.000<br><br><strong>Media Sosial:</strong><br>- Instagram: gumuhsari_rekreasi<br><a target='_blank' rel='noopener noreferrer nofollow' href='https://www.instagram.com/gumuhsari_rekreasi/'><u>https://www.instagram.com/gumuhsari_rekreasi/</u></a><br><br>- Tiktok: gumuhsari_rekreasi<br><a target='_blank' rel='noopener noreferrer nofollow' href='https://www.tiktok.com/@gumuhsari_rekreasi'><u>https://www.tiktok.com/@gumuhsari_rekreasi</u></a></p>",
|
||||
"kategoriId": "cmk3pmwq10008vn9bqdquv153",
|
||||
"imageId": "cmk3q0s8b0009vn9blvh0q5cc"
|
||||
},
|
||||
{
|
||||
"id": "cmdyjuij40002vns5qyyjmzf4",
|
||||
"id": "cmk3rsch1000hvn9bhmyhsjqm",
|
||||
"name": "TPS3R Pudak Mesari",
|
||||
"deskripsi": "<p>Sistem pengelolaan sampah terpadu di Desa Darmasaba yang dikenal dengan TPS3R Pudak Mesari.</p>",
|
||||
"content": "<p>TPS3R Pudak Mesari adalah fasilitas pengelolaan sampah terpadu yang mengoptimalkan pemilahan, pengolahan dan daur ulang sampah masyarakat sehingga mendukung lingkungan yang bersih dan berkelanjutan di Desa Darmasaba.</p>",
|
||||
"kategoriId": "cmk3pmwq10008vn9bqdquv153",
|
||||
"imageId": "cmk3rscb0000fvn9bq6il16ba"
|
||||
},
|
||||
{
|
||||
"id": "cmk3s4kz7000mvn9b8thk98c4",
|
||||
"name": "Bumdes Pudak Mesari",
|
||||
"deskripsi": "<p>Badan Usaha Milik Desa yang bergerak dalam berbagai usaha ekonomi masyarakat Darmasaba.</p>",
|
||||
"content": "<p>Bumdes Pudak Mesari merupakan badan usaha milik desa yang mengembangkan kegiatan usaha produktif untuk meningkatkan perekonomian masyarakat lokal, termasuk pengelolaan usaha jasa dan komoditas lokal yang dikelola secara partisipatif.</p>",
|
||||
"kategoriId": "cmk3s1ks6000ivn9bcrv960ko",
|
||||
"imageId": "cmk3s4kvu000kvn9bahu2vo5f"
|
||||
},
|
||||
{
|
||||
"id": "cmk3sbgge000pvn9bd3r8i0on",
|
||||
"name": "Pertanian",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, Bali, memiliki potensi pertanian yang besar sebagai bagian dari warisan agraris yang telah diwariskan secara turun-temurun. Dengan kondisi tanah yang subur serta sistem irigasi tradisional subak, pertanian di Darmasaba memainkan peran penting dalam ekonomi dan keberlanjutan lingkungan desa.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, Bali, memiliki potensi pertanian yang besar sebagai bagian dari warisan agraris yang telah diwariskan secara turun-temurun. Dengan kondisi tanah yang subur serta sistem irigasi tradisional subak, pertanian di Darmasaba memainkan peran penting dalam ekonomi dan keberlanjutan lingkungan desa.</p><p>Potensi Desa melalui Pertanian:</p><ol><li><p><strong>Potensi dan Komoditas Unggulan</strong></p><p>Pertanian di Desa Darmasaba mengandalkan berbagai jenis tanaman yang memiliki nilai ekonomi tinggi, di antaranya:</p><p>- Padi : Sebagai salah satu desa yang masih mempertahankan sistem subak, Darmasaba menjadi bagian dari lumbung pangan di Bali.</p><p>- Sayur-mayur : Beberapa jenis sayuran seperti kangkung, bayam, cabai, dan tomat banyak dibudidayakan oleh petani lokal.</p><p>- Buah-buahan tropis : Termasuk pisang, mangga, dan kelapa, yang menjadi sumber pendapatan tambahan bagi petani.</p><p>- Tanaman obat dan rempah : Seperti jahe, kunyit, dan lengkuas, yang memiliki permintaan tinggi baik untuk kebutuhan rumah tangga maupun industri herbal.</p></li><li><p><strong>Sistem Irigasi Tradisional Subak:</strong></p><p>Sebagai bagian dari warisan budaya Bali, sistem irigasi subak masih diterapkan di Darmasaba. Sistem ini memungkinkan distribusi air yang adil di antara lahan pertanian dan membantu menjaga keberlanjutan produksi pangan desa.</p></li><li><p><strong>Pengembangan Pertanian Organik:</strong></p><p>Dengan meningkatnya kesadaran akan pentingnya produk sehat dan ramah lingkungan, beberapa petani di Darmasaba mulai beralih ke metode pertanian organik. Hal ini membuka peluang bagi desa untuk mengembangkan produk-produk pertanian yang memiliki nilai jual lebih tinggi.</p></li><li><p><strong>Agrowisata sebagai Sumber Pendapatan Baru:</strong></p><p>Potensi pertanian Darmasaba juga dapat dikembangkan menjadi agrowisata, di mana wisatawan dapat merasakan pengalaman langsung dalam bertani, mengikuti workshop bercocok tanam, serta menikmati hasil pertanian segar. Hal ini dapat menarik wisatawan lokal maupun mancanegara, meningkatkan perekonomian desa.</p></li><li><p><strong>Pemberdayaan Petani dan UMKM Berbasis Pertanian:</strong></p><p>Dengan adanya BUMDes Pudak Mesari dan dukungan dari pemerintah setempat, petani di Darmasaba dapat diberikan pelatihan dan akses pasar yang lebih luas. Produk pertanian dapat diolah menjadi berbagai produk turunan seperti keripik pisang, sambal khas desa, hingga minuman herbal yang dapat dipasarkan ke luar daerah.</p></li></ol><p>Dengan berbagai potensi yang dimiliki, pertanian di Desa Darmasaba dapat terus berkembang melalui inovasi dan pemanfaatan teknologi pertanian modern. Dukungan dari masyarakat, pemerintah, dan lembaga terkait sangat penting untuk menjaga keberlanjutan sektor pertanian dan meningkatkan kesejahteraan petani di desa ini.</p>"
|
||||
"deskripsi": "<p>Sektor pertanian sebagai salah satu potensi utama Desa Darmasaba.</p>",
|
||||
"content": "<p>Pertanian di Darmasaba mencakup berbagai komoditas yang ditanam oleh masyarakat termasuk padi, tanaman hortikultura dan lain-lain, yang menjadi basis mata pencaharian dan kontribusi terhadap ekonomi desa.</p>",
|
||||
"kategoriId": "cmk3s1ks6000ivn9bcrv960ko",
|
||||
"imageId": "cmk3sbgd8000nvn9bqooq77ix"
|
||||
},
|
||||
{
|
||||
"id": "cmdykerrq0002vn6fy4sv7uvm",
|
||||
"name": "Kawasan Kuliner",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, Bali, memiliki potensi besar dalam sektor kuliner. Sebagai desa yang strategis dan terus berkembang, Darmasaba mulai dikenal sebagai destinasi kuliner yang menawarkan beragam makanan khas Bali hingga makanan modern yang menarik minat wisatawan dan masyarakat lokal.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, Bali, memiliki potensi besar dalam sektor kuliner. Sebagai desa yang strategis dan terus berkembang, Darmasaba mulai dikenal sebagai destinasi kuliner yang menawarkan beragam makanan khas Bali hingga makanan modern yang menarik minat wisatawan dan masyarakat lokal.</p><p>Potensi Desa melalui Kawasan Kuliner:</p><ol><li><p><strong>Ragam Kuliner Khas Bali</strong></p><p>Darmasaba memiliki banyak warung dan rumah makan yang menyajikan hidangan khas Bali yang otentik, seperti:</p><p>- Babi Guling : Salah satu kuliner favorit di Bali yang banyak ditemukan di Darmasaba.</p><p>- Ayam Betutu : Hidangan ayam berbumbu khas yang dimasak dengan teknik khas Bali.</p><p>- Lawar : Campuran daging dan sayuran berbumbu khas Bali.</p><p>- Sate Lilit : Sate khas Bali yang terbuat dari daging cincang yang dibalut pada batang serai.</p></li><li><p><strong>Wisata Kuliner Modern & Cafe Kekinian:</strong></p><p>Selain makanan tradisional, Darmasaba juga mulai berkembang dengan hadirnya cafe dan resto kekinian yang menyajikan menu modern seperti kopi spesial, burger, pizza, dan aneka dessert yang digemari anak muda. Keberadaan tempat-tempat ini menjadikan Darmasaba sebagai pilihan destinasi kuliner bagi wisatawan maupun warga sekitar.</p></li><li><p><strong>Pasar Kuliner Malam:</strong></p><p>Salah satu daya tarik Darmasaba adalah pusat kuliner malam yang menghadirkan aneka makanan kaki lima seperti nasi jinggo, tipat cantok, bakso, dan berbagai jajanan khas Bali. Suasana yang ramai dan harga yang terjangkau membuat pasar kuliner ini menjadi tempat favorit bagi masyarakat lokal.</p></li><li><p><strong>Potensi Ekonomi & UMKM Kuliner:</strong></p><p>Dengan berkembangnya sektor kuliner, banyak pelaku UMKM di Darmasaba mulai merintis usaha makanan, baik dalam bentuk warung makan, katering, hingga produksi makanan ringan seperti keripik, sambal, dan minuman tradisional. Potensi ini dapat terus dikembangkan dengan dukungan pemerintah desa dan promosi melalui media sosial.</p></li><li><p><strong>Kawasan Kuliner Berbasis Pariwisata:</strong></p><p>Untuk menarik lebih banyak pengunjung, Darmasaba berpotensi mengembangkan kawasan kuliner berbasis wisata yang menggabungkan pengalaman makan dengan konsep alam terbuka, pertunjukan seni, dan edukasi kuliner khas Bali. Hal ini dapat menjadi daya tarik tambahan bagi wisatawan yang ingin merasakan pengalaman kuliner yang lebih autentik.</p></li></ol><p>Dengan kekayaan kuliner yang dimiliki, Desa Darmasaba berpotensi menjadi kawasan kuliner unggulan di Kabupaten Badung. Dukungan dari masyarakat, pemerintah desa, serta promosi yang lebih luas dapat menjadikan Darmasaba sebagai destinasi kuliner yang semakin dikenal dan berkembang.</p>"
|
||||
"id": "cmk3smnj3000svn9b2ww5hesp",
|
||||
"name": "Jogging Track Tegeh Aban, Karang Gadon dan Munduk Uma",
|
||||
"deskripsi": "<p>Jalur jogging dan area rekreasi alam yang dimanfaatkan masyarakat untuk aktivitas sehat dan pariwisata lokal.</p>",
|
||||
"content": "<p>Jogging Track Tegeh Aban, Karang Gadon dan Munduk Uma adalah area terbuka hijau yang dikembangkan sebagai fasilitas olahraga dan rekreasi, memberikan ruang beraktivitas fisik sambil menikmati suasana alam Desa Darmasaba.</p>",
|
||||
"kategoriId": "cmk3pmwq10008vn9bqdquv153",
|
||||
"imageId": "cmk3smnbf000qvn9b28ma8las"
|
||||
},
|
||||
{
|
||||
"id": "cmdykhfhf0005vn6fnz25kify",
|
||||
"name": "IKM Berbasis Pengolahan Pangan",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam Industri Kecil dan Menengah (IKM) berbasis pengolahan pangan. Dengan sumber daya alam yang melimpah dan warisan kuliner khas Bali, Darmasaba dapat mengembangkan sektor ini untuk meningkatkan kesejahteraan masyarakat dan menciptakan lapangan kerja baru.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam Industri Kecil dan Menengah (IKM) berbasis pengolahan pangan. Dengan sumber daya alam yang melimpah dan warisan kuliner khas Bali, Darmasaba dapat mengembangkan sektor ini untuk meningkatkan kesejahteraan masyarakat dan menciptakan lapangan kerja baru.</p><p>Potensi dan Peran IKM Berbasis Pengolahan Pangan:</p><ol><li><p><strong>Produk Unggulan Pengolahan Pangan</strong></p><p>Beberapa produk olahan pangan yang potensial dikembangkan di Darmasaba meliputi:</p><p>- Keripik dan Snack Tradisional : Seperti keripik pisang, keripik singkong, dan rengginang.</p><p>- Sambal Khas Bali : Seperti sambal matah dan sambal embe yang banyak diminati pasar lokal dan nasional.</p><p>- Minuman Herbal dan Jamu : Berbasis rempah seperti kunyit asam, beras kencur, dan wedang jahe.</p><p>- Olahan Makanan Berbasis Kelapa : Seperti virgin coconut oil (VCO), serundeng, dan gula aren.</p><p>- Kue Tradisional Bali : Seperti jaje laklak, jaje uli, dan klepon yang dapat dikemas secara modern.</p></li><li><p><strong>Peluang Ekonomi dan Pemberdayaan UMKM:</strong></p><p>IKM berbasis pengolahan pangan dapat membuka peluang bagi masyarakat, terutama ibu rumah tangga dan pemuda desa, untuk berwirausaha. Dengan dukungan modal dan pelatihan dari pemerintah desa atau BUMDes Pudak Mesari, usaha kecil ini dapat berkembang menjadi industri yang lebih besar.</p></li><li><p><strong>Digitalisasi dan Pemasaran Online:</strong></p><p>Darmasaba dapat mengembangkan kawasan sentra IKM sebagai pusat produksi, pelatihan, dan pemasaran produk olahan pangan. Dengan adanya fasilitas ini, para pelaku usaha dapat lebih mudah berkolaborasi, meningkatkan kualitas produk, serta mendapatkan akses ke permodalan dan distribusi yang lebih luas.</p></li><li><p><strong>Pengembangan Kawasan Sentra IKM:</strong></p><p>Dengan berkembangnya sektor kuliner, banyak pelaku UMKM di Darmasaba mulai merintis usaha makanan, baik dalam bentuk warung makan, katering, hingga produksi makanan ringan seperti keripik, sambal, dan minuman tradisional. Potensi ini dapat terus dikembangkan dengan dukungan pemerintah desa dan promosi melalui media sosial.</p></li><li><p><strong>Sinergi dengan Pariwisata dan Agrowisata:</strong></p><p>Dengan berkembangnya sektor wisata di Darmasaba, produk olahan pangan dapat dijadikan suvenir khas desa. Pengunjung dapat membeli oleh-oleh seperti sambal kemasan, jajanan khas, atau minuman herbal sebagai bagian dari pengalaman wisata mereka.</p></li></ol><p>IKM berbasis pengolahan pangan memiliki potensi besar untuk menjadi sektor unggulan di Desa Darmasaba. Dengan inovasi, dukungan teknologi, serta pemasaran yang baik, produk-produk lokal dapat bersaing di pasar yang lebih luas, meningkatkan kesejahteraan masyarakat, dan menjadikan Darmasaba sebagai pusat industri pangan kreatif di Kabupaten Badung.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdykjgmv0008vn6fwg0rr2nh",
|
||||
"name": "Genteng",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam industri genteng yang dikelola oleh Usaha Mikro, Kecil, dan Menengah (UMKM). Sebagai desa yang masih mempertahankan nilai-nilai tradisional dalam pembangunan, industri genteng di Darmasaba berperan penting dalam penyediaan bahan bangunan berkualitas bagi masyarakat lokal maupun luar daerah.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam industri genteng yang dikelola oleh Usaha Mikro, Kecil, dan Menengah (UMKM). Sebagai desa yang masih mempertahankan nilai-nilai tradisional dalam pembangunan, industri genteng di Darmasaba berperan penting dalam penyediaan bahan bangunan berkualitas bagi masyarakat lokal maupun luar daerah.</p><p>Potensi dan Peran UMKM Genteng:</p><ol><li><p><strong>Genteng Tradisional Berkualitas Tinggi</strong></p><p>UMKM di Darmasaba memproduksi genteng dari bahan baku pilihan seperti tanah liat berkualitas, yang menghasilkan genteng dengan daya tahan tinggi, kuat, dan cocok untuk iklim tropis. Beberapa jenis genteng yang dihasilkan meliputi:</p><p>- Genteng Tanah Liat : Kuat, tahan lama, dan memiliki estetika khas tradisional.</p><p>- Genteng Beton : Cocok untuk bangunan modern dengan ketahanan lebih tinggi.</p><p>- Genteng Keramik : Memberikan tampilan elegan dan daya serap air yang lebih rendah.</p></li><li><p><strong>Peluang Ekonomi dan Pemberdayaan Masyarakat:</strong></p><p>Industri genteng di Darmasaba memberikan peluang kerja bagi masyarakat setempat, terutama dalam bidang produksi, distribusi, hingga pemasaran. UMKM genteng juga mendukung keberlanjutan ekonomi desa dengan meningkatkan pendapatan warga serta mengurangi angka pengangguran.</p></li><li><p><strong>Inovasi dan Pengembangan Teknologi</strong></p><p>Beberapa pengrajin genteng di Darmasaba telah mulai mengadopsi teknologi modern dalam proses produksi, seperti:</p><p>- Penggunaan cetakan dan oven pembakaran efisien untuk meningkatkan kualitas dan kapasitas produksi.</p><p>- Teknik pelapisan anti bocor dan anti lumut untuk membuat genteng lebih tahan lama.</p><p>- Desain genteng inovatif yang lebih ringan dan mudah dipasang.</p></li><li><p><strong>Pemasaran dan Ekspansi Pasar</strong></p><p>Dengan meningkatnya pembangunan perumahan dan proyek konstruksi di Bali, permintaan akan genteng berkualitas terus bertambah. UMKM genteng Darmasaba dapat memperluas pasarnya dengan:</p><p>- Menjalin kerja sama dengan kontraktor dan pengembang properti.</p><p>- Mempromosikan produk melalui media sosial dan marketplace online.</p><p>- Menyediakan layanan custom sesuai kebutuhan pelanggan.</p></li><li><p><strong>Keberlanjutan dan Ramah Lingkungan:</strong></p><p>Industri genteng di Darmasaba berpotensi dikembangkan secara lebih ramah lingkungan dengan menerapkan metode produksi yang mengurangi limbah dan emisi. Pemanfaatan energi alternatif serta daur ulang bahan limbah dapat membantu menciptakan industri yang lebih berkelanjutan.</p></li></ol><p>UMKM genteng di Desa Darmasaba memiliki potensi besar untuk terus berkembang sebagai sektor industri unggulan. Dengan inovasi, pemasaran yang lebih luas, serta dukungan dari pemerintah dan masyarakat, industri ini dapat meningkatkan kesejahteraan warga dan memperkuat perekonomian desa.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdyklax3000bvn6fdu53f3xq",
|
||||
"name": "Peternakan Ikan Lele",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam sektor peternakan lele. Dengan kondisi lingkungan yang mendukung serta meningkatnya permintaan ikan lele di pasaran, budidaya ikan lele dapat menjadi salah satu sektor ekonomi unggulan yang mampu meningkatkan kesejahteraan masyarakat desa.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam sektor peternakan lele. Dengan kondisi lingkungan yang mendukung serta meningkatnya permintaan ikan lele di pasaran, budidaya ikan lele dapat menjadi salah satu sektor ekonomi unggulan yang mampu meningkatkan kesejahteraan masyarakat desa.</p><p>Potensi dan Peran Peternakan Ikan Lele:</p><ol><li><p><strong>Kondisi Lingkungan yang Mendukung</strong></p><p>Darmasaba memiliki sumber air yang cukup serta iklim yang cocok untuk budidaya ikan lele. Kolam-kolam budidaya dapat dibuat dengan berbagai sistem, seperti:</p><p>- Kolam Terpal : Mudah dibuat dan lebih efisien dalam perawatan.</p><p>- Kolam Beton : Lebih tahan lama dan cocok untuk produksi skala besar.</p><p>- Sistem Bioflok : Teknologi modern yang dapat meningkatkan kepadatan ikan dan mengurangi limbah.</p></li><li><p><strong>Permintaan Pasar yang Tinggi:</strong></p><p>Lele merupakan salah satu jenis ikan yang memiliki permintaan tinggi di Bali, baik untuk konsumsi rumah tangga, warung makan, hingga restoran. Produk olahan seperti lele goreng, pecel lele, dan abon lele semakin diminati, membuka peluang besar bagi peternak lele di Darmasaba.</p></li><li><p><strong>Inovasi dan Pengembangan Teknologi</strong></p><p>Beberapa pengrajin genteng di Darmasaba telah mulai mengadopsi teknologi modern dalam proses produksi, seperti:</p><p>- Penggunaan cetakan dan oven pembakaran efisien untuk meningkatkan kualitas dan kapasitas produksi.</p><p>- Teknik pelapisan anti bocor dan anti lumut untuk membuat genteng lebih tahan lama.</p><p>- Desain genteng inovatif yang lebih ringan dan mudah dipasang.</p></li><li><p><strong>Pemasaran dan Ekspansi Pasar</strong></p><p>Dengan meningkatnya pembangunan perumahan dan proyek konstruksi di Bali, permintaan akan genteng berkualitas terus bertambah. UMKM genteng Darmasaba dapat memperluas pasarnya dengan:</p><p>- Menjalin kerja sama dengan kontraktor dan pengembang properti.</p><p>- Mempromosikan produk melalui media sosial dan marketplace online.</p><p>- Menyediakan layanan custom sesuai kebutuhan pelanggan.</p></li><li><p><strong>Keberlanjutan dan Ramah Lingkungan:</strong></p><p>Industri genteng di Darmasaba berpotensi dikembangkan secara lebih ramah lingkungan dengan menerapkan metode produksi yang mengurangi limbah dan emisi. Pemanfaatan energi alternatif serta daur ulang bahan limbah dapat membantu menciptakan industri yang lebih berkelanjutan.</p></li></ol><p>UMKM genteng di Desa Darmasaba memiliki potensi besar untuk terus berkembang sebagai sektor industri unggulan. Dengan inovasi, pemasaran yang lebih luas, serta dukungan dari pemerintah dan masyarakat, industri ini dapat meningkatkan kesejahteraan warga dan memperkuat perekonomian desa.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdykpkwf000gvn6ftas2cjje",
|
||||
"name": "Jogging Track Tegeh Aban, Karang Gadon dan Munduk Uma Desa",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi wisata olahraga dan rekreasi melalui Jogging Track Tegeh Aban, Karang Gadon, dan Munduk Uma Desa. Jalur jogging ini tidak hanya menjadi fasilitas olahraga bagi warga, tetapi juga berpotensi dikembangkan sebagai destinasi wisata sehat berbasis alam yang menarik bagi wisatawan lokal maupun luar daerah.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi wisata olahraga dan rekreasi melalui Jogging Track Tegeh Aban, Karang Gadon, dan Munduk Uma Desa. Jalur jogging ini tidak hanya menjadi fasilitas olahraga bagi warga, tetapi juga berpotensi dikembangkan sebagai destinasi wisata sehat berbasis alam yang menarik bagi wisatawan lokal maupun luar daerah.</p><p>Potensi dan Peran Jogging Track Tegeh Aban, Karang Gadon dan Munduk Uma Desa:</p><ol><li><p><strong>Keindahan Alam dan Udara Segar:</strong></p><p>Jogging track yang membentang di Tegeh Aban, Karang Gadon, dan Munduk Uma Desa menawarkan pemandangan alam yang asri dengan udara segar khas pedesaan. Jalur ini melewati area persawahan hijau, perkebunan, serta hutan kecil yang memberikan pengalaman jogging yang lebih menyenangkan dan menenangkan.</p></li><li><p><strong>Fasilitas Olahraga dan Rekreasi</strong></p><p>Selain untuk jogging, jalur ini juga cocok digunakan untuk:</p><p>- Bersepeda santai : Jalur yang nyaman untuk pecinta sepeda.</p><p>- Trekking ringan : Cocok bagi wisatawan yang ingin menikmati suasana pedesaan.</p><p>- Meditasi dan Yoga : Area yang tenang dan alami, ideal untuk relaksasi.</p></li><li><p><strong>Destinasi Wisata Sehat dan Edukasi:</strong></p><p>Jogging track ini berpotensi dikembangkan sebagai wisata sehat berbasis alam, di mana pengunjung bisa menikmati udara segar sambil berolahraga. Selain itu, jalur ini dapat dijadikan sebagai rute edukasi lingkungan, mengenalkan keanekaragaman hayati, pertanian, serta kehidupan masyarakat desa.</p></li><li><p><strong>Potensi Ekonomi bagi Masyarakat</strong></p><p>Dengan meningkatnya jumlah pengunjung, masyarakat sekitar dapat memanfaatkan peluang usaha seperti:</p><p>- Warung sehat dan kuliner lokal : Menyediakan makanan dan minuman sehat bagi para pengunjung.</p><p>- Jasa penyewaan sepeda : Menarik bagi wisatawan yang ingin berkeliling lebih jauh.</p><p>- Pemandu wisata lokal : Memberikan pengalaman lebih bagi wisatawan yang ingin mengenal sejarah dan budaya desa.</p></li><li><p><strong>Pengembangan Berkelanjutan:</strong></p><p>Agar semakin menarik, jogging track ini bisa dilengkapi dengan fasilitas tambahan seperti tempat istirahat, spot foto alami, papan informasi tentang flora dan fauna, serta area taman bunga untuk mempercantik jalur jogging.</p></li></ol><p>Jogging Track Tegeh Aban, Karang Gadon, dan Munduk Uma Desa memiliki potensi besar sebagai destinasi wisata sehat dan olahraga berbasis alam. Dengan pengelolaan yang baik serta dukungan dari pemerintah desa dan masyarakat, jalur ini bisa menjadi ikon baru Desa Darmasaba yang menarik bagi wisatawan serta meningkatkan perekonomian warga setempat.</p>"
|
||||
},
|
||||
{
|
||||
"id": "cmdykr76v000jvn6fqngibbmq",
|
||||
"id": "cmk3t8hgs000vvn9bna6pnw8r",
|
||||
"name": "Dam Tanah Putih",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi wisata olahraga dan rekreasi melalui Jogging Track Tegeh Aban, Karang Gadon, dan Munduk Uma Desa. Jalur jogging ini tidak hanya menjadi fasilitas olahraga bagi warga, tetapi juga berpotensi dikembangkan sebagai destinasi wisata sehat berbasis alam yang menarik bagi wisatawan lokal maupun luar daerah.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki Dam Tanah Putih sebagai salah satu potensi desa yang bernilai strategis. Selain berfungsi sebagai infrastruktur pengairan, dam ini juga memiliki potensi untuk dikembangkan sebagai destinasi wisata alam, edukasi, dan rekreasi bagi masyarakat lokal maupun wisatawan.</p><p>Potensi dan Peran Dam Tanah Putih:</p><ol><li><p><strong>Fungsi Utama Sebagai Sumber Pengairan</strong></p><p>Dam Tanah Putih memiliki peran penting dalam sistem irigasi yang menopang sektor pertanian di Darmasaba. Air dari dam ini digunakan untuk:</p><p>- Mengairi sawah dan ladang : Menjamin ketersediaan air bagi petani sepanjang tahun.</p><p>- Menjaga keseimbangan ekosistem : Menjadi habitat bagi ikan air tawar dan berbagai biota air.</p><p>- Menampung air hujan : Membantu mengurangi risiko banjir dan kekeringan.</p></li><li><p><strong>Potensi Wisata Alam dan Rekreasi</strong></p><p>Dengan pemandangan alam yang asri dan suasana yang sejuk, Dam Tanah Putih memiliki potensi besar untuk dikembangkan sebagai tempat wisata alam. Beberapa kegiatan yang bisa dikembangkan di area ini antara lain:</p><p>- Trekking dan jogging di sekitar dam : Menikmati udara segar dan pemandangan indah.</p><p>- Berkemah dan piknik : Cocok untuk keluarga dan komunitas yang ingin menikmati alam.</p><p>- Wisata air : Seperti pemancingan atau wisata perahu kecil yang dapat menarik wisatawan.</p><p>- Spot fotografi alam : Keindahan dam dan sekitarnya menjadi latar yang menarik bagi para fotografer.</p></li><li><p><strong>Potensi Ekonomi dan UMKM Lokal</strong></p><p>Dengan pengembangan dam sebagai destinasi wisata, masyarakat sekitar dapat memanfaatkan peluang usaha seperti:</p><p>- Warung makan dan jajanan tradisional : Menyediakan makanan khas Bali bagi wisatawan.</p><p>- Jasa penyewaan alat rekreasi : Seperti pancing atau perahu kecil.</p><p>- Produk kerajinan tangan dan suvenir : Oleh-oleh khas Darmasaba yang menarik bagi pengunjung.</p></li><li><p><strong>Pengembangan Konservasi dan Edukasi Lingkungan</strong></p><p>Dam Tanah Putih juga bisa menjadi tempat edukasi lingkungan dengan konsep konservasi, di mana pengunjung bisa belajar tentang:</p><p>- Pengelolaan sumber daya air yang berkelanjutan.</p><p>- Keanekaragaman hayati di sekitar dam.</p><p>- Pentingnya ekosistem perairan bagi pertanian dan kehidupan masyarakat.</p></li></ol><p>Dengan berbagai fungsi dan keindahannya, Dam Tanah Putih memiliki potensi besar untuk dikembangkan sebagai destinasi wisata alam, rekreasi, serta edukasi lingkungan. Dengan pengelolaan yang baik dan dukungan dari masyarakat serta pemerintah desa, dam ini dapat menjadi aset penting bagi Darmasaba, baik dari sisi ekonomi maupun kelestarian lingkungan.</p>"
|
||||
"deskripsi": "<p>Bendungan lokal yang berfungsi sebagai sumber irigasi pertanian.</p>",
|
||||
"content": "<p>Dam Tanah Putih merupakan bendungan yang mendukung pengairan sawah dan lahan pertanian di Desa Darmasaba, yang membantu peningkatan produktivitas pertanian masyarakat setempat.</p>",
|
||||
"kategoriId": "cmk3pmwq10008vn9bqdquv153",
|
||||
"imageId": "cmk3t8ha3000tvn9bkqsty6sq"
|
||||
},
|
||||
{
|
||||
"id": "cmdyku9qh000mvn6ft76322sv",
|
||||
"id": "cmk3tlrkb000yvn9bo1vse4c5",
|
||||
"name": "UMKM",
|
||||
"deskripsi": "Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam sektor Usaha Mikro, Kecil, dan Menengah (UMKM). Keberadaan UMKM di desa ini tidak hanya menjadi motor penggerak ekonomi lokal, tetapi juga mendukung pelestarian budaya dan kearifan lokal melalui berbagai produk unggulan.",
|
||||
"content": "<p>Desa Darmasaba, yang terletak di Kecamatan Abiansemal, Kabupaten Badung, memiliki potensi besar dalam sektor Usaha Mikro, Kecil, dan Menengah (UMKM). Keberadaan UMKM di desa ini tidak hanya menjadi motor penggerak ekonomi lokal, tetapi juga mendukung pelestarian budaya dan kearifan lokal melalui berbagai produk unggulan.</p><p>Potensi dan Peran UMKM:</p><ol><li><p><strong>Kerajinan Tangan dan Produk Lokal</strong></p><p>Darmasaba memiliki banyak pengrajin yang menghasilkan produk unik dengan nilai seni tinggi, seperti:</p><p>- Genteng dan bahan bangunan tradisional : Genteng khas Darmasaba yang berkualitas tinggi.</p><p>- Kerajinan anyaman dan ukiran : Produk berbasis rotan dan kayu yang banyak diminati pasar lokal dan internasional.</p><p>- Pakaian adat dan kain tradisional : Mendukung pelestarian budaya Bali.</p></li><li><p><strong>Industri Kuliner Khas Darmasaba</strong></p><p>Kuliner khas desa ini memiliki potensi besar untuk dikembangkan sebagai bisnis UMKM, seperti:</p><p>- Babi Guling : Salah satu kuliner favorit yang banyak diminati wisatawan.</p><p>- Jajanan tradisional Bali : Seperti laklak, jaja uli, dan klepon yang masih dibuat dengan cara tradisional.</p><p>- Olahan ikan lele : Seperti abon lele, lele asap, dan pecel lele yang memiliki pasar luas.</p></li><li><p><strong>UMKM Berbasis Pengolahan Pangan</strong></p><p>Beberapa UMKM di Darmasaba mengolah hasil pertanian dan peternakan menjadi produk bernilai tambah, seperti:</p><p>- Keripik singkong dan pisang : Camilan sehat berbasis bahan lokal.</p><p>- Olahan kelapa : Seperti minyak kelapa murni dan gula aren.</p><p>- Produk herbal dan jamu : Menggunakan bahan-bahan alami dari tanaman lokal.</p></li><li><p><strong>Dukungan dan Pengembangan UMKM</strong></p><p>Agar UMKM di Darmasaba semakin berkembang, perlu adanya:</p><p>- Pelatihan dan pendampingan usaha : Untuk meningkatkan kualitas produk dan manajemen usaha.</p><p>- Pemasaran digital : Menggunakan media sosial dan e-commerce untuk menjangkau pasar lebih luas.</p><p>- Kerja sama dengan BUMDes Pudak Mesari : Untuk membantu akses modal dan pengelolaan bisnis yang lebih profesional.</p></li></ol><p>UMKM di Desa Darmasaba memiliki potensi besar dalam berbagai sektor, mulai dari kerajinan tangan, kuliner, hingga wisata berbasis masyarakat. Dengan inovasi, pemasaran yang lebih luas, dan dukungan dari pemerintah desa serta masyarakat, UMKM Darmasaba dapat berkembang pesat dan menjadi tulang punggung perekonomian desa.</p>"
|
||||
"deskripsi": "<p>Usaha mikro, kecil, dan menengah masyarakat Desa Darmasaba.</p>",
|
||||
"content": "<p>UMKM di Darmasaba mencakup berbagai usaha kecil produktif seperti kuliner, kerajinan tangan, dan jasa lainnya, yang menjadi pilar kegiatan ekonomi dan pemberdayaan masyarakat lokal.</p>",
|
||||
"kategoriId": "cmk3s1ks6000ivn9bcrv960ko",
|
||||
"imageId": "cmk3tlrdl000wvn9b67jkppzh"
|
||||
},
|
||||
{
|
||||
"id": "cmk3tzpku0011vn9beq2kzyen",
|
||||
"name": "Kawasan Kuliner",
|
||||
"deskripsi": "<p>Wilayah yang menyediakan berbagai pilihan kuliner khas lokal Desa Darmasaba.</p>",
|
||||
"content": "<p>Kawasan kuliner di Darmasaba menjadi tempat berkumpul dan menikmati makanan khas lokal, mendukung kegiatan ekonomi UMKM kuliner desa serta daya tarik wisata kuliner.</p>",
|
||||
"kategoriId": "cmk3s1ks6000ivn9bcrv960ko",
|
||||
"imageId": "cmk3tzpd7000zvn9bu519xrjk"
|
||||
},
|
||||
{
|
||||
"id": "cmk3u7gzh0014vn9brvlz3fjp",
|
||||
"name": "IKM berbasis Pengolahan Pangan",
|
||||
"deskripsi": "<p>Industri kecil menengah yang fokus pada pengolahan produk pangan lokal.</p>",
|
||||
"content": "<p>IKM berbasis pengolahan pangan di desa mencakup produksi makanan olahan yang dihasilkan oleh masyarakat setempat, memperkaya nilai tambah hasil pertanian lokal dan menciptakan peluang bisnis.</p>",
|
||||
"kategoriId": "cmk3s1ks6000ivn9bcrv960ko",
|
||||
"imageId": "cmk3u7gsg0012vn9bhqhsw5g6"
|
||||
},
|
||||
{
|
||||
"id": "cmk3udr960017vn9bq33ce6mw",
|
||||
"name": "Peternakan Ikan Lele",
|
||||
"deskripsi": "<p>Usaha budidaya ikan lele sebagai potensi perikanan desa.</p>",
|
||||
"content": "<p>Peternakan ikan lele menjadi salah satu bentuk usaha budidaya perikanan di Darmasaba, memberikan sumber pendapatan tambahan bagi petani ikan dan diversifikasi komoditas desa.</p>",
|
||||
"kategoriId": "cmk3s1ks6000ivn9bcrv960ko",
|
||||
"imageId": "cmk3udr2s0015vn9bqn9hxdxx"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
[
|
||||
{
|
||||
"id": "edit",
|
||||
"biodata": "<p>I.B Surya Prabhawa Manuaba, S.H., M.H., adalah Perbekel Darmasaba periode 2021-2027, seorang advokat, pendiri Mantra Legal Consultants & Advocates, serta aktif di bidang musik dan akademis. Dia menempuh pendidikan hukum di Universitas Udayana dan Universitas Mahasaraswati Denpasar serta memiliki pengalaman luas di berbagai organisasi dan kepemimpinan.</p>",
|
||||
"pengalaman": "<ul><li>2021 - 2027: Perbekel Desa Darmasaba</li><li>2015 - Sekarang: Founder & Managing Director Mantra Legal Consultants & Advocates</li><li>2020 - Sekarang: Founder Ugawa Record Music Studio</li><li>2010 - 2016: Dosen Fakultas Hukum Universitas Mahasaraswati Denpasar</li></ul>",
|
||||
"pengalamanOrganisasi": "<ul> <li>1996 – 1997: Ketua OSIS SMP Negeri 1 Abiansemal</li><li>1999 – 2000: Ketua OSIS SMA Negeri 1 Mengwi</li> <li>2008 – 2009: Ketua BEM Universitas Mahasaraswati Denpasar</li> <li>2008 – 2010: Ketua Sekaa Taruna Sila Dharma, Banjar Tengah, Desa Adat Tegal, Darmasaba</li> <li>2020 – Sekarang: Pengurus Young Lawyer Committee Peradi Denpasar</li> <li>2021 – Sekarang: Dewan Kehormatan Himpunan Pengusaha Muda Indonesia (HIPMI) Badung</li> <li>2023 – 2028: Komite Tetap Advokasi – Bidang Hukum dan Regulasi Kamar Dagang dan Industri Badung</li> </ul>",
|
||||
"programUnggulan": "<h3>Pemberdayaan Ekonomi dan UMKM</h3> <ul> <li>Pelatihan dan pendampingan UMKM lokal</li> <li>Program bantuan modal usaha bagi pelaku usaha kecil</li><li>Digitalisasi UMKM untuk meningkatkan pemasaran produk lokal</li></ul>"
|
||||
}
|
||||
]
|
||||
{
|
||||
"id": "edit",
|
||||
"biodata": "<p>I.B Surya Prabhawa Manuaba, S.H., M.H., adalah Perbekel Darmasaba periode 2021-2027, seorang advokat, pendiri Mantra Legal Consultants & Advocates, serta aktif di bidang musik dan akademis. Dia menempuh pendidikan hukum di Universitas Udayana dan Universitas Mahasaraswati Denpasar serta memiliki pengalaman luas di berbagai organisasi dan kepemimpinan.</p>",
|
||||
"pengalaman": "<ul><li>2021 - 2027: Perbekel Desa Darmasaba</li><li>2015 - Sekarang: Founder & Managing Director Mantra Legal Consultants & Advocates</li><li>2020 - Sekarang: Founder Ugawa Record Music Studio</li><li>2010 - 2016: Dosen Fakultas Hukum Universitas Mahasaraswati Denpasar</li></ul>",
|
||||
"pengalamanOrganisasi": "<ul> <li>1996 – 1997: Ketua OSIS SMP Negeri 1 Abiansemal</li><li>1999 – 2000: Ketua OSIS SMA Negeri 1 Mengwi</li> <li>2008 – 2009: Ketua BEM Universitas Mahasaraswati Denpasar</li> <li>2008 – 2010: Ketua Sekaa Taruna Sila Dharma, Banjar Tengah, Desa Adat Tegal, Darmasaba</li> <li>2020 – Sekarang: Pengurus Young Lawyer Committee Peradi Denpasar</li> <li>2021 – Sekarang: Dewan Kehormatan Himpunan Pengusaha Muda Indonesia (HIPMI) Badung</li> <li>2023 – 2028: Komite Tetap Advokasi – Bidang Hukum dan Regulasi Kamar Dagang dan Industri Badung</li> </ul>",
|
||||
"programUnggulan": "<h3>Pemberdayaan Ekonomi dan UMKM</h3> <ul> <li>Pelatihan dan pendampingan UMKM lokal</li> <li>Program bantuan modal usaha bagi pelaku usaha kecil</li><li>Digitalisasi UMKM untuk meningkatkan pemasaran produk lokal</li></ul>",
|
||||
"imageId": "cmk4xh3gz0000vnwqjy4zfr3w"
|
||||
}
|
||||
]
|
||||
|
||||
65
prisma/data/desa/profile/profile-perbekel-lalu.json
Normal file
65
prisma/data/desa/profile/profile-perbekel-lalu.json
Normal file
@@ -0,0 +1,65 @@
|
||||
[
|
||||
{
|
||||
"id": "cmk4yswiy0003vnwq97wem6t2",
|
||||
"nama": "Si Gede Kania",
|
||||
"periode": "Tahun 1943 - 1946",
|
||||
"imageId": "cmk4yswge0001vnwqgfieunnm",
|
||||
"daerah": "Perbekel Tegal"
|
||||
},
|
||||
{
|
||||
"id": "cmk4yvuxg0006vnwqa0nk7jcx",
|
||||
"nama": "Si Gede Gandem",
|
||||
"periode": "Tahun 1946 - 1950",
|
||||
"imageId": "cmk4yvuv20004vnwqqq8ifxq8",
|
||||
"daerah": "Perbekel Tegal"
|
||||
},
|
||||
{
|
||||
"id": "cmk4ywml70009vnwqnnrp0cpq",
|
||||
"nama": "I Wayan Sama",
|
||||
"periode": "Tahun 1950 - 1953",
|
||||
"imageId": "cmk4ywmiu0007vnwq5ea5jz1z",
|
||||
"daerah": "Perbekel Tegal"
|
||||
},
|
||||
{
|
||||
"id": "cmk4yzix3000cvnwqn2pa57gs",
|
||||
"nama": "I Wayan Nambreg",
|
||||
"periode": "Tahun 1950 - 1960",
|
||||
"imageId": "cmk4yzitx000avnwq7479tvxb",
|
||||
"daerah": "Perbekel Darmasaba"
|
||||
},
|
||||
{
|
||||
"id": "cmk4z0sgd000fvnwqlgjasqx9",
|
||||
"nama": "Ida Bagus Putu Oka",
|
||||
"periode": "Tahun 1953 - 1974",
|
||||
"imageId": "cmk4z0scm000dvnwqlntylzyk",
|
||||
"daerah": "Perbekel Tegal / Darmasaba"
|
||||
},
|
||||
{
|
||||
"id": "cmk4z1w8m000ivnwqe68w5vjg",
|
||||
"nama": "I Nyoman Patra",
|
||||
"periode": "Tahun 1974 - 1991",
|
||||
"imageId": "cmk4z1vqs000gvnwqj579yvnt",
|
||||
"daerah": "Perbekel Darmasaba"
|
||||
},
|
||||
{
|
||||
"id": "cmk4z2y3u000lvnwq7fbr3paz",
|
||||
"nama": "I Made Rudja",
|
||||
"periode": "Tahun 1991 - 2007",
|
||||
"imageId": "cmk4z2y0r000jvnwq2jgrs565",
|
||||
"daerah": "Perbekel Darmasaba"
|
||||
},
|
||||
{
|
||||
"id": "cmk4z6esm000ovnwqacek3jlx",
|
||||
"nama": "I Wayan Kaler, S.H., M.H.",
|
||||
"periode": "Tahun 2007 - 2013",
|
||||
"imageId": "cmk4z6epq000mvnwq81dfq29b",
|
||||
"daerah": "Perbekel Darmasaba"
|
||||
},
|
||||
{
|
||||
"id": "cmk4z7e77000rvnwqo2wkmbwl",
|
||||
"nama": "I Made Taram, S.H.",
|
||||
"periode": "Tahun 2013 - 2019",
|
||||
"imageId": "cmk4z7e5g000pvnwq7k45e366",
|
||||
"daerah": "Perbekel Darmasaba"
|
||||
}
|
||||
]
|
||||
@@ -1,8 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "650e8400-e29b-41d4-a716-446655440001",
|
||||
"atasanId": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"bawahanId": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"tipe": "Langsung Melapor"
|
||||
}
|
||||
]
|
||||
91
prisma/data/ekonomi/struktur-organisasi/pegawai-bumdes.json
Normal file
91
prisma/data/ekonomi/struktur-organisasi/pegawai-bumdes.json
Normal file
@@ -0,0 +1,91 @@
|
||||
[
|
||||
{
|
||||
"id": "cmgewz4gt000704ib91i3f169",
|
||||
"namaLengkap": "Ida Bagus Surya Prabhawa Manuaba, S.H.,M.H., NL.P.",
|
||||
"gelarAkademik": "S.H.,M.H.,NL.P.",
|
||||
"tanggalMasuk": "2020-01-01T00:00:00.000Z",
|
||||
"email": "bagus@desa.id",
|
||||
"telepon": "081234567891",
|
||||
"alamat": "Jl. Raya Desa No. 1",
|
||||
"posisiId": "kepala_desa",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "cmgewxfvw000004ibee5013f4",
|
||||
"namaLengkap": "I Ketut Suwanta",
|
||||
"gelarAkademik": "S.Pt",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "suwanta@desa.id",
|
||||
"telepon": "081234567892",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "sekretaris_desa",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "cmgewxvqw000104ibgm5l8fzs",
|
||||
"namaLengkap": "Ni Wayan Supardiati",
|
||||
"gelarAkademik": "S.Pd",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "supardiati@desa.id",
|
||||
"telepon": "081234567892",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kaur_keuangan",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "cmgewy1g9000204ib2n7hbx0i",
|
||||
"namaLengkap": "I Wayan Agus Juni Artha Saputra",
|
||||
"gelarAkademik": "S.T.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "agus@desa.id",
|
||||
"telepon": "081234567892",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kadus_banjar_dinas_menesa",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "cmgewybah000304ibgqhn1gm2",
|
||||
"namaLengkap": "I Wayan Sueca",
|
||||
"gelarAkademik": "S.H.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "sueca@desa.id",
|
||||
"telepon": "081234567893",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kadus_banjar_dinas_darmasaba",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "cmgewygqz000404ib20sv8nvg",
|
||||
"namaLengkap": "Si Gede Ketut Astawa",
|
||||
"gelarAkademik": "S.T.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "astawa@desa.id",
|
||||
"telepon": "081234567893",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kadus_banjar_dinas_bucu",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "cmgewyos1000504ibcu8o2gyk",
|
||||
"namaLengkap": "I Kadek Arya Minarta",
|
||||
"gelarAkademik": "S.T.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "minarta@desa.id",
|
||||
"telepon": "081234567893",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kadus_banjar_dinas_gulingan",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "cmgewyxk7000604ib8djs3i6c",
|
||||
"namaLengkap": "I Gede Andika Pradnya Diputra",
|
||||
"gelarAkademik": "S.E.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "diputra@desa.id",
|
||||
"telepon": "081234567893",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "kadus_banjar_dinas_taman",
|
||||
"isActive": true
|
||||
}
|
||||
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"namaLengkap": "Budi Santoso",
|
||||
"gelarAkademik": "S.IP",
|
||||
"tanggalMasuk": "2020-01-01T00:00:00.000Z",
|
||||
"email": "budi@desa.id",
|
||||
"telepon": "081234567891",
|
||||
"alamat": "Jl. Raya Desa No. 1",
|
||||
"posisiId": "kepala_desa",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"namaLengkap": "Ani Lestari",
|
||||
"gelarAkademik": "S.Pd",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "ani@desa.id",
|
||||
"telepon": "081234567892",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "sekretaris_desa",
|
||||
"isActive": true
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,159 @@
|
||||
[
|
||||
[
|
||||
{
|
||||
"id": "kepala_desa",
|
||||
"nama": "Kepala Desa",
|
||||
"deskripsi": "Pemimpin desa Darmasaba",
|
||||
"hierarki": 1,
|
||||
"parentId": null
|
||||
},
|
||||
{
|
||||
"id": "kepala_urusan",
|
||||
"nama": "Kepala Urusan",
|
||||
"deskripsi": "Pemimpin urusan desa Darmasaba",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "sekretaris_desa",
|
||||
"nama": "Sekretaris Desa",
|
||||
"deskripsi": "Pengelola administrasi desa",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "kaur_keuangan",
|
||||
"nama": "Kaur Keuangan",
|
||||
"deskripsi": "Pengelola keuangan desa",
|
||||
"hierarki": 3,
|
||||
"parentId": "kaur_umum"
|
||||
},
|
||||
{
|
||||
"id": "kaur_perencanaan",
|
||||
"nama": "Kaur Perencanaan",
|
||||
"deskripsi": "Penyusun program kerja desa",
|
||||
"hierarki": 3,
|
||||
"parentId": "kaur_umum"
|
||||
},
|
||||
{
|
||||
"id": "kaur_umum",
|
||||
"nama": "Kaur Umum & TU",
|
||||
"deskripsi": "Pelayanan umum dan administrasi",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "kasi_pemerintahan",
|
||||
"nama": "Kasi Pemerintahan",
|
||||
"deskripsi": "Urusan pemerintahan dan keamanan",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "kasi_pelayanan",
|
||||
"nama": "Kasi Pelayanan",
|
||||
"deskripsi": "Urusan pelayanan masyarakat",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "kasi_kesejahteraan",
|
||||
"nama": "Kasi Kesejahteraan",
|
||||
"deskripsi": "Urusan sosial dan kesejahteraan",
|
||||
"hierarki": 2,
|
||||
"parentId": "kepala_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_cabe",
|
||||
"nama": "Kepala Dusun Banjar Dinas Cabe",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Cabe",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_menesa",
|
||||
"nama": "Kepala Dusun Banjar Dinas Menesa",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Menesa",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_penenjoan",
|
||||
"nama": "Kepala Dusun Banjar Dinas Penenjoan",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Penenjoan",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_telanga",
|
||||
"nama": "Kepala Dusun Banjar Dinas Telanga",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Telanga",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_tengah",
|
||||
"nama": "Kepala Dusun Banjar Dinas Tengah",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Tengah",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_baler_pasar",
|
||||
"nama": "Kepala Dusun Banjar Dinas Baler Pasar",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Baler Pasar",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_bucu",
|
||||
"nama": "Kepala Dusun Banjar Dinas Bucu",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Bucu",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_gulingan",
|
||||
"nama": "Kepala Dusun Banjar Dinas Gulingan",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Gulingan",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_bersih",
|
||||
"nama": "Kepala Dusun Banjar Dinas Bersih",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Bersih",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_umahanyar",
|
||||
"nama": "Kepala Dusun Banjar Dinas Umahanyar",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Umahanyar",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_taman",
|
||||
"nama": "Kepala Dusun Banjar Dinas Taman",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Taman",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "kadus_banjar_dinas_darmasaba",
|
||||
"nama": "Kepala Dusun Banjar Dinas Darmasaba",
|
||||
"deskripsi": "Pimpinan wilayah Banjar Dinas Darmasaba",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
},
|
||||
{
|
||||
"id": "staf_desa",
|
||||
"nama": "Staf Desa",
|
||||
"deskripsi": "Staf Desa",
|
||||
"hierarki": 3,
|
||||
"parentId": "sekretaris_desa"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "kepala_desa",
|
||||
"nama": "Kepala Desa",
|
||||
"deskripsi": "Kepala Desa",
|
||||
"hierarki": 1
|
||||
},
|
||||
{
|
||||
"id": "sekretaris_desa",
|
||||
"nama": "Sekretaris Desa",
|
||||
"deskripsi": "Sekretaris Desa",
|
||||
"hierarki": 2
|
||||
},
|
||||
{
|
||||
"id": "bendahara_desa",
|
||||
"nama": "Bendahara Desa",
|
||||
"deskripsi": "Bendahara Desa",
|
||||
"hierarki": 3
|
||||
},
|
||||
{
|
||||
"id": "staff_umum",
|
||||
"nama": "Staff Umum",
|
||||
"deskripsi": "Staff Umum",
|
||||
"hierarki": 4
|
||||
}
|
||||
]
|
||||
|
||||
30
prisma/data/fetchWithRetry.ts
Normal file
30
prisma/data/fetchWithRetry.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export default async function fetchWithRetry(
|
||||
url: string,
|
||||
retries = 3,
|
||||
timeoutMs = 20000
|
||||
) {
|
||||
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
||||
|
||||
try {
|
||||
const res = await fetch(url, { signal: controller.signal });
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.warn(`⚠️ Download attempt ${attempt} failed`);
|
||||
|
||||
if (attempt === retries) {
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Unreachable");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
||||
[
|
||||
{ "name": "Semua" },
|
||||
{ "name": "Pemerintahan" },
|
||||
{ "name": "Pembangunan" },
|
||||
{ "name": "Ekonomi" },
|
||||
{ "name": "Sosial" },
|
||||
{ "name": "Budaya" },
|
||||
{ "name": "Teknologi" }
|
||||
]
|
||||
10
prisma/data/kesehatan/posyandu/posyandu.json
Normal file
10
prisma/data/kesehatan/posyandu/posyandu.json
Normal file
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"id": "cmkanjnmx0006vntz1cn7owpb",
|
||||
"name": "Posyandu Pudak Amara",
|
||||
"nomor": "(0361) 8463263",
|
||||
"deskripsi": "<p>Posyandu Pudak Amara merupakan salah satu posyandu aktif di Desa Darmasaba dan pernah berkompetisi dalam lomba kader dan posyandu berprestasi tingkat Provinsi Bali tahun 2025.</p><p>Kegiatan ini melibatkan kader posyandu serta didampingi pihak desa dan puskesmas setempat untuk meningkatkan pelayanan kesehatan ibu dan anak.</p>",
|
||||
"jadwalPelayanan": "<p>Setiap bulan pada satu hari tertentu (mis. minggu ke-2): 08:00 – 12:00 WITA (posyandu balita & ibu hamil)</p>",
|
||||
"imageId": "cmkanjnfh0004vntz8cdbxa7f"
|
||||
}
|
||||
]
|
||||
@@ -1,20 +1,30 @@
|
||||
[
|
||||
{
|
||||
"id": "cmdwrrxkh0005vnd3p5rxkiev",
|
||||
"name": "Tim Bola Voli Putri Dharma Temaja meraih juara 3 dalam Turnamen Bola Voli Mangupura Cup 2024 kategori Putri Se-Bali",
|
||||
"deskripsi": "<p>Tim Bola Voli Putri Dharma Temaja meraih juara 3 dalam Turnamen Bola Voli Mangupura Cup 2024 kategori Putri Se-Bali</p>",
|
||||
"kategoriId": "cmdwrolsl0000vnd3e24q5440"
|
||||
"id": "cmdws0sgq000bvnd32o7m94im",
|
||||
"name": "Desa Darmasaba Raih Juara 1 Lomba Desa Tingkat Provinsi Bali Tahun 2025",
|
||||
"deskripsi": "<p>Pemerintah Desa Darmasaba meraih prestasi gemilang dengan menyabet Juara 1 Lomba Desa Tingkat Provinsi Bali Tahun 2025 dalam Peringatan Hari Jadi Pemerintah Provinsi Bali ke-67. Inovasi pelayanan publik dan sinergi masyarakat menjadi kunci keberhasilan.</p>",
|
||||
"kategoriId": "cmdwrp0pr0002vnd35w6nkjh0",
|
||||
"imageId": "cmkamki230003vntzg25voz53"
|
||||
},
|
||||
{
|
||||
"id": "cmdwrzs740008vnd329ysez5x",
|
||||
"name": "Prestasi Juara 3 dalam Lomba Keluarga Sadar Hukum Kabupaten Badung Tahun 2024",
|
||||
"deskripsi": "<p>Prestasi Juara 3 dalam Lomba Keluarga Sadar Hukum Kabupaten Badung Tahun 2024</p>",
|
||||
"kategoriId": "cmdwrot900001vnd30b5kj96g"
|
||||
"name": "Desa Darmasaba Raih Juara 1 Lomba Desa Tingkat Kabupaten Badung Tahun 2025",
|
||||
"deskripsi": "<p>Desa Darmasaba berhasil meraih Juara 1 Lomba Desa Tingkat Kabupaten Badung Tahun 2025 yang diserahkan langsung oleh Bupati Badung dalam rangka memperingati HUT RI ke-80. Prestasi ini menjadi simbol kerja keras dan inovasi desa.</p>",
|
||||
"kategoriId": "cmdwrp0pr0002vnd35w6nkjh0",
|
||||
"imageId": "cmkamkb150002vntzklhz9ryx"
|
||||
},
|
||||
{
|
||||
"id": "cmdws0sgq000bvnd32o7m94im",
|
||||
"name": "Peringkat 5 Dalam Ajang Bergengsi Mangupura Award",
|
||||
"deskripsi": "<p>Peringkat 5 Dalam Ajang Bergengsi Mangupura Award</p>",
|
||||
"kategoriId": "cmdwrp0pr0002vnd35w6nkjh0"
|
||||
"id": "cmdwrrxkh0005vnd3p5rxkiev",
|
||||
"name": "TPS3R Pudak Mesari Desa Darmasaba Raih Juara 2 dalam Kompetisi TPS3R Kabupaten Badung",
|
||||
"deskripsi": "<p>TPS3R Pudak Mesari Desa Darmasaba meraih Juara 2 dalam Kompetisi TPS3R tingkat Kabupaten Badung pada acara HUT Mangupura ke-16 sebagai wujud komitmen pengelolaan sampah berbasis masyarakat.</p>",
|
||||
"kategoriId": "cmdwrp0pr0002vnd35w6nkjh0",
|
||||
"imageId": "cmkamk2cf0001vntzjg4rzqep"
|
||||
},
|
||||
{
|
||||
"id": "cmk3nxfkd0005vn9bcw8d10sh",
|
||||
"name": "Keluarga Sadar Hukum Desa Darmasaba Raih Prestasi Gemilang (Juara 3)",
|
||||
"deskripsi": "<p>KADARKUM Desa Darmasaba berhasil meraih Juara 3 dalam Lomba Keluarga Sadar Hukum Kabupaten Badung tahun 2024, bukti semangat kolaborasi masyarakat dengan perangkat desa dalam pendidikan hukum.</p>",
|
||||
"kategoriId": "cmdwrp0pr0002vnd35w6nkjh0",
|
||||
"imageId": "cmkamji130000vntz3loca9q2"
|
||||
}
|
||||
]
|
||||
@@ -1,38 +1,26 @@
|
||||
[
|
||||
{
|
||||
"id": "cmds8w2q60002vnbe6i8qhkuo",
|
||||
"name": "Telephone Desa Darmasaba",
|
||||
"iconUrl": "081239580000",
|
||||
"imageId": "cmff3nv180003vn6h5jvedidq"
|
||||
},
|
||||
{
|
||||
"id": "cmds8z7u20005vnbegyyvnbk0",
|
||||
"name": "Email Desa Darmasaba",
|
||||
"iconUrl": "desadarmasaba@badungkab.go.id",
|
||||
"imageId": "cmff3ll130001vn6hkhls3f5y"
|
||||
},
|
||||
{
|
||||
"id": "cmds9023u0008vnbe3oxmhwyf",
|
||||
"name": "Desa Darmasaba",
|
||||
"iconUrl": "https://www.youtube.com/channel/UCtPw9WOQO7d2HIKzKgel4Xg",
|
||||
"imageId": "cmff3joae0000vn6h8sgs0ilg"
|
||||
"imageId": "cmk2cgqgm0003vn96jun52pik"
|
||||
},
|
||||
{
|
||||
"id": "cmds90oul000bvnbe2bqkptoi",
|
||||
"name": "Pemerintah Desa Darmasaba",
|
||||
"iconUrl": "https://www.facebook.com/DarmasabaDesaku",
|
||||
"imageId": "cmff3mtat0002vn6hs8vyyhdd"
|
||||
"imageId": "cmk2cmr000006vn96qepq6gvl"
|
||||
},
|
||||
{
|
||||
"id": "cmds91i4e000evnbe8gtf1gub",
|
||||
"name": "ddarmasaba",
|
||||
"iconUrl": "https://www.instagram.com/ddarmasaba/",
|
||||
"imageId": "cmff3oouh0004vn6hd94brzv9"
|
||||
"imageId": "cmk2cpeba0009vn966jcrpf3u"
|
||||
},
|
||||
{
|
||||
"id": "cmds92de5000hvnbemlu6sq5x",
|
||||
"name": "desa.darmasaba",
|
||||
"iconUrl": "https://www.tiktok.com/@desa.darmasaba?is_from_webapp=1&sender_device=pc",
|
||||
"imageId": "cmff3q12g0005vn6h5ojov2qa"
|
||||
"imageId": "cmk2crcl1000cvn96j8pmgmo5"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"id": "edit",
|
||||
"name": "I.B Surya Prabhawa Manuaba, S.H., M.H.",
|
||||
"position": "Perbekel Darmasaba periode 2021-2027",
|
||||
"imageId": "cmff2w5ly000avn0telhct71k"
|
||||
"imageId": "cmk2a2dl6001nvngck1n0k8qc"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,51 +1,58 @@
|
||||
[
|
||||
{
|
||||
"id": "cmdr755pf0005vn5rp8tyuubw",
|
||||
"name": "Dmangan",
|
||||
"description": "Darmasaba Aman Pangan",
|
||||
"link": "https://darmasaba.desa.id/berita/61452-kader-d-mangan-berhasil-meraih-prestasi-dalam-ajang-lomba-banjar-bali-quis-bbq-tahun-2024",
|
||||
"imageId" : "cmff0z34f0005vn0tjtvq519p"
|
||||
"id": "cmdr77vbw000bvn5rvpmoq31s",
|
||||
"name": "Bares",
|
||||
"description": "<p>BARES (Darmasaba Recycling Waste Stock/Exchange) adalah Bursa Sampah yang merupakan sebuah sistem inovatif untuk mengelola dan menukar sampah daur ulang menjadi nilai ekonomis melalui TPS3R Pudak Mesari, dengan tujuan memberdayakan masyarakat dan menjaga kebersihan lingkungan, menjadikannya bagian dari upaya lebih besar seperti program desa pintar (smart village) dan pengelolaan sampah terintegrasi.</p>",
|
||||
"link": "https://darmasaba.desa.id/berita/56722-bares#:~:text=Sampah%20jadi%20uang%20nie%20Semeton,masing%20banjar%20se%2DDesa%20Darmasaba.",
|
||||
"imageId" : "cmk20mg320000vnevxy0k73fr"
|
||||
},
|
||||
{
|
||||
"id": "cmdr76nqk0008vn5rdddvcxnr",
|
||||
"name": "Bicara Darmasaba",
|
||||
"description": "Bicara Darmasaba",
|
||||
"link": "https://darmasaba.desa.id/berita/42506-bicara-darmasaba",
|
||||
"imageId" : "cmff0tnf00003vn0t3kgzi0u0"
|
||||
"description": "<p>Bicara Darmasaba adalah program ruang dialog terbuka yang digagas Pemerintah Desa Darmasaba sebagai forum strategis untuk menggali ide, menyampaikan aspirasi masyarakat, serta merangkai aksi nyata dalam menyelesaikan persoalan desa, seperti pengelolaan sampah. Kegiatan ini diselenggarakan secara berkala dengan pendekatan partisipatif dan ditayangkan secara live streaming agar transparan dan melibatkan banyak pihak, sehingga mendukung tata kelola desa yang responsif terhadap kebutuhan warga.</p>",
|
||||
"link": "https://badungkab.go.id/kab/berita/67159-bicara-darmasaba-bahas-berbagai-persoalan-warga-didorong-berani-speak-up-sampaikan-aspirasi-dan-cari-solusi-nyata",
|
||||
"imageId" : "cmk20nqmu0001vnevfte29rk0"
|
||||
},
|
||||
{
|
||||
"id": "cmdr77vbw000bvn5rvpmoq31s",
|
||||
"name": "Bares",
|
||||
"description": "Darmasaba Recycling Stock/Exchange",
|
||||
"link": "http://darmasaba.desa.id/berita/56722-bares",
|
||||
"imageId" : "cmff0rr4z0002vn0twp333m2"
|
||||
"id": "cmdr755pf0005vn5rp8tyuubw",
|
||||
"name": "Dmangan",
|
||||
"description": "<p>Dmangan (Darmasaba Aman Pangan) adalah program inovasi Desa Darmasaba yang fokus pada ketahanan pangan masyarakat desa melalui pengembangan produksi hasil pangan lokal, peningkatan kemandirian pangan, serta pembinaan dan pemberdayaan warga dalam praktik pertanian dan pangan berkelanjutan.</p>",
|
||||
"link": "https://darmasaba.desa.id/berita/51787-desa-darmasaba-juara-1-lomba-desa-pangan-aman-provinsi-bali-tahun-2023#:~:text=Salah%20satu%20program%20inovasi%20Pemdes%20Darmasaba%20yaitu,Darmasaba%20dan%20seluruh%20stakeholder%20terkait%20yang%20telah",
|
||||
"imageId" : "cmk20nyen0002vnevd0hfr3u8"
|
||||
},
|
||||
{
|
||||
"id": "cmdr7bxtp000evn5rmy85wihx",
|
||||
"name": "Sajjana Dharma Raksaka",
|
||||
"description": "Sajjana Dharma Raksaka",
|
||||
"link": "https://ppid.badungkab.go.id/storage/dokumen/5RS9dldGkrgzMQq6bKdZsqsVRHI8gffWv4PGfb3r.pdf",
|
||||
"imageId" : "cmff10cwq0009vn0tse8dzu3j"
|
||||
},
|
||||
{
|
||||
"id": "cmdr7dlnk000hvn5r9lur3z35",
|
||||
"name": "PDKT",
|
||||
"description": "Perangkat Desa Kuat Teknologi",
|
||||
"link": "https://darmasaba.desa.id/berita/53752-p-d-k-t",
|
||||
"imageId" : "cmff1013m0008vn0th7t0d64d"
|
||||
"id": "cmk228ust0009vnev5p8i377o",
|
||||
"name": "Davest",
|
||||
"description": "<p>DAVEST (Darmasaba Investment) merupakan program inovasi Desa Darmasaba yang bertujuan mempromosikan potensi investasi desa secara terintegrasi melalui media digital dan pendampingan langsung. Program ini menjadi sarana penghubung antara pemerintah desa, pelaku usaha, dan investor dalam rangka mendorong pertumbuhan ekonomi desa yang berkelanjutan.</p><p>DAVEST menyajikan informasi potensi unggulan desa seperti sektor UMKM, pariwisata, ekonomi kreatif, serta peluang investasi berbasis sumber daya lokal dengan prinsip transparansi dan kemudahan akses informasi.</p><p>Di tahun 2024 ini Davest (Darmasaba Village Festival) akan diadakan lagi, dengan berbagai kegiatan pemerdayaan, edukasi dan hiburan yang tentunya lebih waahhhh dari dua tahun lalu. Untuk memantapkan hal tersebut, Pemdes Darmasaba melakukan rapat koordinasi (rakor) Davest 2024 yang dipimpin langsung oleh Perbekel Darmasaba I. B. Surya Prabhawa Manuaba, S.H.,M.H. pada hari Senin (22/1/2024) bertempat di Ruang Shanti Gosana Kantor Perbekel Darmasaba.</p><hr><h3>Tujuan Program</h3><ul><li><p>Meningkatkan daya tarik investasi di Desa Darmasaba</p></li><li><p>Mempromosikan potensi unggulan desa secara profesional</p></li><li><p>Mendorong pertumbuhan ekonomi dan penciptaan lapangan kerja</p></li><li><p>Mendukung visi Desa Darmasaba sebagai desa inovatif dan berdaya saing</p></li></ul><hr><h3>Sasaran Program</h3><ul><li><p>Calon investor lokal dan regional</p></li><li><p>Pelaku UMKM dan kelompok usaha desa</p></li><li><p>Masyarakat Desa Darmasaba</p></li></ul><hr><h3>Bentuk Inovasi</h3><ul><li><p>Inovasi ekonomi desa</p></li><li><p>Inovasi digital</p></li><li><p>Inovasi tata kelola pelayanan investasi</p></li></ul><hr><h3>Ruang Lingkup Kegiatan</h3><ul><li><p>Penyusunan profil potensi investasi desa</p></li><li><p>Digitalisasi informasi investasi desa</p></li><li><p>Promosi peluang investasi melalui media online</p></li><li><p>Fasilitasi komunikasi antara investor dan desa</p></li><li><p>Pendampingan awal investasi berbasis desa</p></li></ul>",
|
||||
"link": "https://darmasaba.desa.id/berita/55862-rakor-davest-2024",
|
||||
"imageId" : "cmk228urs0007vnevi5b66bqn"
|
||||
},
|
||||
{
|
||||
"id": "cmdr7ftob000mvn5rfhgdtg8v",
|
||||
"name": "GM",
|
||||
"description": "Galah Melah",
|
||||
"link": "https://darmasaba.desa.id/berita/52880-galah-melah",
|
||||
"imageId" : "cmff38cyq000bvn0t9f01cz3f"
|
||||
"description": "<p>Galah Melah (Gerak dan Langkah Memilah Sampah) adalah program inovasi pengelolaan sampah yang dikembangkan oleh Pemerintah Desa Darmasaba melalui TPS3R Pudak Mesari, dengan fokus pada praktik pemisahan sampah dari sumbernya (di rumah, banjar, maupun fasilitas umum).</p>",
|
||||
"link": "https://mcinews.id/2025/09/12/serius-tangani-sampah-desa-darmasaba-terapkan-inovasi-dan-osaki-composting-system-di-tps-3r-pudak-mesari-dari-2023",
|
||||
"imageId" : "cmk20o7mf0003vnevohrksm1d"
|
||||
},
|
||||
{
|
||||
"id": "cmdr7glue000pvn5r6onzslju",
|
||||
"name": "Inovasi Desa Darmasaba",
|
||||
"description": "Inovasi Desa Darmasaba",
|
||||
"link": "https://darmasaba.desa.id/produk-lokal-desa",
|
||||
"imageId" : "cmff0zqvd0007vn0tv6o5hjcq"
|
||||
"description": "<p>Inovasi Desa Darmasaba adalah kumpulan program inovatif yang dikembangkan oleh Pemerintah Desa Darmasaba untuk memperkuat penyelenggaraan pemerintahan desa, pemberdayaan masyarakat, pengelolaan lingkungan, serta peningkatan kualitas sosial-ekonomi dan budaya desa.</p>",
|
||||
"link": "https://mcinews.id/2025/09/11/inovasi-desa-darmasaba-lanjutkan-perjuangan-ke-tingkat-nasional-dan-diakui-negara-tetangga",
|
||||
"imageId" : "cmk20of8m0004vnev9ujy5o0l"
|
||||
},
|
||||
{
|
||||
"id": "cmdr7dlnk000hvn5r9lur3z35",
|
||||
"name": "PDKT",
|
||||
"description": "<p>PDKT (Perangkat Desa Kuat Teknologi) merupakan program inovasi Desa Darmasaba yang dirancang untuk menguatkan kapasitas dan kompetensi perangkat desa dalam memanfaatkan teknologi informasi dan komunikasi guna mendukung tata kelola pemerintahan desa yang modern, transparan, cepat, dan responsif.</p>",
|
||||
"link": "https://darmasaba.desa.id/berita/67168-desa-darmasaba-masuk-5-besar-nasional-dalam-ajang-pemerintah-desa-dan-kelurahan-award-2025",
|
||||
"imageId" : "cmk20omzq0005vnevgi6f4edu"
|
||||
},
|
||||
{
|
||||
"id": "cmdr7bxtp000evn5rmy85wihx",
|
||||
"name": "Sajjana Dharma Raksaka",
|
||||
"description": "<p>Sajjana Dharma Raksaka adalah program inovasi Desa Darmasaba yang bertujuan untuk memperkuat akses perlindungan hukum dan pendampingan hukum non-litigasi bagi masyarakat desa. Melalui inovasi ini, desa menyediakan layanan pendampingan, mediasi, dan bantuan dalam menyelesaikan persoalan hukum secara bijaksana, humanis, dan berperspektif keadilan sosial</p>",
|
||||
"link": "https://ppid.badungkab.go.id/storage/dokumen/5RS9dldGkrgzMQq6bKdZsqsVRHI8gffWv4PGfb3r.pdf",
|
||||
"imageId" : "cmk20pf3d0006vnev3mkoqpyy"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,109 +3,109 @@
|
||||
"id": "cmdsjzdl30002vneknuvo4irv",
|
||||
"name": "Desa Tanpa Kemiskinan",
|
||||
"jumlah": "52.62",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3grkby0000vnxwblul7viy"
|
||||
},
|
||||
{
|
||||
"id": "cmdskargd0005vnek0mu2ofk9",
|
||||
"name": "Desa Tanpa Kelaparan",
|
||||
"jumlah": "35.75",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3h87lc0001vnxwxc5sn0xc"
|
||||
},
|
||||
{
|
||||
"id": "cmdskbvl0008vnek5dmieatb",
|
||||
"name": "Desa Sehat Dan Sejahtera",
|
||||
"jumlah": "77.37",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3haaaf0002vnxwy5w07z3n"
|
||||
},
|
||||
{
|
||||
"id": "cmdskcx91000bvneko7tuaoqa",
|
||||
"name": "Pendidikan Desa Berkualitas",
|
||||
"jumlah": "34.11",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3hdeeg0003vnxwezx6zu99"
|
||||
},
|
||||
{
|
||||
"id": "cmdskjare000evnek1hglu0x8",
|
||||
"name": "Keterlibatan Perempuan Desa",
|
||||
"jumlah": "45.70",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3hg3n20004vnxwxofi5ylm"
|
||||
},
|
||||
{
|
||||
"id": "cmdskqcpc0002vnvnqjkqgm92",
|
||||
"name": "Desa Layak Air Bersih Dan Sanitasi",
|
||||
"jumlah": "48.54",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3hjdb10005vnxwjtgd6g9g"
|
||||
},
|
||||
{
|
||||
"id": "cmdsktl3x0005vnvne15seefw",
|
||||
"name": "Desa Berenergi Bersih Dan Terbarukan",
|
||||
"jumlah": "99.64",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3hlu6l0006vnxw9wtsaq4w"
|
||||
},
|
||||
{
|
||||
"id": "cmdskuncw0008vnvcsdqoeog",
|
||||
"name": "Pertumbuhan Ekonomi Desa Merata",
|
||||
"jumlah": "40.92",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3hp5ht0007vnxwakvsa7ld"
|
||||
},
|
||||
{
|
||||
"id": "cmdskw83j000bvvn9szqrea6",
|
||||
"name": "Infrastruktur Dan Inovasi Desa Sesuai Kebutuhan",
|
||||
"jumlah": "35.37",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3hrkmp0008vnxwijncfayo"
|
||||
},
|
||||
{
|
||||
"id": "cmdskwrq7000envnvy0c5nbgf",
|
||||
"name": "Desa Tanpa Kesenjangan",
|
||||
"jumlah": "35.47",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3hu2lh0009vnxw82qb8l6h"
|
||||
},
|
||||
{
|
||||
"id": "cmdskxivx000hnvnvsx520gv1",
|
||||
"name": "Kawasan Pemukiman Desa Aman Dan Nyaman",
|
||||
"jumlah": "40.35",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3hwtsm000avnxwfug41t9x"
|
||||
},
|
||||
{
|
||||
"id": "cmdskzg4c000kvnnkiv61gkt",
|
||||
"name": "Konsumsi Dan Produksi Desa Sadar Lingkungan",
|
||||
"jumlah": "16.67",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3i09sa000bvnxw95elyupj"
|
||||
},
|
||||
{
|
||||
"id": "cmdsl07lk000nvnnvnrepsdy5m",
|
||||
"name": "Desa Tanggap Perubahan Iklim",
|
||||
"jumlah": "0.00",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3i2q07000cvnxwkjxqld0c"
|
||||
},
|
||||
{
|
||||
"id": "cmdsl10rq000qvnvnlch9c1yv",
|
||||
"name": "Desa Peduli Lingkungan Laut",
|
||||
"jumlah": "50.00",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3i53wr000dvnxw074gyuar"
|
||||
},
|
||||
{
|
||||
"id": "cmdsl1mc2000tvnvn357n8usi",
|
||||
"name": "Desa Peduli Lingkungan Darat",
|
||||
"jumlah": "0.00",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3i7krz000evnxwe4uturyn"
|
||||
},
|
||||
{
|
||||
"id": "cmdsl2bx3000wvnvntshi4gnj",
|
||||
"name": "Desa Damai Berkeadilan",
|
||||
"jumlah": "78.65",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3ib07w000fvnxwsnb1hga3"
|
||||
},
|
||||
{
|
||||
"id": "cmdsl2yz3000zvnvnmf60ok7q",
|
||||
"name": "Kemitraan Untuk Pembangunan Desa",
|
||||
"jumlah": "20.00",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3idx0t000gvnxw16urbiw5"
|
||||
},
|
||||
{
|
||||
"id": "cmdsl492h0012vnvnmckm3n2x",
|
||||
"name": "Kelembagaan Desa Dinamis Dan Budaya Desa Adaptif",
|
||||
"jumlah": "47.22",
|
||||
"imageId": ""
|
||||
"imageId": "cmk3ig4ho000hvnxwewlheju3"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{ "nama": "Kebersihan" },
|
||||
{ "nama": "Infrastruktur" },
|
||||
{ "nama": "Sosial" },
|
||||
{ "nama": "Lingkungan" }
|
||||
]
|
||||
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{ "id": "cmghqwjs4000404l8c5uvc300", "nama": "PAUD" },
|
||||
{ "id": "cmghqwjs4000404l8c5uvc301", "nama": "TK" },
|
||||
{ "id": "cmghqwjs4000404l8c5uvc302", "nama": "SD" },
|
||||
{ "id": "cmghqwjs4000404l8c5uvc303", "nama": "SMP" },
|
||||
{ "id": "cmghqwjs4000404l8c5uvc304", "nama": "SMA" },
|
||||
{ "id": "cmghqwjs4000404l8c5uvc305", "nama": "SMK" }
|
||||
]
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": "cmeppcwzk0000vn5exmudcipd",
|
||||
"jenisInformasi": "Potensi Desa",
|
||||
"deskripsi": "<p>“Potensi desa adalah segenap sumber daya alam dan sumber daya manusia yang dimiliki desa sebagai modal dasar yang perlu dikelola dan dikembangkan bagi kelangsungan dan perkembangan desa. Adapun potensi yang dimiliki Desa Darmasaba yaitu:</p><ol><li><p>TPS3R Pudak Mesari</p></li><li><p>Bumdes Pudak Mesari</p></li><li><p>Pertanian</p></li><li><p>Jogging Track Tegeh Aban, Karang Gadon dan Munduk Uma Desa</p></li><li><p>Taman Beji Cengana</p></li><li><p>Dam Tanah Putih</p></li><li><p>Gumuh Sari Water Park</p></li><li><p>UMKM</p></li><li><p>Kawasan Kuliner</p></li><li><p>IKM berbasis Pengolahan Pangan</p></li><li><p>Genteng</p></li><li><p>Peternakan Ikan Lele</p></li><li><p>Pemotongan Daging”</p></li></ol>",
|
||||
"jenisInformasi": "Penyelenggaraan Informasi Publik Desa",
|
||||
"deskripsi": "<p>Kegiatan pengadaan/pembuatan aplikasi dan pelatihan untuk kemudahan akses informasi publik desa Darmasaba melalui sarana digital sebagai bagian dari transparansi layanan masyarakat.</p>",
|
||||
"tanggal": "2021-05-25"
|
||||
},
|
||||
{
|
||||
"id": "cmeppieay0001vn5e8qe658ub",
|
||||
"jenisInformasi": "Layanan Surat Keterangan Desa",
|
||||
"deskripsi": "<p>“Desa Darmasaba menyediakan berbagai jenis layanan surat keterangan untuk kebutuhan administratif, antara lain:</p><ul><li><p>Surat Keterangan Domisili Organisasi</p></li><li><p>Surat Keterangan Penghasilan</p></li><li><p>Surat Keterangan Tidak Mampu</p></li><li><p>Surat Keterangan Kelahiran</p></li><li><p>Surat Keterangan Usaha</p></li><li><p>Surat Keterangan Tempat Usaha</p></li><li><p>Surat Keterangan Belum Kawin</p></li><li><p>Surat Keterangan Kelakuan Baik (Pengantar SKCK)</p></li><li><p>Surat Keterangan Kematian</p></li><li><p>Surat Keterangan Perbedaan Biodata Diri</p></li><li><p>Surat Keterangan Yatim/Piatu/Yatim Piatu<br>Untuk surat keterangan lainnya, masyarakat dapat berkonsultasi langsung ke kantor Perbekel Darmasaba.”<br><em>(Sumber: Laman Layanan Desa Darmasaba)</em></p></li></ul>",
|
||||
"jenisInformasi": "Kemudahan Akses Internet dan Informasi Desa",
|
||||
"deskripsi": "<p>Penyediaan jaringan/ instalasi komunikasi dan informasi desa untuk mempermudah akses internet dan informasi kepada masyarakat desa Darmasaba, bagian dari penyelenggaraan informasi desa.</p>",
|
||||
"tanggal": "2025-02-21"
|
||||
}
|
||||
]
|
||||
@@ -5,6 +5,7 @@
|
||||
"biodata": "<p>I.B Surya Prabhawa Manuaba, S.H., M.H., adalah Perbekel Darmasaba periode 2021-2027, seorang advokat, pendiri Mantra Legal Consultants & Advocates, serta aktif di bidang musik dan akademis. Dia menempuh pendidikan hukum di Universitas Udayana dan Universitas Mahasaraswati Denpasar, serta memiliki pengalaman luas di berbagai organisasi dan kepemimpinan.</p>",
|
||||
"riwayat": "<ul> <li>2021 - 2027: Perbekel Desa Darmasaba</li> <li>2015 - Sekarang: Founder & Managing Director Mantra Legal Consultants & Advocates</li> <li>2020 - Sekarang: Founder Ugawa Record Music Studio</li> <li>2010 - 2016: Dosen Fakultas Hukum Universitas Mahasaraswati Denpasar</li> </ul>",
|
||||
"pengalaman": "<ul> <li>1996 – 1997: Ketua OSIS SMP Negeri 1 Abiansemal</li><li>1999 – 2000: Ketua OSIS SMA Negeri 1 Mengwi</li> <li>2008 – 2009: Ketua BEM Universitas Mahasaraswati Denpasar</li> <li>2008 – 2010: Ketua Sekaa Taruna Sila Dharma, Banjar Tengah, Desa Adat Tegal, Darmasaba</li> <li>2020 – Sekarang: Pengurus Young Lawyer Committee Peradi Denpasar</li> <li>2021 – Sekarang: Dewan Kehormatan Himpunan Pengusaha Muda Indonesia (HIPMI) Badung</li> <li>2023 – 2028: Komite Tetap Advokasi – Bidang Hukum dan Regulasi Kamar Dagang dan Industri Badung</li> </ul>",
|
||||
"unggulan": "<h3>Pemberdayaan Ekonomi dan UMKM</h3> <ul> <li>Pelatihan dan pendampingan UMKM lokal</li> <li>Program bantuan modal usaha bagi pelaku usaha kecil</li><li>Digitalisasi UMKM untuk meningkatkan pemasaran produk lokal</li></ul>"
|
||||
"unggulan": "<h3>Pemberdayaan Ekonomi dan UMKM</h3> <ul> <li>Pelatihan dan pendampingan UMKM lokal</li> <li>Program bantuan modal usaha bagi pelaku usaha kecil</li><li>Digitalisasi UMKM untuk meningkatkan pemasaran produk lokal</li></ul>",
|
||||
"imageId" : "cmk3o5wxs0006vn9b1u5kbqyw"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"id": "cmgewz4gt000704ib91i3f169",
|
||||
"namaLengkap": "Ida Bagus Surya Prabhawa Manuaba, S.H.,M.H., NL.P.",
|
||||
"gelarAkademik": "S.H.,M.H.,NL.P.",
|
||||
"tanggalMasuk": "2020-01-01T00:00:00.000Z",
|
||||
@@ -11,7 +11,7 @@
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"id": "cmgewxfvw000004ibee5013f4",
|
||||
"namaLengkap": "I Ketut Suwanta",
|
||||
"gelarAkademik": "S.Pt",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
@@ -22,7 +22,7 @@
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440006",
|
||||
"id": "cmgewxvqw000104ibgm5l8fzs",
|
||||
"namaLengkap": "Ni Wayan Supardiati",
|
||||
"gelarAkademik": "S.Pd",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
@@ -33,7 +33,7 @@
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440011",
|
||||
"id": "cmgewy1g9000204ib2n7hbx0i",
|
||||
"namaLengkap": "I Wayan Agus Juni Artha Saputra",
|
||||
"gelarAkademik": "S.T.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
@@ -44,7 +44,7 @@
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440012",
|
||||
"id": "cmgewybah000304ibgqhn1gm2",
|
||||
"namaLengkap": "I Wayan Sueca",
|
||||
"gelarAkademik": "S.H.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
@@ -55,7 +55,7 @@
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440017",
|
||||
"id": "cmgewygqz000404ib20sv8nvg",
|
||||
"namaLengkap": "Si Gede Ketut Astawa",
|
||||
"gelarAkademik": "S.T.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
@@ -66,7 +66,7 @@
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440018",
|
||||
"id": "cmgewyos1000504ibcu8o2gyk",
|
||||
"namaLengkap": "I Kadek Arya Minarta",
|
||||
"gelarAkademik": "S.T.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
@@ -77,7 +77,7 @@
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440021",
|
||||
"id": "cmgewyxk7000604ib8djs3i6c",
|
||||
"namaLengkap": "I Gede Andika Pradnya Diputra",
|
||||
"gelarAkademik": "S.E.",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
|
||||
11
prisma/data/resolveImageId.ts
Normal file
11
prisma/data/resolveImageId.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import safeImageId from "./safeImageId";
|
||||
|
||||
export default async function resolveImageIdForSeed(
|
||||
existingImageId: string | null | undefined,
|
||||
seedImageId: string | null | undefined
|
||||
) {
|
||||
if (existingImageId) return existingImageId;
|
||||
|
||||
// ✅ Skip validasi saat seed
|
||||
return await safeImageId(seedImageId, true);
|
||||
}
|
||||
24
prisma/data/safeImageId.ts
Normal file
24
prisma/data/safeImageId.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export default async function safeImageId(
|
||||
imageId?: string | null,
|
||||
skipValidation = false // ✅ tambah param
|
||||
) {
|
||||
if (!imageId) return null;
|
||||
|
||||
if (skipValidation) {
|
||||
console.log(`⚠️ Skipping validation for ${imageId} (seed mode)`);
|
||||
return imageId; // langsung return tanpa cek DB
|
||||
}
|
||||
|
||||
const exists = await prisma.fileStorage.findUnique({
|
||||
where: { id: imageId },
|
||||
});
|
||||
|
||||
if (!exists) {
|
||||
console.warn(`⚠️ imageId ${imageId} not found in FileStorage`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return imageId;
|
||||
}
|
||||
@@ -1,29 +1,32 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"name": "ADMIN DESA",
|
||||
"description": "Administrator Desa",
|
||||
"permissions": ["manage_users", "manage_content", "view_reports"],
|
||||
"isActive": true,
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "ADMIN KESEHATAN",
|
||||
"description": "Administrator Bidang Kesehatan",
|
||||
"permissions": ["manage_health_data", "view_reports"],
|
||||
"isActive": true,
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"name": "ADMIN SEKOLAH",
|
||||
"description": "Administrator Sekolah",
|
||||
"permissions": ["manage_school_data", "view_reports"],
|
||||
"isActive": true,
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
}
|
||||
]
|
||||
{
|
||||
"id": "0",
|
||||
"name": "DEVELOPER",
|
||||
"description": "Developer",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"name": "SUPER ADMIN",
|
||||
"description": "Administrator",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "ADMIN DESA",
|
||||
"description": "Administrator Desa",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"name": "ADMIN KESEHATAN",
|
||||
"description": "Administrator Bidang Kesehatan",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"name": "ADMIN PENDIDIKAN",
|
||||
"description": "Administrator Bidang Pendidikan",
|
||||
"isActive": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,32 +1,10 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"nama": "Admin Desa",
|
||||
"nomor": "089647037426",
|
||||
"roleId": "1",
|
||||
"isActive": true,
|
||||
"lastLogin": "2025-08-31T10:00:00.000Z",
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"nama": "Admin Kesehatan",
|
||||
"nomor": "082339004198",
|
||||
"roleId": "2",
|
||||
"isActive": true,
|
||||
"lastLogin": null,
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"nama": "Admin Sekolah",
|
||||
"nomor": "085237157222",
|
||||
"roleId": "3",
|
||||
"isActive": true,
|
||||
"lastLogin": null,
|
||||
"createdAt": "2025-09-01T00:00:00.000Z",
|
||||
"updatedAt": "2025-09-01T00:00:00.000Z"
|
||||
}
|
||||
]
|
||||
{
|
||||
"id": "cmie1o0zh0002vn132vtzg7hh",
|
||||
"username": "SuperAdmin-Nico",
|
||||
"nomor": "6289647037426",
|
||||
"roleId": 0,
|
||||
"isActive": true,
|
||||
"sessionInvalid": false
|
||||
}
|
||||
]
|
||||
|
||||
1127
prisma/migrations/20251119062255_add_unique_username/migration.sql
Normal file
1127
prisma/migrations/20251119062255_add_unique_username/migration.sql
Normal file
File diff suppressed because it is too large
Load Diff
142
prisma/migrations/20260106072549_nico_6_jan2025/migration.sql
Normal file
142
prisma/migrations/20260106072549_nico_6_jan2025/migration.sql
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `dokterdanTenagaMedisId` on the `FasilitasKesehatan` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `tarifDanLayananId` on the `FasilitasKesehatan` table. All the data in the column will be lost.
|
||||
- You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `UserSession` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `permissions` table. If the table is not empty, all the data it contains will be lost.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "FasilitasKesehatan" DROP CONSTRAINT "FasilitasKesehatan_dokterdanTenagaMedisId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "FasilitasKesehatan" DROP CONSTRAINT "FasilitasKesehatan_tarifDanLayananId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "User" DROP CONSTRAINT "User_roleId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "UserSession" DROP CONSTRAINT "UserSession_userId_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "DokterdanTenagaMedis" ADD COLUMN "jadwalLibur" TEXT,
|
||||
ADD COLUMN "jamBukaLibur" TEXT,
|
||||
ADD COLUMN "jamBukaOperasional" TEXT,
|
||||
ADD COLUMN "jamTutupLibur" TEXT,
|
||||
ADD COLUMN "jamTutupOperasional" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "FasilitasKesehatan" DROP COLUMN "dokterdanTenagaMedisId",
|
||||
DROP COLUMN "tarifDanLayananId";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "MediaSosial" ADD COLUMN "icon" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "roles" ALTER COLUMN "permissions" DROP NOT NULL;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "User";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "UserSession";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "permissions";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "users" (
|
||||
"id" TEXT NOT NULL,
|
||||
"username" TEXT NOT NULL,
|
||||
"nomor" TEXT NOT NULL,
|
||||
"roleId" TEXT NOT NULL DEFAULT '2',
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT false,
|
||||
"sessionInvalid" BOOLEAN NOT NULL DEFAULT false,
|
||||
"lastLogin" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"permissions" JSONB,
|
||||
|
||||
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "user_sessions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"token" TEXT NOT NULL,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"userId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "user_sessions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "UserMenuAccess" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"menuId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "UserMenuAccess_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_Tarif" (
|
||||
"A" TEXT NOT NULL,
|
||||
"B" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "_Tarif_AB_pkey" PRIMARY KEY ("A","B")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_Dokter" (
|
||||
"A" TEXT NOT NULL,
|
||||
"B" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "_Dokter_AB_pkey" PRIMARY KEY ("A","B")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_nomor_key" ON "users"("nomor");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "user_sessions_userId_idx" ON "user_sessions"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "user_sessions_token_idx" ON "user_sessions"("token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "UserMenuAccess_userId_menuId_key" ON "UserMenuAccess"("userId", "menuId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_Tarif_B_index" ON "_Tarif"("B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_Dokter_B_index" ON "_Dokter"("B");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "users" ADD CONSTRAINT "users_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "roles"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "user_sessions" ADD CONSTRAINT "user_sessions_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "UserMenuAccess" ADD CONSTRAINT "UserMenuAccess_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_Tarif" ADD CONSTRAINT "_Tarif_A_fkey" FOREIGN KEY ("A") REFERENCES "FasilitasKesehatan"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_Tarif" ADD CONSTRAINT "_Tarif_B_fkey" FOREIGN KEY ("B") REFERENCES "TarifDanLayanan"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_Dokter" ADD CONSTRAINT "_Dokter_A_fkey" FOREIGN KEY ("A") REFERENCES "DokterdanTenagaMedis"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_Dokter" ADD CONSTRAINT "_Dokter_B_fkey" FOREIGN KEY ("B") REFERENCES "FasilitasKesehatan"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
25
prisma/resolveImageByName.ts
Normal file
25
prisma/resolveImageByName.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
// Ganti nama fungsi dan logikanya
|
||||
export default async function resolveImageById(
|
||||
imageId?: string | null
|
||||
): Promise<string | null> {
|
||||
if (!imageId) return null;
|
||||
|
||||
const image = await prisma.fileStorage.findFirst({
|
||||
where: {
|
||||
id: imageId, // ← cari berdasarkan ID
|
||||
category: "image",
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!image) {
|
||||
console.warn(`⚠️ Image with ID ${imageId} not found`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return image.id;
|
||||
}
|
||||
63
prisma/safeseedUnique.ts
Normal file
63
prisma/safeseedUnique.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
type SafeSeedOptions = {
|
||||
skipUpdate?: boolean;
|
||||
};
|
||||
|
||||
// prisma/safeseedUnique.ts
|
||||
export async function safeSeedUnique<T extends keyof PrismaClient>(
|
||||
model: T,
|
||||
where: Record<string, any>,
|
||||
data: Record<string, any>,
|
||||
options: SafeSeedOptions = {}
|
||||
) {
|
||||
const m = prisma[model] as any;
|
||||
if (!m) throw new Error(`Model ${String(model)} tidak ditemukan`);
|
||||
|
||||
try {
|
||||
// Pastikan `where` berisi field yang benar-benar unique (misal: `id`)
|
||||
const result = await m.upsert({
|
||||
where,
|
||||
update: options.skipUpdate ? {} : data,
|
||||
create: data, // ✅ Jangan duplikasi `where` ke `create`
|
||||
});
|
||||
console.log(`✅ Seed ${String(model)}:`, where);
|
||||
return result;
|
||||
} catch (err) {
|
||||
console.error(`❌ Gagal seed ${String(model)}:`, where, err);
|
||||
throw err; // ✅ Rethrow agar seeding berhenti jika kritis
|
||||
}
|
||||
}
|
||||
|
||||
// /* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// import { PrismaClient } from "@prisma/client";
|
||||
|
||||
// const prisma = new PrismaClient();
|
||||
|
||||
// type SafeSeedOptions = {
|
||||
// skipUpdate?: boolean;
|
||||
// };
|
||||
|
||||
// export async function safeSeedUnique<T extends keyof PrismaClient>(
|
||||
// model: T,
|
||||
// where: Record<string, any>,
|
||||
// data: Record<string, any>,
|
||||
// options: SafeSeedOptions = {}
|
||||
// ) {
|
||||
// const m = prisma[model] as any;
|
||||
// if (!m) throw new Error(`Model ${String(model)} tidak ditemukan`);
|
||||
|
||||
// try {
|
||||
// await m.upsert({
|
||||
// where,
|
||||
// update: options.skipUpdate ? {} : data,
|
||||
// create: { ...where, ...data },
|
||||
// });
|
||||
|
||||
// console.log(`✅ Seed ${String(model)}:`, where);
|
||||
// } catch (err) {
|
||||
// console.error(`❌ Gagal seed ${String(model)}:`, where, err);
|
||||
// }
|
||||
// }
|
||||
@@ -81,7 +81,7 @@ model FileStorage {
|
||||
PelayananSuratKeteranganImage PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage")
|
||||
PelayananSuratKeteranganImage2 PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage2")
|
||||
PasarDesa PasarDesa[]
|
||||
Pegawai Pegawai[]
|
||||
PegawaiBumDes PegawaiBumDes[]
|
||||
DesaDigital DesaDigital[]
|
||||
InfoTekno InfoTekno[]
|
||||
PengaduanMasyarakat PengaduanMasyarakat[]
|
||||
@@ -101,6 +101,7 @@ model FileStorage {
|
||||
MitraKolaborasi MitraKolaborasi[]
|
||||
|
||||
ArtikelKesehatan ArtikelKesehatan[]
|
||||
StrukturBumDes StrukturBumDes[]
|
||||
}
|
||||
|
||||
//========================================= MENU LANDING PAGE ========================================= //
|
||||
@@ -135,6 +136,7 @@ model MediaSosial {
|
||||
name String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
icon String?
|
||||
iconUrl String? @db.VarChar(255)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -142,7 +144,7 @@ model MediaSosial {
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
//========================================= PROFILE ========================================= //
|
||||
//========================================= DESA ANTI KORUPSI ========================================= //
|
||||
model DesaAntiKorupsi {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
@@ -183,18 +185,46 @@ model SdgsDesa {
|
||||
//========================================= APBDes ========================================= //
|
||||
model APBDes {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
jumlah String
|
||||
tahun Int?
|
||||
name String? // misalnya: "APBDes Tahun 2025"
|
||||
deskripsi String?
|
||||
jumlah String? // total keseluruhan (opsional, bisa juga dihitung dari items)
|
||||
items APBDesItem[]
|
||||
image FileStorage? @relation("APBDesImage", fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
file FileStorage? @relation("APBDesFile", fields: [fileId], references: [id])
|
||||
fileId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
deletedAt DateTime? // opsional, tidak perlu default now()
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model APBDesItem {
|
||||
id String @id @default(cuid())
|
||||
kode String // contoh: "4", "4.1", "4.1.2"
|
||||
uraian String // nama item, contoh: "Pendapatan Asli Desa", "Hasil Usaha"
|
||||
anggaran Float // dalam satuan Rupiah (bisa DECIMAL di DB, tapi Float umum di TS/JS)
|
||||
realisasi Float
|
||||
selisih Float // realisasi - anggaran
|
||||
persentase Float
|
||||
tipe String? // (realisasi / anggaran) * 100
|
||||
level Int // 1 = kelompok utama, 2 = sub-kelompok, 3 = detail
|
||||
parentId String? // untuk relasi hierarki
|
||||
parent APBDesItem? @relation("APBDesItemParent", fields: [parentId], references: [id])
|
||||
children APBDesItem[] @relation("APBDesItemParent")
|
||||
apbdesId String
|
||||
apbdes APBDes @relation(fields: [apbdesId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
isActive Boolean @default(true)
|
||||
|
||||
@@index([kode])
|
||||
@@index([level])
|
||||
@@index([apbdesId])
|
||||
}
|
||||
|
||||
//========================================= PRESTASI DESA ========================================= //
|
||||
model PrestasiDesa {
|
||||
id String @id @default(cuid())
|
||||
@@ -286,49 +316,51 @@ model StrukturPPID {
|
||||
}
|
||||
|
||||
model PosisiOrganisasiPPID {
|
||||
id String @id @default(cuid())
|
||||
nama String @db.VarChar(100)
|
||||
deskripsi String? @db.Text
|
||||
hierarki Int
|
||||
pegawai PegawaiPPID[]
|
||||
strukturOrganisasi StrukturPPID[] // Relasi balik
|
||||
parentId String?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id])
|
||||
children PosisiOrganisasiPPID[] @relation("Parent")
|
||||
id String @id @default(cuid())
|
||||
nama String @db.VarChar(100)
|
||||
deskripsi String? @db.Text
|
||||
hierarki Int
|
||||
pegawai PegawaiPPID[]
|
||||
strukturOrganisasi StrukturPPID[] // Relasi balik
|
||||
parentId String?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id])
|
||||
children PosisiOrganisasiPPID[] @relation("Parent")
|
||||
StrukturOrganisasiPPID StrukturOrganisasiPPID[]
|
||||
}
|
||||
|
||||
model PegawaiPPID {
|
||||
id String @id @default(cuid())
|
||||
namaLengkap String @db.VarChar(255)
|
||||
gelarAkademik String? @db.VarChar(100)
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
tanggalMasuk DateTime? @db.Date
|
||||
email String? @unique @db.VarChar(255)
|
||||
telepon String? @db.VarChar(20)
|
||||
alamat String? @db.Text
|
||||
posisiId String @db.VarChar(50)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
posisi PosisiOrganisasiPPID @relation(fields: [posisiId], references: [id])
|
||||
strukturOrganisasi StrukturPPID[] // Relasi balik
|
||||
id String @id @default(cuid())
|
||||
namaLengkap String @db.VarChar(255)
|
||||
gelarAkademik String? @db.VarChar(100)
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
tanggalMasuk DateTime? @db.Date
|
||||
email String? @unique @db.VarChar(255)
|
||||
telepon String? @db.VarChar(20)
|
||||
alamat String? @db.Text
|
||||
posisiId String @db.VarChar(50)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
posisi PosisiOrganisasiPPID @relation(fields: [posisiId], references: [id])
|
||||
strukturOrganisasi StrukturPPID[] // Relasi balik
|
||||
StrukturOrganisasiPPID StrukturOrganisasiPPID[]
|
||||
}
|
||||
|
||||
model StrukturOrganisasiPPID {
|
||||
id String @id @default(uuid())
|
||||
posisiOrganisasiId String @db.VarChar(50)
|
||||
pegawaiId String @db.Uuid
|
||||
hubunganOrganisasiId String @db.Uuid
|
||||
posisiOrganisasi PosisiOrganisasi @relation(fields: [posisiOrganisasiId], references: [id])
|
||||
pegawai Pegawai @relation(fields: [pegawaiId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
id String @id @default(uuid())
|
||||
posisiOrganisasiId String @db.VarChar(50)
|
||||
pegawaiId String
|
||||
hubunganOrganisasiId String
|
||||
posisiOrganisasi PosisiOrganisasiPPID @relation(fields: [posisiOrganisasiId], references: [id])
|
||||
pegawai PegawaiPPID @relation(fields: [pegawaiId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
isActive Boolean @default(true)
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= VISI MISI PPID ========================================= //
|
||||
@@ -575,8 +607,8 @@ model Berita {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
content String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -672,17 +704,18 @@ model GalleryVideo {
|
||||
|
||||
// ========================================= LAYANAN DESA ========================================= //
|
||||
model PelayananSuratKeterangan {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsi String @db.Text
|
||||
image FileStorage? @relation("PelayananSuratKeteranganImage", fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
image2 FileStorage? @relation("PelayananSuratKeteranganImage2", fields: [image2Id], references: [id])
|
||||
image2Id String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsi String @db.Text
|
||||
image FileStorage? @relation("PelayananSuratKeteranganImage", fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
image2 FileStorage? @relation("PelayananSuratKeteranganImage2", fields: [image2Id], references: [id])
|
||||
image2Id String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
AjukanPermohonan AjukanPermohonan[]
|
||||
}
|
||||
|
||||
model PelayananTelunjukSaktiDesa {
|
||||
@@ -717,6 +750,20 @@ model PelayananPendudukNonPermanen {
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model AjukanPermohonan {
|
||||
id String @id @default(cuid())
|
||||
nama String
|
||||
nik String
|
||||
alamat String
|
||||
nomorKk String
|
||||
kategori PelayananSuratKeterangan @relation(fields: [kategoriId], references: [id])
|
||||
kategoriId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= PENGHARGAAN ========================================= //
|
||||
model Penghargaan {
|
||||
id String @id @default(cuid())
|
||||
@@ -736,24 +783,22 @@ model Penghargaan {
|
||||
|
||||
// ========================================= FASILITAS KESEHATAN ========================================= //
|
||||
model FasilitasKesehatan {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
informasiumum InformasiUmum @relation(fields: [informasiUmumId], references: [id])
|
||||
informasiUmumId String
|
||||
layananunggulan LayananUnggulan @relation(fields: [layananUnggulanId], references: [id])
|
||||
layananUnggulanId String
|
||||
dokterdantenagamedis DokterdanTenagaMedis @relation(fields: [dokterdanTenagaMedisId], references: [id])
|
||||
dokterdanTenagaMedisId String
|
||||
fasilitaspendukung FasilitasPendukung @relation(fields: [fasilitasPendukungId], references: [id])
|
||||
fasilitasPendukungId String
|
||||
prosedurpendaftaran ProsedurPendaftaran @relation(fields: [prosedurPendaftaranId], references: [id])
|
||||
prosedurPendaftaranId String
|
||||
tarifdanlayanan TarifDanLayanan @relation(fields: [tarifDanLayananId], references: [id])
|
||||
tarifDanLayananId String
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
informasiumum InformasiUmum @relation(fields: [informasiUmumId], references: [id])
|
||||
informasiUmumId String
|
||||
layananunggulan LayananUnggulan @relation(fields: [layananUnggulanId], references: [id])
|
||||
layananUnggulanId String
|
||||
dokterdantenagamedis DokterdanTenagaMedis[] @relation("Dokter")
|
||||
fasilitaspendukung FasilitasPendukung @relation(fields: [fasilitasPendukungId], references: [id])
|
||||
fasilitasPendukungId String
|
||||
prosedurpendaftaran ProsedurPendaftaran @relation(fields: [prosedurPendaftaranId], references: [id])
|
||||
prosedurPendaftaranId String
|
||||
tarifdanlayanan TarifDanLayanan[] @relation("Tarif")
|
||||
}
|
||||
|
||||
model InformasiUmum {
|
||||
@@ -779,15 +824,20 @@ model LayananUnggulan {
|
||||
}
|
||||
|
||||
model DokterdanTenagaMedis {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
specialist String
|
||||
jadwal String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
FasilitasKesehatan FasilitasKesehatan[]
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
specialist String
|
||||
jadwal String
|
||||
jadwalLibur String?
|
||||
jamBukaOperasional String?
|
||||
jamTutupOperasional String?
|
||||
jamBukaLibur String?
|
||||
jamTutupLibur String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
FasilitasKesehatan FasilitasKesehatan[] @relation("Dokter")
|
||||
}
|
||||
|
||||
model FasilitasPendukung {
|
||||
@@ -818,7 +868,7 @@ model TarifDanLayanan {
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
FasilitasKesehatan FasilitasKesehatan[]
|
||||
FasilitasKesehatan FasilitasKesehatan[] @relation("Tarif")
|
||||
}
|
||||
|
||||
// ========================================= JADWAL KEGIATAN ========================================= //
|
||||
@@ -835,8 +885,8 @@ model JadwalKegiatan {
|
||||
syaratKetentuanJadwalKegiatanId String
|
||||
dokumenjadwalkegiatan DokumenJadwalKegiatan @relation(fields: [dokumenJadwalKegiatanId], references: [id])
|
||||
dokumenJadwalKegiatanId String
|
||||
pendaftaranjadwalkegiatan PendaftaranJadwalKegiatan @relation(fields: [pendaftaranJadwalKegiatanId], references: [id])
|
||||
pendaftaranJadwalKegiatanId String
|
||||
pendaftaranjadwalkegiatan PendaftaranJadwalKegiatan? @relation(fields: [pendaftaranJadwalKegiatanId], references: [id])
|
||||
pendaftaranJadwalKegiatanId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
@@ -1152,6 +1202,7 @@ model KontakDarurat {
|
||||
deskripsi String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
whatsapp String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
@@ -1254,15 +1305,15 @@ model KontakDaruratToItem {
|
||||
|
||||
// ========================================= PENCEGAHAN KRIMINALITAS ========================================= //
|
||||
model PencegahanKriminalitas {
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String
|
||||
deskripsiSingkat String
|
||||
linkVideo String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String
|
||||
deskripsiSingkat String
|
||||
linkVideo String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= LAPORAN PUBLIK ========================================= //
|
||||
@@ -1325,6 +1376,7 @@ model PasarDesa {
|
||||
harga Int
|
||||
rating Float
|
||||
alamatUsaha String
|
||||
kontak String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
@@ -1367,6 +1419,7 @@ model LowonganPekerjaan {
|
||||
gaji String
|
||||
deskripsi String
|
||||
kualifikasi String
|
||||
notelp String
|
||||
tanggalPosting DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
@@ -1376,79 +1429,67 @@ model LowonganPekerjaan {
|
||||
|
||||
// ========================================= STRUKTUR ORGANISASI ========================================= //
|
||||
|
||||
model PosisiOrganisasi {
|
||||
id String @id @default(uuid()) @db.VarChar(50)
|
||||
nama String @db.VarChar(100)
|
||||
deskripsi String? @db.Text
|
||||
hierarki Int
|
||||
|
||||
pegawai Pegawai[]
|
||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||
StrukturOrganisasiPPID StrukturOrganisasiPPID[]
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("posisi_organisasi")
|
||||
model StrukturBumDes {
|
||||
id String @id @default(cuid())
|
||||
name String @db.Text
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
PosisiOrganisasiBumDes PosisiOrganisasiBumDes? @relation(fields: [posisiOrganisasiBumDesId], references: [id])
|
||||
posisiOrganisasiBumDesId String?
|
||||
PegawaiBumDes PegawaiBumDes? @relation(fields: [pegawaiBumDesId], references: [id])
|
||||
pegawaiBumDesId String?
|
||||
}
|
||||
|
||||
model Pegawai {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
namaLengkap String @db.VarChar(255)
|
||||
gelarAkademik String? @db.VarChar(100)
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
tanggalMasuk DateTime? @db.Date
|
||||
email String? @unique @db.VarChar(255)
|
||||
telepon String? @db.VarChar(20)
|
||||
alamat String? @db.Text
|
||||
posisiId String @db.VarChar(50)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
posisi PosisiOrganisasi @relation(fields: [posisiId], references: [id])
|
||||
|
||||
sebagaiAtasan HubunganOrganisasi[] @relation("AtasanToBawahan")
|
||||
sebagaiBawahan HubunganOrganisasi[] @relation("BawahanToAtasan")
|
||||
|
||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||
StrukturOrganisasiPPID StrukturOrganisasiPPID[]
|
||||
|
||||
@@map("pegawai")
|
||||
model PosisiOrganisasiBumDes {
|
||||
id String @id @default(cuid())
|
||||
nama String @db.VarChar(100)
|
||||
deskripsi String? @db.Text
|
||||
hierarki Int
|
||||
pegawai PegawaiBumDes[]
|
||||
strukturOrganisasi StrukturBumDes[] // Relasi balik
|
||||
parentId String?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
parent PosisiOrganisasiBumDes? @relation("Parent", fields: [parentId], references: [id])
|
||||
children PosisiOrganisasiBumDes[] @relation("Parent")
|
||||
StrukturOrganisasiBumDes StrukturOrganisasiBumDes[]
|
||||
}
|
||||
|
||||
model HubunganOrganisasi {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
atasanId String @db.Uuid
|
||||
bawahanId String @db.Uuid
|
||||
tipe String? @db.VarChar(50)
|
||||
|
||||
atasan Pegawai @relation("AtasanToBawahan", fields: [atasanId], references: [id])
|
||||
bawahan Pegawai @relation("BawahanToAtasan", fields: [bawahanId], references: [id])
|
||||
|
||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||
|
||||
@@unique([atasanId, bawahanId])
|
||||
@@map("hubungan_organisasi")
|
||||
model PegawaiBumDes {
|
||||
id String @id @default(cuid())
|
||||
namaLengkap String @db.VarChar(255)
|
||||
gelarAkademik String? @db.VarChar(100)
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
tanggalMasuk DateTime? @db.Date
|
||||
email String? @unique @db.VarChar(255)
|
||||
telepon String? @db.VarChar(20)
|
||||
alamat String? @db.Text
|
||||
posisiId String @db.VarChar(50)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
posisi PosisiOrganisasiBumDes @relation(fields: [posisiId], references: [id])
|
||||
strukturOrganisasi StrukturBumDes[] // Relasi balik
|
||||
StrukturOrganisasiBumDes StrukturOrganisasiBumDes[]
|
||||
}
|
||||
|
||||
model StrukturOrganisasi {
|
||||
id String @id @default(uuid())
|
||||
posisiOrganisasiId String @db.VarChar(50)
|
||||
pegawaiId String @db.Uuid
|
||||
hubunganOrganisasiId String @db.Uuid
|
||||
|
||||
posisiOrganisasi PosisiOrganisasi @relation(fields: [posisiOrganisasiId], references: [id])
|
||||
pegawai Pegawai @relation(fields: [pegawaiId], references: [id])
|
||||
hubunganOrganisasi HubunganOrganisasi @relation(fields: [hubunganOrganisasiId], references: [id])
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
isActive Boolean @default(true)
|
||||
|
||||
@@map("struktur_organisasi")
|
||||
model StrukturOrganisasiBumDes {
|
||||
id String @id @default(uuid())
|
||||
posisiOrganisasiId String @db.VarChar(50)
|
||||
pegawaiId String
|
||||
hubunganOrganisasiId String
|
||||
posisiOrganisasi PosisiOrganisasiBumDes @relation(fields: [posisiOrganisasiId], references: [id])
|
||||
pegawai PegawaiBumDes @relation(fields: [pegawaiId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= PROGRAM KEMISKINAN ========================================= //
|
||||
@@ -1597,7 +1638,7 @@ model Pembiayaan {
|
||||
ApbDesa ApbDesa[] @relation("ApbDesaPembiayaan")
|
||||
}
|
||||
|
||||
// ========================================= INOVASI ========================================= //
|
||||
// ========================================= MENU INOVASI ========================================= //
|
||||
// ========================================= DESA DIGITAL / SMART VILLAGE ========================================= //
|
||||
model DesaDigital {
|
||||
id String @id @default(cuid())
|
||||
@@ -1933,23 +1974,28 @@ model KeunggulanProgram {
|
||||
}
|
||||
|
||||
model BeasiswaPendaftar {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
namaLengkap String
|
||||
nik String @unique
|
||||
nis String?
|
||||
kelas String?
|
||||
jenisKelamin JenisKelamin
|
||||
alamatDomisili String?
|
||||
tempatLahir String
|
||||
tanggalLahir DateTime
|
||||
jenisKelamin JenisKelamin
|
||||
kewarganegaraan String
|
||||
agama Agama
|
||||
alamatKTP String
|
||||
alamatDomisili String?
|
||||
namaOrtu String?
|
||||
nik String @unique
|
||||
pekerjaanOrtu String?
|
||||
penghasilan String?
|
||||
noHp String
|
||||
email String @unique
|
||||
statusPernikahan StatusPernikahan
|
||||
kewarganegaraan String?
|
||||
agama Agama?
|
||||
alamatKTP String?
|
||||
email String? @unique
|
||||
statusPernikahan StatusPernikahan?
|
||||
ukuranBaju UkuranBaju?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
enum JenisKelamin {
|
||||
@@ -2078,6 +2124,9 @@ model DataPerpustakaan {
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
|
||||
// relasi baru ke peminjaman
|
||||
peminjamanBuku PeminjamanBuku[]
|
||||
}
|
||||
|
||||
model KategoriBuku {
|
||||
@@ -2090,28 +2139,56 @@ model KategoriBuku {
|
||||
DataPerpustakaan DataPerpustakaan[]
|
||||
}
|
||||
|
||||
model PeminjamanBuku {
|
||||
id String @id @default(cuid())
|
||||
nama String
|
||||
noTelp String
|
||||
alamat String
|
||||
bukuId String
|
||||
tanggalPinjam DateTime @default(now())
|
||||
batasKembali DateTime // tenggat waktu pengembalian
|
||||
tanggalKembali DateTime? // diisi saat dikembalikan
|
||||
status StatusPeminjaman @default(Dipinjam)
|
||||
catatan String? // opsional, misal: kondisi buku
|
||||
buku DataPerpustakaan @relation(fields: [bukuId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
enum StatusPeminjaman {
|
||||
Dipinjam
|
||||
Dikembalikan
|
||||
Terlambat
|
||||
Dibatalkan
|
||||
}
|
||||
|
||||
// ========================================= USER ========================================= //
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
username String
|
||||
nomor String @unique
|
||||
role Role @relation(fields: [roleId], references: [id])
|
||||
roleId String @default("1")
|
||||
instansi String?
|
||||
UserSession UserSession? // Nama instansi (Puskesmas, Sekolah, dll)
|
||||
isActive Boolean @default(true)
|
||||
lastLogin DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
id String @id @default(cuid())
|
||||
username String
|
||||
nomor String @unique
|
||||
roleId String @default("2")
|
||||
isActive Boolean @default(false)
|
||||
sessionInvalid Boolean @default(false)
|
||||
lastLogin DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
permissions Json?
|
||||
sessions UserSession[] // ✅ Relasi one-to-many
|
||||
role Role @relation(fields: [roleId], references: [id])
|
||||
menuAccesses UserMenuAccess[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
model Role {
|
||||
id String @id @default(cuid())
|
||||
name String @unique // ADMIN_DESA, ADMIN_KESEHATAN, ADMIN_SEKOLAH
|
||||
description String?
|
||||
permissions Json // Menyimpan permission dalam format JSON
|
||||
permissions Json?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -2130,26 +2207,32 @@ model KodeOtp {
|
||||
otp Int
|
||||
}
|
||||
|
||||
// Tabel untuk menyimpan permission
|
||||
model Permission {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
description String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
model UserSession {
|
||||
id String @id @default(cuid())
|
||||
token String @db.Text // ✅ JWT bisa panjang
|
||||
expiresAt DateTime // ✅ Ubah jadi expiresAt (konsisten)
|
||||
active Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
|
||||
@@map("permissions")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String // ✅ HAPUS @unique - user bisa punya multiple sessions
|
||||
|
||||
@@index([userId]) // ✅ Index untuk query cepat
|
||||
@@index([token]) // ✅ Index untuk verify cepat
|
||||
@@map("user_sessions")
|
||||
}
|
||||
|
||||
model UserSession {
|
||||
id String @id @default(cuid())
|
||||
token String
|
||||
expires DateTime?
|
||||
active Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
User User @relation(fields: [userId], references: [id])
|
||||
userId String @unique
|
||||
model UserMenuAccess {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
menuId String // ID menu (misal: "Landing Page", "Kesehatan")
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
|
||||
@@unique([userId, menuId]) // Satu user tidak bisa punya akses menu yang sama dua kali
|
||||
}
|
||||
|
||||
// ========================================= DATA PENDIDIKAN ========================================= //
|
||||
|
||||
873
prisma/seed.ts
873
prisma/seed.ts
File diff suppressed because it is too large
Load Diff
174
prisma/seed_assets.ts
Normal file
174
prisma/seed_assets.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
// prisma/seedAssets.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import AdmZip from "adm-zip";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import sharp from "sharp";
|
||||
import fetchWithRetry from "./data/fetchWithRetry";
|
||||
|
||||
const UPLOADS_DIR =
|
||||
process.env.WIBU_UPLOAD_DIR || path.join(process.cwd(), "uploads");
|
||||
|
||||
// --- Helper: deteksi kategori file ---
|
||||
function detectCategory(filename: string): "image" | "document" | "other" {
|
||||
const ext = path.extname(filename).toLowerCase();
|
||||
if ([".jpg", ".jpeg", ".png", ".webp"].includes(ext)) return "image";
|
||||
if ([".pdf", ".doc", ".docx"].includes(ext)) return "document";
|
||||
return "other";
|
||||
}
|
||||
|
||||
// --- Helper: recursive walk dir ---
|
||||
async function walkDir(
|
||||
dir: string,
|
||||
fileList: string[] = []
|
||||
): Promise<string[]> {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
if (entry.name === "__MACOSX") continue; // skip folder sampah
|
||||
await walkDir(fullPath, fileList);
|
||||
} else {
|
||||
if (entry.name.startsWith(".") || entry.name === ".DS_Store") continue; // skip file sampah
|
||||
fileList.push(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
return fileList;
|
||||
}
|
||||
|
||||
export default async function seedAssets() {
|
||||
console.log("🚀 Seeding assets...");
|
||||
|
||||
// 1. Download zip
|
||||
const url =
|
||||
"https://cld-dkr-makuro-seafile.wibudev.com/f/bc437c719af64c0bb7f2/?dl=1";
|
||||
const res = await fetchWithRetry(url, 3, 20000);
|
||||
|
||||
// Validasi content-type
|
||||
const contentType = res.headers.get("content-type");
|
||||
if (!contentType?.includes("zip")) {
|
||||
throw new Error(`Invalid content-type (${contentType}). Expected ZIP file`);
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(await res.arrayBuffer());
|
||||
|
||||
// Validasi ukuran file
|
||||
if (buffer.length < 100) {
|
||||
throw new Error("Downloaded ZIP is empty or corrupted");
|
||||
}
|
||||
|
||||
// Validasi signature ZIP ("PK")
|
||||
if (buffer.toString("utf8", 0, 2) !== "PK") {
|
||||
throw new Error("Invalid ZIP signature (PK not found)");
|
||||
}
|
||||
|
||||
// 2. Extract zip ke folder tmp
|
||||
const extractDir = path.join(process.cwd(), "tmp_assets");
|
||||
await fs.rm(extractDir, { recursive: true, force: true });
|
||||
await fs.mkdir(extractDir, { recursive: true });
|
||||
|
||||
let zip: AdmZip;
|
||||
|
||||
try {
|
||||
zip = new AdmZip(buffer);
|
||||
} catch (err) {
|
||||
throw new Error("Failed to parse ZIP file (corrupted or invalid)");
|
||||
}
|
||||
|
||||
try {
|
||||
zip.extractAllTo(extractDir, true);
|
||||
} catch (err) {
|
||||
throw new Error("Failed to extract ZIP contents");
|
||||
}
|
||||
|
||||
// 3. Cari semua file valid (recursive)
|
||||
const files = await walkDir(extractDir);
|
||||
|
||||
// 4. Loop tiap file & simpan
|
||||
for (const filePath of files) {
|
||||
const entryName = path.basename(filePath);
|
||||
const category = detectCategory(entryName);
|
||||
|
||||
let finalName = entryName;
|
||||
let mimeType = "application/octet-stream";
|
||||
let targetPath = "";
|
||||
|
||||
if (category === "image") {
|
||||
const fileBaseName = path.parse(entryName).name;
|
||||
finalName = `${fileBaseName}.webp`;
|
||||
targetPath = path.join(UPLOADS_DIR, "images", finalName);
|
||||
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
||||
await sharp(filePath).webp({ quality: 80 }).toFile(targetPath);
|
||||
mimeType = "image/webp";
|
||||
} else if (category === "document") {
|
||||
targetPath = path.join(UPLOADS_DIR, "documents", entryName);
|
||||
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
||||
await fs.copyFile(filePath, targetPath);
|
||||
mimeType = "application/pdf";
|
||||
} else {
|
||||
targetPath = path.join(UPLOADS_DIR, "other", entryName);
|
||||
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
||||
await fs.copyFile(filePath, targetPath);
|
||||
}
|
||||
|
||||
const existing = await prisma.fileStorage.findUnique({
|
||||
where: { name: finalName },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
// Restore kalau soft deleted
|
||||
await prisma.fileStorage.update({
|
||||
where: { name: finalName },
|
||||
data: {
|
||||
path: targetPath,
|
||||
realName: entryName,
|
||||
mimeType,
|
||||
link: `/uploads/${category}/${finalName}`,
|
||||
category,
|
||||
deletedAt: null,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`♻️ restored: ${category}/${finalName}`);
|
||||
} else {
|
||||
await prisma.fileStorage.create({
|
||||
data: {
|
||||
name: finalName,
|
||||
realName: entryName,
|
||||
path: targetPath,
|
||||
mimeType,
|
||||
link: `/uploads/${category}/${finalName}`,
|
||||
category,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`📂 created: ${category}/${finalName}`);
|
||||
}
|
||||
|
||||
console.log(`📂 saved: ${category}/${finalName}`);
|
||||
}
|
||||
|
||||
// 6. Cleanup
|
||||
await fs.rm(extractDir, { recursive: true, force: true });
|
||||
|
||||
console.log("✅ Selesai seed assets!");
|
||||
console.log("DB URL (asset):", process.env.DATABASE_URL);
|
||||
|
||||
}
|
||||
|
||||
// --- Auto run kalau dipanggil langsung ---
|
||||
if (import.meta.main) {
|
||||
seedAssets()
|
||||
.catch((err) => {
|
||||
console.error("❌ Error seeding assets:", err);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
}
|
||||
BIN
public/beasiswa-siswa.png
Normal file
BIN
public/beasiswa-siswa.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 446 KiB |
BIN
public/mangupuraaward.jpeg
Normal file
BIN
public/mangupuraaward.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 177 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 378 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 195 KiB After Width: | Height: | Size: 47 KiB |
@@ -7,6 +7,7 @@ import Underline from '@tiptap/extension-underline';
|
||||
import TextAlign from '@tiptap/extension-text-align';
|
||||
import Superscript from '@tiptap/extension-superscript';
|
||||
import SubScript from '@tiptap/extension-subscript';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
type CreateEditorProps = {
|
||||
value: string;
|
||||
@@ -32,6 +33,13 @@ export default function CreateEditor({ value, onChange }: CreateEditorProps) {
|
||||
},
|
||||
});
|
||||
|
||||
// 👇 Tambahkan efek untuk sinkronisasi value dari luar (resetForm)
|
||||
useEffect(() => {
|
||||
if (editor && value !== editor.getHTML()) {
|
||||
editor.commands.setContent(value || '');
|
||||
}
|
||||
}, [value, editor]);
|
||||
|
||||
return (
|
||||
<RichTextEditor editor={editor}>
|
||||
<RichTextEditor.Toolbar sticky stickyOffset="var(--docs-header-height)">
|
||||
|
||||
@@ -47,6 +47,7 @@ export default function EditEditor({ value, onChange }: EditEditorProps) {
|
||||
editor.off('update', updateHandler);
|
||||
};
|
||||
}, [editor, onChange]);
|
||||
|
||||
|
||||
return (
|
||||
<RichTextEditor editor={editor}>
|
||||
|
||||
36
src/app/admin/(dashboard)/_com/modalNonaktif.tsx
Normal file
36
src/app/admin/(dashboard)/_com/modalNonaktif.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
// components/modal/ModalKonfirmasiHapus.tsx
|
||||
import colors from "@/con/colors"
|
||||
import { Modal, Text, Button, Flex } from "@mantine/core"
|
||||
|
||||
interface ModalKonfirmasiNonAktifProps {
|
||||
opened: boolean
|
||||
loading?: boolean
|
||||
onClose: () => void
|
||||
onConfirm: () => void
|
||||
text: string
|
||||
}
|
||||
|
||||
export function ModalKonfirmasiNonAktif({
|
||||
opened,
|
||||
loading = false,
|
||||
onClose,
|
||||
onConfirm,
|
||||
text,
|
||||
}: ModalKonfirmasiNonAktifProps) {
|
||||
return (
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
title={<Text fw={"bold"} fz={"xl"}>Konfirmasi Non Aktif</Text>}
|
||||
centered
|
||||
>
|
||||
<Text mb="md">{text}</Text>
|
||||
<Flex justify="flex-end" gap="sm">
|
||||
<Button style={{color: "white"}} bg={colors['blue-button']} variant="default" onClick={onClose}>Batal</Button>
|
||||
<Button color="red" onClick={onConfirm} loading={loading}>
|
||||
Yakin Non Aktif
|
||||
</Button>
|
||||
</Flex>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
'use client';
|
||||
|
||||
import { Box, rem, Select } from '@mantine/core';
|
||||
import { Box, Group, rem, Select, SelectProps } from '@mantine/core';
|
||||
import {
|
||||
IconAmbulance,
|
||||
IconCash,
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
IconTrophy,
|
||||
IconTruckFilled,
|
||||
IconBuilding,
|
||||
IconAlertTriangle
|
||||
IconAlertTriangle,
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
const iconMap = {
|
||||
@@ -38,26 +38,26 @@ const iconMap = {
|
||||
scale: { label: 'Scale', icon: IconScale },
|
||||
clipboard: { label: 'Clipboard', icon: IconClipboardTextFilled },
|
||||
trash: { label: 'Trash', icon: IconTrashFilled },
|
||||
lingkunganSehat: {label: 'Lingkungan Sehat', icon: IconHomeEco},
|
||||
sumberOksigen: {label: 'Sumber Oksigen', icon: IconChristmasTreeFilled},
|
||||
ekonomiBerkelanjutan: {label: 'Ekonomi Berkelanjutan', icon: IconTrendingUp},
|
||||
mencegahBencana: {label: 'Mencegah Bencana', icon: IconShieldFilled},
|
||||
rumah: {label: 'Rumah', icon: IconHome},
|
||||
pohon: {label: 'Pohon', icon: IconTree},
|
||||
air: {label: 'Air', icon: IconDroplet},
|
||||
bantuan: {label: 'Bantuan', icon: IconCash},
|
||||
pelatihan: {label: 'Pelatihan', icon: IconSchool},
|
||||
subsidi: {label: 'Subsidi', icon: IconShoppingCart},
|
||||
layananKesehatan: {label: 'Layanan Kesehatan', icon: IconHospital},
|
||||
polisi: {label: 'Polisi', icon: IconShieldFilled},
|
||||
ambulans: {label: 'Ambulans', icon: IconAmbulance},
|
||||
pemadam: {label: 'Pemadam', icon: IconFiretruck},
|
||||
rumahSakit: {label: 'Rumah Sakit', icon: IconHospital},
|
||||
bangunan: {label: 'Bangunan', icon: IconBuilding},
|
||||
darurat: {label: 'Darurat', icon: IconAlertTriangle},
|
||||
lingkunganSehat: { label: 'Lingkungan Sehat', icon: IconHomeEco },
|
||||
sumberOksigen: { label: 'Sumber Oksigen', icon: IconChristmasTreeFilled },
|
||||
ekonomiBerkelanjutan: { label: 'Ekonomi Berkelanjutan', icon: IconTrendingUp },
|
||||
mencegahBencana: { label: 'Mencegah Bencana', icon: IconShieldFilled },
|
||||
rumah: { label: 'Rumah', icon: IconHome },
|
||||
pohon: { label: 'Pohon', icon: IconTree },
|
||||
air: { label: 'Air', icon: IconDroplet },
|
||||
bantuan: { label: 'Bantuan', icon: IconCash },
|
||||
pelatihan: { label: 'Pelatihan', icon: IconSchool },
|
||||
subsidi: { label: 'Subsidi', icon: IconShoppingCart },
|
||||
layananKesehatan: { label: 'Layanan Kesehatan', icon: IconHospital },
|
||||
polisi: { label: 'Polisi', icon: IconShieldFilled },
|
||||
ambulans: { label: 'Ambulans', icon: IconAmbulance },
|
||||
pemadam: { label: 'Pemadam', icon: IconFiretruck },
|
||||
rumahSakit: { label: 'Rumah Sakit', icon: IconHospital },
|
||||
bangunan: { label: 'Bangunan', icon: IconBuilding },
|
||||
darurat: { label: 'Darurat', icon: IconAlertTriangle },
|
||||
};
|
||||
|
||||
type IconKey = keyof typeof iconMap;
|
||||
export type IconKey = keyof typeof iconMap;
|
||||
|
||||
const iconList = Object.entries(iconMap).map(([value, data]) => ({
|
||||
value,
|
||||
@@ -67,44 +67,52 @@ const iconList = Object.entries(iconMap).map(([value, data]) => ({
|
||||
export default function SelectIconProgramEdit({
|
||||
onChange,
|
||||
value,
|
||||
...props
|
||||
}: {
|
||||
onChange: (value: IconKey) => void;
|
||||
value: IconKey;
|
||||
}) {
|
||||
const IconComponent = iconMap[value]?.icon || null;
|
||||
|
||||
onChange: (value: IconKey | '') => void;
|
||||
value: IconKey | '';
|
||||
} & Omit<SelectProps, 'onChange' | 'value' | 'data'>) {
|
||||
return (
|
||||
<Box maw={300}>
|
||||
<Select
|
||||
placeholder="Pilih ikon"
|
||||
value={value}
|
||||
onChange={(value) => {
|
||||
if (value) onChange(value as IconKey);
|
||||
value={value || ''}
|
||||
onChange={(val: string | null) => {
|
||||
if (val) {
|
||||
onChange(val as IconKey);
|
||||
} else {
|
||||
onChange('');
|
||||
}
|
||||
}}
|
||||
data={iconList}
|
||||
renderOption={({ option }) => {
|
||||
const Icon = iconMap[option.value as IconKey]?.icon;
|
||||
return (
|
||||
<Group gap="sm">
|
||||
{Icon && <Icon size={18} stroke={1.5} />}
|
||||
{option.label}
|
||||
</Group>
|
||||
);
|
||||
}}
|
||||
leftSection={
|
||||
IconComponent && (
|
||||
<Box>
|
||||
<IconComponent size={24} stroke={1.5} />
|
||||
value && iconMap[value as IconKey] ? (
|
||||
<Box ml={-4}>
|
||||
{(() => {
|
||||
const Icon = iconMap[value as IconKey].icon;
|
||||
return <Icon size={20} stroke={1.5} />;
|
||||
})()}
|
||||
</Box>
|
||||
)
|
||||
) : null
|
||||
}
|
||||
withCheckIcon={false}
|
||||
searchable={false}
|
||||
rightSectionWidth={0}
|
||||
searchable
|
||||
styles={{
|
||||
input: {
|
||||
textAlign: 'left',
|
||||
fontSize: rem(16),
|
||||
paddingLeft: 40,
|
||||
},
|
||||
section: {
|
||||
left: 10,
|
||||
right: 'auto',
|
||||
fontSize: rem(16),
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
76
src/app/admin/(dashboard)/_com/selectSocialMedia.tsx
Normal file
76
src/app/admin/(dashboard)/_com/selectSocialMedia.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
'use client';
|
||||
|
||||
import { Box, Image, Select, rem } from '@mantine/core';
|
||||
|
||||
const sosmedMap = {
|
||||
facebook: { label: 'Facebook', src: '/assets/images/sosmed/facebook.png' },
|
||||
instagram: { label: 'Instagram', src: '/assets/images/sosmed/instagram.png' },
|
||||
tiktok: { label: 'Tiktok', src: '/assets/images/sosmed/tiktok.png' },
|
||||
youtube: { label: 'YouTube', src: '/assets/images/sosmed/youtube.png' },
|
||||
whatsapp: { label: 'WhatsApp', src: '/assets/images/sosmed/whatsapp.png' },
|
||||
gmail: { label: 'Gmail', src: '/assets/images/sosmed/gmail.png' },
|
||||
telegram: { label: 'Telegram', src: '/assets/images/sosmed/telegram.png' },
|
||||
x: { label: 'X (Twitter)', src: '/assets/images/sosmed/x-twitter.png' },
|
||||
telephone: { label: 'Telephone', src: '/assets/images/sosmed/telephone-call.png' },
|
||||
custom: { label: 'Custom Icon', src: null },
|
||||
};
|
||||
|
||||
type SosmedKey = keyof typeof sosmedMap;
|
||||
|
||||
const sosmedList = Object.entries(sosmedMap).map(([value, item]) => ({
|
||||
value,
|
||||
label: item.label,
|
||||
}));
|
||||
|
||||
export default function SelectSosialMedia({
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
value: SosmedKey;
|
||||
onChange: (value: SosmedKey) => void;
|
||||
}) {
|
||||
const selected = value;
|
||||
const selectedImage = sosmedMap[selected]?.src;
|
||||
|
||||
return (
|
||||
<Box maw={300}>
|
||||
<Select
|
||||
placeholder="Pilih sosial media"
|
||||
value={selected}
|
||||
data={sosmedList}
|
||||
searchable={false}
|
||||
withCheckIcon={false}
|
||||
onChange={(val) => val && onChange(val as SosmedKey)}
|
||||
styles={{
|
||||
input: {
|
||||
textAlign: 'left',
|
||||
fontSize: rem(16),
|
||||
paddingLeft: 36,
|
||||
},
|
||||
section: {
|
||||
left: 10,
|
||||
right: 'auto',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 🔥 PREVIEW DIPISAH DI LUAR SELECT */}
|
||||
{selectedImage && (
|
||||
<Box mt="md">
|
||||
<Image
|
||||
alt=""
|
||||
src={selectedImage}
|
||||
radius="md"
|
||||
style={{
|
||||
width: 120,
|
||||
height: 120,
|
||||
objectFit: 'contain',
|
||||
border: '1px solid #eee',
|
||||
padding: 8,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
56
src/app/admin/(dashboard)/_com/selectSocialMediaEdit.tsx
Normal file
56
src/app/admin/(dashboard)/_com/selectSocialMediaEdit.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
'use client';
|
||||
|
||||
import { Box, Select } from '@mantine/core';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export const sosmedMap = {
|
||||
facebook: { label: 'Facebook', src: '/assets/images/sosmed/facebook.png' },
|
||||
instagram: { label: 'Instagram', src: '/assets/images/sosmed/instagram.png' },
|
||||
tiktok: { label: 'Tiktok', src: '/assets/images/sosmed/tiktok.png' },
|
||||
youtube: { label: 'YouTube', src: '/assets/images/sosmed/youtube.png' },
|
||||
whatsapp: { label: 'WhatsApp', src: '/assets/images/sosmed/whatsapp.png' },
|
||||
gmail: { label: 'Gmail', src: '/assets/images/sosmed/gmail.png' },
|
||||
telegram: { label: 'Telegram', src: '/assets/images/sosmed/telegram.png' },
|
||||
x: { label: 'X (Twitter)', src: '/assets/images/sosmed/x-twitter.png' },
|
||||
telephone: { label: 'Telephone', src: '/assets/images/sosmed/telephone-call.png' },
|
||||
custom: { label: 'Custom Icon', src: null },
|
||||
};
|
||||
|
||||
type SosmedKey = keyof typeof sosmedMap;
|
||||
|
||||
const sosmedList = Object.entries(sosmedMap).map(([value, item]) => ({
|
||||
value,
|
||||
label: item.label,
|
||||
}));
|
||||
|
||||
export default function SelectSocialMediaEdit({
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
value: string;
|
||||
onChange: (val: SosmedKey) => void;
|
||||
}) {
|
||||
const [selected, setSelected] = useState<SosmedKey>('facebook');
|
||||
|
||||
useEffect(() => {
|
||||
if (value && sosmedMap[value as SosmedKey]) {
|
||||
setSelected(value as SosmedKey);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Select
|
||||
label="Jenis Media Sosial"
|
||||
value={selected}
|
||||
data={sosmedList}
|
||||
searchable={false}
|
||||
onChange={(val) => {
|
||||
if (!val) return;
|
||||
setSelected(val as SosmedKey);
|
||||
onChange(val as SosmedKey);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -75,17 +75,18 @@ const berita = proxy({
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||
berita.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
const startTime = Date.now();
|
||||
berita.findMany.loading = true;
|
||||
berita.findMany.page = page;
|
||||
berita.findMany.search = search;
|
||||
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (kategori) query.kategori = kategori;
|
||||
|
||||
|
||||
const res = await ApiFetch.api.desa.berita["find-many"].get({ query });
|
||||
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
berita.findMany.data = res.data.data ?? [];
|
||||
berita.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
@@ -98,9 +99,16 @@ const berita = proxy({
|
||||
berita.findMany.data = [];
|
||||
berita.findMany.totalPages = 1;
|
||||
} finally {
|
||||
berita.findMany.loading = false;
|
||||
// pastikan minimal 300ms sebelum loading = false (biar UX smooth)
|
||||
const elapsed = Date.now() - startTime;
|
||||
const minDelay = 300;
|
||||
const delay = elapsed < minDelay ? minDelay - elapsed : 0;
|
||||
|
||||
setTimeout(() => {
|
||||
berita.findMany.loading = false;
|
||||
}, delay);
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
|
||||
@@ -71,6 +71,22 @@ const pelayananPendudukNonPermanenForm = {
|
||||
deskripsi: "",
|
||||
};
|
||||
|
||||
const templateAjukanForm = z.object({
|
||||
nama: z.string().min(1).max(5000),
|
||||
nik: z.string().min(1).max(5000),
|
||||
alamat: z.string().min(1).max(5000),
|
||||
nomorKk: z.string().min(1).max(5000),
|
||||
kategoriId: z.string().min(1).max(5000),
|
||||
});
|
||||
|
||||
const defaultAjukanForm = {
|
||||
nama: "",
|
||||
nik: "",
|
||||
alamat: "",
|
||||
nomorKk: "",
|
||||
kategoriId: "",
|
||||
};
|
||||
|
||||
const suratKeterangan = proxy({
|
||||
create: {
|
||||
form: { ...suratKeteranganForm },
|
||||
@@ -146,6 +162,30 @@ const suratKeterangan = proxy({
|
||||
}
|
||||
},
|
||||
},
|
||||
findManyAll: {
|
||||
data: null as Prisma.PelayananSuratKeteranganGetPayload<{
|
||||
omit: { isActive: true };
|
||||
}>[] | null,
|
||||
loading: false,
|
||||
load: async () => {
|
||||
suratKeterangan.findManyAll.loading = true;
|
||||
try {
|
||||
const res = await ApiFetch.api.desa.layanan.pelayanansuratketerangan["findManyAll"].get();
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
suratKeterangan.findManyAll.data = res.data.data || [];
|
||||
} else {
|
||||
suratKeterangan.findManyAll.data = [];
|
||||
console.error("Failed to load surat keterangan all:", res.data?.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading surat keterangan all:", error);
|
||||
suratKeterangan.findManyAll.data = [];
|
||||
} finally {
|
||||
suratKeterangan.findManyAll.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.PelayananSuratKeteranganGetPayload<{
|
||||
include: {
|
||||
@@ -541,33 +581,24 @@ const pelayananPerizinanBerusaha = proxy({
|
||||
findById: {
|
||||
data: null as pelayananPerizinanBerusahaForm | null,
|
||||
loading: false,
|
||||
initialize() {
|
||||
pelayananPerizinanBerusaha.findById.data = {
|
||||
id: "",
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
link: "",
|
||||
} as pelayananPerizinanBerusahaForm;
|
||||
},
|
||||
async load(id: string) {
|
||||
try {
|
||||
pelayananPerizinanBerusaha.findById.loading = true;
|
||||
const res = await fetch(
|
||||
`/api/desa/layanan/pelayananperizinanberusaha/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
pelayananPerizinanBerusaha.findById.data = data.data ?? null;
|
||||
} else {
|
||||
console.error(
|
||||
"Failed to fetch pelayanan perizinan berusaha:",
|
||||
res.statusText
|
||||
);
|
||||
pelayananPerizinanBerusaha.findById.data = null;
|
||||
this.loading = true;
|
||||
const response = await fetch(`/api/desa/layanan/pelayananperizinanberusaha/${id}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
this.data = result.data; // Make sure this matches your API response structure
|
||||
}
|
||||
return result?.data || null;
|
||||
} catch (error) {
|
||||
console.error("Error fetching pelayanan perizinan berusaha:", error);
|
||||
pelayananPerizinanBerusaha.findById.data = null;
|
||||
console.error('Error loading data:', error);
|
||||
toast.error('Gagal memuat data');
|
||||
return null;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -769,11 +800,250 @@ const pelayananPendudukNonPermanen = proxy({
|
||||
},
|
||||
});
|
||||
|
||||
const ajukanPermohonan = proxy({
|
||||
create: {
|
||||
form: { ...defaultAjukanForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateAjukanForm.safeParse(
|
||||
ajukanPermohonan.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
ajukanPermohonan.create.loading = true;
|
||||
const res = await ApiFetch.api.desa.ajukanpermohonan[
|
||||
"create"
|
||||
].post(ajukanPermohonan.create.form);
|
||||
if (res.status === 200) {
|
||||
ajukanPermohonan.findMany.load();
|
||||
return toast.success("Ajukan permohonan berhasil disimpan!");
|
||||
}
|
||||
return toast.error("Gagal menyimpan ajukan permohonan");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
ajukanPermohonan.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
ajukanPermohonan.create.form = { ...defaultAjukanForm };
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Prisma.AjukanPermohonanGetPayload<{
|
||||
include: {
|
||||
kategori: true;
|
||||
};
|
||||
}>[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
ajukanPermohonan.findMany.loading = true; // Use the full path to access the property
|
||||
ajukanPermohonan.findMany.page = page;
|
||||
ajukanPermohonan.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
const res = await ApiFetch.api.desa.ajukanpermohonan[
|
||||
"findMany"
|
||||
].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
ajukanPermohonan.findMany.data = res.data.data || [];
|
||||
ajukanPermohonan.findMany.total = res.data.total || 0;
|
||||
ajukanPermohonan.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load ajukan permohonan:", res.data?.message);
|
||||
ajukanPermohonan.findMany.data = [];
|
||||
ajukanPermohonan.findMany.total = 0;
|
||||
ajukanPermohonan.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading ajukan permohonan:", error);
|
||||
ajukanPermohonan.findMany.data = [];
|
||||
ajukanPermohonan.findMany.total = 0;
|
||||
ajukanPermohonan.findMany.totalPages = 1;
|
||||
} finally {
|
||||
ajukanPermohonan.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.AjukanPermohonanGetPayload<{
|
||||
include: {
|
||||
kategori: true;
|
||||
}
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/desa/ajukanpermohonan/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
ajukanPermohonan.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch ajukan permohonan:", res.statusText);
|
||||
ajukanPermohonan.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching ajukan permohonan:", error);
|
||||
ajukanPermohonan.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
try {
|
||||
ajukanPermohonan.delete.loading = true;
|
||||
const response = await fetch(
|
||||
`/api/desa/ajukanpermohonan/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
const result = await response.json();
|
||||
if (response.ok) {
|
||||
toast.success(result.message || "Ajukan permohonan berhasil dihapus");
|
||||
await ajukanPermohonan.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result.message || "Gagal menghapus ajukan permohonan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus ajukan permohonan");
|
||||
} finally {
|
||||
ajukanPermohonan.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultAjukanForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/desa/ajukanpermohonan/${id}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
nama: data.nama,
|
||||
nik: data.nik,
|
||||
alamat: data.alamat,
|
||||
nomorKk: data.nomorKk,
|
||||
kategoriId: data.kategoriId,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching ajukan permohonan:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateAjukanForm.safeParse(
|
||||
ajukanPermohonan.edit.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
ajukanPermohonan.edit.loading = true;
|
||||
const response = await fetch(
|
||||
`/api/desa/ajukanpermohonan/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nama: this.form.nama,
|
||||
nik: this.form.nik,
|
||||
alamat: this.form.alamat,
|
||||
nomorKk: this.form.nomorKk,
|
||||
kategoriId: this.form.kategoriId,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(result.message || "Ajukan permohonan berhasil diupdate");
|
||||
await ajukanPermohonan.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal mengupdate ajukan permohonan"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating ajukan permohonan:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update ajukan permohonan"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
ajukanPermohonan.edit.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const stateLayananDesa = proxy({
|
||||
suratKeterangan,
|
||||
pelayananPerizinanBerusaha,
|
||||
pelayananTelunjukSaktiDesa,
|
||||
pelayananPendudukNonPermanen,
|
||||
ajukanPermohonan,
|
||||
});
|
||||
|
||||
export default stateLayananDesa;
|
||||
|
||||
@@ -39,7 +39,7 @@ const penghargaanState = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
penghargaanState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -68,7 +68,7 @@ const category = proxy({
|
||||
const res = await ApiFetch.api.desa.kategoripengumuman[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
query: { page, limit, search },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
@@ -287,7 +287,7 @@ const pengumuman = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
pengumuman.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -65,7 +65,7 @@ const potensiDesa = proxy({
|
||||
const res = await ApiFetch.api.desa.potensi[
|
||||
"find-many"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
query: { page, limit, search },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
|
||||
@@ -101,6 +101,38 @@ const ApbDesa = proxy({
|
||||
}
|
||||
},
|
||||
},
|
||||
findFirst: {
|
||||
data: null as Prisma.ApbDesaGetPayload<{
|
||||
include: { pendapatan: true; belanja: true; pembiayaan: true };
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(params?: Record<string, any>) {
|
||||
try {
|
||||
this.loading = true;
|
||||
|
||||
// ✅ request ke endpoint find-first
|
||||
const res = await ApiFetch.api.ekonomi.pendapatanaslidesa.apbdesa[
|
||||
"find-first"
|
||||
].get({ query: params || {} });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
this.data = res.data.data ?? null;
|
||||
} else {
|
||||
this.data = null;
|
||||
toast.error(res.data?.message || "Gagal memuat data pertama APB Desa");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error findFirst APB Desa:", error);
|
||||
toast.error("Gagal memuat data APB Desa pertama");
|
||||
this.data = null;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.data = null;
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...ApbDesaDefaultForm },
|
||||
|
||||
@@ -49,7 +49,7 @@ const demografiPekerjaan = proxy({
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
demografiPekerjaan.create.form = { ...defaultForm };
|
||||
demografiPekerjaan.findMany.load();
|
||||
return id;
|
||||
|
||||
@@ -47,7 +47,7 @@ const jumlahPendudukMiskin = proxy({
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
jumlahPendudukMiskin.create.form = {
|
||||
year: 0,
|
||||
totalPoorPopulation: 0,
|
||||
|
||||
@@ -89,7 +89,7 @@ const jumlahPengangguran = proxy({
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
jumlahPengangguran.create.form = { ...jumlahPengangguranForm };
|
||||
jumlahPengangguran.findMany.load();
|
||||
return id;
|
||||
|
||||
@@ -13,6 +13,7 @@ const templateForm = z.object({
|
||||
gaji: z.string(),
|
||||
deskripsi: z.string(),
|
||||
kualifikasi: z.string(),
|
||||
notelp: z.string(),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
@@ -23,6 +24,7 @@ const defaultForm = {
|
||||
gaji: "",
|
||||
deskripsi: "",
|
||||
kualifikasi: "",
|
||||
notelp: "",
|
||||
};
|
||||
|
||||
const lowonganKerjaState = proxy({
|
||||
@@ -45,7 +47,7 @@ const lowonganKerjaState = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
lowonganKerjaState.create.loading = false;
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
@@ -179,6 +181,7 @@ const lowonganKerjaState = proxy({
|
||||
gaji: data.gaji,
|
||||
deskripsi: data.deskripsi,
|
||||
kualifikasi: data.kualifikasi,
|
||||
notelp: data.notelp,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
@@ -218,6 +221,7 @@ const lowonganKerjaState = proxy({
|
||||
gaji: this.form.gaji,
|
||||
deskripsi: this.form.deskripsi,
|
||||
kualifikasi: this.form.kualifikasi,
|
||||
notelp: this.form.notelp,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
|
||||
@@ -12,6 +12,7 @@ const templatePasarDesaForm = z.object({
|
||||
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||
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"),
|
||||
});
|
||||
|
||||
const defaultPasarDesaForm = {
|
||||
@@ -21,6 +22,7 @@ const defaultPasarDesaForm = {
|
||||
imageId: "",
|
||||
rating: 0,
|
||||
kategoriId: [] as string[],
|
||||
kontak: "",
|
||||
};
|
||||
|
||||
const pasarDesa = proxy({
|
||||
@@ -188,6 +190,7 @@ const pasarDesa = proxy({
|
||||
imageId: data.imageId,
|
||||
rating: data.rating,
|
||||
kategoriId: data.kategoriId,
|
||||
kontak: data.kontak,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
@@ -225,6 +228,7 @@ const pasarDesa = proxy({
|
||||
imageId: this.form.imageId,
|
||||
rating: this.form.rating,
|
||||
kategoriId: this.form.kategoriId,
|
||||
kontak: this.form.kontak,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
@@ -308,15 +312,15 @@ const kategoriProduk = proxy({
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search2: "",
|
||||
load: async (page = 1, limit = 10, search2 = "") => {
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
kategoriProduk.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
kategoriProduk.findMany.page = page;
|
||||
kategoriProduk.findMany.search2 = search2;
|
||||
kategoriProduk.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search2) query.search2 = search2;
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get({ query });
|
||||
|
||||
@@ -336,6 +340,40 @@ const kategoriProduk = proxy({
|
||||
}
|
||||
},
|
||||
},
|
||||
// ✅ Versi findManyAll (ambil semua tanpa pagination)
|
||||
findManyAll: {
|
||||
data: null as
|
||||
| Prisma.KategoriProdukGetPayload<{
|
||||
omit: { isActive: true };
|
||||
}>[]
|
||||
| null,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (search = "") => {
|
||||
kategoriProduk.findManyAll.loading = true;
|
||||
kategoriProduk.findManyAll.search = search;
|
||||
|
||||
try {
|
||||
const query: any = {};
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many-all"].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kategoriProduk.findManyAll.data = res.data.data ?? [];
|
||||
} else {
|
||||
kategoriProduk.findManyAll.data = [];
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch kategori produk (all):", err);
|
||||
kategoriProduk.findManyAll.data = [];
|
||||
} finally {
|
||||
kategoriProduk.findManyAll.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KategoriProdukGetPayload<{
|
||||
omit: { isActive: true };
|
||||
|
||||
@@ -45,7 +45,7 @@ const programKemiskinanState = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
programKemiskinanState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -46,7 +46,7 @@ const grafikSektorUnggulan = proxy({
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
grafikSektorUnggulan.create.form = {
|
||||
name: "",
|
||||
description: "",
|
||||
|
||||
@@ -1,9 +1,173 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
import { toast } from "react-toastify";
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
type StrukturBumDesForm = Prisma.StrukturBumDesGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
name: true;
|
||||
imageId: true;
|
||||
image?: {
|
||||
select: {
|
||||
link: true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>;
|
||||
|
||||
const stateStruktur = proxy({
|
||||
struktur: {
|
||||
data: null as StrukturBumDesForm | null,
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/ekonomi/struktur-organisasi/${id}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
this.data = result.data;
|
||||
return result.data;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal mengambil data struktur");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Load struktur error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat mengambil data struktur");
|
||||
return null;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.data = null;
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
},
|
||||
},
|
||||
|
||||
editStruktur: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
isReadOnly: false,
|
||||
|
||||
initialize(strukturData: StrukturBumDesForm) {
|
||||
this.id = strukturData.id;
|
||||
this.isReadOnly = false;
|
||||
this.form = {
|
||||
name: strukturData.name || "",
|
||||
imageId: strukturData.imageId || "",
|
||||
};
|
||||
},
|
||||
|
||||
updateField(field: keyof typeof defaultForm, value: string) {
|
||||
this.form[field] = value;
|
||||
},
|
||||
|
||||
async submit() {
|
||||
const validation = templateForm.safeParse(this.form);
|
||||
|
||||
if (!validation.success) {
|
||||
const errors = validation.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/ekonomi/struktur-organisasi/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update struktur");
|
||||
await stateStruktur.struktur.load(this.id);
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update struktur");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Update struktur error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat update struktur");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...defaultForm };
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
this.isReadOnly = false;
|
||||
},
|
||||
},
|
||||
|
||||
async loadForEdit(id: string) {
|
||||
const strukturData = await this.struktur.load(id);
|
||||
if (strukturData) {
|
||||
this.editStruktur.initialize(strukturData);
|
||||
}
|
||||
return strukturData;
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.struktur.reset();
|
||||
this.editStruktur.reset();
|
||||
},
|
||||
});
|
||||
|
||||
const templatePosisiOrganisasi = z.object({
|
||||
nama: z.string().min(1, "Nama harus diisi"),
|
||||
@@ -30,9 +194,7 @@ const posisiOrganisasi = proxy({
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
|
||||
"posisi-organisasi"
|
||||
]["create"].post(this.form);
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["create"].post(this.form);
|
||||
if (res.status === 200) {
|
||||
toast.success("Berhasil menambahkan posisi organisasi");
|
||||
posisiOrganisasi.findMany.load();
|
||||
@@ -52,6 +214,29 @@ const posisiOrganisasi = proxy({
|
||||
},
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as Prisma.StrukturOrganisasiBumDesGetPayload<{
|
||||
omit: { isActive: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/ekonomi/struktur-organisasi/posisi-organisasi/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
posisiOrganisasi.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch posisiOrganisasi:", res.statusText);
|
||||
posisiOrganisasi.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching posisiOrganisasi:", error);
|
||||
posisiOrganisasi.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...posisiOrganisasiDefaultForm },
|
||||
@@ -165,17 +350,17 @@ const posisiOrganisasi = proxy({
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
posisiOrganisasi.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
load: async (page = 1, limit?: number, search = "") => {
|
||||
const appliedLimit = limit ?? 10;
|
||||
posisiOrganisasi.findMany.page = page;
|
||||
posisiOrganisasi.findMany.search = search;
|
||||
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
const query: any = { page, limit: appliedLimit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["find-many"].get({ query });
|
||||
|
||||
|
||||
const res = await ApiFetch.api.ekonomi['struktur-organisasi']['posisi-organisasi']['find-many'].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
posisiOrganisasi.findMany.data = res.data.data ?? [];
|
||||
posisiOrganisasi.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
@@ -192,7 +377,42 @@ const posisiOrganisasi = proxy({
|
||||
}
|
||||
},
|
||||
},
|
||||
findManyAll: {
|
||||
data: [] as Array<{
|
||||
id: string;
|
||||
nama: string;
|
||||
deskripsi: string | null;
|
||||
hierarki: number;
|
||||
}>,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (search = "") => {
|
||||
// Change to arrow function
|
||||
posisiOrganisasi.findManyAll.loading = true; // Use the full path to access the property
|
||||
posisiOrganisasi.findManyAll.search = search;
|
||||
try {
|
||||
const query: any = { search };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.ekonomi['struktur-organisasi']['posisi-organisasi']['find-many-all'].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
posisiOrganisasi.findManyAll.data = res.data.data || [];
|
||||
|
||||
} else {
|
||||
console.error("Failed to load posisiOrganisasi:", res.data?.message);
|
||||
posisiOrganisasi.findManyAll.data = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading posisiOrganisasi:", error);
|
||||
posisiOrganisasi.findManyAll.data = [];
|
||||
} finally {
|
||||
posisiOrganisasi.findManyAll.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
@@ -231,12 +451,12 @@ const posisiOrganisasi = proxy({
|
||||
|
||||
const templatePegawai = z.object({
|
||||
namaLengkap: z.string().min(1, "Nama wajib diisi"),
|
||||
gelarAkademik: z.string().optional(),
|
||||
imageId: z.string().nullable().optional(),
|
||||
tanggalMasuk: z.string().optional(), // ISO format
|
||||
gelarAkademik: z.string().min(1, "Gelar Akademik wajib diisi"),
|
||||
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||
tanggalMasuk: z.string().min(1, "Tanggal masuk wajib diisi"), // ISO format
|
||||
email: z.string().email("Email tidak valid").optional(),
|
||||
telepon: z.string().optional(),
|
||||
alamat: z.string().optional(),
|
||||
telepon: z.string().min(1, "Telepom wajib diisi"),
|
||||
alamat: z.string().min(1, "Alamat wajib diisi"),
|
||||
posisiId: z.string().min(1, "Posisi wajib diisi"),
|
||||
isActive: z.boolean().default(true),
|
||||
});
|
||||
@@ -267,9 +487,9 @@ const pegawai = proxy({
|
||||
|
||||
try {
|
||||
pegawai.create.loading = true;
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
|
||||
"pegawai"
|
||||
]["create"].post(pegawai.create.form);
|
||||
const res = await ApiFetch.api.ekonomi['struktur-organisasi'].pegawai['create'].post(
|
||||
pegawai.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
toast.success("Pegawai berhasil ditambahkan");
|
||||
await pegawai.findMany.load();
|
||||
@@ -286,45 +506,56 @@ const pegawai = proxy({
|
||||
},
|
||||
|
||||
// In struktur-organisasi.ts
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
pegawai.findMany.loading = true; // Use the full path to access the property
|
||||
pegawai.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
|
||||
"pegawai"
|
||||
]["find-many"].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PegawaiBumDesGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
posisi: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
pegawai.findMany.loading = true; // Use the full path to access the property
|
||||
pegawai.findMany.page = page;
|
||||
pegawai.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pegawai.findMany.data = res.data.data || [];
|
||||
pegawai.findMany.total = res.data.total || 0;
|
||||
pegawai.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load pegawai:", res.data?.message);
|
||||
const res = await ApiFetch.api.ekonomi['struktur-organisasi'].pegawai['find-many'].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pegawai.findMany.data = res.data.data || [];
|
||||
pegawai.findMany.total = res.data.total || 0;
|
||||
pegawai.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load pegawai:", res.data?.message);
|
||||
pegawai.findMany.data = [];
|
||||
pegawai.findMany.total = 0;
|
||||
pegawai.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pegawai:", error);
|
||||
pegawai.findMany.data = [];
|
||||
pegawai.findMany.total = 0;
|
||||
pegawai.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pegawai.findMany.loading = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pegawai:", error);
|
||||
pegawai.findMany.data = [];
|
||||
pegawai.findMany.total = 0;
|
||||
pegawai.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pegawai.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as
|
||||
| (Prisma.PegawaiGetPayload<{
|
||||
| (Prisma.PegawaiBumDesGetPayload<{
|
||||
include: { posisi: true; image: true };
|
||||
}> & { isActive: boolean })
|
||||
| null,
|
||||
@@ -350,12 +581,9 @@ findMany: {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
try {
|
||||
pegawai.delete.loading = true;
|
||||
const res = await fetch(
|
||||
`/api/ekonomi/struktur-organisasi/pegawai/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
}
|
||||
);
|
||||
const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/del/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
const json = await res.json();
|
||||
if (res.ok) {
|
||||
toast.success(json.message ?? "Berhasil hapus pegawai");
|
||||
@@ -372,6 +600,31 @@ findMany: {
|
||||
},
|
||||
},
|
||||
|
||||
nonActive: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
try {
|
||||
pegawai.nonActive.loading = true;
|
||||
const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/non-active/${id}`, {
|
||||
method: "DELETE", // biasanya nonActive pakai PATCH
|
||||
});
|
||||
const json = await res.json();
|
||||
if (res.ok) {
|
||||
toast.success(json.message ?? "Pegawai berhasil dinonaktifkan");
|
||||
await pegawai.findMany.load(); // refresh data
|
||||
} else {
|
||||
toast.error(json.message ?? "Gagal menonaktifkan pegawai");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal nonActive:", error);
|
||||
toast.error("Terjadi kesalahan saat menonaktifkan pegawai");
|
||||
} finally {
|
||||
pegawai.nonActive.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...pegawaiDefaultForm },
|
||||
@@ -384,15 +637,12 @@ findMany: {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/ekonomi/struktur-organisasi/pegawai/${id}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
const response = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
@@ -503,299 +753,10 @@ findMany: {
|
||||
},
|
||||
});
|
||||
|
||||
// Schema Zod untuk form validasi
|
||||
const templateHubunganOrganisasiForm = z.object({
|
||||
atasanId: z.string().min(1, "Atasan wajib dipilih"),
|
||||
bawahanId: z.string().min(1, "Bawahan wajib dipilih"),
|
||||
tipe: z.string().optional(),
|
||||
});
|
||||
|
||||
// Default form state
|
||||
const defaultHubunganOrganisasiForm = {
|
||||
atasanId: "",
|
||||
bawahanId: "",
|
||||
tipe: "",
|
||||
};
|
||||
|
||||
// ====================== STATE ===========================
|
||||
const hubunganOrganisasi = proxy({
|
||||
create: {
|
||||
form: { ...defaultHubunganOrganisasiForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateHubunganOrganisasiForm.safeParse(
|
||||
hubunganOrganisasi.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}: ${v.message}`)
|
||||
.join("\n")}]`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
hubunganOrganisasi.create.loading = true;
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
|
||||
"hubungan-organisasi"
|
||||
]["create"].post(hubunganOrganisasi.create.form);
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
hubunganOrganisasi.findMany.load();
|
||||
return toast.success("Berhasil menambahkan hubungan organisasi");
|
||||
} else {
|
||||
return toast.error(res.data?.message || "Gagal menambahkan data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal create:", error);
|
||||
toast.error("Terjadi kesalahan saat menambahkan");
|
||||
} finally {
|
||||
hubunganOrganisasi.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<{
|
||||
id: string;
|
||||
atasanId: string;
|
||||
bawahanId: string;
|
||||
tipe?: string | null;
|
||||
atasan: {
|
||||
id: string;
|
||||
namaLengkap: string;
|
||||
gelarAkademik: string | null;
|
||||
imageId: string | null;
|
||||
tanggalMasuk: Date | null;
|
||||
email: string | null;
|
||||
telepon: string | null;
|
||||
alamat: string | null;
|
||||
posisiId: string;
|
||||
isActive: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
bawahan: {
|
||||
id: string;
|
||||
namaLengkap: string;
|
||||
gelarAkademik: string | null;
|
||||
imageId: string | null;
|
||||
tanggalMasuk: Date | null;
|
||||
email: string | null;
|
||||
telepon: string | null;
|
||||
alamat: string | null;
|
||||
posisiId: string;
|
||||
isActive: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
}> | null,
|
||||
|
||||
async load() {
|
||||
try {
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
|
||||
"hubungan-organisasi"
|
||||
]["find-many"].get();
|
||||
|
||||
if (res.status === 200) {
|
||||
hubunganOrganisasi.findMany.data = (res.data?.data ?? []).map(
|
||||
(item: any) => ({
|
||||
...item,
|
||||
atasan: item.atasan
|
||||
? {
|
||||
...item.atasan,
|
||||
isActive: item.atasan.isActive ?? item.atasan.aktif ?? true,
|
||||
}
|
||||
: null,
|
||||
bawahan: item.bawahan
|
||||
? {
|
||||
...item.bawahan,
|
||||
isActive:
|
||||
item.bawahan.isActive ?? item.bawahan.aktif ?? true,
|
||||
}
|
||||
: null,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
hubunganOrganisasi.findMany.data = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Fetch list error:", error);
|
||||
toast.error("Gagal memuat data hubungan organisasi");
|
||||
hubunganOrganisasi.findMany.data = [];
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as {
|
||||
id: string;
|
||||
atasanId: string;
|
||||
bawahanId: string;
|
||||
tipe?: string | null;
|
||||
atasan?: {
|
||||
id: string;
|
||||
namaLengkap: string;
|
||||
gelarAkademik: string | null;
|
||||
imageId: string;
|
||||
tanggalMasuk: Date | null;
|
||||
email: string | null;
|
||||
telepon: string | null;
|
||||
alamat: string | null;
|
||||
posisiId: string;
|
||||
aktif: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
bawahan?: {
|
||||
id: string;
|
||||
namaLengkap: string;
|
||||
gelarAkademik: string | null;
|
||||
imageId: string;
|
||||
tanggalMasuk: Date | null;
|
||||
email: string | null;
|
||||
telepon: string | null;
|
||||
alamat: string | null;
|
||||
posisiId: string;
|
||||
aktif: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
} | null,
|
||||
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}`
|
||||
);
|
||||
const result = await res.json();
|
||||
|
||||
if (res.ok && result?.success) {
|
||||
hubunganOrganisasi.findUnique.data = result.data;
|
||||
} else {
|
||||
hubunganOrganisasi.findUnique.data = null;
|
||||
toast.error(result?.message || "Gagal mengambil data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Find unique error:", error);
|
||||
hubunganOrganisasi.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultHubunganOrganisasiForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}`
|
||||
);
|
||||
const result = await res.json();
|
||||
|
||||
if (res.ok && result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
atasanId: data.atasanId,
|
||||
bawahanId: data.bawahanId,
|
||||
tipe: data.tipe || "",
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateHubunganOrganisasiForm.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}: ${v.message}`)
|
||||
.join("\n")}]`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await fetch(
|
||||
`/api/ekonomi/struktur-organisasi/hubungan-organisasi/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
);
|
||||
|
||||
const result = await res.json();
|
||||
if (res.ok && result.success) {
|
||||
await hubunganOrganisasi.findMany.load();
|
||||
toast.success("Berhasil mengupdate hubungan organisasi");
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal mengupdate");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Update error:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal update");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
hubunganOrganisasi.edit.id = "";
|
||||
hubunganOrganisasi.edit.form = { ...defaultHubunganOrganisasiForm };
|
||||
},
|
||||
},
|
||||
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
hubunganOrganisasi.delete.loading = true;
|
||||
const res = await fetch(
|
||||
`/api/ekonomi/struktur-organisasi/hubungan-organisasi/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
}
|
||||
);
|
||||
|
||||
const result = await res.json();
|
||||
if (res.ok && result?.success) {
|
||||
toast.success("Hubungan organisasi berhasil dihapus");
|
||||
hubunganOrganisasi.findMany.load();
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus hubungan organisasi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Delete error:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus");
|
||||
} finally {
|
||||
hubunganOrganisasi.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const strukturorganisasiState = proxy({
|
||||
const stateStrukturBumDes = proxy({
|
||||
stateStruktur,
|
||||
posisiOrganisasi,
|
||||
pegawai,
|
||||
hubunganOrganisasi,
|
||||
});
|
||||
|
||||
export default strukturorganisasiState;
|
||||
export default stateStrukturBumDes;
|
||||
|
||||
@@ -51,7 +51,7 @@ const grafikBerdasarkanUsiaKerjaNganggur = proxy({
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
grafikBerdasarkanUsiaKerjaNganggur.create.form = {
|
||||
usia18_25: "",
|
||||
usia26_35: "",
|
||||
@@ -255,7 +255,7 @@ const grafikBerdasarkanPendidikan = proxy({
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
grafikBerdasarkanPendidikan.create.form = {
|
||||
SD: "",
|
||||
SMP: "",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -61,10 +62,37 @@ const ajukanIdeInovatifState = proxy({
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.inovasi.ajukanideinovatif["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
ajukanIdeInovatifState.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
ajukanIdeInovatifState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
ajukanIdeInovatifState.findMany.page = page;
|
||||
ajukanIdeInovatifState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res =
|
||||
await ApiFetch.api.inovasi.ajukanideinovatif[
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
ajukanIdeInovatifState.findMany.data = res.data.data ?? [];
|
||||
ajukanIdeInovatifState.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
ajukanIdeInovatifState.findMany.data = [];
|
||||
ajukanIdeInovatifState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch ajukan ide inovatif paginated:", err);
|
||||
ajukanIdeInovatifState.findMany.data = [];
|
||||
ajukanIdeInovatifState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
ajukanIdeInovatifState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -97,16 +125,21 @@ const ajukanIdeInovatifState = proxy({
|
||||
|
||||
try {
|
||||
ajukanIdeInovatifState.delete.loading = true;
|
||||
const response = await fetch(`/api/inovasi/ajukanideinovatif/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const response = await fetch(
|
||||
`/api/inovasi/ajukanideinovatif/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
toast.success(result.message || "Ajukan Ide Inovatif berhasil dihapus");
|
||||
toast.success(
|
||||
result.message || "Ajukan Ide Inovatif berhasil dihapus"
|
||||
);
|
||||
await ajukanIdeInovatifState.findMany.load();
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus ajukan ide inovatif");
|
||||
|
||||
@@ -37,7 +37,7 @@ const desaDigitalState = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
desaDigitalState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -37,7 +37,7 @@ const infoTeknoState = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
infoTeknoState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -54,19 +55,20 @@ const administrasiOnline = proxy({
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<
|
||||
Prisma.AdministrasiOnlineGetPayload<{
|
||||
include: {
|
||||
jenisLayanan: true;
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
Prisma.AdministrasiOnlineGetPayload<{
|
||||
include: {
|
||||
jenisLayanan: true;
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
|
||||
async load(page = 1, limit = 10) {
|
||||
search: "",
|
||||
async load(page = 1, limit = 10, search = "") {
|
||||
administrasiOnline.findMany.loading = true;
|
||||
administrasiOnline.findMany.page = page;
|
||||
administrasiOnline.findMany.search = search;
|
||||
try {
|
||||
const res =
|
||||
await ApiFetch.api.inovasi.layananonlinedesa.administrasionline[
|
||||
@@ -75,6 +77,7 @@ const administrasiOnline = proxy({
|
||||
query: {
|
||||
page,
|
||||
limit,
|
||||
search,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -91,10 +94,10 @@ const administrasiOnline = proxy({
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.AdministrasiOnlineGetPayload<{
|
||||
include: {
|
||||
jenisLayanan: true;
|
||||
};
|
||||
}> | null,
|
||||
include: {
|
||||
jenisLayanan: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
@@ -199,13 +202,37 @@ const jenisLayanan = proxy({
|
||||
nama: string;
|
||||
deskripsi: string;
|
||||
}> | null,
|
||||
async load() {
|
||||
const res =
|
||||
await ApiFetch.api.inovasi.layananonlinedesa.administrasionline.jenislayanan[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
jenisLayanan.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
jenisLayanan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
jenisLayanan.findMany.page = page;
|
||||
jenisLayanan.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res =
|
||||
await ApiFetch.api.inovasi.layananonlinedesa.administrasionline.jenislayanan[
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
jenisLayanan.findMany.data = res.data.data ?? [];
|
||||
jenisLayanan.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
jenisLayanan.findMany.data = [];
|
||||
jenisLayanan.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch jenis layanan paginated:", err);
|
||||
jenisLayanan.findMany.data = [];
|
||||
jenisLayanan.findMany.totalPages = 1;
|
||||
} finally {
|
||||
jenisLayanan.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -403,7 +430,9 @@ const templatePengaduanMasyarakatForm = z.object({
|
||||
nik: z.string().min(1, "NIK minimal 1 karakter"),
|
||||
judulPengaduan: z.string().min(1, "Judul pengaduan minimal 1 karakter"),
|
||||
lokasiKejadian: z.string().min(1, "Lokasi kejadian minimal 1 karakter"),
|
||||
deskripsiPengaduan: z.string().min(1, "Deskripsi pengaduan minimal 1 karakter"),
|
||||
deskripsiPengaduan: z
|
||||
.string()
|
||||
.min(1, "Deskripsi pengaduan minimal 1 karakter"),
|
||||
jenisPengaduanId: z.string().min(1, "Jenis pengaduan minimal 1 karakter"),
|
||||
imageId: z.string().min(1, "Image minimal 1 karakter"),
|
||||
});
|
||||
@@ -455,37 +484,42 @@ const pengaduanMasyarakat = proxy({
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<
|
||||
Prisma.PengaduanMasyarakatGetPayload<{
|
||||
include: {
|
||||
jenisPengaduan: true;
|
||||
image: true;
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
Prisma.PengaduanMasyarakatGetPayload<{
|
||||
include: {
|
||||
jenisPengaduan: true;
|
||||
image: true;
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
|
||||
async load(page = 1, limit = 10) {
|
||||
pengaduanMasyarakat.findMany.loading = true;
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
pengaduanMasyarakat.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
pengaduanMasyarakat.findMany.page = page;
|
||||
pengaduanMasyarakat.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res =
|
||||
await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat[
|
||||
"find-many"
|
||||
].get({
|
||||
query: {
|
||||
page,
|
||||
limit,
|
||||
},
|
||||
});
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pengaduanMasyarakat.findMany.data = res.data.data ?? [];
|
||||
pengaduanMasyarakat.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
pengaduanMasyarakat.findMany.data = [];
|
||||
pengaduanMasyarakat.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch pengaduan masyarakat paginated:", err);
|
||||
pengaduanMasyarakat.findMany.data = [];
|
||||
pengaduanMasyarakat.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pengaduanMasyarakat.findMany.loading = false;
|
||||
}
|
||||
@@ -493,11 +527,11 @@ const pengaduanMasyarakat = proxy({
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.PengaduanMasyarakatGetPayload<{
|
||||
include: {
|
||||
jenisPengaduan: true;
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
include: {
|
||||
jenisPengaduan: true;
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
@@ -507,7 +541,10 @@ const pengaduanMasyarakat = proxy({
|
||||
const data = await res.json();
|
||||
pengaduanMasyarakat.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch pengaduan masyarakat:", res.statusText);
|
||||
console.error(
|
||||
"Failed to fetch pengaduan masyarakat:",
|
||||
res.statusText
|
||||
);
|
||||
pengaduanMasyarakat.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -542,7 +579,9 @@ const pengaduanMasyarakat = proxy({
|
||||
);
|
||||
await pengaduanMasyarakat.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus pengaduan masyarakat");
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus pengaduan masyarakat"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
@@ -567,7 +606,9 @@ const jenisPengaduan = proxy({
|
||||
form: { ...defaultJenisPengaduanForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateJenisPengaduanForm.safeParse(jenisPengaduan.create.form);
|
||||
const cek = templateJenisPengaduanForm.safeParse(
|
||||
jenisPengaduan.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
@@ -598,13 +639,37 @@ const jenisPengaduan = proxy({
|
||||
id: string;
|
||||
nama: string;
|
||||
}> | null,
|
||||
async load() {
|
||||
const res =
|
||||
await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat.jenispengaduan[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
jenisPengaduan.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
jenisPengaduan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
jenisPengaduan.findMany.page = page;
|
||||
jenisPengaduan.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res =
|
||||
await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat.jenispengaduan[
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
jenisPengaduan.findMany.data = res.data.data ?? [];
|
||||
jenisPengaduan.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
jenisPengaduan.findMany.data = [];
|
||||
jenisPengaduan.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch jenis pengaduan paginated:", err);
|
||||
jenisPengaduan.findMany.data = [];
|
||||
jenisPengaduan.findMany.totalPages = 1;
|
||||
} finally {
|
||||
jenisPengaduan.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -693,7 +758,7 @@ const jenisPengaduan = proxy({
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
nama: data.nama
|
||||
nama: data.nama,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
@@ -709,7 +774,9 @@ const jenisPengaduan = proxy({
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateJenisPengaduanForm.safeParse(jenisPengaduan.edit.form);
|
||||
const cek = templateJenisPengaduanForm.safeParse(
|
||||
jenisPengaduan.edit.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
@@ -759,7 +826,9 @@ const jenisPengaduan = proxy({
|
||||
await jenisPengaduan.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal mengupdate jenis pengaduan");
|
||||
throw new Error(
|
||||
result.message || "Gagal mengupdate jenis pengaduan"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// If JSON parsing fails, try to get the response text for better error messages
|
||||
@@ -792,7 +861,6 @@ const jenisPengaduan = proxy({
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const layananonlineDesa = proxy({
|
||||
administrasiOnline,
|
||||
jenisLayanan,
|
||||
|
||||
@@ -6,9 +6,9 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(1, "Nama minimal 1 karakter"),
|
||||
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||
slug: z.string().min(1, "Deskripsi singkat minimal 1 karakter"),
|
||||
name: z.string().min(5, "Nama minimal 5 karakter"),
|
||||
deskripsi: z.string().min(5, "Deskripsi minimal 5 karakter"),
|
||||
slug: z.string().min(5, "Deskripsi singkat minimal 5 karakter"),
|
||||
icon: z.string().min(1, "Icon minimal 1 karakter"),
|
||||
});
|
||||
|
||||
@@ -29,59 +29,64 @@ const programKreatifState = proxy({
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
toast.error(err);
|
||||
return false; // ⬅️ ini penting
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
programKreatifState.create.loading = true;
|
||||
const res = await ApiFetch.api.inovasi.programkreatif["create"].post(
|
||||
programKreatifState.create.form
|
||||
);
|
||||
|
||||
if (res.status === 200) {
|
||||
programKreatifState.findMany.load();
|
||||
return toast.success("success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
return true;
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
toast.error("failed create");
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat create");
|
||||
return false;
|
||||
} finally {
|
||||
programKreatifState.create.loading = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
},
|
||||
findMany: {
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => {
|
||||
// Change to arrow function
|
||||
programKreatifState.findMany.loading = true; // Use the full path to access the property
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
programKreatifState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
programKreatifState.findMany.page = page;
|
||||
programKreatifState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const res = await ApiFetch.api.inovasi.programkreatif["find-many"].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.inovasi.programkreatif[
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
programKreatifState.findMany.data = res.data.data || [];
|
||||
programKreatifState.findMany.total = res.data.total || 0;
|
||||
programKreatifState.findMany.totalPages = res.data.totalPages || 1;
|
||||
programKreatifState.findMany.data = res.data.data ?? [];
|
||||
programKreatifState.findMany.totalPages =
|
||||
res.data.totalPages ?? 1;
|
||||
} else {
|
||||
console.error(
|
||||
"Failed to load grafik berdasarkan jenis kelamin:",
|
||||
res.data?.message
|
||||
);
|
||||
programKreatifState.findMany.data = [];
|
||||
programKreatifState.findMany.total = 0;
|
||||
programKreatifState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading grafik berdasarkan jenis kelamin:", error);
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch program kreatif paginated:", err);
|
||||
programKreatifState.findMany.data = [];
|
||||
programKreatifState.findMany.total = 0;
|
||||
programKreatifState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
programKreatifState.findMany.loading = false;
|
||||
|
||||
@@ -37,7 +37,7 @@ const keamananLingkunganState = proxy({
|
||||
].post(keamananLingkunganState.create.form);
|
||||
if (res.status === 200) {
|
||||
keamananLingkunganState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -38,7 +38,7 @@ const kontakDaruratKeamananState = proxy({
|
||||
].post(kontakDaruratKeamananState.create.form);
|
||||
if (res.status === 200) {
|
||||
kontakDaruratKeamananState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
@@ -294,7 +294,7 @@ const kontakDaruratItem = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
kontakDaruratItem.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -88,7 +88,7 @@ const laporanPublikState = proxy({
|
||||
|
||||
if (res.status === 200) {
|
||||
laporanPublikState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
|
||||
console.log(res);
|
||||
|
||||
@@ -40,7 +40,7 @@ const pencegahanKriminalitasState = proxy({
|
||||
].post(pencegahanKriminalitasState.create.form);
|
||||
if (res.status === 200) {
|
||||
pencegahanKriminalitasState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -37,7 +37,7 @@ const tipsKeamananState = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
tipsKeamananState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -9,29 +9,30 @@ import { z } from "zod";
|
||||
// Validasi form
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
|
||||
informasiUmum: z.object({
|
||||
fasilitas: z.string().min(1, "Fasilitas harus diisi"),
|
||||
alamat: z.string().min(1, "Alamat harus diisi"),
|
||||
jamOperasional: z.string().min(1, "Jam operasional harus diisi"),
|
||||
fasilitas: z.string().min(1),
|
||||
alamat: z.string().min(1),
|
||||
jamOperasional: z.string().min(1),
|
||||
}),
|
||||
|
||||
layananUnggulan: z.object({
|
||||
content: z.string().min(1, "Layanan unggulan harus diisi"),
|
||||
}),
|
||||
dokterdanTenagaMedis: z.object({
|
||||
name: z.string().min(1, "Nama dokter harus diisi"),
|
||||
specialist: z.string().min(1, "Spesialis harus diisi"),
|
||||
jadwal: z.string().min(1, "Jadwal harus diisi"),
|
||||
content: z.string().min(1),
|
||||
}),
|
||||
|
||||
// NOW ARRAY OF STRING (ID)
|
||||
dokterdanTenagaMedis: z.array(z.string()).min(1, "Minimal pilih 1 dokter"),
|
||||
|
||||
fasilitasPendukung: z.object({
|
||||
content: z.string().min(1, "Fasilitas pendukung harus diisi"),
|
||||
content: z.string().min(1),
|
||||
}),
|
||||
|
||||
prosedurPendaftaran: z.object({
|
||||
content: z.string().min(1, "Prosedur pendaftaran harus diisi"),
|
||||
}),
|
||||
tarifDanLayanan: z.object({
|
||||
layanan: z.string().min(1, "Layanan harus diisi"),
|
||||
tarif: z.string().min(1, "Tarif harus diisi"),
|
||||
content: z.string().min(1),
|
||||
}),
|
||||
|
||||
// NOW ARRAY OF STRING (ID)
|
||||
tarifDanLayanan: z.array(z.string()).min(1, "Minimal pilih 1 tarif"),
|
||||
});
|
||||
|
||||
// Default form kosong
|
||||
@@ -45,21 +46,34 @@ const defaultForm = {
|
||||
layananUnggulan: {
|
||||
content: "",
|
||||
},
|
||||
dokterdanTenagaMedis: {
|
||||
name: "",
|
||||
specialist: "",
|
||||
jadwal: "",
|
||||
},
|
||||
|
||||
dokterdanTenagaMedis: [] as string[], // ← array kosong
|
||||
tarifDanLayanan: [] as string[], // ← array kosong
|
||||
|
||||
fasilitasPendukung: {
|
||||
content: "",
|
||||
},
|
||||
prosedurPendaftaran: {
|
||||
content: "",
|
||||
},
|
||||
tarifDanLayanan: {
|
||||
layanan: "",
|
||||
tarif: "",
|
||||
},
|
||||
};
|
||||
|
||||
type DokterItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
specialist: string;
|
||||
jadwal: string;
|
||||
jadwalLibur: string;
|
||||
jamBukaOperasional: string;
|
||||
jamTutupOperasional: string;
|
||||
jamBukaLibur: string;
|
||||
jamTutupLibur: string;
|
||||
};
|
||||
|
||||
type TarifItem = {
|
||||
id: string;
|
||||
layanan: string;
|
||||
tarif: string;
|
||||
};
|
||||
|
||||
const fasilitasKesehatan = proxy({
|
||||
@@ -186,33 +200,26 @@ const fasilitasKesehatan = proxy({
|
||||
|
||||
const result = await res.json();
|
||||
const data = result.data;
|
||||
|
||||
fasilitasKesehatan.edit.id = data.id;
|
||||
fasilitasKesehatan.edit.form = {
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
informasiUmum: {
|
||||
fasilitas: data.informasiumum.fasilitas,
|
||||
alamat: data.informasiumum.alamat,
|
||||
jamOperasional: data.informasiumum.jamOperasional,
|
||||
},
|
||||
layananUnggulan: {
|
||||
content: data.layananunggulan.content,
|
||||
},
|
||||
dokterdanTenagaMedis: {
|
||||
name: data.dokterdantenagamedis.name,
|
||||
specialist: data.dokterdantenagamedis.specialist,
|
||||
jadwal: data.dokterdantenagamedis.jadwal,
|
||||
},
|
||||
fasilitasPendukung: {
|
||||
content: data.fasilitaspendukung.content,
|
||||
},
|
||||
prosedurPendaftaran: {
|
||||
content: data.prosedurpendaftaran.content,
|
||||
},
|
||||
tarifDanLayanan: {
|
||||
layanan: data.tarifdanlayanan.layanan,
|
||||
tarif: data.tarifdanlayanan.tarif,
|
||||
// map relasi -> array of IDs
|
||||
layananUnggulan: {
|
||||
content: data.layananunggulan.content,
|
||||
},
|
||||
dokterdanTenagaMedis: data.dokterdantenagamedis?.map((v: DokterItem) => v.id) ?? [],
|
||||
tarifDanLayanan: data.tarifdanlayanan?.map((v: TarifItem) => v.id) ?? [],
|
||||
};
|
||||
},
|
||||
async submit() {
|
||||
@@ -238,22 +245,15 @@ const fasilitasKesehatan = proxy({
|
||||
layananUnggulan: {
|
||||
content: fasilitasKesehatan.edit.form.layananUnggulan.content,
|
||||
},
|
||||
dokterdanTenagaMedis: {
|
||||
name: fasilitasKesehatan.edit.form.dokterdanTenagaMedis.name,
|
||||
specialist:
|
||||
fasilitasKesehatan.edit.form.dokterdanTenagaMedis.specialist,
|
||||
jadwal: fasilitasKesehatan.edit.form.dokterdanTenagaMedis.jadwal,
|
||||
},
|
||||
dokterdanTenagaMedis:
|
||||
fasilitasKesehatan.edit.form.dokterdanTenagaMedis,
|
||||
fasilitasPendukung: {
|
||||
content: fasilitasKesehatan.edit.form.fasilitasPendukung.content,
|
||||
},
|
||||
prosedurPendaftaran: {
|
||||
content: fasilitasKesehatan.edit.form.prosedurPendaftaran.content,
|
||||
},
|
||||
tarifDanLayanan: {
|
||||
layanan: fasilitasKesehatan.edit.form.tarifDanLayanan.layanan,
|
||||
tarif: fasilitasKesehatan.edit.form.tarifDanLayanan.tarif,
|
||||
},
|
||||
tarifDanLayanan: fasilitasKesehatan.edit.form.tarifDanLayanan,
|
||||
};
|
||||
|
||||
const res = await fetch(
|
||||
@@ -320,12 +320,26 @@ const templateDokterForm = z.object({
|
||||
name: z.string().min(1, "Nama tidak boleh kosong"),
|
||||
specialist: z.string().min(1, "Spesialis tidak boleh kosong"),
|
||||
jadwal: z.string().min(1, "Jadwal tidak boleh kosong"),
|
||||
jadwalLibur: z.string().min(1, "Jadwal libur tidak boleh kosong"),
|
||||
jamBukaOperasional: z
|
||||
.string()
|
||||
.min(1, "Jam buka operasional tidak boleh kosong"),
|
||||
jamTutupOperasional: z
|
||||
.string()
|
||||
.min(1, "Jam tutup operasional tidak boleh kosong"),
|
||||
jamBukaLibur: z.string().min(1, "Jam buka libur tidak boleh kosong"),
|
||||
jamTutupLibur: z.string().min(1, "Jam tutup libur tidak boleh kosong"),
|
||||
});
|
||||
|
||||
const defaultDokterForm = {
|
||||
name: "",
|
||||
specialist: "",
|
||||
jadwal: "",
|
||||
jadwalLibur: "",
|
||||
jamBukaOperasional: "",
|
||||
jamTutupOperasional: "",
|
||||
jamBukaLibur: "",
|
||||
jamTutupLibur: "",
|
||||
};
|
||||
|
||||
const dokter = proxy({
|
||||
@@ -351,7 +365,7 @@ const dokter = proxy({
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
dokter.create.create.form = { ...defaultDokterForm };
|
||||
dokter.findMany.load();
|
||||
return id;
|
||||
@@ -463,6 +477,11 @@ const dokter = proxy({
|
||||
name: data.name,
|
||||
specialist: data.specialist,
|
||||
jadwal: data.jadwal,
|
||||
jadwalLibur: data.jadwalLibur,
|
||||
jamBukaOperasional: data.jamBukaOperasional,
|
||||
jamTutupOperasional: data.jamTutupOperasional,
|
||||
jamBukaLibur: data.jamBukaLibur,
|
||||
jamTutupLibur: data.jamTutupLibur,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
@@ -487,6 +506,11 @@ const dokter = proxy({
|
||||
name: this.form.name,
|
||||
specialist: this.form.specialist,
|
||||
jadwal: this.form.jadwal,
|
||||
jadwalLibur: this.form.jadwalLibur,
|
||||
jamBukaOperasional: this.form.jamBukaOperasional,
|
||||
jamTutupOperasional: this.form.jamTutupOperasional,
|
||||
jamBukaLibur: this.form.jamBukaLibur,
|
||||
jamTutupLibur: this.form.jamTutupLibur,
|
||||
};
|
||||
|
||||
const cek = templateDokterForm.safeParse(formData);
|
||||
@@ -567,9 +591,255 @@ const dokter = proxy({
|
||||
},
|
||||
});
|
||||
|
||||
const templateTarifForm = z.object({
|
||||
tarif: z.string().min(1, "Tarif tidak boleh kosong"),
|
||||
layanan: z.string().min(1, "Layanan tidak boleh kosong"),
|
||||
});
|
||||
|
||||
const defaultTarifForm = {
|
||||
tarif: "",
|
||||
layanan: "",
|
||||
};
|
||||
|
||||
const tarif = proxy({
|
||||
create: {
|
||||
form: defaultTarifForm,
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateTarifForm.safeParse(tarif.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
tarif.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.tarifdanlayanan["create"].post(
|
||||
tarif.create.form
|
||||
);
|
||||
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data;
|
||||
if (id) {
|
||||
toast.success("Sukses menambahkan");
|
||||
tarif.create.form = { ...defaultTarifForm };
|
||||
tarif.findMany.load();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
toast.error("failed create");
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
return null;
|
||||
} finally {
|
||||
tarif.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.TarifDanLayananGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
tarif.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
tarif.findMany.page = page;
|
||||
tarif.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan.tarifdanlayanan[
|
||||
"findMany"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
tarif.findMany.data = res.data.data ?? [];
|
||||
tarif.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
tarif.findMany.data = [];
|
||||
tarif.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch tarif dan layanan paginated:", err);
|
||||
tarif.findMany.data = [];
|
||||
tarif.findMany.totalPages = 1;
|
||||
} finally {
|
||||
tarif.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.TarifDanLayananGetPayload<{
|
||||
omit: { isActive: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/tarifdanlayanan/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
tarif.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error(
|
||||
"Failed to fetch tarif dan layanan",
|
||||
res.statusText
|
||||
);
|
||||
tarif.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching tarif dan layanan", error);
|
||||
tarif.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultTarifForm },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/tarifdanlayanan/${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 = {
|
||||
tarif: data.tarif,
|
||||
layanan: data.layanan
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading tarif dan layanan:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
const formData = {
|
||||
tarif: this.form.tarif,
|
||||
layanan: this.form.layanan
|
||||
};
|
||||
|
||||
const cek = templateTarifForm.safeParse(formData);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v: any) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await fetch(`/api/kesehatan/tarifdanlayanan/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
|
||||
if (!res.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
|
||||
toast.success("Berhasil update data!");
|
||||
await tarif.findMany.load();
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Update error:", error);
|
||||
toast.error("Gagal update data tarif dan layanan");
|
||||
throw error;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) {
|
||||
return toast.warn("ID tidak valid");
|
||||
}
|
||||
try {
|
||||
tarif.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/kesehatan/tarifdanlayanan/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "tarif dan layanan berhasil dihapus"
|
||||
);
|
||||
await tarif.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus tarif dan layanan"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus tarif dan layanan");
|
||||
} finally {
|
||||
tarif.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const fasilitasKesehatanState = proxy({
|
||||
fasilitasKesehatan,
|
||||
dokter,
|
||||
tarif
|
||||
});
|
||||
|
||||
export default fasilitasKesehatanState;
|
||||
|
||||
@@ -43,7 +43,7 @@ const grafikkepuasan = proxy({
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
grafikkepuasan.create.form = { ...defaultForm };
|
||||
grafikkepuasan.findMany.load();
|
||||
return id;
|
||||
|
||||
@@ -26,14 +26,6 @@ const templateForm = z.object({
|
||||
dokumenJadwalKegiatan: z.object({
|
||||
content: z.string().min(1, "Content minimal 1 karakter"),
|
||||
}),
|
||||
pendaftaranJadwalKegiatan: z.object({
|
||||
name: z.string().min(1, "Name minimal 1 karakter"),
|
||||
tanggal: z.string().min(1, "Tanggal minimal 1 karakter"),
|
||||
namaOrangtua: z.string().min(1, "Nama Orangtua minimal 1 karakter"),
|
||||
nomor: z.string().min(1, "Nomor minimal 1 karakter"),
|
||||
alamat: z.string().min(1, "Alamat minimal 1 karakter"),
|
||||
catatan: z.string().min(1, "Catatan minimal 1 karakter"),
|
||||
}),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
@@ -55,15 +47,7 @@ const defaultForm = {
|
||||
},
|
||||
dokumenJadwalKegiatan: {
|
||||
content: "",
|
||||
},
|
||||
pendaftaranJadwalKegiatan: {
|
||||
name: "",
|
||||
tanggal: "",
|
||||
namaOrangtua: "",
|
||||
nomor: "",
|
||||
alamat: "",
|
||||
catatan: "",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const jadwalkegiatanState = proxy({
|
||||
@@ -116,7 +100,6 @@ const jadwalkegiatanState = proxy({
|
||||
deskripsijadwalkegiatan: true;
|
||||
layananjadwalkegiatan: true;
|
||||
dokumenjadwalkegiatan: true;
|
||||
pendaftaranjadwalkegiatan: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
@@ -161,7 +144,6 @@ const jadwalkegiatanState = proxy({
|
||||
layananjadwalkegiatan: true;
|
||||
syaratketentuanjadwalkegiatan: true;
|
||||
dokumenjadwalkegiatan: true;
|
||||
pendaftaranjadwalkegiatan: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
@@ -209,15 +191,7 @@ const jadwalkegiatanState = proxy({
|
||||
},
|
||||
dokumenJadwalKegiatan: {
|
||||
content: data.dokumenjadwalkegiatan.content,
|
||||
},
|
||||
pendaftaranJadwalKegiatan: {
|
||||
name: data.pendaftaranjadwalkegiatan.name,
|
||||
tanggal: data.pendaftaranjadwalkegiatan.tanggal,
|
||||
namaOrangtua: data.pendaftaranjadwalkegiatan.namaOrangtua,
|
||||
nomor: data.pendaftaranjadwalkegiatan.nomor,
|
||||
alamat: data.pendaftaranjadwalkegiatan.alamat,
|
||||
catatan: data.pendaftaranjadwalkegiatan.catatan,
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
async submit() {
|
||||
@@ -259,20 +233,6 @@ const jadwalkegiatanState = proxy({
|
||||
content:
|
||||
jadwalkegiatanState.edit.form.dokumenJadwalKegiatan.content,
|
||||
},
|
||||
pendaftaranJadwalKegiatan: {
|
||||
name: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.name,
|
||||
tanggal:
|
||||
jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.tanggal,
|
||||
namaOrangtua:
|
||||
jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan
|
||||
.namaOrangtua,
|
||||
nomor:
|
||||
jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.nomor,
|
||||
alamat:
|
||||
jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.alamat,
|
||||
catatan:
|
||||
jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.catatan,
|
||||
},
|
||||
};
|
||||
|
||||
const res = await fetch(
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(1, "Name minimal 1 karakter"),
|
||||
tanggal: z.string().min(1, "Tanggal minimal 1 karakter"),
|
||||
namaOrangtua: z.string().min(1, "Nama Orangtua minimal 1 karakter"),
|
||||
nomor: z.string().min(1, "Nomor minimal 1 karakter"),
|
||||
alamat: z.string().min(1, "Alamat minimal 1 karakter"),
|
||||
catatan: z.string().min(1, "Catatan minimal 1 karakter"),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
tanggal: "",
|
||||
namaOrangtua: "",
|
||||
nomor: "",
|
||||
alamat: "",
|
||||
catatan: "",
|
||||
};
|
||||
|
||||
const pendaftaranJadwalKegiatanState = proxy({
|
||||
create: {
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async submit() {
|
||||
const cek = templateForm.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const errMsg = cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}: ${v.message}`)
|
||||
.join("\n");
|
||||
toast.error(errMsg);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const payload = { ...this.form };
|
||||
|
||||
const res = await (ApiFetch.api.kesehatan as any)[
|
||||
"pendaftaran-jadwal-kegiatan"
|
||||
].create.post(payload);
|
||||
|
||||
if (res.status === 200) {
|
||||
toast.success("Berhasil menambahkan jadwal kegiatan");
|
||||
this.resetForm();
|
||||
await pendaftaranJadwalKegiatanState.findMany.load();
|
||||
return res.data;
|
||||
}
|
||||
} catch (err: any) {
|
||||
const msg = err?.message || "Terjadi kesalahan saat mengirim data";
|
||||
toast.error(msg);
|
||||
console.error("SUBMIT ERROR:", err);
|
||||
return null;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
this.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PendaftaranJadwalKegiatanGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
pendaftaranJadwalKegiatanState.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
pendaftaranJadwalKegiatanState.findMany.page = page;
|
||||
pendaftaranJadwalKegiatanState.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.kesehatan["pendaftaran-jadwal-kegiatan"][
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pendaftaranJadwalKegiatanState.findMany.data = res.data.data ?? [];
|
||||
pendaftaranJadwalKegiatanState.findMany.totalPages =
|
||||
res.data.totalPages ?? 1;
|
||||
} else {
|
||||
pendaftaranJadwalKegiatanState.findMany.data = [];
|
||||
pendaftaranJadwalKegiatanState.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"Gagal fetch pendaftaran jadwal kegiatan paginated:",
|
||||
err
|
||||
);
|
||||
pendaftaranJadwalKegiatanState.findMany.data = [];
|
||||
pendaftaranJadwalKegiatanState.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pendaftaranJadwalKegiatanState.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.PendaftaranJadwalKegiatanGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/kesehatan/pendaftaran-jadwal-kegiatan/${id}`
|
||||
);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
pendaftaranJadwalKegiatanState.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
pendaftaranJadwalKegiatanState.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
pendaftaranJadwalKegiatanState.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
pendaftaranJadwalKegiatanState.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/kesehatan/pendaftaran-jadwal-kegiatan/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "Pendaftaran jadwal kegiatan berhasil dihapus"
|
||||
);
|
||||
await pendaftaranJadwalKegiatanState.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus pendaftaran jadwal kegiatan"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat menghapus pendaftaran jadwal kegiatan"
|
||||
);
|
||||
} finally {
|
||||
pendaftaranJadwalKegiatanState.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/pendaftaran-jadwal-kegiatan/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
tanggal: data.tanggal,
|
||||
namaOrangtua: data.namaOrangtua,
|
||||
nomor: data.nomor,
|
||||
alamat: data.alamat,
|
||||
catatan: data.catatan,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pendaftaran jadwal kegiatan:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(pendaftaranJadwalKegiatanState.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
pendaftaranJadwalKegiatanState.edit.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/kesehatan/pendaftaran-jadwal-kegiatan/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
tanggal: this.form.tanggal,
|
||||
namaOrangtua: this.form.namaOrangtua,
|
||||
nomor: this.form.nomor,
|
||||
alamat: this.form.alamat,
|
||||
catatan: this.form.catatan,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update pendaftaran jadwal kegiatan");
|
||||
await pendaftaranJadwalKegiatanState.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update pendaftaran jadwal kegiatan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating pendaftaran jadwal kegiatan:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update pendaftaran jadwal kegiatan"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
pendaftaranJadwalKegiatanState.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
pendaftaranJadwalKegiatanState.edit.id = "";
|
||||
pendaftaranJadwalKegiatanState.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default pendaftaranJadwalKegiatanState;
|
||||
@@ -50,7 +50,7 @@ const persentasekelahiran = proxy({
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
persentasekelahiran.create.form = { ...defaultForm };
|
||||
persentasekelahiran.findMany.load();
|
||||
return id;
|
||||
|
||||
@@ -9,12 +9,14 @@ const templateForm = z.object({
|
||||
name: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
imageId: z.string().nonempty(),
|
||||
whatsapp: z.string().min(10, "Whatsapp minimal 10 karakter"),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
whatsapp: "",
|
||||
};
|
||||
|
||||
const kontakDarurat = proxy({
|
||||
@@ -171,6 +173,7 @@ const kontakDarurat = proxy({
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId,
|
||||
whatsapp: data.whatsapp,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
@@ -207,6 +210,7 @@ const kontakDarurat = proxy({
|
||||
name: this.form.name,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
whatsapp: this.form.whatsapp,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -5,58 +5,117 @@ import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateapbDesaForm = z.object({
|
||||
name: z.string().min(1, "Judul minimal 1 karakter"),
|
||||
jumlah: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||
imageId: z.string().min(1, "File minimal 1"),
|
||||
fileId: z.string().min(1, "File minimal 1"),
|
||||
// --- Zod Schema ---
|
||||
const ApbdesItemSchema = z.object({
|
||||
kode: z.string().min(1, "Kode wajib diisi"),
|
||||
uraian: z.string().min(1, "Uraian wajib diisi"),
|
||||
anggaran: z.number().min(0),
|
||||
realisasi: z.number().min(0),
|
||||
selisih: z.number(),
|
||||
persentase: z.number(),
|
||||
level: z.number().int().min(1).max(3),
|
||||
tipe: z.enum(['pendapatan', 'belanja', 'pembiayaan']).nullable().optional(),
|
||||
});
|
||||
|
||||
const defaultapbdesForm = {
|
||||
name: "",
|
||||
jumlah: "",
|
||||
const ApbdesFormSchema = z.object({
|
||||
tahun: z.number().int().min(2000, "Tahun tidak valid"),
|
||||
imageId: z.string().min(1, "Gambar wajib diunggah"),
|
||||
fileId: z.string().min(1, "File wajib diunggah"),
|
||||
items: z.array(ApbdesItemSchema).min(1, "Minimal ada 1 item"),
|
||||
});
|
||||
|
||||
// --- Default Form ---
|
||||
const defaultApbdesForm = {
|
||||
tahun: new Date().getFullYear(),
|
||||
imageId: "",
|
||||
fileId: "",
|
||||
items: [] as z.infer<typeof ApbdesItemSchema>[],
|
||||
};
|
||||
|
||||
// --- Helper: hitung selisih & persentase otomatis (opsional di frontend) ---
|
||||
// --- Helper: hitung selisih & persentase otomatis (opsional di frontend) ---
|
||||
function normalizeItem(item: Partial<z.infer<typeof ApbdesItemSchema>>): z.infer<typeof ApbdesItemSchema> {
|
||||
const anggaran = item.anggaran ?? 0;
|
||||
const realisasi = item.realisasi ?? 0;
|
||||
|
||||
|
||||
|
||||
|
||||
// ✅ Formula yang benar
|
||||
const selisih = anggaran - realisasi; // positif = sisa anggaran, negatif = over budget
|
||||
const persentase = anggaran > 0 ? (realisasi / anggaran) * 100 : 0; // persentase realisasi terhadap anggaran
|
||||
|
||||
return {
|
||||
kode: item.kode || "",
|
||||
uraian: item.uraian || "",
|
||||
anggaran,
|
||||
realisasi,
|
||||
selisih,
|
||||
persentase,
|
||||
level: item.level || 1,
|
||||
tipe: item.tipe, // biarkan null jika memang null
|
||||
};
|
||||
}
|
||||
|
||||
// --- State Utama ---
|
||||
const apbdes = proxy({
|
||||
create: {
|
||||
form: { ...defaultapbdesForm },
|
||||
form: { ...defaultApbdesForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateapbDesaForm.safeParse(apbdes.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
apbdes.create.loading = true;
|
||||
const res = await ApiFetch.api.landingpage.apbdes["create"].post({
|
||||
...apbdes.create.form,
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
addItem(item: Partial<z.infer<typeof ApbdesItemSchema>>) {
|
||||
const normalized = normalizeItem(item);
|
||||
this.form.items.push(normalized);
|
||||
},
|
||||
|
||||
removeItem(index: number) {
|
||||
this.form.items.splice(index, 1);
|
||||
},
|
||||
|
||||
updateItem(index: number, updates: Partial<z.infer<typeof ApbdesItemSchema>>) {
|
||||
const current = this.form.items[index];
|
||||
if (current) {
|
||||
const updated = normalizeItem({ ...current, ...updates });
|
||||
this.form.items[index] = updated;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.form = { ...defaultApbdesForm };
|
||||
},
|
||||
|
||||
async create() {
|
||||
const parsed = ApbdesFormSchema.safeParse(this.form);
|
||||
if (!parsed.success) {
|
||||
const errors = parsed.error.issues.map((issue) => `${issue.path.join(".")} - ${issue.message}`);
|
||||
toast.error(`Validasi gagal:\n${errors.join("\n")}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await ApiFetch.api.landingpage.apbdes["create"].post(parsed.data);
|
||||
|
||||
if (res.data?.success) {
|
||||
toast.success("APBDes berhasil dibuat");
|
||||
apbdes.findMany.load();
|
||||
return toast.success("Data berhasil ditambahkan");
|
||||
this.reset();
|
||||
} else {
|
||||
toast.error(res.data?.message || "Gagal membuat APBDes");
|
||||
}
|
||||
return toast.error("Gagal menambahkan data");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error("Gagal menambahkan data");
|
||||
} catch (error: any) {
|
||||
console.error("Create APBDes error:", error);
|
||||
toast.error(error?.message || "Terjadi kesalahan saat membuat APBDes");
|
||||
} finally {
|
||||
apbdes.create.loading = false;
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.APBDesGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
file: true;
|
||||
};
|
||||
include: { image: true; file: true; items: true };
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
@@ -64,194 +123,202 @@ const apbdes = proxy({
|
||||
total: 0,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||
apbdes.findMany.loading = true; // Use the full path to access the property
|
||||
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
apbdes.findMany.loading = true;
|
||||
apbdes.findMany.page = page;
|
||||
apbdes.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
const query: Record<string, string> = { page: String(page), limit: String(limit) };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.apbdes[
|
||||
"findMany"
|
||||
].get({
|
||||
query
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
|
||||
const res = await ApiFetch.api.landingpage.apbdes["findMany"].get({ query });
|
||||
|
||||
if (res.data?.success) {
|
||||
apbdes.findMany.data = res.data.data || [];
|
||||
apbdes.findMany.total = res.data.total || 0;
|
||||
apbdes.findMany.totalPages = res.data.totalPages || 1;
|
||||
apbdes.findMany.total = res.data.meta?.total || 0;
|
||||
apbdes.findMany.totalPages = res.data.meta?.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load pegawai:", res.data?.message);
|
||||
apbdes.findMany.data = [];
|
||||
apbdes.findMany.total = 0;
|
||||
apbdes.findMany.totalPages = 1;
|
||||
toast.error(res.data?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pegawai:", error);
|
||||
console.error("FindMany error:", error);
|
||||
apbdes.findMany.data = [];
|
||||
apbdes.findMany.total = 0;
|
||||
apbdes.findMany.totalPages = 1;
|
||||
toast.error("Gagal memuat daftar APBDes");
|
||||
} finally {
|
||||
apbdes.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as Prisma.APBDesGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
file: true;
|
||||
};
|
||||
}> | null,
|
||||
data: null as
|
||||
| Prisma.APBDesGetPayload<{
|
||||
include: { image: true; file: true; items: true };
|
||||
}>
|
||||
| null,
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id || id.trim() === '') {
|
||||
this.data = null;
|
||||
this.error = "ID tidak valid";
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/landingpage/apbdes/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
apbdes.findUnique.data = data.data ?? null;
|
||||
// Pastikan URL-nya benar
|
||||
const url = `/api/landingpage/apbdes/${id}`;
|
||||
console.log("🌐 Fetching:", url);
|
||||
|
||||
// Gunakan fetch biasa atau ApiFetch dengan cara yang benar
|
||||
const response = await fetch(url);
|
||||
const res = await response.json();
|
||||
|
||||
console.log("📦 Response:", res);
|
||||
|
||||
if (res.success && res.data) {
|
||||
this.data = res.data;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
apbdes.findUnique.data = null;
|
||||
this.data = null;
|
||||
this.error = res.message || "Gagal memuat detail APBDes";
|
||||
toast.error(this.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
apbdes.findUnique.data = null;
|
||||
console.error("❌ FindUnique error:", error);
|
||||
this.data = null;
|
||||
this.error = "Gagal memuat detail APBDes";
|
||||
toast.error(this.error);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
apbdes.delete.loading = true;
|
||||
this.loading = true;
|
||||
const res = await (ApiFetch.api.landingpage.apbdes as any)["del"][id].delete();
|
||||
|
||||
const response = await fetch(`/api/landingpage/apbdes/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "apbdes berhasil dihapus");
|
||||
await apbdes.findMany.load(); // refresh list
|
||||
if (res.data?.success) {
|
||||
toast.success("APBDes berhasil dihapus");
|
||||
apbdes.findMany.load();
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus apbdes");
|
||||
toast.error(res.data?.message || "Gagal menghapus APBDes");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus apbdes");
|
||||
} catch (error: any) {
|
||||
console.error("Delete error:", error);
|
||||
toast.error(error?.message || "Terjadi kesalahan saat menghapus");
|
||||
} finally {
|
||||
apbdes.delete.loading = false;
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultapbdesForm },
|
||||
form: { ...defaultApbdesForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
apbdes.edit.loading = true;
|
||||
this.loading = true;
|
||||
const res = await (ApiFetch.api.landingpage.apbdes as any)[id].get();
|
||||
|
||||
const response = await fetch(`/api/landingpage/apbdes/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
if (res.data?.success) {
|
||||
const data = res.data.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
jumlah: data.jumlah,
|
||||
imageId: data.imageId,
|
||||
fileId: data.fileId,
|
||||
tahun: data.tahun || new Date().getFullYear(),
|
||||
imageId: data.imageId || "",
|
||||
fileId: data.fileId || "",
|
||||
items: (data.items || []).map((item: any) => ({
|
||||
kode: item.kode,
|
||||
uraian: item.uraian,
|
||||
anggaran: item.anggaran,
|
||||
realisasi: item.realisasi,
|
||||
selisih: item.selisih,
|
||||
persentase: item.persentase,
|
||||
level: item.level,
|
||||
tipe: item.tipe || 'pendapatan',
|
||||
})),
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
throw new Error(res.data?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading apbdes:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
} catch (error: any) {
|
||||
console.error("Edit load error:", error);
|
||||
toast.error(error.message || "Gagal memuat data untuk diedit");
|
||||
} finally {
|
||||
apbdes.edit.loading = false;
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateapbDesaForm.safeParse(apbdes.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
const parsed = ApbdesFormSchema.safeParse(this.form);
|
||||
if (!parsed.success) {
|
||||
const errors = parsed.error.issues.map((issue) => `${issue.path.join(".")} - ${issue.message}`);
|
||||
toast.error(`Validasi gagal:\n${errors.join("\n")}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
apbdes.edit.loading = true;
|
||||
const response = await fetch(`/api/landingpage/apbdes/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
jumlah: this.form.jumlah,
|
||||
imageId: this.form.imageId,
|
||||
fileId: this.form.fileId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update apbdes");
|
||||
await apbdes.findMany.load(); // refresh list
|
||||
this.loading = true;
|
||||
// Include the ID in the request body
|
||||
const requestData = {
|
||||
...parsed.data,
|
||||
id: this.id, // Add the ID to the request body
|
||||
};
|
||||
|
||||
const res = await (ApiFetch.api.landingpage.apbdes as any)[this.id].put(requestData);
|
||||
|
||||
if (res.data?.success) {
|
||||
toast.success("APBDes berhasil diperbarui");
|
||||
apbdes.findMany.load();
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal mengupdate apbdes");
|
||||
throw new Error(res.data?.message || "Gagal memperbarui APBDes");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating apbdes:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal mengupdate apbdes"
|
||||
);
|
||||
} catch (error: any) {
|
||||
console.error("Update error:", error);
|
||||
toast.error(error.message || "Gagal memperbarui APBDes");
|
||||
return false;
|
||||
} finally {
|
||||
apbdes.edit.loading = false;
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
addItem(item: Partial<z.infer<typeof ApbdesItemSchema>>) {
|
||||
const normalized = normalizeItem(item);
|
||||
this.form.items.push(normalized);
|
||||
},
|
||||
|
||||
removeItem(index: number) {
|
||||
this.form.items.splice(index, 1);
|
||||
},
|
||||
|
||||
reset() {
|
||||
apbdes.edit.id = "";
|
||||
apbdes.edit.form = { ...defaultapbdesForm };
|
||||
this.id = "";
|
||||
this.form = { ...defaultApbdesForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default apbdes;
|
||||
export default apbdes;
|
||||
@@ -60,13 +60,18 @@ const responden = proxy({
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => {
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
responden.findMany.loading = true; // Use the full path to access the property
|
||||
responden.findMany.page = page;
|
||||
responden.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.responden["findMany"].get({
|
||||
query: { page, limit },
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
|
||||
@@ -27,7 +27,7 @@ const programInovasi = proxy({
|
||||
name: "",
|
||||
description: "",
|
||||
imageId: "",
|
||||
link: ""
|
||||
link: "",
|
||||
} as ProgramInovasiForm,
|
||||
loading: false,
|
||||
async create() {
|
||||
@@ -53,7 +53,7 @@ const programInovasi = proxy({
|
||||
].post(formData);
|
||||
if (res.status === 200) {
|
||||
programInovasi.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
@@ -71,20 +71,21 @@ const programInovasi = proxy({
|
||||
total: 0,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||
programInovasi.findMany.loading = true; // Use the full path to access the property
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
programInovasi.findMany.loading = true; // Use the full path to access the property
|
||||
programInovasi.findMany.page = page;
|
||||
programInovasi.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
|
||||
const res = await ApiFetch.api.landingpage.programinovasi[
|
||||
"findMany"
|
||||
].get({
|
||||
query
|
||||
query,
|
||||
});
|
||||
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
programInovasi.findMany.data = res.data.data || [];
|
||||
programInovasi.findMany.total = res.data.total || 0;
|
||||
@@ -388,16 +389,18 @@ const pejabatDesa = proxy({
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/landingpage/pejabatdesa/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
// Ensure ID is properly encoded in the URL
|
||||
const url = new URL(
|
||||
`/api/landingpage/pejabatdesa/${encodeURIComponent(this.id)}`,
|
||||
window.location.origin
|
||||
);
|
||||
const response = await fetch(url.toString(), {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
@@ -439,16 +442,19 @@ const pejabatDesa = proxy({
|
||||
|
||||
const templateMediaSosial = z.object({
|
||||
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||
imageId: z.string().nullable().optional(),
|
||||
iconUrl: z.string().min(3, "Icon URL minimal 3 karakter"),
|
||||
icon: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
type MediaSosialForm = {
|
||||
name: string;
|
||||
imageId: string;
|
||||
imageId: string | null; // boleh null
|
||||
iconUrl: string;
|
||||
icon: string | null; // boleh null
|
||||
};
|
||||
|
||||
|
||||
const mediaSosial = proxy({
|
||||
create: {
|
||||
form: {} as MediaSosialForm,
|
||||
@@ -456,9 +462,10 @@ const mediaSosial = proxy({
|
||||
async create() {
|
||||
// Ensure all required fields are non-null
|
||||
const formData = {
|
||||
name: mediaSosial.create.form.name || "",
|
||||
imageId: mediaSosial.create.form.imageId || "",
|
||||
iconUrl: mediaSosial.create.form.iconUrl || "",
|
||||
name: mediaSosial.create.form.name ?? "",
|
||||
imageId: mediaSosial.create.form.imageId ?? null, // FIXED
|
||||
iconUrl: mediaSosial.create.form.iconUrl ?? "",
|
||||
icon: mediaSosial.create.form.icon ?? null, // FIXED
|
||||
};
|
||||
|
||||
const cek = templateMediaSosial.safeParse(formData);
|
||||
@@ -475,7 +482,7 @@ const mediaSosial = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
mediaSosial.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
@@ -493,20 +500,19 @@ const mediaSosial = proxy({
|
||||
total: 0,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
|
||||
mediaSosial.findMany.loading = true; // Use the full path to access the property
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
mediaSosial.findMany.loading = true; // Use the full path to access the property
|
||||
mediaSosial.findMany.page = page;
|
||||
mediaSosial.findMany.search = search;
|
||||
try {
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.landingpage.mediasosial[
|
||||
"findMany"
|
||||
].get({
|
||||
|
||||
const res = await ApiFetch.api.landingpage.mediasosial["findMany"].get({
|
||||
query,
|
||||
});
|
||||
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
mediaSosial.findMany.data = res.data.data || [];
|
||||
mediaSosial.findMany.total = res.data.total || 0;
|
||||
@@ -538,7 +544,7 @@ const mediaSosial = proxy({
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
mediaSosial.update.loading = true;
|
||||
try {
|
||||
const res = await fetch(`/api/landingpage/mediasosial/${id}`);
|
||||
@@ -587,66 +593,72 @@ const mediaSosial = proxy({
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: {} as MediaSosialForm,
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
id: "",
|
||||
form: {} as MediaSosialForm,
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
mediaSosial.update.loading = true; // ✅ Tambahkan ini di awal
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/landingpage/mediasosial/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
mediaSosial.update.loading = true; // ✅ Tambahkan ini di awal
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/landingpage/mediasosial/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name || "",
|
||||
imageId: data.imageId || "",
|
||||
iconUrl: data.iconUrl || "",
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal mengambil data media sosial");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengambil data media sosial");
|
||||
} finally {
|
||||
mediaSosial.update.loading = false; // ✅ Supaya berhenti loading walau error
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name || "",
|
||||
imageId: data.imageId || null,
|
||||
iconUrl: data.iconUrl || "",
|
||||
icon: data.icon || null,
|
||||
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(
|
||||
result?.message || "Gagal mengambil data media sosial"
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateMediaSosial.safeParse(mediaSosial.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
mediaSosial.update.loading = true;
|
||||
|
||||
const response = await fetch(`/api/landingpage/mediasosial/${this.id}`, {
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengambil data media sosial");
|
||||
} finally {
|
||||
mediaSosial.update.loading = false; // ✅ Supaya berhenti loading walau error
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateMediaSosial.safeParse(mediaSosial.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
mediaSosial.update.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/landingpage/mediasosial/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -655,38 +667,40 @@ const mediaSosial = proxy({
|
||||
name: this.form.name,
|
||||
imageId: this.form.imageId,
|
||||
iconUrl: this.form.iconUrl,
|
||||
icon: this.form.icon,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update media sosial");
|
||||
await mediaSosial.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update media sosial");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating media sosial:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update media sosial"
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
mediaSosial.update.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update media sosial");
|
||||
await mediaSosial.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update media sosial");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating media sosial:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update media sosial"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
mediaSosial.update.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const profileLandingPageState = proxy({
|
||||
|
||||
@@ -93,6 +93,34 @@ const sdgsDesa = proxy({
|
||||
}
|
||||
},
|
||||
},
|
||||
findManyAll: {
|
||||
data: null as any[] | null,
|
||||
loading: false,
|
||||
load: async () => { // Change to arrow function
|
||||
sdgsDesa.findManyAll.loading = true; // Use the full path to access the property
|
||||
try {
|
||||
const query: any = {};
|
||||
|
||||
const res = await ApiFetch.api.landingpage.sdgsdesa[
|
||||
"findManyAll"
|
||||
].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
sdgsDesa.findManyAll.data = res.data.data || [];
|
||||
} else {
|
||||
console.error("Failed to load media sosial:", res.data?.message);
|
||||
sdgsDesa.findManyAll.data = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading media sosial:", error);
|
||||
sdgsDesa.findManyAll.data = [];
|
||||
} finally {
|
||||
sdgsDesa.findManyAll.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.SdgsDesaGetPayload<{
|
||||
include: {
|
||||
|
||||
@@ -39,7 +39,7 @@ const dataLingkunganDesaState = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
dataLingkunganDesaState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -354,14 +354,39 @@ const kategoriKegiatan = proxy({
|
||||
id: string;
|
||||
nama: string;
|
||||
}> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.lingkungan.kategorikegiatan[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
kategoriKegiatan.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
kategoriKegiatan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
kategoriKegiatan.findMany.page = page;
|
||||
kategoriKegiatan.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res =
|
||||
await ApiFetch.api.lingkungan.kategorikegiatan[
|
||||
"find-many"
|
||||
].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kategoriKegiatan.findMany.data = res.data.data ?? [];
|
||||
kategoriKegiatan.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
kategoriKegiatan.findMany.data = [];
|
||||
kategoriKegiatan.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch kategori kegiatan paginated:", err);
|
||||
kategoriKegiatan.findMany.data = [];
|
||||
kategoriKegiatan.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kategoriKegiatan.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KategoriKegiatanGetPayload<{
|
||||
|
||||
@@ -35,7 +35,7 @@ const pengelolaanSampah = proxy({
|
||||
].post(pengelolaanSampah.create.form);
|
||||
if (res.status === 200) {
|
||||
pengelolaanSampah.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -39,7 +39,7 @@ const programPenghijauanState = proxy({
|
||||
);
|
||||
if (res.status === 200) {
|
||||
programPenghijauanState.findMany.load();
|
||||
return toast.success("success create");
|
||||
return toast.success("Sukses menambahkan");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -9,34 +9,32 @@ import { z } from "zod";
|
||||
|
||||
const templateBeasiswaPendaftar = z.object({
|
||||
namaLengkap: z.string().min(1, "Nama harus diisi"),
|
||||
nik: z.string().min(1, "NIK harus diisi"),
|
||||
nis: z.string().min(1, "NIS harus diisi"),
|
||||
kelas: z.string().min(1, "Kelas harus diisi"),
|
||||
jenisKelamin: z.string().min(1, "Jenis kelamin harus diisi"),
|
||||
alamatDomisili: z.string().min(1, "Alamat domisili harus diisi"),
|
||||
tempatLahir: z.string().min(1, "Tempat lahir harus diisi"),
|
||||
tanggalLahir: z.string().min(1, "Tanggal lahir harus diisi"),
|
||||
jenisKelamin: z.string().min(1, "Jenis kelamin harus diisi"),
|
||||
kewarganegaraan: z.string().min(1, "Kewarganegaraan harus diisi"),
|
||||
agama: z.string().min(1, "Agama harus diisi"),
|
||||
alamatKTP: z.string().min(1, "Alamat KTP harus diisi"),
|
||||
alamatDomisili: z.string().min(1, "Alamat domisili harus diisi"),
|
||||
namaOrtu: z.string().min(1, "Nama ortu harus diisi"),
|
||||
nik: z.string().min(1, "NIK harus diisi"),
|
||||
pekerjaanOrtu: z.string().min(1, "Pekerjaan ortu harus diisi"),
|
||||
penghasilan: z.string().min(1, "Penghasilan ortu harus diisi"),
|
||||
noHp: z.string().min(1, "No HP harus diisi"),
|
||||
email: z.string().min(1, "Email harus diisi"),
|
||||
statusPernikahan: z.string().min(1, "Status pernikahan harus diisi"),
|
||||
ukuranBaju: z.string().min(1, "Ukuran baju harus diisi"),
|
||||
});
|
||||
|
||||
const defaultBeasiswaPendaftar = {
|
||||
namaLengkap: "",
|
||||
nik: "",
|
||||
nis: "",
|
||||
kelas: "",
|
||||
jenisKelamin: "",
|
||||
alamatDomisili: "",
|
||||
tempatLahir: "",
|
||||
tanggalLahir: "",
|
||||
jenisKelamin: "",
|
||||
kewarganegaraan: "",
|
||||
agama: "",
|
||||
alamatKTP: "",
|
||||
alamatDomisili: "",
|
||||
namaOrtu: "",
|
||||
nik: "",
|
||||
pekerjaanOrtu: "",
|
||||
penghasilan: "",
|
||||
noHp: "",
|
||||
email: "",
|
||||
statusPernikahan: "",
|
||||
ukuranBaju: "",
|
||||
};
|
||||
|
||||
const beasiswaPendaftar = proxy({
|
||||
@@ -200,18 +198,17 @@ const beasiswaPendaftar = proxy({
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
namaLengkap: data.namaLengkap,
|
||||
nik: data.nik,
|
||||
nis: data.nis,
|
||||
kelas: data.kelas,
|
||||
jenisKelamin: data.jenisKelamin,
|
||||
alamatDomisili: data.alamatDomisili,
|
||||
tempatLahir: data.tempatLahir,
|
||||
tanggalLahir: data.tanggalLahir,
|
||||
jenisKelamin: data.jenisKelamin,
|
||||
kewarganegaraan: data.kewarganegaraan,
|
||||
agama: data.agama,
|
||||
alamatKTP: data.alamatKTP,
|
||||
alamatDomisili: data.alamatDomisili,
|
||||
namaOrtu: data.namaOrtu,
|
||||
nik: data.nik,
|
||||
pekerjaanOrtu: data.pekerjaanOrtu,
|
||||
penghasilan: data.penghasilan,
|
||||
noHp: data.noHp,
|
||||
email: data.email,
|
||||
statusPernikahan: data.statusPernikahan,
|
||||
ukuranBaju: data.ukuranBaju,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
@@ -249,17 +246,17 @@ const beasiswaPendaftar = proxy({
|
||||
},
|
||||
body: JSON.stringify({
|
||||
namaLengkap: this.form.namaLengkap,
|
||||
nik: this.form.nik,
|
||||
tanggalLahir: this.form.tanggalLahir,
|
||||
nis: this.form.nis,
|
||||
kelas: this.form.kelas,
|
||||
jenisKelamin: this.form.jenisKelamin,
|
||||
kewarganegaraan: this.form.kewarganegaraan,
|
||||
agama: this.form.agama,
|
||||
alamatKTP: this.form.alamatKTP,
|
||||
alamatDomisili: this.form.alamatDomisili,
|
||||
tempatLahir: this.form.tempatLahir,
|
||||
tanggalLahir: this.form.tanggalLahir,
|
||||
namaOrtu: this.form.namaOrtu,
|
||||
nik: this.form.nik,
|
||||
pekerjaanOrtu: this.form.pekerjaanOrtu,
|
||||
penghasilan: this.form.penghasilan,
|
||||
noHp: this.form.noHp,
|
||||
email: this.form.email,
|
||||
statusPernikahan: this.form.statusPernikahan,
|
||||
ukuranBaju: this.form.ukuranBaju,
|
||||
}),
|
||||
}
|
||||
);
|
||||
@@ -332,7 +329,7 @@ const keunggulanProgram = proxy({
|
||||
].post(keunggulanProgram.create.form);
|
||||
if (res.status === 200) {
|
||||
keunggulanProgram.findMany.load();
|
||||
return toast.success("Data Berhasil Dibuat, Silahkan Menunggu Konfirmasi dari Admin di WhatsApp");
|
||||
return toast.success("Data Berhasil Dibuat");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -42,7 +43,7 @@ const dataPendidikan = proxy({
|
||||
if (res.status === 200) {
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
toast.success("Sukses menambahkan");
|
||||
dataPendidikan.create.form = {
|
||||
name: "",
|
||||
jumlah: "",
|
||||
@@ -65,13 +66,46 @@ const dataPendidikan = proxy({
|
||||
select: { id: true; name: true; jumlah: true };
|
||||
}>[]
|
||||
| null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.pendidikan.datapendidikan[
|
||||
"findMany"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
dataPendidikan.findMany.data = res.data?.data ?? [];
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "") => {
|
||||
// Change to arrow function
|
||||
dataPendidikan.findMany.loading = true; // Use the full path to access the property
|
||||
dataPendidikan.findMany.page = page;
|
||||
dataPendidikan.findMany.search = search;
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
|
||||
const res = await ApiFetch.api.pendidikan.datapendidikan[
|
||||
"findMany"
|
||||
].get({
|
||||
query,
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
dataPendidikan.findMany.data = res.data.data || [];
|
||||
dataPendidikan.findMany.total = res.data.total || 0;
|
||||
dataPendidikan.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error(
|
||||
"Failed to load data pendidikan:",
|
||||
res.data?.message
|
||||
);
|
||||
dataPendidikan.findMany.data = [];
|
||||
dataPendidikan.findMany.total = 0;
|
||||
dataPendidikan.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading data pendidikan:", error);
|
||||
dataPendidikan.findMany.data = [];
|
||||
dataPendidikan.findMany.total = 0;
|
||||
dataPendidikan.findMany.totalPages = 1;
|
||||
} finally {
|
||||
dataPendidikan.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user