Compare commits
7 Commits
nico/5-aug
...
nico/11-au
| Author | SHA1 | Date | |
|---|---|---|---|
| 2fe8b8ce1a | |||
| 5cbf7810bc | |||
| b3bf6b0327 | |||
| a65529cb23 | |||
| afc7bced44 | |||
| 0ac9fa1f53 | |||
| d4af56b508 |
74
prisma/data/desa/potensi/potensi-desa.json
Normal file
74
prisma/data/desa/potensi/potensi-desa.json
Normal 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 & Cafe Kekinian:</strong></p><p>Selain makanan tradisional, Darmasaba juga mulai berkembang dengan hadirnya cafe dan resto kekinian yang menyajikan menu modern seperti kopi spesial, burger, pizza, dan aneka dessert yang digemari anak muda. Keberadaan tempat-tempat ini menjadikan Darmasaba sebagai pilihan destinasi kuliner bagi wisatawan maupun warga sekitar.</p></li><li><p><strong>Pasar Kuliner Malam:</strong></p><p>Salah satu daya tarik Darmasaba adalah pusat kuliner malam yang menghadirkan aneka makanan kaki lima seperti nasi jinggo, tipat cantok, bakso, dan berbagai jajanan khas Bali. Suasana yang ramai dan harga yang terjangkau membuat pasar kuliner ini menjadi tempat favorit bagi masyarakat lokal.</p></li><li><p><strong>Potensi Ekonomi & UMKM Kuliner:</strong></p><p>Dengan berkembangnya sektor kuliner, banyak pelaku UMKM di Darmasaba mulai merintis usaha makanan, baik dalam bentuk warung makan, katering, hingga produksi makanan ringan seperti keripik, sambal, dan minuman tradisional. Potensi ini dapat terus dikembangkan dengan dukungan pemerintah desa dan promosi melalui media sosial.</p></li><li><p><strong>Kawasan Kuliner Berbasis Pariwisata:</strong></p><p>Untuk menarik lebih banyak pengunjung, Darmasaba berpotensi mengembangkan kawasan kuliner berbasis wisata yang menggabungkan pengalaman makan dengan konsep alam terbuka, pertunjukan seni, dan edukasi kuliner khas Bali. Hal ini dapat menjadi daya tarik tambahan bagi wisatawan yang ingin merasakan pengalaman kuliner yang lebih autentik.</p></li></ol><p>Dengan kekayaan kuliner yang dimiliki, Desa Darmasaba berpotensi menjadi kawasan kuliner unggulan di Kabupaten Badung. Dukungan dari masyarakat, pemerintah desa, serta promosi yang lebih luas dapat menjadikan Darmasaba sebagai destinasi kuliner yang semakin dikenal dan berkembang.</p>"
|
||||
},
|
||||
{
|
||||
"id": "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>"
|
||||
}
|
||||
]
|
||||
26
prisma/data/landing-page/penghargaan/penghargaan.json
Normal file
26
prisma/data/landing-page/penghargaan/penghargaan.json
Normal 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> <br>@surya_prabhawa <br>@kecamatanabiansemal <br>@dpmdbadungkab <br>@pemkabbadung<br>@prokompimbadung <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>"
|
||||
}
|
||||
]
|
||||
@@ -221,81 +221,6 @@ model KategoriPrestasiDesa {
|
||||
PrestasiDesa PrestasiDesa[]
|
||||
}
|
||||
|
||||
//========================================= INDEKS KEPUASAN MASYARAKAT ========================================= //
|
||||
// Entitas Survey
|
||||
model Survey {
|
||||
id String @id @default(cuid())
|
||||
title String // Judul survei
|
||||
totalRespondents Int // Total jumlah responden
|
||||
averageScore Float // Rata-rata skor
|
||||
monthlyStats MonthlyStat[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// Entitas Statistik Bulanan
|
||||
model MonthlyStat {
|
||||
id String @id @default(cuid())
|
||||
month String // Nama bulan (e.g., "Januari", "Februari")
|
||||
respondentsCount Int // Jumlah responden per bulan
|
||||
surveyId String @unique(map: "monthly_stat_survey_id_month_key")
|
||||
survey Survey @relation(fields: [surveyId], references: [id])
|
||||
AgeStat AgeStat[]
|
||||
ResponseStat ResponseStat[]
|
||||
genderStat genderStat[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// Entitas Gender
|
||||
|
||||
model genderStat {
|
||||
id String @id @default(cuid())
|
||||
laki Int
|
||||
perempuan Int
|
||||
percentLaki Float
|
||||
percentPerempuan Float
|
||||
total Int
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
MonthlyStat MonthlyStat? @relation(fields: [monthlyStatId], references: [id])
|
||||
monthlyStatId String?
|
||||
}
|
||||
|
||||
// Entitas Age
|
||||
|
||||
model AgeStat {
|
||||
id String @id @default(cuid())
|
||||
group String // "18-44", "45+" dll
|
||||
count Int
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
MonthlyStat MonthlyStat? @relation(fields: [monthlyStatId], references: [id])
|
||||
monthlyStatId String?
|
||||
}
|
||||
|
||||
// Entitas Response
|
||||
|
||||
model ResponseStat {
|
||||
id String @id @default(cuid())
|
||||
label String // BAIK / BURUK
|
||||
count Int
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
MonthlyStat MonthlyStat? @relation(fields: [monthlyStatId], references: [id])
|
||||
monthlyStatId String?
|
||||
}
|
||||
|
||||
//========================================= MENU PPID ========================================= //
|
||||
|
||||
//========================================= STRUKTUR PPID ========================================= //
|
||||
@@ -732,16 +657,16 @@ model PelayananPendudukNonPermanen {
|
||||
|
||||
// ========================================= PENGHARGAAN ========================================= //
|
||||
model Penghargaan {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
juara 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(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= MENU KESEHATAN ========================================= //
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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";
|
||||
@@ -43,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 ===========
|
||||
@@ -101,6 +103,25 @@ 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({
|
||||
@@ -116,7 +137,25 @@ import pegawaiPPID from "./data/ppid/struktur-ppid/pegawai-PPID.json";
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("media sosial success ...");
|
||||
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) {
|
||||
|
||||
@@ -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";
|
||||
@@ -22,19 +23,6 @@ const defaultForm = {
|
||||
kategoriBeritaId: "",
|
||||
};
|
||||
|
||||
// 3. Kategori proxy
|
||||
const category = proxy({
|
||||
findMany: {
|
||||
data: [] as Prisma.KategoriBeritaGetPayload<{ omit: { isActive: true } }>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.berita.category["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
category.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 4. Berita proxy
|
||||
const berita = proxy({
|
||||
create: {
|
||||
@@ -71,6 +59,8 @@ const berita = proxy({
|
||||
},
|
||||
},
|
||||
|
||||
// State untuk berita utama (hanya 1)
|
||||
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.BeritaGetPayload<{
|
||||
@@ -83,38 +73,43 @@ const berita = proxy({
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
|
||||
async load(page = 1, limit = 10) {
|
||||
berita.findMany.loading = true;
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||
berita.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
berita.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.desa.berita["find-many"].get({
|
||||
query: {
|
||||
page,
|
||||
limit,
|
||||
},
|
||||
});
|
||||
berita.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (kategori) query.kategori = kategori;
|
||||
|
||||
const res = await ApiFetch.api.desa.berita["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
berita.findMany.data = res.data.data ?? [];
|
||||
berita.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
berita.findMany.data = [];
|
||||
berita.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch berita paginated:", err);
|
||||
berita.findMany.data = [];
|
||||
berita.findMany.totalPages = 1;
|
||||
} finally {
|
||||
berita.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
findUnique: {
|
||||
data: null as
|
||||
| Prisma.BeritaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategoriBerita: true;
|
||||
};
|
||||
}> | null,
|
||||
data: null as Prisma.BeritaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategoriBerita: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/desa/berita/${id}`);
|
||||
@@ -122,11 +117,11 @@ const berita = proxy({
|
||||
const data = await res.json();
|
||||
berita.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error('Failed to fetch berita:', res.statusText);
|
||||
console.error("Failed to fetch berita:", res.statusText);
|
||||
berita.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching berita:', error);
|
||||
console.error("Error fetching berita:", error);
|
||||
berita.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
@@ -140,14 +135,14 @@ const berita = proxy({
|
||||
berita.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/berita/delete/${id}`, {
|
||||
method: 'DELETE',
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Berita berhasil dihapus");
|
||||
await berita.findMany.load(); // refresh list
|
||||
@@ -172,21 +167,21 @@ const berita = proxy({
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/berita/${id}`, {
|
||||
method: 'GET',
|
||||
method: "GET",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"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;
|
||||
@@ -203,7 +198,9 @@ const berita = proxy({
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading berita:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
@@ -217,14 +214,14 @@ const berita = proxy({
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
berita.edit.loading = true;
|
||||
|
||||
|
||||
const response = await fetch(`/api/desa/berita/${this.id}`, {
|
||||
method: 'PUT',
|
||||
method: "PUT",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
judul: this.form.judul,
|
||||
@@ -234,14 +231,16 @@ const berita = proxy({
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
throw new Error(
|
||||
errorData.message || `HTTP error! status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
if (result.success) {
|
||||
toast.success("Berhasil update berita");
|
||||
await berita.findMany.load(); // refresh list
|
||||
@@ -251,7 +250,11 @@ const berita = proxy({
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating berita:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update berita");
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update berita"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
berita.edit.loading = false;
|
||||
@@ -271,21 +274,22 @@ const berita = proxy({
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load() {
|
||||
// findFirst.load()
|
||||
async load(kategori?: string) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await ApiFetch.api.desa.berita["find-first"].get();
|
||||
const res = await ApiFetch.api.desa.berita["find-first"].get({
|
||||
query: kategori ? { kategori } : {},
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
// Add type assertion to ensure type safety
|
||||
berita.findFirst.data = res.data.data as Prisma.BeritaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
kategoriBerita: true;
|
||||
};
|
||||
}> | null;
|
||||
this.data = res.data.data || null;
|
||||
} else {
|
||||
this.data = null;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch berita terbaru:", err);
|
||||
this.data = null;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
@@ -299,7 +303,7 @@ const berita = proxy({
|
||||
};
|
||||
}>[],
|
||||
loading: false,
|
||||
|
||||
|
||||
async load() {
|
||||
try {
|
||||
this.loading = true;
|
||||
@@ -313,15 +317,227 @@ const berita = proxy({
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
//=============== Kategori Berita ===============
|
||||
|
||||
const templateKategoriBerita = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
});
|
||||
|
||||
const defaultKategoriBerita = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
const kategoriBerita = proxy({
|
||||
create: {
|
||||
form: { ...defaultKategoriBerita },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateKategoriBerita.safeParse(kategoriBerita.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kategoriBerita.create.loading = true;
|
||||
const res = await ApiFetch.api.desa.kategoriberita["create"].post(
|
||||
kategoriBerita.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
kategoriBerita.findMany.load();
|
||||
return toast.success("Data Kategori Berita Berhasil Dibuat");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return toast.error("failed create");
|
||||
} finally {
|
||||
kategoriBerita.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as Prisma.KategoriBeritaGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}>[],
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.kategoriberita["findMany"].get();
|
||||
if (res.status === 200) {
|
||||
kategoriBerita.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KategoriBeritaGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/desa/kategoriberita/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kategoriBerita.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
kategoriBerita.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
kategoriBerita.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async delete(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
kategoriBerita.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/kategoriberita/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 Berita berhasil dihapus"
|
||||
);
|
||||
await kategoriBerita.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus Data Kategori Berita"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus Data Kategori Berita");
|
||||
} finally {
|
||||
kategoriBerita.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultKategoriBerita },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/kategoriberita/${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,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori berita:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateKategoriBerita.safeParse(kategoriBerita.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
kategoriBerita.update.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/kategoriberita/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
}),
|
||||
});
|
||||
|
||||
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 berita");
|
||||
await kategoriBerita.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal update data kategori berita"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating data kategori berita:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update data kategori berita"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
kategoriBerita.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
kategoriBerita.update.id = "";
|
||||
kategoriBerita.update.form = { ...defaultKategoriBerita };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 5. State global
|
||||
const stateDashboardBerita = proxy({
|
||||
category,
|
||||
kategoriBerita,
|
||||
berita,
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,6 +5,228 @@ import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateKategoriPengumuman = z.object({
|
||||
name: z.string().min(1, "Nama harus diisi"),
|
||||
});
|
||||
|
||||
const defaultKategoriPengumuman = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
const category = proxy({
|
||||
create: {
|
||||
form: { ...defaultKategoriPengumuman },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateKategoriPengumuman.safeParse(category.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
category.create.loading = true;
|
||||
const res = await ApiFetch.api.desa.kategoripengumuman["create"].post(
|
||||
category.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
category.findMany.load();
|
||||
return toast.success("Data Kategori Pengumuman Berhasil Dibuat");
|
||||
}
|
||||
console.log(res);
|
||||
return toast.error("failed create");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return toast.error("failed create");
|
||||
} finally {
|
||||
category.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: [] as (Prisma.CategoryPengumumanGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> & {
|
||||
_count: {
|
||||
pengumumans: number;
|
||||
};
|
||||
})[],
|
||||
loading: false,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.kategoripengumuman["findMany"].get();
|
||||
if (res.status === 200) {
|
||||
category.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.CategoryPengumumanGetPayload<{
|
||||
omit: {
|
||||
isActive: true;
|
||||
};
|
||||
}> | null,
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/desa/kategoripengumuman/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
category.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
category.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
category.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async delete(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
category.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/kategoripengumuman/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 Pengumuman berhasil dihapus"
|
||||
);
|
||||
await category.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus Data Kategori Pengumuman"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error(
|
||||
"Terjadi kesalahan saat menghapus Data Kategori Pengumuman"
|
||||
);
|
||||
} finally {
|
||||
category.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultKategoriPengumuman },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/kategoripengumuman/${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,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori pengumuman:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateKategoriPengumuman.safeParse(category.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
category.update.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/desa/kategoripengumuman/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
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 pengumuman");
|
||||
await category.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal update data kategori pengumuman"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating data kategori pengumuman:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Terjadi kesalahan saat update data kategori pengumuman"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
category.update.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
category.update.id = "";
|
||||
category.update.form = { ...defaultKategoriPengumuman };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const templateFormPengumuman = z.object({
|
||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
@@ -12,33 +234,15 @@ const templateFormPengumuman = z.object({
|
||||
categoryPengumumanId: z.string().nonempty(),
|
||||
});
|
||||
|
||||
const category = proxy({
|
||||
findMany: {
|
||||
data: null as
|
||||
| null
|
||||
| Prisma.CategoryPengumumanGetPayload<{ omit: { isActive: true } }>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.pengumuman.category[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
category.findMany.data = (res.data?.data as any) ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type PengumumanForm = Prisma.PengumumanGetPayload<{
|
||||
select: {
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
content: true;
|
||||
categoryPengumumanId: true;
|
||||
};
|
||||
}>;
|
||||
const defaultForm = {
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
content: "",
|
||||
categoryPengumumanId: "",
|
||||
};
|
||||
const pengumuman = proxy({
|
||||
create: {
|
||||
form: {} as PengumumanForm,
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateFormPengumuman.safeParse(pengumuman.create.form);
|
||||
@@ -74,11 +278,35 @@ const pengumuman = proxy({
|
||||
};
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.desa.pengumuman["find-many"].get();
|
||||
console.log(res);
|
||||
if (res.status === 200) {
|
||||
pengumuman.findMany.data = res.data?.data ?? [];
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
loading: false,
|
||||
search: "",
|
||||
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||
pengumuman.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||
pengumuman.findMany.page = page;
|
||||
pengumuman.findMany.search = search;
|
||||
|
||||
try {
|
||||
const query: any = { page, limit };
|
||||
if (search) query.search = search;
|
||||
if (kategori) query.kategori = kategori;
|
||||
|
||||
const res = await ApiFetch.api.desa.pengumuman["find-many"].get({ query });
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
pengumuman.findMany.data = res.data.data ?? [];
|
||||
pengumuman.findMany.totalPages = res.data.totalPages ?? 1;
|
||||
} else {
|
||||
pengumuman.findMany.data = [];
|
||||
pengumuman.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Gagal fetch pengumuman paginated:", err);
|
||||
pengumuman.findMany.data = [];
|
||||
pengumuman.findMany.totalPages = 1;
|
||||
} finally {
|
||||
pengumuman.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -112,7 +340,7 @@ const pengumuman = proxy({
|
||||
try {
|
||||
pengumuman.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/pengumuman/delete/${id}`, {
|
||||
const response = await fetch(`/api/desa/pengumuman/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -135,9 +363,9 @@ const pengumuman = proxy({
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
edit: {
|
||||
id: "",
|
||||
form: {} as PengumumanForm,
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
@@ -153,6 +381,7 @@ const pengumuman = proxy({
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
@@ -168,20 +397,21 @@ const pengumuman = proxy({
|
||||
content: data.content,
|
||||
categoryPengumumanId: data.categoryPengumumanId || "",
|
||||
};
|
||||
return data;
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal mengambil data pengumuman");
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast.error("Terjadi kesalahan saat mengambil data pengumuman");
|
||||
} finally {
|
||||
pengumuman.update.loading = false;
|
||||
console.error("Error loading pengumuman:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateFormPengumuman.safeParse(pengumuman.update.form);
|
||||
const cek = templateFormPengumuman.safeParse(pengumuman.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
@@ -191,7 +421,7 @@ const pengumuman = proxy({
|
||||
}
|
||||
|
||||
try {
|
||||
pengumuman.update.loading = true;
|
||||
pengumuman.edit.loading = true;
|
||||
|
||||
const response = await fetch(`/api/desa/pengumuman/${this.id}`, {
|
||||
method: "PUT",
|
||||
@@ -202,7 +432,7 @@ const pengumuman = proxy({
|
||||
judul: this.form.judul,
|
||||
deskripsi: this.form.deskripsi,
|
||||
content: this.form.content,
|
||||
categoryPengumumanId: this.form.categoryPengumumanId,
|
||||
categoryPengumumanId: this.form.categoryPengumumanId || null,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -231,9 +461,14 @@ const pengumuman = proxy({
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
pengumuman.update.loading = false;
|
||||
pengumuman.edit.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
pengumuman.edit.id = "";
|
||||
pengumuman.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
findFirst: {
|
||||
data: null as Prisma.PengumumanGetPayload<{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,25 +5,21 @@ import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core'
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
function LayoutTabs({ children }: { children: React.ReactNode }) {
|
||||
function LayoutTabsBerita({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const tabs = [
|
||||
{
|
||||
label: "List Survey",
|
||||
value: "listSurvey",
|
||||
href: "/admin/landing-page/indeks-kepuasan-masyarakat/list-survey"
|
||||
label: "List Berita",
|
||||
value: "list_berita",
|
||||
href: "/admin/desa/berita/list-berita"
|
||||
},
|
||||
{
|
||||
label: "List Bulanan",
|
||||
value: "listBulanan",
|
||||
href: "/admin/landing-page/indeks-kepuasan-masyarakat/list-bulanan"
|
||||
label: "Kategori Berita",
|
||||
value: "kategori_berita",
|
||||
href: "/admin/desa/berita/kategori-berita"
|
||||
},
|
||||
{
|
||||
label: "List Gender Stat",
|
||||
value: "listGenderStat",
|
||||
href: "/admin/landing-page/indeks-kepuasan-masyarakat/list-gender-stat"
|
||||
}
|
||||
|
||||
];
|
||||
const curentTab = tabs.find(tab => tab.href === pathname)
|
||||
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
|
||||
@@ -45,7 +41,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={3}>Indeks Kepuasan Masyarakat</Title>
|
||||
<Title order={3}>Gallery</Title>
|
||||
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
|
||||
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
||||
{tabs.map((e, i) => (
|
||||
@@ -64,4 +60,4 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default LayoutTabs;
|
||||
export default LayoutTabsBerita;
|
||||
@@ -0,0 +1,80 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||
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 EditKategoriBerita() {
|
||||
const editState = useProxy(stateDashboardBerita.kategoriBerita)
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [formData, setFormData] = useState({
|
||||
name: editState.update.form.name || '',
|
||||
});
|
||||
|
||||
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({
|
||||
name: data.name || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori Berita:", error);
|
||||
toast.error("Gagal memuat data kategori Berita");
|
||||
}
|
||||
};
|
||||
|
||||
loadKategori();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
editState.update.form = {
|
||||
...editState.update.form,
|
||||
name: formData.name,
|
||||
};
|
||||
await editState.update.update();
|
||||
toast.success('Kategori Berita berhasil diperbarui!');
|
||||
router.push('/admin/desa/berita/kategori-berita');
|
||||
} catch (error) {
|
||||
console.error('Error updating kategori Berita:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui kategori Berita');
|
||||
}
|
||||
};
|
||||
|
||||
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 Berita</Title>
|
||||
<TextInput
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Nama Kategori Berita</Text>}
|
||||
placeholder="masukkan nama kategori Berita"
|
||||
/>
|
||||
|
||||
<Button onClick={handleSubmit}>Simpan</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditKategoriBerita;
|
||||
@@ -0,0 +1,55 @@
|
||||
'use client'
|
||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||
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 CreateKategoriBerita() {
|
||||
const createState = useProxy(stateDashboardBerita.kategoriBerita)
|
||||
const router = useRouter();
|
||||
|
||||
const resetForm = () => {
|
||||
createState.create.form = {
|
||||
name: "",
|
||||
};
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await createState.create.create();
|
||||
resetForm();
|
||||
router.push("/admin/desa/berita/kategori-berita")
|
||||
};
|
||||
|
||||
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 Berita</Title>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Nama Kategori Berita</Text>}
|
||||
placeholder='Masukkan nama kategori Berita'
|
||||
value={createState.create.form.name}
|
||||
onChange={(val) => {
|
||||
createState.create.form.name = val.target.value;
|
||||
}}
|
||||
/>
|
||||
<Group>
|
||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateKategoriBerita;
|
||||
128
src/app/admin/(dashboard)/desa/berita/kategori-berita/page.tsx
Normal file
128
src/app/admin/(dashboard)/desa/berita/kategori-berita/page.tsx
Normal 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 stateDashboardBerita from '../../../_state/desa/berita';
|
||||
|
||||
|
||||
|
||||
function KategoriBerita() {
|
||||
const [search, setSearch] = useState('');
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Kategori Berita'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListKategoriBerita search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListKategoriBerita({ search }: { search: string }) {
|
||||
const listDataState = useProxy(stateDashboardBerita.kategoriBerita)
|
||||
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.name.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 Berita'
|
||||
href='/admin/desa/berita/kategori-berita/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.name}</TableTd>
|
||||
<TableTd>
|
||||
<Button color='green' onClick={() => router.push(`/admin/pendidikan/perpustakaan-digital/kategori-Berita/${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 Berita ini?'
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default KategoriBerita;
|
||||
13
src/app/admin/(dashboard)/desa/berita/layout.tsx
Normal file
13
src/app/admin/(dashboard)/desa/berita/layout.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
'use client'
|
||||
import React from 'react';
|
||||
import LayoutTabsBerita from './_com/layoutTabs';
|
||||
|
||||
function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<LayoutTabsBerita>
|
||||
{children}
|
||||
</LayoutTabsBerita>
|
||||
);
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
"use client";
|
||||
|
||||
import {
|
||||
@@ -7,7 +8,6 @@ import {
|
||||
Image,
|
||||
Paper,
|
||||
Select,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
@@ -22,9 +22,8 @@ import EditEditor from "@/app/admin/(dashboard)/_com/editEditor";
|
||||
import colors from "@/con/colors";
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { FileInput } from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import stateDashboardBerita from "../../../../_state/desa/berita";
|
||||
import stateDashboardBerita from "@/app/admin/(dashboard)/_state/desa/berita";
|
||||
|
||||
|
||||
function EditBerita() {
|
||||
const beritaState = useProxy(stateDashboardBerita);
|
||||
@@ -43,6 +42,7 @@ function EditBerita() {
|
||||
|
||||
// Load berita by id saat pertama kali
|
||||
useEffect(() => {
|
||||
beritaState.kategoriBerita.findMany.load()
|
||||
const loadBerita = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
@@ -99,7 +99,7 @@ function EditBerita() {
|
||||
|
||||
await beritaState.berita.edit.update();
|
||||
toast.success("Berita berhasil diperbarui!");
|
||||
router.push("/admin/desa/berita");
|
||||
router.push("/admin/desa/berita/list-berita");
|
||||
} catch (error) {
|
||||
console.error("Error updating berita:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui berita");
|
||||
@@ -154,22 +154,29 @@ function EditBerita() {
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||
<EditEditor
|
||||
value={formData.content}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, content: htmlContent }));
|
||||
beritaState.berita.edit.form.content = htmlContent;
|
||||
}}
|
||||
value={formData.content}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, content: htmlContent }));
|
||||
beritaState.berita.edit.form.content = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<SelectCategory
|
||||
<Select
|
||||
value={formData.kategoriBeritaId}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
kategoriBeritaId: val?.id || ''
|
||||
});
|
||||
}}
|
||||
onChange={(val) => setFormData({ ...formData, kategoriBeritaId: val || "" })}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Kategori</Text>}
|
||||
placeholder='Pilih kategori'
|
||||
data={
|
||||
beritaState.kategoriBerita.findMany.data?.map((v) => ({
|
||||
value: v.id,
|
||||
label: v.name
|
||||
})) || []
|
||||
}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
error={!formData.kategoriBeritaId ? "Pilih kategori" : undefined}
|
||||
/>
|
||||
|
||||
<Button onClick={handleSubmit}>Edit Berita</Button>
|
||||
@@ -179,61 +186,4 @@ function EditBerita() {
|
||||
);
|
||||
}
|
||||
|
||||
interface SelectCategoryProps {
|
||||
onChange: (value: Prisma.KategoriBeritaGetPayload<{
|
||||
select: {
|
||||
name: true;
|
||||
id: true;
|
||||
};
|
||||
}> | null) => void;
|
||||
value?: string | null;
|
||||
defaultValue?: string | null;
|
||||
}
|
||||
|
||||
function SelectCategory({
|
||||
onChange,
|
||||
value,
|
||||
defaultValue,
|
||||
}: SelectCategoryProps) {
|
||||
const categoryState = useProxy(stateDashboardBerita.category);
|
||||
|
||||
useShallowEffect(() => {
|
||||
categoryState.findMany.load().then(() => {
|
||||
console.log("Kategori berhasil dimuat:", categoryState.findMany.data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!categoryState.findMany.data) {
|
||||
return <Skeleton height={38} />;
|
||||
}
|
||||
|
||||
|
||||
const selectedValue = value || defaultValue;
|
||||
|
||||
return (
|
||||
<Select
|
||||
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
||||
placeholder="Pilih kategori"
|
||||
data={categoryState.findMany.data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={selectedValue || null}
|
||||
onChange={(val: string | null) => {
|
||||
if (val) {
|
||||
const selected = categoryState.findMany.data?.find((item) => item.id === val);
|
||||
if (selected) {
|
||||
onChange(selected);
|
||||
}
|
||||
} else {
|
||||
onChange(null);
|
||||
}
|
||||
}}
|
||||
searchable
|
||||
clearable
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditBerita;
|
||||
@@ -8,8 +8,8 @@ import { useParams, useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
|
||||
import colors from '@/con/colors';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import stateDashboardBerita from '../../../_state/desa/berita';
|
||||
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||
|
||||
function DetailBerita() {
|
||||
const beritaState = useProxy(stateDashboardBerita)
|
||||
@@ -28,7 +28,7 @@ function DetailBerita() {
|
||||
beritaState.berita.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/desa/berita")
|
||||
router.push("/admin/desa/berita/list-berita")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ function DetailBerita() {
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (beritaState.berita.findUnique.data) {
|
||||
router.push(`/admin/desa/berita/${beritaState.berita.findUnique.data.id}/edit`);
|
||||
router.push(`/admin/desa/berita/list-berita/${beritaState.berita.findUnique.data.id}/edit`);
|
||||
}
|
||||
}}
|
||||
disabled={!beritaState.berita.findUnique.data}
|
||||
@@ -1,16 +1,16 @@
|
||||
'use client'
|
||||
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Center, FileInput, Image, Paper, Select, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Center, FileInput, Image, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import CreateEditor from '../../../_com/createEditor';
|
||||
import stateDashboardBerita from '../../../_state/desa/berita';
|
||||
|
||||
|
||||
export default function CreateBerita() {
|
||||
const beritaState = useProxy(stateDashboardBerita);
|
||||
@@ -18,6 +18,10 @@ export default function CreateBerita() {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
beritaState.kategoriBerita.findMany.load()
|
||||
}, []);
|
||||
|
||||
const resetForm = () => {
|
||||
// Reset state di valtio
|
||||
beritaState.berita.create.form = {
|
||||
@@ -57,7 +61,7 @@ export default function CreateBerita() {
|
||||
|
||||
// Reset form setelah submit
|
||||
resetForm();
|
||||
router.push("/admin/desa/berita")
|
||||
router.push("/admin/desa/berita/list-berita")
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -78,11 +82,27 @@ export default function CreateBerita() {
|
||||
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
<SelectCategory
|
||||
value={beritaState.berita.create.form.kategoriBeritaId}
|
||||
onChange={(val) => {
|
||||
beritaState.berita.create.form.kategoriBeritaId = val?.id || "";
|
||||
<Select
|
||||
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
||||
placeholder="Pilih kategori"
|
||||
data={beritaState.kategoriBerita.findMany.data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={beritaState.berita.create.form.kategoriBeritaId || null}
|
||||
onChange={(val: string | null) => {
|
||||
if (val) {
|
||||
const selected = beritaState.kategoriBerita.findMany.data?.find((item) => item.id === val);
|
||||
if (selected) {
|
||||
beritaState.berita.create.form.kategoriBeritaId = selected.id;
|
||||
}
|
||||
} else {
|
||||
beritaState.berita.create.form.kategoriBeritaId = "";
|
||||
}
|
||||
}}
|
||||
searchable
|
||||
clearable
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
<TextInput
|
||||
value={beritaState.berita.create.form.deskripsi}
|
||||
@@ -126,61 +146,4 @@ export default function CreateBerita() {
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
|
||||
interface SelectCategoryProps {
|
||||
onChange: (value: Prisma.KategoriBeritaGetPayload<{
|
||||
select: {
|
||||
name: true;
|
||||
id: true;
|
||||
};
|
||||
}> | null) => void;
|
||||
value?: string | null;
|
||||
defaultValue?: string | null;
|
||||
}
|
||||
|
||||
function SelectCategory({
|
||||
onChange,
|
||||
value,
|
||||
defaultValue,
|
||||
}: SelectCategoryProps) {
|
||||
const categoryState = useProxy(stateDashboardBerita.category);
|
||||
|
||||
useShallowEffect(() => {
|
||||
categoryState.findMany.load().then(() => {
|
||||
console.log("Kategori berhasil dimuat:", categoryState.findMany.data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!categoryState.findMany.data) {
|
||||
return <Skeleton height={38} />;
|
||||
}
|
||||
|
||||
|
||||
const selectedValue = value || defaultValue;
|
||||
|
||||
return (
|
||||
<Select
|
||||
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
||||
placeholder="Pilih kategori"
|
||||
data={categoryState.findMany.data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={selectedValue || null}
|
||||
onChange={(val: string | null) => {
|
||||
if (val) {
|
||||
const selected = categoryState.findMany.data?.find((item) => item.id === val);
|
||||
if (selected) {
|
||||
onChange(selected);
|
||||
}
|
||||
} else {
|
||||
onChange(null);
|
||||
}
|
||||
}}
|
||||
searchable
|
||||
clearable
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,9 @@ import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/ico
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import stateDashboardBerita from '../../_state/desa/berita';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import stateDashboardBerita from '../../../_state/desa/berita';
|
||||
|
||||
|
||||
|
||||
function Berita() {
|
||||
@@ -38,22 +39,16 @@ function ListBerita({ search }: { search: string }) {
|
||||
} = beritaState.berita.findMany;
|
||||
|
||||
|
||||
// Fetch pertama kali
|
||||
// Fetch data when page or search changes
|
||||
useShallowEffect(() => {
|
||||
load(page, 10); // awal page = 1
|
||||
}, [page]);
|
||||
|
||||
const filteredData = (data || []).filter((item) => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.judul.toLowerCase().includes(keyword) ||
|
||||
item.kategoriBerita?.name.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
|
||||
if (loading || !data) {
|
||||
return <Skeleton h={500} />;
|
||||
}
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -67,7 +62,7 @@ function ListBerita({ search }: { search: string }) {
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button
|
||||
onClick={() => router.push("/admin/desa/berita/create")}
|
||||
onClick={() => router.push("/admin/desa/berita/list-berita/create")}
|
||||
bg={colors["blue-button"]}
|
||||
>
|
||||
<IconCircleDashedPlus size={25} />
|
||||
@@ -107,7 +102,7 @@ function ListBerita({ search }: { search: string }) {
|
||||
<Button
|
||||
bg={"green"}
|
||||
onClick={() =>
|
||||
router.push(`/admin/desa/berita/${item.id}`)
|
||||
router.push(`/admin/desa/berita/list-berita/${item.id}`)
|
||||
}
|
||||
>
|
||||
<IconDeviceImacCog size={25} />
|
||||
@@ -51,7 +51,6 @@ function ListSuratKeterangan({ search }: { search: string }) {
|
||||
item.deskripsi?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
.sort((a, b) => a.posisi?.hierarki - b.posisi?.hierarki);
|
||||
}, [data, search]);
|
||||
|
||||
// Handle loading state
|
||||
|
||||
@@ -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={() => {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
'use client'
|
||||
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 { KeamananEditor } from '@/app/admin/(dashboard)/keamanan/_com/keamananEditor';
|
||||
|
||||
function EditPengumuman() {
|
||||
const router = useRouter();
|
||||
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}>Edit Pengumuman</Title>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Judul</Text>}
|
||||
placeholder='Masukkan judul'
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Deskripsi Singkat</Text>}
|
||||
placeholder='Masukkan deskripsi singkat'
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Tanggal</Text>}
|
||||
placeholder='Masukkan tanggal'
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Waktu</Text>}
|
||||
placeholder='Masukkan waktu'
|
||||
/>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>
|
||||
<KeamananEditor
|
||||
showSubmit={false}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Group>
|
||||
<Button bg={colors['blue-button']}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditPengumuman;
|
||||
@@ -0,0 +1,62 @@
|
||||
/* 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 LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const tabs = [
|
||||
{
|
||||
label: "List Pengumuman",
|
||||
value: "listpengumuman",
|
||||
href: "/admin/desa/pengumuman/list-pengumuman"
|
||||
},
|
||||
{
|
||||
label: "Kategori Pengumuman",
|
||||
value: "kategoripengumuman",
|
||||
href: "/admin/desa/pengumuman/kategori-pengumuman"
|
||||
},
|
||||
];
|
||||
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}>Pengumuman</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 LayoutTabsLayanan;
|
||||
@@ -0,0 +1,80 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import stateDesaPengumuman from '@/app/admin/(dashboard)/_state/desa/pengumuman';
|
||||
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 EditKategoriPengumuman() {
|
||||
const editState = useProxy(stateDesaPengumuman.category)
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [formData, setFormData] = useState({
|
||||
name: editState.update.form.name || '',
|
||||
});
|
||||
|
||||
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({
|
||||
name: data.name || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori Pengumuman:", error);
|
||||
toast.error("Gagal memuat data kategori Pengumuman");
|
||||
}
|
||||
};
|
||||
|
||||
loadKategori();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
editState.update.form = {
|
||||
...editState.update.form,
|
||||
name: formData.name,
|
||||
};
|
||||
await editState.update.update();
|
||||
toast.success('Kategori Pengumuman berhasil diperbarui!');
|
||||
router.push('/admin/desa/pengumuman/kategori-pengumuman');
|
||||
} catch (error) {
|
||||
console.error('Error updating kategori Pengumuman:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui kategori Pengumuman');
|
||||
}
|
||||
};
|
||||
|
||||
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 Pengumuman</Title>
|
||||
<TextInput
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Nama Kategori Pengumuman</Text>}
|
||||
placeholder="masukkan nama kategori Pengumuman"
|
||||
/>
|
||||
|
||||
<Button onClick={handleSubmit}>Simpan</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditKategoriPengumuman;
|
||||
@@ -0,0 +1,55 @@
|
||||
'use client'
|
||||
import stateDesaPengumuman from '@/app/admin/(dashboard)/_state/desa/pengumuman';
|
||||
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 CreateKategoriPengumuman() {
|
||||
const createState = useProxy(stateDesaPengumuman.category)
|
||||
const router = useRouter();
|
||||
|
||||
const resetForm = () => {
|
||||
createState.create.form = {
|
||||
name: "",
|
||||
};
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await createState.create.create();
|
||||
resetForm();
|
||||
router.push("/admin/desa/pengumuman/kategori-pengumuman")
|
||||
};
|
||||
|
||||
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 Pengumuman</Title>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Nama Kategori Pengumuman</Text>}
|
||||
placeholder='Masukkan nama kategori Pengumuman'
|
||||
value={createState.create.form.name}
|
||||
onChange={(val) => {
|
||||
createState.create.form.name = val.target.value;
|
||||
}}
|
||||
/>
|
||||
<Group>
|
||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateKategoriPengumuman;
|
||||
@@ -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 stateDesaPengumuman from '../../../_state/desa/pengumuman';
|
||||
|
||||
|
||||
|
||||
function KategoriPengumuman() {
|
||||
const [search, setSearch] = useState('');
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Kategori Pengumuman'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListKategoriPengumuman search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListKategoriPengumuman({ search }: { search: string }) {
|
||||
const listDataState = useProxy(stateDesaPengumuman.category)
|
||||
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.name.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 Pengumuman'
|
||||
href='/admin/desa/pengumuman/kategori-pengumuman/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.name}</TableTd>
|
||||
<TableTd>
|
||||
<Button color='green' onClick={() => router.push(`/admin/desa/pengumuman/kategori-pengumuman/${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 Pengumuman ini?'
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default KategoriPengumuman;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import LayoutTabs from './_lib/layoutTabs';
|
||||
import LayoutTabs from './_com/layoutTabs';
|
||||
|
||||
function Layout({children} : {children: React.ReactNode}) {
|
||||
function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<LayoutTabs>
|
||||
{children}
|
||||
@@ -0,0 +1,140 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
"use client";
|
||||
|
||||
import EditEditor from "@/app/admin/(dashboard)/_com/editEditor";
|
||||
import stateDesaPengumuman from "@/app/admin/(dashboard)/_state/desa/pengumuman";
|
||||
import colors from "@/con/colors";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Paper,
|
||||
Select,
|
||||
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 EditPengumuman() {
|
||||
const editState = useProxy(stateDesaPengumuman);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
judul: editState.pengumuman.edit.form.judul || '',
|
||||
deskripsi: editState.pengumuman.edit.form.deskripsi || '',
|
||||
categoryPengumumanId: editState.pengumuman.edit.form.categoryPengumumanId || '',
|
||||
content: editState.pengumuman.edit.form.content || ''
|
||||
});
|
||||
|
||||
// Load pengumuman by id saat pertama kali
|
||||
useEffect(() => {
|
||||
editState.category.findMany.load()
|
||||
const loadpengumuman = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await stateDesaPengumuman.pengumuman.edit.load(id); // akses langsung, bukan dari proxy
|
||||
if (data) {
|
||||
setFormData({
|
||||
judul: data.judul || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
categoryPengumumanId: data.categoryPengumumanId || '',
|
||||
content: data.content || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pengumuman:", error);
|
||||
toast.error("Gagal memuat data pengumuman");
|
||||
}
|
||||
};
|
||||
|
||||
loadpengumuman();
|
||||
}, [params?.id]); // ✅ hapus editState dari dependency
|
||||
|
||||
const handleSubmit = async () => {
|
||||
|
||||
try {
|
||||
// edit global state with form data
|
||||
editState.pengumuman.edit.form = {
|
||||
...editState.pengumuman.edit.form,
|
||||
judul: formData.judul,
|
||||
deskripsi: formData.deskripsi,
|
||||
content: formData.content,
|
||||
categoryPengumumanId: formData.categoryPengumumanId || ''
|
||||
};
|
||||
await editState.pengumuman.edit.update();
|
||||
toast.success("pengumuman berhasil diperbarui!");
|
||||
router.push("/admin/desa/pengumuman/list-pengumuman");
|
||||
} catch (error) {
|
||||
console.error("Error updating pengumuman:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui pengumuman");
|
||||
}
|
||||
};
|
||||
|
||||
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 pengumuman</Title>
|
||||
<TextInput
|
||||
value={formData.judul}
|
||||
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
value={formData.deskripsi}
|
||||
onChange={(e) => setFormData({ ...formData, deskripsi: e.target.value })}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
|
||||
placeholder="masukkan deskripsi"
|
||||
/>
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||
<EditEditor
|
||||
value={formData.content}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, content: htmlContent }));
|
||||
editState.pengumuman.edit.form.content = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Select
|
||||
value={formData.categoryPengumumanId}
|
||||
onChange={(val) => setFormData({ ...formData, categoryPengumumanId: val || "" })}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Kategori</Text>}
|
||||
placeholder='Pilih kategori'
|
||||
data={
|
||||
editState.category.findMany.data?.map((v) => ({
|
||||
value: v.id,
|
||||
label: v.name
|
||||
})) || []
|
||||
}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
error={!formData.categoryPengumumanId ? "Pilih kategori" : undefined}
|
||||
/>
|
||||
|
||||
<Button onClick={handleSubmit}>Edit pengumuman</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditPengumuman;
|
||||
@@ -1,40 +1,46 @@
|
||||
'use client'
|
||||
|
||||
import { Box, Button, Paper } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import colors from '@/con/colors';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import stateDesaPengumuman from '@/app/admin/(dashboard)/_state/desa/pengumuman';
|
||||
import { useState } from 'react';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||
|
||||
|
||||
function DetailPengumuman() {
|
||||
// const pengumumanState = useProxy(stateDesaPengumuman)
|
||||
// const [modalHapus, setModalHapus] = useState(false)
|
||||
// const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
// const params = useParams()
|
||||
const pengumumanState = useProxy(stateDesaPengumuman)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const params = useParams()
|
||||
const router = useRouter()
|
||||
|
||||
// useShallowEffect(() => {
|
||||
// pengumumanState.pengumuman.findUnique.load(params?.id as string)
|
||||
// }, [])
|
||||
useShallowEffect(() => {
|
||||
pengumumanState.pengumuman.findUnique.load(params?.id as string)
|
||||
}, [])
|
||||
|
||||
|
||||
// const handleHapus = () => {
|
||||
// if (selectedId) {
|
||||
// pengumumanState.pengumuman.delete.byId(selectedId)
|
||||
// setModalHapus(false)
|
||||
// setSelectedId(null)
|
||||
// router.push("/admin/desa/pengumuman")
|
||||
// }
|
||||
// }
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
pengumumanState.pengumuman.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/desa/pengumuman/list-pengumuman")
|
||||
}
|
||||
}
|
||||
|
||||
// if (!pengumumanState.pengumuman.findUnique.data) {
|
||||
// return (
|
||||
// <Stack py={10}>
|
||||
// <Skeleton h={400} />
|
||||
// </Stack>
|
||||
// )
|
||||
// }
|
||||
if (!pengumumanState.pengumuman.findUnique.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={400} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
@@ -44,7 +50,7 @@ function DetailPengumuman() {
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
|
||||
{/* <Stack>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Detail Pengumuman</Text>
|
||||
{pengumumanState.pengumuman.findUnique.data ? (
|
||||
<Paper key={pengumumanState.pengumuman.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||
@@ -79,11 +85,11 @@ function DetailPengumuman() {
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (pengumumanState.pengumuman.findUnique.data) {
|
||||
router.push(`/admin/desa/pengumuman/${pengumumanState.pengumuman.findUnique.data.id}/edit`);
|
||||
}
|
||||
}}
|
||||
onClick={() => {
|
||||
if (pengumumanState.pengumuman.findUnique.data) {
|
||||
router.push(`/admin/desa/pengumuman/list-pengumuman/${pengumumanState.pengumuman.findUnique.data.id}/edit`);
|
||||
}
|
||||
}}
|
||||
disabled={!pengumumanState.pengumuman.findUnique.data}
|
||||
color={"green"}
|
||||
>
|
||||
@@ -93,16 +99,16 @@ function DetailPengumuman() {
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : null}
|
||||
</Stack> */}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Konfirmasi Hapus
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text='Apakah anda yakin ingin menghapus berita ini?'
|
||||
/> */}
|
||||
text='Apakah anda yakin ingin menghapus pengumuman ini?'
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,22 +1,26 @@
|
||||
'use client'
|
||||
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||
import stateDesaPengumuman from '@/app/admin/(dashboard)/_state/desa/pengumuman';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import CreateEditor from '../../../_com/createEditor';
|
||||
import stateDesaPengumuman from '../../../_state/desa/pengumuman';
|
||||
|
||||
|
||||
function CreatePengumuman() {
|
||||
const pengumumanState = useProxy(stateDesaPengumuman)
|
||||
const router = useRouter();
|
||||
|
||||
useShallowEffect(() => {
|
||||
pengumumanState.category.findMany.load()
|
||||
}, [])
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await pengumumanState.pengumuman.create.create()
|
||||
resetForm()
|
||||
router.push("/admin/desa/pengumuman")
|
||||
router.push("/admin/desa/pengumuman/list-pengumuman")
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
@@ -45,10 +49,21 @@ function CreatePengumuman() {
|
||||
pengumumanState.pengumuman.create.form.judul = val.target.value
|
||||
}}
|
||||
/>
|
||||
<SelectCategory
|
||||
<Select
|
||||
label={<Text fw={"bold"} fz={"sm"}>Kategori</Text>}
|
||||
placeholder='Pilih kategori'
|
||||
data={pengumumanState.category.findMany.data?.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
onChange={(val) => {
|
||||
pengumumanState.pengumuman.create.form.categoryPengumumanId = val.id;
|
||||
const selected = pengumumanState.category.findMany.data?.find((item) => item.id === val);
|
||||
if (selected) {
|
||||
pengumumanState.pengumuman.create.form.categoryPengumumanId = selected.id;
|
||||
}
|
||||
}}
|
||||
searchable
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Deskripsi Singkat</Text>}
|
||||
@@ -76,35 +91,4 @@ function CreatePengumuman() {
|
||||
);
|
||||
}
|
||||
|
||||
function SelectCategory({
|
||||
onChange,
|
||||
}: {
|
||||
onChange: (value: Prisma.CategoryPengumumanGetPayload<{ select: { name: true; id: true; } }>) => void;
|
||||
}) {
|
||||
const categoryState = useProxy(stateDesaPengumuman.category);
|
||||
|
||||
useShallowEffect(() => {
|
||||
categoryState.findMany.load();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Select
|
||||
label={<Text fw={"bold"} fz={"sm"}>Kategori</Text>}
|
||||
placeholder='Pilih kategori'
|
||||
data={categoryState.findMany.data?.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
onChange={(val) => {
|
||||
const selected = categoryState.findMany.data?.find((item) => item.id === val);
|
||||
if (selected) {
|
||||
onChange(selected);
|
||||
}
|
||||
}}
|
||||
searchable
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreatePengumuman;
|
||||
@@ -1,14 +1,13 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Center, Grid, GridCol, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import stateDesaPengumuman from '../../_state/desa/pengumuman';
|
||||
import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import stateDesaPengumuman from '../../../_state/desa/pengumuman';
|
||||
|
||||
|
||||
function Pengumuman() {
|
||||
@@ -16,7 +15,7 @@ function Pengumuman() {
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Posisi Organisasi'
|
||||
title='List Pengumuman'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
@@ -30,31 +29,21 @@ function Pengumuman() {
|
||||
function ListPengumuman({ search }: { search: string }) {
|
||||
const pengumumanState = useProxy(stateDesaPengumuman)
|
||||
const router = useRouter()
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = pengumumanState.pengumuman.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
pengumumanState.pengumuman.findMany.load()
|
||||
}, [])
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
|
||||
const filteredData = (data || [])
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
pengumumanState.pengumuman.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
}
|
||||
}
|
||||
|
||||
const filteredData = (pengumumanState.pengumuman.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.judul.toLowerCase().includes(keyword) ||
|
||||
item.CategoryPengumuman?.name.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!pengumumanState.pengumuman.findMany.data) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
@@ -71,7 +60,7 @@ function ListPengumuman({ search }: { search: string }) {
|
||||
<Text fz={"xl"} fw={"bold"}>List Pengumuman</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button onClick={() => router.push("/admin/desa/pengumuman/create")} bg={colors['blue-button']}>
|
||||
<Button onClick={() => router.push("/admin/desa/pengumuman/list-pengumuman/create")} bg={colors['blue-button']}>
|
||||
<IconCircleDashedPlus size={25} />
|
||||
</Button>
|
||||
</GridCol>
|
||||
@@ -96,7 +85,7 @@ function ListPengumuman({ search }: { search: string }) {
|
||||
</TableTd>
|
||||
<TableTd >{item.CategoryPengumuman?.name}</TableTd>
|
||||
<TableTd>
|
||||
<Button bg={"green"} onClick={() => router.push(`/admin/desa/pengumuman/detail`)}>
|
||||
<Button bg={"green"} onClick={() => router.push(`/admin/desa/pengumuman/list-pengumuman/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
@@ -107,14 +96,15 @@ function ListPengumuman({ search }: { search: string }) {
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text='Apakah anda yakin ingin menghapus berita ini?'
|
||||
/>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => load(newPage)}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Select, Stack, Text, TextInput } 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 EditListBulanan() {
|
||||
const editState = useProxy(indeksKepuasanState.monthlyStatState)
|
||||
const params = useParams()
|
||||
const router = useRouter()
|
||||
const [formData, setFormData] = useState({
|
||||
month: editState.edit.form.month || '',
|
||||
respondentsCount: editState.edit.form.respondentsCount || 0,
|
||||
surveyId: editState.edit.form.surveyId || '',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const loadSurvey = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await editState.edit.load(id); // akses langsung, bukan dari proxy
|
||||
if (data) {
|
||||
setFormData({
|
||||
month: data.month,
|
||||
respondentsCount: data.respondentsCount,
|
||||
surveyId: data.surveyId,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading list bulanan:", error);
|
||||
toast.error("Gagal memuat data list bulanan");
|
||||
}
|
||||
};
|
||||
indeksKepuasanState.surveyState.findMany.load();
|
||||
loadSurvey();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
|
||||
try {
|
||||
// edit global state with form data
|
||||
editState.edit.form = {
|
||||
...editState.edit.form,
|
||||
month: formData.month,
|
||||
respondentsCount: formData.respondentsCount,
|
||||
surveyId: formData.surveyId,
|
||||
};
|
||||
|
||||
await editState.edit.update();
|
||||
toast.success("list bulanan berhasil diperbarui!");
|
||||
router.push("/admin/landing-page/indeks-kepuasan-masyarakat/list-bulanan");
|
||||
} catch (error) {
|
||||
console.error("Error updating list bulanan:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui list bulanan");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Edit List Survey</Text>
|
||||
<Paper>
|
||||
<Stack gap={"xs"}>
|
||||
<TextInput
|
||||
value={formData.month}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
month: val.target.value
|
||||
})
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Bulan</Text>}
|
||||
placeholder='Masukkan bulan'
|
||||
/>
|
||||
<TextInput
|
||||
value={formData.respondentsCount}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
respondentsCount: Number(val.target.value)
|
||||
})
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Total Responden</Text>}
|
||||
placeholder='Masukkan total responden'
|
||||
/>
|
||||
<Select
|
||||
label={<Text fw="bold" fz="sm">Pilih Survey</Text>}
|
||||
placeholder="Pilih survey"
|
||||
value={formData.surveyId}
|
||||
onChange={(value) => {
|
||||
if (value) setFormData({
|
||||
...formData,
|
||||
surveyId: value
|
||||
})
|
||||
}}
|
||||
data={
|
||||
indeksKepuasanState.surveyState.findMany.data?.map((survey) => ({
|
||||
value: survey.id,
|
||||
label: `${survey.title} (${survey.totalRespondents} responden)`,
|
||||
})) || []
|
||||
}
|
||||
/>
|
||||
<Group>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditListBulanan;
|
||||
@@ -1,109 +0,0 @@
|
||||
'use client'
|
||||
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function DetailListBulanan() {
|
||||
const detailState = useProxy(indeksKepuasanState.monthlyStatState)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const params = useParams()
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
detailState.findUnique.load(params?.id as string)
|
||||
}, [])
|
||||
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
detailState.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/landing-page/indeks-kepuasan-masyarakat/list-bulanan")
|
||||
}
|
||||
}
|
||||
|
||||
if (!detailState.findUnique.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={40} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
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']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Detail List Survey</Text>
|
||||
{detailState.findUnique.data ? (
|
||||
<Paper key={detailState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Bulan</Text>
|
||||
<Text fz={"lg"}>{detailState.findUnique.data?.month}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Total Responden</Text>
|
||||
<Text fz={"lg"}>{detailState.findUnique.data?.respondentsCount}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Survey</Text>
|
||||
<Text fz={"lg"}>{detailState.findUnique.data?.survey.title }</Text>
|
||||
</Box>
|
||||
<Flex gap={"xs"} mt={10}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (detailState.findUnique.data) {
|
||||
setSelectedId(detailState.findUnique.data.id);
|
||||
setModalHapus(true);
|
||||
}
|
||||
}}
|
||||
disabled={detailState.delete.loading || !detailState.findUnique.data}
|
||||
color={"red"}
|
||||
>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (detailState.findUnique.data) {
|
||||
router.push(`/admin/landing-page/indeks-kepuasan-masyarakat/list-bulanan/${detailState.findUnique.data.id}/edit`);
|
||||
}
|
||||
}}
|
||||
disabled={!detailState.findUnique.data}
|
||||
color={"green"}
|
||||
>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text='Apakah anda yakin ingin menghapus list bulanan ini?'
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailListBulanan;
|
||||
@@ -1,84 +0,0 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function CreateListBulanan() {
|
||||
const router = useRouter();
|
||||
const stateCreate = useProxy(indeksKepuasanState.monthlyStatState)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
stateCreate.findMany.load();
|
||||
indeksKepuasanState.surveyState.findMany.load();
|
||||
}, []);
|
||||
|
||||
const resetForm = () => {
|
||||
stateCreate.create.form = {
|
||||
month: "",
|
||||
respondentsCount: 0,
|
||||
surveyId: "",
|
||||
};
|
||||
};
|
||||
const handleSubmit = async () => {
|
||||
await stateCreate.create.create();
|
||||
resetForm();
|
||||
router.push("/admin/landing-page/indeks-kepuasan-masyarakat/list-bulanan")
|
||||
}
|
||||
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 List Bulanan</Title>
|
||||
<TextInput
|
||||
value={stateCreate.create.form.month}
|
||||
onChange={(val) => {
|
||||
stateCreate.create.form.month = val.target.value;
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Bulan</Text>}
|
||||
placeholder='Masukkan bulan'
|
||||
/>
|
||||
<TextInput
|
||||
value={stateCreate.create.form.respondentsCount}
|
||||
onChange={(val) => {
|
||||
stateCreate.create.form.respondentsCount = Number(val.target.value);
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Total Responden</Text>}
|
||||
placeholder='Masukkan total responden'
|
||||
/>
|
||||
<Select
|
||||
label={<Text fw="bold" fz="sm">Pilih Survey</Text>}
|
||||
placeholder="Pilih survey"
|
||||
value={stateCreate.create.form.surveyId}
|
||||
onChange={(value) => {
|
||||
if (value) stateCreate.create.form.surveyId = value;
|
||||
}}
|
||||
data={
|
||||
indeksKepuasanState.surveyState.findMany.data?.map((survey) => ({
|
||||
value: survey.id,
|
||||
label: `${survey.title} (${survey.totalRespondents} responden)`,
|
||||
})) || []
|
||||
}
|
||||
/>
|
||||
<Group>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateListBulanan;
|
||||
@@ -1,98 +0,0 @@
|
||||
/* 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 { 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 indeksKepuasanState from '../../../_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
|
||||
|
||||
|
||||
function ListBulananLandingPage() {
|
||||
const [search, setSearch] = useState('');
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='List Bulanan'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListBUlanan search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListBUlanan({ search }: { search: string }) {
|
||||
const listState = useProxy(indeksKepuasanState.monthlyStatState)
|
||||
const router = useRouter();
|
||||
useEffect(() => {
|
||||
listState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (listState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.month.toLowerCase().includes(keyword) ||
|
||||
item.respondentsCount.toString().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!listState.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 Bulanan'
|
||||
href='/admin/landing-page/indeks-kepuasan-masyarakat/list-bulanan/create'
|
||||
/>
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Bulan</TableTh>
|
||||
<TableTh>Total Responden</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
<Text truncate="end" fz={"sm"}>{item.month}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate="end" fz={"sm"}>{item.respondentsCount}</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/landing-page/indeks-kepuasan-masyarakat/list-bulanan/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default ListBulananLandingPage;
|
||||
@@ -1,186 +0,0 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Select, Stack, Text, TextInput } 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 EditListSurvey() {
|
||||
const editState = useProxy(indeksKepuasanState.genderStatState)
|
||||
const params = useParams()
|
||||
const router = useRouter()
|
||||
const [formData, setFormData] = useState({
|
||||
laki: editState.edit.form.laki || 0,
|
||||
perempuan: editState.edit.form.perempuan || 0,
|
||||
monthlyStatId: editState.edit.form.monthlyStatId || '',
|
||||
total: editState.edit.form.total || 0,
|
||||
percentLaki: editState.edit.form.percentLaki || 0,
|
||||
percentPerempuan: editState.edit.form.percentPerempuan || 0,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
indeksKepuasanState.monthlyStatState.findMany.load();
|
||||
const loadSurvey = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await editState.edit.load(id); // akses langsung, bukan dari proxy
|
||||
if (data) {
|
||||
setFormData({
|
||||
laki: data.laki || 0,
|
||||
perempuan: data.perempuan || 0,
|
||||
monthlyStatId: data.monthlyStatId || '',
|
||||
total: data.total || 0,
|
||||
percentLaki: data.percentLaki || 0,
|
||||
percentPerempuan: data.percentPerempuan || 0,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading list survey:", error);
|
||||
toast.error("Gagal memuat data list survey");
|
||||
}
|
||||
};
|
||||
loadSurvey();
|
||||
}, [params?.id]);
|
||||
|
||||
// Hitung total dan persentase saat nilai laki atau perempuan berubah
|
||||
useEffect(() => {
|
||||
const total = formData.laki + formData.perempuan;
|
||||
const percentLaki = total > 0 ? Math.round((formData.laki / total) * 100) : 0;
|
||||
const percentPerempuan = 100 - percentLaki;
|
||||
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
total,
|
||||
percentLaki,
|
||||
percentPerempuan
|
||||
}));
|
||||
}, [formData.laki, formData.perempuan]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.monthlyStatId) {
|
||||
return toast.error("Silakan pilih bulan terlebih dahulu");
|
||||
}
|
||||
|
||||
if (formData.laki < 0 || formData.perempuan < 0) {
|
||||
return toast.error("Nilai tidak boleh negatif");
|
||||
}
|
||||
|
||||
try {
|
||||
// edit global state with form data
|
||||
editState.edit.form = {
|
||||
...editState.edit.form,
|
||||
laki: formData.laki,
|
||||
perempuan: formData.perempuan,
|
||||
monthlyStatId: formData.monthlyStatId,
|
||||
total: formData.total,
|
||||
percentLaki: formData.percentLaki,
|
||||
percentPerempuan: formData.percentPerempuan,
|
||||
};
|
||||
|
||||
await editState.edit.update();
|
||||
toast.success("Data gender berhasil diperbarui!");
|
||||
router.push("/admin/landing-page/indeks-kepuasan-masyarakat/list-gender-stat");
|
||||
} catch (error) {
|
||||
console.error("Error updating list gender:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui data gender");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Edit List Survey</Text>
|
||||
<Paper>
|
||||
<Stack gap={"xs"}>
|
||||
<TextInput
|
||||
type="number"
|
||||
value={formData.laki}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
laki: Number(val.target.value)
|
||||
})
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Laki - Laki</Text>}
|
||||
placeholder='Masukkan laki - laki'
|
||||
/>
|
||||
<TextInput
|
||||
type="number"
|
||||
value={formData.perempuan}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
perempuan: Number(val.target.value)
|
||||
})
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Perempuan</Text>}
|
||||
placeholder='Masukkan perempuan'
|
||||
/>
|
||||
<TextInput
|
||||
type="number"
|
||||
value={formData.total}
|
||||
label={<Text fw="bold" fz="sm">Total</Text>}
|
||||
disabled
|
||||
/>
|
||||
<TextInput
|
||||
type="number"
|
||||
value={formData.percentLaki}
|
||||
label={<Text fw="bold" fz="sm">Persentase Laki Laki</Text>}
|
||||
disabled
|
||||
/>
|
||||
<TextInput
|
||||
type="number"
|
||||
value={formData.percentPerempuan}
|
||||
label={<Text fw="bold" fz="sm">Persentase Perempuan</Text>}
|
||||
disabled
|
||||
/>
|
||||
<Select
|
||||
label={"Pilih Bulan"}
|
||||
placeholder="Pilih bulanan"
|
||||
value={formData.monthlyStatId}
|
||||
onChange={(value) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
monthlyStatId: value || ''
|
||||
}));
|
||||
}}
|
||||
data={
|
||||
indeksKepuasanState.monthlyStatState.findMany.data?.map((monthlyStat) => ({
|
||||
value: monthlyStat.id,
|
||||
label: `${monthlyStat.month} (${monthlyStat.respondentsCount} responden)`,
|
||||
})) || []
|
||||
}
|
||||
required
|
||||
error={!formData.monthlyStatId ? 'Bulan harus dipilih' : undefined}
|
||||
/>
|
||||
<Group>
|
||||
<Button
|
||||
bg={colors['blue-button']}
|
||||
onClick={handleSubmit}
|
||||
loading={editState.edit.loading}
|
||||
>
|
||||
{editState.edit.loading ? 'Menyimpan...' : 'Simpan Perubahan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditListSurvey;
|
||||
@@ -1,113 +0,0 @@
|
||||
'use client'
|
||||
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function DetailGenderStat() {
|
||||
const detailState = useProxy(indeksKepuasanState.genderStatState)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const params = useParams()
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
detailState.findUnique.load(params?.id as string)
|
||||
}, [])
|
||||
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
detailState.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/landing-page/indeks-kepuasan-masyarakat/list-gender-stat")
|
||||
}
|
||||
}
|
||||
|
||||
if (!detailState.findUnique.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={40} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
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']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Detail List Survey</Text>
|
||||
{detailState.findUnique.data ? (
|
||||
<Paper key={detailState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Laki Laki</Text>
|
||||
<Text fz={"lg"}>{detailState.findUnique.data?.laki}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Perempuan</Text>
|
||||
<Text fz={"lg"}>{detailState.findUnique.data?.perempuan}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Persentase Laki Laki</Text>
|
||||
<Text fz={"lg"}>{detailState.findUnique.data?.percentLaki}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Persentase Perempuan</Text>
|
||||
<Text fz={"lg"}>{detailState.findUnique.data?.percentPerempuan}</Text>
|
||||
</Box>
|
||||
<Flex gap={"xs"} mt={10}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (detailState.findUnique.data) {
|
||||
setSelectedId(detailState.findUnique.data.id);
|
||||
setModalHapus(true);
|
||||
}
|
||||
}}
|
||||
disabled={detailState.delete.loading || !detailState.findUnique.data}
|
||||
color={"red"}
|
||||
>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (detailState.findUnique.data) {
|
||||
router.push(`/admin/landing-page/indeks-kepuasan-masyarakat/list-gender-stat/${detailState.findUnique.data.id}/edit`);
|
||||
}
|
||||
}}
|
||||
disabled={!detailState.findUnique.data}
|
||||
color={"green"}
|
||||
>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text='Apakah anda yakin ingin menghapus list gender stat ini?'
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailGenderStat;
|
||||
@@ -1,103 +0,0 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
|
||||
function CreateListGenderStat() {
|
||||
const router = useRouter();
|
||||
const stateCreate = useProxy(indeksKepuasanState.genderStatState)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
stateCreate.findMany.load();
|
||||
indeksKepuasanState.monthlyStatState.findMany.load();
|
||||
}, []);
|
||||
|
||||
const resetForm = () => {
|
||||
stateCreate.create.form = {
|
||||
laki: 0,
|
||||
perempuan: 0,
|
||||
monthlyStatId: "",
|
||||
total: 0,
|
||||
percentLaki: 0,
|
||||
percentPerempuan: 0,
|
||||
};
|
||||
};
|
||||
const handleSubmit = async () => {
|
||||
await stateCreate.create.create();
|
||||
resetForm();
|
||||
router.push("/admin/landing-page/indeks-kepuasan-masyarakat/list-gender-stat")
|
||||
}
|
||||
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 List Gender Stat</Title>
|
||||
<TextInput
|
||||
value={stateCreate.create.form.laki}
|
||||
onChange={(val) => {
|
||||
stateCreate.create.form.laki = Number(val.target.value);
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Laki Laki</Text>}
|
||||
placeholder='Masukkan laki laki'
|
||||
/>
|
||||
<TextInput
|
||||
value={stateCreate.create.form.perempuan}
|
||||
onChange={(val) => {
|
||||
stateCreate.create.form.perempuan = Number(val.target.value);
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Perempuan</Text>}
|
||||
placeholder='Masukkan perempuan'
|
||||
/>
|
||||
<TextInput
|
||||
value={stateCreate.create.form.total}
|
||||
label={<Text fw="bold" fz="sm">Total</Text>}
|
||||
disabled
|
||||
/>
|
||||
<TextInput
|
||||
value={stateCreate.create.form.percentLaki}
|
||||
label={<Text fw="bold" fz="sm">Persentase Laki Laki</Text>}
|
||||
disabled
|
||||
/>
|
||||
<TextInput
|
||||
value={stateCreate.create.form.percentPerempuan}
|
||||
label={<Text fw="bold" fz="sm">Persentase Perempuan</Text>}
|
||||
disabled
|
||||
/>
|
||||
<Select
|
||||
label={<Text fw="bold" fz="sm">Pilih Bulan</Text>}
|
||||
placeholder="Pilih bulanan"
|
||||
value={stateCreate.create.form.monthlyStatId}
|
||||
onChange={(value) => {
|
||||
if (value) stateCreate.create.form.monthlyStatId = value;
|
||||
}}
|
||||
data={
|
||||
indeksKepuasanState.monthlyStatState.findMany.data?.map((monthlyStat) => ({
|
||||
value: monthlyStat.id,
|
||||
label: `${monthlyStat.month} (${monthlyStat.respondentsCount} responden)`,
|
||||
})) || []
|
||||
}
|
||||
/>
|
||||
<Group>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateListGenderStat;
|
||||
@@ -1,106 +0,0 @@
|
||||
/* 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 { 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 indeksKepuasanState from '../../../_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
|
||||
|
||||
|
||||
function ListGenderStat() {
|
||||
const [search, setSearch] = useState('');
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Gender Stat'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListGender search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListGender({ search }: { search: string }) {
|
||||
const listState = useProxy(indeksKepuasanState.genderStatState)
|
||||
const router = useRouter();
|
||||
useEffect(() => {
|
||||
listState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (listState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.laki.toString().includes(keyword) ||
|
||||
item.perempuan.toString().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!listState.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 Gender Stat'
|
||||
href='/admin/landing-page/indeks-kepuasan-masyarakat/list-gender-stat/create'
|
||||
/>
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Laki Laki</TableTh>
|
||||
<TableTh>Perempuan</TableTh>
|
||||
<TableTh>Persentase Laki Laki</TableTh>
|
||||
<TableTh>Persentase Perempuan</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
<Text truncate="end" fz={"sm"}>{item.laki}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate="end" fz={"sm"}>{item.perempuan}</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate="end" fz={"sm"}>{item.percentLaki}%</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate="end" fz={"sm"}>{item.percentPerempuan}%</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/landing-page/indeks-kepuasan-masyarakat/list-gender-stat/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default ListGenderStat;
|
||||
@@ -1,122 +0,0 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, Text, TextInput } 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 EditListSurvey() {
|
||||
const editState = useProxy(indeksKepuasanState.surveyState)
|
||||
const params = useParams()
|
||||
const router = useRouter()
|
||||
const [formData, setFormData] = useState({
|
||||
title: editState.edit.form.title || '',
|
||||
totalRespondents: editState.edit.form.totalRespondents || 0,
|
||||
averageScore: editState.edit.form.averageScore || 0,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const loadSurvey = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await editState.edit.load(id); // akses langsung, bukan dari proxy
|
||||
if (data) {
|
||||
setFormData({
|
||||
title: data.title || '',
|
||||
totalRespondents: data.totalRespondents || 0,
|
||||
averageScore: data.averageScore || 0,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading list survey:", error);
|
||||
toast.error("Gagal memuat data list survey");
|
||||
}
|
||||
};
|
||||
|
||||
loadSurvey();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
|
||||
try {
|
||||
// edit global state with form data
|
||||
editState.edit.form = {
|
||||
...editState.edit.form,
|
||||
title: formData.title,
|
||||
totalRespondents: formData.totalRespondents,
|
||||
averageScore: formData.averageScore,
|
||||
};
|
||||
|
||||
await editState.edit.update();
|
||||
toast.success("list survey berhasil diperbarui!");
|
||||
router.push("/admin/landing-page/indeks-kepuasan-masyarakat/list-survey");
|
||||
} catch (error) {
|
||||
console.error("Error updating list survey:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui list survey");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Edit List Survey</Text>
|
||||
<Paper>
|
||||
<Stack gap={"xs"}>
|
||||
<TextInput
|
||||
value={formData.title}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
title: val.target.value
|
||||
})
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Judul</Text>}
|
||||
placeholder='Masukkan judul'
|
||||
/>
|
||||
<TextInput
|
||||
value={formData.totalRespondents}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
totalRespondents: Number(val.target.value)
|
||||
})
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Total Responden</Text>}
|
||||
placeholder='Masukkan total responden'
|
||||
/>
|
||||
<TextInput
|
||||
value={formData.averageScore}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
averageScore: Number(val.target.value)
|
||||
})
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Skor Rata-rata</Text>}
|
||||
placeholder='Masukkan skor'
|
||||
/>
|
||||
<Group>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditListSurvey;
|
||||
@@ -1,109 +0,0 @@
|
||||
'use client'
|
||||
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function DetailKegiatanDesa() {
|
||||
const detailState = useProxy(indeksKepuasanState.surveyState)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const params = useParams()
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
detailState.findUnique.load(params?.id as string)
|
||||
}, [])
|
||||
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
detailState.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/landing-page/indeks-kepuasan-masyarakat/list-survey")
|
||||
}
|
||||
}
|
||||
|
||||
if (!detailState.findUnique.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={40} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
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']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Detail List Survey</Text>
|
||||
{detailState.findUnique.data ? (
|
||||
<Paper key={detailState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Judul</Text>
|
||||
<Text fz={"lg"}>{detailState.findUnique.data?.title}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Total Responden</Text>
|
||||
<Text fz={"lg"}>{detailState.findUnique.data?.totalRespondents}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"lg"}>Rata-rata Skor</Text>
|
||||
<Text fz={"lg"}>{detailState.findUnique.data?.averageScore}</Text>
|
||||
</Box>
|
||||
<Flex gap={"xs"} mt={10}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (detailState.findUnique.data) {
|
||||
setSelectedId(detailState.findUnique.data.id);
|
||||
setModalHapus(true);
|
||||
}
|
||||
}}
|
||||
disabled={detailState.delete.loading || !detailState.findUnique.data}
|
||||
color={"red"}
|
||||
>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (detailState.findUnique.data) {
|
||||
router.push(`/admin/landing-page/indeks-kepuasan-masyarakat/list-survey/${detailState.findUnique.data.id}/edit`);
|
||||
}
|
||||
}}
|
||||
disabled={!detailState.findUnique.data}
|
||||
color={"green"}
|
||||
>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text='Apakah anda yakin ingin menghapus list survey ini?'
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailKegiatanDesa;
|
||||
@@ -1,78 +0,0 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
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 { useEffect } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
|
||||
function CreateListSurvey() {
|
||||
const router = useRouter();
|
||||
const stateCreate = useProxy(indeksKepuasanState.surveyState)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
stateCreate.findMany.load();
|
||||
}, []);
|
||||
|
||||
const resetForm = () => {
|
||||
stateCreate.create.form = {
|
||||
title: "",
|
||||
totalRespondents: 0,
|
||||
averageScore: 0,
|
||||
};
|
||||
};
|
||||
const handleSubmit = async () => {
|
||||
await stateCreate.create.create();
|
||||
resetForm();
|
||||
router.push("/admin/landing-page/indeks-kepuasan-masyarakat/list-survey")
|
||||
}
|
||||
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 List Survey</Title>
|
||||
<TextInput
|
||||
value={stateCreate.create.form.title}
|
||||
onChange={(val) => {
|
||||
stateCreate.create.form.title = val.target.value;
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Judul</Text>}
|
||||
placeholder='Masukkan judul'
|
||||
/>
|
||||
<TextInput
|
||||
value={stateCreate.create.form.totalRespondents}
|
||||
onChange={(val) => {
|
||||
stateCreate.create.form.totalRespondents = Number(val.target.value);
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Total Responden</Text>}
|
||||
placeholder='Masukkan tahun'
|
||||
/>
|
||||
<TextInput
|
||||
value={stateCreate.create.form.averageScore}
|
||||
onChange={(val) => {
|
||||
stateCreate.create.form.averageScore = Number(val.target.value);
|
||||
}}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Skor</Text>}
|
||||
placeholder='Masukkan skor'
|
||||
/>
|
||||
<Group>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateListSurvey;
|
||||
@@ -1,103 +0,0 @@
|
||||
/* 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 { 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 indeksKepuasanState from '../../../_state/landing-page/indeks-kepuasan-masyarakat';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
|
||||
|
||||
|
||||
function ListSurveyLandingPage() {
|
||||
const [search, setSearch] = useState('');
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='List Survey'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListSurvey search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListSurvey({ search }: { search: string }) {
|
||||
const listState = useProxy(indeksKepuasanState.surveyState)
|
||||
const router = useRouter();
|
||||
useEffect(() => {
|
||||
listState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (listState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.title.toLowerCase().includes(keyword) ||
|
||||
item.totalRespondents.toString().includes(keyword) ||
|
||||
item.averageScore.toString().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!listState.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 Survey'
|
||||
href='/admin/landing-page/indeks-kepuasan-masyarakat/list-survey/create'
|
||||
/>
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Judul</TableTh>
|
||||
<TableTh>Total Responden</TableTh>
|
||||
<TableTh>Skor Rata-rata</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
<Text truncate="end" fz={"sm"}>{item.title}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate="end" fz={"sm"}>{item.totalRespondents}</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate="end" fz={"sm"}>{item.averageScore}</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/landing-page/indeks-kepuasan-masyarakat/list-survey/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default ListSurveyLandingPage;
|
||||
@@ -103,12 +103,12 @@ export const navBar = [
|
||||
{
|
||||
id: "Desa_3",
|
||||
name: "Berita",
|
||||
path: "/admin/desa/berita"
|
||||
path: "/admin/desa/berita/list-berita"
|
||||
},
|
||||
{
|
||||
id: "Desa_4",
|
||||
name: "Pengumuman",
|
||||
path: "/admin/desa/pengumuman"
|
||||
path: "/admin/desa/pengumuman/list-pengumuman"
|
||||
},
|
||||
{
|
||||
id: "Desa_5",
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
async function kategoriBeritaFindMany() {
|
||||
const data = await prisma.kategoriBerita.findMany();
|
||||
return { data };
|
||||
}
|
||||
|
||||
export default kategoriBeritaFindMany
|
||||
|
||||
@@ -1,44 +1,71 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
async function beritaFindMany(context: Context) {
|
||||
// Ambil parameter dari query
|
||||
const page = Number(context.query.page) || 1;
|
||||
const limit = Number(context.query.limit) || 10;
|
||||
const search = (context.query.search as string) || '';
|
||||
const kategori = (context.query.kategori as string) || ''; // 🔥 Parameter kategori baru
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
// Buat where clause
|
||||
const where: any = { isActive: true };
|
||||
|
||||
// Filter berdasarkan kategori (jika ada)
|
||||
if (kategori) {
|
||||
where.kategoriBerita = {
|
||||
name: {
|
||||
equals: kategori,
|
||||
mode: 'insensitive' // Tidak case-sensitive
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Tambahkan pencarian (jika ada)
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ judul: { contains: search, mode: 'insensitive' } },
|
||||
{ deskripsi: { contains: search, mode: 'insensitive' } },
|
||||
{ content: { contains: search, mode: 'insensitive' } },
|
||||
{ kategoriBerita: { name: { contains: search, mode: 'insensitive' } } }
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// Ambil data dan total count secara paralel
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.berita.findMany({
|
||||
where: { isActive: true },
|
||||
where,
|
||||
include: {
|
||||
image: true,
|
||||
kategoriBerita: true,
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' }, // opsional, kalau mau urut berdasarkan waktu
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma.berita.count({
|
||||
where: { isActive: true }
|
||||
})
|
||||
prisma.berita.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success fetch berita with pagination",
|
||||
message: "Berhasil ambil berita dengan pagination",
|
||||
data,
|
||||
page,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
limit,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Find many paginated error:", e);
|
||||
console.error("Error di findMany paginated:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed fetch berita with pagination",
|
||||
message: "Gagal mengambil data berita",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default beritaFindMany;
|
||||
export default beritaFindMany;
|
||||
@@ -1,30 +1,41 @@
|
||||
import prisma from '@/lib/prisma';
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// find-first.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
async function beritaFindFirst(context: Context) {
|
||||
const kategori = (context.query.kategori as string) || '';
|
||||
|
||||
const where: any = { isActive: true };
|
||||
|
||||
if (kategori) {
|
||||
where.kategoriBerita = {
|
||||
name: { equals: kategori, mode: 'insensitive' }
|
||||
};
|
||||
}
|
||||
|
||||
export default async function beritaFindFirst() {
|
||||
try {
|
||||
const result = await prisma.berita.findFirst({
|
||||
where: {
|
||||
isActive: true, // opsional kalau kamu punya field ini
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc', // ambil yang paling terbaru
|
||||
},
|
||||
const data = await prisma.berita.findFirst({
|
||||
where,
|
||||
include: {
|
||||
image: true,
|
||||
kategoriBerita: true,
|
||||
}
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Berhasil ambil berita terbaru',
|
||||
data: result,
|
||||
message: "Berhasil ambil berita terbaru",
|
||||
data,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[findFirstBerita] Error:', error);
|
||||
} catch (e) {
|
||||
console.error("Error di findFirst:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: 'Gagal ambil berita terbaru',
|
||||
message: "Gagal ambil berita terbaru",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default beritaFindFirst;
|
||||
14
src/app/api/[[...slugs]]/_lib/desa/berita/findManyUI.ts
Normal file
14
src/app/api/[[...slugs]]/_lib/desa/berita/findManyUI.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export default async function findManyUI() {
|
||||
const data = await prisma.berita.findMany({
|
||||
include: {
|
||||
image: true,
|
||||
kategoriBerita: true,
|
||||
},
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
data,
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import kategoriBeritaFindMany from "./category";
|
||||
import beritaFindMany from "./find-many";
|
||||
import beritaCreate from "./create";
|
||||
import beritaDelete from "./del";
|
||||
@@ -9,8 +8,8 @@ import beritaFindFirst from "./findFirst";
|
||||
import findRecentBerita from "./findRecent";
|
||||
|
||||
const Berita = new Elysia({ prefix: "/berita", tags: ["Desa/Berita"] })
|
||||
.get("/category/find-many", kategoriBeritaFindMany)
|
||||
.get("/find-many", beritaFindMany)
|
||||
.get("/find-many-ui", beritaFindMany)
|
||||
.get("/:id", async (context) => {
|
||||
const response = await findBeritaById(new Request(context.request));
|
||||
return response;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormCreate = {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default async function kategoriBeritaCreate(context: Context) {
|
||||
const body = (await context.body) as FormCreate;
|
||||
|
||||
try {
|
||||
const result = await prisma.kategoriBerita.create({
|
||||
data: {
|
||||
name: body.name,
|
||||
},
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil membuat kategori berita",
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error creating kategori berita:", error);
|
||||
throw new Error("Gagal membuat kategori berita: " + (error as Error).message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function kategoriBeritaDelete(context: Context) {
|
||||
const id = context.params.id as string;
|
||||
|
||||
await prisma.kategoriBerita.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
success: true,
|
||||
message: "Success delete kategori berita",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export default async function kategoriBeritaFindMany() {
|
||||
const data = await prisma.kategoriBerita.findMany();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success get all kategori berita",
|
||||
data,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export default async function kategoriBeritaFindUnique(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.kategoriBerita.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Data not found",
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success get kategori berita",
|
||||
data,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Find by ID error:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import kategoriBeritaCreate from "./create";
|
||||
import kategoriBeritaDelete from "./del";
|
||||
import kategoriBeritaFindMany from "./findMany";
|
||||
import kategoriBeritaFindUnique from "./findUnique";
|
||||
import kategoriBeritaUpdate from "./updt";
|
||||
|
||||
const KategoriBerita = new Elysia({
|
||||
prefix: "/kategoriberita",
|
||||
tags: ["Desa / Berita / Kategori Berita"],
|
||||
})
|
||||
|
||||
.post("/create", kategoriBeritaCreate, {
|
||||
body: t.Object({
|
||||
name: t.String(),
|
||||
}),
|
||||
})
|
||||
|
||||
.get("/findMany", kategoriBeritaFindMany)
|
||||
.get("/:id", async (context) => {
|
||||
const response = await kategoriBeritaFindUnique(
|
||||
new Request(context.request)
|
||||
);
|
||||
return response;
|
||||
})
|
||||
.put("/:id", kategoriBeritaUpdate, {
|
||||
body: t.Object({
|
||||
name: t.String(),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", kategoriBeritaDelete);
|
||||
|
||||
export default KategoriBerita;
|
||||
@@ -0,0 +1,28 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormUpdate = {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default async function kategoriBeritaUpdate(context: Context) {
|
||||
const body = (await context.body) as FormUpdate;
|
||||
const id = context.params.id as string;
|
||||
|
||||
try {
|
||||
const result = await prisma.kategoriBerita.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: body.name,
|
||||
},
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil mengupdate kategori berita",
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error updating kategori berita:", error);
|
||||
throw new Error("Gagal mengupdate kategori berita: " + (error as Error).message);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import GalleryVideo from "./gallery/video";
|
||||
import LayananDesa from "./layanan";
|
||||
import Penghargaan from "./penghargaan";
|
||||
import KategoriPotensi from "./potensi/kategori-potensi";
|
||||
import KategoriBerita from "./berita/kategori-berita";
|
||||
import KategoriPengumuman from "./pengumuman/kategori-pengumuman";
|
||||
|
||||
|
||||
const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
|
||||
@@ -20,5 +22,7 @@ const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
|
||||
.use(LayananDesa)
|
||||
.use(Penghargaan)
|
||||
.use(KategoriPotensi)
|
||||
.use(KategoriBerita)
|
||||
.use(KategoriPengumuman)
|
||||
|
||||
export default Desa;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,70 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
async function pengumumanFindMany(context: Context) {
|
||||
// Ambil parameter dari query
|
||||
const page = Number(context.query.page) || 1;
|
||||
const limit = Number(context.query.limit) || 10;
|
||||
const search = (context.query.search as string) || '';
|
||||
const kategori = (context.query.kategori as string) || ''; // 🔥 Parameter kategori baru
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
// Buat where clause
|
||||
const where: any = { isActive: true };
|
||||
|
||||
// Filter berdasarkan kategori (jika ada)
|
||||
if (kategori) {
|
||||
where.CategoryPengumuman = {
|
||||
name: {
|
||||
equals: kategori,
|
||||
mode: 'insensitive' // Tidak case-sensitive
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Tambahkan pencarian (jika ada)
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ judul: { contains: search, mode: 'insensitive' } },
|
||||
{ deskripsi: { contains: search, mode: 'insensitive' } },
|
||||
{ content: { contains: search, mode: 'insensitive' } },
|
||||
{ CategoryPengumuman: { name: { contains: search, mode: 'insensitive' } } }
|
||||
];
|
||||
}
|
||||
|
||||
export default async function pengumumanFindMany() {
|
||||
try {
|
||||
const data = await prisma.pengumuman.findMany({
|
||||
where: { isActive: true },
|
||||
include: {
|
||||
CategoryPengumuman: true,
|
||||
},
|
||||
});
|
||||
// Ambil data dan total count secara paralel
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.pengumuman.findMany({
|
||||
where,
|
||||
include: {
|
||||
CategoryPengumuman: true,
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}),
|
||||
prisma.pengumuman.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success fetch pengumuman",
|
||||
message: "Berhasil ambil pengumuman dengan pagination",
|
||||
data,
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Find many error:", e);
|
||||
console.error("Error di findMany paginated:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed fetch pengumuman",
|
||||
message: "Gagal mengambil data pengumuman",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default pengumumanFindMany;
|
||||
@@ -1,19 +1,16 @@
|
||||
import Elysia from "elysia";
|
||||
import Elysia, { t } from "elysia";
|
||||
import { pengumumanCreate } from "./create";
|
||||
import pengumumanFindMany from "./find-many";
|
||||
import { t } from "elysia";
|
||||
import pengumumanCategoryFindMany from "./category";
|
||||
import pengumumanDelete from "./del";
|
||||
import pengumumanFindById from "./find-by-id";
|
||||
import pengumumanUpdate from "./updt";
|
||||
import pengumumanFindMany from "./find-many";
|
||||
import pengumumanFindFirst from "./findFirst";
|
||||
import pengumumanFindRecent from "./findRecent";
|
||||
import pengumumanUpdate from "./updt";
|
||||
|
||||
const Pengumuman = new Elysia({ prefix: "/pengumuman", tags: ["Desa/Pengumuman"] })
|
||||
.get("/category/find-many", pengumumanCategoryFindMany)
|
||||
.get("/find-many", pengumumanFindMany)
|
||||
.get("/:id", pengumumanFindById)
|
||||
.delete("/delete/:id", pengumumanDelete)
|
||||
.delete("/del/:id", pengumumanDelete)
|
||||
.post("/create", pengumumanCreate, {
|
||||
body: t.Object({
|
||||
judul: t.String(),
|
||||
@@ -26,7 +23,6 @@ const Pengumuman = new Elysia({ prefix: "/pengumuman", tags: ["Desa/Pengumuman"]
|
||||
.get("/find-recent", pengumumanFindRecent)
|
||||
.put("/:id", pengumumanUpdate, {
|
||||
body: t.Object({
|
||||
id: t.String(),
|
||||
judul: t.String(),
|
||||
deskripsi: t.String(),
|
||||
content: t.String(),
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormCreate = {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default async function kategoriPengumumanCreate(context: Context) {
|
||||
const body = (await context.body) as FormCreate;
|
||||
|
||||
try {
|
||||
const result = await prisma.categoryPengumuman.create({
|
||||
data: {
|
||||
name: body.name,
|
||||
},
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil membuat kategori pengumuman",
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error creating kategori pengumuman:", error);
|
||||
throw new Error("Gagal membuat kategori pengumuman: " + (error as Error).message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function kategoriPengumumanDelete(context: Context) {
|
||||
const id = context.params.id as string;
|
||||
|
||||
await prisma.categoryPengumuman.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
success: true,
|
||||
message: "Success delete kategori pengumuman",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
async function kategoriPengumumanFindMany() {
|
||||
const data = await prisma.categoryPengumuman.findMany({
|
||||
include: {
|
||||
_count: {
|
||||
select: {
|
||||
pengumumans: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return { data };
|
||||
}
|
||||
|
||||
export default kategoriPengumumanFindMany
|
||||
@@ -0,0 +1,46 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export default async function kategoriPengumumanFindUnique(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.categoryPengumuman.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Data not found",
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success get kategori pengumuman",
|
||||
data,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Find by ID error:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import kategoriPengumumanCreate from "./create";
|
||||
import kategoriPengumumanDelete from "./del";
|
||||
import kategoriPengumumanFindMany from "./findMany";
|
||||
import kategoriPengumumanFindUnique from "./findUnique";
|
||||
import kategoriPengumumanUpdate from "./updt";
|
||||
|
||||
const KategoriPengumuman = new Elysia({
|
||||
prefix: "/kategoripengumuman",
|
||||
tags: ["Desa / Pengumuman / Kategori Pengumuman"],
|
||||
})
|
||||
|
||||
.post("/create", kategoriPengumumanCreate, {
|
||||
body: t.Object({
|
||||
name: t.String(),
|
||||
}),
|
||||
})
|
||||
|
||||
.get("/findMany", kategoriPengumumanFindMany)
|
||||
.get("/:id", async (context) => {
|
||||
const response = await kategoriPengumumanFindUnique(
|
||||
new Request(context.request)
|
||||
);
|
||||
return response;
|
||||
})
|
||||
.put("/:id", kategoriPengumumanUpdate, {
|
||||
body: t.Object({
|
||||
name: t.String(),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", kategoriPengumumanDelete);
|
||||
|
||||
export default KategoriPengumuman;
|
||||
@@ -0,0 +1,28 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormUpdate = {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default async function kategoriPengumumanUpdate(context: Context) {
|
||||
const body = (await context.body) as FormUpdate;
|
||||
const id = context.params.id as string;
|
||||
|
||||
try {
|
||||
const result = await prisma.categoryPengumuman.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: body.name,
|
||||
},
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil mengupdate kategori pengumuman",
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error updating kategori pengumuman:", error);
|
||||
throw new Error("Gagal mengupdate kategori pengumuman: " + (error as Error).message);
|
||||
}
|
||||
}
|
||||
@@ -3,37 +3,75 @@ import { Prisma } from "@prisma/client";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormUpdate = Prisma.PengumumanGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
content: true;
|
||||
categoryPengumumanId: true;
|
||||
imageId: true;
|
||||
};
|
||||
select: {
|
||||
id: true;
|
||||
judul: true;
|
||||
deskripsi: true;
|
||||
content: true;
|
||||
categoryPengumumanId: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
async function pengumumanUpdate(context: Context) {
|
||||
const body = context.body as FormUpdate;
|
||||
|
||||
await prisma.pengumuman.update({
|
||||
where: { id: body.id },
|
||||
data: {
|
||||
judul: body.judul,
|
||||
deskripsi: body.deskripsi,
|
||||
content: body.content,
|
||||
categoryPengumumanId: body.categoryPengumumanId,
|
||||
},
|
||||
try {
|
||||
const id = context.params?.id as string; // ambil dari URL
|
||||
const body = (await context.body) as Omit<FormUpdate, "id">;
|
||||
|
||||
const {
|
||||
judul,
|
||||
deskripsi,
|
||||
content,
|
||||
categoryPengumumanId,
|
||||
} = body;
|
||||
|
||||
if (!id) {
|
||||
return new Response(
|
||||
JSON.stringify({ success: false, message: "ID tidak boleh kosong" }),
|
||||
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
const existing = await prisma.pengumuman.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
CategoryPengumuman: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
if (!existing) {
|
||||
return new Response(
|
||||
JSON.stringify({ success: false, message: "pengumuman tidak ditemukan" }),
|
||||
{ status: 404, headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
const updated = await prisma.pengumuman.update({
|
||||
where: { id },
|
||||
data: {
|
||||
judul,
|
||||
deskripsi,
|
||||
content,
|
||||
categoryPengumumanId: categoryPengumumanId || null,
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: "Success update pengumuman",
|
||||
data: {
|
||||
...body,
|
||||
},
|
||||
};
|
||||
message: "pengumuman berhasil diupdate",
|
||||
data: updated,
|
||||
}),
|
||||
{ status: 200, headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error updating pengumuman:", error);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat mengupdate pengumuman",
|
||||
}),
|
||||
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default pengumumanUpdate;
|
||||
|
||||
export default pengumumanUpdate;
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const ageStatCreate = async (context: Context) => {
|
||||
const body = (await context.body) as {
|
||||
group: string;
|
||||
count: number;
|
||||
monthlyStatId: string;
|
||||
};
|
||||
|
||||
try {
|
||||
const created = await prisma.ageStat.create({
|
||||
data: {
|
||||
group: body.group,
|
||||
count: body.count,
|
||||
monthlyStatId: body.monthlyStatId,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Age Stat berhasil dibuat",
|
||||
data: created,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal create age stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal membuat data age stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default ageStatCreate;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const ageStatDelete = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
await prisma.ageStat.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Data age berhasil dihapus",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal hapus Age Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal menghapus data age stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default ageStatDelete;
|
||||
@@ -1,26 +0,0 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const ageStatFindMany = async () => {
|
||||
try {
|
||||
const result = await prisma.ageStat.findMany({
|
||||
include: {
|
||||
MonthlyStat: true,
|
||||
},
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal ambil data Age Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data age stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default ageStatFindMany;
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const ageStatFindUnique = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
const result = await prisma.ageStat.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
MonthlyStat: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Data age tidak ditemukan",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal ambil data Age Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat mengambil data age stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default ageStatFindUnique;
|
||||
@@ -1,31 +0,0 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import ageStatCreate from "./create";
|
||||
import ageStatDelete from "./del";
|
||||
import ageStatFindMany from "./findMany";
|
||||
import ageStatFindUnique from "./findUnique";
|
||||
import ageStatUpdate from "./updt";
|
||||
|
||||
|
||||
const age = new Elysia({
|
||||
prefix: "/age",
|
||||
tags: ["Landing Page/Indeks Kepuasan Masyarakat/Age"],
|
||||
})
|
||||
.post("/create", ageStatCreate, {
|
||||
body: t.Object({
|
||||
monthlyStatId: t.String(),
|
||||
group: t.String(),
|
||||
count: t.Number(),
|
||||
}),
|
||||
})
|
||||
.get("/findMany", ageStatFindMany)
|
||||
.get("/:id", ageStatFindUnique)
|
||||
.put("/:id", ageStatUpdate, {
|
||||
body: t.Object({
|
||||
group: t.String(),
|
||||
count: t.Number(),
|
||||
monthlyStatId: t.String(),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", ageStatDelete);
|
||||
|
||||
export default age;
|
||||
@@ -1,35 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const ageStatUpdate = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
const body = (await context.body) as {
|
||||
group?: string;
|
||||
count?: number;
|
||||
monthlyStatId?: string;
|
||||
};
|
||||
|
||||
try {
|
||||
const updated = await prisma.ageStat.update({
|
||||
where: { id },
|
||||
data: {
|
||||
group: body.group,
|
||||
count: body.count,
|
||||
monthlyStatId: body.monthlyStatId,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Data age berhasil diperbarui",
|
||||
data: updated,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal update Age Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal memperbarui data age stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default ageStatUpdate;
|
||||
@@ -1,40 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const genderStatCreate = async (context: Context) => {
|
||||
const body = (await context.body) as {
|
||||
laki: number;
|
||||
perempuan: number;
|
||||
monthlyStatId: string;
|
||||
};
|
||||
|
||||
const total = body.laki + body.perempuan;
|
||||
const percentLaki = total > 0 ? Number(((body.laki / total) * 100).toFixed(2)) : 0;
|
||||
const percentPerempuan = total > 0 ? Number(((body.perempuan / total) * 100).toFixed(2)) : 0;
|
||||
|
||||
try {
|
||||
const created = await prisma.genderStat.create({
|
||||
data: {
|
||||
laki: body.laki,
|
||||
perempuan: body.perempuan,
|
||||
total,
|
||||
percentLaki: Number(percentLaki),
|
||||
percentPerempuan: Number(percentPerempuan),
|
||||
monthlyStatId: body.monthlyStatId,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Gender Stat berhasil dibuat",
|
||||
data: created,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal create gender stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal membuat data gender stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default genderStatCreate;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const genderStatDelete = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
await prisma.genderStat.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Data gender berhasil dihapus",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal hapus Gender Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal menghapus data gender",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default genderStatDelete;
|
||||
@@ -1,26 +0,0 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const genderStatFindMany = async () => {
|
||||
try {
|
||||
const result = await prisma.genderStat.findMany({
|
||||
include: {
|
||||
MonthlyStat: true,
|
||||
},
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal ambil data Gender Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data gender stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default genderStatFindMany;
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const genderStatFindUnique = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
const result = await prisma.genderStat.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
MonthlyStat: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Data gender tidak ditemukan",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal ambil data Gender Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat mengambil data gender stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default genderStatFindUnique;
|
||||
@@ -1,37 +0,0 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import genderStatCreate from "./create";
|
||||
import genderStatDelete from "./del";
|
||||
import genderStatFindMany from "./findMany";
|
||||
import genderStatFindUnique from "./findUnique";
|
||||
import genderStatUpdate from "./updt";
|
||||
|
||||
|
||||
const gender = new Elysia({
|
||||
prefix: "/gender",
|
||||
tags: ["Landing Page/Indeks Kepuasan Masyarakat/Gender"],
|
||||
})
|
||||
.post("/create", genderStatCreate, {
|
||||
body: t.Object({
|
||||
monthlyStatId: t.String(),
|
||||
laki: t.Number(),
|
||||
perempuan: t.Number(),
|
||||
total: t.Number(),
|
||||
percentLaki: t.Number(),
|
||||
percentPerempuan: t.Number(),
|
||||
}),
|
||||
})
|
||||
.get("/findMany", genderStatFindMany)
|
||||
.get("/:id", genderStatFindUnique)
|
||||
.put("/:id", genderStatUpdate, {
|
||||
body: t.Object({
|
||||
laki: t.Number(),
|
||||
perempuan: t.Number(),
|
||||
monthlyStatId: t.String(),
|
||||
total: t.Number(),
|
||||
percentLaki: t.Number(),
|
||||
percentPerempuan: t.Number(),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", genderStatDelete);
|
||||
|
||||
export default gender;
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const genderStatUpdate = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
const body = (await context.body) as {
|
||||
laki?: number;
|
||||
perempuan?: number;
|
||||
monthlyStatId?: string;
|
||||
};
|
||||
|
||||
const total = (body.laki ?? 0) + (body.perempuan ?? 0);
|
||||
const percentLaki = total > 0 ? Number(((body.laki ?? 0) / total) * 100).toFixed(2) : 0;
|
||||
const percentPerempuan = total > 0 ? Number(((body.perempuan ?? 0) / total) * 100).toFixed(2) : 0;
|
||||
|
||||
try {
|
||||
const updated = await prisma.genderStat.update({
|
||||
where: { id },
|
||||
data: {
|
||||
laki: body.laki,
|
||||
perempuan: body.perempuan,
|
||||
total,
|
||||
percentLaki: Number(percentLaki),
|
||||
percentPerempuan: Number(percentPerempuan),
|
||||
monthlyStatId: body.monthlyStatId,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Data gender berhasil diperbarui",
|
||||
data: updated,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal update Gender Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal memperbarui data gender stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default genderStatUpdate;
|
||||
@@ -1,20 +0,0 @@
|
||||
import Elysia from "elysia";
|
||||
import gender from "./gender";
|
||||
import monthlyStat from "./monthly-stat";
|
||||
import survey from "./survey";
|
||||
import response from "./response";
|
||||
import age from "./age";
|
||||
|
||||
|
||||
const IndeksKepuasanMasyarakat = new Elysia({
|
||||
prefix: "/indekskepuasanmasyarakat",
|
||||
tags: ["Landing Page/Profile/Indeks Kepuasan Masyarakat"],
|
||||
})
|
||||
.use(gender)
|
||||
.use(monthlyStat)
|
||||
.use(survey)
|
||||
.use(response)
|
||||
.use(age)
|
||||
|
||||
|
||||
export default IndeksKepuasanMasyarakat
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const createMonthlyStat = async (context: Context) => {
|
||||
const body = await context.body as {
|
||||
month: string;
|
||||
respondentsCount: number;
|
||||
surveyId: string;
|
||||
};
|
||||
|
||||
try {
|
||||
const created = await prisma.monthlyStat.create({
|
||||
data: {
|
||||
month: body.month,
|
||||
respondentsCount: body.respondentsCount,
|
||||
surveyId: body.surveyId,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Monthly stat berhasil dibuat",
|
||||
data: created,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal create monthly stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal membuat monthly stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default createMonthlyStat;
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const deleteMonthlyStat = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
await prisma.monthlyStat.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Data statistik bulanan berhasil dihapus",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal hapus MonthlyStat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal menghapus data statistik bulanan",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default deleteMonthlyStat;
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const findManyMonthlyStat = async () => {
|
||||
try {
|
||||
const result = await prisma.monthlyStat.findMany({
|
||||
include: {
|
||||
survey: true,
|
||||
},
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal ambil data MonthlyStat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data statistik bulanan",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default findManyMonthlyStat;
|
||||
@@ -1,35 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const findUniqueMonthlyStat = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
const result = await prisma.monthlyStat.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
survey: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Data tidak ditemukan",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal ambil data MonthlyStat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat mengambil data",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default findUniqueMonthlyStat;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import createMonthlyStat from "./create";
|
||||
import deleteMonthlyStat from "./del";
|
||||
import findManyMonthlyStat from "./findMany";
|
||||
import findUniqueMonthlyStat from "./findUnique";
|
||||
import updateMonthlyStat from "./updt";
|
||||
|
||||
|
||||
const surveyResponse = new Elysia({
|
||||
prefix: "/monthlystat",
|
||||
tags: ["Landing Page/Indeks Kepuasan Masyarakat/Survey Response"],
|
||||
})
|
||||
.post("/create", createMonthlyStat, {
|
||||
body: t.Object({
|
||||
month: t.String(),
|
||||
respondentsCount: t.Number(),
|
||||
surveyId: t.String(),
|
||||
}),
|
||||
})
|
||||
.get("/findMany", findManyMonthlyStat)
|
||||
.get("/:id", findUniqueMonthlyStat)
|
||||
.put("/:id", updateMonthlyStat, {
|
||||
body: t.Object({
|
||||
month: t.String(),
|
||||
respondentsCount: t.Number(),
|
||||
surveyId: t.String(),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", deleteMonthlyStat);
|
||||
|
||||
export default surveyResponse;
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const updateMonthlyStat = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
const body = await context.body as {
|
||||
month?: string;
|
||||
respondentsCount?: number;
|
||||
};
|
||||
|
||||
try {
|
||||
const updated = await prisma.monthlyStat.update({
|
||||
where: { id },
|
||||
data: {
|
||||
month: body.month,
|
||||
respondentsCount: body.respondentsCount,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Data MonthlyStat berhasil diupdate",
|
||||
data: updated,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal update MonthlyStat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengupdate data statistik bulanan",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default updateMonthlyStat;
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const responseStatCreate = async (context: Context) => {
|
||||
const body = (await context.body) as {
|
||||
label: string;
|
||||
count: number;
|
||||
monthlyStatId: string;
|
||||
};
|
||||
|
||||
try {
|
||||
const created = await prisma.responseStat.create({
|
||||
data: {
|
||||
label: body.label,
|
||||
count: body.count,
|
||||
monthlyStatId: body.monthlyStatId,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Response Stat berhasil dibuat",
|
||||
data: created,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal create response stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal membuat data response stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default responseStatCreate;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const responseStatDelete = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
await prisma.responseStat.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Data response berhasil dihapus",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal hapus Response Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal menghapus data response stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default responseStatDelete;
|
||||
@@ -1,26 +0,0 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const responseStatFindMany = async () => {
|
||||
try {
|
||||
const result = await prisma.responseStat.findMany({
|
||||
include: {
|
||||
MonthlyStat: true,
|
||||
},
|
||||
orderBy: {
|
||||
id: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal ambil data Response Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data response stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default responseStatFindMany;
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const responseStatFindUnique = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
const result = await prisma.responseStat.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
MonthlyStat: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Data response tidak ditemukan",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal ambil data Response Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat mengambil data response stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default responseStatFindUnique;
|
||||
@@ -1,31 +0,0 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import responseStatCreate from "./create";
|
||||
import responseStatDelete from "./del";
|
||||
import responseStatFindMany from "./findMany";
|
||||
import responseStatFindUnique from "./findUnique";
|
||||
import responseStatUpdate from "./updt";
|
||||
|
||||
|
||||
const response = new Elysia({
|
||||
prefix: "/response",
|
||||
tags: ["Landing Page/Indeks Kepuasan Masyarakat/Response"],
|
||||
})
|
||||
.post("/create", responseStatCreate, {
|
||||
body: t.Object({
|
||||
monthlyStatId: t.String(),
|
||||
label: t.String(),
|
||||
count: t.Number(),
|
||||
}),
|
||||
})
|
||||
.get("/findMany", responseStatFindMany)
|
||||
.get("/:id", responseStatFindUnique)
|
||||
.put("/:id", responseStatUpdate, {
|
||||
body: t.Object({
|
||||
label: t.String(),
|
||||
count: t.Number(),
|
||||
monthlyStatId: t.String(),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", responseStatDelete);
|
||||
|
||||
export default response;
|
||||
@@ -1,35 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const responseStatUpdate = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
const body = (await context.body) as {
|
||||
label?: string;
|
||||
count?: number;
|
||||
monthlyStatId?: string;
|
||||
};
|
||||
|
||||
try {
|
||||
const updated = await prisma.responseStat.update({
|
||||
where: { id },
|
||||
data: {
|
||||
label: body.label,
|
||||
count: body.count,
|
||||
monthlyStatId: body.monthlyStatId,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Data response berhasil diperbarui",
|
||||
data: updated,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal update Response Stat:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal memperbarui data response stat",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default responseStatUpdate;
|
||||
@@ -1,42 +0,0 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
const surveyCreate = async (context: Context) => {
|
||||
const body = await context.body as {
|
||||
title: string;
|
||||
totalRespondents: number;
|
||||
averageScore: number;
|
||||
};
|
||||
|
||||
// Validasi
|
||||
if (!body.title || !body.totalRespondents || body.averageScore === undefined) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Field title, tahun, dan skor wajib diisi",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const created = await prisma.survey.create({
|
||||
data: {
|
||||
title: body.title,
|
||||
totalRespondents: body.totalRespondents,
|
||||
averageScore: body.averageScore,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Survey berhasil dibuat",
|
||||
data: created,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal create survey :", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat membuat survey ",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default surveyCreate;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const surveyDelete = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
await prisma.survey.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Survey berhasil dihapus",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal hapus survey:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal menghapus survey",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default surveyDelete;
|
||||
@@ -1,28 +0,0 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const surveyFindMany = async () => {
|
||||
try {
|
||||
const surveys = await prisma.survey.findMany({
|
||||
include: {
|
||||
monthlyStats: true,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: surveys,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal ambil data survey:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat mengambil data survey",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default surveyFindMany;
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Context } from "elysia";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export const surveyFindUnique = async (context: Context) => {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
const survey = await prisma.survey.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
monthlyStats: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!survey) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Survey tidak ditemukan",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: survey,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal ambil data survey:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat mengambil data survey",
|
||||
};
|
||||
}
|
||||
};
|
||||
export default surveyFindUnique;
|
||||
@@ -1,30 +0,0 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import surveyCreate from "./create";
|
||||
import surveyUpdate from "./updt";
|
||||
import surveyFindMany from "./findMany";
|
||||
import surveyFindUnique from "./findUnique";
|
||||
import surveyDelete from "./del";
|
||||
|
||||
const survey = new Elysia({
|
||||
prefix: "/survey",
|
||||
tags: ["Landing Page/Indeks Kepuasan Masyarakat/Survey "],
|
||||
})
|
||||
.post("/create", surveyCreate, {
|
||||
body: t.Object({
|
||||
title: t.String(),
|
||||
totalRespondents: t.Number(),
|
||||
averageScore: t.Number(),
|
||||
}),
|
||||
})
|
||||
.get("/findMany", surveyFindMany)
|
||||
.get("/:id", surveyFindUnique)
|
||||
.put("/:id", surveyUpdate, {
|
||||
body: t.Object({
|
||||
title: t.String(),
|
||||
totalRespondents: t.Number(),
|
||||
averageScore: t.Number(),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", surveyDelete);
|
||||
|
||||
export default survey;
|
||||
@@ -1,49 +0,0 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormUpdateSurvey = {
|
||||
title?: string;
|
||||
tahun?: number;
|
||||
totalRespondents?: number;
|
||||
averageScore?: number;
|
||||
};
|
||||
|
||||
const surveyUpdate = async (context: Context) => {
|
||||
const id = context.params?.id as string;
|
||||
const body = await context.body as FormUpdateSurvey;
|
||||
|
||||
if (!id) {
|
||||
return {
|
||||
success: false,
|
||||
message: "ID survey wajib diisi",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const updated = await prisma.survey.update({
|
||||
where: { id },
|
||||
data: {
|
||||
title: body.title,
|
||||
totalRespondents: body.totalRespondents,
|
||||
averageScore: body.averageScore,
|
||||
},
|
||||
include: {
|
||||
monthlyStats: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil update survey ",
|
||||
data: updated,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Gagal update survey :", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal update survey ",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default surveyUpdate;
|
||||
@@ -8,7 +8,6 @@ import SDGSDesa from "./sdgs-desa";
|
||||
import APBDes from "./apbdes";
|
||||
import PrestasiDesa from "./prestasi-desa";
|
||||
import KategoriPrestasi from "./prestasi-desa/kategori-prestasi";
|
||||
import IndeksKepuasanMasyarakat from "./indeks-kepuasan-masyarakat";
|
||||
|
||||
const LandingPage = new Elysia({
|
||||
prefix: "/api/landingpage",
|
||||
@@ -24,6 +23,5 @@ const LandingPage = new Elysia({
|
||||
.use(APBDes)
|
||||
.use(PrestasiDesa)
|
||||
.use(KategoriPrestasi)
|
||||
.use(IndeksKepuasanMasyarakat)
|
||||
|
||||
export default LandingPage
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Center, Container, Divider, Grid, GridCol, Group, Image, Pagination, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
|
||||
import { IconCalendar, IconUser } from '@tabler/icons-react';
|
||||
import React from 'react';
|
||||
|
||||
const dataBeritaTerbaru = [
|
||||
{
|
||||
id: 1,
|
||||
judul: 'FESTIVAL SENI BUDAYA KAB. BADUNG',
|
||||
image: "/api/img/tari-3.jpg",
|
||||
tanggal: "Selasa, 11 Januari 2025",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
judul: 'LATIHAN TARI REJANG GIRI PUTRI',
|
||||
image: "/api/img/tari-3.jpg",
|
||||
tanggal: "Kamis, 13 Januari 2025",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
judul: 'LATIHAN TARI REJANG GIRI PUTRI',
|
||||
image: "/api/img/tari-3.jpg",
|
||||
tanggal: "Kamis, 13 Januari 2025",
|
||||
},
|
||||
]
|
||||
function Budaya() {
|
||||
return (
|
||||
<Box py={20}>
|
||||
<Container size="xl" px={{ base: "md", md: "xl" }}>
|
||||
<Grid gutter={{ base: "md", md: "xl" }} pb={70}>
|
||||
{/* Berita Utama */}
|
||||
<GridCol span={{ base: 12, md: 8 }}>
|
||||
<Paper p="md" shadow="sm" radius="md">
|
||||
<Stack gap="md">
|
||||
<Box>
|
||||
<Image
|
||||
src="/api/img/budaya-1.jpg"
|
||||
alt="Darmasaba Smart Village"
|
||||
radius="md"
|
||||
fit="cover"
|
||||
h={{ base: 450, md: 610 }}
|
||||
/>
|
||||
</Box>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Budaya</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz={{ base: "xl", md: "2xl" }} fw="bold" lineClamp={2}>
|
||||
FESTIVAL SENI BUDAYA KAB. BADUNG
|
||||
</Text>
|
||||
<Text size="md" lineClamp={3}>
|
||||
Semeton Darmasaba yang suka menikmati seni seperti baleganjur, gong kebyar, tari, dan lainnya. Nih! ada acara keren di Puspem Badung tepatnya di Balai Budaya Giri Nata Mandala yaitu Festival Seni Budaya dari tanggal 1 November 2023 s.d. 16 November 2023.
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">Selasa, 11 Januari 2025</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</GridCol>
|
||||
|
||||
{/* Berita Sampingan */}
|
||||
<GridCol span={{ base: 12, md: 4 }}>
|
||||
<Stack gap="md">
|
||||
{/* Berita Sampingan 1 */}
|
||||
<Paper p="md" shadow="sm" radius="md">
|
||||
<Stack gap="sm">
|
||||
<Image
|
||||
radius="md"
|
||||
src="/api/img/tari-3.jpg"
|
||||
alt="Prestasi Voli"
|
||||
fit="cover"
|
||||
h={180}
|
||||
/>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Budaya</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz="lg" fw="bold" lineClamp={2}>
|
||||
PELATIHAN TARI WALI
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">Selasa, 11 Januari 2025</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Berita Sampingan 2 */}
|
||||
<Paper p="md" shadow="sm" radius="md">
|
||||
<Stack gap="sm">
|
||||
<Image
|
||||
radius="md"
|
||||
src="/api/img/tari-3.jpg"
|
||||
alt="Prestasi Voli"
|
||||
fit="cover"
|
||||
h={180}
|
||||
/>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Budaya</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz="lg" fw="bold" lineClamp={2}>
|
||||
LATIHAN TARI REJANG GIRI PUTRI
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">Selasa, 11 Januari 2025</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Box pb={30}>
|
||||
<Text fz={"h1"} fw={"bold"}>Berita Terbaru</Text>
|
||||
<Divider color={colors["blue-button"]} />
|
||||
</Box>
|
||||
<SimpleGrid cols={{ base: 1, md: 3 }}>
|
||||
{dataBeritaTerbaru.map((v, k) => {
|
||||
return (
|
||||
<Paper key={k} p="md" shadow="sm" radius="md">
|
||||
<Stack gap="sm">
|
||||
<Image
|
||||
radius="md"
|
||||
src={v.image}
|
||||
alt=""
|
||||
fit="cover"
|
||||
h={282}
|
||||
/>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Budaya</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz="lg" fw="bold" lineClamp={2}>
|
||||
{v.judul}
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">{v.tanggal}</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
)
|
||||
})}
|
||||
</SimpleGrid>
|
||||
<Box py={"xl"}>
|
||||
<Center>
|
||||
<Pagination total={10} />
|
||||
</Center>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Budaya;
|
||||
@@ -1,185 +0,0 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Center, Container, Divider, Grid, GridCol, Group, Image, Pagination, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
|
||||
import { IconCalendar, IconUser } from '@tabler/icons-react';
|
||||
import React from 'react';
|
||||
|
||||
const dataBeritaTerbaru = [
|
||||
{
|
||||
id: 1,
|
||||
judul: 'PROGRAM KETAHANAN PANGAN PEMERINTAH DESA DARMASABA TAHUN 2023',
|
||||
image: "/api/img/ekonomi-sampingan-3.png",
|
||||
tanggal: "Selasa, 11 Januari 2025",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
judul: 'Sinergitas Pemkab Badung-TNI Wujudkan Kedaulatan Pangan di Subak Aban Darmasaba',
|
||||
image: "/api/img/ekonomi-sampingan.png",
|
||||
tanggal: "Kamis, 13 Januari 2025",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
judul: 'ANTUSIASME WARGA DARMASABA MELAKUKAN PEMBUKAAN REKENING BANK BPD BALI ',
|
||||
image: "/api/img/ekonomi-sampingan-2.png",
|
||||
tanggal: "Kamis, 13 Januari 2025",
|
||||
},
|
||||
]
|
||||
function Ekonomi() {
|
||||
return (
|
||||
<Box py={20}>
|
||||
<Container size="xl" px={{ base: "md", md: "xl" }}>
|
||||
<Grid gutter={{ base: "md", md: "xl" }} pb={70}>
|
||||
{/* Berita Utama */}
|
||||
<GridCol span={{ base: 12, md: 8 }}>
|
||||
<Paper p="md" shadow="sm" radius="md">
|
||||
<Stack gap="md">
|
||||
<Box>
|
||||
<Image
|
||||
src="/api/img/ekonomi-utama.png"
|
||||
alt="Darmasaba Smart Village"
|
||||
radius="md"
|
||||
fit="cover"
|
||||
h={{ base: 450, md: 660 }}
|
||||
/>
|
||||
</Box>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Ekonomi</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz={{ base: "xl", md: "2xl" }} fw="bold" lineClamp={2}>
|
||||
PROGRAM KETAHANAN PANGAN PEMERINTAH DESA DARMASABA TAHUN 2023
|
||||
</Text>
|
||||
<Text size="md" lineClamp={2}>
|
||||
Pemerintah Desa Darmasaba melalui kegiatan ketahanan pangan ini menjalankan dua kategori yaitu pertanian dan peternakan untuk kategori pertanian telah membuahkan hasil panen pertama pada hari Kamis, 24 Agustus 2023 melakukan panen bawang merah di lokasi ketahanan pangan Br. Taman, Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung. Adapun varietas bawang yang dipanen adalah Bawang Bali Karet (Batu Ijo) pada lahan seluas kurang lebih 7 are.
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">Selasa, 11 Januari 2025</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</GridCol>
|
||||
|
||||
{/* Berita Sampingan */}
|
||||
<GridCol span={{ base: 12, md: 4 }}>
|
||||
<Stack gap="md">
|
||||
{/* Berita Sampingan 1 */}
|
||||
<Paper p="md" shadow="sm" radius="md">
|
||||
<Stack gap="sm">
|
||||
<Image
|
||||
radius="md"
|
||||
src="/api/img/ekonomi-sampingan.png"
|
||||
alt="Prestasi Voli"
|
||||
fit="cover"
|
||||
h={180}
|
||||
/>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Ekonomi</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz="lg" fw="bold" lineClamp={2}>
|
||||
Sinergitas Pemkab Badung-TNI Wujudkan Kedaulatan Pangan di Subak Aban Darmasaba
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">Selasa, 11 Januari 2025</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Berita Sampingan 2 */}
|
||||
<Paper p="md" shadow="sm" radius="md">
|
||||
<Stack gap="sm">
|
||||
<Image
|
||||
radius="md"
|
||||
src="/api/img/ekonomi-sampingan-2.png"
|
||||
alt="Prestasi Voli"
|
||||
fit="cover"
|
||||
h={180}
|
||||
/>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Ekonomi</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz="lg" fw="bold" lineClamp={1}>
|
||||
ANTUSIASME WARGA DARMASABA MELAKUKAN PEMBUKAAN REKENING BANK BPD BALI
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">Selasa, 11 Januari 2025</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Box pb={30}>
|
||||
<Text fz={"h1"} fw={"bold"}>Berita Terbaru</Text>
|
||||
<Divider color={colors["blue-button"]} />
|
||||
</Box>
|
||||
<SimpleGrid cols={{ base: 1, md: 3 }}>
|
||||
{dataBeritaTerbaru.map((v, k) => {
|
||||
return (
|
||||
<Paper key={k} p="md" shadow="sm" radius="md">
|
||||
<Stack gap="sm">
|
||||
<Image
|
||||
radius="md"
|
||||
src={v.image}
|
||||
alt="Prestasi Voli"
|
||||
fit="cover"
|
||||
h={180}
|
||||
/>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Ekonomi</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz="lg" fw="bold" lineClamp={2}>
|
||||
{v.judul}
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">{v.tanggal}</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
)
|
||||
})}
|
||||
</SimpleGrid>
|
||||
<Box py={"xl"}>
|
||||
<Center>
|
||||
<Pagination total={10} />
|
||||
</Center>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Ekonomi;
|
||||
@@ -1,187 +0,0 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Center, Container, Divider, Grid, GridCol, Group, Image, Pagination, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
|
||||
import { IconCalendar, IconUser } from '@tabler/icons-react';
|
||||
import React from 'react';
|
||||
|
||||
const dataBeritaTerbaru = [
|
||||
{
|
||||
id: 1,
|
||||
judul: 'PEMBANGUNAN GOT JALAN LINGKUNGAN BR. TAMAN',
|
||||
image: "/api/img/pembangunan-1.jpg",
|
||||
tanggal: "Kamis, 13 Januari 2025",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
judul: 'PEMBANGUNAN BALE GAMBELAN SETRA AGENG DESA ADAT TEGAL',
|
||||
image: "/api/img/pembangunan-3.jpg",
|
||||
tanggal: "Kamis, 13 Januari 2025",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
judul: 'PEMBANGUNAN TROTOARISASI JALAN LINGKUNGAN BR. GULINGAN',
|
||||
image: "/api/img/pembangunan-1.jpg",
|
||||
tanggal: "Selasa, 11 Januari 2025",
|
||||
},
|
||||
]
|
||||
function Pembangunan() {
|
||||
return (
|
||||
<Box py={20}>
|
||||
<Container size="xl" px={{ base: "md", md: "xl" }}>
|
||||
<Grid gutter={{ base: "md", md: "xl" }} pb={70}>
|
||||
{/* Berita Utama */}
|
||||
<GridCol span={{ base: 12, xl: 8 }}>
|
||||
<Paper p="md" shadow="sm" radius="md">
|
||||
<Stack gap="md">
|
||||
<Box>
|
||||
<Image
|
||||
src="/api/img/pembangunan-1.jpg"
|
||||
alt="PEMBANGUNAN TROTOARISASI JALAN LINGKUNGAN BR. GULINGAN"
|
||||
radius="md"
|
||||
fit="cover"
|
||||
h={{ base: 450, md: 615 }}
|
||||
/>
|
||||
</Box>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Pembangunan</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz={{ base: "xl", md: "2xl" }} fw="bold" lineClamp={2}>
|
||||
PEMBANGUNAN TROTOARISASI JALAN LINGKUNGAN BR. GULINGAN
|
||||
</Text>
|
||||
<Text size="md" lineClamp={3}>
|
||||
Sudah tertata rapi dan bersih niki Semeton Darmasaba! Sekarang pejalan kaki di seputaran jalan lingkungan Br. Gulingan bisa aman dan nyaman nie!!!
|
||||
|
||||
Pemdes Darmasaba dalam rangka memenuhi kebutuhan masyarakat khususnya warga Br. Gulingan yang memerlukan trotoarisasi guna memperlancar saluran air dan memberikan kenyamanan ketika berjalan, kini sudah menyelesaikan pembangunan trotoarisasi jalan lingkungan Br. Gulingan. Pembangunan tersebut menggunakan APBDes Darmasaba T. A. 2023 dengan realisasi sebesar Rp357.011.299,00 dari pagu yang disiapkan sebesar Rp439.066.327,00. Pembangunan trotoar tersebut melibatkan pekerja lokal dan dibantu oleh warga Br. Gulingan.
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">Selasa, 11 Januari 2025</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</GridCol>
|
||||
|
||||
{/* Berita Sampingan */}
|
||||
<GridCol span={{ base: 12, xl: 4 }}>
|
||||
<Stack gap="md">
|
||||
{/* Berita Sampingan 1 */}
|
||||
<Paper p="md" shadow="sm" radius="md">
|
||||
<Stack gap="sm">
|
||||
<Image
|
||||
radius="md"
|
||||
src="/api/img/pembangunan-2.jpg"
|
||||
alt="Prestasi Voli"
|
||||
fit="cover"
|
||||
h={300}
|
||||
/>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Pembangunan</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz="lg" fw="bold" lineClamp={2}>
|
||||
PEMBANGUNAN GOT JALAN LINGKUNGAN BR. TAMAN
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">Selasa, 11 Januari 2025</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Berita Sampingan 2 */}
|
||||
<Paper p="md" shadow="sm" radius="md">
|
||||
<Stack gap="sm">
|
||||
<Image
|
||||
radius="md"
|
||||
src="/api/img/pembangunan-3.jpg"
|
||||
alt="Prestasi Voli"
|
||||
fit="cover"
|
||||
h={250}
|
||||
/>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Pembangunan</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz="lg" fw="bold" lineClamp={2}>
|
||||
PEMBANGUNAN BALE GAMBELAN SETRA AGENG DESA ADAT TEGAL
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">Selasa, 11 Januari 2025</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Box pb={30}>
|
||||
<Text fz={"h1"} fw={"bold"}>Berita Terbaru</Text>
|
||||
<Divider color={colors["blue-button"]} />
|
||||
</Box>
|
||||
<SimpleGrid cols={{ base: 1, md: 3 }}>
|
||||
{dataBeritaTerbaru.map((v, k) => {
|
||||
return (
|
||||
<Paper key={k} p="md" shadow="sm" radius="md">
|
||||
<Stack gap="sm">
|
||||
<Image
|
||||
radius="md"
|
||||
src={v.image}
|
||||
alt="Prestasi Voli"
|
||||
fit="cover"
|
||||
h={200}
|
||||
/>
|
||||
<Group>
|
||||
<Paper px={10} py={5} radius="xl" bg={colors['BG-trans']}>
|
||||
<Text size="sm">Pembangunan</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Text fz="lg" fw="bold" lineClamp={2}>
|
||||
{v.judul}
|
||||
</Text>
|
||||
<Group>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={18} />
|
||||
<Text size="sm">{v.tanggal}</Text>
|
||||
</Group>
|
||||
<Group gap="xs">
|
||||
<IconUser size={18} />
|
||||
<Text size="sm">Admin Desa</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
)
|
||||
})}
|
||||
</SimpleGrid>
|
||||
<Box py={"xl"}>
|
||||
<Center>
|
||||
<Pagination total={10} />
|
||||
</Center>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Pembangunan;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user