Compare commits

..

9 Commits

119 changed files with 4776 additions and 3237 deletions

View File

@@ -0,0 +1,57 @@
[
{
"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&nbsp;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>"
}
]

View File

@@ -0,0 +1,20 @@
[
{
"id": "cmdy0dwx10000vnnb6nmt06rv",
"name": "Telunjuk Sakti Desa Akta Kelahiran (Petunjuk Pengajuan pada link berikut : Download",
"deskripsi": "Akta Kelahiran",
"link": "https://darmasaba.desa.id/storage/files/PERSYARATAN%20DAN%20ALUR%20PENGAJUAN%20AKTA%20KELAHIRAN_(dengan%20contoh%20Formulir).pdf"
},
{
"id": "cmdy0ttpz0001vnnbrvr9jb3z",
"name": "Telunjuk Sakti Desa Akta Perkawinan (Petunjuk Pengajuan pada link berikut : Download",
"deskripsi": "Akta Perkawinan",
"link": "https://darmasaba.desa.id/storage/files/PERSYARATAN%20DAN%20ALUR%20PENGAJUAN%20AKTA%20PERKAWINAN_(dengan%20contoh%20Formulir).pdf"
},
{
"id": "cmdy0vjic0002vnnbcp0e9lgq",
"name": "Telunjuk Sakti Desa Akta Kematian (Petunjuk Pengajuan pada link berikut : Download",
"deskripsi": "Akta Kematian",
"link": "https://darmasaba.desa.id/storage/files/PERSYARATAN%20DAN%20ALUR%20PENGAJUAN%20AKTA%20KEMATIAN_(dengan%20contoh%20Formulir).pdf"
}
]

View File

@@ -0,0 +1,74 @@
[
{
"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",
"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>"
},
{
"id": "cmdybckcz000avngjfzpy60uk",
"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>"
},
{
"id": "cmdyjuij40002vns5qyyjmzf4",
"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>"
},
{
"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 &amp; 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 &amp; 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": "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",
"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>"
},
{
"id": "cmdyku9qh000mvn6ft76322sv",
"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>"
}
]

View File

@@ -0,0 +1,22 @@
[
{
"id": "cmdwq7qp60008vntw67s4j6sq",
"name": "Pembiayaan",
"jumlah": "295 M"
},
{
"id": "cmdwpsprc0003vntw9o4d33dr",
"name": "Pendapatan",
"jumlah": "495 M"
},
{
"id": "cmdwqe8xl000cvntwcuqpvdhp",
"name": "Belanja",
"jumlah": "395 M"
},
{
"id": "cmdwqq4b6000gvntwm07rinx4",
"name": "Pangan",
"jumlah": "285 M"
}
]

View File

@@ -0,0 +1,26 @@
[
{
"id" : "cmdzdewrs0003vnp0yh1klh0m",
"name" : "Penghargaan Bhawana Sewaka Nugraha kepada Perbekel Darmasaba",
"juara" : "Penghargaan Bhawana Sewaka Nugraha kepada Perbekel Darmasaba",
"deskripsi" : "<p>Pada hari Jumat, 27 Desember 2024, sebuah momen membanggakan tercipta bagi Desa Darmasaba. Perbekel Darmasaba, Ida Bagus Surya Prabhawa Manuaba, S.H., M.H., NL.P., menerima Penghargaan Bhawana Sewaka Nugraha sebagai Penggiat Lingkungan Hidup.<br><br>Penghargaan bergengsi ini diserahkan langsung oleh Bapak Irjen. Pol. (Purn.) Drs. Sang Made Mahendra Jaya, M.H., selaku Pejabat Gubernur Provinsi Bali, dalam sebuah acara resmi yang berlangsung di Gedung Jaya Sabha, Denpasar.<br><br>Penghargaan ini merupakan pengakuan atas dedikasi, kerja keras, dan komitmen Perbekel Darmasaba dalam menjaga kelestarian lingkungan serta menginspirasi masyarakat untuk menciptakan desa yang lebih hijau, bersih, dan berkelanjutan.<br><br>Semoga prestasi ini tidak hanya menjadi kebanggaan bagi Desa Darmasaba, tetapi juga memotivasi kita semua untuk terus menjaga lingkungan demi masa depan yang lebih baik. Mari bersama-sama wujudkan Bali yang lestari dan berkelanjutan!</p>"
},
{
"id" : "cmdzdlwqe0006vnp0lsrp5ybn",
"name" : "DESA DARMASABA KEMBALI MERAIH PERINGKAT V DALAM AJANG MANGUPURA AWARD",
"juara" : "5",
"deskripsi" : "<p>Bangga, Darmasaba Berprestasi!</p><p><br>Pada hari Senin, 18 November 2024, Desa Darmasaba kembali mencetak prestasi gemilang dengan meraih peringkat V dalam ajang bergengsi Mangupura Award. Penganugerahan ini berlangsung dalam rangkaian acara HUT Kota Mangupura ke-15 yang dihadiri oleh Drs. I Ketut Suiasa, S.H., Plt Bupati Badung, serta para tokoh penting lainnya.</p><p><br>Penghargaan ini merupakan buah kerja keras seluruh elemen masyarakat Darmasaba yang telah berpartisipasi aktif dalam memajukan desa. Proses menuju penghargaan ini melalui tahapan yang menantang, yaitu:<br>- Presentasi Desa pada tanggal 19 Juli 2024<br>- Verifikasi Faktual Lapangan oleh Tim Juri Mangupura Award pada tanggal 5 September 2024</p><p><br>✨ Penghargaan ini bukan hanya sebuah pengakuan, tetapi juga menjadi motivasi bagi Desa Darmasaba untuk terus meningkatkan inovasi, pelayanan, pembangunan serta transformasi digital.<br>Mari bersama kita lanjutkan semangat kolaborasi dan inovasi untuk menjadikan Darmasaba sebagai desa yang semakin unggul dan inspiratif!</p><p><br>#DesaDarmasaba<br>#MangupuraAward2024<br>#BanggaDarmasaba<br>#InovasiDesa<br>#HUTKotaMangupura15<br>#KemajuanDesa<br>#PemdesDarmasaba<br>#PerbekelDarmasaba<br>#DarmasabaBisa<br>#DarmasabaJuara<br>&nbsp;<br>@surya_prabhawa&nbsp;<br>@kecamatanabiansemal&nbsp;<br>@dpmdbadungkab&nbsp;<br>@pemkabbadung<br>@prokompimbadung&nbsp;<br>@seputar_darmasaba</p>"
},
{
"id" : "cmdzdorcb000avnp0ldlsx73f",
"name" : "JEGEG DARMASABA MERAIH JUARA 2 DUTA INVESTASI BADUNG 2024",
"juara" : "2",
"deskripsi" : "<p>JUARA 2</p><p>DUTA INVESTASI KAB. BADUNG</p><p>TAHUN 2024</p><p>Selamat kepada Ni Made Amelia Prasetya Putri (Jegeg Darmasaba Th 2023) telah berhasil mengharumkan nama Desa Darmasaba dengan meraih Juara 2 Duta Invenstasi Kab. Badung dalam ajang Badung Investment Week 2024. Setelah bersaing dengan 40an peserta lainnya dan support penuh dari Pemdes Darmasaba, usaha memang tidak pernah menghianati hasil.</p>"
},
{
"id" : "cmdzdq8ns000dvnp0v917pevj",
"name" : "DARMASABA BORONG JUARA BADUNG INVESTMENT AWARD 2024",
"juara" : "DARMASABA BORONG JUARA BADUNG INVESTMENT AWARD 2024",
"deskripsi" : "<p>DARMASABA MEMBORONG JUARA</p><p>Darmasaba bisa, Darmasaba juara,</p><p>Pemdes Darmasaba tak henti-henti menorehkan prestasi nie Semeton Darmasaba!</p><p>Pemdes Darmasaba berpartisipasi aktif dalam kegiatan Badung Invesment Week Tahun 2024 yang diselenggarakan oleh Dinas Penanaman Modal dan Pelayanan Terpadu Satu Pintu Kab. Badung. Pemdes Darmasaba melalui talenta-talenta terbaiknya mampu meraih prestasi dalam ajang tersebut diantaranya:</p><p>1. Juara 2 Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024.</p><p>2. Juara 2 Duta Invenstasi Kabupaten Badung 2024.</p><p>3. Juara Favorit Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024.</p><p>Penyerahan penghargaan lomba-lomba tersebut diserahkan dalam acara Badung Invesment Award 2024 pada hari Jumat (18/10/2024) bertempat di Balai Budaya Giri Nata Mandala Puspem Kab. Badung. Penghargaan Juara Lomba diserahkan langsung oleh Plt. Bupati Badung Drs. I Ketut Suiasa, S.H. didampingi Kadis DPMPTSP Kab. Badung Dr. Ir. I Made Agus Aryawan ST., MT.</p>"
}
]

View File

@@ -0,0 +1,14 @@
[
{
"id": "cmdwrolsl0000vnd3e24q5440",
"name": "Olahraga dan Kepemudaan"
},
{
"id": "cmdwrot900001vnd30b5kj96g",
"name": "Hukum dan Kesadaran Masyarakat"
},
{
"id": "cmdwrp0pr0002vnd35w6nkjh0",
"name": "Tata Kelola dan Inovasi Desa"
}
]

View File

@@ -0,0 +1,20 @@
[
{
"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": "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"
},
{
"id": "cmdws0sgq000bvnd32o7m94im",
"name": "Peringkat 5 Dalam Ajang Bergengsi Mangupura Award",
"deskripsi": "<p>Peringkat 5 Dalam Ajang Bergengsi Mangupura Award</p>",
"kategoriId": "cmdwrp0pr0002vnd35w6nkjh0"
}
]

View File

@@ -0,0 +1,114 @@
[
{
"id": "cmdsjzdl30002vneknuvo4irv",
"name": "Desa Tanpa Kemiskinan",
"jumlah": "52.62",
"imageId": ""
},
{
"id": "cmdskargd0005vnek0mu2ofk9",
"name": "Desa Tanpa Kelaparan",
"jumlah": "35.75",
"imageId": ""
},
{
"id": "cmdskbvl0008vnek5dmieatb",
"name": "Desa Sehat Dan Sejahtera",
"jumlah": "77.37",
"imageId": ""
},
{
"id": "cmdskcx91000bvneko7tuaoqa",
"name": "Pendidikan Desa Berkualitas",
"jumlah": "34.11",
"imageId": ""
},
{
"id": "cmdskjare000evnek1hglu0x8",
"name": "Keterlibatan Perempuan Desa",
"jumlah": "45.70",
"imageId": ""
},
{
"id": "cmdskqcpc0002vnvnqjkqgm92",
"name": "Desa Layak Air Bersih Dan Sanitasi",
"jumlah": "48.54",
"imageId": ""
},
{
"id": "cmdsktl3x0005vnvne15seefw",
"name": "Desa Berenergi Bersih Dan Terbarukan",
"jumlah": "99.64",
"imageId": ""
},
{
"id": "cmdskuncw0008vnvcsdqoeog",
"name": "Pertumbuhan Ekonomi Desa Merata",
"jumlah": "40.92",
"imageId": ""
},
{
"id": "cmdskw83j000bvvn9szqrea6",
"name": "Infrastruktur Dan Inovasi Desa Sesuai Kebutuhan",
"jumlah": "35.37",
"imageId": ""
},
{
"id": "cmdskwrq7000envnvy0c5nbgf",
"name": "Desa Tanpa Kesenjangan",
"jumlah": "35.47",
"imageId": ""
},
{
"id": "cmdskxivx000hnvnvsx520gv1",
"name": "Kawasan Pemukiman Desa Aman Dan Nyaman",
"jumlah": "40.35",
"imageId": ""
},
{
"id": "cmdskzg4c000kvnnkiv61gkt",
"name": "Konsumsi Dan Produksi Desa Sadar Lingkungan",
"jumlah": "16.67",
"imageId": ""
},
{
"id": "cmdsl07lk000nvnnvnrepsdy5m",
"name": "Desa Tanggap Perubahan Iklim",
"jumlah": "0.00",
"imageId": ""
},
{
"id": "cmdsl10rq000qvnvnlch9c1yv",
"name": "Desa Peduli Lingkungan Laut",
"jumlah": "50.00",
"imageId": ""
},
{
"id": "cmdsl1mc2000tvnvn357n8usi",
"name": "Desa Peduli Lingkungan Darat",
"jumlah": "0.00",
"imageId": ""
},
{
"id": "cmdsl2bx3000wvnvntshi4gnj",
"name": "Desa Damai Berkeadilan",
"jumlah": "78.65",
"imageId": ""
},
{
"id": "cmdsl2yz3000zvnvnmf60ok7q",
"name": "Kemitraan Untuk Pembangunan Desa",
"jumlah": "20.00",
"imageId": ""
},
{
"id": "cmdsl492h0012vnvnmckm3n2x",
"name": "Kelembagaan Desa Dinamis Dan Budaya Desa Adaptif",
"jumlah": "47.22",
"imageId": ""
}
]

View File

@@ -50,52 +50,54 @@ model AppMenuChild {
// ========================================= FILE STORAGE ========================================= //
model FileStorage {
id String @id @default(cuid())
name String @unique
realName String
path String
mimeType String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
isActive Boolean @default(true)
link String
category String // "image" / "document" / "other"
Berita Berita[]
PotensiDesa PotensiDesa[]
Posyandu Posyandu[]
StrukturPPID StrukturPPID[]
GalleryFoto GalleryFoto[]
PelayananSuratKeterangan PelayananSuratKeterangan[]
Penghargaan Penghargaan[]
ProfileDesaImage ProfileDesaImage[]
ProfilePPID ProfilePPID[]
ProfilPerbekel ProfilPerbekel[]
Puskesmas Puskesmas[]
ProgramKesehatan ProgramKesehatan[]
PenangananDarurat PenangananDarurat[]
KontakDarurat KontakDarurat[]
InfoWabahPenyakit InfoWabahPenyakit[]
KeamananLingkungan KeamananLingkungan[]
MenuTipsKeamanan MenuTipsKeamanan[]
Pelapor Pelapor[]
PasarDesa PasarDesa[]
KontakDaruratKeamanan KontakDaruratKeamanan[]
KontakItem KontakItem[]
Pegawai Pegawai[]
DesaDigital DesaDigital[]
KolaborasiInovasi KolaborasiInovasi[]
InfoTekno InfoTekno[]
PengaduanMasyarakat PengaduanMasyarakat[]
KegiatanDesa KegiatanDesa[]
ProgramInovasi ProgramInovasi[]
PejabatDesa PejabatDesa[]
MediaSosial MediaSosial[]
DesaAntiKorupsi DesaAntiKorupsi[]
SDGSDesa SDGSDesa[]
APBDesImage APBDes[] @relation("APBDesImage")
APBDesFile APBDes[] @relation("APBDesFile")
PrestasiDesa PrestasiDesa[]
id String @id @default(cuid())
name String @unique
realName String
path String
mimeType String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
isActive Boolean @default(true)
link String
category String // "image" / "document" / "other"
Berita Berita[]
PotensiDesa PotensiDesa[]
Posyandu Posyandu[]
StrukturPPID StrukturPPID[]
GalleryFoto GalleryFoto[]
Pelapor Pelapor[]
Penghargaan Penghargaan[]
ProfileDesaImage ProfileDesaImage[]
ProfilePPID ProfilePPID[]
ProfilPerbekel ProfilPerbekel[]
Puskesmas Puskesmas[]
ProgramKesehatan ProgramKesehatan[]
PenangananDarurat PenangananDarurat[]
KontakDarurat KontakDarurat[]
InfoWabahPenyakit InfoWabahPenyakit[]
KeamananLingkungan KeamananLingkungan[]
MenuTipsKeamanan MenuTipsKeamanan[]
PelayananSuratKeteranganImage PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage")
PelayananSuratKeteranganImage2 PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage2")
PasarDesa PasarDesa[]
KontakDaruratKeamanan KontakDaruratKeamanan[]
KontakItem KontakItem[]
Pegawai Pegawai[]
DesaDigital DesaDigital[]
KolaborasiInovasi KolaborasiInovasi[]
InfoTekno InfoTekno[]
PengaduanMasyarakat PengaduanMasyarakat[]
KegiatanDesa KegiatanDesa[]
ProgramInovasi ProgramInovasi[]
PejabatDesa PejabatDesa[]
MediaSosial MediaSosial[]
DesaAntiKorupsi DesaAntiKorupsi[]
SDGSDesa SDGSDesa[]
APBDesImage APBDes[] @relation("APBDesImage")
APBDesFile APBDes[] @relation("APBDesFile")
PrestasiDesa PrestasiDesa[]
DataPerpustakaan DataPerpustakaan[]
@@ -130,15 +132,15 @@ model ProgramInovasi {
}
model MediaSosial {
id String @id @default(cuid())
id String @id @default(cuid())
name String
image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
iconUrl String? @db.VarChar(255)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
iconUrl String? @db.VarChar(255)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
isActive Boolean @default(true)
isActive Boolean @default(true)
}
//========================================= PROFILE ========================================= //
@@ -148,7 +150,7 @@ model DesaAntiKorupsi {
deskripsi String @db.Text
kategori KategoriDesaAntiKorupsi @relation(fields: [kategoriId], references: [id])
kategoriId String
file FileStorage? @relation(fields: [fileId], references: [id])
file FileStorage? @relation(fields: [fileId], references: [id])
fileId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -168,30 +170,30 @@ model KategoriDesaAntiKorupsi {
//========================================= SDGS Desa ========================================= //
model SDGSDesa {
id String @id @default(cuid())
name String @unique
id String @id @default(cuid())
name String @unique
jumlah String
image FileStorage @relation(fields: [imageId], references: [id])
imageId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
//========================================= APBDes ========================================= //
model APBDes {
id String @id @default(cuid())
name String @unique
id String @id @default(cuid())
name String @unique
jumlah String
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())
isActive Boolean @default(true)
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())
isActive Boolean @default(true)
}
//========================================= PRESTASI DESA ========================================= //
@@ -313,14 +315,14 @@ model StrukturPPID {
}
model PosisiOrganisasiPPID {
id String @id @default(cuid())
nama String @db.VarChar(100)
deskripsi String? @db.Text
id String @id @default(cuid())
nama String @db.VarChar(100)
deskripsi String? @db.Text
hierarki Int
pegawai PegawaiPPID[]
strukturOrganisasi StrukturPPID[] // Relasi balik
parentId String?
parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id])
parentId String?
parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id])
children PosisiOrganisasiPPID[] @relation("Parent")
}
@@ -609,17 +611,28 @@ model KategoriBerita {
// ========================================= POTENSI DESA ========================================= //
model PotensiDesa {
id String @id @default(cuid())
name String
deskripsi String
kategori String
image FileStorage @relation(fields: [imageId], references: [id])
imageId String
content String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
id String @id @default(cuid())
name String
deskripsi String
kategori KategoriPotensi? @relation(fields: [kategoriId], references: [id])
kategoriId String?
image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
content String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model KategoriPotensi {
id String @id @default(cuid())
nama String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
PotensiDesa PotensiDesa[]
}
// ========================================= PENGUMUMAN ========================================= //
@@ -672,15 +685,17 @@ model GalleryVideo {
// ========================================= LAYANAN DESA ========================================= //
model PelayananSuratKeterangan {
id String @id @default(cuid())
id String @id @default(cuid())
name String
deskripsi 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)
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)
}
model PelayananTelunjukSaktiDesa {
@@ -721,8 +736,8 @@ model Penghargaan {
name String
juara String
deskripsi String @db.Text
image FileStorage @relation(fields: [imageId], references: [id])
imageId String
image FileStorage? @relation(fields: [imageId], references: [id])
imageId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())

View File

@@ -1,7 +1,11 @@
import prisma from "@/lib/prisma";
import profilePejabatDesa from "./data/landing-page/profile/profile.json";
import penghargaan from "./data/landing-page/penghargaan/penghargaan.json";
import programInovasi from "./data/landing-page/profile/programInovasi.json";
import mediaSosial from "./data/landing-page/profile/mediaSosial.json";
import sdgsDesa from "./data/landing-page/sdgs-desa/sdgs-desa.json";
import apbdes from "./data/landing-page/apbdes/apbdes.json";
import pelayananSuratKeterangan from "./data/desa/layanan/pelayananSuratKeterangan.json";
import categoryPengumuman from "./data/category-pengumuman.json";
import kategoriBerita from "./data/kategori-berita.json";
import caraMemperolehInformasi from "./data/list-caraMemperolehInformasi.json";
@@ -40,6 +44,7 @@ import tempatKegiatan from "./data/pendidikan/pendidikan-non-formal/tempat-kegia
import jenisProgramYangDiselenggarakan from "./data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json";
import posisiOrganisasiPPID from "./data/ppid/struktur-ppid/posisi-organisasi-PPID.json";
import pegawaiPPID from "./data/ppid/struktur-ppid/pegawai-PPID.json";
import pelayananTelunjukSaktiDesa from "./data/desa/layanan/pelayananTelunjukSaktiDesa.json";
(async () => {
// =========== LANDING PAGE ===========
@@ -98,6 +103,60 @@ import pegawaiPPID from "./data/ppid/struktur-ppid/pegawai-PPID.json";
}
console.log("media sosial success ...");
// =========== PENGHARGAAN ===========
for (const p of penghargaan) {
await prisma.penghargaan.upsert({
where: { id: p.id },
update: {
name: p.name,
juara: p.juara,
deskripsi: p.deskripsi,
},
create: {
id: p.id,
name: p.name,
juara: p.juara,
deskripsi: p.deskripsi,
},
});
}
console.log("penghargaan success ...");
// =========== LAYANAN DESA ===========
for (const p of pelayananSuratKeterangan) {
await prisma.pelayananSuratKeterangan.upsert({
where: { id: p.id },
update: {
name: p.name,
deskripsi: p.deskripsi,
},
create: {
id: p.id,
name: p.name,
deskripsi: p.deskripsi,
},
});
}
console.log("pelayanan surat keterangan success ...");
for (const p of pelayananTelunjukSaktiDesa) {
await prisma.pelayananTelunjukSaktiDesa.upsert({
where: { id: p.id },
update: {
name: p.name,
deskripsi: p.deskripsi,
link: p.link,
},
create: {
id: p.id,
name: p.name,
deskripsi: p.deskripsi,
link: p.link,
},
});
}
console.log("pelayanan surat keterangan success ...");
// =========== LAYANAN ===========
for (const l of layanan) {
await prisma.layanan.upsert({
@@ -115,6 +174,46 @@ import pegawaiPPID from "./data/ppid/struktur-ppid/pegawai-PPID.json";
console.log("layanan success ...");
// =========== SDGSDesa ===========
for (const l of sdgsDesa) {
await prisma.sDGSDesa.upsert({
where: {
name: l.name,
jumlah: l.jumlah,
},
update: {
name: l.name,
jumlah: l.jumlah,
},
create: {
name: l.name,
jumlah: l.jumlah,
},
});
}
console.log("sdgs desa success ...");
// =========== APBDes ===========
for (const l of apbdes) {
await prisma.aPBDes.upsert({
where: {
name: l.name,
jumlah: l.jumlah,
},
update: {
name: l.name,
jumlah: l.jumlah,
},
create: {
name: l.name,
jumlah: l.jumlah,
},
});
}
console.log("sdgs desa success ...");
for (const l of sejarahDesa) {
await prisma.sejarahDesa.upsert({
where: {

View File

@@ -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";
@@ -8,12 +9,14 @@ const templateSuratKeteranganForm = z.object({
name: z.string().min(3, "Nama minimal 3 karakter"),
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
imageId: z.string().nonempty(),
image2Id: z.string().nonempty(),
});
const suratKeteranganForm = {
name: "",
deskripsi: "",
imageId: "",
image2Id: "",
};
const telunjukSaktiDesaForm = {
@@ -105,15 +108,38 @@ const suratKeterangan = proxy({
},
},
findMany: {
data: [] as Prisma.PelayananSuratKeteranganGetPayload<{
include: { image: true };
}>[],
async load() {
const res = await ApiFetch.api.desa.layanan.pelayanansuratketerangan[
"find-many"
].get();
if (res.status === 200) {
suratKeterangan.findMany.data = res.data?.data ?? [];
data: null as any[] | null,
page: 1,
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
suratKeterangan.findMany.loading = true; // Use the full path to access the property
suratKeterangan.findMany.page = page;
try {
const res = await ApiFetch.api.desa.layanan.pelayanansuratketerangan[
"find-many"
].get({
query: { page, limit },
});
if (res.status === 200 && res.data?.success) {
suratKeterangan.findMany.data = res.data.data || [];
suratKeterangan.findMany.total = res.data.total || 0;
suratKeterangan.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load surat keterangan:", res.data?.message);
suratKeterangan.findMany.data = [];
suratKeterangan.findMany.total = 0;
suratKeterangan.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading surat keterangan:", error);
suratKeterangan.findMany.data = [];
suratKeterangan.findMany.total = 0;
suratKeterangan.findMany.totalPages = 1;
} finally {
suratKeterangan.findMany.loading = false;
}
},
},
@@ -121,6 +147,7 @@ const suratKeterangan = proxy({
data: null as Prisma.PelayananSuratKeteranganGetPayload<{
include: {
image: true;
image2: true;
};
}> | null,
async load(id: string) {
@@ -202,6 +229,7 @@ const suratKeterangan = proxy({
name: data.name,
deskripsi: data.deskripsi,
imageId: data.imageId || "",
image2Id: data.image2Id || "",
};
return data;
} else {
@@ -238,6 +266,7 @@ const suratKeterangan = proxy({
name: this.form.name,
deskripsi: this.form.deskripsi,
imageId: this.form.imageId,
image2Id: this.form.image2Id,
}),
}
);
@@ -307,15 +336,38 @@ const pelayananTelunjukSaktiDesa = proxy({
},
},
findMany: {
data: [] as Prisma.PelayananTelunjukSaktiDesaGetPayload<{
omit: { isActive: true };
}>[],
async load() {
const res = await ApiFetch.api.desa.layanan.pelayanantelunjuksaktidesa[
"find-many"
].get();
if (res.status === 200) {
pelayananTelunjukSaktiDesa.findMany.data = res.data?.data ?? [];
data: null as any[] | null,
page: 1,
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
pelayananTelunjukSaktiDesa.findMany.loading = true; // Use the full path to access the property
pelayananTelunjukSaktiDesa.findMany.page = page;
try {
const res = await ApiFetch.api.desa.layanan.pelayanantelunjuksaktidesa[
"find-many"
].get({
query: { page, limit },
});
if (res.status === 200 && res.data?.success) {
pelayananTelunjukSaktiDesa.findMany.data = res.data.data || [];
pelayananTelunjukSaktiDesa.findMany.total = res.data.total || 0;
pelayananTelunjukSaktiDesa.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load telunjuk sakti desa:", res.data?.message);
pelayananTelunjukSaktiDesa.findMany.data = [];
pelayananTelunjukSaktiDesa.findMany.total = 0;
pelayananTelunjukSaktiDesa.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading telunjuk sakti desa:", error);
pelayananTelunjukSaktiDesa.findMany.data = [];
pelayananTelunjukSaktiDesa.findMany.total = 0;
pelayananTelunjukSaktiDesa.findMany.totalPages = 1;
} finally {
pelayananTelunjukSaktiDesa.findMany.loading = false;
}
},
},

View File

@@ -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";
@@ -5,10 +6,10 @@ import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
name: z.string().min(1).max(50),
juara: z.string().min(1).max(50),
name: z.string().min(1).max(5000),
juara: z.string().min(1).max(5000),
deskripsi: z.string().min(1).max(5000),
imageId: z.string().min(1).max(50),
imageId: z.string().min(1).max(5000),
});
const defaultForm = {
@@ -50,17 +51,38 @@ const penghargaanState = proxy({
},
},
findMany: {
data: null as
| Prisma.PenghargaanGetPayload<{
include: {
image: true;
};
}>[]
| null,
async load() {
const res = await ApiFetch.api.desa.penghargaan["find-many"].get();
if (res.status === 200) {
penghargaanState.findMany.data = res.data?.data ?? [];
data: null as any[] | null,
page: 1,
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
penghargaanState.findMany.loading = true; // Use the full path to access the property
penghargaanState.findMany.page = page;
try {
const res = await ApiFetch.api.desa.penghargaan[
"find-many"
].get({
query: { page, limit },
});
if (res.status === 200 && res.data?.success) {
penghargaanState.findMany.data = res.data.data || [];
penghargaanState.findMany.total = res.data.total || 0;
penghargaanState.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load penghargaan:", res.data?.message);
penghargaanState.findMany.data = [];
penghargaanState.findMany.total = 0;
penghargaanState.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading penghargaan:", error);
penghargaanState.findMany.data = [];
penghargaanState.findMany.total = 0;
penghargaanState.findMany.totalPages = 1;
} finally {
penghargaanState.findMany.loading = false;
}
},
},

View File

@@ -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";
@@ -5,219 +6,470 @@ import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
name: z.string().min(1).max(50),
deskripsi: z.string().min(1).max(5000),
kategori: z.string().min(1).max(50),
imageId: z.string().min(1).max(50),
content: z.string().min(1).max(5000),
})
name: z.string().min(1).max(5000),
deskripsi: z.string().min(1).max(5000),
kategoriId: z.string().min(1).max(50),
imageId: z.string().min(1).max(50),
content: z.string().min(1).max(5000),
});
const defaultForm = {
name: "",
deskripsi: "",
kategori: "",
imageId: "",
content: "",
}
name: "",
deskripsi: "",
kategoriId: "",
imageId: "",
content: "",
};
const potensiDesa = proxy({
create: {
form: { ...defaultForm },
loading: false,
async create() {
const cek = templateForm.safeParse(potensiDesa.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
potensiDesa.create.loading = true;
const res = await ApiFetch.api.desa.potensi["create"].post(
potensiDesa.create.form
);
if (res.status === 200) {
potensiDesa.findMany.load();
return toast.success("Potensi berhasil disimpan!");
}
return toast.error("Gagal menyimpan potensi");
} catch (error) {
console.log((error as Error).message);
} finally {
potensiDesa.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
potensiDesa.findMany.loading = true; // Use the full path to access the property
potensiDesa.findMany.page = page;
try {
const res = await ApiFetch.api.desa.potensi[
"find-many"
].get({
query: { page, limit },
});
if (res.status === 200 && res.data?.success) {
potensiDesa.findMany.data = res.data.data || [];
potensiDesa.findMany.total = res.data.total || 0;
potensiDesa.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load potensi desa:", res.data?.message);
potensiDesa.findMany.data = [];
potensiDesa.findMany.total = 0;
potensiDesa.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading potensi desa:", error);
potensiDesa.findMany.data = [];
potensiDesa.findMany.total = 0;
potensiDesa.findMany.totalPages = 1;
} finally {
potensiDesa.findMany.loading = false;
}
},
},
findUnique: {
data: null as Prisma.PotensiDesaGetPayload<{
include: {
image: true;
kategori: true;
};
}> | null,
async load(id: string) {
try {
const res = await fetch(`/api/desa/potensi/${id}`);
if (res.ok) {
const data = await res.json();
potensiDesa.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch potensi:", res.statusText);
potensiDesa.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching potensi:", error);
potensiDesa.findUnique.data = null;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
potensiDesa.delete.loading = true;
const response = await fetch(`/api/desa/potensi/del/${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(result.message || "Potensi berhasil dihapus");
await potensiDesa.findMany.load(); // refresh list
} else {
toast.error(result?.message || "Gagal menghapus potensi");
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus potensi");
} finally {
potensiDesa.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/desa/potensi/${id}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result?.success) {
const data = result.data;
this.id = data.id;
this.form = {
name: data.name,
deskripsi: data.deskripsi,
kategoriId: data.kategoriId,
imageId: data.imageId || "",
content: data.content,
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error loading potensi:", error);
toast.error(
error instanceof Error ? error.message : "Gagal memuat data"
);
return null;
}
},
async update() {
const cek = templateForm.safeParse(potensiDesa.edit.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return false;
}
try {
potensiDesa.edit.loading = true;
const response = await fetch(`/api/desa/potensi/${this.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
deskripsi: this.form.deskripsi,
kategoriId: this.form.kategoriId,
imageId: this.form.imageId,
content: this.form.content,
}),
});
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 potensi");
await potensiDesa.findMany.load(); // refresh list
return true;
} else {
throw new Error(result.message || "Gagal update potensi");
}
} catch (error) {
console.error("Error updating potensi:", error);
toast.error(
error instanceof Error
? error.message
: "Terjadi kesalahan saat update potensi"
);
return false;
} finally {
potensiDesa.edit.loading = false;
}
},
reset() {
potensiDesa.edit.id = "";
potensiDesa.edit.form = { ...defaultForm };
},
},
});
const templateKategoriPotensi = z.object({
nama: z.string().min(1, "Nama harus diisi"),
});
const defaultKategoriPotensi = {
nama: "",
};
const kategoriPotensi = proxy({
create: {
form: { ...defaultKategoriPotensi },
loading: false,
async create() {
const cek = templateKategoriPotensi.safeParse(
kategoriPotensi.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
kategoriPotensi.create.loading = true;
const res = await ApiFetch.api.desa.kategoripotensi["create"].post(
kategoriPotensi.create.form
);
if (res.status === 200) {
kategoriPotensi.findMany.load();
return toast.success("Data Kategori Potensi Berhasil Dibuat");
}
console.log(res);
return toast.error("failed create");
} catch (error) {
console.log(error);
return toast.error("failed create");
} finally {
kategoriPotensi.create.loading = false;
}
},
},
findMany: {
data: [] as Prisma.KategoriPotensiGetPayload<{
omit: {
isActive: true;
};
}>[],
loading: false,
async load() {
const res = await ApiFetch.api.desa.kategoripotensi["findMany"].get();
if (res.status === 200) {
kategoriPotensi.findMany.data = res.data?.data ?? [];
}
},
},
findUnique: {
data: null as Prisma.KategoriPotensiGetPayload<{
omit: {
isActive: true;
};
}> | null,
loading: false,
async load(id: string) {
try {
const res = await fetch(`/api/desa/kategoripotensi/${id}`);
if (res.ok) {
const data = await res.json();
kategoriPotensi.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch data", res.status, res.statusText);
kategoriPotensi.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching data:", error);
kategoriPotensi.findUnique.data = null;
}
},
},
delete: {
loading: false,
async delete(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
kategoriPotensi.delete.loading = true;
const response = await fetch(`/api/desa/kategoripotensi/del/${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(
result.message || "Data Kategori Potensi berhasil dihapus"
);
await kategoriPotensi.findMany.load(); // refresh list
} else {
toast.error(
result?.message || "Gagal menghapus Data Kategori Potensi"
);
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus Data Kategori Potensi");
} finally {
kategoriPotensi.delete.loading = false;
}
},
},
update: {
id: "",
form: { ...defaultKategoriPotensi },
loading: false,
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
try {
const response = await fetch(`/api/desa/kategoripotensi/${id}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result?.success) {
const data = result.data;
this.id = data.id;
this.form = {
nama: data.nama,
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error loading kategori potensi:", error);
toast.error(
error instanceof Error ? error.message : "Gagal memuat data"
);
return null;
}
},
async update() {
const cek = templateKategoriPotensi.safeParse(
kategoriPotensi.update.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return false;
}
try {
kategoriPotensi.update.loading = true;
const response = await fetch(`/api/desa/kategoripotensi/${this.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
nama: this.form.nama,
}),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message || `HTTP error! status: ${response.status}`
);
}
const result = await response.json();
if (result.success) {
toast.success("Berhasil update data kategori potensi");
await kategoriPotensi.findMany.load(); // refresh list
return true;
} else {
throw new Error(
result.message || "Gagal update data kategori potensi"
);
}
} catch (error) {
console.error("Error updating data kategori potensi:", error);
toast.error(
error instanceof Error
? error.message
: "Terjadi kesalahan saat update data kategori potensi"
);
return false;
} finally {
kategoriPotensi.update.loading = false;
}
},
reset() {
kategoriPotensi.update.id = "";
kategoriPotensi.update.form = { ...defaultKategoriPotensi };
},
},
});
const potensiDesaState = proxy({
create: {
form: { ...defaultForm },
loading: false,
async create() {
const cek = templateForm.safeParse(potensiDesaState.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
potensiDesaState.create.loading = true;
const res = await ApiFetch.api.desa.potensi["create"].post(potensiDesaState.create.form);
if (res.status === 200) {
potensiDesaState.findMany.load();
return toast.success("Potensi berhasil disimpan!");
}
return toast.error("Gagal menyimpan potensi");
} catch (error) {
console.log((error as Error).message);
} finally {
potensiDesaState.create.loading = false;
}
}
},
findMany: {
data: null as
| Prisma.PotensiDesaGetPayload<{
include: {
image: true;
}
}>[]
| null,
async load() {
const res = await ApiFetch.api.desa.potensi["find-many"].get();
if (res.status === 200) {
potensiDesaState.findMany.data = res.data?.data ?? [];
}
}
},
findUnique: {
data: null as
| Prisma.PotensiDesaGetPayload<{
include: {
image: true;
}
}> | null,
async load(id: string) {
try {
const res = await fetch(`/api/desa/potensi/${id}`);
if (res.ok) {
const data = await res.json();
potensiDesaState.findUnique.data = data.data ?? null;
} else {
console.error('Failed to fetch potensi:', res.statusText);
potensiDesaState.findUnique.data = null;
}
} catch (error) {
console.error('Error fetching potensi:', error);
potensiDesaState.findUnique.data = null;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
potensiDesaState.delete.loading = true;
const response = await fetch(`/api/desa/potensi/del/${id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(result.message || "Potensi berhasil dihapus");
await potensiDesaState.findMany.load(); // refresh list
} else {
toast.error(result?.message || "Gagal menghapus potensi");
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus potensi");
} finally {
potensiDesaState.delete.loading = false;
}
},
},
edit: {
id: "",
form: { ...defaultForm },
loading: false,
potensiDesa,
kategoriPotensi,
});
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
try {
const response = await fetch(`/api/desa/potensi/${id}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result?.success) {
const data = result.data;
this.id = data.id;
this.form = {
name: data.name,
deskripsi: data.deskripsi,
kategori: data.kategori,
imageId: data.imageId || "",
content: data.content,
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error loading potensi:", error);
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
return null;
}
},
async update() {
const cek = templateForm.safeParse(potensiDesaState.edit.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return false;
}
try {
potensiDesaState.edit.loading = true;
const response = await fetch(`/api/desa/potensi/${this.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: this.form.name,
deskripsi: this.form.deskripsi,
kategori: this.form.kategori,
imageId: this.form.imageId,
content: this.form.content,
}),
});
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 potensi");
await potensiDesaState.findMany.load(); // refresh list
return true;
} else {
throw new Error(result.message || "Gagal update potensi");
}
} catch (error) {
console.error("Error updating potensi:", error);
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update potensi");
return false;
} finally {
potensiDesaState.edit.loading = false;
}
},
reset() {
potensiDesaState.edit.id = "";
potensiDesaState.edit.form = { ...defaultForm };
}
}
})
export default potensiDesaState
export default potensiDesaState;

View File

@@ -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";
@@ -52,19 +53,38 @@ const sdgsDesa = proxy({
},
},
findMany: {
data: null as Array<
Prisma.SDGSDesaGetPayload<{
include: {
image: true;
};
}>
> | null,
async load() {
const res = await ApiFetch.api.landingpage.sdgsdesa[
"find-many"
].get();
if (res.status === 200) {
sdgsDesa.findMany.data = res.data?.data ?? [];
data: null as any[] | null,
page: 1,
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
sdgsDesa.findMany.loading = true; // Use the full path to access the property
sdgsDesa.findMany.page = page;
try {
const res = await ApiFetch.api.landingpage.sdgsdesa[
"findMany"
].get({
query: { page, limit },
});
if (res.status === 200 && res.data?.success) {
sdgsDesa.findMany.data = res.data.data || [];
sdgsDesa.findMany.total = res.data.total || 0;
sdgsDesa.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load media sosial:", res.data?.message);
sdgsDesa.findMany.data = [];
sdgsDesa.findMany.total = 0;
sdgsDesa.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading media sosial:", error);
sdgsDesa.findMany.data = [];
sdgsDesa.findMany.total = 0;
sdgsDesa.findMany.totalPages = 1;
} finally {
sdgsDesa.findMany.loading = false;
}
},
},

View File

@@ -4,8 +4,9 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
import { Box, Button, Center, FileInput, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
@@ -16,11 +17,14 @@ function EditSuratKeterangan() {
const params = useParams()
const stateSurat = useProxy(stateLayananDesa.suratKeterangan)
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [previewImage2, setPreviewImage2] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const [file2, setFile2] = useState<File | null>(null);
const [formData, setFormData] = useState({
name: stateSurat.edit.form.name,
deskripsi: stateSurat.edit.form.deskripsi,
imageId: stateSurat.edit.form.imageId,
image2Id: stateSurat.edit.form.image2Id,
})
useEffect(() => {
@@ -31,12 +35,22 @@ function EditSuratKeterangan() {
const data = await stateSurat.edit.load(id);
if (data) {
setFormData({
name: data.name,
deskripsi: data.deskripsi,
imageId: data.imageId,
name: data.name || "",
deskripsi: data.deskripsi || "",
imageId: data.imageId || "",
image2Id: data.image2Id || "",
});
if (data?.image?.link) {
if (data.image?.link) {
setPreviewImage(data.image.link);
} else {
setPreviewImage(null);
}
if (data.image2?.link) {
setPreviewImage2(data.image2.link);
} else {
setPreviewImage2(null);
}
}
} catch (error) {
@@ -54,6 +68,7 @@ function EditSuratKeterangan() {
name: formData.name,
deskripsi: formData.deskripsi,
imageId: formData.imageId,
image2Id: formData.image2Id,
}
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
@@ -66,6 +81,17 @@ function EditSuratKeterangan() {
stateSurat.edit.form.imageId = uploaded.id;
}
if (file2) {
const res = await ApiFetch.api.fileStorage.create.post({ file: file2, name: file2.name });
const uploaded = res.data?.data;
if (!uploaded?.id) {
return toast.error("Gagal upload gambar");
}
stateSurat.edit.form.image2Id = uploaded.id;
}
await stateSurat.edit.update()
toast.success("Surat berhasil diperbarui!")
router.push("/admin/desa/layanan/pelayanan_surat_keterangan")
@@ -103,25 +129,106 @@ function EditSuratKeterangan() {
}}
/>
</Box>
<FileInput
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar Konten</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
"data:image/png;base64," + Buffer.from(buf).toString("base64")
);
setPreviewImage(base64);
}}
/>
{previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg={"gray"}>
<IconImageInPicture />
</Center>
)}
<Box>
<Text fz={"md"} fw={"bold"}>Gambar</Text>
<Box >
<Dropzone
onDrop={(files) => {
const file = files[0]; // Hanya ambil file pertama
if (file) {
setFile(file);
setPreviewImage(URL.createObjectURL(file)); // Buat preview
}
}}
maxSize={5 * 1024 ** 2} // 5MB
accept={{
'image/*': ['.jpeg', '.jpg', '.png', '.webp']
}}
>
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
</Dropzone.Accept>
<Dropzone.Reject>
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Drag images here or click to select files
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
Attach as many files as you like, each file should not exceed 5mb
</Text>
</div>
</Group>
</Dropzone>
{previewImage && (
<Image
src={previewImage}
alt="Preview"
width={280}
height={180}
fit="cover"
radius="sm"
mt="md"
/>
)}
</Box>
</Box>
<Box>
<Text fz={"md"} fw={"bold"}>Gambar</Text>
<Box >
<Dropzone
onDrop={(files) => {
const file = files[0]; // Hanya ambil file pertama
if (file) {
setFile2(file);
setPreviewImage2(URL.createObjectURL(file)); // Buat preview
}
}}
maxSize={5 * 1024 ** 2} // 5MB
accept={{
'image/*': ['.jpeg', '.jpg', '.png', '.webp']
}}
>
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
</Dropzone.Accept>
<Dropzone.Reject>
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Drag images here or click to select files
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
Attach as many files as you like, each file should not exceed 5mb
</Text>
</div>
</Group>
</Dropzone>
{previewImage2 && (
<Image
src={previewImage2}
alt="Preview"
width={280}
height={180}
fit="cover"
radius="sm"
mt="md"
/>
)}
</Box>
</Box>
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
</Stack>
</Paper>

View File

@@ -64,6 +64,10 @@ function DetailSuratKeterangan() {
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 150, md: 150, lg: 150 }} src={suratKeteranganState.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 150, md: 150, lg: 150 }} src={suratKeteranganState.findUnique.data?.image2?.link} alt="gambar" />
</Box>
<Flex gap={"xs"} mt={10}>
<Button
onClick={() => {

View File

@@ -3,8 +3,9 @@ import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
import { Box, Button, Center, FileInput, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { toast } from 'react-toastify';
@@ -12,8 +13,8 @@ import { useProxy } from 'valtio/utils';
function CreateSuratKeterangan() {
const stateSurat = useProxy(stateLayananDesa.suratKeterangan)
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const [previewImage2, setPreviewImage2] = useState<{ preview: string; file: File } | null>(null);
const [previewImage, setPreviewImage] = useState<{ preview: string; file: File } | null>(null);
const router = useRouter()
const resetForm = () => {
@@ -21,33 +22,57 @@ function CreateSuratKeterangan() {
name: "",
deskripsi: "",
imageId: "",
image2Id: ""
}
setPreviewImage(null)
setFile(null)
setPreviewImage2(null)
}
const handleSubmit = async () => {
if (!file) {
return toast.error("Silahkan pilih file gambar terlebih dahulu")
if (!previewImage) {
return toast.warn("Pilih file gambar utama terlebih dahulu");
}
const res = await ApiFetch.api.fileStorage.create.post({
file: file,
name: file.name
})
try {
// Upload gambar utama
const res1 = await ApiFetch.api.fileStorage.create.post({
file: previewImage.file,
name: `main_${previewImage.file.name}`,
});
const uploaded = res.data?.data
if (!uploaded?.id) {
return toast.error("Gagal upload gambar")
const uploadedImage1 = res1.data?.data;
if (!uploadedImage1?.id) {
return toast.error("Gagal upload gambar utama");
}
let uploadedImage2 = null;
// Upload gambar kedua jika ada
if (previewImage2) {
const res2 = await ApiFetch.api.fileStorage.create.post({
file: previewImage2.file,
name: `secondary_${previewImage2.file.name}`,
});
uploadedImage2 = res2.data?.data;
}
// Set form data
stateSurat.create.form.imageId = uploadedImage1.id;
if (uploadedImage2?.id) {
stateSurat.create.form.image2Id = uploadedImage2.id;
}
// Create the record
await stateSurat.create.create();
// Reset form dan redirect
resetForm();
toast.success("Data surat keterangan berhasil ditambahkan");
router.push("/admin/desa/layanan/pelayanan_surat_keterangan");
} catch (error) {
console.error("Error creating surat keterangan:", error);
toast.error("Terjadi kesalahan saat menambahkan surat keterangan");
}
stateSurat.create.form.imageId = uploaded.id
await stateSurat.create.create()
resetForm()
router.push("/admin/desa/layanan/pelayanan_surat_keterangan")
}
};
return (
<Box>
<Box mb={10}>
@@ -75,25 +100,105 @@ function CreateSuratKeterangan() {
}}
/>
</Box>
<FileInput
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar Konten</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
"data:image/png;base64," + Buffer.from(buf).toString("base64")
);
setPreviewImage(base64);
}}
/>
{previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg={"gray"}>
<IconImageInPicture />
</Center>
)}
<Box>
<Text fz={"md"} fw={"bold"} mb="sm">Gambar Utama</Text>
<Dropzone
onDrop={(files) => {
const file = files[0];
if (file) {
setPreviewImage({
file,
preview: URL.createObjectURL(file)
});
}
}}
maxSize={5 * 1024 ** 2}
accept={{
'image/*': ['.jpeg', '.jpg', '.png', '.webp']
}}
>
<Group justify="center" gap="xl" mih={120} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload size={32} color="var(--mantine-color-blue-6)" stroke={1.5} />
</Dropzone.Accept>
<Dropzone.Reject>
<IconX size={32} color="var(--mantine-color-red-6)" stroke={1.5} />
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size={32} color="var(--mantine-color-dimmed)" stroke={1.5} />
</Dropzone.Idle>
<div>
<Text size="md" inline>Seret gambar ke sini atau klik untuk memilih</Text>
<Text size="sm" c="dimmed" inline mt={7} display="block">
Ukuran maksimal 5MB (JPEG, JPG, PNG, WebP)
</Text>
</div>
</Group>
</Dropzone>
{previewImage && (
<Image
src={previewImage.preview}
alt="Preview Gambar Utama"
width={280}
height={180}
fit="cover"
radius="sm"
mt="md"
/>
)}
</Box>
<Box mt="lg">
<Text fz={"md"} fw={"bold"} mb="sm">Gambar Tambahan (Opsional)</Text>
<Dropzone
onDrop={(files) => {
const file = files[0];
if (file) {
setPreviewImage2({
file,
preview: URL.createObjectURL(file)
});
}
}}
maxSize={5 * 1024 ** 2}
accept={{
'image/*': ['.jpeg', '.jpg', '.png', '.webp']
}}
>
<Group justify="center" gap="xl" mih={120} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload size={32} color="var(--mantine-color-blue-6)" stroke={1.5} />
</Dropzone.Accept>
<Dropzone.Reject>
<IconX size={32} color="var(--mantine-color-red-6)" stroke={1.5} />
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size={32} color="var(--mantine-color-dimmed)" stroke={1.5} />
</Dropzone.Idle>
<div>
<Text size="md" inline>Seret gambar ke sini atau klik untuk memilih</Text>
<Text size="sm" c="dimmed" inline mt={7} display="block">
Ukuran maksimal 5MB (JPEG, JPG, PNG, WebP)
</Text>
</div>
</Group>
</Dropzone>
{previewImage2 ? (
<Image
src={previewImage2.preview}
alt="Preview Gambar Tambahan"
width={280}
height={180}
fit="cover"
radius="sm"
mt="md"
/>
) : (
<Text size="sm" c="dimmed" mt="sm">
Kosongkan jika tidak ada gambar tambahan
</Text>
)}
</Box>
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
</Stack>
</Paper>

View File

@@ -1,21 +1,21 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import JudulListTab from '@/app/admin/(dashboard)/_com/judulListTab';
import colors from '@/con/colors';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useProxy } from 'valtio/utils';
import stateLayananDesa from '../../../_state/desa/layananDesa';
import { useShallowEffect } from '@mantine/hooks';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import stateLayananDesa from '../../../_state/desa/layananDesa';
function SuratKeterangan() {
const [search, setSearch] = useState("");
return (
<Box>
<HeaderSearch
title='Posisi Organisasi'
title='Pelayanan Surat Keterangan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
@@ -30,54 +30,89 @@ function ListSuratKeterangan({ search }: { search: string }) {
const suratKeteranganState = useProxy(stateLayananDesa.suratKeterangan)
const router = useRouter()
useShallowEffect(() => {
suratKeteranganState.findMany.load()
const {
data,
page,
totalPages,
loading,
load,
} = suratKeteranganState.findMany;
useEffect(() => {
load(page, 10)
}, [])
const filteredData = (suratKeteranganState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword)
);
});
if (!suratKeteranganState.findMany.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
const filteredData = useMemo(() => {
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.deskripsi?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
// Handle loading state
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton height={300} />
</Stack>
);
}
if (data.length === 0) {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulList
title='List Surat Keterangan'
href='/admin/desa/layanan/pelayanan_surat_keterangan/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
</Table>
</Box>
</Paper>
</Box>
);
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
<JudulList
title='List Surat Keterangan'
href='/admin/desa/layanan/pelayanan_surat_keterangan/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="gambar" />
<Box w={200}>
<Text truncate="end" fz={"sm"}>{item.name}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={300}>
<Text truncate="end" lineClamp={1} fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
</TableTd>
<TableTd>
<Text>
<Button onClick={() => router.push(`/admin/desa/layanan/pelayanan_surat_keterangan/${item.id}`)}>
@@ -90,6 +125,18 @@ function ListSuratKeterangan({ search }: { search: string }) {
</TableTbody>
</Table>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10);
window.scrollTo(0, 0);
}}
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
);
}

View File

@@ -1,6 +1,5 @@
'use client'
/* eslint-disable react-hooks/exhaustive-deps */
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
@@ -76,6 +75,14 @@ function EditPelayananTelunjukSakti() {
label={<Text fz={"sm"} fw={"bold"}>Nama Surat Keterangan</Text>}
placeholder="masukkan nama surat keterangan"
/>
<TextInput
value={formData.deskripsi}
onChange={(val) => {
setFormData({ ...formData, deskripsi: val.target.value });
}}
label={<Text fz={"sm"} fw={"bold"}>Tautan Link</Text>}
placeholder="masukkan tautan link"
/>
<TextInput
value={formData.link}
onChange={(val) => {
@@ -84,15 +91,6 @@ function EditPelayananTelunjukSakti() {
label={<Text fz={"sm"} fw={"bold"}>Link</Text>}
placeholder="masukkan link"
/>
<Box>
<Text fz={"sm"} fw={"bold"}>Konten</Text>
<EditEditor
value={formData.deskripsi}
onChange={(htmlContent) => {
setFormData({ ...formData, deskripsi: htmlContent });
}}
/>
</Box>
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
</Stack>
</Paper>

View File

@@ -58,11 +58,24 @@ function DetailPelayananTelunjukSakti() {
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Link</Text>
<Text fz={"lg"}>{telunjukSaktiState.findUnique.data?.link}</Text>
<Text
component="a"
href={telunjukSaktiState.findUnique.data?.link}
target="_blank"
rel="noopener noreferrer"
style={{
display: 'block',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}}
>
{telunjukSaktiState.findUnique.data?.link}
</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
<Text fz={"lg"}dangerouslySetInnerHTML={{ __html: telunjukSaktiState.findUnique.data?.deskripsi }}></Text>
<Text fz={"lg"}>{telunjukSaktiState.findUnique.data?.deskripsi}</Text>
</Box>
<Flex gap={"xs"} mt={10}>
<Button

View File

@@ -1,5 +1,4 @@
'use client'
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
@@ -43,6 +42,14 @@ function CreatePelayananTelunjukDesa() {
label={<Text fz={"sm"} fw={"bold"}>Nama Pelayanan Telunjuk Sakti Desa</Text>}
placeholder="masukkan nama pelayanan telunjuk sakti desa"
/>
<TextInput
value={stateTelunjukDesa.create.form.deskripsi}
onChange={(val) => {
stateTelunjukDesa.create.form.deskripsi = val.target.value;
}}
label={<Text fz={"sm"} fw={"bold"}>Tautan Link</Text>}
placeholder="masukkan tautan link"
/>
<TextInput
value={stateTelunjukDesa.create.form.link}
onChange={(val) => {
@@ -51,15 +58,6 @@ function CreatePelayananTelunjukDesa() {
label={<Text fz={"sm"} fw={"bold"}>Link</Text>}
placeholder="masukkan link"
/>
<Box>
<Text fz={"sm"} fw={"bold"}>Konten</Text>
<CreateEditor
value={stateTelunjukDesa.create.form.deskripsi}
onChange={(htmlContent) => {
stateTelunjukDesa.create.form.deskripsi = htmlContent;
}}
/>
</Box>
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
</Stack>
</Paper>

View File

@@ -1,89 +1,153 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import JudulListTab from '@/app/admin/(dashboard)/_com/judulListTab';
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useProxy } from 'valtio/utils';
import stateLayananDesa from '../../../_state/desa/layananDesa';
import { useShallowEffect } from '@mantine/hooks';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import stateLayananDesa from '../../../_state/desa/layananDesa';
function PelayananTelunjukSakti() {
const [search, setSearch] = useState("");
return (
<Box>
<HeaderSearch
title='Posisi Organisasi'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPelayananTelunjukSakti search={search} />
</Box>
);
const [search, setSearch] = useState("");
return (
<Box>
<HeaderSearch
title='Posisi Organisasi'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPelayananTelunjukSakti search={search} />
</Box>
);
}
function ListPelayananTelunjukSakti({ search }: { search: string }) {
const telunjukSaktiState = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa)
const router = useRouter()
useShallowEffect(() => {
telunjukSaktiState.findMany.load()
const {
data,
page,
totalPages,
loading,
load,
} = telunjukSaktiState.findMany;
useEffect(() => {
load(page, 10)
}, [])
const filteredData = (telunjukSaktiState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword)
);
});
const filteredData = useMemo(() => {
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.link?.toLowerCase().includes(keyword) ||
item.deskripsi?.toLowerCase().includes(keyword)
);
})
.sort((a, b) => a.posisi?.hierarki - b.posisi?.hierarki);
}, [data, search]);
if (!telunjukSaktiState.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
<Skeleton height={300} />
</Stack>
)
);
}
if (data.length === 0) {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulList
title='List Pelayanan Telunjuk Sakti Desa'
href='/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama</TableTh>
<TableTh>Link</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd colSpan={3}>
<Text fz={"sm"} color="gray.5">
Tidak ada data
</Text>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Paper>
</Box>
);
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
<JudulList
title='List Pelayanan Telunjuk Sakti Desa'
href='/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Link</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd><Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} /></TableTd>
<TableTd>
<Text>
<Button onClick={() => router.push(`/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</Text>
</TableTd>
</TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" lineClamp={1} fz={"sm"} dangerouslySetInnerHTML={{ __html: item.name }} />
</Box>
</TableTd>
<TableTd>
<Box w={100}>
<a href={item.link} target="_blank" rel="noopener noreferrer">
<Text dangerouslySetInnerHTML={{ __html: item.deskripsi }} truncate="end" fz={"sm"} />
</a>
</Box>
</TableTd>
<TableTd>
<Text>
<Button onClick={() => router.push(`/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</Text>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10);
window.scrollTo(0, 0);
}}
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
);
}

View File

@@ -50,6 +50,10 @@ function DetailPenghargaan() {
{statePenghargaan.findUnique.data ? (
<Paper key={statePenghargaan.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 400, md: 400, lg: 400 }} src={statePenghargaan.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Judul</Text>
<Text fz={"lg"}>{statePenghargaan.findUnique.data?.name}</Text>
@@ -62,10 +66,6 @@ function DetailPenghargaan() {
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: statePenghargaan.findUnique.data?.deskripsi }} />
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 150, md: 150, lg: 150 }} src={statePenghargaan.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Flex gap={"xs"} mt={10}>
<Button
onClick={() => {

View File

@@ -1,61 +1,103 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import penghargaanState from '@/app/admin/(dashboard)/_state/desa/penghargaan';
import colors from '@/con/colors';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../_com/judulListTab';
import { useState } from 'react';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
function Penghargaan() {
const [search, setSearch] = useState("");
return (
<Box>
<HeaderSearch
title='Posisi Organisasi'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPenghargaan search={search} />
</Box>
);
const [search, setSearch] = useState("");
return (
<Box>
<HeaderSearch
title='Penghargaan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPenghargaan search={search} />
</Box>
);
}
function ListPenghargaan({ search }: { search: string }) {
const state = useProxy(penghargaanState)
const router = useRouter()
useShallowEffect(() => {
state.findMany.load()
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany;
useEffect(() => {
load(page, 10)
}, [])
const filteredData = (state.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword)
);
});
const filteredData = useMemo(() => {
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.deskripsi?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
if (!state.findMany.data) {
return(
// Handle loading state
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
<Skeleton height={300} />
</Stack>
);
}
if (data.length === 0) {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulList
title='List Penghargaan'
href='/admin/desa/penghargaan/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd colSpan={4}>
<Text ta="center">Tidak ada data</Text>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Paper>
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
<JudulList
title='List Penghargaan'
href='/admin/desa/penghargaan/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
@@ -69,25 +111,43 @@ function ListPenghargaan({ search }: { search: string }) {
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
<Box w={100}>
<Text lineClamp={1} truncate="end" fz={"sm"}>{item.name}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={100}>
<Text lineClamp={1} truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="gambar" />
</TableTd>
</TableTd>
<TableTd>
<Text>
<Button onClick={() => router.push(`/admin/desa/penghargaan/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</Text>
</TableTd>
</TableTr>
))}
<IconDeviceImac size={20} />
</Button>
</Text>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10);
window.scrollTo(0, 0);
}}
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
);
}

View File

@@ -0,0 +1,63 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
function LayoutTabsPotensi({ children }: { children: React.ReactNode }) {
const router = useRouter()
const pathname = usePathname()
const tabs = [
{
label: "List Potensi",
value: "list_potensi",
href: "/admin/desa/potensi/list-potensi"
},
{
label: "Kategori Potensi",
value: "kategori_potensi",
href: "/admin/desa/potensi/kategori-potensi"
},
];
const curentTab = tabs.find(tab => tab.href === pathname)
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
const handleTabChange = (value: string | null) => {
const tab = tabs.find(t => t.value === value)
if (tab) {
router.push(tab.href)
}
setActiveTab(value)
}
useEffect(() => {
const match = tabs.find(tab => tab.href === pathname)
if (match) {
setActiveTab(match.value)
}
}, [pathname])
return (
<Stack>
<Title order={3}>Potensi</Title>
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
{tabs.map((e, i) => (
<TabsTab key={i} value={e.value}>{e.label}</TabsTab>
))}
</TabsList>
{tabs.map((e, i) => (
<TabsPanel key={i} value={e.value}>
{/* Konten dummy, bisa diganti tergantung routing */}
<></>
</TabsPanel>
))}
</Tabs>
{children}
</Stack>
);
}
export default LayoutTabsPotensi;

View File

@@ -1,128 +0,0 @@
'use client';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
import { Box, Button, Center, FileInput, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
import potensiDesaState from '../../../_state/desa/potensi';
import { useRouter } from 'next/navigation';
import CreateEditor from '../../../_com/createEditor';
function CreatePotensi() {
const potensiState = useProxy(potensiDesaState);
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const router = useRouter();
const handleSubmit = async () => {
if (!file) return toast.warn('Pilih file gambar terlebih dahulu');
const res = await ApiFetch.api.fileStorage.create.post({
file,
name: file.name,
});
const uploaded = res.data?.data;
if (!uploaded?.id) {
return toast.error('Gagal upload gambar');
}
potensiState.create.form.imageId = uploaded.id;
await potensiState.create.create();
resetForm();
router.push('/admin/desa/potensi');
};
const resetForm = () => {
potensiState.create.form = {
name: '',
deskripsi: '',
kategori: '',
imageId: '',
content: '',
};
setPreviewImage(null);
setFile(null);
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Potensi</Title>
<TextInput
value={potensiState.create.form.name}
onChange={(val) => (potensiState.create.form.name = val.target.value)}
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
value={potensiState.create.form.deskripsi}
onChange={(val) => (potensiState.create.form.deskripsi = val.target.value)}
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<TextInput
value={potensiState.create.form.kategori}
onChange={(val) => (potensiState.create.form.kategori = val.target.value)}
label={<Text fz="sm" fw="bold">Kategori</Text>}
placeholder="masukkan kategori"
/>
<FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/>
{previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)}
<Box>
<Text fz="sm" fw="bold">Konten</Text>
<CreateEditor
value={potensiState.create.form.content}
onChange={(htmlContent) => {
potensiState.create.form.content = htmlContent;
}}
/>
</Box>
<Button bg={colors['blue-button']} onClick={handleSubmit}>
Simpan Potensi
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreatePotensi;

View File

@@ -0,0 +1,80 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import potensiDesaState from '@/app/admin/(dashboard)/_state/desa/potensi';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
function EditKategoriPotensi() {
const editState = useProxy(potensiDesaState.kategoriPotensi)
const router = useRouter();
const params = useParams();
const [formData, setFormData] = useState({
nama: editState.update.form.nama || '',
});
useEffect(() => {
const loadKategori = async () => {
const id = params?.id as string;
if (!id) return;
try {
const data = await editState.update.load(id); // akses langsung, bukan dari proxy
if (data) {
setFormData({
nama: data.nama || '',
});
}
} catch (error) {
console.error("Error loading kategori potensi:", error);
toast.error("Gagal memuat data kategori potensi");
}
};
loadKategori();
}, [params?.id]);
const handleSubmit = async () => {
try {
editState.update.form = {
...editState.update.form,
nama: formData.nama,
};
await editState.update.update();
toast.success('Kategori Potensi berhasil diperbarui!');
router.push('/admin/desa/potensi/kategori-potensi');
} catch (error) {
console.error('Error updating kategori potensi:', error);
toast.error('Terjadi kesalahan saat memperbarui kategori potensi');
}
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors["blue-button"]} size={30} />
</Button>
</Box>
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
<Stack gap={"xs"}>
<Title order={3}>Edit Kategori Potensi</Title>
<TextInput
value={formData.nama}
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
label={<Text fz={"sm"} fw={"bold"}>Nama Kategori Potensi</Text>}
placeholder="masukkan nama kategori potensi"
/>
<Button onClick={handleSubmit}>Simpan</Button>
</Stack>
</Paper>
</Box>
);
}
export default EditKategoriPotensi;

View File

@@ -0,0 +1,55 @@
'use client'
import potensiDesaState from '@/app/admin/(dashboard)/_state/desa/potensi';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
function CreateKategoriPotensi() {
const createState = useProxy(potensiDesaState.kategoriPotensi)
const router = useRouter();
const resetForm = () => {
createState.create.form = {
nama: "",
};
};
const handleSubmit = async () => {
await createState.create.create();
resetForm();
router.push("/admin/desa/potensi/kategori-potensi")
};
return (
<Box>
<Box mb={10}>
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Create Kategori Potensi</Title>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Kategori Potensi</Text>}
placeholder='Masukkan nama kategori Potensi'
value={createState.create.form.nama}
onChange={(val) => {
createState.create.form.nama = val.target.value;
}}
/>
<Group>
<Button onClick={handleSubmit} bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
);
}
export default CreateKategoriPotensi;

View File

@@ -0,0 +1,128 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import potensiDesaState from '../../../_state/desa/potensi';
function KategoriPotensi() {
const [search, setSearch] = useState('');
return (
<Box>
<HeaderSearch
title='Kategori Potensi'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListKategoriPotensi search={search} />
</Box>
);
}
function ListKategoriPotensi({ search }: { search: string }) {
const listDataState = useProxy(potensiDesaState.kategoriPotensi)
const router = useRouter();
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
useEffect(() => {
listDataState.findMany.load()
}, [])
const handleDelete = () => {
if (selectedId) {
listDataState.delete.delete(selectedId)
setModalHapus(false)
setSelectedId(null)
listDataState.findMany.load()
}
}
const filteredData = (listDataState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.nama.toLowerCase().includes(keyword)
);
});
if (!listDataState.findMany.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p="md">
<Stack>
<JudulList
title='List Kategori Potensi'
href='/admin/desa/potensi/kategori-potensi/create'
/>
<Box style={{ overflowX: 'auto' }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>No</TableTh>
<TableTh>Nama</TableTh>
<TableTh>Edit</TableTh>
<TableTh>Hapus</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item, index) => (
<TableTr key={item.id}>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>{index + 1}</Text>
</Box>
</TableTd>
<TableTd>{item.nama}</TableTd>
<TableTd>
<Button color='green' onClick={() => router.push(`/admin/desa/potensi/kategori-potensi/${item.id}`)}>
<IconEdit size={20} />
</Button>
</TableTd>
<TableTd>
<Button
color='red'
disabled={listDataState.delete.loading}
onClick={() => {
setSelectedId(item.id)
setModalHapus(true)
}}>
<IconTrash size={20} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleDelete}
text='Apakah anda yakin ingin menghapus kategori Potensi ini?'
/>
</Box>
)
}
export default KategoriPotensi;

View File

@@ -0,0 +1,14 @@
'use client'
import React from 'react';
import LayoutTabsPotensi from './_lib/layoutTabs';
function Layout({ children }: { children: React.ReactNode }) {
return (
<LayoutTabsPotensi>
{children}
</LayoutTabsPotensi>
)
}
export default Layout;

View File

@@ -1,21 +1,22 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import EditEditor from "@/app/admin/(dashboard)/_com/editEditor";
import potensiDesaState from "@/app/admin/(dashboard)/_state/desa/potensi";
import colors from "@/con/colors";
import ApiFetch from "@/lib/api-fetch";
import { Box, Button, Paper, Stack, Title, TextInput, FileInput, Center, Text, Image } from "@mantine/core";
import { IconArrowBack, IconImageInPicture } from "@tabler/icons-react";
import { useParams } from "next/navigation";
import { useRouter } from "next/navigation";
import { useState, useEffect } from "react";
import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { IconArrowBack, IconPhoto, IconUpload, IconX } from "@tabler/icons-react";
import { useParams, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { toast } from "react-toastify";
import { useProxy } from "valtio/utils";
function EditPotensi() {
const potensiState = useProxy(potensiDesaState)
const potensiState = useProxy(potensiDesaState.potensiDesa)
const router = useRouter()
const params = useParams()
@@ -24,23 +25,24 @@ function EditPotensi() {
const [formData, setFormData] = useState({
name: '',
deskripsi: '',
kategori: '',
kategoriId: '',
content: '',
imageId: ''
});
useEffect(() => {
potensiDesaState.kategoriPotensi.findMany.load()
const loadPotensi = async () => {
const id = params?.id as string;
if (!id) return;
try {
const data = await potensiDesaState.edit.load(id); // ambil data dari API
const data = await potensiState.edit.load(id); // ambil data dari API
if (data) {
setFormData({
name: data.name || '',
deskripsi: data.deskripsi || '',
kategori: data.kategori || '',
kategoriId: data.kategoriId || '',
content: data.content || '',
imageId: data.imageId || '',
});
@@ -65,7 +67,7 @@ function EditPotensi() {
...potensiState.edit.form,
name: formData.name,
deskripsi: formData.deskripsi,
kategori: formData.kategori,
kategoriId: formData.kategoriId,
content: formData.content,
};
@@ -82,7 +84,7 @@ function EditPotensi() {
await potensiState.edit.update();
toast.success("Potensi berhasil diperbarui!");
router.push("/admin/desa/potensi");
router.push("/admin/desa/potensi/list-potensi");
} catch (error) {
console.error("Error updating potensi:", error);
toast.error("Terjadi kesalahan saat memperbarui potensi");
@@ -119,40 +121,82 @@ function EditPotensi() {
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<TextInput
value={formData.kategori}
onChange={(e) => {
const val = e.target.value;
setFormData((prev) => ({ ...prev, kategori: val }));
potensiState.edit.form.kategori = val;
}}
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
placeholder="masukkan kategori"
<Select
value={formData.kategoriId}
onChange={(val) => setFormData({ ...formData, kategoriId: val || "" })}
label={<Text fw={"bold"} fz={"sm"}>Kategori</Text>}
placeholder='Pilih kategori'
data={
potensiDesaState.kategoriPotensi.findMany.data?.map((v) => ({
value: v.id,
label: v.nama
})) || []
}
clearable
searchable
required
error={!formData.kategoriId ? "Pilih kategori" : undefined}
/>
<Box>
<Text fz={"md"} fw={"bold"}>Gambar</Text>
<Box>
<Dropzone
onDrop={(files) => {
const selectedFile = files[0]; // Ambil file pertama
if (selectedFile) {
setFile(selectedFile);
setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview
}
}}
onReject={() => toast.error('File tidak valid.')}
maxSize={5 * 1024 ** 2} // Maks 5MB
accept={{ 'image/*': [] }}
>
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
</Dropzone.Accept>
<Dropzone.Reject>
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
</Dropzone.Idle>
<FileInput
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
"data:image/png;base64," + Buffer.from(buf).toString("base64")
);
setPreviewImage(base64);
}}
/>
{previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg={"gray"}>
<IconImageInPicture />
</Center>
)}
<div>
<Text size="xl" inline>
Drag gambar ke sini atau klik untuk pilih file
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
Maksimal 5MB dan harus format gambar
</Text>
</div>
</Group>
</Dropzone>
{/* Tampilkan preview kalau ada */}
{previewImage && (
<Box mt="sm">
<Image
src={previewImage}
alt="Preview"
style={{
maxWidth: '100%',
maxHeight: '200px',
objectFit: 'contain',
borderRadius: '8px',
border: '1px solid #ddd',
}}
/>
</Box>
)}
</Box>
</Box>
<Box>
<Text fz={"sm"} fw={"bold"}>Konten</Text>
<EditEditor
value={formData.content}
value={formData.content}
onChange={(htmlContent) => {
setFormData((prev) => ({ ...prev, content: htmlContent }));
potensiState.edit.form.content = htmlContent;

View File

@@ -5,18 +5,19 @@ import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import React from 'react';
import { useProxy } from 'valtio/utils';
import potensiDesaState from '../../../../_state/desa/potensi';
import { useShallowEffect } from '@mantine/hooks';
import { useParams } from 'next/navigation';
import { useState } from 'react';
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
import potensiDesaState from '@/app/admin/(dashboard)/_state/desa/potensi';
export default function DetailPotensi() {
const router = useRouter()
const params = useParams()
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const potensiState = useProxy(potensiDesaState)
const potensiState = useProxy(potensiDesaState.potensiDesa)
useShallowEffect(() => {
potensiState.findUnique.load(params?.id as string)
@@ -60,7 +61,7 @@ export default function DetailPotensi() {
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Kategori</Text>
<Text fz={"lg"}>{potensiState.findUnique.data.kategori}</Text>
<Text fz={"lg"}>{potensiState.findUnique.data.kategori?.nama}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
@@ -91,7 +92,7 @@ export default function DetailPotensi() {
<Button
onClick={() => {
if (potensiState.findUnique.data) {
router.push(`/admin/desa/potensi/edit/${potensiState.findUnique.data.id}`)
router.push(`/admin/desa/potensi/list-potensi/${potensiState.findUnique.data.id}/edit`)
}
}}
disabled={!potensiState.findUnique.data}

View File

@@ -0,0 +1,176 @@
'use client';
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
import potensiDesaState from '@/app/admin/(dashboard)/_state/desa/potensi';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
function CreatePotensi() {
const potensiState = useProxy(potensiDesaState.potensiDesa);
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const router = useRouter();
useEffect(() => {
potensiDesaState.kategoriPotensi.findMany.load()
}, [])
const handleSubmit = async () => {
if (!file) return toast.warn('Pilih file gambar terlebih dahulu');
const res = await ApiFetch.api.fileStorage.create.post({
file,
name: file.name,
});
const uploaded = res.data?.data;
if (!uploaded?.id) {
return toast.error('Gagal upload gambar');
}
potensiState.create.form.imageId = uploaded.id;
await potensiState.create.create();
resetForm();
router.push('/admin/desa/potensi/list-potensi');
};
const resetForm = () => {
potensiState.create.form = {
name: '',
deskripsi: '',
kategoriId: '',
imageId: '',
content: '',
};
setPreviewImage(null);
setFile(null);
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Potensi</Title>
<TextInput
value={potensiState.create.form.name}
onChange={(val) => (potensiState.create.form.name = val.target.value)}
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
value={potensiState.create.form.deskripsi}
onChange={(val) => (potensiState.create.form.deskripsi = val.target.value)}
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<Select
label={<Text fw={"bold"} fz={"sm"}>Kategori</Text>}
placeholder='Pilih kategori'
value={potensiState.create.form.kategoriId || ""}
onChange={(val) => {
potensiState.create.form.kategoriId = val ?? "";
}}
data={potensiDesaState.kategoriPotensi.findMany.data?.map((item) => ({
value: item.id,
label: item.nama,
}))}
/>
<Box>
<Text fz={"md"} fw={"bold"}>Gambar</Text>
<Box>
<Dropzone
onDrop={(files) => {
const selectedFile = files[0]; // Ambil file pertama
if (selectedFile) {
setFile(selectedFile);
setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview
}
}}
onReject={() => toast.error('File tidak valid.')}
maxSize={5 * 1024 ** 2} // Maks 5MB
accept={{ 'image/*': [] }}
>
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
</Dropzone.Accept>
<Dropzone.Reject>
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Drag gambar ke sini atau klik untuk pilih file
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
Maksimal 5MB dan harus format gambar
</Text>
</div>
</Group>
</Dropzone>
{/* Tampilkan preview kalau ada */}
{previewImage && (
<Box mt="sm">
<Image
src={previewImage}
alt="Preview"
style={{
maxWidth: '100%',
maxHeight: '200px',
objectFit: 'contain',
borderRadius: '8px',
border: '1px solid #ddd',
}}
/>
</Box>
)}
</Box>
</Box>
<Box>
<Text fz="sm" fw="bold">Konten</Text>
<CreateEditor
value={potensiState.create.form.content}
onChange={(htmlContent) => {
potensiState.create.form.content = htmlContent;
}}
/>
</Box>
<Button bg={colors['blue-button']} onClick={handleSubmit}>
Simpan Potensi
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreatePotensi;

View File

@@ -0,0 +1,158 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import potensiDesaState from '../../../_state/desa/potensi';
function Potensi() {
const [search, setSearch] = useState("");
return (
<Box>
<HeaderSearch
title='Posisi Organisasi'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPotensi search={search} />
</Box>
);
}
function ListPotensi({ search }: { search: string }) {
const potensiState = useProxy(potensiDesaState)
const router = useRouter()
const {
data,
page,
totalPages,
loading,
load,
} = potensiState.potensiDesa.findMany;
useEffect(() => {
potensiState.kategoriPotensi.findMany.load()
load(page, 10)
}, [])
const filteredData = (potensiState.potensiDesa.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.kategori?.nama.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword)
);
});
// Handle loading state
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton height={300} />
</Stack>
);
}
if (data.length === 0) {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Potensi'
href='/admin/desa/potensi/list-potensi/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Kategori</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd colSpan={4}>Tidak Ada Data</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Potensi'
href='/admin/desa/potensi/list-potensi/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Kategori</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>{item.name}</Text>
</Box></TableTd>
<TableTd>{item.kategori?.nama}</TableTd>
<TableTd>
<Box w={300}>
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/desa/potensi/list-potensi/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10);
window.scrollTo(0, 0);
}}
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
)
}
export default Potensi;

View File

@@ -1,100 +0,0 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
import potensiDesaState from '../../_state/desa/potensi';
import { useState } from 'react';
function Potensi() {
const [search, setSearch] = useState("");
return (
<Box>
<HeaderSearch
title='Posisi Organisasi'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPotensi search={search} />
</Box>
);
}
function ListPotensi({ search }: { search: string }) {
const potensiState = useProxy(potensiDesaState)
const router = useRouter()
useShallowEffect(() => {
potensiState.findMany.load()
}, [])
const filteredData = (potensiState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.kategori.toLowerCase().includes(keyword)
);
});
if (!potensiState.findMany.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Potensi'
href='/admin/desa/potensi/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Kategori</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>{item.name}</Text>
</Box></TableTd>
<TableTd>{item.kategori}</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="image" />
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/desa/potensi/detail/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default Potensi;

View File

@@ -195,7 +195,6 @@ function CreateAPBDes() {
placeholder='Masukkan judul'
/>
<TextInput
type='number'
value={stateAPBDes.create.form.jumlah}
onChange={(val) => {
stateAPBDes.create.form.jumlah = val.target.value;

View File

@@ -77,7 +77,7 @@ function ListAPBDes({ search }: { search: string }) {
</Box>
</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"}>{item.jumlah}</Text>
<Text truncate="end" fz={"sm"}>Rp. {item.jumlah}</Text>
</TableTd>
<TableTd>
{item.file?.link ? (

View File

@@ -68,7 +68,7 @@ function ListKategoriKegiatan({ search }: { search: string }) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton height={300} />
<Skeleton height={550} />
</Stack>
);
}

View File

@@ -59,7 +59,7 @@ function ListDesaAntiKorupsi({ search }: { search: string }) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton height={300} />
<Skeleton height={550} />
</Stack>
);
}

View File

@@ -77,8 +77,10 @@ function ListPrestasi({ search }: { search: string }) {
<Text truncate="end" fz={"sm"}>{item.name}</Text>
</Box>
</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
<TableTd >
<Box w={150}>
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
</TableTd>
<TableTd>{item.kategori?.name}</TableTd>
<TableTd>

View File

@@ -57,7 +57,7 @@ function ListMediaSosial({ search }: { search: string }) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton height={300} />
<Skeleton height={550} />
</Stack>
);
}

View File

@@ -57,7 +57,7 @@ function ListProgramInovasi({ search }: { search: string }) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton height={300} />
<Skeleton height={550} />
</Stack>
);
}

View File

@@ -163,7 +163,7 @@ function EditKolaborasiInovasi() {
<iframe
src={previewImage}
width="100%"
height="500px"
height="250px"
style={{ border: "1px solid #ccc", borderRadius: "8px" }}
/>
) : (

View File

@@ -110,7 +110,7 @@ function CreateSDGsDesa() {
<iframe
src={previewImage}
width="100%"
height="500px"
height="250px"
style={{ border: "1px solid #ccc", borderRadius: "8px" }}
/>
) : (

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../_com/header';
import sdgsDesa from '../../_state/landing-page/sdgs-desa';
@@ -30,24 +30,61 @@ function SdgsDesa() {
function ListSdgsDesa({ search }: { search: string }) {
const listState = useProxy(sdgsDesa)
const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = listState.findMany;
useEffect(() => {
listState.findMany.load()
load(page, 10)
}, [])
const filteredData = (listState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.jumlah.toLowerCase().includes(keyword)
)
});
const filteredData = useMemo(() => {
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.jumlah?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
if (!listState.findMany.data) {
// Handle loading state
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
<Skeleton height={550} />
</Stack>
)
);
}
if (data.length === 0) {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulList
title='List Desa Anti Korupsi'
href='/admin/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama SDGs Desa</TableTh>
<TableTh>Jumlah SDGs Desa</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
</Table>
</Box>
</Paper>
</Box>
);
}
return (
@@ -90,6 +127,18 @@ function ListSdgsDesa({ search }: { search: string }) {
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10);
window.scrollTo(0, 0);
}}
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
)
}

View File

@@ -98,7 +98,7 @@ export const navBar = [
{
id: "Desa_2",
name: "Potensi",
path: "/admin/desa/potensi"
path: "/admin/desa/potensi/list-potensi"
},
{
id: "Desa_3",

View File

@@ -7,6 +7,7 @@ import GalleryFoto from "./gallery/foto";
import GalleryVideo from "./gallery/video";
import LayananDesa from "./layanan";
import Penghargaan from "./penghargaan";
import KategoriPotensi from "./potensi/kategori-potensi";
const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
@@ -18,5 +19,6 @@ const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
.use(GalleryVideo)
.use(LayananDesa)
.use(Penghargaan)
.use(KategoriPotensi)
export default Desa;

View File

@@ -7,6 +7,7 @@ type FormCreate = Prisma.PelayananSuratKeteranganGetPayload<{
name: true;
deskripsi: true;
imageId: true;
image2Id: true;
};
}>;
async function createPelayananSuratKeterangan(context: Context) {
@@ -17,6 +18,7 @@ async function createPelayananSuratKeterangan(context: Context) {
name: body.name,
deskripsi: body.deskripsi,
imageId: body.imageId,
image2Id: body.image2Id,
},
});
return {

View File

@@ -17,6 +17,7 @@ const pelayananSuratKeteranganDelete = async (context: Context) => {
where: { id },
include: {
image: true,
image2: true,
},
});
@@ -40,6 +41,18 @@ const pelayananSuratKeteranganDelete = async (context: Context) => {
}
}
if (pelayananSuratKeterangan.image2) {
try {
const filePath = path.join(pelayananSuratKeterangan.image2.path, pelayananSuratKeterangan.image2.name);
await fs.unlink(filePath);
await prisma.fileStorage.delete({
where: { id: pelayananSuratKeterangan.image2.id },
});
} catch (err) {
console.error("Gagal hapus gambar lama:", err);
}
}
const deleted = await prisma.pelayananSuratKeterangan.delete({
where: { id },
});

View File

@@ -1,24 +1,47 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function pelayananSuratKeteranganFindMany() {
try {
const data = await prisma.pelayananSuratKeterangan.findMany({
where: { isActive: true },
include: {
image: true,
},
});
export default async function pelayananSuratKeteranganFindMany(context: Context) {
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const skip = (page - 1) * limit;
return {
success: true,
message: "Success fetch pelayanan surat keterangan",
data,
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch pelayanan surat keterangan",
};
}
try {
const [data, total] = await Promise.all([
prisma.pelayananSuratKeterangan.findMany({
where: { isActive: true },
include: {
image: true,
image2: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.pelayananSuratKeterangan.count({
where: { isActive: true }
})
]);
const totalPages = Math.ceil(total / limit);
return {
success: true,
message: "Success fetch pelayanan surat keterangan with pagination",
data,
page,
totalPages,
total,
};
} catch (e) {
console.error("Find many paginated error:", e);
return {
success: false,
message: "Failed fetch pelayanan surat keterangan with pagination",
data: [],
page: 1,
totalPages: 1,
total: 0,
};
}
}

View File

@@ -24,6 +24,7 @@ export default async function pelayananSuratKeteranganFindUnique(request: Reques
where: { id },
include: {
image: true,
image2: true,
},
});

View File

@@ -18,6 +18,7 @@ const PelayananSuratKeterangan = new Elysia({ prefix: "/pelayanansuratketerangan
name: t.String(),
deskripsi: t.String(),
imageId: t.String(),
image2Id: t.String(),
}),
})
.delete("/del/:id", pelayananSuratKeteranganDelete)
@@ -30,6 +31,7 @@ const PelayananSuratKeterangan = new Elysia({ prefix: "/pelayanansuratketerangan
name: t.String(),
deskripsi: t.String(),
imageId: t.String(),
image2Id: t.String(),
}),
})
export default PelayananSuratKeterangan;

View File

@@ -9,6 +9,7 @@ type FormUpdate = Prisma.PelayananSuratKeteranganGetPayload<{
name: true;
deskripsi: true;
imageId: true;
image2Id: true;
};
}>;
export default async function updatePelayananSuratKeterangan(context: Context) {
@@ -16,7 +17,7 @@ export default async function updatePelayananSuratKeterangan(context: Context) {
const id = context.params?.id;
const body = (await context.body) as Omit<FormUpdate, "id">;
const { name, deskripsi, imageId } = body;
const { name, deskripsi, imageId, image2Id } = body;
if (!id) {
return new Response(JSON.stringify({
@@ -63,12 +64,28 @@ export default async function updatePelayananSuratKeterangan(context: Context) {
}
}
if (existing.image2Id && existing.image2Id !== image2Id) {
const oldImage = existing.image;
if (oldImage) {
try {
const filePath = path.join(oldImage.path, oldImage.name);
await fs.unlink(filePath);
await prisma.fileStorage.delete({
where: { id: oldImage.id },
});
} catch (err) {
console.error("Gagal hapus gambar lama:", err);
}
}
}
const updated = await prisma.pelayananSuratKeterangan.update({
where: { id },
data: {
name,
deskripsi,
imageId,
image2Id,
},
})

View File

@@ -1,21 +1,43 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function pelayananTelunjukSaktiDesaFindMany() {
export default async function pelayananTelunjukSaktiDesaFindMany(context: Context) {
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const skip = (page - 1) * limit;
try {
const data = await prisma.pelayananTelunjukSaktiDesa.findMany({
where: { isActive: true },
});
return {
success: true,
message: "Success fetch pelayanan telunjuk sakti desa",
data,
};
const [data, total] = await Promise.all([
prisma.pelayananTelunjukSaktiDesa.findMany({
where: { isActive: true },
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.pelayananTelunjukSaktiDesa.count({
where: { isActive: true }
})
]);
const totalPages = Math.ceil(total / limit);
return {
success: true,
message: "Success fetch pelayanan telunjuk sakti desa with pagination",
data,
page,
totalPages,
total,
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch pelayanan telunjuk sakti desa",
};
console.error("Find many paginated error:", e);
return {
success: false,
message: "Failed fetch pelayanan telunjuk sakti desa with pagination",
data: [],
page: 1,
totalPages: 1,
total: 0,
};
}
}

View File

@@ -1,23 +1,46 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function penghargaanFindMany(context: Context) {
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const skip = (page - 1) * limit;
export default async function penghargaanFindMany() {
try {
const data = await prisma.penghargaan.findMany({
include: {
image: true,
},
});
const [data, total] = await Promise.all([
prisma.penghargaan.findMany({
where: { isActive: true },
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.penghargaan.count({
where: { isActive: true }
})
]);
const totalPages = Math.ceil(total / limit);
return {
success: true,
message: "Success fetch penghargaan",
message: "Success fetch penghargaan with pagination",
data,
page,
totalPages,
total,
};
} catch (error) {
console.error("Find many error:", error);
} catch (e) {
console.error("Find many paginated error:", e);
return {
success: false,
message: "Failed fetch penghargaan",
message: "Failed fetch penghargaan with pagination",
data: [],
page: 1,
totalPages: 1,
total: 0,
};
}
}
}

View File

@@ -6,7 +6,7 @@ type FormCreate = Prisma.PotensiDesaGetPayload<{
select: {
name: true;
deskripsi: true;
kategori: true;
kategoriId: true;
imageId: true;
content: true;
}
@@ -18,7 +18,7 @@ export default async function potensiDesaCreate(context: Context) {
data: {
name: body.name,
deskripsi: body.deskripsi,
kategori: body.kategori,
kategoriId: body.kategoriId,
imageId: body.imageId,
content: body.content,
},

View File

@@ -17,6 +17,7 @@ const potensiDesaDelete = async (context: Context) => {
where: { id },
include: {
image: true,
kategori: true
},
});

View File

@@ -1,26 +1,47 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function potensiDesaFindMany() {
export default async function potensiDesaFindMany(context: Context) {
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const skip = (page - 1) * limit;
try {
const data = await prisma.potensiDesa.findMany({
where: { isActive: true },
include: {
image: true,
},
});
return {
success: true,
message: "Success fetch potensi desa",
data,
};
const [data, total] = await Promise.all([
prisma.potensiDesa.findMany({
where: { isActive: true },
skip,
take: limit,
include: {
image: true,
kategori: true
},
orderBy: { createdAt: 'desc' },
}),
prisma.potensiDesa.count({
where: { isActive: true }
})
]);
const totalPages = Math.ceil(total / limit);
return {
success: true,
message: "Success fetch potensi desa with pagination",
data,
page,
totalPages,
total,
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch potensi desa",
};
console.error("Find many paginated error:", e);
return {
success: false,
message: "Failed fetch potensi desa with pagination",
data: [],
page: 1,
totalPages: 1,
total: 0,
};
}
}
export default potensiDesaFindMany
}

View File

@@ -25,6 +25,7 @@ export default async function findUnique(
where: { id },
include: {
image: true,
kategori: true
},
});

View File

@@ -14,7 +14,7 @@ const PotensiDesa = new Elysia({
body: t.Object({
name: t.String(),
deskripsi: t.String(),
kategori: t.String(),
kategoriId: t.String(),
imageId: t.String(),
content: t.String(),
}),
@@ -35,7 +35,7 @@ const PotensiDesa = new Elysia({
body: t.Object({
name: t.String(),
deskripsi: t.String(),
kategori: t.String(),
kategoriId: t.String(),
imageId: t.String(),
content: t.String(),
}),

View File

@@ -0,0 +1,27 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormCreate = {
nama: string;
}
export default async function kategoriPotensiCreate(context: Context) {
const body = (await context.body) as FormCreate;
try {
const result = await prisma.kategoriPotensi.create({
data: {
nama: body.nama
},
});
return {
success: true,
message: "Berhasil membuat kategori potensi",
data: result,
};
} catch (error) {
console.error("Error creating kategori potensi:", error);
throw new Error("Gagal membuat kategori potensi: " + (error as Error).message);
}
}

View File

@@ -0,0 +1,16 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function kategoriPotensiDelete(context: Context) {
const id = context.params.id as string;
await prisma.kategoriPotensi.delete({
where: { id },
});
return {
status: 200,
success: true,
message: "Success delete kategori potensi",
};
}

View File

@@ -0,0 +1,11 @@
import prisma from "@/lib/prisma";
export default async function kategoriPotensiFindMany() {
const data = await prisma.kategoriPotensi.findMany();
return {
success: true,
message: "Success get all kategori potensi",
data,
};
}

View File

@@ -0,0 +1,46 @@
import prisma from "@/lib/prisma";
export default async function kategoriBukuFindUnique(request: Request) {
const url = new URL(request.url);
const pathSegments = url.pathname.split('/');
const id = pathSegments[pathSegments.length - 1];
if (!id) {
return {
success: false,
message: "ID is required",
}
}
try {
if (typeof id !== 'string') {
return {
success: false,
message: "ID is required",
}
}
const data = await prisma.kategoriPotensi.findUnique({
where: { id },
});
if (!data) {
return {
success: false,
message: "Data not found",
}
}
return {
success: true,
message: "Success get kategori potensi",
data,
}
} catch (error) {
console.error("Find by ID error:", error);
return {
success: false,
message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'),
}
}
}

View File

@@ -0,0 +1,33 @@
import Elysia, { t } from "elysia";
import kategoriPotensiCreate from "./create";
import kategoriPotensiDelete from "./del";
import kategoriPotensiFindMany from "./findMany";
import kategoriPotensiFindUnique from "./findUnique";
import kategoriPotensiUpdate from "./updt";
const KategoriPotensi = new Elysia({
prefix: "/kategoripotensi",
tags: ["Desa / Potensi"],
})
.post("/create", kategoriPotensiCreate, {
body: t.Object({
nama: t.String(),
}),
})
.get("/findMany", kategoriPotensiFindMany)
.get("/:id", async (context) => {
const response = await kategoriPotensiFindUnique(
new Request(context.request)
);
return response;
})
.put("/:id", kategoriPotensiUpdate, {
body: t.Object({
nama: t.String(),
}),
})
.delete("/del/:id", kategoriPotensiDelete);
export default KategoriPotensi;

View File

@@ -0,0 +1,28 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdate = {
nama: string;
}
export default async function kategoriPotensiUpdate(context: Context) {
const body = (await context.body) as FormUpdate;
const id = context.params.id as string;
try {
const result = await prisma.kategoriPotensi.update({
where: { id },
data: {
nama: body.nama,
},
});
return {
success: true,
message: "Berhasil mengupdate kategori potensi",
data: result,
};
} catch (error) {
console.error("Error updating kategori potensi:", error);
throw new Error("Gagal mengupdate kategori potensi: " + (error as Error).message);
}
}

View File

@@ -9,7 +9,7 @@ type FormUpdate = Prisma.PotensiDesaGetPayload<{
id: true;
name: true;
deskripsi: true;
kategori: true;
kategoriId: true;
imageId: true;
content: true;
};
@@ -23,7 +23,7 @@ export default async function potensiDesaUpdate(context: Context) {
const {
name,
deskripsi,
kategori,
kategoriId,
imageId,
content,
} = body;
@@ -39,6 +39,7 @@ export default async function potensiDesaUpdate(context: Context) {
where: { id },
include: {
image: true,
kategori: true
}
});
@@ -69,7 +70,7 @@ export default async function potensiDesaUpdate(context: Context) {
data: {
name,
deskripsi,
kategori,
kategoriId,
imageId,
content,
},

View File

@@ -17,7 +17,7 @@ async function apbdesFindMany(context: Context) {
},
skip,
take: limit,
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu
}),
prisma.aPBDes.count({
where: { isActive: true },

View File

@@ -16,7 +16,7 @@ async function sdgsDesaFindMany(context: Context) {
},
skip,
take: limit,
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
orderBy: { jumlah: "desc" }, // opsional, kalau mau urut berdasarkan waktu
}),
prisma.sDGSDesa.count({
where: { isActive: true },

View File

@@ -11,7 +11,7 @@ const SDGSDesa = new Elysia({
})
// ✅ Find all
.get("/find-many", sdgsDesaFindMany)
.get("/findMany", sdgsDesaFindMany)
// ✅ Find by ID
.get("/:id", sdgsDesaFindUnique)

View File

@@ -1,42 +0,0 @@
import colors from '@/con/colors';
import { Stack, Container, List, ListItem, Box, Text, Image, Button, Center, Group } from '@mantine/core';
import React from 'react';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }} >
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Beda Biodata Diri
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text fz={{ base: "h4", md: "h2" }} pb={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{ base: "sm", md: "h3" }}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Fotocopy Ktp atau Kartu Keluarga</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Fotocopy dokumen bersangkutan yang terdapat perbedaan biodata diri misal : Sertifikat Tanah/Ijazah/Polis Asuransi dan lainnya.</ListItem>
</List>
<Box py={20}>
<Text fz={{ base: "h4", md: "h2" }} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-beda-biodatadiri-removebg-preview.png" alt='' />
</Center>
</Box>
<Group justify='center'>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Group>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,42 +0,0 @@
import colors from '@/con/colors';
import { Stack, Container, List, ListItem, Box, Text, Image, Button, Center, Group } from '@mantine/core';
import React from 'react';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }} >
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Belum Kawin
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text fz={{ base: "h4", md: "h2" }} pb={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{ base: "sm", md: "h3" }}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Fotocopy Ktp atau Kartu Keluarga</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Khusus bagi yang berstatus duda atau janda melampirkan fotocopy akta cerai atau dokumen pendukung lainnya</ListItem>
</List>
<Box py={20}>
<Text fz={{ base: "h4", md: "h2" }} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-beda-biodatadiri-removebg-preview.png" alt='' />
</Center>
</Box>
<Group justify='center'>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Group>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,42 +0,0 @@
import colors from '@/con/colors';
import { Box, Button, Center, Container, Image, List, ListItem, Stack, Text } from '@mantine/core';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}>
<BackButton />
</Box>
<Container w={{ base: "100%", md: "50%" }} >
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Domisili Organisasi
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0} >
<Text fz={{ base: "h4", md: "h2" }} pb={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{ base: "sm", md: "h3" }}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Fotocopy Surat Keterangan Terdaftar (SKT) organisasi atau Pengukuhan Kelompok</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Jika Pengajuan baru pembuatan SKT maka melengkapi Susunan Pengurus lengkap dengan Kop Organisasi</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Tanggal berdiri/Tahun berdiri/Sejak kapan berdirinya organisasi</ListItem>
</List>
<Box py={20}>
<Text fz={{ base: "h4", md: "h2" }} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-beda-biodatadiri-removebg-preview.png" alt='' />
</Center>
</Box>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,43 +0,0 @@
import colors from '@/con/colors';
import { Stack, Container, List, ListItem, Box, Text, Image, Button, Group, Center } from '@mantine/core';
import React from 'react';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }}>
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Kelahiran
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text fz={{ base: "h4", md: "h2" }} pb={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{ base: "sm", md: 'h3' }}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Fotocopy Surat lahir dari dokter/bidan (jika ada)</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Fotocopy Kartu Keluarga</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Fotocopy KTP 2 orang saksi</ListItem>
</List>
<Box py={20}>
<Text fz={{ base: "h4", md: "h2" }} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-kelahiran-removebg-preview.png" alt='' />
</Center>
</Box>
<Group justify='center'>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Group>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,41 +0,0 @@
import colors from '@/con/colors';
import { Stack, Container, List, ListItem, Box, Text, Image, Button, Center, Group } from '@mantine/core';
import React from 'react';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }} >
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Kelakuan Baik (Pengantar SKCK)
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text fz={{ base: "h4", md: "h2" }} pb={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{ base: "sm", md: 'h3' }}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Fotocopy Ktp atau Kartu Keluarga</ListItem>
</List>
<Box py={20}>
<Text fz={{ base: "h4", md: "h2" }} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-beda-biodatadiri-removebg-preview.png" alt='' />
</Center>
</Box>
<Group justify='center'>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Group>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,43 +0,0 @@
import colors from '@/con/colors';
import { Stack, Container, List, ListItem, Box, Text, Image, Button, Center, Group } from '@mantine/core';
import React from 'react';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }}>
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Kematian
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text fz={{ base: "h4", md: "h2" }} pb={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{ base: "sm", md: 'h3' }}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Fotocopy KTP atau Kartu keluarga</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Surat Kematian dari rumah sakit atau dokter (jika ada)</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Tanggal dan Waktu Kematian</ListItem>
</List>
<Box py={20}>
<Text fz={{ base: "h4", md: "h2" }} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-beda-biodatadiri-removebg-preview.png" alt='' />
</Center>
</Box>
<Group justify='center'>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Group>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,42 +0,0 @@
import colors from '@/con/colors';
import { Stack, Container, List, ListItem, Box, Text, Image, Button, Center, Group } from '@mantine/core';
import React from 'react';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }} >
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Penghasilan
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text fz={{base:"h4", md: "h2"}} pb={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{base:"sm", md: "h3"}}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{base:"sm", md: "h3"}}>Fotocopy KTP orang tua atau Fotocopy Kartu keluarga</ListItem>
<ListItem fz={{base:"sm", md: "h3"}}>Membuat Surat Pernyataan Penghasilan bermaterai (disertai jumlah penghasilan)</ListItem>
</List>
<Box py={20}>
<Text fz={{base:"h4", md: "h2"}} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-beda-biodatadiri-removebg-preview.png" alt='' />
</Center>
</Box>
<Group justify='center'>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Group>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,43 +0,0 @@
import colors from '@/con/colors';
import { Stack, Container, List, ListItem, Box, Text, Image, Button, Center, Group } from '@mantine/core';
import React from 'react';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }} >
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Tempat Usaha
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text fz={{ base: "h4", md: 'h2' }} pb={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{ base: "sm", md: "h3" }}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Fotocopy KTP atau Kartu keluarga</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Foto Lokasi dan Kegiatan Usaha di cetak dalam selembar kertas (diparaf dan stempel oleh Kelian Banjar Dinas)</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>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)</ListItem>
</List>
<Box py={20}>
<Text fz={{ base: "h4", md: 'h2' }} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-tempat-usaha-removebg-preview.png" alt='' />
</Center>
</Box>
<Group justify='center'>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Group>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,44 +0,0 @@
import colors from '@/con/colors';
import { Stack, Container, List, ListItem, Box, Text, Image, Button, Center, Group } from '@mantine/core';
import React from 'react';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }} >
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Tidak Mampu
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text fz={{ base: "h4", md: "h2" }} py={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{ base: "sm", md: "h3" }}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Fotocopy KTP/KIA atau Kartu Keluarga</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Fotocopy Kartu Indonesia Pintar/Kartu Perlindungan Sosial/Terdaftar dalam DTKS</ListItem>
<ListItem fz={{ base: "sm", md: "h3" }}>Jika tidak memiliki Kartu tersebut diatas diwajibkan membuat Surat Pernyataan Tidak Mampu
</ListItem>
</List>
<Box py={20}>
<Text fz={{ base: "h4", md: "h2" }} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-tidak-mampu-removebg-preview.png" alt='' />
</Center>
</Box>
<Group justify='center'>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Group>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,42 +0,0 @@
import colors from '@/con/colors';
import { Stack, Container, List, ListItem, Box, Text, Image, Button, Center, Group } from '@mantine/core';
import React from 'react';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }} >
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Usaha
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text fz={{ base: "h4", md: 'h2' }} pb={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{ base: "sm", md: 'h3' }}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Fotocopy KTP atau Kartu keluarga</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Foto Lokasi dan Kegiatan Usaha di cetak dalam selembar kertas (diparaf dan stempel oleh Kelian Banjar Dinas)</ListItem>
</List>
<Box py={20}>
<Text fz={{ base: "h4", md: 'h2' }} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-beda-biodatadiri-removebg-preview.png" alt='' />
</Center>
</Box>
<Group justify='center'>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Group>
</Stack>
</Box>
</Stack >
);
}
export default Page;

View File

@@ -1,41 +0,0 @@
import colors from '@/con/colors';
import { Stack, Container, List, ListItem, Box, Text, Image, Button, Center, Group } from '@mantine/core';
import React from 'react';
import BackButton from '../../_com/BackButto';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={"22"}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }} >
<Text fz={{base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem"}} ta={"center"} fw={"bold"}>
Surat Keterangan Yatim Piatu
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text fz={{ base: "h4", md: 'h2' }} pb={20}>
Persyaratan Dokumen:
</Text>
<List>
<ListItem fz={{ base: "sm", md: 'h3' }}>Pengantar Kelian Banjar Dinas di Wilayah Masing - masing</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Fotocopy Ktp, KIA atau Kartu Keluarga</ListItem>
</List>
<Box py={20}>
<Text fz={{ base: "h4", md: 'h2' }} py={20}>
Alur Pelayanan:
</Text>
<Center>
<Image src="/api/img/flowchart-suket-beda-biodatadiri-removebg-preview.png" alt='' />
</Center>
</Box>
<Group justify='center'>
<Button radius={"lg"} fz={'h4'} bg={colors['blue-button']}>Ajukan Permohonan</Button>
</Group>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -0,0 +1,106 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import { useParams } from 'next/navigation';
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
import { Box, Button, Center, Container, Group, Image, Skeleton, Stack, Text } from '@mantine/core';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../_com/BackButto';
interface LayananData {
id: string;
name: string;
deskripsi: string;
imageId: string;
image2Id: string;
image?: {
id: string;
link: string;
};
image2?: {
id: string;
link: string;
};
}
function Page() {
const params = useParams<{ id: string }>();
const id = Array.isArray(params.id) ? params.id[0] : params.id;
const state = useProxy(stateLayananDesa);
const [loading, setLoading] = useState(true);
const [data, setData] = useState<LayananData | null>(null);
useEffect(() => {
const loadData = async () => {
if (!id) return;
try {
setLoading(true);
await state.suratKeterangan.findUnique.load(id);
const result = state.suratKeterangan.findUnique.data as unknown as LayananData;
setData(result);
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
};
loadData();
}, [id]);
if (loading) {
return (
<Center>
<Skeleton height={500} />
</Center>
);
}
if (!data) {
return (
<Center>
<Text>Data tidak ditemukan</Text>
</Center>
);
}
return (
<Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
<Box px={{ base: "md", md: 100 }}>
<BackButton />
</Box>
<Container w={{ base: "100%", md: "50%" }}>
<Text
fz={{ base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem" }}
ta="center"
fw="bold"
>
{data.name}
</Text>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={0}>
<Text
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
fz={{ base: "h4", md: "h2" }}
pb={20}
/>
{data.image2?.link && (
<Center>
<Image src={data.image2.link} alt={data.name} />
</Center>
)}
<Group justify='center' mt="md">
<Button radius="lg" fz="h4" bg={colors['blue-button']}>
Ajukan Permohonan
</Button>
</Group>
</Stack>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,10 +0,0 @@
import { Stack } from "@mantine/core";
export default async function Page({ params }: { params: Promise<{ sub: string }> }) {
const { sub } = await params
return (
<Stack>
{sub}
</Stack>
)
}

View File

@@ -0,0 +1,65 @@
/* eslint-disable react-hooks/exhaustive-deps */
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
import { ActionIcon, Box, Divider, Flex, Skeleton, Text } from '@mantine/core';
import { IconBrandFacebook, IconBrandInstagram, IconBrandTwitter, IconBrandWhatsapp } from '@tabler/icons-react';
import React, { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
function PelayananPendudukNonPermanent() {
const state = useProxy(stateLayananDesa)
const [loading, setLoading] = useState(false)
useEffect(() => {
const loadData = async () => {
try {
setLoading(true)
await state.pelayananPendudukNonPermanen.findById.load('1')
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false)
}
}
loadData()
}, [])
const data = state.pelayananPendudukNonPermanen.findById.data
return (
<Box>
{loading ? (
<Skeleton h={500} />
) : (
<Box>
<Box py={15}>
<Text fz={{ base: "h4", md: "h3" }} fw={"bold"}>{data?.name}</Text>
</Box>
<Text pb={20} fz={{ base: "sm", md: 'h3' }} ta={"justify"} dangerouslySetInnerHTML={{__html: data?.deskripsi || ''}} />
<Divider color={colors["blue-button"]} />
<Flex justify={"space-between"} py={20}>
<Text fz={{ base: "sm", md: 'h3' }}>25 May 2021 . Darmasaba</Text>
<Box>
<Flex gap={"lg"}>
<ActionIcon variant='transparent'>
<IconBrandFacebook color={colors["blue-button"]} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandInstagram color={colors["blue-button"]} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandTwitter color={colors["blue-button"]} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandWhatsapp color={colors["blue-button"]} size={"30"} />
</ActionIcon>
</Flex>
</Box>
</Flex>
<Divider color={colors["blue-button"]} pb={50} />
</Box>
)}
</Box>
);
}
export default PelayananPendudukNonPermanent;

View File

@@ -0,0 +1,106 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import { Box, Button, Center, Group, Skeleton, Stepper, StepperCompleted, StepperStep, Text } from '@mantine/core';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
function PelayananPerizinanBerusaha() {
const state = useProxy(stateLayananDesa)
const [loading, setLoading] = useState(false)
const [active, setActive] = useState(1);
const nextStep = () => setActive((current) => (current < 6 ? current + 1 : current));
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
await state.pelayananPerizinanBerusaha.findById.load('1')
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
}
loadData()
}, [])
const data = state.pelayananPerizinanBerusaha.findById.data;
if (!data) {
return (
<Center>
<Text>Data tidak tersedia</Text>
</Center>
);
}
return (
<Box>
{loading ? (
<Center>
<Skeleton h={250} />
</Center>
) : (
<Box>
<Box py={15}>
<Text fz={{ base: "h4", md: 'h2' }} fw={"bold"}>Pelayanan Perizinan Berusaha Berbasis Risiko Melalui Sistem ONLINE SINGLE SUBMISSION (OSS)</Text>
</Box>
<Text
py={10}
ta={"justify"}
fz={{ base: "sm", md: 'h3' }}
dangerouslySetInnerHTML={{ __html: data.deskripsi || '' }}
/>
<Text py={10} fz={{ base: "sm", md: 'h3' }}>Proses pendaftaran NIB melalui OSS mencakup beberapa langkah umum, seperti:</Text>
<Box p={"xl"} w={{ base: "100%", md: "100%" }}>
<Stepper active={active} onStepClick={setActive} orientation="vertical"
styles={{
separator: {
marginLeft: 25
},
step: {
padding: '12px 0'
}
}}>
<StepperStep label="Langkah Pertama" description="Pendaftaran Akun">
Pendaftaran akun pada portal OSS
</StepperStep>
<StepperStep label="Langkah Kedua" description="Pengisian Data Perusahaan">
Mengisi informasi perusahaan, termasuk data pemegang saham, alamat perusahaan, dan lainnya
</StepperStep>
<StepperStep label="Langkah Ketiga" description="Pemilihan KBLI">
Memilih KBLI dengan jenis usaha yang akan didaftarkan
</StepperStep>
<StepperStep label="Langkah Keempat" description="Pengunggahan Dokumen">
Mengunggah dokumen-dokumen yang diperlukan, seperti akta pendirian perusahaan, surat izin usaha, dan dokumen lainnya sesuai dengan ketentuan yang berlaku
</StepperStep>
<StepperStep label="Langkah Kelima" description="Verifikasi dan Persetujuan">
Proses verifikasi dan persetujuan oleh instansi terkait
</StepperStep>
<StepperStep label="Langkah Keenam" description="Penerimaan NIB">
Jika proses sebelumnya berhasil, perusahaan akan menerima NIB sebagai identitas resmi usaha anda
</StepperStep>
<StepperCompleted>
Selesai, anda telah mengikuti proses pendaftaran NIB melalui OSS
</StepperCompleted>
</Stepper>
<Group justify="center" mt="xl">
<Button variant="default" onClick={prevStep}>Back</Button>
<Button onClick={nextStep}>Next step</Button>
</Group>
<Text py={35} ta={"justify"} fz={{ base: "sm", md: 'h3' }}>
Penting untuk diingat bahwa prosedur dan persyaratan dapat berubah
seiring waktu. Untuk informasi yang lebih akurat dan terkini, saya sarankan untuk mengunjungi situs
resmi OSS <a href="https://oss.go.id/" target="_blank" rel="noopener noreferrer">(https://oss.go.id/)</a> atau menghubungi instansi terkait di pemerintah Indonesia yang bertanggung jawab atas urusan perizinan usaha.
</Text>
</Box>
</Box>
)}
</Box>
);
}
export default PelayananPerizinanBerusaha;

View File

@@ -0,0 +1,97 @@
/* eslint-disable react-hooks/exhaustive-deps */
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
import { BackgroundImage, Box, Button, Center, Group, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
import { useRouter } from 'next/navigation';
import React, { useEffect, useMemo, useState } from 'react';
import { useProxy } from 'valtio/utils';
function PelayananSuratKeterangan({ search }: { search: string }) {
const [loading, setLoading] = useState(false);
const router = useRouter()
const state = useProxy(stateLayananDesa)
const filteredData = useMemo(() => {
if (!state.suratKeterangan.findMany.data) return [];
return state.suratKeterangan.findMany.data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword)
);
})
}, [state.suratKeterangan.findMany.data, search]);
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
await state.suratKeterangan.findMany.load()
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
}
loadData()
}, [])
return (
<Box pb={10}>
<Text fz={{ base: "h4", md: 'h2' }} fw={"bold"}>Pelayanan Surat Keterangan</Text>
<SimpleGrid
py={20}
cols={{
base: 1,
sm: 3
}}
>
{loading ? (
<Center>
<Skeleton h={250} />
</Center>
) : (
filteredData.map((v, k) => {
return (
<BackgroundImage
key={k}
src={v.image?.link || ''}
h={250}
radius={16}
pos={"relative"}
>
<Box
style={{
borderRadius: 16,
zIndex: 0
}}
pos={"absolute"}
w={"100%"}
h={"100%"}
bg={colors.trans.dark[2]}
/>
<Stack justify='space-between' h={"100%"} gap={0} p={"lg"} pos={"relative"}>
<Box p={"lg"}>
<Text
c={"white"}
size={"1.5rem"}
style={{
textAlign: "center",
}}>{v.name}</Text>
</Box>
<Group justify="center">
<Button px={20} radius={"100"} size="md" bg={colors["blue-button"]}
onClick={() => router.push(`/darmasaba/desa/layanan/${v.id}`)}>
Detail
</Button>
</Group>
</Stack>
</BackgroundImage>
)
})
)}
</SimpleGrid>
</Box>
);
}
export default PelayananSuratKeterangan;

View File

@@ -0,0 +1,67 @@
/* eslint-disable react-hooks/exhaustive-deps */
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import { Box, Flex, Skeleton, Text, Title } from '@mantine/core';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
interface ServiceItem {
name: string;
deskripsi: string;
link: string;
}
function PelayananTelunjukSaktiDesa() {
const state = useProxy(stateLayananDesa)
const [loading, setLoading] = useState(false)
useEffect(() => {
const loadData = async () => {
try {
setLoading(true)
await state.pelayananTelunjukSaktiDesa.findMany.load()
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false)
}
}
loadData()
}, [])
const data = (state.pelayananTelunjukSaktiDesa.findMany.data || []) as Array<{
name: string;
id: string;
deskripsi: string;
link: string;
items: ServiceItem[];
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
}>
return (
<Box>
<Title fz="h2" py={10} mb="md">Terwujudnya Layanan umum bertajuk Sistim administrasi Kependudukan Terintegrasi di Desa berbasi Elektronik, Smart dan Aman. Layanan Telunjuk Sakti Desa meliputi :</Title>
{loading ? (
<Skeleton h={500} />
) : (
data.map((v, k) => {
return (
<Box key={k}>
<Box py={10}>
<Flex gap={"3"} align={"center"}>
<Text fz={{ base: "h4", md: "h3" }} fw={"bold"}>{v.name}
</Text>
<Text span fz={{ base: "h4", md: "h3" }}>
<a href={v.link} target="_blank" rel="noopener noreferrer" style={{ color: '#228be6' }}>{v.deskripsi}</a>
</Text>
</Flex>
</Box>
</Box>
)
})
)}
</Box>
);
}
export default PelayananTelunjukSaktiDesa;

View File

@@ -1,85 +1,19 @@
'use client'
import colors from "@/con/colors";
import { ActionIcon, BackgroundImage, Box, Button, Container, Divider, Flex, Group, List, ListItem, SimpleGrid, Stack, Stepper, StepperCompleted, StepperStep, Text, TextInput } from "@mantine/core";
import { IconBrandFacebook, IconBrandInstagram, IconBrandTwitter, IconBrandWhatsapp, IconSearch } from "@tabler/icons-react";
import { useRouter } from "next/navigation";
import { Box, Container, Stack, Text, TextInput } from "@mantine/core";
import { IconSearch } from "@tabler/icons-react";
import { useState } from "react";
import BackButton from "./_com/BackButto";
import PelayananPerizinanBerusaha from "./_com/pelayananPerizinanBerusaha";
import PelayananSuratKeterangan from "./_com/pelayananSuratKeterangan";
import PelayananTelunjukSaktiDesa from "./_com/pelayananTelunjukSaktiDesa";
import PelayananPendudukNonPermanent from "./_com/pelayananPendudukNonPermanent";
const data = [
{
id: 1,
images: "/api/img/test.png",
name: "Surat Keterangan Domisili Organisasi",
link: "/darmasaba/desa/layanan/surat-keterangan-domisili"
},
{
id: 2,
images: "/api/img/test-3.jpeg",
name: "Surat Keterangan Penghasilan",
link: "/darmasaba/desa/layanan/surat-keterangan-penghasilan"
},
{
id: 3,
images: "/api/img/domisili.jpeg",
name: "Surat Keterangan Tidak Mampu",
link: "/darmasaba/desa/layanan/surat-keterangan-tidak-mampu"
},
{
id: 4,
images: "/api/img/kelahiran.jpeg",
name: "Surat Keterangan Kelahiran",
link: "/darmasaba/desa/layanan/surat-keterangan-kelahiran"
},
{
id: 5,
images: "/api/img/keterangan-usaha.jpeg",
name: "Surat Keterangan Usaha",
link: "/darmasaba/desa/layanan/surat-keterangan-usaha"
},
{
id: 6,
images: "/api/img/kematian.jpeg",
name: "Surat Keterangan Kematian",
link: "/darmasaba/desa/layanan/surat-keterangan-kematian"
},
{
id: 7,
images: "/api/img/tempatusaha.jpeg",
name: "Surat Keterangan Tempat Usaha",
link: "/darmasaba/desa/layanan/surat-keterangan-tempat-usaha"
},
{
id: 8,
images: "/api/img/belumkawin.jpeg",
name: "Surat Keterangan Belum Kawin",
link: "/darmasaba/desa/layanan/surat-keterangan-belum-kawin"
},
{
id: 9,
images: "/api/img/berkelakuan-baik.jpeg",
name: "Surat Keterangan Kelakuan Baik",
link: "/darmasaba/desa/layanan/surat-keterangan-kelakuan-baik"
},
{
id: 10,
images: "/api/img/biodata.jpeg",
name: "Surat Keterangan Beda Biodata Diri",
link: "/darmasaba/desa/layanan/surat-keterangan-beda-biodata-diri"
},
{
id: 11,
images: "/api/img/yatim.jpeg",
name: "Surat Keterangan Yatim Piatu",
link: "/darmasaba/desa/layanan/surat-keterangan-yatim-piatu"
}
]
export default function Page() {
const router = useRouter()
const [active, setActive] = useState(1);
const nextStep = () => setActive((current) => (current < 6 ? current + 1 : current));
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
const [search, setSearch] = useState("")
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
@@ -100,149 +34,20 @@ export default function Page() {
w={{ base: "70%", md: "50%" }}
placeholder="Cari Layanan"
leftSection={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
</Stack>
</Container>
<Box px={{ base: "md", md: 100 }}>
{/* Bagian Pelayanan Surat Keterangan */}
<Box pb={10}>
<Text fz={{ base: "h4", md: 'h2' }} fw={"bold"}>Pelayanan Surat Keterangan</Text>
</Box>
<SimpleGrid
py={20}
cols={{
base: 1,
sm: 3
}}
>
{data.map((v, k) => {
return (
<BackgroundImage
key={k}
src={v.images}
h={250}
radius={16}
pos={"relative"}
>
<Box
style={{
borderRadius: 16,
zIndex: 0
}}
pos={"absolute"}
w={"100%"}
h={"100%"}
bg={colors.trans.dark[2]}
/>
<Stack justify='space-between' h={"100%"} gap={0} p={"lg"} pos={"relative"}>
<Box p={"lg"}>
<Text
c={"white"}
size={"1.5rem"}
style={{
textAlign: "center",
}}>{v.name}</Text>
</Box>
<Group justify="center">
<Button px={20} radius={"100"} size="md" bg={colors["blue-button"]}
onClick={() => router.push(v.link)}>
Detail
</Button>
</Group>
</Stack>
</BackgroundImage>
)
})}
</SimpleGrid>
<PelayananSuratKeterangan search={search} />
{/* Bagian Pelayanan Perizinan Berusaha */}
<Box py={15}>
<Text fz={{ base: "h4", md: 'h2' }} fw={"bold"}>Pelayanan Perizinan Berusaha Berbasis Risiko Melalui Sistem ONLINE SINGLE SUBMISSION (OSS)</Text>
</Box>
<Text py={10} ta={"justify"} fz={{ base: "sm", md: 'h3' }}>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.</Text>
<Text py={10} fz={{ base: "sm", md: 'h3' }}>Proses pendaftaran NIB melalui OSS mencakup beberapa langkah umum, seperti:</Text>
<Box p={"xl"} w={{ base: "100%", md: "100%" }} >
<Stepper active={active} onStepClick={setActive} orientation="vertical"
styles={{
separator: {
marginLeft: 25
},
step: {
padding: '12px 0'
}
}}>
<StepperStep label="Langkah Pertama" description="Pendaftaran Akun">
Pendaftaran akun pada portal OSS
</StepperStep>
<StepperStep label="Langkah Kedua" description="Pengisian Data Perusahaan">
Mengisi informasi perusahaan, termasuk data pemegang saham, alamat perusahaan, dan lainnya
</StepperStep>
<StepperStep label="Langkah Ketiga" description="Pemilihan KBLI ">
Memilih KBLI dengan jenis usaha yang akan didaftarkan
</StepperStep>
<StepperStep label="Langkah Keempat" description="Pengunggahan Dokumen">
Mengunggah dokumen-dokumen yang diperlukan, seperti akta pendirian perusahaan, surat izin usaha, dan dokumen lainnya sesuai dengan ketentuan yang berlaku
</StepperStep>
<StepperStep label="Langkah Kelima" description="Verifikasi dan Persetujuan">
Proses verifikasi dan persetujuan oleh instansi terkait
</StepperStep>
<StepperStep label="Langkah Keenam" description="Penerimaan NIB">
Jika proses sebelumnya berhasil, perusahaan akan menerima NIB sebagai identitas resmi usaha anda
</StepperStep>
<StepperCompleted >
Selesai, anda telah mengikuti proses pendaftaran NIB melalui OSS
</StepperCompleted>
</Stepper>
<Group justify="center" mt="xl">
<Button variant="default" onClick={prevStep}>Back</Button>
<Button onClick={nextStep}>Next step</Button>
</Group>
<Text py={35} ta={"justify"} fz={{ base: "sm", md: 'h3' }}>Penting untuk diingat bahwa prosedur dan persyaratan dapat berubah
seiring waktu. Untuk informasi yang lebih akurat dan terkini, saya sarankan untuk mengunjungi situs
resmi OSS <a href={"https://oss.go.id/"}>(https://oss.go.id/)</a> atau menghubungi instansi terkait di pemerintah Indonesia yang bertanggung jawab atas urusan perizinan usaha.</Text>
</Box>
<PelayananPerizinanBerusaha/>
{/* Bagian Pelayanan Telunjuk Sakti Desa */}
<Box py={15}>
<Text fz={{base: "h4", md: "h3"}} fw={"bold"}>Pelayanan Telunjuk Sakti Desa</Text>
</Box>
<Text fz={{ base: "sm", md: 'h3' }} py={10}>Terwujudnya Layanan umum bertajuk Sistim administrasi Kependudukan Terintegrasi di Desa berbasi Elektronik, Smart dan Aman. layanan telunjuk sakti Desa meliputi :</Text>
<List type="ordered" pb={20}>
<ListItem fz={{ base: "sm", md: 'h3' }}>Telunjuk Sakti Desa Akta Kelahiran (Petunjuk Pengajuan pada link berikut : Download <a href="https://darmasaba.desa.id/storage/files/PERSYARATAN%20DAN%20ALUR%20PENGAJUAN%20AKTA%20KELAHIRAN_(dengan%20contoh%20Formulir).pdf">Akta Kelahiran</a>)</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Telunjuk Sakti Desa Akta Perkawinan (Petunjuk Pengajuan pada link berikut : Download <a href="https://darmasaba.desa.id/storage/files/PERSYARATAN%20DAN%20ALUR%20PENGAJUAN%20AKTA%20PERKAWINAN_(dengan%20contoh%20Formulir).pdf">Akta Perkawinan</a>)</ListItem>
<ListItem fz={{ base: "sm", md: 'h3' }}>Telunjuk Sakti Desa Akta Kematian (Petunjuk Pengajuan pada link berikut : Download <a href="https://darmasaba.desa.id/storage/files/PERSYARATAN%20DAN%20ALUR%20PENGAJUAN%20AKTA%20KEMATIAN_(dengan%20contoh%20Formulir).pdf">Akata Kematian</a>)</ListItem>
</List>
{/* Bagian Pelayanan Telunjuk Sakti Desa */}
<Box py={15}>
<Text fz={{base: "h4", md: "h3"}} fw={"bold"}>Pelayanan Penduduk Non-Permanent</Text>
</Box>
<Text pb={20} fz={{ base: "sm", md: 'h3' }} ta={"justify"}>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.</Text>
<Divider color={colors["blue-button"]} />
<Flex justify={"space-between"} py={20}>
<Text fz={{ base: "sm", md: 'h3' }}>25 May 2021 . Darmasaba</Text>
<Box>
<Flex gap={"lg"}>
<ActionIcon variant='transparent'>
<IconBrandFacebook color={colors["blue-button"]} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandInstagram color={colors["blue-button"]} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandTwitter color={colors["blue-button"]} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandWhatsapp color={colors["blue-button"]} size={"30"} />
</ActionIcon>
</Flex>
</Box>
</Flex>
<Divider color={colors["blue-button"]} pb={50} />
<PelayananTelunjukSaktiDesa/>
{/* Bagian Pelayanan Penduduk Non Permanent */}
<PelayananPendudukNonPermanent/>
</Box>
</Stack>
)

View File

@@ -0,0 +1,77 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import potensiDesaState from '@/app/admin/(dashboard)/_state/desa/potensi';
import colors from '@/con/colors';
import { Box, Center, Container, Image, Skeleton, Stack, Text } from '@mantine/core';
import { useParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../layanan/_com/BackButto';
function Page() {
const params = useParams<{ id: string }>();
const id = Array.isArray(params.id) ? params.id[0] : params.id;
const state = useProxy(potensiDesaState.potensiDesa)
const [loading, setLoading] = useState(true)
useEffect(() => {
const loadData = async () => {
if (!id) return;
try {
setLoading(true);
await state.findUnique.load(id);
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
}
loadData()
}, [id])
if (loading) {
return (
<Center>
<Skeleton height={500} />
</Center>
);
}
if (!state.findUnique.data) {
return (
<Center>
<Text>Data tidak ditemukan</Text>
</Center>
);
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"} px={{ base: "md", md: 0 }}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }} >
<Box pb={20}>
<Text ta={"center"} fz={"3.4rem"} c={colors["blue-button"]} fw={"bold"}>
{state.findUnique.data?.name}
</Text>
<Text
ta={"center"}
fw={"bold"}
fz={"1.5rem"}
>
Informasi dan Pelayanan Administrasi Digital
</Text>
</Box>
<Image src={state.findUnique.data?.image?.link || ''} alt='' w={"100%"} />
</Container>
<Box px={{ base: "md", md: 100 }}>
<Text py={20} fz={{ base: "sm", md: "lg" }} ta={"justify"}>
{state.findUnique.data?.deskripsi || ''}
</Text>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,105 +1,36 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { BackgroundImage, Box, Button, Flex, Group, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
import { BackgroundImage, Box, Button, Center, Flex, Group, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
import { useTransitionRouter } from 'next-view-transitions';
import BackButton from '../layanan/_com/BackButto';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import potensiDesaState from '@/app/admin/(dashboard)/_state/desa/potensi';
const datamap = [
{
id: 1,
images: "/api/img/tps.png",
name: "TPS3R Pudak Mesari",
kategori: "Lingkungan",
link: "/darmasaba/desa/potensi/tps3r-pudak-mesari",
},
{
id: 2,
images: "/api/img/ack.png",
name: "Bumdes Pudak Mesari",
kategori: "Ekonomi",
link: "/darmasaba/desa/potensi/bumdes-pudak-mesari",
},
{
id: 3,
images: "/api/img/taman-beji.jpg",
name: "Taman Beji Cengana",
kategori: "Wisata",
link: "/darmasaba/desa/potensi/taman-beji-cengana",
},
{
id: 4,
images: "/api/img/waterpark.png",
name: "Gumuh Sari Water Park",
kategori: "Wisata",
link: "/darmasaba/desa/potensi/gumuh-sari-water-park",
},
{
id: 5,
images: "/api/img/pertanian.jpg",
name: "Pertanian",
kategori: "Ekonomi",
link: "/darmasaba/desa/potensi/pertanian"
},
{
id: 6,
images: "/api/img/warungumkm.jpg",
name: "Kawasan Kuliner",
kategori: "Ekonomi",
link: "/darmasaba/desa/potensi/kawasan-kuliner"
},
{
id: 7,
images: "/api/img/ikm.png",
name: "IKM Berbasis Pengolahan Pangan",
kategori: "Ekonomi",
link: "/darmasaba/desa/potensi/ikm-berbasis-pengolahan-pangan"
},
{
id: 8,
images: "/api/img/genteng.jpeg",
name: "Genteng",
kategori: "Ekonomi",
link: "/darmasaba/desa/potensi/genteng"
},
{
id: 9,
images: "/api/img/peternakanlele.jpg",
name: "Peternakan Ikan Lele",
kategori: "Ekonomi",
link: "/darmasaba/desa/potensi/peternakan-ikan-lele"
},
{
id: 10,
images: "/api/img/jogging.jpg",
name: "Jogging Track Tegeh Aban, Karang Gadon dan Munduk Uma Desa",
kategori: "Lingkungan",
link: "/darmasaba/desa/potensi/jogging-track-tegeh-aban-karang-gadon-dan-munduk-uma-desa"
},
{
id: 11,
images: "/api/img/damtanahputih.jpeg",
name: "Dam Tanah Putih",
kategori: "Wisata",
link: "/darmasaba/desa/potensi/dam-tanah-putih"
},
{
id: 12,
images: "/api/img/umkm.jpeg",
name: "UMKM",
kategori: "Ekonomi",
link: "/darmasaba/desa/potensi/umkm"
},
{
id: 13,
images: "/api/img/potong-daging.png",
name: "Pemotongan Daging",
kategori: "Ekonomi",
link: "/darmasaba/desa/potensi/pemotongan-daging"
},
]
function Page() {
const router = useRouter()
const router = useTransitionRouter()
const [loading, setLoading] = useState(false)
const state = useProxy(potensiDesaState)
useEffect(()=> {
state.kategoriPotensi.findMany.load()
const loadData = async () => {
try {
setLoading(true)
await state.potensiDesa.findMany.load()
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false)
}
}
loadData()
}, [])
const data = state.potensiDesa.findMany.data
// const kategoriData = state.kategoriPotensi.findMany.data
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"} >
<Box px={{ base: "md", md: 100 }}>
@@ -118,13 +49,17 @@ function Page() {
</Text>
</Box>
<Paper radius={"md"} px={"xl"} py={5} bg={colors["blue-button"]} >
<Flex justify={"space-evenly"} align={"center"} gap={"xl"}>
<Flex justify={"space-between"} align={"center"} gap={"xl"}>
<Box>
<Text ta={"center"} fz={"h2"} fw={"bold"} c={"white"}>13</Text>
<Text ta={"center"} fz={"h2"} fw={"bold"} c={"white"}>
{data?.filter(item => item.kategori?.nama.toLowerCase() !== 'wisata' ).length || 0}
</Text>
<Text ta={"center"} fz={"sm"} c={"white"}>Potensi</Text>
</Box>
<Box>
<Text ta={"center"} fz={"h2"} fw={"bold"} c={"white"}>5</Text>
<Text ta={"center"} fz={"h2"} fw={"bold"} c={"white"}>
{data?.filter(item => item.kategori?.nama.toLowerCase() === 'wisata' ).length || 0}
</Text>
<Text ta={"center"} fz={"sm"} c={"white"}>Destinasi Wisata</Text>
</Box>
</Flex>
@@ -140,11 +75,16 @@ function Page() {
sm: 3
}}
>
{datamap.map((v, k) => {
{loading ? (
<Center>
<Skeleton h={250} />
</Center>
) : (
data?.map((v, k) => {
return (
<BackgroundImage
key={k}
src={v.images}
src={v.image?.link || ''}
h={350}
radius={16}
pos={"relative"}
@@ -162,7 +102,7 @@ function Page() {
<Stack justify='space-between' h={"100%"} gap={0} p={"lg"} pos={"relative"}>
<Group>
<Paper radius={"lg"} py={7} px={10}>
<Text>{v.kategori}</Text>
<Text>{v.kategori?.nama}</Text>
</Paper>
</Group>
<Box p={"lg"}>
@@ -176,14 +116,15 @@ function Page() {
</Box>
<Group justify="center">
<Button px={20} radius={"100"} size="md" bg={colors["blue-button"]}
onClick={() => router.push(v.link)}>
onClick={() => router.push(`/darmasaba/desa/potensi/${v.id}`)}>
Detail
</Button>
</Group>
</Stack>
</BackgroundImage>
)
})}
})
)}
</SimpleGrid>
</Box>

View File

@@ -1,47 +1,33 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { ActionIcon, BackgroundImage, Box, Container, Flex, Group, SimpleGrid, Stack, Text } from '@mantine/core';
import { ActionIcon, BackgroundImage, Box, Center, Container, Flex, Group, SimpleGrid, Stack, Text } from '@mantine/core';
import { IconDownload } from '@tabler/icons-react';
import BackButton from '../../(pages)/desa/layanan/_com/BackButto';
import apbdes from '@/app/admin/(dashboard)/_state/landing-page/apbdes';
import { useProxy } from 'valtio/utils';
import { useEffect, useState } from 'react';
import { Link } from 'next-view-transitions';
const data = [
{
id: 1,
title: "Pendapatan",
image: "/api/img/pendapatan.jpeg",
value: "Rp 495M"
},
{
id: 2,
title: "Belanja",
image: "/api/img/belanja.jpeg",
value: "Rp 395M"
},
{
id: 3,
title: "Pembiayaan",
image: "/api/img/pembiayaan.jpeg",
value: "Rp 295M"
},
{
id: 4,
title: "APBDesa 2025",
image: "/api/img/apb-des.jpg",
value: "Rp 500M"
},
{
id: 5,
title: "APBDesa 2024",
image: "/api/img/apb-des.jpg",
value: "Rp 450M"
},
{
id: 6,
title: "APBDesa 2023",
image: "/api/img/apb-des.jpg",
value: "Rp 400M"
},
]
function Page() {
const state = useProxy(apbdes);
const [loading, setLoading] = useState(false);
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
await state.findMany.load();
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
}
loadData();
}, [])
const data = state.findMany.data || [];
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={22}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
@@ -65,53 +51,59 @@ function Page() {
sm: 3,
}}
>
{data.map((v, k) => {
return (
<BackgroundImage
key={k}
src={v.image}
h={350}
radius={16}
pos={"relative"}
>
<Box
style={{
borderRadius: 16,
zIndex: 0
}}
pos={"absolute"}
w={"100%"}
h={"100%"}
bg={colors.trans.dark[2]}
/>
<Stack justify='space-between' h={"100%"} gap={0} p={"lg"} pos={"relative"}>
<Box p={"lg"}>
{loading ? (
<Center>
<Text fz={"2.4rem"}>Memuat Data...</Text>
</Center>
) : (
data.map((v, k) => {
return (
<BackgroundImage
key={k}
src={v.image?.link || ''}
h={350}
radius={16}
pos={"relative"}
>
<Box
style={{
borderRadius: 16,
zIndex: 0
}}
pos={"absolute"}
w={"100%"}
h={"100%"}
bg={colors.trans.dark[2]}
/>
<Stack justify='space-between' h={"100%"} gap={0} p={"lg"} pos={"relative"}>
<Box p={"lg"}>
<Text
c={"white"}
size={"1.5rem"}
style={{
textAlign: "center",
}}>{v.name}</Text>
</Box>
<Text
fw={"bold"}
c={"white"}
size={"1.5rem"}
size={"3.5rem"}
style={{
textAlign: "center",
}}>{v.title}</Text>
</Box>
<Text
fw={"bold"}
c={"white"}
size={"3.5rem"}
style={{
textAlign: "center",
}}>{v.value}</Text>
<Group justify="center">
<ActionIcon px={70} py={20} radius={"xl"} size="md" bg={colors["blue-button"]}>
<Flex gap={"md"}>
<IconDownload size={20} />
<Text fz={"sm"} c={"white"}>Download</Text>
</Flex>
</ActionIcon>
</Group>
</Stack>
</BackgroundImage>
)
})}
}}>{v.jumlah}</Text>
<Group justify="center">
<ActionIcon px={70} py={20} radius={"xl"} size="md" bg={colors["blue-button"]} component={Link} href={v.file?.link || ''}>
<Flex gap={"md"}>
<IconDownload size={20} />
<Text fz={"sm"} c={"white"}>Download</Text>
</Flex>
</ActionIcon>
</Group>
</Stack>
</BackgroundImage>
)
})
)}
</SimpleGrid>
</Stack>
);

View File

@@ -1,73 +0,0 @@
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Box, Button, Container, Flex, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
import { IconClipboardText } from '@tabler/icons-react';
const data = [
{
judul: "PENGUATAN PENGAWASAN",
deskripsi: "5.1 ADANYA BUDAYA LOKAL/HUKUM ADAT YANG MENDORONG UPAYA PENCEGAHAN TINDAK PIDANA KORUPSI",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN PENGAWASAN",
deskripsi: "5.2 ADANYA TOKOH MASYARAKAT, TOKOH AGAMA, TOKOH ADAT, TOKOH PEMUDA, DAN KAUM PEREMPUAN YANG MENDORONG UPAYA PENCEGAHAN TINDAK PIDANA KORUPSI",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
}
]
function Lokal() {
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={22}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"} ta={"center"}>
Kearifan Lokal
</Text>
<Text
py={10}
ta={"justify"}
>
Sebagai bagian dari Program Desa Anti Korupsi, Desa Darmasaba menerapkan kearifan lokal sebagai strategi dalam menciptakan pemerintahan desa yang bersih, transparan, dan berintegritas. Kearifan lokal ini menjadi landasan moral dan budaya yang memperkuat nilai-nilai kejujuran, gotong royong, serta kepedulian sosial dalam kehidupan masyarakat. Adapun beberapa Kearifan Lokal :
</Text>
</Stack>
</Container>
<SimpleGrid
px={{ base: "md", md: 100 }}
cols={{
base: 1,
sm: 2,
}}>
{data.map((v, k) => {
return (
<Box
key={k}
>
<Paper p={"lg"} >
<Stack h="100%">
<Text fz={"lg"} ta={"center"} c={colors["blue-button"]}>{v.judul}</Text>
<Flex direction={{ base: "column", md: "row" }} align="center" justify="center" style={{ flex: 1 }}>
<Box mb={{ base: 10, md: 0 }}>
{v.icon}
</Box>
<Stack px={{ base: 0, md: 20 }} gap={10} style={{ flex: 1 }}>
<Text fz={"sm"} ta={"justify"}>{v.deskripsi}</Text>
<Button bg={colors["blue-button"]} radius={"lg"} w="fit-content">Download</Button>
</Stack>
</Flex>
</Stack>
</Paper>
</Box>
);
})}
</SimpleGrid >
</Stack >
);
}
export default Lokal;

View File

@@ -1,88 +0,0 @@
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Box, Button, Container, Flex, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
import { IconClipboardText } from '@tabler/icons-react';
const data = [
{
judul: "PENGUATAN PENGAWASAN",
deskripsi: "3.1 ADANYA LAYANAN PENGADUAN BAGI MASYARAKAT",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN PENGAWASAN",
deskripsi: "3.2 ADANYA SURVEY KEPUASAN MASYARAKAT (SKM) TERHADAP LAYANAN PEMERINTAH DESA",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN PENGAWASAN",
deskripsi: "3.3 ADANYA KETERBUKAAN AKSES MASYARAKAT TERHADAP INFORMASI LAYANAN PEMERINTAH DESA (KESEHATAN, PENDIDIKAN, SOSIAL, LINGKUNGAN, TRANTIBUMLINMAS, PEKERJAAN UMUM) PEMBANGUNAN, KEPENDUDUKAN, KEUANGAN, DAN PELAYANAN LAINNYA",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN PENGAWASAN",
deskripsi: "3.4 ADANYA MEDIA INFORMASI TENTANG APBDES DI BALAI DESA DAN/ATAU TEMPAT LAIN YANG MUDAH DIAKSES OLEH MASYARAKAT",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN PENGAWASAN",
deskripsi: "3.5 ADANYA MAKLUMAT PELAYANAN",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
]
function Pelayanan() {
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={22}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"} ta={"center"}>
Penguatan Kualitas Pelayanan Publik
</Text>
<Text
py={10}
ta={"justify"}
>
Sebagai bagian dari Program Desa Anti Korupsi, Desa Darmasaba berkomitmen untuk meningkatkan kualitas pelayanan publik agar lebih transparan, cepat, dan bebas dari pungutan liar. Penguatan ini bertujuan untuk memastikan masyarakat mendapatkan pelayanan yang profesional, akuntabel, dan sesuai dengan standar yang telah ditetapkan. Adapun beberapa Penguatan Kualitas Pelayanan Publik :
</Text>
</Stack>
</Container>
<SimpleGrid
px={{ base: "md", md: 100 }}
cols={{
base: 1,
sm: 2,
}}>
{data.map((v, k) => {
return (
<Box
key={k}
>
<Paper p={"lg"} h="100%">
<Stack h="100%">
<Text fz={"lg"} ta={"center"} c={colors["blue-button"]}>{v.judul}</Text>
<Flex direction={{ base: "column", md: "row" }} align="center" justify="center" style={{ flex: 1 }}>
<Box mb={{ base: 10, md: 0 }}>
{v.icon}
</Box>
<Stack px={{ base: 0, md: 20 }} gap={10} style={{ flex: 1 }}>
<Text fz={"sm"} ta={"justify"}>{v.deskripsi}</Text>
<Button bg={colors["blue-button"]} radius={"lg"} w="fit-content">Download</Button>
</Stack>
</Flex>
</Stack>
</Paper>
</Box>
);
})}
</SimpleGrid >
</Stack >
);
}
export default Pelayanan;

View File

@@ -1,75 +0,0 @@
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Box, Button, Container, Flex, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
import { IconClipboardText } from '@tabler/icons-react';
const data = [
{
judul: "PENGUATAN TATA LAKSANA",
deskripsi: "4.1 ADANYA PARTISIPASI DAN KETERLIBATAN MASYARAKAT DALAM PENYUSUNAN RKP DESA",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN TATA LAKSANA",
deskripsi: "4.2 ADANYA KESADARAN MASYARAKAT DALAM MENCEGAH TERJADINYA PRAKTIK GRATIFIKASI, SUAP DAN KONFLIK KEPENTINGAN",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN TATA LAKSANA",
deskripsi: "4.3 ADANYA KETERLIBATAN LEMBAGA KEMASYARAKATAN DALAM PELAKSANAAN PEMBANGUNAN DESA",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
]
function Partisipasi() {
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={22}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"} ta={"center"}>
Penguatan Tata Laksana
</Text>
<Text
py={10}
ta={"justify"}
>
Sebagai bagian dari Program Desa Anti Korupsi, Desa Darmasaba menekankan penguatan partisipasi masyarakat agar warga dapat ikut serta dalam pengawasan, pengambilan keputusan, dan pelaksanaan pembangunan desa yang transparan dan akuntabel. Adapun beberapa Penguatan Partisipasi Masyarakat :
</Text>
</Stack>
</Container>
<SimpleGrid
px={{ base: "md", md: 100 }}
cols={{
base: 1,
sm: 2,
}}>
{data.map((v, k) => {
return (
<Box
key={k}
>
<Paper p={"lg"} >
<Stack h="100%">
<Text fz={"lg"} ta={"center"} c={colors["blue-button"]}>{v.judul}</Text>
<Flex direction={{ base: "column", md: "row" }} align="center" justify="center" style={{ flex: 1 }}>
<Box mb={{ base: 10, md: 0 }}>
{v.icon}
</Box>
<Stack px={{ base: 0, md: 20 }} gap={10} style={{ flex: 1 }}>
<Text fz={"sm"} ta={"justify"}>{v.deskripsi}</Text>
<Button bg={colors["blue-button"]} radius={"lg"} w="fit-content">Download</Button>
</Stack>
</Flex>
</Stack>
</Paper>
</Box>
);
})}
</SimpleGrid >
</Stack >
);
}
export default Partisipasi;

View File

@@ -1,78 +0,0 @@
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Box, Button, Container, Flex, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
import { IconClipboardText } from '@tabler/icons-react';
const data = [
{
judul: "PENGUATAN PENGAWASAN",
deskripsi: "2.1 ADANYA KEGIATAN PENGAWASAN DAN EVALUASI KINERJA PERANGKAT DESA",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN PENGAWASAN",
deskripsi: "2.2 ADANYA TINDAK LANJUT HASIL PEMBINAAN, PETUNJUK, ARAH, PENGAWASAN, DAN PEMERIKSAAN DARI PEMERINTAH PUSAT/DAERAH",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN PENGAWASAN",
deskripsi: "2.3 TIDAK ADANYA APARATUR DESA DALAM 3(TIGA) TAHUN TERAKHIR YANG TERJERAT TINDAKAN PIDANA KORUPSI",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
]
function Pengawasan() {
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={22}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"} ta={"center"}>
Penguatan Pengawasan
</Text>
<Text
py={10}
ta={"justify"}
>
Sebagai bagian dari Program Desa Anti Korupsi, Desa Darmasaba menerapkan penguatan pengawasan untuk memastikan transparansi dan akuntabilitas dalam pengelolaan pemerintahan desa. Sistem pengawasan ini melibatkan berbagai pihak, termasuk aparat desa, masyarakat, dan lembaga terkait, agar tidak ada celah bagi praktik korupsi. Adapun beberapa Penguatan Pengawasan :
</Text>
</Stack>
</Container>
<SimpleGrid
px={{ base: "md", md: 100 }}
cols={{
base: 1,
sm: 2,
}}>
{data.map((v, k) => {
return (
<Box
key={k}
>
<Paper p={"lg"} >
<Stack h="100%">
<Text fz={"lg"} ta={"center"} c={colors["blue-button"]}>{v.judul}</Text>
<Flex direction={{ base: "column", md: "row" }} align="center" justify="center" style={{ flex: 1 }}>
<Box mb={{ base: 10, md: 0 }}>
{v.icon}
</Box>
<Stack px={{ base: 0, md: 20 }} gap={10} style={{ flex: 1 }}>
<Text fz={"sm"} ta={"justify"}>{v.deskripsi}</Text>
<Button bg={colors["blue-button"]} radius={"lg"} w="fit-content">Download</Button>
</Stack>
</Flex>
</Stack>
</Paper>
</Box>
);
})}
</SimpleGrid >
</Stack >
);
}
export default Pengawasan;

View File

@@ -1,90 +0,0 @@
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Box, Button, Container, Flex, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
import { IconClipboardText } from '@tabler/icons-react';
const data = [
{
judul: "PENGUATAN TATA LAKSANA",
deskripsi: "1.1 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PERENCANAAN, PELAKSANAAN, PENATAUSAHAAN DAN PERTANGGUNG JAWABAN APBDES BESERTA IMPLEMENTASINYA",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN TATA LAKSANA",
deskripsi: "1.2 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP MENGENAI MEKANISME EVALUASI KINERJA PERANGKAT DESA",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN TATA LAKSANA",
deskripsi: "1.3 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PENGENDALIAN GRATIFIKASI, SUAP DAN KONFLIK KEPENTINGAN",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN TATA LAKSANA",
deskripsi: "1.4 PERJANJIAN KERJA SAMA ANTARA PELAKSANA KEGIATAN ANGGARAN DENGAN PIHAK PENYEDIA, DAN TELAH MELALUI PROSES PENGADAAN BARANG/JASA DI DESA",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN TATA LAKSANA",
deskripsi: "1.5 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PAKTA INTEGRITAS DAN SEJENISNYA",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
{
judul: "PENGUATAN TATA LAKSANA",
deskripsi: "1.6 ADANYA PERDES/KEPUTUSAN KEPALA DESA/SOP TENTANG PERENCANAAN, PELAKSANAAN, PENATAUSAHAAN DAN PERTANGGUNG JAWABAN APBDES BESERTA IMPLEMENTASINYA",
icon: <IconClipboardText size={100} color={colors["blue-button"]} />,
},
]
function Tatalaksana() {
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={22}>
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"} ta={"center"}>
Penguatan Tata Laksana
</Text>
<Text
py={10}
ta={"justify"}
>
Sebagai bagian dari Program Desa Anti Korupsi, Desa Darmasaba menerapkan penguatan tata laksana untuk memastikan transparansi, akuntabilitas, dan efisiensi dalam pemerintahan desa. Adapun beberapa Penguatan Tata Laksana :
</Text>
</Stack>
</Container>
<SimpleGrid
px={{ base: "md", md: 100 }}
cols={{
base: 1,
sm: 2,
}}>
{data.map((v, k) => {
return (
<Box
key={k}
>
<Paper p={"lg"} >
<Stack h="100%">
<Text fz={"lg"} ta={"center"} c={colors["blue-button"]}>{v.judul}</Text>
<Flex direction={{ base: "column", md: "row" }} align="center" justify="center" style={{ flex: 1 }}>
<Box mb={{ base: 10, md: 0 }}>
{v.icon}
</Box>
<Stack px={{ base: 0, md: 20 }} gap={10} style={{ flex: 1 }}>
<Text fz={"sm"} ta={"justify"}>{v.deskripsi}</Text>
<Button bg={colors["blue-button"]} radius={"lg"} w="fit-content">Download</Button>
</Stack>
</Flex>
</Stack>
</Paper>
</Box>
);
})}
</SimpleGrid >
</Stack >
);
}
export default Tatalaksana;

View File

@@ -0,0 +1,149 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
import korupsiState from '@/app/admin/(dashboard)/_state/landing-page/desa-anti-korupsi';
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import React, { useEffect, useState } from 'react';
import { Box, Button, Container, Flex, Paper, SimpleGrid, Stack, Text, ActionIcon } from '@mantine/core';
import { IconFile } from '@tabler/icons-react';
import { useProxy } from 'valtio/utils';
function Lokal() {
const [selectedKategori, setSelectedKategori] = useState<string>('PENGUATAN TATA LAKSANA');
const [loading, setLoading] = useState(true);
const state = useProxy(korupsiState);
// Load data on component mount
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
await state.desaAntikorupsi.findMany.load(1, 100); // Load first 100 items
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
};
loadData();
}, []);
// Get data from state
const data = state.desaAntikorupsi.findMany.data || [];
// Debug: Log the complete data structure
console.log('Complete data:', JSON.parse(JSON.stringify(data)));
// Get unique categories
const categories = [...new Set(
data
.filter(item => item.kategori?.name) // Only include items with a category name
.map(item => item.kategori.name)
)];
// Filter data based on selected category
const filteredData = selectedKategori === 'PENGUATAN TATA LAKSANA'
? data
: data.filter(item => item.kategori?.name === selectedKategori);
// Debug: Log filtered data
console.log('Filtered data:', JSON.parse(JSON.stringify(filteredData)));
return (
<Stack pos="relative" bg={colors.Bg} py="xl" gap={22}>
<Box px={{ base: "md", md: 100 }}>
<BackButton />
</Box>
<Container w={{ base: "100%", md: "80%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"}>
Desa Anti Korupsi
</Text>
<Text
py={10}
ta={"justify"}
>
Desa antikorupsi mendorong pemerintahan jujur dan transparan. Keuangan desa dikelola terbuka dengan melibatkan warga mengawasi anggaran, sehingga digunakan tepat sasaran sesuai kebutuhan. Adapun beberapa jenis tata penguatan :
</Text>
</Stack>
</Container>
<Container size="lg" px={{ base: "md", md: "xl" }}>
{/* Category Filter Buttons */}
<Flex gap="md" justify="center" mb="xl" wrap="wrap">
{categories.map((kategori) => (
<Button
color={selectedKategori === kategori ? colors['blue-button'] : 'gray'}
key={kategori}
variant={selectedKategori === kategori ? 'filled' : 'outline'}
onClick={() => setSelectedKategori(kategori)}
size="sm"
>
{kategori}
</Button>
))}
</Flex>
{/* Loading State */}
{loading ? (
<Text ta="center">Memuat data...</Text>
) : (
<SimpleGrid
cols={{ base: 1, sm: 2, lg: 3 }}
spacing="xl"
verticalSpacing="xl"
>
{filteredData.map((item) => {
console.log('Item data:', item);
console.log('All item properties:', Object.keys(item).map(key => `${key}: ${item[key]}`).join(', '));
const handleDownload = async (e: React.MouseEvent) => {
e.stopPropagation();
if (!item?.file?.link) return;
try {
const fileUrl = item.file.link.startsWith('http')
? item.file.link
: `${window.location.origin}${item.file.link.startsWith('/') ? '' : '/'}${item.file.link}`;
window.open(fileUrl, '_blank');
} catch (error) {
console.error('Error opening file:', error);
}
};
return (
<Paper key={item.id} p="lg" shadow="sm" radius="md" withBorder>
<Stack h="100%" justify="space-between">
<div>
<Text fz="lg" fw={600} mb="sm" c={colors["blue-button"]}>
{item.name}
</Text>
</div>
{item?.file && (
<ActionIcon
variant="filled"
color={colors["blue-button"]}
size="lg"
onClick={handleDownload}
style={{ marginTop: 10 }}
title="Unduh File"
>
<IconFile size={20} />
</ActionIcon>
)}
</Stack>
</Paper>
);
})}
</SimpleGrid>
)}
</Container>
</Stack>
)
}
export default Lokal;

View File

@@ -69,7 +69,7 @@ function Page() {
<Box>
{v.icon}
</Box>
<Button fz={"h4"} color={colors["blue-button"]} onClick={() => router.push(v.link)}>Detail</Button>
<Button fz={"h4"} color={colors["blue-button"]} onClick={() => router.push("/darmasaba/desa-anti-korupsi/detail")}>Detail</Button>
</Stack>
</Box>
</Paper>

View File

@@ -1,96 +0,0 @@
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Stack, Container, Text, Image, ActionIcon, Box, Divider, Flex, Group } from '@mantine/core';
import { IconBrandFacebook, IconBrandInstagram, IconBrandTwitter, IconBrandWhatsapp } from '@tabler/icons-react';
import React from 'react';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={22}>
<Group px={{ base: "md", md: 100 }}>
<BackButton />
</Group>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"}>
Juara 2
</Text>
<Text
ta={"center"}
fw={"bold"}
>
Duta Invenstasi Kabupaten Badung 2024
</Text>
<Image py={20} src={"/api/img/penghargaan-3.png"} alt='' />
</Stack>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Text
pb={20}
ta={"center"}
fw={"bold"}
>
Desa Dramasaba Raih Juara 2 dalam Duta Invenstasi Kabupaten Badung 2024
</Text>
<Text
ta={"justify"}
>
Desa Dramasaba kembali menunjukkan eksistensinya dalam memajukan potensi desa dengan meraih Juara 2 dalam ajang Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024. Prestasi ini menjadi bukti nyata bahwa Desa Dramasaba memiliki daya tarik serta peluang investasi yang menjanjikan bagi para investor maupun masyarakat yang ingin berkontribusi dalam pengembangan desa.
</Text>
<Text
py={5}
ta={"justify"}
>
Dalam video pendek yang diikutsertakan dalam lomba ini, Desa Dramasaba menampilkan berbagai potensi unggulan yang dimiliki, mulai dari sektor pertanian, pariwisata berbasis budaya, hingga ekonomi kreatif yang sedang berkembang pesat. Dengan pengemasan yang menarik dan penyampaian informasi yang jelas, video tersebut berhasil memikat perhatian dewan juri serta menjadi inspirasi bagi desa-desa lain dalam mengembangkan potensi wilayahnya masing-masing.
</Text>
<Text
ta={"justify"}
>
Keberhasilan ini tidak lepas dari kerja keras seluruh tim kreatif, pemerintah desa, serta masyarakat yang turut berpartisipasi dalam produksi video. Kepala Desa Dramasaba, [Nama Kepala Desa], mengungkapkan rasa syukur dan bangganya atas pencapaian ini. Menurutnya, kemenangan ini bukan sekadar penghargaan, tetapi juga menjadi motivasi untuk terus mengembangkan desa agar semakin dikenal luas dan menarik lebih banyak peluang investasi.
</Text>
<Text
py={5}
ta={"justify"}
>
Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024 merupakan ajang yang diadakan oleh pemerintah daerah dengan tujuan untuk mendorong desa-desa dalam mengenalkan dan mempromosikan potensi investasi mereka. Dengan mengikuti lomba ini, Desa Dramasaba tidak hanya memperoleh apresiasi, tetapi juga membuka peluang lebih besar untuk berkolaborasi dengan investor maupun pihak lain yang tertarik dalam pengembangan desa.
</Text>
<Text
ta={"justify"}
>
Ke depan, Desa Dramasaba berkomitmen untuk terus meningkatkan daya saingnya, baik dalam sektor ekonomi, budaya, maupun infrastruktur, sehingga mampu memberikan manfaat yang lebih besar bagi masyarakat serta menarik perhatian lebih banyak pihak untuk berinvestasi dan berkontribusi dalam pembangunan desa.
</Text>
<Text
py={5}
ta={"justify"}
>
Dengan prestasi ini, diharapkan Desa Dramasaba dapat menjadi contoh bagi desa-desa lain dalam memanfaatkan media digital sebagai sarana promosi dan pengembangan potensi lokal.
</Text>
<Box py={20}>
<Divider />
<Flex justify={"space-between"} py={20}>
<Text fz={"sm"}>25 May 2021 . Darmasaba</Text>
<Box>
<Flex gap={"lg"}>
<ActionIcon variant='transparent'>
<IconBrandFacebook color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandInstagram color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandTwitter color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandWhatsapp color={"gray"} size={"30"} />
</ActionIcon>
</Flex>
</Box>
</Flex>
<Divider pb={50} />
</Box>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,97 +0,0 @@
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Stack, Container, Text, Image, ActionIcon, Box, Divider, Flex, Group } from '@mantine/core';
import { IconBrandFacebook, IconBrandInstagram, IconBrandTwitter, IconBrandWhatsapp } from '@tabler/icons-react';
import React from 'react';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={22}>
<Group px={{ base: "md", md: 100 }}>
<BackButton />
</Group>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"}>
Juara 2
</Text>
<Text
ta={"center"}
fw={"bold"}
>
Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024
</Text>
<Image py={20} src={"/api/img/penghargaan-2.png"} alt='' />
</Stack>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Text
pb={20}
ta={"center"}
fw={"bold"}
>
Desa Dramasaba Raih Juara 2 dalam Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024
</Text>
<Text
ta={"justify"}
>
Desa Dramasaba kembali menunjukkan eksistensinya dalam memajukan potensi desa dengan meraih Juara 2 dalam ajang Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024. Prestasi ini menjadi bukti nyata bahwa Desa Dramasaba memiliki daya tarik serta peluang investasi yang menjanjikan bagi para investor maupun masyarakat yang ingin berkontribusi dalam pengembangan desa.
</Text>
<Text
py={5}
ta={"justify"}
>
Dalam video pendek yang diikutsertakan dalam lomba ini, Desa Dramasaba menampilkan berbagai potensi unggulan yang dimiliki, mulai dari sektor pertanian, pariwisata berbasis budaya, hingga ekonomi kreatif yang sedang berkembang pesat. Dengan pengemasan yang menarik dan penyampaian informasi yang jelas, video tersebut berhasil memikat perhatian dewan juri serta menjadi inspirasi bagi desa-desa lain dalam mengembangkan potensi wilayahnya masing-masing.
</Text>
<Text
ta={"justify"}
>
Keberhasilan ini tidak lepas dari kerja keras seluruh tim kreatif, pemerintah desa, serta masyarakat yang turut berpartisipasi dalam produksi video. Kepala Desa Dramasaba, [Nama Kepala Desa], mengungkapkan rasa syukur dan bangganya atas pencapaian ini. Menurutnya, kemenangan ini bukan sekadar penghargaan, tetapi juga menjadi motivasi untuk terus mengembangkan desa agar semakin dikenal luas dan menarik lebih banyak peluang investasi.
</Text>
<Text
py={5}
ta={"justify"}
>
Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024 merupakan ajang yang diadakan oleh pemerintah daerah dengan tujuan untuk mendorong desa-desa dalam mengenalkan dan mempromosikan potensi investasi mereka. Dengan mengikuti lomba ini, Desa Dramasaba tidak hanya memperoleh apresiasi, tetapi juga membuka peluang lebih besar untuk berkolaborasi dengan investor maupun pihak lain yang tertarik dalam pengembangan desa.
</Text>
<Text
ta={"justify"}
>
Ke depan, Desa Dramasaba berkomitmen untuk terus meningkatkan daya saingnya, baik dalam sektor ekonomi, budaya, maupun infrastruktur, sehingga mampu memberikan manfaat yang lebih besar bagi masyarakat serta menarik perhatian lebih banyak pihak untuk berinvestasi dan berkontribusi dalam pembangunan desa.
</Text>
<Text
py={5}
ta={"justify"}
>
Dengan prestasi ini, diharapkan Desa Dramasaba dapat menjadi contoh bagi desa-desa lain dalam memanfaatkan media digital sebagai sarana promosi dan pengembangan potensi lokal.
</Text>
<Box py={20}>
<Divider />
<Flex justify={"space-between"} py={20}>
<Text fz={"sm"}>25 May 2021 . Darmasaba</Text>
<Box>
<Flex gap={"lg"}>
<ActionIcon variant='transparent'>
<IconBrandFacebook color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandInstagram color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandTwitter color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandWhatsapp color={"gray"} size={"30"} />
</ActionIcon>
</Flex>
</Box>
</Flex>
<Divider pb={50} />
</Box>
</Box>
</Stack >
);
}
export default Page;

Some files were not shown because too many files have changed in this diff Show More