diff --git a/.gitignore b/.gitignore
index fb70e68f..ebd64b35 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,9 @@ next-env.d.ts
# uploads
/uploads
+# download
+/download
+
# cache
/cache
diff --git a/bun.lockb b/bun.lockb
index b7d17ba0..e571b0b1 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 791da768..472efe96 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"version": "0.1.5",
"private": true,
"scripts": {
- "dev": "bun --bun next dev --hostname 0.0.0.0",
+ "dev": "bun --bun next dev",
"build": "bun --bun next build",
"start": "bun --bun next start"
},
@@ -39,19 +39,25 @@
"@tiptap/pm": "^2.11.7",
"@tiptap/react": "^2.11.7",
"@tiptap/starter-kit": "^2.11.7",
+ "@types/adm-zip": "^0.5.7",
"@types/bun": "^1.2.2",
"@types/leaflet": "^1.9.20",
"@types/lodash": "^4.17.16",
+ "@types/nodemailer": "^7.0.2",
"add": "^2.0.6",
+ "adm-zip": "^0.5.16",
"animate.css": "^4.1.1",
"bcryptjs": "^3.0.2",
"bun": "^1.2.2",
"chart.js": "^4.4.8",
"classnames": "^2.5.1",
+ "colors": "^1.4.0",
"dayjs": "^1.11.13",
+ "dotenv": "^17.2.3",
"elysia": "^1.3.5",
"embla-carousel-autoplay": "^8.5.2",
"embla-carousel-react": "^7.1.0",
+ "extract-zip": "^2.0.1",
"form-data": "^4.0.2",
"framer-motion": "^12.23.5",
"get-port": "^7.1.0",
@@ -67,6 +73,7 @@
"next": "^15.5.2",
"next-view-transitions": "^0.3.4",
"node-fetch": "^3.3.2",
+ "nodemailer": "^7.0.10",
"p-limit": "^6.2.0",
"primeicons": "^7.0.0",
"primereact": "^10.9.6",
@@ -78,6 +85,7 @@
"react-simple-toasts": "^6.1.0",
"react-toastify": "^11.0.5",
"react-transition-group": "^4.4.5",
+ "react-zoom-pan-pinch": "^3.7.0",
"readdirp": "^4.1.1",
"recharts": "^2.15.3",
"sharp": "^0.34.3",
diff --git a/prisma/data/kategori-berita.json b/prisma/data/desa/berita/kategori-berita.json
similarity index 87%
rename from prisma/data/kategori-berita.json
rename to prisma/data/desa/berita/kategori-berita.json
index ee0a53b0..4d777965 100644
--- a/prisma/data/kategori-berita.json
+++ b/prisma/data/desa/berita/kategori-berita.json
@@ -1,5 +1,4 @@
[
- { "name": "Semua" },
{ "name": "Pemerintahan" },
{ "name": "Pembangunan" },
{ "name": "Ekonomi" },
diff --git a/prisma/data/desa/layanan/pelayanaPendudukNonPermanen.json b/prisma/data/desa/layanan/pelayanaPendudukNonPermanen.json
index 2259bea4..d39b6e98 100644
--- a/prisma/data/desa/layanan/pelayanaPendudukNonPermanen.json
+++ b/prisma/data/desa/layanan/pelayanaPendudukNonPermanen.json
@@ -1,6 +1,6 @@
[
{
- "id": "1",
+ "id": "edit",
"name": "Pelayanan Penduduk Non-Permanent",
"deskripsi": "
Surat Keterangan Penduduk Non-Permanent adalah dokumen yang dikeluarkan oleh pihak berwenang untuk memberikan keterangan bahwa seseorang atau kelompok orang memiliki status penduduk non-permanent di suatu wilayah. Dokumen ini biasanya digunakan untuk keperluan administratif atau legal, seperti mendapatkan akses ke layanan kesehatan, pendidikan, atau pelayanan publik lainnya.
"
}
diff --git a/prisma/data/desa/layanan/pelayananPerizinanBerusaha.json b/prisma/data/desa/layanan/pelayananPerizinanBerusaha.json
index 8df36cf0..42e7ab7a 100644
--- a/prisma/data/desa/layanan/pelayananPerizinanBerusaha.json
+++ b/prisma/data/desa/layanan/pelayananPerizinanBerusaha.json
@@ -1,6 +1,6 @@
[
{
- "id": "1",
+ "id": "edit",
"name": "Pelayanan Perizinan Berusaha Berbasis Risiko Melalui Sistem ONLINE SINGLE SUBMISSION (OSS)",
"deskripsi": "Penyelenggaraan Perizinan Berusaha Berbasis Risiko melalui Sistem Online Single Submission (OSS) merupakan pelaksanaan Undang-Undang Nomor 11 Tahun 2020 Tentang Cipta Kerja. OSS Berbasis Risiko wajib digunakan oleh Pelaku Usaha, Kementerian/Lembaga, Pemerintah Daerah, Administrator Kawasan Ekonomi Khusus (KEK), dan Badan Pengusahaan Kawasan Perdagangan Bebas Pelabuhan Bebas (KPBPB).Berdasarkan Peraturan Pemerintah Nomor 5 Tahun 2021 terdapat 1.702 kegiatan usaha yang terdiri atas 1.349 Klasifikasi Baku Lapangan Usaha Indonesia (KBLI) yang sudah diimplementasikan dalam Sistem OSS Berbasis Risiko.
",
"link" : "https://oss.go.id/"
diff --git a/prisma/data/ekonomi/struktur-organisasi/hubungan-organisasi.json b/prisma/data/ekonomi/struktur-organisasi/hubungan-organisasi.json
deleted file mode 100644
index 0f0e8271..00000000
--- a/prisma/data/ekonomi/struktur-organisasi/hubungan-organisasi.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[
- {
- "id": "650e8400-e29b-41d4-a716-446655440001",
- "atasanId": "550e8400-e29b-41d4-a716-446655440001",
- "bawahanId": "550e8400-e29b-41d4-a716-446655440002",
- "tipe": "Langsung Melapor"
- }
-]
diff --git a/prisma/data/ekonomi/struktur-organisasi/pegawai-bumdes.json b/prisma/data/ekonomi/struktur-organisasi/pegawai-bumdes.json
new file mode 100644
index 00000000..0976a812
--- /dev/null
+++ b/prisma/data/ekonomi/struktur-organisasi/pegawai-bumdes.json
@@ -0,0 +1,91 @@
+[
+ {
+ "id": "cmgewz4gt000704ib91i3f169",
+ "namaLengkap": "Ida Bagus Surya Prabhawa Manuaba, S.H.,M.H., NL.P.",
+ "gelarAkademik": "S.H.,M.H.,NL.P.",
+ "tanggalMasuk": "2020-01-01T00:00:00.000Z",
+ "email": "bagus@desa.id",
+ "telepon": "081234567891",
+ "alamat": "Jl. Raya Desa No. 1",
+ "posisiId": "kepala_desa",
+ "isActive": true
+ },
+ {
+ "id": "cmgewxfvw000004ibee5013f4",
+ "namaLengkap": "I Ketut Suwanta",
+ "gelarAkademik": "S.Pt",
+ "tanggalMasuk": "2020-02-01T00:00:00.000Z",
+ "email": "suwanta@desa.id",
+ "telepon": "081234567892",
+ "alamat": "Jl. Raya Desa No. 2",
+ "posisiId": "sekretaris_desa",
+ "isActive": true
+ },
+ {
+ "id": "cmgewxvqw000104ibgm5l8fzs",
+ "namaLengkap": "Ni Wayan Supardiati",
+ "gelarAkademik": "S.Pd",
+ "tanggalMasuk": "2020-02-01T00:00:00.000Z",
+ "email": "supardiati@desa.id",
+ "telepon": "081234567892",
+ "alamat": "Jl. Raya Desa No. 2",
+ "posisiId": "kaur_keuangan",
+ "isActive": true
+ },
+ {
+ "id": "cmgewy1g9000204ib2n7hbx0i",
+ "namaLengkap": "I Wayan Agus Juni Artha Saputra",
+ "gelarAkademik": "S.T.",
+ "tanggalMasuk": "2020-02-01T00:00:00.000Z",
+ "email": "agus@desa.id",
+ "telepon": "081234567892",
+ "alamat": "Jl. Raya Desa No. 2",
+ "posisiId": "kadus_banjar_dinas_menesa",
+ "isActive": true
+ },
+ {
+ "id": "cmgewybah000304ibgqhn1gm2",
+ "namaLengkap": "I Wayan Sueca",
+ "gelarAkademik": "S.H.",
+ "tanggalMasuk": "2020-02-01T00:00:00.000Z",
+ "email": "sueca@desa.id",
+ "telepon": "081234567893",
+ "alamat": "Jl. Raya Desa No. 2",
+ "posisiId": "kadus_banjar_dinas_darmasaba",
+ "isActive": true
+ },
+ {
+ "id": "cmgewygqz000404ib20sv8nvg",
+ "namaLengkap": "Si Gede Ketut Astawa",
+ "gelarAkademik": "S.T.",
+ "tanggalMasuk": "2020-02-01T00:00:00.000Z",
+ "email": "astawa@desa.id",
+ "telepon": "081234567893",
+ "alamat": "Jl. Raya Desa No. 2",
+ "posisiId": "kadus_banjar_dinas_bucu",
+ "isActive": true
+ },
+ {
+ "id": "cmgewyos1000504ibcu8o2gyk",
+ "namaLengkap": "I Kadek Arya Minarta",
+ "gelarAkademik": "S.T.",
+ "tanggalMasuk": "2020-02-01T00:00:00.000Z",
+ "email": "minarta@desa.id",
+ "telepon": "081234567893",
+ "alamat": "Jl. Raya Desa No. 2",
+ "posisiId": "kadus_banjar_dinas_gulingan",
+ "isActive": true
+ },
+ {
+ "id": "cmgewyxk7000604ib8djs3i6c",
+ "namaLengkap": "I Gede Andika Pradnya Diputra",
+ "gelarAkademik": "S.E.",
+ "tanggalMasuk": "2020-02-01T00:00:00.000Z",
+ "email": "diputra@desa.id",
+ "telepon": "081234567893",
+ "alamat": "Jl. Raya Desa No. 2",
+ "posisiId": "kadus_banjar_dinas_taman",
+ "isActive": true
+ }
+
+]
\ No newline at end of file
diff --git a/prisma/data/ekonomi/struktur-organisasi/pegawai.json b/prisma/data/ekonomi/struktur-organisasi/pegawai.json
deleted file mode 100644
index 9d7a6437..00000000
--- a/prisma/data/ekonomi/struktur-organisasi/pegawai.json
+++ /dev/null
@@ -1,24 +0,0 @@
-[
- {
- "id": "550e8400-e29b-41d4-a716-446655440001",
- "namaLengkap": "Budi Santoso",
- "gelarAkademik": "S.IP",
- "tanggalMasuk": "2020-01-01T00:00:00.000Z",
- "email": "budi@desa.id",
- "telepon": "081234567891",
- "alamat": "Jl. Raya Desa No. 1",
- "posisiId": "kepala_desa",
- "isActive": true
- },
- {
- "id": "550e8400-e29b-41d4-a716-446655440002",
- "namaLengkap": "Ani Lestari",
- "gelarAkademik": "S.Pd",
- "tanggalMasuk": "2020-02-01T00:00:00.000Z",
- "email": "ani@desa.id",
- "telepon": "081234567892",
- "alamat": "Jl. Raya Desa No. 2",
- "posisiId": "sekretaris_desa",
- "isActive": true
- }
- ]
\ No newline at end of file
diff --git a/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json b/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json
new file mode 100644
index 00000000..4a1699d7
--- /dev/null
+++ b/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json
@@ -0,0 +1,159 @@
+[
+ [
+ {
+ "id": "kepala_desa",
+ "nama": "Kepala Desa",
+ "deskripsi": "Pemimpin desa Darmasaba",
+ "hierarki": 1,
+ "parentId": null
+ },
+ {
+ "id": "kepala_urusan",
+ "nama": "Kepala Urusan",
+ "deskripsi": "Pemimpin urusan desa Darmasaba",
+ "hierarki": 2,
+ "parentId": "kepala_desa"
+ },
+ {
+ "id": "sekretaris_desa",
+ "nama": "Sekretaris Desa",
+ "deskripsi": "Pengelola administrasi desa",
+ "hierarki": 2,
+ "parentId": "kepala_desa"
+ },
+ {
+ "id": "kaur_keuangan",
+ "nama": "Kaur Keuangan",
+ "deskripsi": "Pengelola keuangan desa",
+ "hierarki": 3,
+ "parentId": "kaur_umum"
+ },
+ {
+ "id": "kaur_perencanaan",
+ "nama": "Kaur Perencanaan",
+ "deskripsi": "Penyusun program kerja desa",
+ "hierarki": 3,
+ "parentId": "kaur_umum"
+ },
+ {
+ "id": "kaur_umum",
+ "nama": "Kaur Umum & TU",
+ "deskripsi": "Pelayanan umum dan administrasi",
+ "hierarki": 2,
+ "parentId": "kepala_desa"
+ },
+ {
+ "id": "kasi_pemerintahan",
+ "nama": "Kasi Pemerintahan",
+ "deskripsi": "Urusan pemerintahan dan keamanan",
+ "hierarki": 2,
+ "parentId": "kepala_desa"
+ },
+ {
+ "id": "kasi_pelayanan",
+ "nama": "Kasi Pelayanan",
+ "deskripsi": "Urusan pelayanan masyarakat",
+ "hierarki": 2,
+ "parentId": "kepala_desa"
+ },
+ {
+ "id": "kasi_kesejahteraan",
+ "nama": "Kasi Kesejahteraan",
+ "deskripsi": "Urusan sosial dan kesejahteraan",
+ "hierarki": 2,
+ "parentId": "kepala_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_cabe",
+ "nama": "Kepala Dusun Banjar Dinas Cabe",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Cabe",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_menesa",
+ "nama": "Kepala Dusun Banjar Dinas Menesa",
+ "deskripsi": "Pimpinan wilayah Banjar Menesa",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_penenjoan",
+ "nama": "Kepala Dusun Banjar Dinas Penenjoan",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Penenjoan",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_telanga",
+ "nama": "Kepala Dusun Banjar Dinas Telanga",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Telanga",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_tengah",
+ "nama": "Kepala Dusun Banjar Dinas Tengah",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Tengah",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_baler_pasar",
+ "nama": "Kepala Dusun Banjar Dinas Baler Pasar",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Baler Pasar",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_bucu",
+ "nama": "Kepala Dusun Banjar Dinas Bucu",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Bucu",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_gulingan",
+ "nama": "Kepala Dusun Banjar Dinas Gulingan",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Gulingan",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_bersih",
+ "nama": "Kepala Dusun Banjar Dinas Bersih",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Bersih",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_umahanyar",
+ "nama": "Kepala Dusun Banjar Dinas Umahanyar",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Umahanyar",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_taman",
+ "nama": "Kepala Dusun Banjar Dinas Taman",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Taman",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "kadus_banjar_dinas_darmasaba",
+ "nama": "Kepala Dusun Banjar Dinas Darmasaba",
+ "deskripsi": "Pimpinan wilayah Banjar Dinas Darmasaba",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ },
+ {
+ "id": "staf_desa",
+ "nama": "Staf Desa",
+ "deskripsi": "Staf Desa",
+ "hierarki": 3,
+ "parentId": "sekretaris_desa"
+ }
+ ]
+ ]
+
\ No newline at end of file
diff --git a/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi.json b/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi.json
deleted file mode 100644
index 7596e168..00000000
--- a/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi.json
+++ /dev/null
@@ -1,27 +0,0 @@
-[
- {
- "id": "kepala_desa",
- "nama": "Kepala Desa",
- "deskripsi": "Kepala Desa",
- "hierarki": 1
- },
- {
- "id": "sekretaris_desa",
- "nama": "Sekretaris Desa",
- "deskripsi": "Sekretaris Desa",
- "hierarki": 2
- },
- {
- "id": "bendahara_desa",
- "nama": "Bendahara Desa",
- "deskripsi": "Bendahara Desa",
- "hierarki": 3
- },
- {
- "id": "staff_umum",
- "nama": "Staff Umum",
- "deskripsi": "Staff Umum",
- "hierarki": 4
- }
- ]
-
\ No newline at end of file
diff --git a/prisma/data/landing-page/profile/mediaSosial.json b/prisma/data/landing-page/profile/mediaSosial.json
index e6df4799..9af092a0 100644
--- a/prisma/data/landing-page/profile/mediaSosial.json
+++ b/prisma/data/landing-page/profile/mediaSosial.json
@@ -1,16 +1,4 @@
[
- {
- "id": "cmds8w2q60002vnbe6i8qhkuo",
- "name": "Telephone Desa Darmasaba",
- "iconUrl": "081239580000",
- "imageId": "cmff3nv180003vn6h5jvedidq"
- },
- {
- "id": "cmds8z7u20005vnbegyyvnbk0",
- "name": "Email Desa Darmasaba",
- "iconUrl": "desadarmasaba@badungkab.go.id",
- "imageId": "cmff3ll130001vn6hkhls3f5y"
- },
{
"id": "cmds9023u0008vnbe3oxmhwyf",
"name": "Desa Darmasaba",
diff --git a/prisma/data/lingkungan/gotong-royong/kategori-gotong-royong.json b/prisma/data/lingkungan/gotong-royong/kategori-gotong-royong.json
new file mode 100644
index 00000000..874e2e32
--- /dev/null
+++ b/prisma/data/lingkungan/gotong-royong/kategori-gotong-royong.json
@@ -0,0 +1,6 @@
+[
+ { "nama": "Kebersihan" },
+ { "nama": "Infrastruktur" },
+ { "nama": "Sosial" },
+ { "nama": "Lingkungan" }
+ ]
\ No newline at end of file
diff --git a/prisma/data/pendidikan/info-sekolah/jenjang-pendidikan.json b/prisma/data/pendidikan/info-sekolah/jenjang-pendidikan.json
new file mode 100644
index 00000000..a2d63947
--- /dev/null
+++ b/prisma/data/pendidikan/info-sekolah/jenjang-pendidikan.json
@@ -0,0 +1,9 @@
+[
+ { "id": "cmghqwjs4000404l8c5uvc300", "nama": "PAUD" },
+ { "id": "cmghqwjs4000404l8c5uvc301", "nama": "TK" },
+ { "id": "cmghqwjs4000404l8c5uvc302", "nama": "SD" },
+ { "id": "cmghqwjs4000404l8c5uvc303", "nama": "SMP" },
+ { "id": "cmghqwjs4000404l8c5uvc304", "nama": "SMA" },
+ { "id": "cmghqwjs4000404l8c5uvc305", "nama": "SMK" }
+ ]
+
\ No newline at end of file
diff --git a/prisma/data/ppid/struktur-ppid/pegawai-PPID.json b/prisma/data/ppid/struktur-ppid/pegawai-PPID.json
index e7a7a9cf..713cb799 100644
--- a/prisma/data/ppid/struktur-ppid/pegawai-PPID.json
+++ b/prisma/data/ppid/struktur-ppid/pegawai-PPID.json
@@ -1,6 +1,6 @@
[
{
- "id": "550e8400-e29b-41d4-a716-446655440001",
+ "id": "cmgewz4gt000704ib91i3f169",
"namaLengkap": "Ida Bagus Surya Prabhawa Manuaba, S.H.,M.H., NL.P.",
"gelarAkademik": "S.H.,M.H.,NL.P.",
"tanggalMasuk": "2020-01-01T00:00:00.000Z",
@@ -11,7 +11,7 @@
"isActive": true
},
{
- "id": "550e8400-e29b-41d4-a716-446655440002",
+ "id": "cmgewxfvw000004ibee5013f4",
"namaLengkap": "I Ketut Suwanta",
"gelarAkademik": "S.Pt",
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
@@ -22,7 +22,7 @@
"isActive": true
},
{
- "id": "550e8400-e29b-41d4-a716-446655440006",
+ "id": "cmgewxvqw000104ibgm5l8fzs",
"namaLengkap": "Ni Wayan Supardiati",
"gelarAkademik": "S.Pd",
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
@@ -33,7 +33,7 @@
"isActive": true
},
{
- "id": "550e8400-e29b-41d4-a716-446655440011",
+ "id": "cmgewy1g9000204ib2n7hbx0i",
"namaLengkap": "I Wayan Agus Juni Artha Saputra",
"gelarAkademik": "S.T.",
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
@@ -44,7 +44,7 @@
"isActive": true
},
{
- "id": "550e8400-e29b-41d4-a716-446655440012",
+ "id": "cmgewybah000304ibgqhn1gm2",
"namaLengkap": "I Wayan Sueca",
"gelarAkademik": "S.H.",
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
@@ -55,7 +55,7 @@
"isActive": true
},
{
- "id": "550e8400-e29b-41d4-a716-446655440017",
+ "id": "cmgewygqz000404ib20sv8nvg",
"namaLengkap": "Si Gede Ketut Astawa",
"gelarAkademik": "S.T.",
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
@@ -66,7 +66,7 @@
"isActive": true
},
{
- "id": "550e8400-e29b-41d4-a716-446655440018",
+ "id": "cmgewyos1000504ibcu8o2gyk",
"namaLengkap": "I Kadek Arya Minarta",
"gelarAkademik": "S.T.",
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
@@ -77,7 +77,7 @@
"isActive": true
},
{
- "id": "550e8400-e29b-41d4-a716-446655440021",
+ "id": "cmgewyxk7000604ib8djs3i6c",
"namaLengkap": "I Gede Andika Pradnya Diputra",
"gelarAkademik": "S.E.",
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
diff --git a/prisma/data/user/roles.json b/prisma/data/user/roles.json
index 79da3188..b79f3928 100644
--- a/prisma/data/user/roles.json
+++ b/prisma/data/user/roles.json
@@ -1,29 +1,23 @@
[
{
- "id": "1",
+ "id": "role-1",
"name": "ADMIN DESA",
"description": "Administrator Desa",
"permissions": ["manage_users", "manage_content", "view_reports"],
- "isActive": true,
- "createdAt": "2025-09-01T00:00:00.000Z",
- "updatedAt": "2025-09-01T00:00:00.000Z"
+ "isActive": true
},
{
- "id": "2",
+ "id": "role-2",
"name": "ADMIN KESEHATAN",
"description": "Administrator Bidang Kesehatan",
"permissions": ["manage_health_data", "view_reports"],
- "isActive": true,
- "createdAt": "2025-09-01T00:00:00.000Z",
- "updatedAt": "2025-09-01T00:00:00.000Z"
+ "isActive": true
},
{
- "id": "3",
+ "id": "role-3",
"name": "ADMIN SEKOLAH",
"description": "Administrator Sekolah",
"permissions": ["manage_school_data", "view_reports"],
- "isActive": true,
- "createdAt": "2025-09-01T00:00:00.000Z",
- "updatedAt": "2025-09-01T00:00:00.000Z"
+ "isActive": true
}
]
\ No newline at end of file
diff --git a/prisma/data/user/users.json b/prisma/data/user/users.json
index 2f44c667..eea2a98a 100644
--- a/prisma/data/user/users.json
+++ b/prisma/data/user/users.json
@@ -1,32 +1,23 @@
[
- {
- "id": "1",
- "nama": "Admin Desa",
- "nomor": "089647037426",
- "roleId": "1",
- "isActive": true,
- "lastLogin": "2025-08-31T10:00:00.000Z",
- "createdAt": "2025-09-01T00:00:00.000Z",
- "updatedAt": "2025-09-01T00:00:00.000Z"
- },
- {
- "id": "2",
- "nama": "Admin Kesehatan",
- "nomor": "082339004198",
- "roleId": "2",
- "isActive": true,
- "lastLogin": null,
- "createdAt": "2025-09-01T00:00:00.000Z",
- "updatedAt": "2025-09-01T00:00:00.000Z"
- },
- {
- "id": "3",
- "nama": "Admin Sekolah",
- "nomor": "085237157222",
- "roleId": "3",
- "isActive": true,
- "lastLogin": null,
- "createdAt": "2025-09-01T00:00:00.000Z",
- "updatedAt": "2025-09-01T00:00:00.000Z"
- }
- ]
\ No newline at end of file
+ {
+ "id": "user-1",
+ "nama": "Admin Desa",
+ "nomor": "089647037426",
+ "roleId": "role-1",
+ "isActive": true
+ },
+ {
+ "id": "user-2",
+ "nama": "Admin Kesehatan",
+ "nomor": "082339004198",
+ "roleId": "role-2",
+ "isActive": true
+ },
+ {
+ "id": "user-3",
+ "nama": "Admin Sekolah",
+ "nomor": "085237157222",
+ "roleId": "role-3",
+ "isActive": true
+ }
+]
diff --git a/prisma/safeseedUnique.ts b/prisma/safeseedUnique.ts
new file mode 100644
index 00000000..92d16071
--- /dev/null
+++ b/prisma/safeseedUnique.ts
@@ -0,0 +1,30 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+// helpers/safeSeedUnique.ts
+import { PrismaClient } from "@prisma/client";
+
+const prisma = new PrismaClient();
+
+/**
+ * Helper generic buat seed dengan upsert aman
+ */
+export async function safeSeedUnique(
+ model: T,
+ where: Record,
+ data: Record
+) {
+ const m = prisma[model];
+
+ if (!m) throw new Error(`Model ${String(model)} tidak ditemukan di PrismaClient`);
+
+ try {
+ // @ts-expect-error upsert dynamic
+ await m.upsert({
+ where,
+ update: data,
+ create: { ...where, ...data },
+ });
+ console.log(`β
Seeded ${String(model)} -> ${JSON.stringify(where)}`);
+ } catch (err) {
+ console.error(`β Gagal seed ${String(model)} -> ${JSON.stringify(where)}`, err);
+ }
+}
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 54166bfc..a52a0cb1 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -81,7 +81,7 @@ model FileStorage {
PelayananSuratKeteranganImage PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage")
PelayananSuratKeteranganImage2 PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage2")
PasarDesa PasarDesa[]
- Pegawai Pegawai[]
+ PegawaiBumDes PegawaiBumDes[]
DesaDigital DesaDigital[]
InfoTekno InfoTekno[]
PengaduanMasyarakat PengaduanMasyarakat[]
@@ -101,6 +101,7 @@ model FileStorage {
MitraKolaborasi MitraKolaborasi[]
ArtikelKesehatan ArtikelKesehatan[]
+ StrukturBumDes StrukturBumDes[]
}
//========================================= MENU LANDING PAGE ========================================= //
@@ -142,7 +143,7 @@ model MediaSosial {
isActive Boolean @default(true)
}
-//========================================= PROFILE ========================================= //
+//========================================= DESA ANTI KORUPSI ========================================= //
model DesaAntiKorupsi {
id String @id @default(cuid())
name String @unique
@@ -286,49 +287,51 @@ model StrukturPPID {
}
model PosisiOrganisasiPPID {
- id String @id @default(cuid())
- nama String @db.VarChar(100)
- deskripsi String? @db.Text
- hierarki Int
- pegawai PegawaiPPID[]
- strukturOrganisasi StrukturPPID[] // Relasi balik
- parentId String?
- isActive Boolean @default(true)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id])
- children PosisiOrganisasiPPID[] @relation("Parent")
+ id String @id @default(cuid())
+ nama String @db.VarChar(100)
+ deskripsi String? @db.Text
+ hierarki Int
+ pegawai PegawaiPPID[]
+ strukturOrganisasi StrukturPPID[] // Relasi balik
+ parentId String?
+ isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id])
+ children PosisiOrganisasiPPID[] @relation("Parent")
+ StrukturOrganisasiPPID StrukturOrganisasiPPID[]
}
model PegawaiPPID {
- id String @id @default(cuid())
- namaLengkap String @db.VarChar(255)
- gelarAkademik String? @db.VarChar(100)
- image FileStorage? @relation(fields: [imageId], references: [id])
- imageId String?
- tanggalMasuk DateTime? @db.Date
- email String? @unique @db.VarChar(255)
- telepon String? @db.VarChar(20)
- alamat String? @db.Text
- posisiId String @db.VarChar(50)
- isActive Boolean @default(true)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- posisi PosisiOrganisasiPPID @relation(fields: [posisiId], references: [id])
- strukturOrganisasi StrukturPPID[] // Relasi balik
+ id String @id @default(cuid())
+ namaLengkap String @db.VarChar(255)
+ gelarAkademik String? @db.VarChar(100)
+ image FileStorage? @relation(fields: [imageId], references: [id])
+ imageId String?
+ tanggalMasuk DateTime? @db.Date
+ email String? @unique @db.VarChar(255)
+ telepon String? @db.VarChar(20)
+ alamat String? @db.Text
+ posisiId String @db.VarChar(50)
+ isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ posisi PosisiOrganisasiPPID @relation(fields: [posisiId], references: [id])
+ strukturOrganisasi StrukturPPID[] // Relasi balik
+ StrukturOrganisasiPPID StrukturOrganisasiPPID[]
}
model StrukturOrganisasiPPID {
- id String @id @default(uuid())
- posisiOrganisasiId String @db.VarChar(50)
- pegawaiId String @db.Uuid
- hubunganOrganisasiId String @db.Uuid
- posisiOrganisasi PosisiOrganisasi @relation(fields: [posisiOrganisasiId], references: [id])
- pegawai Pegawai @relation(fields: [pegawaiId], references: [id])
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
+ id String @id @default(uuid())
+ posisiOrganisasiId String @db.VarChar(50)
+ pegawaiId String
+ hubunganOrganisasiId String
+ posisiOrganisasi PosisiOrganisasiPPID @relation(fields: [posisiOrganisasiId], references: [id])
+ pegawai PegawaiPPID @relation(fields: [pegawaiId], references: [id])
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
deletedAt DateTime?
- isActive Boolean @default(true)
+ isActive Boolean @default(true)
}
// ========================================= VISI MISI PPID ========================================= //
@@ -850,7 +853,7 @@ model JadwalKegiatan {
syaratKetentuanJadwalKegiatanId String
dokumenjadwalkegiatan DokumenJadwalKegiatan @relation(fields: [dokumenJadwalKegiatanId], references: [id])
dokumenJadwalKegiatanId String
- pendaftaranjadwalkegiatan PendaftaranJadwalKegiatan? @relation(fields: [pendaftaranJadwalKegiatanId], references: [id])
+ pendaftaranjadwalkegiatan PendaftaranJadwalKegiatan? @relation(fields: [pendaftaranJadwalKegiatanId], references: [id])
pendaftaranJadwalKegiatanId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -1167,6 +1170,7 @@ model KontakDarurat {
deskripsi String
image FileStorage @relation(fields: [imageId], references: [id])
imageId String
+ whatsapp String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
@@ -1340,6 +1344,7 @@ model PasarDesa {
harga Int
rating Float
alamatUsaha String
+ kontak String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
@@ -1382,6 +1387,7 @@ model LowonganPekerjaan {
gaji String
deskripsi String
kualifikasi String
+ notelp String
tanggalPosting DateTime @default(now())
isActive Boolean @default(true)
createdAt DateTime @default(now())
@@ -1391,79 +1397,67 @@ model LowonganPekerjaan {
// ========================================= STRUKTUR ORGANISASI ========================================= //
-model PosisiOrganisasi {
- id String @id @default(uuid()) @db.VarChar(50)
- nama String @db.VarChar(100)
- deskripsi String? @db.Text
- hierarki Int
-
- pegawai Pegawai[]
- strukturOrganisasi StrukturOrganisasi[] // Relasi balik
- StrukturOrganisasiPPID StrukturOrganisasiPPID[]
- isActive Boolean @default(true)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
-
- @@map("posisi_organisasi")
+model StrukturBumDes {
+ id String @id @default(cuid())
+ name String @db.Text
+ image FileStorage? @relation(fields: [imageId], references: [id])
+ imageId String?
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime @default(now())
+ isActive Boolean @default(true)
+ PosisiOrganisasiBumDes PosisiOrganisasiBumDes? @relation(fields: [posisiOrganisasiBumDesId], references: [id])
+ posisiOrganisasiBumDesId String?
+ PegawaiBumDes PegawaiBumDes? @relation(fields: [pegawaiBumDesId], references: [id])
+ pegawaiBumDesId String?
}
-model Pegawai {
- id String @id @default(uuid()) @db.Uuid
- namaLengkap String @db.VarChar(255)
- gelarAkademik String? @db.VarChar(100)
- image FileStorage? @relation(fields: [imageId], references: [id])
- imageId String?
- tanggalMasuk DateTime? @db.Date
- email String? @unique @db.VarChar(255)
- telepon String? @db.VarChar(20)
- alamat String? @db.Text
- posisiId String @db.VarChar(50)
- isActive Boolean @default(true)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
-
- posisi PosisiOrganisasi @relation(fields: [posisiId], references: [id])
-
- sebagaiAtasan HubunganOrganisasi[] @relation("AtasanToBawahan")
- sebagaiBawahan HubunganOrganisasi[] @relation("BawahanToAtasan")
-
- strukturOrganisasi StrukturOrganisasi[] // Relasi balik
- StrukturOrganisasiPPID StrukturOrganisasiPPID[]
-
- @@map("pegawai")
+model PosisiOrganisasiBumDes {
+ id String @id @default(cuid())
+ nama String @db.VarChar(100)
+ deskripsi String? @db.Text
+ hierarki Int
+ pegawai PegawaiBumDes[]
+ strukturOrganisasi StrukturBumDes[] // Relasi balik
+ parentId String?
+ isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ parent PosisiOrganisasiBumDes? @relation("Parent", fields: [parentId], references: [id])
+ children PosisiOrganisasiBumDes[] @relation("Parent")
+ StrukturOrganisasiBumDes StrukturOrganisasiBumDes[]
}
-model HubunganOrganisasi {
- id String @id @default(uuid()) @db.Uuid
- atasanId String @db.Uuid
- bawahanId String @db.Uuid
- tipe String? @db.VarChar(50)
-
- atasan Pegawai @relation("AtasanToBawahan", fields: [atasanId], references: [id])
- bawahan Pegawai @relation("BawahanToAtasan", fields: [bawahanId], references: [id])
-
- strukturOrganisasi StrukturOrganisasi[] // Relasi balik
-
- @@unique([atasanId, bawahanId])
- @@map("hubungan_organisasi")
+model PegawaiBumDes {
+ id String @id @default(cuid())
+ namaLengkap String @db.VarChar(255)
+ gelarAkademik String? @db.VarChar(100)
+ image FileStorage? @relation(fields: [imageId], references: [id])
+ imageId String?
+ tanggalMasuk DateTime? @db.Date
+ email String? @unique @db.VarChar(255)
+ telepon String? @db.VarChar(20)
+ alamat String? @db.Text
+ posisiId String @db.VarChar(50)
+ isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ posisi PosisiOrganisasiBumDes @relation(fields: [posisiId], references: [id])
+ strukturOrganisasi StrukturBumDes[] // Relasi balik
+ StrukturOrganisasiBumDes StrukturOrganisasiBumDes[]
}
-model StrukturOrganisasi {
- id String @id @default(uuid())
- posisiOrganisasiId String @db.VarChar(50)
- pegawaiId String @db.Uuid
- hubunganOrganisasiId String @db.Uuid
-
- posisiOrganisasi PosisiOrganisasi @relation(fields: [posisiOrganisasiId], references: [id])
- pegawai Pegawai @relation(fields: [pegawaiId], references: [id])
- hubunganOrganisasi HubunganOrganisasi @relation(fields: [hubunganOrganisasiId], references: [id])
-
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- deletedAt DateTime?
- isActive Boolean @default(true)
-
- @@map("struktur_organisasi")
+model StrukturOrganisasiBumDes {
+ id String @id @default(uuid())
+ posisiOrganisasiId String @db.VarChar(50)
+ pegawaiId String
+ hubunganOrganisasiId String
+ posisiOrganisasi PosisiOrganisasiBumDes @relation(fields: [posisiOrganisasiId], references: [id])
+ pegawai PegawaiBumDes @relation(fields: [pegawaiId], references: [id])
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime?
+ isActive Boolean @default(true)
}
// ========================================= PROGRAM KEMISKINAN ========================================= //
@@ -1612,7 +1606,7 @@ model Pembiayaan {
ApbDesa ApbDesa[] @relation("ApbDesaPembiayaan")
}
-// ========================================= INOVASI ========================================= //
+// ========================================= MENU INOVASI ========================================= //
// ========================================= DESA DIGITAL / SMART VILLAGE ========================================= //
model DesaDigital {
id String @id @default(cuid())
@@ -2093,6 +2087,9 @@ model DataPerpustakaan {
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
+
+ // relasi baru ke peminjaman
+ peminjamanBuku PeminjamanBuku[]
}
model KategoriBuku {
@@ -2105,6 +2102,31 @@ model KategoriBuku {
DataPerpustakaan DataPerpustakaan[]
}
+model PeminjamanBuku {
+ id String @id @default(cuid())
+ nama String
+ noTelp String
+ alamat String
+ bukuId String
+ tanggalPinjam DateTime @default(now())
+ batasKembali DateTime // tenggat waktu pengembalian
+ tanggalKembali DateTime? // diisi saat dikembalikan
+ status StatusPeminjaman @default(Dipinjam)
+ catatan String? // opsional, misal: kondisi buku
+ buku DataPerpustakaan @relation(fields: [bukuId], references: [id])
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime @default(now())
+ isActive Boolean @default(true)
+}
+
+enum StatusPeminjaman {
+ Dipinjam
+ Dikembalikan
+ Terlambat
+ Dibatalkan
+}
+
// ========================================= USER ========================================= //
model User {
diff --git a/prisma/seed.ts b/prisma/seed.ts
index ab793335..52de4324 100644
--- a/prisma/seed.ts
+++ b/prisma/seed.ts
@@ -31,14 +31,14 @@ import sejarahDesa from "./data/desa/profile/sejarah_desa.json";
import visiMisiDesa from "./data/desa/profile/visi_misi_desa.json";
import detailDataPengangguran from "./data/ekonomi/jumlah-pengangguran/detail-data-pengangguran.json";
import kategoriProduk from "./data/ekonomi/pasar-desa/kategori-produk.json";
-import hubunganOrganisasi from "./data/ekonomi/struktur-organisasi/hubungan-organisasi.json";
-import pegawai from "./data/ekonomi/struktur-organisasi/pegawai.json";
-import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi.json";
-import kategoriBerita from "./data/kategori-berita.json";
+import pegawai from "./data/ekonomi/struktur-organisasi/pegawai-bumdes.json";
+import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json";
+import kategoriBerita from "./data/desa/berita/kategori-berita.json";
import contohEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/contoh-kegiatan-di-desa-darmasaba.json";
import materiEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan.json";
import tujuanEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan.json";
import bentukKonservasiBerdasarkanAdat from "./data/lingkungan/konservasi-adat-bali/bentuk-konservasi.json";
+import kategoriKegiatanData from "./data/lingkungan/gotong-royong/kategori-gotong-royong.json";
import filosofiTriHita from "./data/lingkungan/konservasi-adat-bali/filosofi-tri-hita.json";
import nilaiKonservasiAdat from "./data/lingkungan/konservasi-adat-bali/nilai-konservasi-adat.json";
import caraMemperolehInformasi from "./data/list-caraMemperolehInformasi.json";
@@ -56,6 +56,9 @@ import tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-prog
import roles from "./data/user/roles.json";
import users from "./data/user/users.json";
import fileStorage from "./data/file-storage.json";
+import jenjangPendidikan from "./data/pendidikan/info-sekolah/jenjang-pendidikan.json";
+import seedAssets from "./seed_assets";
+import { safeSeedUnique } from "./safeseedUnique";
(async () => {
// =========== USER & ROLE ===========
@@ -63,23 +66,14 @@ import fileStorage from "./data/file-storage.json";
// =========== ROLES ===========
console.log("π Seeding roles...");
for (const r of roles) {
- await prisma.role.upsert({
- where: { id: r.id },
- update: {
- name: r.name,
- description: r.description,
- permissions: r.permissions,
- isActive: r.isActive,
- },
- create: {
- id: r.id,
- name: r.name,
- description: r.description,
- permissions: r.permissions,
- isActive: r.isActive,
- },
+ await safeSeedUnique("role", { id: r.id }, {
+ name: r.name,
+ description: r.description,
+ permissions: r.permissions,
+ isActive: r.isActive,
});
}
+
console.log("β
Roles seeded");
// =========== USERS ===========
@@ -95,22 +89,12 @@ import fileStorage from "./data/file-storage.json";
continue;
}
- await prisma.user.upsert({
- where: { id: u.id },
- update: {
- username: u.nama,
- nomor: u.nomor,
- roleId: u.roleId,
+ await safeSeedUnique("user", { id: u.id }, {
+ username: u.nama,
+ nomor: u.nomor,
+ roleId: u.roleId,
isActive: u.isActive,
- },
- create: {
- id: u.id,
- username: u.nama,
- nomor: u.nomor,
- roleId: u.roleId,
- isActive: u.isActive,
- },
- });
+ });
}
console.log("β
Users seeded");
@@ -364,6 +348,7 @@ import fileStorage from "./data/file-storage.json";
jumlah: l.jumlah,
},
create: {
+ id: l.id,
name: l.name,
jumlah: l.jumlah,
},
@@ -823,28 +808,34 @@ import fileStorage from "./data/file-storage.json";
}
console.log("kategori produk success ...");
- for (const p of posisiOrganisasi) {
- await prisma.posisiOrganisasi.upsert({
- where: {
- id: p.id,
- },
- update: {
- nama: p.nama,
- deskripsi: p.deskripsi,
- hierarki: p.hierarki,
- },
- create: {
- id: p.id,
- nama: p.nama,
- deskripsi: p.deskripsi,
- hierarki: p.hierarki,
- },
+ const flattenedPosisiBumdes = posisiOrganisasi.flat();
+
+ // β
Urutkan berdasarkan hierarki
+ const sortedPosisiBumdes = flattenedPosisiBumdes.sort((a, b) => a.hierarki - b.hierarki);
+
+ for (const p of sortedPosisiBumdes) {
+ console.log(`Seeding: ${p.nama} (id: ${p.id}, parent: ${p.parentId})`);
+
+ if (p.parentId) {
+ const parentExists = flattenedPosisi.some((pos) => pos.id === p.parentId);
+ if (!parentExists) {
+ console.warn(
+ `β οΈ Parent tidak ditemukan: ${p.parentId} untuk ${p.nama}`
+ );
+ continue;
+ }
+ }
+
+ await prisma.posisiOrganisasiBumDes.upsert({
+ where: { id: p.id },
+ update: p,
+ create: p,
});
}
- console.log("posisi organisasi success ...");
+ console.log("posisi organisasi berhasil");
for (const p of pegawai) {
- await prisma.pegawai.upsert({
+ await prisma.pegawaiBumDes.upsert({
where: {
id: p.id,
},
@@ -873,26 +864,6 @@ import fileStorage from "./data/file-storage.json";
}
console.log("pegawai success ...");
- for (const p of hubunganOrganisasi) {
- await prisma.hubunganOrganisasi.upsert({
- where: {
- atasanId_bawahanId: {
- atasanId: p.atasanId,
- bawahanId: p.bawahanId,
- },
- },
- update: {
- tipe: p.tipe,
- },
- create: {
- atasanId: p.atasanId,
- bawahanId: p.bawahanId,
- tipe: p.tipe,
- },
- });
- }
- console.log("hubungan organisasi success ...");
-
for (const d of detailDataPengangguran) {
await prisma.detailDataPengangguran.upsert({
where: {
@@ -916,6 +887,30 @@ import fileStorage from "./data/file-storage.json";
}
console.log("π detailDataPengangguran success ...");
+ // =========== KATEGORI GOTONG ROYONG ===========
+ // Add IDs to the kategoriKegiatan data
+ const kategoriKegiatan = kategoriKegiatanData.map((k, index) => ({
+ ...k,
+ id: `kategori-${index + 1}`
+ }));
+
+ for (const k of kategoriKegiatan) {
+ await prisma.kategoriKegiatan.upsert({
+ where: {
+ id: k.id,
+ },
+ update: {
+ nama: k.nama,
+ },
+ create: {
+ id: k.id,
+ nama: k.nama,
+ },
+ });
+ }
+
+ console.log("kategori kegiatan success ...");
+
for (const e of tujuanEdukasiLingkungan) {
await prisma.tujuanEdukasiLingkungan.upsert({
where: {
@@ -1169,6 +1164,26 @@ import fileStorage from "./data/file-storage.json";
console.log(
"β
fasilitas bimbingan belajar desa seeded (editable later via UI)"
);
+
+ for (const j of jenjangPendidikan) {
+ await prisma.jenjangPendidikan.upsert({
+ where: {
+ id: j.id || undefined,
+ },
+ update: {
+ nama: j.nama,
+ },
+ create: {
+ nama: j.nama,
+ },
+ });
+ }
+
+ console.log("β
Jenjang Pendidikan seeded successfully");
+
+ // seed assets
+ await seedAssets();
+
})()
.then(() => prisma.$disconnect())
.catch((e) => {
diff --git a/prisma/seed_assets.ts b/prisma/seed_assets.ts
new file mode 100644
index 00000000..f92c0d36
--- /dev/null
+++ b/prisma/seed_assets.ts
@@ -0,0 +1,118 @@
+// prisma/seedAssets.ts
+import fs from "fs/promises";
+import path from "path";
+import sharp from "sharp";
+import fetch from "node-fetch";
+import AdmZip from "adm-zip";
+import prisma from "@/lib/prisma";
+
+const UPLOADS_DIR =
+ process.env.WIBU_UPLOAD_DIR || path.join(process.cwd(), "uploads");
+
+// --- Helper: deteksi kategori file ---
+function detectCategory(filename: string): "image" | "document" | "other" {
+ const ext = path.extname(filename).toLowerCase();
+ if ([".jpg", ".jpeg", ".png", ".webp"].includes(ext)) return "image";
+ if ([".pdf", ".doc", ".docx"].includes(ext)) return "document";
+ return "other";
+}
+
+// --- Helper: recursive walk dir ---
+async function walkDir(dir: string, fileList: string[] = []): Promise {
+ const entries = await fs.readdir(dir, { withFileTypes: true });
+
+ for (const entry of entries) {
+ const fullPath = path.join(dir, entry.name);
+
+ if (entry.isDirectory()) {
+ if (entry.name === "__MACOSX") continue; // skip folder sampah
+ await walkDir(fullPath, fileList);
+ } else {
+ if (entry.name.startsWith(".") || entry.name === ".DS_Store") continue; // skip file sampah
+ fileList.push(fullPath);
+ }
+ }
+
+ return fileList;
+}
+
+export default async function seedAssets() {
+ console.log("π Seeding assets...");
+
+ // 1. Download zip
+ const url =
+ "https://cld-dkr-makuro-seafile.wibudev.com/f/ffd5a548a04f47939474/?dl=1";
+ const res = await fetch(url);
+ if (!res.ok) throw new Error(`Gagal download assets: ${res.statusText}`);
+ const buffer = Buffer.from(await res.arrayBuffer());
+
+ // 2. Extract zip ke folder tmp
+ const extractDir = path.join(process.cwd(), "tmp_assets");
+ await fs.rm(extractDir, { recursive: true, force: true });
+ await fs.mkdir(extractDir, { recursive: true });
+
+ const zip = new AdmZip(buffer);
+ zip.extractAllTo(extractDir, true);
+
+ // 3. Cari semua file valid (recursive)
+ const files = await walkDir(extractDir);
+
+ // 4. Loop tiap file & simpan
+ for (const filePath of files) {
+ const entryName = path.basename(filePath);
+ const category = detectCategory(entryName);
+
+ let finalName = entryName;
+ let mimeType = "application/octet-stream";
+ let targetPath = "";
+
+ if (category === "image") {
+ const fileBaseName = path.parse(entryName).name;
+ finalName = `${fileBaseName}.webp`;
+ targetPath = path.join(UPLOADS_DIR, "images", finalName);
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
+ await sharp(filePath).webp({ quality: 80 }).toFile(targetPath);
+ mimeType = "image/webp";
+ } else if (category === "document") {
+ targetPath = path.join(UPLOADS_DIR, "documents", entryName);
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
+ await fs.copyFile(filePath, targetPath);
+ mimeType = "application/pdf";
+ } else {
+ targetPath = path.join(UPLOADS_DIR, "other", entryName);
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
+ await fs.copyFile(filePath, targetPath);
+ }
+
+ // 5. Simpan ke DB
+ await prisma.fileStorage.create({
+ data: {
+ name: finalName,
+ realName: entryName,
+ path: targetPath,
+ mimeType,
+ link: `/uploads/${category}/${finalName}`,
+ category,
+ },
+ });
+
+ console.log(`π saved: ${category}/${finalName}`);
+ }
+
+ // 6. Cleanup
+ await fs.rm(extractDir, { recursive: true, force: true });
+
+ console.log("β
Selesai seed assets!");
+}
+
+// --- Auto run kalau dipanggil langsung ---
+if (import.meta.main) {
+ seedAssets()
+ .catch((err) => {
+ console.error("β Error seeding assets:", err);
+ process.exit(1);
+ })
+ .finally(async () => {
+ await prisma.$disconnect();
+ });
+}
diff --git a/public/beasiswa-siswa.png b/public/beasiswa-siswa.png
new file mode 100644
index 00000000..ed40216d
Binary files /dev/null and b/public/beasiswa-siswa.png differ
diff --git a/public/perbekel.png b/public/perbekel.png
index 7da92111..a940365b 100644
Binary files a/public/perbekel.png and b/public/perbekel.png differ
diff --git a/src/app/admin/(dashboard)/_state/desa/berita.ts b/src/app/admin/(dashboard)/_state/desa/berita.ts
index 2105e58f..0a7dc17e 100644
--- a/src/app/admin/(dashboard)/_state/desa/berita.ts
+++ b/src/app/admin/(dashboard)/_state/desa/berita.ts
@@ -75,17 +75,18 @@ const berita = proxy({
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "", kategori = "") => {
- berita.findMany.loading = true; // β
Akses langsung via nama path
+ const startTime = Date.now();
+ berita.findMany.loading = true;
berita.findMany.page = page;
berita.findMany.search = search;
-
+
try {
const query: any = { page, limit };
if (search) query.search = search;
if (kategori) query.kategori = kategori;
-
+
const res = await ApiFetch.api.desa.berita["find-many"].get({ query });
-
+
if (res.status === 200 && res.data?.success) {
berita.findMany.data = res.data.data ?? [];
berita.findMany.totalPages = res.data.totalPages ?? 1;
@@ -98,9 +99,16 @@ const berita = proxy({
berita.findMany.data = [];
berita.findMany.totalPages = 1;
} finally {
- berita.findMany.loading = false;
+ // pastikan minimal 300ms sebelum loading = false (biar UX smooth)
+ const elapsed = Date.now() - startTime;
+ const minDelay = 300;
+ const delay = elapsed < minDelay ? minDelay - elapsed : 0;
+
+ setTimeout(() => {
+ berita.findMany.loading = false;
+ }, delay);
}
- },
+ },
},
findUnique: {
diff --git a/src/app/admin/(dashboard)/_state/desa/layananDesa.ts b/src/app/admin/(dashboard)/_state/desa/layananDesa.ts
index b11b67a8..a0a39410 100644
--- a/src/app/admin/(dashboard)/_state/desa/layananDesa.ts
+++ b/src/app/admin/(dashboard)/_state/desa/layananDesa.ts
@@ -581,33 +581,24 @@ const pelayananPerizinanBerusaha = proxy({
findById: {
data: null as pelayananPerizinanBerusahaForm | null,
loading: false,
- initialize() {
- pelayananPerizinanBerusaha.findById.data = {
- id: "",
- name: "",
- deskripsi: "",
- link: "",
- } as pelayananPerizinanBerusahaForm;
- },
async load(id: string) {
try {
- pelayananPerizinanBerusaha.findById.loading = true;
- const res = await fetch(
- `/api/desa/layanan/pelayananperizinanberusaha/${id}`
- );
- if (res.ok) {
- const data = await res.json();
- pelayananPerizinanBerusaha.findById.data = data.data ?? null;
- } else {
- console.error(
- "Failed to fetch pelayanan perizinan berusaha:",
- res.statusText
- );
- pelayananPerizinanBerusaha.findById.data = null;
+ this.loading = true;
+ const response = await fetch(`/api/desa/layanan/pelayananperizinanberusaha/${id}`);
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
}
+ const result = await response.json();
+ if (result?.success) {
+ this.data = result.data; // Make sure this matches your API response structure
+ }
+ return result?.data || null;
} catch (error) {
- console.error("Error fetching pelayanan perizinan berusaha:", error);
- pelayananPerizinanBerusaha.findById.data = null;
+ console.error('Error loading data:', error);
+ toast.error('Gagal memuat data');
+ return null;
+ } finally {
+ this.loading = false;
}
},
},
diff --git a/src/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja.ts b/src/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja.ts
index 0481bbca..227c6b8c 100644
--- a/src/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja.ts
+++ b/src/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja.ts
@@ -13,6 +13,7 @@ const templateForm = z.object({
gaji: z.string(),
deskripsi: z.string(),
kualifikasi: z.string(),
+ notelp: z.string(),
});
const defaultForm = {
@@ -23,6 +24,7 @@ const defaultForm = {
gaji: "",
deskripsi: "",
kualifikasi: "",
+ notelp: "",
};
const lowonganKerjaState = proxy({
@@ -179,6 +181,7 @@ const lowonganKerjaState = proxy({
gaji: data.gaji,
deskripsi: data.deskripsi,
kualifikasi: data.kualifikasi,
+ notelp: data.notelp,
};
return data;
} else {
@@ -218,6 +221,7 @@ const lowonganKerjaState = proxy({
gaji: this.form.gaji,
deskripsi: this.form.deskripsi,
kualifikasi: this.form.kualifikasi,
+ notelp: this.form.notelp,
}),
});
if (!response.ok) {
diff --git a/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts b/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts
index e5cfb7e0..21d8e7b4 100644
--- a/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts
+++ b/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts
@@ -12,6 +12,7 @@ const templatePasarDesaForm = z.object({
imageId: z.string().min(1, "Gambar wajib dipilih"),
rating: z.number().min(1, "Rating minimal 1"),
kategoriId: z.array(z.string()).min(1, "Minimal pilih satu kategori"),
+ kontak: z.string().min(1, "Kontak wajib diisi"),
});
const defaultPasarDesaForm = {
@@ -21,6 +22,7 @@ const defaultPasarDesaForm = {
imageId: "",
rating: 0,
kategoriId: [] as string[],
+ kontak: "",
};
const pasarDesa = proxy({
@@ -188,6 +190,7 @@ const pasarDesa = proxy({
imageId: data.imageId,
rating: data.rating,
kategoriId: data.kategoriId,
+ kontak: data.kontak,
};
return data;
} else {
@@ -225,6 +228,7 @@ const pasarDesa = proxy({
imageId: this.form.imageId,
rating: this.form.rating,
kategoriId: this.form.kategoriId,
+ kontak: this.form.kontak,
}),
});
if (!response.ok) {
@@ -336,6 +340,40 @@ const kategoriProduk = proxy({
}
},
},
+ // β
Versi findManyAll (ambil semua tanpa pagination)
+ findManyAll: {
+ data: null as
+ | Prisma.KategoriProdukGetPayload<{
+ omit: { isActive: true };
+ }>[]
+ | null,
+ loading: false,
+ search: "",
+ load: async (search = "") => {
+ kategoriProduk.findManyAll.loading = true;
+ kategoriProduk.findManyAll.search = search;
+
+ try {
+ const query: any = {};
+ if (search) query.search = search;
+
+ const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many-all"].get({
+ query,
+ });
+
+ if (res.status === 200 && res.data?.success) {
+ kategoriProduk.findManyAll.data = res.data.data ?? [];
+ } else {
+ kategoriProduk.findManyAll.data = [];
+ }
+ } catch (err) {
+ console.error("Gagal fetch kategori produk (all):", err);
+ kategoriProduk.findManyAll.data = [];
+ } finally {
+ kategoriProduk.findManyAll.loading = false;
+ }
+ },
+ },
findUnique: {
data: null as Prisma.KategoriProdukGetPayload<{
omit: { isActive: true };
diff --git a/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts b/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts
index 9fc870d2..cef5c3fa 100644
--- a/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts
+++ b/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts
@@ -1,9 +1,173 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
-import { proxy } from "valtio";
-import { z } from "zod";
-import { toast } from "react-toastify";
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
+import { toast } from "react-toastify";
+import { proxy } from "valtio";
+import { z } from "zod";
+
+const templateForm = z.object({
+ name: z.string().min(3, "Nama minimal 3 karakter"),
+ imageId: z.string().min(1, "Gambar wajib dipilih"),
+});
+
+const defaultForm = {
+ name: "",
+ imageId: "",
+};
+
+type StrukturBumDesForm = Prisma.StrukturBumDesGetPayload<{
+ select: {
+ id: true;
+ name: true;
+ imageId: true;
+ image?: {
+ select: {
+ link: true;
+ };
+ };
+ };
+}>;
+
+const stateStruktur = proxy({
+ struktur: {
+ data: null as StrukturBumDesForm | null,
+ loading: false,
+ error: null as string | null,
+
+ async load(id: string) {
+ if (!id) {
+ toast.warn("ID tidak valid");
+ return null;
+ }
+
+ this.loading = true;
+ this.error = null;
+
+ try {
+ const response = await fetch(`/api/ekonomi/struktur-organisasi/${id}`);
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const result = await response.json();
+
+ if (result.success) {
+ this.data = result.data;
+ return result.data;
+ } else {
+ throw new Error(result.message || "Gagal mengambil data struktur");
+ }
+ } catch (error) {
+ const errorMessage = (error as Error).message;
+ this.error = errorMessage;
+ console.error("Load struktur error:", errorMessage);
+ toast.error("Terjadi kesalahan saat mengambil data struktur");
+ return null;
+ } finally {
+ this.loading = false;
+ }
+ },
+
+ reset() {
+ this.data = null;
+ this.error = null;
+ this.loading = false;
+ },
+ },
+
+ editStruktur: {
+ id: "",
+ form: { ...defaultForm },
+ loading: false,
+ error: null as string | null,
+ isReadOnly: false,
+
+ initialize(strukturData: StrukturBumDesForm) {
+ this.id = strukturData.id;
+ this.isReadOnly = false;
+ this.form = {
+ name: strukturData.name || "",
+ imageId: strukturData.imageId || "",
+ };
+ },
+
+ updateField(field: keyof typeof defaultForm, value: string) {
+ this.form[field] = value;
+ },
+
+ async submit() {
+ const validation = templateForm.safeParse(this.form);
+
+ if (!validation.success) {
+ const errors = validation.error.issues
+ .map((issue) => `${issue.path.join(".")}: ${issue.message}`)
+ .join(", ");
+ toast.error(`Form tidak valid: ${errors}`);
+ return false;
+ }
+
+ this.loading = true;
+ this.error = null;
+
+ try {
+ const response = await fetch(`/api/ekonomi/struktur-organisasi/${this.id}`, {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(this.form),
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}));
+ throw new Error(
+ errorData.message || `HTTP error! status: ${response.status}`
+ );
+ }
+
+ const result = await response.json();
+
+ if (result.success) {
+ toast.success("Berhasil update struktur");
+ await stateStruktur.struktur.load(this.id);
+ return true;
+ } else {
+ throw new Error(result.message || "Gagal update struktur");
+ }
+ } catch (error) {
+ const errorMessage = (error as Error).message;
+ this.error = errorMessage;
+ console.error("Update struktur error:", errorMessage);
+ toast.error("Terjadi kesalahan saat update struktur");
+ return false;
+ } finally {
+ this.loading = false;
+ }
+ },
+
+ reset() {
+ this.id = "";
+ this.form = { ...defaultForm };
+ this.error = null;
+ this.loading = false;
+ this.isReadOnly = false;
+ },
+ },
+
+ async loadForEdit(id: string) {
+ const strukturData = await this.struktur.load(id);
+ if (strukturData) {
+ this.editStruktur.initialize(strukturData);
+ }
+ return strukturData;
+ },
+
+ reset() {
+ this.struktur.reset();
+ this.editStruktur.reset();
+ },
+});
const templatePosisiOrganisasi = z.object({
nama: z.string().min(1, "Nama harus diisi"),
@@ -30,9 +194,7 @@ const posisiOrganisasi = proxy({
try {
this.loading = true;
- const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
- "posisi-organisasi"
- ]["create"].post(this.form);
+ const res = await ApiFetch.api.ekonomi['struktur-organisasi']['posisi-organisasi']['create'].post(this.form);
if (res.status === 200) {
toast.success("Berhasil menambahkan posisi organisasi");
posisiOrganisasi.findMany.load();
@@ -52,6 +214,29 @@ const posisiOrganisasi = proxy({
},
},
+ findUnique: {
+ data: null as Prisma.StrukturOrganisasiBumDesGetPayload<{
+ omit: { isActive: true };
+ }> | null,
+ async load(id: string) {
+ try {
+ const res = await fetch(
+ `/api/ekonomi/struktur-organisasi/posisi-organisasi/${id}`
+ );
+ if (res.ok) {
+ const data = await res.json();
+ posisiOrganisasi.findUnique.data = data.data ?? null;
+ } else {
+ console.error("Failed to fetch posisiOrganisasi:", res.statusText);
+ posisiOrganisasi.findUnique.data = null;
+ }
+ } catch (error) {
+ console.error("Error fetching posisiOrganisasi:", error);
+ posisiOrganisasi.findUnique.data = null;
+ }
+ },
+ },
+
edit: {
id: "",
form: { ...posisiOrganisasiDefaultForm },
@@ -165,17 +350,17 @@ const posisiOrganisasi = proxy({
totalPages: 1,
loading: false,
search: "",
- load: async (page = 1, limit = 10, search = "") => {
- posisiOrganisasi.findMany.loading = true; // β
Akses langsung via nama path
+ load: async (page = 1, limit?: number, search = "") => {
+ const appliedLimit = limit ?? 10;
posisiOrganisasi.findMany.page = page;
posisiOrganisasi.findMany.search = search;
-
+
try {
- const query: any = { page, limit };
+ const query: any = { page, limit: appliedLimit };
if (search) query.search = search;
-
- const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["find-many"].get({ query });
-
+
+ const res = await ApiFetch.api.ekonomi['struktur-organisasi']['posisi-organisasi']['find-many'].get({ query });
+
if (res.status === 200 && res.data?.success) {
posisiOrganisasi.findMany.data = res.data.data ?? [];
posisiOrganisasi.findMany.totalPages = res.data.totalPages ?? 1;
@@ -192,7 +377,42 @@ const posisiOrganisasi = proxy({
}
},
},
+ findManyAll: {
+ data: [] as Array<{
+ id: string;
+ nama: string;
+ deskripsi: string | null;
+ hierarki: number;
+ }>,
+ loading: false,
+ search: "",
+ load: async (search = "") => {
+ // Change to arrow function
+ posisiOrganisasi.findManyAll.loading = true; // Use the full path to access the property
+ posisiOrganisasi.findManyAll.search = search;
+ try {
+ const query: any = { search };
+ if (search) query.search = search;
+ const res = await ApiFetch.api.ekonomi['struktur-organisasi']['posisi-organisasi']['find-many-all'].get({
+ query,
+ });
+
+ if (res.status === 200 && res.data?.success) {
+ posisiOrganisasi.findManyAll.data = res.data.data || [];
+
+ } else {
+ console.error("Failed to load posisiOrganisasi:", res.data?.message);
+ posisiOrganisasi.findManyAll.data = [];
+ }
+ } catch (error) {
+ console.error("Error loading posisiOrganisasi:", error);
+ posisiOrganisasi.findManyAll.data = [];
+ } finally {
+ posisiOrganisasi.findManyAll.loading = false;
+ }
+ },
+ },
delete: {
loading: false,
async byId(id: string) {
@@ -231,12 +451,12 @@ const posisiOrganisasi = proxy({
const templatePegawai = z.object({
namaLengkap: z.string().min(1, "Nama wajib diisi"),
- gelarAkademik: z.string().optional(),
- imageId: z.string().nullable().optional(),
- tanggalMasuk: z.string().optional(), // ISO format
+ gelarAkademik: z.string().min(1, "Gelar Akademik wajib diisi"),
+ imageId: z.string().min(1, "Gambar wajib dipilih"),
+ tanggalMasuk: z.string().min(1, "Tanggal masuk wajib diisi"), // ISO format
email: z.string().email("Email tidak valid").optional(),
- telepon: z.string().optional(),
- alamat: z.string().optional(),
+ telepon: z.string().min(1, "Telepom wajib diisi"),
+ alamat: z.string().min(1, "Alamat wajib diisi"),
posisiId: z.string().min(1, "Posisi wajib diisi"),
isActive: z.boolean().default(true),
});
@@ -267,9 +487,9 @@ const pegawai = proxy({
try {
pegawai.create.loading = true;
- const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
- "pegawai"
- ]["create"].post(pegawai.create.form);
+ const res = await ApiFetch.api.ekonomi['struktur-organisasi'].pegawai['create'].post(
+ pegawai.create.form
+ );
if (res.status === 200) {
toast.success("Pegawai berhasil ditambahkan");
await pegawai.findMany.load();
@@ -286,45 +506,56 @@ const pegawai = proxy({
},
// In struktur-organisasi.ts
-findMany: {
- data: null as any[] | null,
- page: 1,
- totalPages: 1,
- total: 0,
- loading: false,
- load: async (page = 1, limit = 10) => { // Change to arrow function
- pegawai.findMany.loading = true; // Use the full path to access the property
- pegawai.findMany.page = page;
- try {
- const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
- "pegawai"
- ]["find-many"].get({
- query: { page, limit },
- });
+ findMany: {
+ data: null as
+ | Prisma.PegawaiBumDesGetPayload<{
+ include: {
+ image: true;
+ posisi: true;
+ };
+ }>[]
+ | null,
+ page: 1,
+ totalPages: 1,
+ total: 0,
+ loading: false,
+ search: "",
+ load: async (page = 1, limit = 10, search = "") => {
+ // Change to arrow function
+ pegawai.findMany.loading = true; // Use the full path to access the property
+ pegawai.findMany.page = page;
+ pegawai.findMany.search = search;
+ try {
+ const query: any = { page, limit };
+ if (search) query.search = search;
- if (res.status === 200 && res.data?.success) {
- pegawai.findMany.data = res.data.data || [];
- pegawai.findMany.total = res.data.total || 0;
- pegawai.findMany.totalPages = res.data.totalPages || 1;
- } else {
- console.error("Failed to load pegawai:", res.data?.message);
+ const res = await ApiFetch.api.ekonomi['struktur-organisasi'].pegawai['find-many'].get({
+ query,
+ });
+
+ if (res.status === 200 && res.data?.success) {
+ pegawai.findMany.data = res.data.data || [];
+ pegawai.findMany.total = res.data.total || 0;
+ pegawai.findMany.totalPages = res.data.totalPages || 1;
+ } else {
+ console.error("Failed to load pegawai:", res.data?.message);
+ pegawai.findMany.data = [];
+ pegawai.findMany.total = 0;
+ pegawai.findMany.totalPages = 1;
+ }
+ } catch (error) {
+ console.error("Error loading pegawai:", error);
pegawai.findMany.data = [];
pegawai.findMany.total = 0;
pegawai.findMany.totalPages = 1;
+ } finally {
+ pegawai.findMany.loading = false;
}
- } catch (error) {
- console.error("Error loading pegawai:", error);
- pegawai.findMany.data = [];
- pegawai.findMany.total = 0;
- pegawai.findMany.totalPages = 1;
- } finally {
- pegawai.findMany.loading = false;
- }
+ },
},
-},
findUnique: {
data: null as
- | (Prisma.PegawaiGetPayload<{
+ | (Prisma.PegawaiBumDesGetPayload<{
include: { posisi: true; image: true };
}> & { isActive: boolean })
| null,
@@ -350,12 +581,9 @@ findMany: {
if (!id) return toast.warn("ID tidak valid");
try {
pegawai.delete.loading = true;
- const res = await fetch(
- `/api/ekonomi/struktur-organisasi/pegawai/del/${id}`,
- {
- method: "DELETE",
- }
- );
+ const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/del/${id}`, {
+ method: "DELETE",
+ });
const json = await res.json();
if (res.ok) {
toast.success(json.message ?? "Berhasil hapus pegawai");
@@ -372,6 +600,31 @@ findMany: {
},
},
+ nonActive: {
+ loading: false,
+ async byId(id: string) {
+ if (!id) return toast.warn("ID tidak valid");
+ try {
+ pegawai.nonActive.loading = true;
+ const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/non-active/${id}`, {
+ method: "DELETE", // biasanya nonActive pakai PATCH
+ });
+ const json = await res.json();
+ if (res.ok) {
+ toast.success(json.message ?? "Pegawai berhasil dinonaktifkan");
+ await pegawai.findMany.load(); // refresh data
+ } else {
+ toast.error(json.message ?? "Gagal menonaktifkan pegawai");
+ }
+ } catch (error) {
+ console.error("Gagal nonActive:", error);
+ toast.error("Terjadi kesalahan saat menonaktifkan pegawai");
+ } finally {
+ pegawai.nonActive.loading = false;
+ }
+ },
+ },
+
edit: {
id: "",
form: { ...pegawaiDefaultForm },
@@ -384,15 +637,12 @@ findMany: {
}
try {
- const response = await fetch(
- `/api/ekonomi/struktur-organisasi/pegawai/${id}`,
- {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- },
- }
- );
+ const response = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
@@ -503,299 +753,10 @@ findMany: {
},
});
-// Schema Zod untuk form validasi
-const templateHubunganOrganisasiForm = z.object({
- atasanId: z.string().min(1, "Atasan wajib dipilih"),
- bawahanId: z.string().min(1, "Bawahan wajib dipilih"),
- tipe: z.string().optional(),
-});
-
-// Default form state
-const defaultHubunganOrganisasiForm = {
- atasanId: "",
- bawahanId: "",
- tipe: "",
-};
-
-// ====================== STATE ===========================
-const hubunganOrganisasi = proxy({
- create: {
- form: { ...defaultHubunganOrganisasiForm },
- loading: false,
- async create() {
- const cek = templateHubunganOrganisasiForm.safeParse(
- hubunganOrganisasi.create.form
- );
- if (!cek.success) {
- const err = `[${cek.error.issues
- .map((v) => `${v.path.join(".")}: ${v.message}`)
- .join("\n")}]`;
- return toast.error(err);
- }
-
- try {
- hubunganOrganisasi.create.loading = true;
- const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
- "hubungan-organisasi"
- ]["create"].post(hubunganOrganisasi.create.form);
-
- if (res.status === 200 && res.data?.success) {
- hubunganOrganisasi.findMany.load();
- return toast.success("Berhasil menambahkan hubungan organisasi");
- } else {
- return toast.error(res.data?.message || "Gagal menambahkan data");
- }
- } catch (error) {
- console.error("Gagal create:", error);
- toast.error("Terjadi kesalahan saat menambahkan");
- } finally {
- hubunganOrganisasi.create.loading = false;
- }
- },
- },
- findMany: {
- data: null as Array<{
- id: string;
- atasanId: string;
- bawahanId: string;
- tipe?: string | null;
- atasan: {
- id: string;
- namaLengkap: string;
- gelarAkademik: string | null;
- imageId: string | null;
- tanggalMasuk: Date | null;
- email: string | null;
- telepon: string | null;
- alamat: string | null;
- posisiId: string;
- isActive: boolean;
- createdAt: Date;
- updatedAt: Date;
- };
- bawahan: {
- id: string;
- namaLengkap: string;
- gelarAkademik: string | null;
- imageId: string | null;
- tanggalMasuk: Date | null;
- email: string | null;
- telepon: string | null;
- alamat: string | null;
- posisiId: string;
- isActive: boolean;
- createdAt: Date;
- updatedAt: Date;
- };
- }> | null,
-
- async load() {
- try {
- const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
- "hubungan-organisasi"
- ]["find-many"].get();
-
- if (res.status === 200) {
- hubunganOrganisasi.findMany.data = (res.data?.data ?? []).map(
- (item: any) => ({
- ...item,
- atasan: item.atasan
- ? {
- ...item.atasan,
- isActive: item.atasan.isActive ?? item.atasan.aktif ?? true,
- }
- : null,
- bawahan: item.bawahan
- ? {
- ...item.bawahan,
- isActive:
- item.bawahan.isActive ?? item.bawahan.aktif ?? true,
- }
- : null,
- })
- );
- } else {
- hubunganOrganisasi.findMany.data = [];
- }
- } catch (error) {
- console.error("Fetch list error:", error);
- toast.error("Gagal memuat data hubungan organisasi");
- hubunganOrganisasi.findMany.data = [];
- }
- },
- },
-
- findUnique: {
- data: null as {
- id: string;
- atasanId: string;
- bawahanId: string;
- tipe?: string | null;
- atasan?: {
- id: string;
- namaLengkap: string;
- gelarAkademik: string | null;
- imageId: string;
- tanggalMasuk: Date | null;
- email: string | null;
- telepon: string | null;
- alamat: string | null;
- posisiId: string;
- aktif: boolean;
- createdAt: Date;
- updatedAt: Date;
- };
- bawahan?: {
- id: string;
- namaLengkap: string;
- gelarAkademik: string | null;
- imageId: string;
- tanggalMasuk: Date | null;
- email: string | null;
- telepon: string | null;
- alamat: string | null;
- posisiId: string;
- aktif: boolean;
- createdAt: Date;
- updatedAt: Date;
- };
- } | null,
-
- async load(id: string) {
- try {
- const res = await fetch(
- `/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}`
- );
- const result = await res.json();
-
- if (res.ok && result?.success) {
- hubunganOrganisasi.findUnique.data = result.data;
- } else {
- hubunganOrganisasi.findUnique.data = null;
- toast.error(result?.message || "Gagal mengambil data");
- }
- } catch (error) {
- console.error("Find unique error:", error);
- hubunganOrganisasi.findUnique.data = null;
- }
- },
- },
-
- edit: {
- id: "",
- form: { ...defaultHubunganOrganisasiForm },
- loading: false,
-
- async load(id: string) {
- if (!id) return toast.warn("ID tidak valid");
-
- try {
- const res = await fetch(
- `/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}`
- );
- const result = await res.json();
-
- if (res.ok && result?.success) {
- const data = result.data;
- this.id = data.id;
- this.form = {
- atasanId: data.atasanId,
- bawahanId: data.bawahanId,
- tipe: data.tipe || "",
- };
- return data;
- } else {
- throw new Error(result?.message || "Gagal memuat data");
- }
- } catch (error) {
- console.error("Error loading:", error);
- toast.error(
- error instanceof Error ? error.message : "Gagal memuat data"
- );
- return null;
- }
- },
-
- async update() {
- const cek = templateHubunganOrganisasiForm.safeParse(this.form);
- if (!cek.success) {
- const err = `[${cek.error.issues
- .map((v) => `${v.path.join(".")}: ${v.message}`)
- .join("\n")}]`;
- return toast.error(err);
- }
-
- try {
- this.loading = true;
- const res = await fetch(
- `/api/ekonomi/struktur-organisasi/hubungan-organisasi/${this.id}`,
- {
- method: "PUT",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(this.form),
- }
- );
-
- const result = await res.json();
- if (res.ok && result.success) {
- await hubunganOrganisasi.findMany.load();
- toast.success("Berhasil mengupdate hubungan organisasi");
- return true;
- } else {
- throw new Error(result?.message || "Gagal mengupdate");
- }
- } catch (error) {
- console.error("Update error:", error);
- toast.error(error instanceof Error ? error.message : "Gagal update");
- return false;
- } finally {
- this.loading = false;
- }
- },
-
- reset() {
- hubunganOrganisasi.edit.id = "";
- hubunganOrganisasi.edit.form = { ...defaultHubunganOrganisasiForm };
- },
- },
-
- delete: {
- loading: false,
- async byId(id: string) {
- if (!id) return toast.warn("ID tidak valid");
-
- try {
- hubunganOrganisasi.delete.loading = true;
- const res = await fetch(
- `/api/ekonomi/struktur-organisasi/hubungan-organisasi/del/${id}`,
- {
- method: "DELETE",
- }
- );
-
- const result = await res.json();
- if (res.ok && result?.success) {
- toast.success("Hubungan organisasi berhasil dihapus");
- hubunganOrganisasi.findMany.load();
- } else {
- toast.error(result?.message || "Gagal menghapus hubungan organisasi");
- }
- } catch (error) {
- console.error("Delete error:", error);
- toast.error("Terjadi kesalahan saat menghapus");
- } finally {
- hubunganOrganisasi.delete.loading = false;
- }
- },
- },
-});
-
-const strukturorganisasiState = proxy({
+const stateStrukturBumDes = proxy({
+ stateStruktur,
posisiOrganisasi,
pegawai,
- hubunganOrganisasi,
});
-export default strukturorganisasiState;
+export default stateStrukturBumDes;
diff --git a/src/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat.ts b/src/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat.ts
index 8f96d2e2..85480912 100644
--- a/src/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat.ts
+++ b/src/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat.ts
@@ -9,12 +9,14 @@ const templateForm = z.object({
name: z.string().min(3, "Judul minimal 3 karakter"),
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
imageId: z.string().nonempty(),
+ whatsapp: z.string().min(10, "Whatsapp minimal 10 karakter"),
});
const defaultForm = {
name: "",
deskripsi: "",
imageId: "",
+ whatsapp: "",
};
const kontakDarurat = proxy({
@@ -171,6 +173,7 @@ const kontakDarurat = proxy({
name: data.name,
deskripsi: data.deskripsi,
imageId: data.imageId,
+ whatsapp: data.whatsapp,
};
return data; // Return the loaded data
} else {
@@ -207,6 +210,7 @@ const kontakDarurat = proxy({
name: this.form.name,
deskripsi: this.form.deskripsi,
imageId: this.form.imageId,
+ whatsapp: this.form.whatsapp,
}),
}
);
diff --git a/src/app/admin/(dashboard)/_state/landing-page/profile.ts b/src/app/admin/(dashboard)/_state/landing-page/profile.ts
index b2d28e0e..be6b2e7f 100644
--- a/src/app/admin/(dashboard)/_state/landing-page/profile.ts
+++ b/src/app/admin/(dashboard)/_state/landing-page/profile.ts
@@ -388,16 +388,15 @@ const pejabatDesa = proxy({
this.error = null;
try {
- const response = await fetch(
- `/api/landingpage/pejabatdesa/${this.id}`,
- {
- method: "PUT",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(this.form),
- }
- );
+ // Ensure ID is properly encoded in the URL
+ const url = new URL(`/api/landingpage/pejabatdesa/${encodeURIComponent(this.id)}`, window.location.origin);
+ const response = await fetch(url.toString(), {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(this.form),
+ });
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
diff --git a/src/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa.ts b/src/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa.ts
index d8866b37..7337d64c 100644
--- a/src/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa.ts
+++ b/src/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa.ts
@@ -332,7 +332,7 @@ const keunggulanProgram = proxy({
].post(keunggulanProgram.create.form);
if (res.status === 200) {
keunggulanProgram.findMany.load();
- return toast.success("Data Berhasil Dibuat, Silahkan Menunggu Konfirmasi dari Admin di WhatsApp");
+ return toast.success("Data Berhasil Dibuat");
}
console.log(res);
return toast.error("failed create");
diff --git a/src/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital.ts b/src/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital.ts
index da6411e9..69234af2 100644
--- a/src/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital.ts
+++ b/src/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital.ts
@@ -55,46 +55,95 @@ const dataPerpustakaan = proxy({
},
},
findMany: {
- data: null as
- | Prisma.DataPerpustakaanGetPayload<{
- include: {
- image: true;
- kategori: true;
- };
- }>[]
- | null,
- page: 1,
- totalPages: 1,
- loading: false,
- search: "",
- load: async (page = 1, limit = 10, search = "", kategori = "") => {
- dataPerpustakaan.findMany.loading = true; // β
Akses langsung via nama path
- dataPerpustakaan.findMany.page = page;
- dataPerpustakaan.findMany.search = search;
-
- try {
- const query: any = { page, limit };
- if (search) query.search = search;
- if (kategori) query.kategori = kategori;
-
- const res = await ApiFetch.api.pendidikan.perpustakaandigital.dataperpustakaan["findMany"].get({ query });
-
- if (res.status === 200 && res.data?.success) {
- dataPerpustakaan.findMany.data = res.data.data ?? [];
- dataPerpustakaan.findMany.totalPages = res.data.totalPages ?? 1;
- } else {
- dataPerpustakaan.findMany.data = [];
- dataPerpustakaan.findMany.totalPages = 1;
- }
- } catch (err) {
- console.error("Gagal fetch data perpustakaan paginated:", err);
+ data: null as
+ | Prisma.DataPerpustakaanGetPayload<{
+ include: {
+ image: true;
+ kategori: true;
+ };
+ }>[]
+ | null,
+ page: 1,
+ totalPages: 1,
+ loading: false,
+ search: "",
+ load: async (page = 1, limit = 10, search = "", kategori = "") => {
+ const startTime = Date.now();
+ dataPerpustakaan.findMany.loading = true; // β
Akses langsung via nama path
+ dataPerpustakaan.findMany.page = page;
+ dataPerpustakaan.findMany.search = search;
+
+ try {
+ const query: any = { page, limit };
+ if (search) query.search = search;
+ if (kategori) query.kategori = kategori;
+
+ const res =
+ await ApiFetch.api.pendidikan.perpustakaandigital.dataperpustakaan[
+ "findMany"
+ ].get({ query });
+
+ if (res.status === 200 && res.data?.success) {
+ dataPerpustakaan.findMany.data = res.data.data ?? [];
+ dataPerpustakaan.findMany.totalPages = res.data.totalPages ?? 1;
+ } else {
dataPerpustakaan.findMany.data = [];
dataPerpustakaan.findMany.totalPages = 1;
- } finally {
- dataPerpustakaan.findMany.loading = false;
}
- },
+ } catch (err) {
+ console.error("Gagal fetch data perpustakaan paginated:", err);
+ dataPerpustakaan.findMany.data = [];
+ dataPerpustakaan.findMany.totalPages = 1;
+ } finally {
+ // pastikan minimal 300ms sebelum loading = false (biar UX smooth)
+ const elapsed = Date.now() - startTime;
+ const minDelay = 300;
+ const delay = elapsed < minDelay ? minDelay - elapsed : 0;
+
+ setTimeout(() => {
+ dataPerpustakaan.findMany.loading = false;
+ }, delay);
+ }
},
+ },
+ findManyAll: {
+ data: null as
+ | Prisma.DataPerpustakaanGetPayload<{
+ include: {
+ image: true;
+ kategori: true;
+ };
+ }>[]
+ | null,
+ loading: false,
+ search: "",
+ load: async (search = "", kategori = "") => {
+ dataPerpustakaan.findMany.loading = true; // β
Akses langsung via nama path
+ dataPerpustakaan.findMany.search = search;
+
+ try {
+ const query: any = {};
+ if (search) query.search = search;
+ if (kategori) query.kategori = kategori;
+
+ const res =
+ await ApiFetch.api.pendidikan.perpustakaandigital.dataperpustakaan[
+ "findManyAll"
+ ].get({ query });
+
+ if (res.status === 200 && res.data?.success) {
+ dataPerpustakaan.findManyAll.data = res.data.data ?? [];
+ } else {
+ dataPerpustakaan.findManyAll.data = [];
+ }
+ } catch (err) {
+ console.error("Gagal fetch data perpustakaan paginated:", err);
+ dataPerpustakaan.findManyAll.data = [];
+ } finally {
+ dataPerpustakaan.findManyAll.loading = false;
+ }
+ },
+ },
findUnique: {
data: null as Prisma.DataPerpustakaanGetPayload<{
include: {
@@ -321,17 +370,20 @@ const kategoriBuku = proxy({
totalPages: 1,
loading: false,
search: "",
- load: async (page = 1, limit = 10, search = "") => {
+ load: async (page = 1, limit = 10, search = "") => {
kategoriBuku.findMany.loading = true; // β
Akses langsung via nama path
kategoriBuku.findMany.page = page;
kategoriBuku.findMany.search = search;
-
+
try {
const query: any = { page, limit };
if (search) query.search = search;
-
- const res = await ApiFetch.api.pendidikan.perpustakaandigital.kategoribuku["findMany"].get({ query });
-
+
+ const res =
+ await ApiFetch.api.pendidikan.perpustakaandigital.kategoribuku[
+ "findMany"
+ ].get({ query });
+
if (res.status === 200 && res.data?.success) {
kategoriBuku.findMany.data = res.data.data ?? [];
kategoriBuku.findMany.totalPages = res.data.totalPages ?? 1;
@@ -514,9 +566,319 @@ const kategoriBuku = proxy({
},
});
+const templatePeminjamanBuku = z.object({
+ nama: z.string().min(1, "Nama harus diisi"),
+ noTelp: z.string().min(1, "No Telp harus diisi"),
+ alamat: z.string().min(1, "Alamat harus diisi"),
+ bukuId: z.string().min(1, "Buku ID harus diisi"),
+ tanggalPinjam: z.string().min(1, "Tanggal Pinjam harus diisi"),
+ batasKembali: z.string().min(1, "Batas Kembali harus diisi"),
+ tanggalKembali: z.string().min(1, "Tanggal Kembali harus diisi"),
+ catatan: z.string().min(1, "Catatan harus diisi"),
+});
+
+const defaultPeminjamanBuku = {
+ nama: "",
+ noTelp: "",
+ alamat: "",
+ bukuId: "",
+ tanggalPinjam: "",
+ batasKembali: "",
+ tanggalKembali: "",
+ catatan: "",
+};
+
+interface FormEditData {
+ nama: string;
+ noTelp: string;
+ alamat: string;
+ bukuId: string;
+ buku?: {
+ id: string;
+ judul: string;
+ };
+ tanggalPinjam: string;
+ batasKembali: string;
+ tanggalKembali: string;
+ catatan: string;
+ status: "Dipinjam" | "Dikembalikan" | "Terlambat" | "Dibatalkan";
+}
+
+const editForm: FormEditData = {
+ nama: "",
+ noTelp: "",
+ alamat: "",
+ bukuId: "",
+ tanggalPinjam: "",
+ batasKembali: "",
+ tanggalKembali: "",
+ catatan: "",
+ status: "Dipinjam",
+};
+
+const peminjamanBuku = proxy({
+ create: {
+ form: { ...defaultPeminjamanBuku },
+ loading: false,
+ async create() {
+ const cek = templatePeminjamanBuku.safeParse(peminjamanBuku.create.form);
+ if (!cek.success) {
+ const err = `[${cek.error.issues
+ .map((v) => `${v.path.join(".")}`)
+ .join("\n")}] required`;
+ return toast.error(err);
+ }
+
+ try {
+ peminjamanBuku.create.loading = true;
+ const res =
+ await ApiFetch.api.pendidikan.perpustakaandigital.peminjamanbuku[
+ "create"
+ ].post(peminjamanBuku.create.form);
+ if (res.status === 200) {
+ peminjamanBuku.findMany.load();
+ return toast.success("Data Peminjaman Buku Berhasil Dibuat");
+ }
+ console.log(res);
+ return toast.error("failed create");
+ } catch (error) {
+ console.log(error);
+ return toast.error("failed create");
+ } finally {
+ peminjamanBuku.create.loading = false;
+ }
+ },
+ },
+ findMany: {
+ data: [] as Prisma.PeminjamanBukuGetPayload<{
+ include: {
+ buku: true;
+ };
+ }>[],
+ page: 1,
+ totalPages: 1,
+ loading: false,
+ search: "",
+ load: async (page = 1, limit = 10, search = "") => {
+ peminjamanBuku.findMany.loading = true; // β
Akses langsung via nama path
+ peminjamanBuku.findMany.page = page;
+ peminjamanBuku.findMany.search = search;
+
+ try {
+ const query: any = { page, limit };
+ if (search) query.search = search;
+
+ const res =
+ await ApiFetch.api.pendidikan.perpustakaandigital.peminjamanbuku[
+ "findMany"
+ ].get({ query });
+
+ if (res.status === 200 && res.data?.success) {
+ peminjamanBuku.findMany.data = res.data.data ?? [];
+ peminjamanBuku.findMany.totalPages = res.data.totalPages ?? 1;
+ } else {
+ peminjamanBuku.findMany.data = [];
+ peminjamanBuku.findMany.totalPages = 1;
+ }
+ } catch (err) {
+ console.error("Gagal fetch data peminjaman buku paginated:", err);
+ peminjamanBuku.findMany.data = [];
+ peminjamanBuku.findMany.totalPages = 1;
+ } finally {
+ peminjamanBuku.findMany.loading = false;
+ }
+ },
+ },
+ findUnique: {
+ data: null as Prisma.PeminjamanBukuGetPayload<{
+ include: {
+ buku: true;
+ };
+ }> | null,
+ loading: false,
+ async load(id: string) {
+ try {
+ const res = await fetch(
+ `/api/pendidikan/perpustakaandigital/peminjamanbuku/${id}`
+ );
+ if (res.ok) {
+ const data = await res.json();
+ peminjamanBuku.findUnique.data = data.data ?? null;
+ } else {
+ console.error("Failed to fetch data", res.status, res.statusText);
+ peminjamanBuku.findUnique.data = null;
+ }
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ peminjamanBuku.findUnique.data = null;
+ }
+ },
+ },
+ delete: {
+ loading: false,
+ async delete(id: string) {
+ if (!id) return toast.warn("ID tidak valid");
+
+ try {
+ peminjamanBuku.delete.loading = true;
+
+ const response = await fetch(
+ `/api/pendidikan/perpustakaandigital/peminjamanbuku/del/${id}`,
+ {
+ method: "DELETE",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ }
+ );
+
+ const result = await response.json();
+
+ if (response.ok && result?.success) {
+ toast.success(
+ result.message || "Data Peminjaman Buku berhasil dihapus"
+ );
+ await peminjamanBuku.findMany.load(); // refresh list
+ } else {
+ toast.error(
+ result?.message || "Gagal menghapus Data Peminjaman Buku"
+ );
+ }
+ } catch (error) {
+ console.error("Gagal delete:", error);
+ toast.error("Terjadi kesalahan saat menghapus Data Peminjaman Buku");
+ } finally {
+ peminjamanBuku.delete.loading = false;
+ }
+ },
+ },
+ update: {
+ id: "",
+ form: { ...editForm },
+ loading: false,
+ async load(id: string) {
+ if (!id) {
+ toast.warn("ID tidak valid");
+ return null;
+ }
+
+ try {
+ const response = await fetch(
+ `/api/pendidikan/perpustakaandigital/peminjamanbuku/${id}`,
+ {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ }
+ );
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const result = await response.json();
+
+ if (result?.success) {
+ const data = result.data;
+ this.id = data.id;
+ this.form = {
+ nama: data.nama,
+ noTelp: data.noTelp,
+ alamat: data.alamat,
+ bukuId: data.bukuId,
+ tanggalPinjam: data.tanggalPinjam,
+ batasKembali: data.batasKembali,
+ tanggalKembali: data.tanggalKembali,
+ catatan: data.catatan,
+ status: data.status,
+ };
+ return data; // Return the loaded data
+ } else {
+ throw new Error(result?.message || "Gagal memuat data");
+ }
+ } catch (error) {
+ console.error("Error loading peminjaman buku:", error);
+ toast.error(
+ error instanceof Error ? error.message : "Gagal memuat data"
+ );
+ return null;
+ }
+ },
+ async update() {
+ const cek = templatePeminjamanBuku.safeParse(peminjamanBuku.update.form);
+ if (!cek.success) {
+ const err = `[${cek.error.issues
+ .map((v) => `${v.path.join(".")}`)
+ .join("\n")}] required`;
+ toast.error(err);
+ return false;
+ }
+
+ try {
+ peminjamanBuku.update.loading = true;
+
+ const response = await fetch(
+ `/api/pendidikan/perpustakaandigital/peminjamanbuku/${this.id}`,
+ {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ nama: this.form.nama,
+ noTelp: this.form.noTelp,
+ alamat: this.form.alamat,
+ bukuId: this.form.bukuId,
+ tanggalPinjam: this.form.tanggalPinjam,
+ batasKembali: this.form.batasKembali,
+ tanggalKembali: this.form.tanggalKembali,
+ catatan: this.form.catatan,
+ status: this.form.status,
+ }),
+ }
+ );
+
+ 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 peminjaman buku");
+ await peminjamanBuku.findMany.load(); // refresh list
+ return true;
+ } else {
+ throw new Error(
+ result.message || "Gagal update data peminjaman buku"
+ );
+ }
+ } catch (error) {
+ console.error("Error updating data peminjaman buku:", error);
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : "Terjadi kesalahan saat update data peminjaman buku"
+ );
+ return false;
+ } finally {
+ peminjamanBuku.update.loading = false;
+ }
+ },
+ reset() {
+ peminjamanBuku.update.id = "";
+ peminjamanBuku.update.form = { ...editForm };
+ },
+ },
+});
+
const perpustakaanDigitalState = proxy({
dataPerpustakaan,
kategoriBuku,
+ peminjamanBuku,
});
export default perpustakaanDigitalState;
diff --git a/src/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik.ts b/src/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik.ts
index 363998cf..56b734d9 100644
--- a/src/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik.ts
+++ b/src/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik.ts
@@ -112,7 +112,32 @@ const statepermohonanInformasiPublik = proxy({
statepermohonanInformasiPublik.findMany.data = res.data?.data ?? [];
}
}
- }
+ },
+ findUnique: {
+ data: null as Prisma.PermohonanInformasiPublikGetPayload<{
+ include: {
+ jenisInformasiDiminta: true,
+ caraMemperolehInformasi: true,
+ caraMemperolehSalinanInformasi: true,
+ };
+ }> | null,
+ async load(id: string) {
+ try {
+ const res = await fetch(`/api/ppid/permohonaninformasipublik/${id}`);
+ if (res.ok) {
+ const data = await res.json();
+ statepermohonanInformasiPublik.findUnique.data = data.data ?? null;
+ } else {
+ console.error("Failed to fetch program inovasi:", res.statusText);
+ statepermohonanInformasiPublik.findUnique.data = null;
+ }
+ } catch (error) {
+ console.error("Error fetching program inovasi:", error);
+ statepermohonanInformasiPublik.findUnique.data = null;
+ }
+ },
+ },
+
})
const statepermohonanInformasiPublikForm = proxy({
diff --git a/src/app/admin/(dashboard)/_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi.ts b/src/app/admin/(dashboard)/_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi.ts
index 0decf48a..92373c3e 100644
--- a/src/app/admin/(dashboard)/_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi.ts
+++ b/src/app/admin/(dashboard)/_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi.ts
@@ -57,7 +57,29 @@ const permohonanKeberatanInformasi = proxy({
permohonanKeberatanInformasi.findMany.data = res.data?.data ?? [];
}
}
- }
+ },
+ findUnique: {
+ data: null as Prisma.FormulirPermohonanKeberatanGetPayload<{
+ omit: {
+ isActive: true;
+ };
+ }> | null,
+ async load(id: string) {
+ try {
+ const res = await fetch(`/api/ppid/permohonankeberataninformasipublik/${id}`);
+ if (res.ok) {
+ const data = await res.json();
+ permohonanKeberatanInformasi.findUnique.data = data.data ?? null;
+ } else {
+ console.error("Failed to fetch permohonan keberatan informasi:", res.statusText);
+ permohonanKeberatanInformasi.findUnique.data = null;
+ }
+ } catch (error) {
+ console.error("Error fetching permohonan keberatan informasi:", error);
+ permohonanKeberatanInformasi.findUnique.data = null;
+ }
+ },
+ }
});
export default permohonanKeberatanInformasi;
diff --git a/src/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID.ts b/src/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID.ts
index 9dba990e..cc326aed 100644
--- a/src/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID.ts
+++ b/src/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID.ts
@@ -381,7 +381,44 @@ const posisiOrganisasi = proxy({
}
},
},
+ findManyAll: {
+ data: [] as Array<{
+ id: string;
+ nama: string;
+ deskripsi: string | null;
+ hierarki: number;
+ }>,
+ loading: false,
+ search: "",
+ load: async (search = "") => {
+ // Change to arrow function
+ posisiOrganisasi.findManyAll.loading = true; // Use the full path to access the property
+ posisiOrganisasi.findManyAll.search = search;
+ try {
+ const query: any = { search };
+ if (search) query.search = search;
+ const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi[
+ "find-many-all"
+ ].get({
+ query,
+ });
+
+ if (res.status === 200 && res.data?.success) {
+ posisiOrganisasi.findManyAll.data = res.data.data || [];
+
+ } else {
+ console.error("Failed to load posisiOrganisasi:", res.data?.message);
+ posisiOrganisasi.findManyAll.data = [];
+ }
+ } catch (error) {
+ console.error("Error loading posisiOrganisasi:", error);
+ posisiOrganisasi.findManyAll.data = [];
+ } finally {
+ posisiOrganisasi.findManyAll.loading = false;
+ }
+ },
+ },
delete: {
loading: false,
async byId(id: string) {
@@ -524,9 +561,48 @@ const pegawai = proxy({
}
},
},
+ findManyAll: {
+ data: null as
+ | Prisma.PegawaiPPIDGetPayload<{
+ include: {
+ image: true;
+ posisi: true;
+ };
+ }>[]
+ | null,
+ loading: false,
+ search: "",
+ load: async (search = "") => {
+ // Change to arrow function
+ pegawai.findManyAll.loading = true; // Use the full path to access the property
+ pegawai.findManyAll.search = search;
+ try {
+ const query: any = { search };
+ if (search) query.search = search;
+
+ const res = await ApiFetch.api.ppid.strukturppid.pegawai[
+ "find-many-all"
+ ].get({
+ query,
+ });
+
+ if (res.status === 200 && res.data?.success) {
+ pegawai.findManyAll.data = res.data.data || [];
+ } else {
+ console.error("Failed to load pegawai:", res.data?.message);
+ pegawai.findManyAll.data = [];
+ }
+ } catch (error) {
+ console.error("Error loading pegawai:", error);
+ pegawai.findManyAll.data = [];
+ } finally {
+ pegawai.findManyAll.loading = false;
+ }
+ },
+ },
findUnique: {
data: null as
- | (Prisma.PegawaiGetPayload<{
+ | (Prisma.PegawaiPPIDGetPayload<{
include: { posisi: true; image: true };
}> & { isActive: boolean })
| null,
@@ -571,6 +647,31 @@ const pegawai = proxy({
},
},
+ nonActive: {
+ loading: false,
+ async byId(id: string) {
+ if (!id) return toast.warn("ID tidak valid");
+ try {
+ pegawai.nonActive.loading = true;
+ const res = await fetch(`/api/ppid/strukturppid/pegawai/non-active/${id}`, {
+ method: "DELETE", // biasanya nonActive pakai PATCH
+ });
+ const json = await res.json();
+ if (res.ok) {
+ toast.success(json.message ?? "Pegawai berhasil dinonaktifkan");
+ await pegawai.findMany.load(); // refresh data
+ } else {
+ toast.error(json.message ?? "Gagal menonaktifkan pegawai");
+ }
+ } catch (error) {
+ console.error("Gagal nonActive:", error);
+ toast.error("Terjadi kesalahan saat menonaktifkan pegawai");
+ } finally {
+ pegawai.nonActive.loading = false;
+ }
+ },
+ },
+
edit: {
id: "",
form: { ...pegawaiDefaultForm },
diff --git a/src/app/admin/(dashboard)/desa/berita/kategori-berita/[id]/page.tsx b/src/app/admin/(dashboard)/desa/berita/kategori-berita/[id]/page.tsx
index e42a0071..4ffdd64a 100644
--- a/src/app/admin/(dashboard)/desa/berita/kategori-berita/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/desa/berita/kategori-berita/[id]/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
+
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
import colors from '@/con/colors';
import {
@@ -10,7 +11,7 @@ import {
Stack,
TextInput,
Title,
- Tooltip
+ Tooltip,
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -24,7 +25,7 @@ function EditKategoriBerita() {
const params = useParams();
const [formData, setFormData] = useState({
- name: editState.update.form.name || '',
+ name: '',
});
useEffect(() => {
@@ -48,8 +49,16 @@ function EditKategoriBerita() {
loadKategori();
}, [params?.id]);
+ const handleChange = (e: React.ChangeEvent) => {
+ setFormData((prev) => ({
+ ...prev,
+ [e.target.name]: e.target.value,
+ }));
+ };
+
const handleSubmit = async () => {
try {
+ // update global state hanya saat submit
editState.update.form = {
...editState.update.form,
name: formData.name,
@@ -94,10 +103,11 @@ function EditKategoriBerita() {
>
setFormData({ ...formData, name: e.target.value })}
+ onChange={handleChange}
required
/>
diff --git a/src/app/admin/(dashboard)/desa/berita/kategori-berita/create/page.tsx b/src/app/admin/(dashboard)/desa/berita/kategori-berita/create/page.tsx
index 06fa984b..db9e2b6a 100644
--- a/src/app/admin/(dashboard)/desa/berita/kategori-berita/create/page.tsx
+++ b/src/app/admin/(dashboard)/desa/berita/kategori-berita/create/page.tsx
@@ -7,10 +7,9 @@ import {
Group,
Paper,
Stack,
- Text,
TextInput,
Title,
- Tooltip,
+ Tooltip
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -62,9 +61,9 @@ function CreateKategoriBerita() {
>
Nama Kategori Berita}
+ label="Nama Kategori Berita"
placeholder="Masukkan nama kategori berita"
- value={createState.create.form.name || ''}
+ defaultValue={createState.create.form.name || ''}
onChange={(e) => (createState.create.form.name = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/desa/berita/list-berita/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/berita/list-berita/[id]/edit/page.tsx
index 7c52837f..c34049cc 100644
--- a/src/app/admin/(dashboard)/desa/berita/list-berita/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/desa/berita/list-berita/[id]/edit/page.tsx
@@ -19,7 +19,12 @@ import {
Tooltip,
} from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
-import { IconArrowBack, IconPhoto, IconUpload, IconX } from "@tabler/icons-react";
+import {
+ IconArrowBack,
+ IconPhoto,
+ IconUpload,
+ IconX,
+} from "@tabler/icons-react";
import { useParams, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { toast } from "react-toastify";
@@ -33,16 +38,17 @@ function EditBerita() {
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
const [formData, setFormData] = useState({
- judul: beritaState.berita.edit.form.judul || "",
- deskripsi: beritaState.berita.edit.form.deskripsi || "",
- kategoriBeritaId: beritaState.berita.edit.form.kategoriBeritaId || "",
- content: beritaState.berita.edit.form.content || "",
- imageId: beritaState.berita.edit.form.imageId || "",
+ judul: "",
+ deskripsi: "",
+ kategoriBeritaId: "",
+ content: "",
+ imageId: "",
});
- // Load berita by id saat pertama kali
+ // Load kategori + berita
useEffect(() => {
beritaState.kategoriBerita.findMany.load();
+
const loadBerita = async () => {
const id = params?.id as string;
if (!id) return;
@@ -71,8 +77,13 @@ function EditBerita() {
loadBerita();
}, [params?.id]);
+ const handleChange = (field: string, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
+ // Update global state hanya sekali di sini
beritaState.berita.edit.form = {
...beritaState.berita.edit.form,
...formData,
@@ -103,6 +114,7 @@ function EditBerita() {
return (
+ {/* Header */}
+ {/* Form */}
- setFormData({ ...formData, judul: e.target.value })
- }
+ onChange={(e) => handleChange("judul", e.target.value)}
required
/>
-
- setFormData({ ...formData, deskripsi: e.target.value })
+ handleChange("kategoriBeritaId", val || "")}
+ label="Kategori"
+ 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}
/>
+
+
+ Deskripsi Singkat
+
+
+ setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
+ }
+ />
+
+
+
+ {/* Upload Gambar */}
Gambar Berita
@@ -160,7 +192,9 @@ function EditBerita() {
setPreviewImage(URL.createObjectURL(selectedFile));
}
}}
- onReject={() => toast.error("File tidak valid, gunakan format gambar")}
+ onReject={() =>
+ toast.error("File tidak valid, gunakan format gambar")
+ }
maxSize={5 * 1024 ** 2}
accept={{ "image/*": [] }}
radius="md"
@@ -168,7 +202,11 @@ function EditBerita() {
>
-
+
@@ -204,38 +242,20 @@ function EditBerita() {
)}
+ {/* Konten */}
Konten
{
- setFormData((prev) => ({ ...prev, content: htmlContent }));
- beritaState.berita.edit.form.content = htmlContent;
- }}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({ ...prev, content: htmlContent }))
+ }
/>
-
- setFormData({ ...formData, kategoriBeritaId: val || "" })
- }
- label="Kategori"
- 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}
- />
-
+ {/* Action */}
Deskripsi
- {data.deskripsi || '-'}
+
diff --git a/src/app/admin/(dashboard)/desa/berita/list-berita/create/page.tsx b/src/app/admin/(dashboard)/desa/berita/list-berita/create/page.tsx
index 3100163a..ab176266 100644
--- a/src/app/admin/(dashboard)/desa/berita/list-berita/create/page.tsx
+++ b/src/app/admin/(dashboard)/desa/berita/list-berita/create/page.tsx
@@ -100,7 +100,7 @@ export default function CreateBerita() {
(beritaState.berita.create.form.judul = e.target.value)}
required
/>
@@ -112,7 +112,7 @@ export default function CreateBerita() {
label: item.name,
value: item.id,
}))}
- value={beritaState.berita.create.form.kategoriBeritaId || null}
+ defaultValue={beritaState.berita.create.form.kategoriBeritaId || null}
onChange={(val: string | null) => {
if (val) {
const selected = beritaState.kategoriBerita.findMany.data?.find(
@@ -131,12 +131,17 @@ export default function CreateBerita() {
required
/>
- (beritaState.berita.create.form.deskripsi = e.target.value)}
- />
+
+
+ Deskripsi Singkat
+
+ {
+ beritaState.berita.create.form.deskripsi = htmlContent;
+ }}
+ />
+
diff --git a/src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts b/src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts
index 26fa1175..afbca78b 100644
--- a/src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts
+++ b/src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts
@@ -1,31 +1,11 @@
+// export function convertYoutubeUrlToEmbed(url: string) {
+// const videoIdMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
+// return videoIdMatch ? `https://www.youtube.com/embed/${videoIdMatch[1]}` : null;
+// }
+
export function convertYoutubeUrlToEmbed(url: string) {
- const videoIdMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
+ const videoIdMatch = url.match(
+ /(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/
+ );
return videoIdMatch ? `https://www.youtube.com/embed/${videoIdMatch[1]}` : null;
-}
-
-
-
-
-
-
-
-
-
-// (url: string): string | null {
-// const watchRegex = /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/;
-// const shortRegex = /(?:https?:\/\/)?youtu\.be\/([^?]+)/;
-
-// const matchWatch = url.match(watchRegex);
-// const matchShort = url.match(shortRegex);
-
-// if (matchWatch) {
-// return `https://www.youtube.com/embed/${matchWatch[1]}`;
-// }
-
-// if (matchShort) {
-// return `https://www.youtube.com/embed/${matchShort[1]}`;
-// }
-
-// return null;
-// }
-
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/app/admin/(dashboard)/desa/gallery/video/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/gallery/video/[id]/edit/page.tsx
index df68af39..1b4260fd 100644
--- a/src/app/admin/(dashboard)/desa/gallery/video/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/desa/gallery/video/[id]/edit/page.tsx
@@ -15,7 +15,7 @@ import {
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect, useState } from 'react';
+import { useEffect, useState, useCallback } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
import { convertYoutubeUrlToEmbed } from '../../../lib/youtube-utils';
@@ -31,17 +31,19 @@ function EditVideo() {
linkVideo: '',
});
+ // load data video sekali saat id ada
useEffect(() => {
const loadVideo = async () => {
const id = params?.id as string;
if (!id) return;
+
try {
const data = await videoState.update.load(id);
if (data) {
setFormData({
- name: data.name || '',
- deskripsi: data.deskripsi || '',
- linkVideo: data.linkVideo || '',
+ name: data.name ?? '',
+ deskripsi: data.deskripsi ?? '',
+ linkVideo: data.linkVideo ?? '',
});
}
} catch (error) {
@@ -49,10 +51,16 @@ function EditVideo() {
toast.error('Gagal memuat data video');
}
};
+
loadVideo();
}, [params?.id]);
- const embedLink = convertYoutubeUrlToEmbed(formData.linkVideo);
+ const handleChange = useCallback(
+ (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ },
+ []
+ );
const handleSubmit = async () => {
const converted = convertYoutubeUrlToEmbed(formData.linkVideo);
@@ -63,7 +71,6 @@ function EditVideo() {
try {
videoState.update.form = {
- ...videoState.update.form,
name: formData.name,
deskripsi: formData.deskripsi,
linkVideo: formData.linkVideo,
@@ -77,11 +84,18 @@ function EditVideo() {
}
};
+ const embedLink = convertYoutubeUrlToEmbed(formData.linkVideo);
+
return (
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -103,7 +117,7 @@ function EditVideo() {
label="Judul Video"
placeholder="Masukkan judul video"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange('name', e.currentTarget.value)}
required
/>
@@ -112,7 +126,7 @@ function EditVideo() {
label="Link Video YouTube"
placeholder="https://www.youtube.com/watch?v=abc123"
value={formData.linkVideo}
- onChange={(e) => setFormData({ ...formData, linkVideo: e.currentTarget.value })}
+ onChange={(e) => handleChange('linkVideo', e.currentTarget.value)}
required
/>
{embedLink && (
@@ -135,7 +149,7 @@ function EditVideo() {
setFormData({ ...formData, deskripsi: val })}
+ onChange={(val) => handleChange('deskripsi', val)}
/>
diff --git a/src/app/admin/(dashboard)/desa/gallery/video/[id]/page.tsx b/src/app/admin/(dashboard)/desa/gallery/video/[id]/page.tsx
index 52bef7cd..65a579a0 100644
--- a/src/app/admin/(dashboard)/desa/gallery/video/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/desa/gallery/video/[id]/page.tsx
@@ -102,6 +102,7 @@ function DetailVideo() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
) : (
Tidak ada deskripsi
diff --git a/src/app/admin/(dashboard)/desa/gallery/video/create/page.tsx b/src/app/admin/(dashboard)/desa/gallery/video/create/page.tsx
index 9194a15e..9badd230 100644
--- a/src/app/admin/(dashboard)/desa/gallery/video/create/page.tsx
+++ b/src/app/admin/(dashboard)/desa/gallery/video/create/page.tsx
@@ -80,7 +80,7 @@ function CreateVideo() {
{
videoState.create.form.name = e.currentTarget.value;
}}
@@ -91,7 +91,7 @@ function CreateVideo() {
setLink(e.currentTarget.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/desa/layanan/ajukan_permohonan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/layanan/ajukan_permohonan/[id]/edit/page.tsx
index 79fe9452..bd56c1c3 100644
--- a/src/app/admin/(dashboard)/desa/layanan/ajukan_permohonan/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/ajukan_permohonan/[id]/edit/page.tsx
@@ -1,17 +1,17 @@
-'use client'
/* eslint-disable react-hooks/exhaustive-deps */
+'use client'
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
import {
- Box,
- Button,
- Group,
- Paper,
- Select,
- Stack,
- TextInput,
- Title,
- Tooltip
+ Box,
+ Button,
+ Group,
+ Paper,
+ Select,
+ Stack,
+ TextInput,
+ Title,
+ Tooltip,
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -20,159 +20,159 @@ import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
function EditAjukanPermohonan() {
- const router = useRouter();
- const params = useParams();
- const stateAjukan = useProxy(stateLayananDesa.ajukanPermohonan);
+ const router = useRouter();
+ const params = useParams();
+ const stateAjukan = useProxy(stateLayananDesa.ajukanPermohonan);
- const [formData, setFormData] = useState({
- nama: stateAjukan.edit.form.nama,
- nik: stateAjukan.edit.form.nik,
- alamat: stateAjukan.edit.form.alamat,
- nomorKk: stateAjukan.edit.form.nomorKk,
- kategoriId: stateAjukan.edit.form.kategoriId,
- });
+ // State lokal form
+ const [formData, setFormData] = useState({
+ nama: '',
+ nik: '',
+ alamat: '',
+ nomorKk: '',
+ kategoriId: '',
+ });
- useEffect(() => {
- stateLayananDesa.suratKeterangan.findManyAll.load();
- const loadAjukan = async () => {
- const id = params?.id as string;
- if (!id) return;
+ // Load data awal
+ useEffect(() => {
+ stateLayananDesa.suratKeterangan.findManyAll.load();
- try {
- const data = await stateAjukan.edit.load(id);
- if (data) {
- setFormData({
- nama: data.nama || '',
- nik: data.nik || '',
- alamat: data.alamat || '',
- nomorKk: data.nomorKk || '',
- kategoriId: data.kategoriId || '',
- });
- }
- } catch (error) {
- console.error('Error loading ajukan:', error);
- toast.error('Gagal memuat data ajukan');
- }
- };
+ const loadAjukan = async () => {
+ const id = params?.id as string;
+ if (!id) return;
- loadAjukan();
- }, [params?.id]);
-
- const handleSubmit = async () => {
- try {
- stateAjukan.edit.form = {
- ...stateAjukan.edit.form,
- ...formData,
- };
- toast.success('Ajukan berhasil diperbarui!');
- router.push('/admin/desa/layanan/ajukan_permohonan');
- } catch (error) {
- console.error('Error updating ajukan:', error);
- toast.error('Terjadi kesalahan saat memperbarui ajukan');
+ try {
+ const data = await stateAjukan.edit.load(id);
+ if (data) {
+ setFormData({
+ nama: data.nama || '',
+ nik: data.nik || '',
+ alamat: data.alamat || '',
+ nomorKk: data.nomorKk || '',
+ kategoriId: data.kategoriId || '',
+ });
}
+ } catch (error) {
+ console.error('Error loading ajukan:', error);
+ toast.error('Gagal memuat data ajukan');
+ }
};
- return (
-
- {/* Back Button */}
-
-
- router.back()} p="xs" radius="md">
-
-
-
-
- Edit Ajukan Permohonan
-
-
+ loadAjukan();
+ }, [params?.id]);
- {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
+ const handleSubmit = async () => {
+ try {
+ stateAjukan.edit.form = {
+ ...stateAjukan.edit.form,
+ ...formData,
+ };
+ toast.success('Ajukan berhasil diperbarui!');
+ router.push('/admin/desa/layanan/ajukan_permohonan');
+ } catch (error) {
+ console.error('Error updating ajukan:', error);
+ toast.error('Terjadi kesalahan saat memperbarui ajukan');
+ }
+ };
+
+ return (
+
+ {/* Back Button */}
+
+
+ router.back()} p="xs" radius="md">
+
+
+
+
+ Edit Ajukan Permohonan
+
+
+
+
+
+ handleChange('nama', e.target.value)}
+ required
+ />
+
+ handleChange('nik', e.target.value)}
+ required
+ />
+
+ handleChange('alamat', e.target.value)}
+ required
+ />
+
+ handleChange('nomorKk', e.target.value)}
+ required
+ />
+
+ ({
+ label: item.name,
+ value: item.id,
+ }))}
+ value={formData.kategoriId || null}
+ onChange={(val) => handleChange('kategoriId', val || '')}
+ searchable
+ clearable
+ nothingFoundMessage="Tidak ditemukan"
+ required
+ />
+
+
+
-
- setFormData({ ...formData, nama: e.target.value })}
- required
- />
-
- setFormData({ ...formData, nik: e.target.value })}
- required
- />
-
- setFormData({ ...formData, alamat: e.target.value })}
- required
- />
-
- setFormData({ ...formData, nomorKk: e.target.value })}
- required
- />
-
- ({
- label: item.name,
- value: item.id,
- }))}
- value={formData.kategoriId || null}
- onChange={(val: string | null) => {
- if (val) {
- const selected = stateLayananDesa.suratKeterangan.findMany.data?.find(
- (item) => item.id === val
- );
- if (selected) {
- stateAjukan.edit.form.kategoriId = selected.id;
- }
- } else {
- stateAjukan.edit.form.kategoriId = '';
- }
- }}
- searchable
- clearable
- nothingFoundMessage="Tidak ditemukan"
- required
- />
-
-
-
- Simpan
-
-
-
-
-
- );
+ Simpan
+
+
+
+
+
+ );
}
export default EditAjukanPermohonan;
diff --git a/src/app/admin/(dashboard)/desa/layanan/ajukan_permohonan/[id]/page.tsx b/src/app/admin/(dashboard)/desa/layanan/ajukan_permohonan/[id]/page.tsx
index 808c0229..7d10ba08 100644
--- a/src/app/admin/(dashboard)/desa/layanan/ajukan_permohonan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/ajukan_permohonan/[id]/page.tsx
@@ -3,14 +3,14 @@ import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirma
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
import {
- Box,
- Button,
- Group,
- Paper,
- Skeleton,
- Stack,
- Text,
- Tooltip
+ Box,
+ Button,
+ Group,
+ Paper,
+ Skeleton,
+ Stack,
+ Text,
+ Tooltip
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
@@ -79,7 +79,7 @@ function DetailAjukanPermohonan() {
Nama
-
+
{data?.nama || '-'}
@@ -97,7 +97,7 @@ function DetailAjukanPermohonan() {
Alamat
-
+
{data?.alamat || '-'}
diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_penduduk_non_permanent/edit/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_penduduk_non_permanent/[id]/page.tsx
similarity index 65%
rename from src/app/admin/(dashboard)/desa/layanan/pelayanan_penduduk_non_permanent/edit/page.tsx
rename to src/app/admin/(dashboard)/desa/layanan/pelayanan_penduduk_non_permanent/[id]/page.tsx
index eed1a9a3..7f26b666 100644
--- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_penduduk_non_permanent/edit/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_penduduk_non_permanent/[id]/page.tsx
@@ -1,9 +1,20 @@
-'use client'
/* eslint-disable react-hooks/exhaustive-deps */
+'use client'
+
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
-import { Box, Button, Group, Paper, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
+import {
+ Box,
+ Button,
+ Group,
+ Paper,
+ Stack,
+ Text,
+ TextInput,
+ Title,
+ Tooltip,
+} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
@@ -12,17 +23,22 @@ import { useProxy } from 'valtio/utils';
function EditPelayananPendudukNonPermanent() {
const router = useRouter();
- const params = useParams()
- const statePendudukNonPermanent = useProxy(stateLayananDesa.pelayananPendudukNonPermanen)
- const [formData, setFormData] = useState({
- name: statePendudukNonPermanent.findById.data?.name || '',
- deskripsi: statePendudukNonPermanent.findById.data?.deskripsi || '',
- })
+ const params = useParams();
+ const statePendudukNonPermanent = useProxy(
+ stateLayananDesa.pelayananPendudukNonPermanen
+ );
+ const [formData, setFormData] = useState({
+ name: '',
+ deskripsi: '',
+ });
+
+ // Load data sekali dari backend
useEffect(() => {
- const loadPelayananPerizinan = async () => {
+ const loadData = async () => {
const id = params?.id as string;
if (!id) return;
+
try {
const data = await statePendudukNonPermanent.update.load(id);
if (data) {
@@ -32,27 +48,48 @@ function EditPelayananPendudukNonPermanent() {
});
}
} catch (error) {
- console.error("Error loading pelayanan perizinan berusaha:", error);
- toast.error("Gagal memuat data pelayanan perizinan berusaha");
+ console.error('Error loading data:', error);
+ toast.error('Gagal memuat data pelayanan penduduk non permanent');
}
};
- loadPelayananPerizinan();
+
+ loadData();
}, [params?.id]);
+ const handleChange =
+ (field: keyof typeof formData) =>
+ (value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
const handleSubmit = async () => {
- if (statePendudukNonPermanent.findById.data) {
- statePendudukNonPermanent.findById.data.name = formData.name;
- statePendudukNonPermanent.findById.data.deskripsi = formData.deskripsi;
- statePendudukNonPermanent.update.update(statePendudukNonPermanent.findById.data)
- }
- router.push('/admin/desa/layanan/pelayanan_penduduk_non_permanent')
- }
+ if (!statePendudukNonPermanent.findById.data) return;
+
+ // Update global state hanya di submit
+ const updated = {
+ ...statePendudukNonPermanent.findById.data,
+ name: formData.name,
+ deskripsi: formData.deskripsi,
+ };
+
+ await statePendudukNonPermanent.update.update(updated);
+ router.push('/admin/desa/layanan/pelayanan_penduduk_non_permanent');
+ };
+
return (
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -62,7 +99,7 @@ function EditPelayananPendudukNonPermanent() {
- setFormData({ ...formData, name: e.target.value })
- }
+ onChange={(e) => handleChange('name')(e.target.value)}
required
/>
- {/* Posisi Field */}
+ {/* Deskripsi Field */}
Deskripsi
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- }}
+ onChange={handleChange('deskripsi')}
/>
@@ -104,7 +137,9 @@ function EditPelayananPendudukNonPermanent() {
loading={statePendudukNonPermanent.update.loading}
disabled={!formData.name}
>
- {statePendudukNonPermanent.update.loading ? 'Menyimpan...' : 'Simpan Perubahan'}
+ {statePendudukNonPermanent.update.loading
+ ? 'Menyimpan...'
+ : 'Simpan Perubahan'}
{
- pelayananPendudukNonPermanen.findById.load('1');
+ pelayananPendudukNonPermanen.findById.load('edit');
}, []);
if (!pelayananPendudukNonPermanen.findById.data) {
@@ -59,7 +59,7 @@ function PelayananPendudukNonPermanent() {
radius="md"
onClick={() =>
router.push(
- '/admin/desa/layanan/pelayanan_penduduk_non_permanent/edit'
+ `/admin/desa/layanan/pelayanan_penduduk_non_permanent/${data.id}`
)
}
>
@@ -91,6 +91,7 @@ function PelayananPendudukNonPermanent() {
ta="justify"
fz={{ base: '1rem', md: '1.2rem' }}
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/edit/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/[id]/page.tsx
similarity index 52%
rename from src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/edit/page.tsx
rename to src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/[id]/page.tsx
index 05488b2d..e8f4a8b6 100644
--- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/edit/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/[id]/page.tsx
@@ -1,9 +1,20 @@
-'use client'
/* eslint-disable react-hooks/exhaustive-deps */
+'use client'
+
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
-import { Box, Button, Group, Paper, Stack, TextInput, Title, Tooltip } from '@mantine/core';
+import {
+ Box,
+ Button,
+ Group,
+ Paper,
+ Skeleton,
+ Stack,
+ TextInput,
+ Title,
+ Tooltip,
+} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
@@ -12,53 +23,90 @@ import { useProxy } from 'valtio/utils';
function EditPelayananPerizinanBerusaha() {
const router = useRouter();
- const params = useParams()
- const statePerizinanBerusaha = useProxy(stateLayananDesa.pelayananPerizinanBerusaha)
+ const params = useParams<{ id: string }>();
+ const id = params?.id; // ini langsung string
+ const state = useProxy(stateLayananDesa.pelayananPerizinanBerusaha);
+ const [loading, setLoading] = useState(true);
const [formData, setFormData] = useState({
- name: statePerizinanBerusaha.findById.data?.name || '',
- deskripsi: statePerizinanBerusaha.findById.data?.deskripsi || '',
- link: statePerizinanBerusaha.findById.data?.link || '',
- })
+ id: '',
+ name: '',
+ deskripsi: '',
+ link: '',
+ });
+ // Load data detail
useEffect(() => {
- const loadPelayananPerizinan = async () => {
- const id = params?.id as string;
- if (!id) return;
+ if (!id) {
+ toast.error("ID tidak valid");
+ return;
+ }
+
+ const loadData = async () => {
try {
- const data = await statePerizinanBerusaha.update.load(id);
+ setLoading(true);
+ const data = await state.findById.load(id);
if (data) {
setFormData({
- name: data.name || '',
- deskripsi: data.deskripsi || '',
- link: data.link || '',
+ id: data.id,
+ name: data.name || "",
+ deskripsi: data.deskripsi || "",
+ link: data.link || "",
});
+ } else {
+ toast.error("Data tidak ditemukan");
}
} catch (error) {
- console.error("Error loading pelayanan perizinan berusaha:", error);
- toast.error("Gagal memuat data pelayanan perizinan berusaha");
+ console.error("Error loading data:", error);
+ toast.error("Gagal memuat data");
+ } finally {
+ setLoading(false);
}
};
- loadPelayananPerizinan();
- }, [params?.id]);
+
+ loadData();
+ }, [id]);
+
+
+ const handleChange =
+ (field: keyof typeof formData) =>
+ (value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
const handleSubmit = async () => {
- if (statePerizinanBerusaha.findById.data) {
- statePerizinanBerusaha.findById.data.name = formData.name;
- statePerizinanBerusaha.findById.data.deskripsi = formData.deskripsi;
- statePerizinanBerusaha.findById.data.link = formData.link;
- statePerizinanBerusaha.update.update(statePerizinanBerusaha.findById.data)
+ try {
+ await state.update.update(formData);
+ router.push('/admin/desa/layanan/pelayanan_perizinan_berusaha');
+ } catch (error) {
+ console.error('Error updating pelayanan perizinan berusaha:', error);
+ toast.error('Terjadi kesalahan saat update data');
}
- router.push('/admin/desa/layanan/pelayanan_perizinan_berusaha')
+ };
+
+ if (loading) {
+ return (
+
+
+
+ );
}
return (
- {/* Header Section */}
+ {/* Header */}
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -67,9 +115,9 @@ function EditPelayananPerizinanBerusaha() {
- {/* Form Section */}
+ {/* Form */}
Edit Pelayanan Perizinan Berusaha
- {/* Nama Field */}
setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange('name')(e.target.value)}
required
/>
- {/* Link Field */}
setFormData({ ...formData, link: e.target.value })}
+ onChange={(e) => handleChange('link')(e.target.value)}
/>
- {/* Deskripsi Field */}
Deskripsi
setFormData({ ...formData, deskripsi: val })}
+ onChange={handleChange('deskripsi')}
/>
- {/* Action Buttons */}
- {statePerizinanBerusaha.update.loading ? 'Menyimpan...' : 'Simpan Perubahan'}
+ {state.update.loading ? 'Menyimpan...' : 'Simpan Perubahan'}
router.back()}
- disabled={statePerizinanBerusaha.update.loading}
+ disabled={state.update.loading}
>
Batal
diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/page.tsx
index 2c6a41fe..3edb28cd 100644
--- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/page.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import {
@@ -19,35 +20,58 @@ import {
Tooltip,
} from '@mantine/core';
import { IconEdit } from '@tabler/icons-react';
-import { useRouter } from 'next/navigation';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
import stateLayananDesa from '../../../_state/desa/layananDesa';
import { useProxy } from 'valtio/utils';
-import { useShallowEffect } from '@mantine/hooks';
+import { useRouter } from 'next/navigation';
function PerizinanBerusaha() {
const router = useRouter();
const pelayananPerizinanBerusaha = useProxy(
stateLayananDesa.pelayananPerizinanBerusaha
);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState('');
const [active, setActive] = useState(1);
const nextStep = () =>
setActive((current) => (current < 6 ? current + 1 : current));
const prevStep = () =>
setActive((current) => (current > 0 ? current - 1 : current));
- useShallowEffect(() => {
- pelayananPerizinanBerusaha.findById.load('1');
+ useEffect(() => {
+ const loadData = async () => {
+ try {
+ setLoading(true);
+ // You should get the ID from your router query or params
+ const id = 'edit'; // Replace with actual ID or get from URL params
+ await pelayananPerizinanBerusaha.findById.load(id);
+ } catch (err) {
+ setError('Gagal memuat data');
+ console.error('Error:', err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ loadData();
}, []);
- if (!pelayananPerizinanBerusaha.findById.data) {
+ if (loading) {
return (
-
+
);
}
+ if (error || !pelayananPerizinanBerusaha.findById.data) {
+ return (
+
+ {error || 'Data tidak ditemukan'}
+
+ );
+ }
+
const data = pelayananPerizinanBerusaha.findById.data;
return (
@@ -69,7 +93,7 @@ function PerizinanBerusaha() {
radius="md"
onClick={() =>
router.push(
- '/admin/desa/layanan/pelayanan_perizinan_berusaha/edit'
+ `/admin/desa/layanan/pelayanan_perizinan_berusaha/${data.id}`
)
}
>
@@ -101,6 +125,7 @@ function PerizinanBerusaha() {
ta="justify"
fz={{ base: '1rem', md: '1.2rem' }}
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
(null);
- const [previewImage2, setPreviewImage2] = useState(null);
- const [file, setFile] = useState(null);
- const [file2, setFile2] = useState(null);
+ // state lokal untuk form
const [formData, setFormData] = useState({
- name: stateSurat.edit.form.name,
- deskripsi: stateSurat.edit.form.deskripsi,
- imageId: stateSurat.edit.form.imageId,
- image2Id: stateSurat.edit.form.image2Id,
+ name: '',
+ deskripsi: '',
+ imageId: '',
+ image2Id: '',
});
+ // state file upload
+ const [file, setFile] = useState(null);
+ const [file2, setFile2] = useState(null);
+
+ // state preview gambar
+ const [previewImage, setPreviewImage] = useState(null);
+ const [previewImage2, setPreviewImage2] = useState(null);
+
+ // load data awal
useEffect(() => {
const loadSurat = async () => {
const id = params?.id as string;
@@ -46,33 +51,39 @@ function EditSuratKeterangan() {
try {
const data = await stateSurat.edit.load(id);
- if (data) {
- setFormData({
- name: data.name || '',
- deskripsi: data.deskripsi || '',
- imageId: data.imageId || '',
- image2Id: data.image2Id || '',
- });
+ if (!data) return;
- setPreviewImage(data.image?.link || null);
- setPreviewImage2(data.image2?.link || null);
- }
+ setFormData((prev) => ({
+ ...prev,
+ ...{
+ name: prev.name || data.name || "",
+ deskripsi: prev.deskripsi || data.deskripsi || "",
+ imageId: prev.imageId || data.imageId || "",
+ image2Id: prev.image2Id || data.image2Id || "",
+ },
+ }));
+
+ if (data.image?.link && !previewImage) setPreviewImage(data.image.link);
+ if (data.image2?.link && !previewImage2) setPreviewImage2(data.image2.link);
} catch (error) {
- console.error('Error loading surat:', error);
- toast.error('Gagal memuat data surat');
+ console.error("Error loading surat:", error);
+ toast.error("Gagal memuat data surat");
}
};
loadSurat();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [params?.id]);
- const handleSubmit = async () => {
- try {
- stateSurat.edit.form = {
- ...stateSurat.edit.form,
- ...formData,
- };
+
+ // handler untuk submit
+ const handleSubmit = useCallback(async () => {
+ try {
+ // update form global hanya saat submit
+ stateSurat.edit.form = { ...stateSurat.edit.form, ...formData };
+
+ // upload file 1
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
@@ -80,6 +91,7 @@ function EditSuratKeterangan() {
stateSurat.edit.form.imageId = uploaded.id;
}
+ // upload file 2
if (file2) {
const res = await ApiFetch.api.fileStorage.create.post({ file: file2, name: file2.name });
const uploaded = res.data?.data;
@@ -94,7 +106,7 @@ function EditSuratKeterangan() {
console.error('Error updating surat:', error);
toast.error('Terjadi kesalahan saat memperbarui surat');
}
- };
+ }, [formData, file, file2, router, stateSurat.edit]);
return (
@@ -119,28 +131,32 @@ function EditSuratKeterangan() {
style={{ border: '1px solid #e0e0e0' }}
>
+ {/* Input nama */}
setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}
required
/>
+ {/* Input deskripsi */}
Konten
setFormData({ ...formData, deskripsi: htmlContent })}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
+ }
/>
{/* Upload Gambar 1 */}
- Gambar Konten Pelayanan
+ Gambar Konten Pelayanan
{
diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/[id]/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/[id]/page.tsx
index 2da12ed5..59d992ac 100644
--- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/[id]/page.tsx
@@ -95,6 +95,7 @@ function DetailSuratKeterangan() {
dangerouslySetInnerHTML={{
__html: data?.deskripsi || '-',
}}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/create/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/create/page.tsx
index 2af7efc7..ba52a647 100644
--- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/create/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/create/page.tsx
@@ -106,9 +106,9 @@ function CreateSuratKeterangan() {
{/* Nama Surat */}
(stateSurat.create.form.name = val.target.value)}
- label={Nama Surat Keterangan }
+ label="Nama Surat Keterangan"
placeholder="Masukkan nama surat keterangan"
required
/>
diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/page.tsx
index db80a721..e4948edf 100644
--- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_surat_keterangan/page.tsx
@@ -119,6 +119,7 @@ function ListSuratKeterangan({ search }: { search: string }) {
diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/[id]/edit/page.tsx
index a80f7bb1..27b89bdb 100644
--- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/[id]/edit/page.tsx
@@ -8,17 +8,15 @@ import {
Group,
Paper,
Stack,
- Text,
TextInput,
Title,
- Tooltip,
+ Tooltip
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect, useState } from 'react';
+import { useCallback, useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
function EditPelayananTelunjukSakti() {
const stateTelunjukDesa = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa);
@@ -26,22 +24,24 @@ function EditPelayananTelunjukSakti() {
const params = useParams();
const [formData, setFormData] = useState({
- name: stateTelunjukDesa.edit.form.name,
- deskripsi: stateTelunjukDesa.edit.form.deskripsi,
- link: stateTelunjukDesa.edit.form.link,
+ name: '',
+ deskripsi: '',
+ link: '',
});
+ // Load data awal hanya sekali (pas ada id)
useEffect(() => {
- const loadPelayananTelunjukSakti = async () => {
+ const loadData = async () => {
const id = params?.id as string;
if (!id) return;
+
try {
const data = await stateTelunjukDesa.edit.load(id);
if (data) {
setFormData({
- name: data.name || '',
- deskripsi: data.deskripsi || '',
- link: data.link || '',
+ name: data.name ?? '',
+ deskripsi: data.deskripsi ?? '',
+ link: data.link ?? '',
});
}
} catch (error) {
@@ -49,9 +49,19 @@ function EditPelayananTelunjukSakti() {
toast.error('Gagal memuat data pelayanan telunjuk sakti');
}
};
- loadPelayananTelunjukSakti();
+
+ loadData();
}, [params?.id]);
+ // Handler input controlled
+ const handleChange = useCallback(
+ (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ },
+ []
+ );
+
+ // Submit: update global state hanya saat simpan
const handleSubmit = async () => {
try {
stateTelunjukDesa.edit.form = {
@@ -95,27 +105,25 @@ function EditPelayananTelunjukSakti() {
label="Nama Pelayanan"
placeholder="Masukkan nama pelayanan"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange('name', e.target.value)}
required
/>
- {/* Deskripsi pakai editor */}
-
-
- Deskripsi
-
- setFormData({ ...formData, deskripsi: htmlContent })}
- />
-
+ {/* Deskripsi */}
+ handleChange('deskripsi', e.target.value)}
+ label="Judul Link"
+ placeholder="Masukkan judul link"
+ required
+ />
{/* Link */}
setFormData({ ...formData, link: e.target.value })}
+ onChange={(e) => handleChange('link', e.target.value)}
/>
{/* Tombol Simpan */}
diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/[id]/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/[id]/page.tsx
index 37ca8a44..04b7a37f 100644
--- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/[id]/page.tsx
@@ -103,6 +103,7 @@ function DetailPelayananTelunjukSakti() {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
+ wordBreak: "break-word",
}}
>
{data.link}
@@ -124,6 +125,7 @@ function DetailPelayananTelunjukSakti() {
dangerouslySetInnerHTML={{
__html: data?.deskripsi || '-',
}}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/create/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/create/page.tsx
index 5cf7028f..2fd23ace 100644
--- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/create/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/create/page.tsx
@@ -8,15 +8,14 @@ import {
Group,
Paper,
Stack,
- Text,
TextInput,
Title,
- Tooltip,
+ Tooltip
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
-import { useProxy } from 'valtio/utils';
import { toast } from 'react-toastify';
+import { useProxy } from 'valtio/utils';
function CreatePelayananTelunjukDesa() {
const stateTelunjukDesa = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa);
@@ -68,33 +67,35 @@ function CreatePelayananTelunjukDesa() {
{/* Nama */}
{
stateTelunjukDesa.create.form.name = val.target.value;
}}
- label={Nama Pelayanan }
+ label="Nama Pelayanan"
placeholder="Masukkan nama pelayanan telunjuk sakti desa"
required
/>
{/* Deskripsi */}
{
stateTelunjukDesa.create.form.deskripsi = val.target.value;
}}
- label={Deskripsi }
- placeholder="Masukkan deskripsi pelayanan"
+ label="Judul Link"
+ placeholder="Masukkan judul link"
+ required
/>
{/* Link */}
{
stateTelunjukDesa.create.form.link = val.target.value;
}}
- label={Link }
+ label="Link"
placeholder="Masukkan link pelayanan"
+ required
/>
{/* Tombol Simpan */}
diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/page.tsx
index be6ea158..03752f68 100644
--- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/page.tsx
+++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_telunjuk_sakti_desa/page.tsx
@@ -261,7 +261,7 @@ function ListPelayananTelunjukSakti({ search }: { search: string }) {
-
+
diff --git a/src/app/admin/(dashboard)/desa/penghargaan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/penghargaan/[id]/edit/page.tsx
index 8e5606dc..2f68bc4e 100644
--- a/src/app/admin/(dashboard)/desa/penghargaan/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/desa/penghargaan/[id]/edit/page.tsx
@@ -24,18 +24,22 @@ import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
function EditPenghargaan() {
- const statePenghargaan = useProxy(penghargaanState)
- const router = useRouter()
- const params = useParams()
- const [previewImage, setPreviewImage] = useState(null)
- const [file, setFile] = useState(null)
- const [formData, setFormData] = useState({
- name: statePenghargaan.findUnique.data?.name || '',
- juara: statePenghargaan.findUnique.data?.juara || '',
- deskripsi: statePenghargaan.findUnique.data?.deskripsi || '',
- imageId: statePenghargaan.findUnique.data?.imageId || '',
- })
+ const statePenghargaan = useProxy(penghargaanState);
+ const router = useRouter();
+ const params = useParams();
+ const [previewImage, setPreviewImage] = useState(null);
+ const [file, setFile] = useState(null);
+
+ // Lokal formData
+ const [formData, setFormData] = useState({
+ name: '',
+ juara: '',
+ deskripsi: '',
+ imageId: '',
+ });
+
+ // Load data pertama kali
useEffect(() => {
const loadPenghargaan = async () => {
const id = params?.id as string;
@@ -56,43 +60,43 @@ function EditPenghargaan() {
}
}
} catch (error) {
- console.error("Error loading penghargaan:", error);
- toast.error("Gagal memuat data penghargaan");
+ console.error('Error loading penghargaan:', error);
+ toast.error('Gagal memuat data penghargaan');
}
};
loadPenghargaan();
}, [params?.id]);
+ // Submit
const handleSubmit = async () => {
try {
+ // Sync ke global state saat submit
statePenghargaan.edit.form = {
...statePenghargaan.edit.form,
- name: formData.name,
- juara: formData.juara,
- deskripsi: formData.deskripsi,
- imageId: formData.imageId,
- }
+ ...formData,
+ };
+ // Upload file baru (kalau ada)
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
if (!uploaded?.id) {
- return toast.error("Gagal upload gambar");
+ return toast.error('Gagal upload gambar');
}
statePenghargaan.edit.form.imageId = uploaded.id;
}
await statePenghargaan.edit.update();
- toast.success("Penghargaan berhasil diperbarui!");
- router.push("/admin/desa/penghargaan");
+ toast.success('Penghargaan berhasil diperbarui!');
+ router.push('/admin/desa/penghargaan');
} catch (error) {
- console.error("Error updating penghargaan:", error);
- toast.error("Terjadi kesalahan saat memperbarui penghargaan");
+ console.error('Error updating penghargaan:', error);
+ toast.error('Terjadi kesalahan saat memperbarui penghargaan');
}
- }
+ };
return (
@@ -123,7 +127,7 @@ function EditPenghargaan() {
label="Judul"
placeholder="Masukkan judul penghargaan"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}
required
/>
@@ -132,7 +136,7 @@ function EditPenghargaan() {
label="Juara"
placeholder="Masukkan juara"
value={formData.juara}
- onChange={(e) => setFormData({ ...formData, juara: e.target.value })}
+ onChange={(e) => setFormData((prev) => ({ ...prev, juara: e.target.value }))}
required
/>
@@ -182,7 +186,11 @@ function EditPenghargaan() {
src={previewImage}
alt="Preview Gambar"
radius="md"
- style={{ maxHeight: 220, objectFit: 'contain', border: `1px solid ${colors['blue-button']}` }}
+ style={{
+ maxHeight: 220,
+ objectFit: 'contain',
+ border: `1px solid ${colors['blue-button']}`,
+ }}
loading="lazy"
/>
@@ -196,10 +204,9 @@ function EditPenghargaan() {
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- statePenghargaan.edit.form.deskripsi = htmlContent;
- }}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
+ }
/>
diff --git a/src/app/admin/(dashboard)/desa/penghargaan/[id]/page.tsx b/src/app/admin/(dashboard)/desa/penghargaan/[id]/page.tsx
index a8d902e4..05a5774a 100644
--- a/src/app/admin/(dashboard)/desa/penghargaan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/desa/penghargaan/[id]/page.tsx
@@ -122,6 +122,7 @@ function DetailPenghargaan() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi || '-' }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/desa/penghargaan/create/page.tsx b/src/app/admin/(dashboard)/desa/penghargaan/create/page.tsx
index 94a3c4e4..dbfb717c 100644
--- a/src/app/admin/(dashboard)/desa/penghargaan/create/page.tsx
+++ b/src/app/admin/(dashboard)/desa/penghargaan/create/page.tsx
@@ -87,17 +87,17 @@ function CreatePenghargaan() {
>
(statePenghargaan.create.form.name = val.target.value)}
- label={Nama Penghargaan }
+ label="Nama Penghargaan"
placeholder="Masukkan nama penghargaan"
required
/>
(statePenghargaan.create.form.juara = val.target.value)}
- label={Juara }
+ label="Juara"
placeholder="Masukkan juara"
required
/>
diff --git a/src/app/admin/(dashboard)/desa/penghargaan/page.tsx b/src/app/admin/(dashboard)/desa/penghargaan/page.tsx
index 4e54e837..ac4de300 100644
--- a/src/app/admin/(dashboard)/desa/penghargaan/page.tsx
+++ b/src/app/admin/(dashboard)/desa/penghargaan/page.tsx
@@ -108,6 +108,7 @@ function ListPenghargaan({ search }: { search: string }) {
fz="sm"
c="dimmed"
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
diff --git a/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/[id]/page.tsx b/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/[id]/page.tsx
index fe07bfd4..c2eeb624 100644
--- a/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/[id]/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
+
import stateDesaPengumuman from '@/app/admin/(dashboard)/_state/desa/pengumuman';
import colors from '@/con/colors';
import {
@@ -11,7 +12,6 @@ import {
TextInput,
Title,
Tooltip,
- Text,
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -24,10 +24,9 @@ function EditKategoriPengumuman() {
const router = useRouter();
const params = useParams();
- const [formData, setFormData] = useState({
- name: editState.update.form.name || '',
- });
+ const [formData, setFormData] = useState({ name: '' });
+ // Load data awal sekali aja
useEffect(() => {
const loadKategori = async () => {
const id = params?.id as string;
@@ -36,9 +35,7 @@ function EditKategoriPengumuman() {
try {
const data = await editState.update.load(id);
if (data) {
- setFormData({
- name: data.name || '',
- });
+ setFormData({ name: data.name || '' });
}
} catch (error) {
console.error('Error loading kategori Pengumuman:', error);
@@ -49,8 +46,16 @@ function EditKategoriPengumuman() {
loadKategori();
}, [params?.id]);
+ const handleChange = (field: string, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
const handleSubmit = async () => {
try {
+ // Update global state hanya di sini
editState.update.form = {
...editState.update.form,
name: formData.name,
@@ -95,16 +100,10 @@ function EditKategoriPengumuman() {
>
- Nama Kategori Pengumuman
-
- }
+ label="Nama Kategori Pengumuman"
placeholder="Masukkan nama kategori Pengumuman"
value={formData.name}
- onChange={(e) =>
- setFormData({ ...formData, name: e.target.value })
- }
+ onChange={(e) => handleChange('name', e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/create/page.tsx b/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/create/page.tsx
index 1284d18f..c5e4bac0 100644
--- a/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/create/page.tsx
+++ b/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/create/page.tsx
@@ -7,10 +7,9 @@ import {
Group,
Paper,
Stack,
- Text,
TextInput,
Title,
- Tooltip,
+ Tooltip
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -62,9 +61,9 @@ function CreateKategoriPengumuman() {
>
Nama Kategori Pengumuman }
+ label="Nama Kategori Pengumuman"
placeholder="Masukkan nama kategori pengumuman"
- value={createState.create.form.name || ''}
+ defaultValue={createState.create.form.name || ''}
onChange={(e) => (createState.create.form.name = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/edit/page.tsx
index 745672ee..9e7a622f 100644
--- a/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/edit/page.tsx
@@ -28,16 +28,16 @@ function EditPengumuman() {
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 || "",
+ judul: "",
+ deskripsi: "",
+ categoryPengumumanId: "",
+ content: "",
});
- // Load pengumuman by id saat pertama kali
+ // Load kategori & pengumuman by id saat pertama kali
useEffect(() => {
editState.category.findMany.load();
+
const loadpengumuman = async () => {
const id = params?.id as string;
if (!id) return;
@@ -61,9 +61,13 @@ function EditPengumuman() {
loadpengumuman();
}, [params?.id]);
+ const handleChange = (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
- // update global state
+ // update global state hanya sekali pas submit
editState.pengumuman.edit.form = {
...editState.pengumuman.edit.form,
...formData,
@@ -109,7 +113,7 @@ function EditPengumuman() {
label="Judul Pengumuman"
placeholder="Masukkan judul"
value={formData.judul}
- onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
+ onChange={(e) => handleChange("judul", e.target.value)}
required
/>
@@ -117,17 +121,13 @@ function EditPengumuman() {
label="Deskripsi Singkat"
placeholder="Masukkan deskripsi"
value={formData.deskripsi}
- onChange={(e) =>
- setFormData({ ...formData, deskripsi: e.target.value })
- }
+ onChange={(e) => handleChange("deskripsi", e.target.value)}
required
/>
- setFormData({ ...formData, categoryPengumumanId: val || "" })
- }
+ onChange={(val) => handleChange("categoryPengumumanId", val || "")}
label="Kategori"
placeholder="Pilih kategori"
data={
@@ -150,9 +150,7 @@ function EditPengumuman() {
- setFormData({ ...formData, content: htmlContent })
- }
+ onChange={(htmlContent) => handleChange("content", htmlContent)}
/>
diff --git a/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/page.tsx b/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/page.tsx
index eaa244ba..e1847094 100644
--- a/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/page.tsx
@@ -97,7 +97,7 @@ export default function DetailPengumuman() {
Deskripsi
-
+
{data?.deskripsi || '-'}
@@ -112,6 +112,7 @@ export default function DetailPengumuman() {
dangerouslySetInnerHTML={{
__html: data?.content || '-',
}}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/create/page.tsx b/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/create/page.tsx
index fb142b99..d2d62314 100644
--- a/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/create/page.tsx
+++ b/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/create/page.tsx
@@ -68,16 +68,16 @@ function CreatePengumuman() {
{/* Judul */}
(pengumumanState.pengumuman.create.form.judul = val.target.value)}
- label={Judul }
+ label="Judul"
placeholder="Masukkan judul pengumuman"
required
/>
{/* Kategori */}
Kategori }
+ label="Kategori"
placeholder="Pilih kategori"
value={pengumumanState.pengumuman.create.form.categoryPengumumanId || ""}
onChange={(val) => {
@@ -93,9 +93,9 @@ function CreatePengumuman() {
{/* Deskripsi Singkat */}
(pengumumanState.pengumuman.create.form.deskripsi = val.target.value)}
- label={Deskripsi Singkat }
+ label="Deskripsi Singkat"
placeholder="Masukkan deskripsi singkat"
required
/>
diff --git a/src/app/admin/(dashboard)/desa/potensi/kategori-potensi/[id]/page.tsx b/src/app/admin/(dashboard)/desa/potensi/kategori-potensi/[id]/page.tsx
index 73092b8f..16d0d951 100644
--- a/src/app/admin/(dashboard)/desa/potensi/kategori-potensi/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/desa/potensi/kategori-potensi/[id]/page.tsx
@@ -24,9 +24,10 @@ function EditKategoriPotensi() {
const params = useParams();
const [formData, setFormData] = useState({
- nama: editState.update.form.nama || '',
+ nama: '',
});
+ // Load data dari backend -> isi ke formData lokal
useEffect(() => {
const loadKategori = async () => {
const id = params?.id as string;
@@ -48,8 +49,16 @@ function EditKategoriPotensi() {
loadKategori();
}, [params?.id]);
+ const handleChange = (field: string, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
const handleSubmit = async () => {
try {
+ // Update global state hanya pas submit
editState.update.form = {
...editState.update.form,
nama: formData.nama,
@@ -68,7 +77,12 @@ function EditKategoriPotensi() {
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -90,11 +104,11 @@ function EditKategoriPotensi() {
label="Nama Kategori Potensi"
placeholder="Masukkan nama kategori potensi"
value={formData.nama}
- onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
+ onChange={(e) => handleChange('nama', e.currentTarget.value)}
required
/>
-
+
Nama Kategori Potensi}
+ label="Nama Kategori Potensi"
placeholder="Masukkan nama kategori potensi"
- value={createState.create.form.nama || ''}
+ defaultValue={createState.create.form.nama || ''}
onChange={(e) => (createState.create.form.nama = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/desa/potensi/list-potensi/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/potensi/list-potensi/[id]/edit/page.tsx
index 1bcdc41a..68a453b3 100644
--- a/src/app/admin/(dashboard)/desa/potensi/list-potensi/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/desa/potensi/list-potensi/[id]/edit/page.tsx
@@ -40,12 +40,18 @@ function EditPotensi() {
imageId: "",
});
+ // handle input changes
+ const handleChange = (field: string, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
useEffect(() => {
potensiDesaState.kategoriPotensi.findMany.load();
+
const loadPotensi = async () => {
const id = params?.id as string;
if (!id) return;
-
+
try {
const data = await potensiState.edit.load(id);
if (data) {
@@ -56,7 +62,17 @@ function EditPotensi() {
content: data.content || "",
imageId: data.imageId || "",
});
-
+
+ // // merge, bukan replace
+ // setFormData((prev) => ({
+ // ...prev,
+ // name: data.name ?? prev.name,
+ // deskripsi: data.deskripsi ?? prev.deskripsi,
+ // kategoriId: data.kategoriId ?? prev.kategoriId,
+ // content: data.content ?? prev.content,
+ // imageId: data.imageId ?? prev.imageId,
+ // }));
+
if (data?.image?.link) {
setPreviewImage(data.image.link);
}
@@ -66,28 +82,34 @@ function EditPotensi() {
toast.error("Gagal memuat data potensi");
}
};
-
+
loadPotensi();
}, [params?.id]);
+
const handleSubmit = async () => {
try {
- potensiState.edit.form = {
- ...potensiState.edit.form,
- ...formData,
- };
+ let imageId = formData.imageId;
if (file) {
- const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
+ const res = await ApiFetch.api.fileStorage.create.post({
+ file,
+ name: file.name,
+ });
const uploaded = res.data?.data;
if (!uploaded?.id) {
return toast.error("Gagal upload gambar");
}
- potensiState.edit.form.imageId = uploaded.id;
+ imageId = uploaded.id;
}
+ potensiState.edit.form = {
+ ...formData,
+ imageId,
+ };
+
await potensiState.edit.update();
toast.success("Potensi berhasil diperbarui!");
router.push("/admin/desa/potensi/list-potensi");
@@ -101,7 +123,12 @@ function EditPotensi() {
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -123,21 +150,25 @@ function EditPotensi() {
label="Judul Potensi"
placeholder="Masukkan judul"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange("name", e.target.value)}
required
/>
- setFormData({ ...formData, deskripsi: e.target.value })}
- required
- />
+
+
+ Deskripsi Singkat
+
+
+ handleChange("deskripsi", htmlContent)
+ }
+ />
+
setFormData({ ...formData, kategoriId: val || "" })}
+ onChange={(val) => handleChange("kategoriId", val || "")}
label="Kategori"
placeholder="Pilih kategori"
data={
@@ -164,7 +195,9 @@ function EditPotensi() {
setPreviewImage(URL.createObjectURL(selectedFile));
}
}}
- onReject={() => toast.error("File tidak valid, gunakan format gambar")}
+ onReject={() =>
+ toast.error("File tidak valid, gunakan format gambar")
+ }
maxSize={5 * 1024 ** 2}
accept={{ "image/*": [] }}
radius="md"
@@ -172,7 +205,11 @@ function EditPotensi() {
>
-
+
@@ -214,7 +251,9 @@ function EditPotensi() {
setFormData({ ...formData, content: htmlContent })}
+ onChange={(htmlContent) =>
+ handleChange("content", htmlContent)
+ }
/>
diff --git a/src/app/admin/(dashboard)/desa/potensi/list-potensi/[id]/page.tsx b/src/app/admin/(dashboard)/desa/potensi/list-potensi/[id]/page.tsx
index dc40fae6..73531d4e 100644
--- a/src/app/admin/(dashboard)/desa/potensi/list-potensi/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/desa/potensi/list-potensi/[id]/page.tsx
@@ -25,7 +25,7 @@ export default function DetailPotensi() {
potensiState.delete.byId(selectedId);
setModalHapus(false);
setSelectedId(null);
- router.push("/admin/desa/potensi");
+ router.push("/admin/desa/potensi/list-potensi");
}
};
@@ -77,7 +77,7 @@ export default function DetailPotensi() {
Deskripsi
- {data.deskripsi || '-'}
+
@@ -103,6 +103,7 @@ export default function DetailPotensi() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.content || '-' }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/desa/potensi/list-potensi/create/page.tsx b/src/app/admin/(dashboard)/desa/potensi/list-potensi/create/page.tsx
index b7a9f1dd..6eeb29b4 100644
--- a/src/app/admin/(dashboard)/desa/potensi/list-potensi/create/page.tsx
+++ b/src/app/admin/(dashboard)/desa/potensi/list-potensi/create/page.tsx
@@ -93,25 +93,29 @@ function CreatePotensi() {
{/* Judul */}
(potensiState.create.form.name = val.target.value)}
- label={Judul }
+ label="Judul"
placeholder="Masukkan judul potensi"
required
/>
{/* Deskripsi */}
- (potensiState.create.form.deskripsi = val.target.value)}
- label={Deskripsi }
- placeholder="Masukkan deskripsi singkat"
- required
- />
+
+
+ Deskripsi Singkat
+
+ {
+ potensiState.create.form.deskripsi = htmlContent;
+ }}
+ />
+
{/* Kategori */}
Kategori}
+ label="Kategori"
placeholder="Pilih kategori"
value={potensiState.create.form.kategoriId || ""}
onChange={(val) => {
diff --git a/src/app/admin/(dashboard)/desa/potensi/list-potensi/page.tsx b/src/app/admin/(dashboard)/desa/potensi/list-potensi/page.tsx
index 2a7c6eb8..0599c55b 100644
--- a/src/app/admin/(dashboard)/desa/potensi/list-potensi/page.tsx
+++ b/src/app/admin/(dashboard)/desa/potensi/list-potensi/page.tsx
@@ -102,19 +102,25 @@ function ListPotensi({ search }: { search: string }) {
filteredData.map((item) => (
-
- {item.name}
-
+
+
+ {item.name}
+
+
- {item.kategori?.nama || '-'}
+
+ {item.kategori?.nama || '-'}
+
diff --git a/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/lambang_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/lambang_desa/page.tsx
index c0ee0456..03660631 100644
--- a/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/lambang_desa/page.tsx
+++ b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/lambang_desa/page.tsx
@@ -115,7 +115,7 @@ function Page() {
Judul}
placeholder="Judul lambang"
- value={lambangState.update.form.judul}
+ defaultValue={lambangState.update.form.judul}
onChange={(e) => lambangState.update.form.judul = e.currentTarget.value}
error={!lambangState.update.form.judul && "Judul wajib diisi"}
/>
diff --git a/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/maskot_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/maskot_desa/page.tsx
index 8926e3bf..8ead63e3 100644
--- a/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/maskot_desa/page.tsx
+++ b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/maskot_desa/page.tsx
@@ -18,7 +18,9 @@ function Page() {
const router = useRouter();
const params = useParams();
- const [images, setImages] = useState>([]);
+ const [images, setImages] = useState<
+ Array<{ file: File | null; preview: string; label: string; imageId?: string }>
+>([]);
const [formData, setFormData] = useState({
judul: '',
deskripsi: '',
@@ -55,6 +57,7 @@ function Page() {
file: null,
preview: img.image.link,
label: img.label,
+ imageId: img.image.id, // simpan id lama
})));
}
}
@@ -88,25 +91,24 @@ function Page() {
// Upload semua gambar baru
for (const img of images) {
if (!img.file) {
- // Kalau gambar lama, skip upload
- if (!img.preview) continue;
- uploadedImages.push({ imageId: '', label: img.label });
+ if (!img.imageId) continue; // kalau benar2 kosong, skip
+ uploadedImages.push({ imageId: img.imageId, label: img.label });
continue;
}
-
+
+ // upload baru
const res = await ApiFetch.api.fileStorage.create.post({
file: img.file,
name: img.file.name,
});
-
const uploaded = res.data?.data;
if (!uploaded?.id) {
toast.error("Gagal upload salah satu gambar");
return;
}
-
- uploadedImages.push({ imageId: uploaded.id, label: img.label || 'main' });
+ uploadedImages.push({ imageId: uploaded.id, label: img.label || "main" });
}
+
// Update ke global state
maskotState.update.updateField("judul", formData.judul);
@@ -175,7 +177,7 @@ function Page() {
Judul}
placeholder="Masukkan judul maskot"
- value={formData.judul}
+ defaultValue={formData.judul}
onChange={(e) => setFormData({ ...formData, judul: e.currentTarget.value })}
error={!formData.judul && "Judul wajib diisi"}
/>
diff --git a/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/sejarah_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/sejarah_desa/page.tsx
index 89bd1d64..c941dd4f 100644
--- a/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/sejarah_desa/page.tsx
+++ b/src/app/admin/(dashboard)/desa/profile/profile-desa/[id]/sejarah_desa/page.tsx
@@ -116,7 +116,7 @@ function Page() {
Judul}
placeholder="Judul sejarah"
- value={sejarahState.update.form.judul}
+ defaultValue={sejarahState.update.form.judul}
onChange={(e) => sejarahState.update.form.judul = e.currentTarget.value}
error={!sejarahState.update.form.judul && "Judul wajib diisi"}
/>
diff --git a/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx
index 218819e3..0dee1b80 100644
--- a/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx
+++ b/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx
@@ -70,7 +70,7 @@ function Page() {
-
+
@@ -118,9 +118,9 @@ function Page() {
Visi Desa
-
+
Misi Desa
-
+
@@ -162,12 +162,12 @@ function Page() {
style={{ mt: -30, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
>
- Lambang Desa
+ {lambang.judul}
-
+
@@ -214,9 +214,9 @@ function Page() {
-
+
-
+
{maskot.images.map((img, idx) => (
diff --git a/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/[id]/edit/page.tsx
index f48a8bba..56f2c2e2 100644
--- a/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/[id]/edit/page.tsx
@@ -26,15 +26,17 @@ function EditPerbekelDariMasaKeMasa() {
const state = useProxy(stateProfileDesa.mantanPerbekel);
const router = useRouter();
const params = useParams();
+
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
const [formData, setFormData] = useState({
- nama: state.update.form.nama || '',
- daerah: state.update.form.daerah || '',
- periode: state.update.form.periode || '',
- imageId: state.update.form.imageId || ''
+ nama: '',
+ daerah: '',
+ periode: '',
+ imageId: ''
});
+ // load data pertama kali
useEffect(() => {
const loadFoto = async () => {
const id = params?.id as string;
@@ -48,7 +50,9 @@ function EditPerbekelDariMasaKeMasa() {
periode: data.periode || '',
imageId: data.imageId || ''
});
- if (data?.imageGalleryFoto?.link) setPreviewImage(data.imageGalleryFoto.link);
+ if (data?.imageGalleryFoto?.link) {
+ setPreviewImage(data.imageGalleryFoto.link);
+ }
}
} catch (error) {
console.error('Error loading foto:', error);
@@ -58,8 +62,17 @@ function EditPerbekelDariMasaKeMasa() {
loadFoto();
}, [params?.id]);
+ // helper ubah state formData
+ const handleChange = (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
const handleSubmit = async () => {
try {
+ // update global state hanya sekali pas submit
state.update.form = { ...state.update.form, ...formData };
if (file) {
@@ -107,7 +120,7 @@ function EditPerbekelDariMasaKeMasa() {
label="Nama"
placeholder="Masukkan nama"
value={formData.nama}
- onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
+ onChange={(e) => handleChange('nama', e.target.value)}
required
/>
@@ -161,7 +174,7 @@ function EditPerbekelDariMasaKeMasa() {
objectFit: 'contain',
border: `1px solid ${colors['blue-button']}`,
}}
- loading='lazy'
+ loading="lazy"
/>
)}
@@ -171,7 +184,7 @@ function EditPerbekelDariMasaKeMasa() {
label="Daerah"
placeholder="Masukkan daerah"
value={formData.daerah}
- onChange={(e) => setFormData({ ...formData, daerah: e.target.value })}
+ onChange={(e) => handleChange('daerah', e.target.value)}
required
/>
@@ -179,7 +192,7 @@ function EditPerbekelDariMasaKeMasa() {
label="Periode"
placeholder="Masukkan periode"
value={formData.periode}
- onChange={(e) => setFormData({ ...formData, periode: e.target.value })}
+ onChange={(e) => handleChange('periode', e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/create/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/create/page.tsx
index ff5f7104..d2c5ca5e 100644
--- a/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/create/page.tsx
+++ b/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/create/page.tsx
@@ -70,23 +70,23 @@ function CreatePerbekelDariMasaKeMasa() {
>
Nama Perbekel}
+ label="Nama Perbekel"
placeholder="Masukkan nama perbekel"
- value={state.create.form.nama}
+ defaultValue={state.create.form.nama}
onChange={(e) => (state.create.form.nama = e.target.value)}
required
/>
Daerah}
+ label="Daerah"
placeholder="Masukkan daerah"
- value={state.create.form.daerah}
+ defaultValue={state.create.form.daerah}
onChange={(e) => (state.create.form.daerah = e.target.value)}
required
/>
Periode}
+ label="Periode"
placeholder="Masukkan periode"
- value={state.create.form.periode}
+ defaultValue={state.create.form.periode}
onChange={(e) => (state.create.form.periode = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/page.tsx
index 01367ad9..6957d8f5 100644
--- a/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/page.tsx
+++ b/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/page.tsx
@@ -75,22 +75,28 @@ function ListPerbekelDariMasaKeMasa({ search }: { search: string }) {
filteredData.map((item) => (
- {item.nama}
+
+ {item.nama}
+
- {item.periode}
+
+ {item.periode}
+
- }
- onClick={() => router.push(`/admin/desa/profile/profile-perbekel-dari-masa-ke-masa/${item.id}`)}
- >
- Detail
-
+
+ }
+ onClick={() => router.push(`/admin/desa/profile/profile-perbekel-dari-masa-ke-masa/${item.id}`)}
+ >
+ Detail
+
+
))
diff --git a/src/app/admin/(dashboard)/desa/profile/profile-perbekel/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-perbekel/page.tsx
index 1f435fea..73da958e 100644
--- a/src/app/admin/(dashboard)/desa/profile/profile-perbekel/page.tsx
+++ b/src/app/admin/(dashboard)/desa/profile/profile-perbekel/page.tsx
@@ -97,16 +97,16 @@ function Page() {
{/* Biodata & Info */}
Biodata
-
+
Pengalaman
-
+
Pengalaman Organisasi
-
+
Program Kerja Unggulan
-
+
diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/[id]/edit/page.tsx
index 7d07fd77..956f84e1 100644
--- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/[id]/edit/page.tsx
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
-'use client'
/* eslint-disable react-hooks/exhaustive-deps */
+'use client'
import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
import colors from '@/con/colors';
import {
@@ -29,13 +29,13 @@ function EditAPBDesa() {
const params = useParams();
const [formData, setFormData] = useState({
- tahun: apbState.update.form.tahun || '',
- pendapatanIds: apbState.update.form.pendapatanIds || [],
- belanjaIds: apbState.update.form.belanjaIds || [],
- pembiayaanIds: apbState.update.form.pembiayaanIds || [],
+ tahun: '',
+ pendapatanIds: [] as string[],
+ belanjaIds: [] as string[],
+ pembiayaanIds: [] as string[],
});
- // Load APB desa by id
+ // Load APB desa by id β hanya update formData, bukan global state
useEffect(() => {
const loadAPBdesa = async () => {
const id = params?.id as string;
@@ -45,7 +45,7 @@ function EditAPBDesa() {
const data = await apbState.update.load(id);
if (data) {
setFormData({
- tahun: data.tahun || 0,
+ tahun: String(data.tahun || ''),
pendapatanIds: data.pendapatan?.map((p: any) => p.id) || [],
belanjaIds: data.belanja?.map((b: any) => b.id) || [],
pembiayaanIds: data.pembiayaan?.map((p: any) => p.id) || [],
@@ -60,8 +60,13 @@ function EditAPBDesa() {
loadAPBdesa();
}, [params?.id]);
+ const handleChange = (field: keyof typeof formData, value: any) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
+ // update global state cuma pas submit
apbState.update.form = {
...apbState.update.form,
tahun: Number(formData.tahun),
@@ -112,9 +117,7 @@ function EditAPBDesa() {
- setFormData({ ...formData, tahun: e.target.value })
- }
+ onChange={(e) => handleChange("tahun", e.target.value)}
label={Tahun }
placeholder="Masukkan tahun anggaran"
required
@@ -123,23 +126,17 @@ function EditAPBDesa() {
{/* Selects */}
- setFormData({ ...formData, pendapatanIds: ids })
- }
+ onSelectionChange={(ids) => handleChange("pendapatanIds", ids)}
/>
- setFormData({ ...formData, belanjaIds: ids })
- }
+ onSelectionChange={(ids) => handleChange("belanjaIds", ids)}
/>
- setFormData({ ...formData, pembiayaanIds: ids })
- }
+ onSelectionChange={(ids) => handleChange("pembiayaanIds", ids)}
/>
{/* Save Button */}
@@ -164,7 +161,13 @@ function EditAPBDesa() {
/* --- Sub Components --- */
- function SelectPendapatan({ selectedIds, onSelectionChange }: { selectedIds: string[]; onSelectionChange: (ids: string[]) => void }) {
+ function SelectPendapatan({
+ selectedIds,
+ onSelectionChange,
+ }: {
+ selectedIds: string[];
+ onSelectionChange: (ids: string[]) => void;
+ }) {
const pendapatanState = useProxy(PendapatanAsliDesa.pendapatan);
useShallowEffect(() => {
@@ -192,7 +195,13 @@ function EditAPBDesa() {
);
}
- function SelectBelanja({ selectedIds, onSelectionChange }: { selectedIds: string[]; onSelectionChange: (ids: string[]) => void }) {
+ function SelectBelanja({
+ selectedIds,
+ onSelectionChange,
+ }: {
+ selectedIds: string[];
+ onSelectionChange: (ids: string[]) => void;
+ }) {
const belanjaState = useProxy(PendapatanAsliDesa.belanja);
useShallowEffect(() => {
@@ -220,7 +229,13 @@ function EditAPBDesa() {
);
}
- function SelectPembiayaan({ selectedIds, onSelectionChange }: { selectedIds: string[]; onSelectionChange: (ids: string[]) => void }) {
+ function SelectPembiayaan({
+ selectedIds,
+ onSelectionChange,
+ }: {
+ selectedIds: string[];
+ onSelectionChange: (ids: string[]) => void;
+ }) {
const pembiayaanState = useProxy(PendapatanAsliDesa.pembiayaan);
useShallowEffect(() => {
diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/create/page.tsx
index 174d0c9a..797cc92f 100644
--- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/create/page.tsx
@@ -65,7 +65,7 @@ function CreateAPBDesa() {
{
apbDesaState.create.form.tahun = Number(val.target.value);
}}
diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/[id]/page.tsx
index ea3729d2..7c03d4a8 100644
--- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/[id]/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
+
import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
import colors from '@/con/colors';
import {
@@ -24,12 +25,16 @@ function EditBelanja() {
const params = useParams();
const [formData, setFormData] = useState({
- name: belanjaState.update.form.name || '',
- value: belanjaState.update.form.value || '',
+ name: '',
+ value: '',
});
+ // format angka ke rupiah
const formatRupiah = (value: number | string) => {
- const number = typeof value === 'number' ? value : Number(value.replace(/\D/g, ''));
+ const number =
+ typeof value === 'number'
+ ? value
+ : Number(value.replace(/\D/g, '')) || 0;
return new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
@@ -37,8 +42,9 @@ function EditBelanja() {
}).format(number);
};
+ // buang semua simbol jadi angka murni
const unformatRupiah = (value: string) => {
- return Number(value.replace(/\D/g, ''));
+ return Number(value.replace(/\D/g, '')) || 0;
};
useEffect(() => {
@@ -51,7 +57,7 @@ function EditBelanja() {
if (data) {
setFormData({
name: data.name || '',
- value: data.value || '',
+ value: String(data.value || ''),
});
}
} catch (error) {
@@ -69,7 +75,7 @@ function EditBelanja() {
...belanjaState.update.form,
name: formData.name,
value: Number(formData.value),
- }
+ };
await belanjaState.update.update();
toast.success("Jenis Belanja berhasil diperbarui!");
@@ -78,7 +84,7 @@ function EditBelanja() {
console.error("Error updating jenis belanja:", error);
toast.error("Terjadi kesalahan saat memperbarui jenis belanja");
}
- }
+ };
return (
@@ -113,18 +119,20 @@ function EditBelanja() {
label="Nama Jenis Belanja"
placeholder="Masukkan nama jenis belanja"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) =>
+ setFormData({ ...formData, name: e.target.value })
+ }
required
/>
{
const raw = e.currentTarget.value;
const cleanValue = unformatRupiah(raw);
- setFormData({ ...formData, value: cleanValue });
+ setFormData({ ...formData, value: String(cleanValue) });
}}
required
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/create/page.tsx
index 76bc332c..7cc80cf9 100644
--- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/create/page.tsx
@@ -85,7 +85,7 @@ function CreateBelanja() {
Nama Jenis Belanja}
placeholder="Masukkan nama jenis belanja"
- value={belanjaState.create.form.name}
+ defaultValue={belanjaState.create.form.name}
onChange={(e) => (belanjaState.create.form.name = e.target.value)}
required
/>
@@ -94,7 +94,7 @@ function CreateBelanja() {
type="text"
label={Nilai }
placeholder="Masukkan nilai belanja"
- value={formatRupiah(belanjaState.create.form.value)}
+ defaultValue={formatRupiah(belanjaState.create.form.value)}
onChange={(e) => {
const raw = e.currentTarget.value;
belanjaState.create.form.value = unformatRupiah(raw);
diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/[id]/page.tsx
index 0492b8ee..25df9c5b 100644
--- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/[id]/page.tsx
@@ -24,12 +24,15 @@ function EditPembiayaan() {
const params = useParams();
const [formData, setFormData] = useState({
- name: pembiayaanState.update.form.name || '',
- value: pembiayaanState.update.form.value || '',
+ name: '',
+ value: '',
});
const formatRupiah = (value: number | string) => {
- const number = typeof value === 'number' ? value : Number(value.toString().replace(/\D/g, ''));
+ const number =
+ typeof value === 'number'
+ ? value
+ : Number(value.toString().replace(/\D/g, '')) || 0;
return new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
@@ -38,7 +41,7 @@ function EditPembiayaan() {
};
const unformatRupiah = (value: string) => {
- return Number(value.replace(/\D/g, ''));
+ return Number(value.replace(/\D/g, '')) || 0;
};
useEffect(() => {
@@ -51,7 +54,7 @@ function EditPembiayaan() {
if (data) {
setFormData({
name: data.name || '',
- value: data.value || '',
+ value: String(data.value || ''),
});
}
} catch (error) {
@@ -68,7 +71,7 @@ function EditPembiayaan() {
pembiayaanState.update.form = {
...pembiayaanState.update.form,
name: formData.name,
- value: Number(formData.value),
+ value: unformatRupiah(formData.value),
};
await pembiayaanState.update.update();
@@ -82,7 +85,7 @@ function EditPembiayaan() {
return (
- {/* Header dengan Back Button */}
+ {/* Header */}
setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) =>
+ setFormData((prev) => ({ ...prev, name: e.target.value }))
+ }
required
/>
{
const raw = e.currentTarget.value;
const cleanValue = unformatRupiah(raw);
- setFormData({ ...formData, value: cleanValue });
+ setFormData((prev) => ({ ...prev, value: String(cleanValue) }));
}}
required
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/create/page.tsx
index ff6e17ff..a432a431 100644
--- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/create/page.tsx
@@ -85,7 +85,7 @@ function CreatePembiayaan() {
Nama Jenis Pembiayaan}
placeholder="Masukkan nama jenis pembiayaan"
- value={pembiayaanState.create.form.name}
+ defaultValue={pembiayaanState.create.form.name}
onChange={(e) => {
pembiayaanState.create.form.name = e.currentTarget.value;
}}
@@ -96,7 +96,7 @@ function CreatePembiayaan() {
type="text"
label={Nilai }
placeholder="Masukkan nilai"
- value={formatRupiah(pembiayaanState.create.form.value)}
+ defaultValue={formatRupiah(pembiayaanState.create.form.value)}
onChange={(e) => {
const raw = e.currentTarget.value;
pembiayaanState.create.form.value = unformatRupiah(raw);
diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pendapatan/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pendapatan/[id]/page.tsx
index 1970c267..cf8e65f0 100644
--- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pendapatan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pendapatan/[id]/page.tsx
@@ -24,12 +24,16 @@ function EditPendapatan() {
const params = useParams();
const [formData, setFormData] = useState({
- name: pendapatanState.update.form.name || '',
- value: pendapatanState.update.form.value || '',
+ name: '',
+ value: '',
});
+ // helper format
const formatRupiah = (value: number | string) => {
- const number = typeof value === 'number' ? value : Number(value.toString().replace(/\D/g, ''));
+ const number = typeof value === 'number'
+ ? value
+ : Number(value.toString().replace(/\D/g, ''));
+
return new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
@@ -39,6 +43,7 @@ function EditPendapatan() {
const unformatRupiah = (value: string) => Number(value.replace(/\D/g, ''));
+ // load data once
useEffect(() => {
const id = params?.id as string;
if (!id) return;
@@ -48,8 +53,8 @@ function EditPendapatan() {
const data = await pendapatanState.update.load(id);
if (data) {
setFormData({
- name: data.name || '',
- value: data.value || '',
+ name: data.name ?? '',
+ value: data.value?.toString() ?? '',
});
}
} catch (error) {
@@ -61,6 +66,13 @@ function EditPendapatan() {
loadPendapatan();
}, [params?.id]);
+ const handleChange = (field: string, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
const handleSubmit = async () => {
try {
pendapatanState.update.form = {
@@ -111,18 +123,18 @@ function EditPendapatan() {
label="Nama Jenis Pendapatan"
placeholder="Masukkan nama jenis pendapatan"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange('name', e.target.value)}
required
/>
{
const raw = e.currentTarget.value;
- const cleanValue = unformatRupiah(raw);
- setFormData({ ...formData, value: cleanValue });
+ const cleanValue = unformatRupiah(raw).toString();
+ handleChange('value', cleanValue);
}}
required
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pendapatan/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pendapatan/create/page.tsx
index 9ff6882f..663ae35b 100644
--- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pendapatan/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pendapatan/create/page.tsx
@@ -7,10 +7,9 @@ import {
Group,
Paper,
Stack,
- Text,
TextInput,
Title,
- Tooltip,
+ Tooltip
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -76,32 +75,24 @@ function CreatePendapatan() {
>
{
pendapatanState.create.form.name = val.target.value;
}}
- label={
-
- Nama Jenis Pendapatan
-
- }
+ label="Nama Jenis Pendapatan"
placeholder="Masukkan nama jenis pendapatan"
required
/>
{
const raw = val.currentTarget.value;
const cleanValue = unformatRupiah(raw);
pendapatanState.create.form.value = cleanValue;
}}
- label={
-
- Nilai
-
- }
+ label="Nilai"
placeholder="Masukkan nilai"
required
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/[id]/page.tsx
index 20e7e339..016cb96b 100644
--- a/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/[id]/page.tsx
@@ -10,47 +10,80 @@ import {
Stack,
TextInput,
Title,
- Tooltip
+ Tooltip,
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect } from 'react';
+import { useEffect, useState, useCallback } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
import demografiPekerjaan from '../../../_state/ekonomi/demografi-pekerjaan';
-function EditDemografiPekerjaan() {
+interface FormData {
+ pekerjaan: string;
+ lakiLaki: number;
+ perempuan: number;
+}
+
+export default function EditDemografiPekerjaan() {
const router = useRouter();
- const params = useParams() as { id: string };
+ const { id } = useParams() as { id: string };
const stateDemografi = useProxy(demografiPekerjaan);
- const id = params.id;
+ const [formData, setFormData] = useState({
+ pekerjaan: '',
+ lakiLaki: 0,
+ perempuan: 0,
+ });
+ // β
Load data hanya sekali di awal (tidak reset form)
useEffect(() => {
if (!id) return;
- stateDemografi.update.id = id;
- stateDemografi.findUnique
- .load(id)
- .then(() => {
+
+ const loadData = async () => {
+ try {
+ stateDemografi.update.id = id;
+ await stateDemografi.findUnique.load(id);
+
const data = stateDemografi.findUnique.data;
if (data) {
- stateDemografi.update.form = {
- pekerjaan: String(data.pekerjaan || ''),
- lakiLaki: Number(data.lakiLaki || 0),
- perempuan: Number(data.perempuan || 0),
- };
+ setFormData({
+ pekerjaan: data.pekerjaan ?? '',
+ lakiLaki: Number(data.lakiLaki ?? 0),
+ perempuan: Number(data.perempuan ?? 0),
+ });
}
- })
- .catch((error) => {
+ } catch (error) {
console.error('Error loading data:', error);
toast.error('Gagal memuat data');
- });
+ }
+ };
+
+ loadData();
}, [id]);
+ // β
Handler input terkontrol (tidak buat re-render berlebihan)
+ const handleChange = useCallback(
+ (field: keyof FormData) =>
+ (e: React.ChangeEvent) => {
+ const value =
+ field === 'lakiLaki' || field === 'perempuan'
+ ? Number(e.currentTarget.value)
+ : e.currentTarget.value;
+
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ },
+ []
+ );
+
+ // β
Submit hanya update global state sekali
const handleSubmit = async () => {
try {
stateDemografi.update.id = id;
+ stateDemografi.update.form = { ...formData };
+
await stateDemografi.update.submit();
+
toast.success('Data berhasil diperbarui');
router.push('/admin/ekonomi/demografi-pekerjaan');
} catch (error) {
@@ -91,10 +124,8 @@ function EditDemografiPekerjaan() {
- (stateDemografi.update.form.pekerjaan = e.currentTarget.value)
- }
+ value={formData.pekerjaan}
+ onChange={handleChange('pekerjaan')}
required
/>
@@ -102,12 +133,8 @@ function EditDemografiPekerjaan() {
label="Jumlah Pekerja Laki-laki"
type="number"
placeholder="Masukkan jumlah pekerja laki-laki"
- value={stateDemografi.update.form.lakiLaki}
- onChange={(e) =>
- (stateDemografi.update.form.lakiLaki = Number(
- e.currentTarget.value
- ))
- }
+ value={formData.lakiLaki}
+ onChange={handleChange('lakiLaki')}
required
/>
@@ -115,12 +142,8 @@ function EditDemografiPekerjaan() {
label="Jumlah Pekerja Perempuan"
type="number"
placeholder="Masukkan jumlah pekerja perempuan"
- value={stateDemografi.update.form.perempuan}
- onChange={(e) =>
- (stateDemografi.update.form.perempuan = Number(
- e.currentTarget.value
- ))
- }
+ value={formData.perempuan}
+ onChange={handleChange('perempuan')}
required
/>
@@ -143,5 +166,3 @@ function EditDemografiPekerjaan() {
);
}
-
-export default EditDemografiPekerjaan;
diff --git a/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/create/page.tsx
index f7ef972e..d590b347 100644
--- a/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/create/page.tsx
@@ -77,7 +77,7 @@ function CreateDemografiPekerjaan() {
{
stateDemografi.create.form.pekerjaan = val.currentTarget.value;
@@ -87,7 +87,7 @@ function CreateDemografiPekerjaan() {
{
stateDemografi.create.form.lakiLaki = Number(val.currentTarget.value);
@@ -97,7 +97,7 @@ function CreateDemografiPekerjaan() {
{
stateDemografi.create.form.perempuan = Number(val.currentTarget.value);
diff --git a/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/page.tsx b/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/page.tsx
index cc679025..0b2d4a92 100644
--- a/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/page.tsx
@@ -126,9 +126,9 @@ function ListDemografiPekerjaan({ search }: { search: string }) {
- Pekerjaan
- Laki - Laki
- Perempuan
+ Pekerjaan
+ Laki - Laki
+ Perempuan
Edit
Hapus
@@ -137,9 +137,9 @@ function ListDemografiPekerjaan({ search }: { search: string }) {
{filteredData.length > 0 ? (
filteredData.map((item) => (
- {item.pekerjaan}
- {item.lakiLaki}
- {item.perempuan}
+ {item.pekerjaan}
+ {item.lakiLaki}
+ {item.perempuan}
{
if (!id) return;
@@ -25,10 +41,10 @@ function EditJumlahPendudukMiskin() {
await stateJPM.findUnique.load(id);
const data = stateJPM.findUnique.data;
if (data) {
- stateJPM.update.form = {
+ setFormData({
year: data.year || 0,
totalPoorPopulation: data.totalPoorPopulation || 0,
- };
+ });
}
} catch (error) {
console.error('Gagal memuat data:', error);
@@ -39,9 +55,21 @@ function EditJumlahPendudukMiskin() {
loadData();
}, [id]);
+ // πΉ Handler input controlled
+ const handleChange = (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: Number(value),
+ }));
+ };
+
+ // πΉ Submit form
const handleSubmit = async () => {
try {
stateJPM.update.id = id;
+ // update global state cuma saat submit
+ stateJPM.update.form = { ...formData };
+
await stateJPM.update.submit();
toast.success('Data jumlah penduduk miskin berhasil diperbarui!');
router.push('/admin/ekonomi/jumlah-penduduk-miskin');
@@ -55,7 +83,12 @@ function EditJumlahPendudukMiskin() {
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -78,10 +111,8 @@ function EditJumlahPendudukMiskin() {
placeholder="Masukkan tahun"
type="number"
required
- value={stateJPM.update.form.year}
- onChange={(val) => {
- stateJPM.update.form.year = Number(val.currentTarget.value);
- }}
+ value={formData.year}
+ onChange={(e) => handleChange('year', e.currentTarget.value)}
/>
{
- stateJPM.update.form.totalPoorPopulation = Number(val.currentTarget.value);
- }}
+ value={formData.totalPoorPopulation}
+ onChange={(e) =>
+ handleChange('totalPoorPopulation', e.currentTarget.value)
+ }
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-miskin/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-miskin/create/page.tsx
index d045d1d6..fdebedfd 100644
--- a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-miskin/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-miskin/create/page.tsx
@@ -61,7 +61,7 @@ export default function CreateJumlahPendudukMiskin() {
{
const value = e.currentTarget.value;
@@ -73,7 +73,7 @@ export default function CreateJumlahPendudukMiskin() {
{
stateJPM.create.form.totalPoorPopulation = Number(e.currentTarget.value);
diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-miskin/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-miskin/page.tsx
index 498a8ce4..57fde078 100644
--- a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-miskin/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-miskin/page.tsx
@@ -1,23 +1,43 @@
'use client'
import colors from '@/con/colors';
-import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core';
-import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
-import HeaderSearch from '../../_com/header';
+import {
+ Box,
+ Button,
+ Center,
+ Group,
+ Pagination,
+ Paper,
+ Skeleton,
+ Stack,
+ Table,
+ TableTbody,
+ TableTd,
+ TableTh,
+ TableThead,
+ TableTr,
+ Text,
+ Title,
+ Tooltip,
+} from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconEdit, IconSearch, IconTrash, IconPlus } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
-import { useShallowEffect, useMediaQuery } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
-import jumlahPendudukMiskin from '../../_state/ekonomi/jumlah-penduduk-miskin';
-import { Bar, BarChart, Legend, XAxis, YAxis, Tooltip as RechartsTooltip } from 'recharts';
+import HeaderSearch from '../../_com/header';
import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
+import jumlahPendudukMiskin from '../../_state/ekonomi/jumlah-penduduk-miskin';
+
+// β
BarChart Mantine
+import { BarChart } from '@mantine/charts';
function JumlahPendudukMiskin() {
const [search, setSearch] = useState("");
return (
}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
@@ -28,7 +48,7 @@ function JumlahPendudukMiskin() {
}
function ListJumlahPendudukMiskin({ search }: { search: string }) {
- type JPMGrafik = { id: string; year: number; totalPoorPopulation: number }
+ type JPMGrafik = { year: number; totalPoorPopulation: number };
const stateJPM = useProxy(jumlahPendudukMiskin);
const [chartData, setChartData] = useState([]);
const [mounted, setMounted] = useState(false);
@@ -36,33 +56,27 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
const [selectedId, setSelectedId] = useState(null);
const router = useRouter();
- const isTablet = useMediaQuery('(max-width:1024px)');
- const isMobile = useMediaQuery('(max-width:768px)');
+ const { data, page, loading, load, totalPages } = stateJPM.findMany;
- const {
- data,
- page,
- loading,
- load,
- totalPages,
- } = stateJPM.findMany;
- // Load data
+ // Load data awal
useShallowEffect(() => {
setMounted(true);
load(page, 10, search);
}, [page, search]);
+ // Update chart data
useEffect(() => {
if (stateJPM.findMany.data) {
- setChartData(stateJPM.findMany.data.map(item => ({
- id: item.id,
- year: Number(item.year),
- totalPoorPopulation: Number(item.totalPoorPopulation)
- })));
+ setChartData(
+ stateJPM.findMany.data.map((item) => ({
+ year: Number(item.year),
+ totalPoorPopulation: Number(item.totalPoorPopulation),
+ }))
+ );
}
}, [stateJPM.findMany.data]);
- const filteredData = data || []
+ const filteredData = data || [];
const handleDelete = () => {
if (selectedId) {
@@ -71,7 +85,7 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
setSelectedId(null);
stateJPM.findMany.load();
}
- }
+ };
if (loading || !data) {
return (
@@ -83,15 +97,18 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
return (
+ {/* Tabel */}
Daftar Jumlah Penduduk Miskin
}
+ leftSection={ }
color="blue"
variant="light"
- onClick={() => router.push('/admin/ekonomi/jumlah-penduduk-miskin/create')}
+ onClick={() =>
+ router.push('/admin/ekonomi/jumlah-penduduk-miskin/create')
+ }
>
Tambah Baru
@@ -109,22 +126,38 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
- {filteredData.length > 0 ? filteredData.map(item => (
-
- {item.year}
- {item.totalPoorPopulation}
-
- router.push(`/admin/ekonomi/jumlah-penduduk-miskin/${item.id}`)}>
-
-
-
-
- { setSelectedId(item.id); setModalHapus(true) }}>
-
-
-
-
- )) : (
+ {filteredData.length > 0 ? (
+ filteredData.map((item) => (
+
+ {item.year}
+ {item.totalPoorPopulation}
+
+
+ router.push(`/admin/ekonomi/jumlah-penduduk-miskin/${item.id}`)
+ }
+ >
+
+
+
+
+ {
+ setSelectedId(item.id);
+ setModalHapus(true);
+ }}
+ >
+
+
+
+
+ ))
+ ) : (
@@ -138,6 +171,7 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
+ {/* Pagination */}
- {/* Chart */}
+ {/* Bar Chart */}
-
- Grafik Jumlah Penduduk Miskin
- {mounted && chartData.length > 0 ? (
-
-
-
-
-
-
-
- ) : (
- Belum ada data untuk ditampilkan dalam grafik
- )}
-
+
+ Grafik Jumlah Penduduk Miskin
+
+ {mounted && chartData.length > 0 ? (
+ ({
+ name: item.year.toString(),
+ value: item.totalPoorPopulation,
+ }))}
+ dataKey="name"
+ series={[
+ { name: 'value', color: colors['blue-button'] },
+ ]}
+ withTooltip
+ valueFormatter={(v) => `${v.toLocaleString()} jiwa`}
+ />
+ ) : (
+ Belum ada data untuk ditampilkan dalam grafik
+ )}
-
{/* Modal Hapus */}
setModalHapus(false)}
onConfirm={handleDelete}
- text='Apakah anda yakin ingin menghapus data ini?'
+ text="Apakah anda yakin ingin menghapus data ini?"
/>
);
diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/[id]/page.tsx
index 5d58837e..7e069310 100644
--- a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/[id]/page.tsx
@@ -1,11 +1,11 @@
+/* eslint-disable react-hooks/exhaustive-deps */
'use client';
import grafikNganggur from '@/app/admin/(dashboard)/_state/ekonomi/usia-kerja-nganggur';
-/* eslint-disable react-hooks/exhaustive-deps */
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, TextInput, Title, Tooltip } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
function EditGrafikBerdasarkanPendidikan() {
@@ -14,34 +14,59 @@ function EditGrafikBerdasarkanPendidikan() {
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanPendidikan);
const id = params.id;
+ // state lokal untuk form
+ const [formData, setFormData] = useState({
+ SD: '',
+ SMP: '',
+ SMA: '',
+ D3: '',
+ S1: '',
+ });
+
useEffect(() => {
if (id) {
stategrafik.findUnique.load(id).then(() => {
const data = stategrafik.findUnique.data;
if (data) {
- stategrafik.update.form = {
+ setFormData({
SD: data.SD || '',
SMP: data.SMP || '',
SMA: data.SMA || '',
D3: data.D3 || '',
S1: data.S1 || '',
- };
+ });
}
});
}
}, [id]);
+ const handleChange = (field: keyof typeof formData) =>
+ (e: React.ChangeEvent) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: e.currentTarget.value,
+ }));
+ };
+
const handleSubmit = async () => {
stategrafik.update.id = id;
+ stategrafik.update.form = { ...formData }; // update global state pas submit aja
await stategrafik.update.submit();
- router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan');
+ router.push(
+ '/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan'
+ );
};
return (
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -63,36 +88,36 @@ function EditGrafikBerdasarkanPendidikan() {
label="SD"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.update.form.SD}
- onChange={(val) => (stategrafik.update.form.SD = val.currentTarget.value)}
+ value={formData.SD}
+ onChange={handleChange('SD')}
/>
(stategrafik.update.form.SMP = val.currentTarget.value)}
+ value={formData.SMP}
+ onChange={handleChange('SMP')}
/>
(stategrafik.update.form.SMA = val.currentTarget.value)}
+ value={formData.SMA}
+ onChange={handleChange('SMA')}
/>
(stategrafik.update.form.D3 = val.currentTarget.value)}
+ value={formData.D3}
+ onChange={handleChange('D3')}
/>
(stategrafik.update.form.S1 = val.currentTarget.value)}
+ value={formData.S1}
+ onChange={handleChange('S1')}
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/create/page.tsx
index d62b2de1..e57a797a 100644
--- a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/create/page.tsx
@@ -67,7 +67,7 @@ function CreateGrafikBerdasarkanPendidikan() {
label="SD"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.create.form.SD}
+ defaultValue={stategrafik.create.form.SD}
onChange={(val) => (stategrafik.create.form.SD = val.currentTarget.value)}
required
/>
@@ -75,7 +75,7 @@ function CreateGrafikBerdasarkanPendidikan() {
label="SMP"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.create.form.SMP}
+ defaultValue={stategrafik.create.form.SMP}
onChange={(val) => (stategrafik.create.form.SMP = val.currentTarget.value)}
required
/>
@@ -83,7 +83,7 @@ function CreateGrafikBerdasarkanPendidikan() {
label="SMA"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.create.form.SMA}
+ defaultValue={stategrafik.create.form.SMA}
onChange={(val) => (stategrafik.create.form.SMA = val.currentTarget.value)}
required
/>
@@ -91,7 +91,7 @@ function CreateGrafikBerdasarkanPendidikan() {
label="D3"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.create.form.D3}
+ defaultValue={stategrafik.create.form.D3}
onChange={(val) => (stategrafik.create.form.D3 = val.currentTarget.value)}
required
/>
@@ -99,7 +99,7 @@ function CreateGrafikBerdasarkanPendidikan() {
label="S1"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.create.form.S1}
+ defaultValue={stategrafik.create.form.S1}
onChange={(val) => (stategrafik.create.form.S1 = val.currentTarget.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/page.tsx
index 328a19da..74137342 100644
--- a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/page.tsx
@@ -1,24 +1,42 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import colors from '@/con/colors';
-import { Box, Button, Center, Flex, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core';
+import {
+ Box,
+ Button,
+ Center,
+ Flex,
+ Pagination,
+ Paper,
+ Skeleton,
+ Stack,
+ Table,
+ TableTbody,
+ TableTd,
+ TableTh,
+ TableThead,
+ TableTr,
+ Text,
+ Title,
+ Tooltip,
+} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
-import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils';
+import { DonutChart } from '@mantine/charts';
import HeaderSearch from '../../../_com/header';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur';
function GrafikBerdasarkanPendidikan() {
- const [search, setSearch] = useState("");
+ const [search, setSearch] = useState('');
return (
}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
@@ -31,7 +49,6 @@ function GrafikBerdasarkanPendidikan() {
function ListGrafikBerdasarkanPendidikan({ search }: { search: string }) {
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanPendidikan);
const [donutData, setDonutData] = useState([]);
- const [mounted, setMounted] = useState(false);
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState(null);
const router = useRouter();
@@ -45,37 +62,45 @@ function ListGrafikBerdasarkanPendidikan({ search }: { search: string }) {
}
};
- const {
- data,
- page,
- totalPages,
- loading,
- load,
- } = stategrafik.findMany;
+ const { data, page, totalPages, loading, load } = stategrafik.findMany;
useShallowEffect(() => {
- setMounted(true);
load(page, 10, search);
}, [page, search]);
useEffect(() => {
if (stategrafik.findMany.data) {
- const SD = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.SD || 0), 0);
- const SMP = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.SMP || 0), 0);
- const SMA = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.SMA || 0), 0);
- const D3 = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.D3 || 0), 0);
- const S1 = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.S1 || 0), 0);
+ const SD = stategrafik.findMany.data.reduce(
+ (acc: number, cur: any) => acc + Number(cur.SD || 0),
+ 0,
+ );
+ const SMP = stategrafik.findMany.data.reduce(
+ (acc: number, cur: any) => acc + Number(cur.SMP || 0),
+ 0,
+ );
+ const SMA = stategrafik.findMany.data.reduce(
+ (acc: number, cur: any) => acc + Number(cur.SMA || 0),
+ 0,
+ );
+ const D3 = stategrafik.findMany.data.reduce(
+ (acc: number, cur: any) => acc + Number(cur.D3 || 0),
+ 0,
+ );
+ const S1 = stategrafik.findMany.data.reduce(
+ (acc: number, cur: any) => acc + Number(cur.S1 || 0),
+ 0,
+ );
setDonutData([
- { name: 'SD', value: SD, color: '#4b6Ef5', key: 'SD' },
- { name: 'SMP', value: SMP, color: '#14b885', key: 'SMP' },
- { name: 'SMA', value: SMA, color: '#E6A03B', key: 'SMA' },
- { name: 'D3', value: D3, color: '#DB524D', key: 'D3' },
- { name: 'S1', value: S1, color: '#1018A8FF', key: 'S1' },
+ { name: 'SD', value: SD, color: '#4b6Ef5' },
+ { name: 'SMP', value: SMP, color: '#14b885' },
+ { name: 'SMA', value: SMA, color: '#E6A03B' },
+ { name: 'D3', value: D3, color: '#DB524D' },
+ { name: 'S1', value: S1, color: '#1018A8FF' },
]);
}
}, [stategrafik.findMany.data]);
- const filteredData = data || []
+ const filteredData = data || [];
if (loading || !data) {
return (
@@ -87,21 +112,26 @@ function ListGrafikBerdasarkanPendidikan({ search }: { search: string }) {
return (
+ {/* Table Data */}
- {/* Header */}
- List Pengangguran Berdasarkan Usia Kerja
+ List Pengangguran Berdasarkan Pendidikan
}
color="blue"
variant="light"
- onClick={() => router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create')}
+ onClick={() =>
+ router.push(
+ '/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/create',
+ )
+ }
>
Tambah Baru
+
@@ -120,7 +150,9 @@ function ListGrafikBerdasarkanPendidikan({ search }: { search: string }) {
- Belum ada data grafik responden
+
+ Belum ada data grafik responden
+
@@ -134,7 +166,15 @@ function ListGrafikBerdasarkanPendidikan({ search }: { search: string }) {
{item.S1}
- router.push(`/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/${item.id}`)}>
+
+ router.push(
+ `/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/${item.id}`,
+ )
+ }
+ >
@@ -148,7 +188,8 @@ function ListGrafikBerdasarkanPendidikan({ search }: { search: string }) {
onClick={() => {
setSelectedId(item.id);
setModalHapus(true);
- }}>
+ }}
+ >
@@ -161,6 +202,7 @@ function ListGrafikBerdasarkanPendidikan({ search }: { search: string }) {
+ {/* Pagination */}
- {/* Chart */}
-
-
-
- Grafik Pengangguran Berdasarkan Pendidikan
- {mounted && donutData.length > 0 ? (
-
-
-
- {donutData.map((entry, index) => (
- |
- ))}
-
-
-
- {donutData.map((entry) => (
-
-
- {entry.name} : {entry.value}
-
- ))}
-
-
- ) : (
- Belum ada data untuk ditampilkan dalam grafik
- )}
-
-
-
+ {/* Donut Chart */}
+
+
+
+ Grafik Pengangguran Berdasarkan Pendidikan
+
+ {donutData.length > 0 ? (
+
+ ) : (
+
+ Belum ada data untuk ditampilkan dalam grafik
+
+ )}
+
+
- {/* Modal Konfirmasi Hapus */}
+ {/* Modal Hapus */}
setModalHapus(false)}
onConfirm={handleDelete}
- text='Apakah anda yakin ingin menghapus grafik pengangguran berdasarkan pendidikan ini?'
+ text="Apakah anda yakin ingin menghapus grafik pengangguran berdasarkan pendidikan ini?"
/>
);
diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/[id]/page.tsx
index 8a6ee1b8..f5e77711 100644
--- a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/[id]/page.tsx
@@ -2,39 +2,70 @@
'use client';
import grafikNganggur from '@/app/admin/(dashboard)/_state/ekonomi/usia-kerja-nganggur';
import colors from '@/con/colors';
-import { Box, Button, Paper, Stack, TextInput, Title, Group, Tooltip } from '@mantine/core';
+import {
+ Box,
+ Button,
+ Paper,
+ Stack,
+ TextInput,
+ Title,
+ Group,
+ Tooltip,
+} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import { toast } from 'react-toastify';
function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() {
const router = useRouter();
const params = useParams() as { id: string };
- const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur);
+ const stategrafik = useProxy(
+ grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur
+ );
const id = params.id;
+ // β
state lokal, controlled
+ const [formData, setFormData] = useState({
+ usia18_25: '',
+ usia26_35: '',
+ usia36_45: '',
+ usia46_keatas: '',
+ });
+
+ // load data dari global state -> masukin ke local state
useEffect(() => {
if (id) {
stategrafik.findUnique.load(id).then(() => {
const data = stategrafik.findUnique.data;
if (data) {
- stategrafik.update.form = {
+ setFormData({
usia18_25: data.usia18_25 || '',
usia26_35: data.usia26_35 || '',
usia36_45: data.usia36_45 || '',
usia46_keatas: data.usia46_keatas || '',
- };
+ });
}
});
}
}, [id]);
+ const handleChange = (field: string, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
const handleSubmit = async () => {
try {
+ // β
baru update global state pas submit
stategrafik.update.id = id;
+ stategrafik.update.form = { ...formData };
+
await stategrafik.update.submit();
+
toast.success('Data grafik berhasil diperbarui!');
router.push(
'/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia'
@@ -49,7 +80,12 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() {
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -71,40 +107,32 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() {
label="Usia 18 - 25"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.update.form.usia18_25}
- onChange={(val) => {
- stategrafik.update.form.usia18_25 = val.currentTarget.value;
- }}
+ value={formData.usia18_25}
+ onChange={(e) => handleChange('usia18_25', e.currentTarget.value)}
required
/>
{
- stategrafik.update.form.usia26_35 = val.currentTarget.value;
- }}
+ value={formData.usia26_35}
+ onChange={(e) => handleChange('usia26_35', e.currentTarget.value)}
required
/>
{
- stategrafik.update.form.usia36_45 = val.currentTarget.value;
- }}
+ value={formData.usia36_45}
+ onChange={(e) => handleChange('usia36_45', e.currentTarget.value)}
required
/>
{
- stategrafik.update.form.usia46_keatas = val.currentTarget.value;
- }}
+ value={formData.usia46_keatas}
+ onChange={(e) => handleChange('usia46_keatas', e.currentTarget.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create/page.tsx
index 673256f5..77c362fd 100644
--- a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create/page.tsx
@@ -66,7 +66,7 @@ function CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
label="Usia 18 - 25"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.create.form.usia18_25}
+ defaultValue={stategrafik.create.form.usia18_25}
onChange={(val) => (stategrafik.create.form.usia18_25 = val.currentTarget.value)}
required
/>
@@ -74,7 +74,7 @@ function CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
label="Usia 26 - 35"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.create.form.usia26_35}
+ defaultValue={stategrafik.create.form.usia26_35}
onChange={(val) => (stategrafik.create.form.usia26_35 = val.currentTarget.value)}
required
/>
@@ -82,7 +82,7 @@ function CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
label="Usia 36 - 45"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.create.form.usia36_45}
+ defaultValue={stategrafik.create.form.usia36_45}
onChange={(val) => (stategrafik.create.form.usia36_45 = val.currentTarget.value)}
required
/>
@@ -90,7 +90,7 @@ function CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
label="Usia 46 +"
type="number"
placeholder="Masukkan jumlah"
- value={stategrafik.create.form.usia46_keatas}
+ defaultValue={stategrafik.create.form.usia46_keatas}
onChange={(val) => (stategrafik.create.form.usia46_keatas = val.currentTarget.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/page.tsx
index 0c8b70d2..3c0fcfd6 100644
--- a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/page.tsx
@@ -1,13 +1,30 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import colors from '@/con/colors';
-import { Box, Button, Center, Flex, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core';
+import {
+ Box,
+ Button,
+ Center,
+ Flex,
+ Pagination,
+ Paper,
+ Skeleton,
+ Stack,
+ Table,
+ TableTbody,
+ TableTd,
+ TableTh,
+ TableThead,
+ TableTr,
+ Text,
+ Title,
+} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
-import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils';
+import { DonutChart } from '@mantine/charts';
import HeaderSearch from '../../../_com/header';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur';
@@ -17,8 +34,8 @@ function GrafikBerdasarkanUsiaKerjaYangMenganggur() {
return (
}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
@@ -31,7 +48,6 @@ function GrafikBerdasarkanUsiaKerjaYangMenganggur() {
function ListGrafikBerdasarkanUsiaKerjaYangMenganggur({ search }: { search: string }) {
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur);
const [donutData, setDonutData] = useState([]);
- const [mounted, setMounted] = useState(false);
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState(null);
const router = useRouter();
@@ -45,17 +61,10 @@ function ListGrafikBerdasarkanUsiaKerjaYangMenganggur({ search }: { search: stri
}
};
- const {
- data,
- page,
- totalPages,
- loading,
- load,
- } = stategrafik.findMany;
+ const { data, page, totalPages, loading, load } = stategrafik.findMany;
useShallowEffect(() => {
- setMounted(true);
- load(page, 10, search)
+ load(page, 10, search);
}, [page, search]);
useEffect(() => {
@@ -64,16 +73,17 @@ function ListGrafikBerdasarkanUsiaKerjaYangMenganggur({ search }: { search: stri
const totalUsia26_35 = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia26_35 || 0), 0);
const totalUsia36_45 = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia36_45 || 0), 0);
const totalUsia46_keatas = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.usia46_keatas || 0), 0);
+
setDonutData([
- { name: 'usia18_25', value: totalUsia18_25, color: colors['blue-button'], key: 'usia18_25' },
- { name: 'usia26_35', value: totalUsia26_35, color: '#10A85AFF', key: 'usia26_35' },
- { name: 'usia36_45', value: totalUsia36_45, color: '#C07B13FF', key: 'usia36_45' },
- { name: 'usia46_keatas', value: totalUsia46_keatas, color: '#1094A8FF', key: 'usia46_keatas' },
+ { name: 'Usia 18-25', value: totalUsia18_25, color: colors['blue-button'] },
+ { name: 'Usia 26-35', value: totalUsia26_35, color: '#10A85AFF' },
+ { name: 'Usia 36-45', value: totalUsia36_45, color: '#C07B13FF' },
+ { name: 'Usia 46+', value: totalUsia46_keatas, color: '#1094A8FF' },
]);
}
}, [stategrafik.findMany.data]);
- const filteredData = data || []
+ const filteredData = data || [];
if (loading || !data) {
return (
@@ -85,24 +95,23 @@ function ListGrafikBerdasarkanUsiaKerjaYangMenganggur({ search }: { search: stri
return (
+ {/* Table */}
- {/* Header */}
List Pengangguran Berdasarkan Usia Kerja
-
- }
- color="blue"
- variant="light"
- onClick={() => router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create')}
- >
- Tambah Baru
-
-
+ }
+ color="blue"
+ variant="light"
+ onClick={() =>
+ router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create')
+ }
+ >
+ Tambah Baru
+
- {/* Table */}
@@ -110,26 +119,38 @@ function ListGrafikBerdasarkanUsiaKerjaYangMenganggur({ search }: { search: stri
Usia 18-25
Usia 26-35
Usia 36-45
- Usia 46 +
+ Usia 46+
Edit
Delete
{filteredData.length > 0 ? (
- filteredData.map(item => (
+ filteredData.map((item) => (
{item.usia18_25}
{item.usia26_35}
{item.usia36_45}
{item.usia46_keatas}
- router.push(`/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/${item.id}`)}>
+
+ router.push(`/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/${item.id}`)
+ }
+ >
- { setSelectedId(item.id); setModalHapus(true); }}>
+ {
+ setSelectedId(item.id);
+ setModalHapus(true);
+ }}
+ >
@@ -147,10 +168,10 @@ function ListGrafikBerdasarkanUsiaKerjaYangMenganggur({ search }: { search: stri
-
+ {/* Pagination */}
- {/* Chart */}
+ {/* Donut Chart */}
- Grafik Pengangguran Berdasarkan Usia Kerja
- {mounted && donutData.length > 0 ? (
-
-
-
- {donutData.map((entry, index) => (
- |
- ))}
-
-
-
-
-
- Usia 18-25 : {donutData.find((entry) => entry.name === 'usia18_25')?.value}
-
-
-
- Usia 26-35 : {donutData.find((entry) => entry.name === 'usia26_35')?.value}
-
-
-
-
- Usia 36-45 : {donutData.find((entry) => entry.name === 'usia36_45')?.value}
-
-
-
-
- Usia 46 + : {donutData.find((entry) => entry.name === 'usia46_keatas')?.value}
-
-
-
-
+
+ Grafik Pengangguran Berdasarkan Usia Kerja
+
+ {donutData.length > 0 ? (
+
+
+
) : (
Belum ada data untuk ditampilkan dalam grafik
)}
-
{/* Modal Konfirmasi Hapus */}
{
- const total = formData.educatedUnemployment + formData.uneducatedUnemployment;
+ // --- hitung total + persentase perubahan
+ const calculateTotalAndChange = useCallback(
+ async (data: typeof formData) => {
+ const total = data.educatedUnemployment + data.uneducatedUnemployment;
+ let percentageChange = 0;
- let percentageChange = 0;
- const monthOrder = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'];
- const currentMonthIndex = monthOrder.indexOf(formData.month);
+ const currentMonthIndex = MONTHS.indexOf(data.month);
+ if (currentMonthIndex !== -1) {
+ let prevMonthIndex = currentMonthIndex - 1;
+ let prevYear = data.year;
- if (currentMonthIndex !== -1) {
- let prevMonthIndex = currentMonthIndex - 1;
- let prevYear = formData.year;
+ if (prevMonthIndex < 0) {
+ prevMonthIndex = 11;
+ prevYear--;
+ }
- if (prevMonthIndex < 0) {
- prevMonthIndex = 11;
- prevYear--;
+ const prevData = await stateDetail.findByMonthYear.load({
+ month: MONTHS[prevMonthIndex],
+ year: prevYear,
+ });
+
+ if (prevData && prevData.totalUnemployment > 0) {
+ const change =
+ ((total - prevData.totalUnemployment) /
+ prevData.totalUnemployment) * 100;
+ percentageChange = parseFloat(change.toFixed(1));
+ }
}
- const prevMonth = monthOrder[prevMonthIndex];
- const prevData = await stateDetail.findByMonthYear.load({ month: prevMonth, year: prevYear });
-
- if (prevData && prevData.totalUnemployment > 0) {
- const change = ((total - prevData.totalUnemployment) / prevData.totalUnemployment) * 100;
- percentageChange = parseFloat(change.toFixed(1));
- }
- }
-
- return { total, percentageChange };
- };
+ return { total, percentageChange };
+ },
+ [stateDetail.findByMonthYear]
+ );
+ // --- update state lokal
const updateFormData = async (updates: Partial) => {
const newData = { ...formData, ...updates };
- const { total, percentageChange } = await calculateTotalAndChange();
+ const { total, percentageChange } = await calculateTotalAndChange(newData);
setFormData({ ...newData, totalUnemployment: total, percentageChange });
};
+ // --- load detail by ID (sekali)
useEffect(() => {
const loadDetail = async () => {
const id = params?.id as string;
if (!id) return;
try {
- await stateDetail.findUnique.load(id); // ambil by ID
+ await stateDetail.findUnique.load(id);
const data = stateDetail.findUnique.data;
+ if (!data) return;
- if (data) {
-
- // Convert year from Date to number if needed
- const yearValue = data.year && typeof data.year === 'object' && 'getFullYear' in data.year
+ const yearValue =
+ data.year && typeof data.year === 'object' && 'getFullYear' in data.year
? (data.year as Date).getFullYear()
: Number(data.year);
- // Set the ID for update
- stateDetail.update.id = id;
+ stateDetail.update.id = id; // simpan id untuk update
- // Update Valtio state with converted year
- stateDetail.update.form = {
- ...data,
- year: yearValue,
- percentageChange: data.percentageChange || 0 // Ensure it's always a number
- };
-
- // Update local formData with converted year
- setFormData({
- month: data.month,
- year: yearValue,
- totalUnemployment: data.totalUnemployment,
- educatedUnemployment: data.educatedUnemployment,
- uneducatedUnemployment: data.uneducatedUnemployment,
- percentageChange: data.percentageChange || 0, // Ensure it's always a number
- });
- }
- } catch (error) {
- console.error("Error loading detail:", error);
- toast.error("Gagal memuat data detail");
+ setFormData({
+ month: data.month,
+ year: yearValue,
+ educatedUnemployment: data.educatedUnemployment,
+ uneducatedUnemployment: data.uneducatedUnemployment,
+ totalUnemployment: data.totalUnemployment,
+ percentageChange: data.percentageChange || 0,
+ });
+ } catch (err) {
+ console.error('Error loading detail:', err);
+ toast.error('Gagal memuat data detail');
}
};
loadDetail();
}, [params?.id]);
+ // --- submit form
const handleSubmit = async () => {
- const { total, percentageChange } = await calculateTotalAndChange();
try {
- stateDetail.update.form = { ...formData, totalUnemployment: total, percentageChange };
+ const { total, percentageChange } = await calculateTotalAndChange(formData);
+
+ stateDetail.update.form = {
+ ...formData,
+ totalUnemployment: total,
+ percentageChange,
+ };
+
const success = await stateDetail.update.submit();
if (success) {
toast.success('Detail data pengangguran berhasil diperbarui!');
router.push('/admin/ekonomi/jumlah-pengangguran');
}
- } catch (error) {
- console.error('Error updating:', error);
+ } catch (err) {
+ console.error('Error updating:', err);
toast.error('Terjadi kesalahan saat memperbarui data');
}
};
@@ -129,11 +151,18 @@ function EditDetailDataPengangguran() {
-
+
updateFormData({ month: val || '' })}
/>
@@ -141,21 +170,39 @@ function EditDetailDataPengangguran() {
label="Tahun"
value={formData.year}
onChange={(val) => updateFormData({ year: Number(val) })}
+ required
/>
updateFormData({ educatedUnemployment: Number(val.currentTarget.value) || 0 })}
+ onChange={(e) =>
+ updateFormData({
+ educatedUnemployment: Number(e.currentTarget.value) || 0,
+ })
+ }
+ required
/>
updateFormData({ uneducatedUnemployment: Number(val.currentTarget.value) || 0 })}
+ onChange={(e) =>
+ updateFormData({
+ uneducatedUnemployment: Number(e.currentTarget.value) || 0,
+ })
+ }
+ required
/>
- Total Otomatis: {formData.totalUnemployment}
- Perubahan Otomatis: {formData.percentageChange !== null ? `${formData.percentageChange}%` : '-'}
+
+ Total Otomatis: {formData.totalUnemployment}
+
+
+ Perubahan Otomatis:{' '}
+ {formData.percentageChange !== null
+ ? `${formData.percentageChange}%`
+ : '-'}
+
{
const loadLowongan = async () => {
const id = params?.id as string;
@@ -51,6 +53,7 @@ function EditLowonganKerja() {
gaji: data.gaji || '',
deskripsi: data.deskripsi || '',
kualifikasi: data.kualifikasi || '',
+ notelp: data.notelp || '',
});
}
} catch (error) {
@@ -62,14 +65,17 @@ function EditLowonganKerja() {
loadLowongan();
}, [params?.id]);
+ const handleChange = (field: string, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
const handleSubmit = async () => {
try {
lowonganState.update.id = params?.id as string;
-
- lowonganState.update.form = {
- ...lowonganState.update.form,
- ...formData,
- };
+ lowonganState.update.form = { ...formData };
await lowonganState.update.update();
toast.success("Lowongan kerja berhasil diperbarui!");
@@ -108,7 +114,7 @@ function EditLowonganKerja() {
label="Posisi"
placeholder="Masukkan posisi"
value={formData.posisi}
- onChange={(e) => setFormData({ ...formData, posisi: e.target.value })}
+ onChange={(e) => handleChange("posisi", e.target.value)}
required
/>
@@ -116,7 +122,7 @@ function EditLowonganKerja() {
label="Nama Perusahaan"
placeholder="Masukkan nama perusahaan"
value={formData.namaPerusahaan}
- onChange={(e) => setFormData({ ...formData, namaPerusahaan: e.target.value })}
+ onChange={(e) => handleChange("namaPerusahaan", e.target.value)}
required
/>
@@ -124,7 +130,15 @@ function EditLowonganKerja() {
label="Lokasi"
placeholder="Masukkan lokasi"
value={formData.lokasi}
- onChange={(e) => setFormData({ ...formData, lokasi: e.target.value })}
+ onChange={(e) => handleChange("lokasi", e.target.value)}
+ required
+ />
+
+ handleChange("notelp", e.target.value)}
required
/>
@@ -132,7 +146,7 @@ function EditLowonganKerja() {
label="Tipe Pekerjaan"
placeholder="Masukkan tipe pekerjaan"
value={formData.tipePekerjaan}
- onChange={(e) => setFormData({ ...formData, tipePekerjaan: e.target.value })}
+ onChange={(e) => handleChange("tipePekerjaan", e.target.value)}
required
/>
@@ -140,7 +154,7 @@ function EditLowonganKerja() {
label="Gaji (per bulan)"
placeholder="Masukkan gaji"
value={formData.gaji}
- onChange={(e) => setFormData({ ...formData, gaji: e.target.value })}
+ onChange={(e) => handleChange("gaji", e.target.value)}
required
/>
@@ -150,7 +164,7 @@ function EditLowonganKerja() {
setFormData({ ...formData, deskripsi: val })}
+ onChange={(val) => handleChange("deskripsi", val)}
/>
@@ -160,7 +174,7 @@ function EditLowonganKerja() {
setFormData({ ...formData, kualifikasi: val })}
+ onChange={(val) => handleChange("kualifikasi", val)}
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/[id]/page.tsx
index 79af7355..980529aa 100644
--- a/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/[id]/page.tsx
@@ -82,6 +82,11 @@ function DetailLowonganKerjaLokal() {
{data.lokasi || '-'}
+
+ Nomor Yang Dapat Dihubungi
+ {data.notelp || '-'}
+
+
Tipe Pekerjaan
{data.tipePekerjaan || '-'}
@@ -94,12 +99,12 @@ function DetailLowonganKerjaLokal() {
Deskripsi
-
+
Kualifikasi
-
+
diff --git a/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/create/page.tsx
index 61adecdf..606100e8 100644
--- a/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/lowongan-kerja-lokal/create/page.tsx
@@ -30,6 +30,7 @@ function CreateLowonganKerja() {
gaji: '',
deskripsi: '',
kualifikasi: '',
+ notelp: '',
};
};
@@ -69,7 +70,7 @@ function CreateLowonganKerja() {
>
(lowonganState.create.form.posisi = val.target.value)
}
@@ -78,7 +79,7 @@ function CreateLowonganKerja() {
required
/>
(lowonganState.create.form.namaPerusahaan = val.target.value)
}
@@ -87,7 +88,16 @@ function CreateLowonganKerja() {
required
/>
+ (lowonganState.create.form.notelp = val.target.value)
+ }
+ label="Nomor Yang Dapat Dihubungi"
+ placeholder="Masukkan nomor yang dapat dihubungi"
+ required
+ />
+
(lowonganState.create.form.lokasi = val.target.value)
}
@@ -96,7 +106,7 @@ function CreateLowonganKerja() {
required
/>
(lowonganState.create.form.tipePekerjaan = val.target.value)
}
@@ -105,7 +115,7 @@ function CreateLowonganKerja() {
required
/>
(lowonganState.create.form.gaji = val.target.value)
}
diff --git a/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/[id]/page.tsx
index 310cfdfb..451a4a71 100644
--- a/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/[id]/page.tsx
@@ -25,9 +25,8 @@ function EditKategoriProduk() {
const id = params?.id as string;
const statePasar = useProxy(pasarDesaState.kategoriProduk);
- const [formData, setFormData] = useState({
- nama: '',
- });
+ const [formData, setFormData] = useState({ nama: '' });
+ const [loading, setLoading] = useState(true);
useEffect(() => {
const loadKategoriProduk = async () => {
@@ -37,21 +36,30 @@ function EditKategoriProduk() {
const data = await statePasar.edit.load(id);
if (data) {
- // pastikan id-nya masuk ke state edit
+ // simpan id ke state global hanya untuk referensi
statePasar.edit.id = id;
- setFormData({
- nama: data.nama || '',
- });
+
+ // simpan data ke state lokal
+ setFormData({ nama: data.nama || '' });
}
} catch (error) {
console.error('Error loading kategori produk:', error);
toast.error('Gagal memuat data kategori produk');
+ } finally {
+ setLoading(false);
}
};
loadKategoriProduk();
}, [id]);
+ const handleChange = (e: React.ChangeEvent) => {
+ setFormData((prev) => ({
+ ...prev,
+ [e.target.name]: e.target.value,
+ }));
+ };
+
const handleSubmit = async () => {
try {
if (!formData.nama.trim()) {
@@ -59,10 +67,8 @@ function EditKategoriProduk() {
return;
}
- statePasar.edit.form = {
- nama: formData.nama.trim(),
- };
-
+ // update global state hanya saat submit
+ statePasar.edit.form = { nama: formData.nama.trim() };
if (!statePasar.edit.id) {
statePasar.edit.id = id; // fallback
}
@@ -79,12 +85,21 @@ function EditKategoriProduk() {
}
};
+ if (loading) {
+ return Loading... ;
+ }
+
return (
{/* Header dengan tombol back */}
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -104,10 +119,11 @@ function EditKategoriProduk() {
>
Nama Kategori Produk}
placeholder="Masukkan nama kategori produk"
value={formData.nama}
- onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
+ onChange={handleChange}
required
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/create/page.tsx
index cf153d3b..93598be7 100644
--- a/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/create/page.tsx
@@ -74,7 +74,7 @@ function CreateKategoriProduk() {
(statePasar.create.form.nama = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/edit/page.tsx
index 098c500e..17ebd925 100644
--- a/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/edit/page.tsx
@@ -1,5 +1,5 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react-hooks/exhaustive-deps */
+/* eslint-disable @typescript-eslint/no-explicit-any */
'use client';
import pasarDesaState from '@/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa';
import colors from '@/con/colors';
@@ -24,6 +24,16 @@ import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
+type FormData = {
+ nama: string;
+ harga: number;
+ alamatUsaha: string;
+ imageId: string;
+ rating: number;
+ kategoriId: string[];
+ kontak: string;
+};
+
function EditPasarDesa() {
const pasarState = useProxy(pasarDesaState);
const router = useRouter();
@@ -31,17 +41,20 @@ function EditPasarDesa() {
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
- const [formData, setFormData] = useState({
- nama: pasarState.pasarDesa.edit.form.nama || '',
- harga: pasarState.pasarDesa.edit.form.harga || 0,
- alamatUsaha: pasarState.pasarDesa.edit.form.alamatUsaha || '',
- imageId: pasarState.pasarDesa.edit.form.imageId || '',
- rating: pasarState.pasarDesa.edit.form.rating || 0,
- kategoriId: pasarState.pasarDesa.edit.form.kategoriId || [],
+ const [formData, setFormData] = useState({
+ nama: '',
+ harga: 0,
+ alamatUsaha: '',
+ imageId: '',
+ rating: 0,
+ kategoriId: [],
+ kontak: '',
});
+ // load data awal
useEffect(() => {
- pasarState.kategoriProduk.findMany.load();
+ pasarState.kategoriProduk.findManyAll.load();
+
const loadPasarDesa = async () => {
const id = params?.id as string;
if (!id) return;
@@ -56,6 +69,7 @@ function EditPasarDesa() {
imageId: data.imageId || '',
rating: data.rating || 0,
kategoriId: data.KategoriToPasar?.map((k: any) => k.kategoriId) || [],
+ kontak: data.kontak || '',
});
if (data.image?.link) setPreviewImage(data.image.link);
}
@@ -70,19 +84,27 @@ function EditPasarDesa() {
loadPasarDesa();
}, [params?.id]);
+ const handleChange = (key: keyof FormData, value: any) => {
+ setFormData((prev) => ({ ...prev, [key]: value }));
+ };
+
const handleSubmit = async () => {
try {
- pasarState.pasarDesa.edit.form = { ...pasarState.pasarDesa.edit.form, ...formData };
-
+ // upload image kalau ada file baru
+ let imageId = formData.imageId;
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
-
if (!uploaded?.id) return toast.error('Gagal upload gambar');
-
- pasarState.pasarDesa.edit.form.imageId = uploaded.id;
+ imageId = uploaded.id;
}
+ // update global state hanya saat submit
+ pasarState.pasarDesa.edit.form = {
+ ...formData,
+ imageId,
+ };
+
await pasarState.pasarDesa.edit.update();
toast.success('Pasar desa berhasil diperbarui!');
router.push('/admin/ekonomi/pasar-desa/produk-pasar-desa');
@@ -114,6 +136,7 @@ function EditPasarDesa() {
style={{ border: '1px solid #e0e0e0' }}
>
+ {/* Dropzone upload */}
Gambar Produk
@@ -170,11 +193,12 @@ function EditPasarDesa() {
)}
+ {/* Controlled Inputs */}
setFormData({ ...formData, nama: e.target.value })}
+ onChange={(e) => handleChange('nama', e.target.value)}
required
/>
@@ -183,7 +207,7 @@ function EditPasarDesa() {
label="Harga Produk"
placeholder="Masukkan harga produk"
value={formData.harga}
- onChange={(e) => setFormData({ ...formData, harga: Number(e.target.value) })}
+ onChange={(e) => handleChange('harga', Number(e.target.value))}
required
/>
@@ -195,7 +219,7 @@ function EditPasarDesa() {
label="Rating Produk"
placeholder="Masukkan rating produk (0-5)"
value={formData.rating}
- onChange={(e) => setFormData({ ...formData, rating: Number(e.target.value) })}
+ onChange={(e) => handleChange('rating', Number(e.target.value))}
required
/>
@@ -203,7 +227,15 @@ function EditPasarDesa() {
label="Alamat Usaha"
placeholder="Masukkan alamat usaha"
value={formData.alamatUsaha}
- onChange={(e) => setFormData({ ...formData, alamatUsaha: e.target.value })}
+ onChange={(e) => handleChange('alamatUsaha', e.target.value)}
+ required
+ />
+
+ handleChange('kontak', e.target.value)}
required
/>
@@ -211,9 +243,9 @@ function EditPasarDesa() {
label="Kategori Produk"
placeholder="Pilih kategori produk"
value={formData.kategoriId}
- onChange={(val) => setFormData({ ...formData, kategoriId: val })}
+ onChange={(val) => handleChange('kategoriId', val)}
data={
- pasarState.kategoriProduk.findMany.data?.map((v) => ({
+ pasarState.kategoriProduk.findManyAll.data?.map((v) => ({
value: v.id,
label: v.nama,
})) || []
diff --git a/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/page.tsx
index c6881f1a..96f52523 100644
--- a/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/[id]/page.tsx
@@ -85,6 +85,11 @@ function DetailPasarDesa() {
{data.alamatUsaha || '-'}
+
+ Kontak
+ {data.kontak || '-'}
+
+
Gambar
{data.image?.link ? (
diff --git a/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/create/page.tsx
index 861b1069..72265081 100644
--- a/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/pasar-desa/produk-pasar-desa/create/page.tsx
@@ -30,7 +30,7 @@ export default function CreatePasarDesa() {
const [file, setFile] = useState(null);
useEffect(() => {
- statePasar.kategoriProduk.findMany.load();
+ statePasar.kategoriProduk.findManyAll.load();
}, []);
const resetForm = () => {
@@ -41,6 +41,7 @@ export default function CreatePasarDesa() {
imageId: '',
rating: 0,
kategoriId: [],
+ kontak: '',
};
setPreviewImage(null);
setFile(null);
@@ -144,7 +145,7 @@ export default function CreatePasarDesa() {
(statePasar.pasarDesa.create.form.nama = e.target.value)}
required
/>
@@ -154,7 +155,7 @@ export default function CreatePasarDesa() {
type="number"
label="Harga Produk"
placeholder="Masukkan harga produk"
- value={statePasar.pasarDesa.create.form.harga}
+ defaultValue={statePasar.pasarDesa.create.form.harga}
onChange={(e) => (statePasar.pasarDesa.create.form.harga = Number(e.target.value))}
required
/>
@@ -167,7 +168,7 @@ export default function CreatePasarDesa() {
step={0.1}
label="Rating Produk (0β5)"
placeholder="Masukkan rating produk"
- value={statePasar.pasarDesa.create.form.rating}
+ defaultValue={statePasar.pasarDesa.create.form.rating}
onChange={(e) => {
const value = Number(e.target.value);
if (value >= 0 && value <= 5) {
@@ -180,10 +181,19 @@ export default function CreatePasarDesa() {
(statePasar.pasarDesa.create.form.alamatUsaha = e.target.value)}
/>
+ {/* Kontak */}
+ (statePasar.pasarDesa.create.form.kontak = e.target.value)}
+ />
+
{/* Kategori Produk */}
(statePasar.pasarDesa.create.form.kategoriId = val)}
data={
- statePasar.kategoriProduk.findMany.data?.map((v) => ({
+ statePasar.kategoriProduk.findManyAll.data?.map((v) => ({
value: v.id,
label: v.nama,
})) || []
diff --git a/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/[id]/edit/page.tsx
index e51f0058..6dbb6b03 100644
--- a/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/[id]/edit/page.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
@@ -19,53 +18,105 @@ import {
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect } from 'react';
+import { useEffect, useState, useCallback } from 'react';
import { useProxy } from 'valtio/utils';
import { toast } from 'react-toastify';
+type Statistik = {
+ tahun: string;
+ jumlah: string;
+};
+
+type FormData = {
+ nama: string;
+ deskripsi: string;
+ icon: string;
+ statistik: Statistik;
+};
+
+const initialForm: FormData = {
+ nama: '',
+ deskripsi: '',
+ icon: '',
+ statistik: {
+ tahun: '',
+ jumlah: '',
+ },
+};
+
function EditProgramKemiskinan() {
const router = useRouter();
- const params = useParams() as { id: string };
+ const { id } = useParams() as { id: string };
const stateProgram = useProxy(programKemiskinanState);
- const id = params.id;
+ const [formData, setFormData] = useState(initialForm);
+
+ // Load data 1x dari global state β isi local state
useEffect(() => {
- if (id) {
- stateProgram.findUnique.load(id).then(() => {
+ if (!id) return;
+
+ const loadData = async () => {
+ try {
+ await stateProgram.findUnique.load(id);
const data = stateProgram.findUnique.data;
if (data) {
- stateProgram.update.form = {
- nama: data.nama || '',
- deskripsi: data.deskripsi || '',
- icon: data.icon || '',
+ setFormData({
+ nama: data.nama ?? '',
+ deskripsi: data.deskripsi ?? '',
+ icon: data.icon ?? '',
statistik: {
- tahun: data.statistik?.tahun?.toString() || '',
- jumlah: data.statistik?.jumlah?.toString() || '',
+ tahun: data.statistik?.tahun?.toString() ?? '',
+ jumlah: data.statistik?.jumlah?.toString() ?? '',
},
- };
+ });
}
- }).catch((err) => {
- console.error("Error load data:", err);
- toast.error("Gagal mengambil data program");
- });
- }
- }, [id]);
+ } catch (err) {
+ console.error('Error load data:', err);
+ toast.error('Gagal mengambil data program');
+ }
+ };
+
+ loadData();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [id]); // β
hanya trigger saat id berubah
+
+
+ // generic handler untuk field top-level
+ const handleChange = useCallback(
+ (field: keyof FormData, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ },
+ []
+ );
+
+ // khusus nested statistik
+ const handleStatistikChange = useCallback(
+ (field: keyof Statistik, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ statistik: { ...prev.statistik, [field]: value },
+ }));
+ },
+ []
+ );
const handleSubmit = async () => {
try {
stateProgram.update.id = id;
+ stateProgram.update.form = formData;
await stateProgram.update.update();
- toast.success("Program berhasil diperbarui!");
+
+ toast.success('Program berhasil diperbarui!');
router.push('/admin/ekonomi/program-kemiskinan');
} catch (error) {
- console.error("Error update program:", error);
- toast.error("Terjadi kesalahan saat memperbarui program");
+ console.error('Error update program:', error);
+ toast.error('Terjadi kesalahan saat memperbarui program');
}
};
return (
- {/* Header dengan tombol kembali */}
+ {/* Header */}
{
- stateProgram.update.form.nama = e.target.value;
- }}
+ value={formData.nama}
+ onChange={(e) => handleChange('nama', e.target.value)}
label={Judul Program }
placeholder="Masukkan judul program"
required
@@ -106,10 +155,8 @@ function EditProgramKemiskinan() {
Deskripsi
{
- stateProgram.update.form.deskripsi = val;
- }}
+ value={formData.deskripsi}
+ onChange={(val) => handleChange('deskripsi', val)}
/>
@@ -118,10 +165,8 @@ function EditProgramKemiskinan() {
Ikon Program Kreatif Desa
{
- stateProgram.update.form.icon = value;
- }}
+ value={formData.icon as IconKey}
+ onChange={(val) => handleChange('icon', val)}
/>
@@ -131,10 +176,8 @@ function EditProgramKemiskinan() {
{
- stateProgram.update.form.statistik.jumlah = e.target.value;
- }}
+ value={formData.statistik.jumlah}
+ onChange={(e) => handleStatistikChange('jumlah', e.target.value)}
label="Jumlah Masyarakat Miskin"
placeholder="Masukkan jumlah masyarakat miskin"
required
@@ -142,10 +185,8 @@ function EditProgramKemiskinan() {
{
- stateProgram.update.form.statistik.tahun = e.target.value;
- }}
+ value={formData.statistik.tahun}
+ onChange={(e) => handleStatistikChange('tahun', e.target.value)}
label="Tahun"
placeholder="Masukkan tahun"
required
diff --git a/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/[id]/page.tsx
index 2d87c9bc..5e4acf64 100644
--- a/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/[id]/page.tsx
@@ -90,6 +90,7 @@ function DetailProgramKemiskinan() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi || '-' }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/create/page.tsx
index 37d93d55..cf4c0696 100644
--- a/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/program-kemiskinan/create/page.tsx
@@ -93,7 +93,7 @@ function CreateProgramKemiskinan() {
(programState.create.form.nama = val.target.value)}
required
/>
@@ -128,7 +128,7 @@ function CreateProgramKemiskinan() {
(programState.create.form.statistik.jumlah = val.target.value)
}
@@ -138,7 +138,7 @@ function CreateProgramKemiskinan() {
/>
(programState.create.form.statistik.tahun = val.target.value)
}
diff --git a/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/[id]/edit/page.tsx
index 603c194e..f746afeb 100644
--- a/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/[id]/edit/page.tsx
@@ -16,7 +16,7 @@ import {
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import { toast } from 'react-toastify';
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
@@ -28,28 +28,46 @@ function EditSektorUnggulanDesa() {
const id = params.id;
+ // state lokal buat form
+ const [formData, setFormData] = useState({
+ name: '',
+ description: '',
+ value: 0,
+ });
+
// Load data saat komponen mount
useEffect(() => {
if (id) {
- stateGrafik.findUnique.load(id).then(() => {
- const data = stateGrafik.findUnique.data;
- if (data) {
- stateGrafik.update.form = {
- name: data.name || '',
- description: data.description || '',
- value: data.value || 0,
- };
- }
- }).catch((err) => {
- console.error('Error load sektor unggulan:', err);
- toast.error('Gagal mengambil data sektor unggulan');
- });
+ stateGrafik.findUnique
+ .load(id)
+ .then(() => {
+ const data = stateGrafik.findUnique.data;
+ if (data) {
+ setFormData({
+ name: data.name || '',
+ description: data.description || '',
+ value: data.value || 0,
+ });
+ }
+ })
+ .catch((err) => {
+ console.error('Error load sektor unggulan:', err);
+ toast.error('Gagal mengambil data sektor unggulan');
+ });
}
}, [id]);
+ const handleChange =
+ (field: keyof typeof formData) =>
+ (e: React.ChangeEvent) => {
+ const value = field === 'value' ? Number(e.currentTarget.value) : e.currentTarget.value;
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
stateGrafik.update.id = id;
+ stateGrafik.update.form = { ...formData }; // update global pas submit
await stateGrafik.update.submit();
toast.success('Sektor unggulan berhasil diperbarui!');
router.push('/admin/ekonomi/sektor-unggulan-desa');
@@ -89,10 +107,8 @@ function EditSektorUnggulanDesa() {
{
- stateGrafik.update.form.name = val.currentTarget.value;
- }}
+ value={formData.name}
+ onChange={handleChange('name')}
required
/>
@@ -100,20 +116,18 @@ function EditSektorUnggulanDesa() {
Konten
{
- stateGrafik.update.form.description = htmlContent;
- }}
+ value={formData.description}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({ ...prev, description: htmlContent }))
+ }
/>
{
- stateGrafik.update.form.value = Number(val.currentTarget.value);
- }}
+ value={formData.value}
+ onChange={handleChange('value')}
required
/>
diff --git a/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/[id]/page.tsx
index f89351a9..631a4f52 100644
--- a/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/[id]/page.tsx
@@ -82,7 +82,7 @@ function DetailSektorUnggulanDesa() {
Deskripsi
-
+
diff --git a/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/create/page.tsx
index 507cb066..4761de26 100644
--- a/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/sektor-unggulan-desa/create/page.tsx
@@ -78,7 +78,7 @@ function CreateSektorUnggulanDesa() {
{
stateGrafik.create.form.name = e.currentTarget.value;
}}
@@ -101,7 +101,7 @@ function CreateSektorUnggulanDesa() {
label="Jumlah"
type="number"
placeholder="Masukkan jumlah"
- value={stateGrafik.create.form.value}
+ defaultValue={stateGrafik.create.form.value}
onChange={(e) => {
stateGrafik.create.form.value = Number(e.currentTarget.value);
}}
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/_lib/layoutTabs.tsx
index e920da92..4425aa88 100644
--- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/_lib/layoutTabs.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/_lib/layoutTabs.tsx
@@ -2,6 +2,7 @@
'use client'
import colors from '@/con/colors';
import {
+ ScrollArea,
Stack,
Tabs,
TabsList,
@@ -9,28 +10,20 @@ import {
TabsTab,
Title,
Tooltip,
- ScrollArea,
} from '@mantine/core';
+import {
+ IconBuildingCommunity,
+ IconHierarchy,
+ IconUsers
+} from '@tabler/icons-react';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
-import {
- IconHierarchy,
- IconUsers,
- IconGitBranch,
-} from '@tabler/icons-react';
function LayoutTabs({ children }: { children: React.ReactNode }) {
const router = useRouter();
const pathname = usePathname();
const tabs = [
- {
- label: "Posisi Organisasi",
- value: "posisiorganisasi",
- href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi",
- icon: ,
- tooltip: "Kelola daftar posisi organisasi",
- },
{
label: "Pegawai",
value: "pegawai",
@@ -39,12 +32,19 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
tooltip: "Kelola data pegawai BUMDesa",
},
{
- label: "Hubungan Organisasi",
- value: "hubunganorganisasi",
- href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi",
- icon: ,
- tooltip: "Atur hubungan antar posisi organisasi",
+ label: "Posisi Organisasi",
+ value: "posisiorganisasi",
+ href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi",
+ icon: ,
+ tooltip: "Kelola daftar posisi organisasi",
},
+ {
+ label: "Struktur Organisasi",
+ value: "strukturorganisasi",
+ href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/struktur-organisasi",
+ icon: ,
+ tooltip: "Kelola struktur organisasi BUMDesa"
+ }
];
const currentTab = tabs.find((tab) => tab.href === pathname);
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/[id]/page.tsx
deleted file mode 100644
index d063805e..00000000
--- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/[id]/page.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-/* eslint-disable react-hooks/exhaustive-deps */
-'use client';
-
-import { useEffect, useState } from 'react';
-import { useParams, useRouter } from 'next/navigation';
-import { Box, Button, Paper, Select, Stack, TextInput, Title } from '@mantine/core';
-import { toast } from 'react-toastify';
-import { useProxy } from 'valtio/utils';
-import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
-
-export default function EditHubunganOrganisasi() {
- const router = useRouter();
- const { id } = useParams<{ id: string }>();
- const state = useProxy(strukturorganisasiState.hubunganOrganisasi);
- const pegawaiList = strukturorganisasiState.pegawai.findMany.data;
-
- const [form, setForm] = useState({
- atasanId: '',
- bawahanId: '',
- tipe: '',
- });
-
- useEffect(() => {
- strukturorganisasiState.pegawai.findMany.load();
-
- if (id) {
- state.edit.load(id).then(data => {
- if (data) {
- setForm({
- atasanId: data.atasanId,
- bawahanId: data.bawahanId,
- tipe: data.tipe || '',
- });
- }
- });
- }
- }, [id]);
-
- const handleSubmit = async () => {
- if (!form.atasanId || !form.bawahanId) {
- toast.warn("Atasan dan bawahan harus diisi");
- return;
- }
-
- state.edit.id = id;
- state.edit.form = form;
-
- const result = await state.edit.update();
-
- if (result) {
- toast.success("Data berhasil diperbarui");
- router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi');
- }
- };
-
- return (
-
-
-
- Edit Hubungan Organisasi
- ({
- value: p.id,
- label: p.namaLengkap,
- })) || []}
- value={form.atasanId}
- onChange={(val) => setForm({ ...form, atasanId: val || '' })}
- />
- ({
- value: p.id,
- label: p.namaLengkap,
- })) || []}
- value={form.bawahanId}
- onChange={(val) => setForm({ ...form, bawahanId: val || '' })}
- />
- setForm({ ...form, tipe: e.currentTarget.value })}
- />
- Simpan
-
-
-
- );
-}
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/create/page.tsx
deleted file mode 100644
index 39e0a23a..00000000
--- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/create/page.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-'use client';
-
-import { useEffect, useState } from 'react';
-import { useRouter } from 'next/navigation';
-import { Box, Button, Paper, Select, Stack, TextInput, Title } from '@mantine/core';
-import { toast } from 'react-toastify';
-import { useProxy } from 'valtio/utils';
-import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
-
-export default function CreateHubunganOrganisasi() {
- const router = useRouter();
- const state = useProxy(strukturorganisasiState.hubunganOrganisasi);
- const pegawaiList = strukturorganisasiState.pegawai.findMany.data;
-
- const [form, setForm] = useState({
- atasanId: '',
- bawahanId: '',
- tipe: '',
- });
-
- useEffect(() => {
- strukturorganisasiState.pegawai.findMany.load();
- }, []);
-
- const handleSubmit = async () => {
- if (!form.atasanId || !form.bawahanId) {
- toast.warn("Atasan dan bawahan harus diisi");
- return;
- }
-
- state.create.form = form;
- const result = await state.create.create();
-
- if (result) {
- toast.success("Hubungan Organisasi berhasil ditambahkan");
- router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi');
- }
- };
-
- return (
-
-
-
- Create Hubungan Organisasi
- ({
- value: p.id,
- label: p.namaLengkap,
- })) || []}
- value={form.atasanId}
- onChange={(val) => setForm({ ...form, atasanId: val || '' })}
- />
- ({
- value: p.id,
- label: p.namaLengkap,
- })) || []}
- value={form.bawahanId}
- onChange={(val) => setForm({ ...form, bawahanId: val || '' })}
- />
- setForm({ ...form, tipe: e.currentTarget.value })}
- />
- Simpan
-
-
-
- );
-}
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/page.tsx
deleted file mode 100644
index a4018cba..00000000
--- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/page.tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-'use client'
-import colors from '@/con/colors';
-import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
-import { useShallowEffect } from '@mantine/hooks';
-import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
-import { useRouter } from 'next/navigation';
-import { useProxy } from 'valtio/utils';
-import HeaderSearch from '../../../_com/header';
-import JudulList from '../../../_com/judulList';
-import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
-import { useState } from 'react';
-import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
-
-function HubunganOrganisasi() {
- const [search, setSearch] = useState("");
-
- return (
-
- }
- value={search}
- onChange={(e) => setSearch(e.currentTarget.value)}
- />
-
-
- );
-}
-
-
-function ListHubunganOrganisasi({ search }: { search: string }) {
- const stateOrganisasi = useProxy(strukturorganisasiState.hubunganOrganisasi);
- const [modalHapus, setModalHapus] = useState(false);
- const [selectedId, setSelectedId] = useState(null);
- const router = useRouter();
-
- useShallowEffect(() => {
- stateOrganisasi.findMany.load();
- }, []);
-
- const handleHapus = () => {
- if (selectedId) {
- stateOrganisasi.delete.byId(selectedId);
- setModalHapus(false);
- setSelectedId(null);
- }
- };
-
- const filteredData = (stateOrganisasi.findMany.data || []).filter(item => {
- const keyword = search.toLowerCase();
- return (
- item.atasan?.namaLengkap?.toLowerCase().includes(keyword) ||
- item.bawahan?.namaLengkap?.toLowerCase().includes(keyword) ||
- item.tipe?.toLowerCase().includes(keyword)
- );
- });
-
- if (!stateOrganisasi.findMany.data) {
- return (
-
-
-
- );
- }
-
- return (
-
-
-
-
-
-
- Atasan
- Bawahan
- Tipe
- Edit
- Hapus
-
-
-
- {filteredData
- .sort((a, b) =>
- a.atasan?.namaLengkap.localeCompare(b.atasan?.namaLengkap)
- )
- .map((item) => (
-
- {item.atasan?.namaLengkap}
- {item.bawahan?.namaLengkap}
- {item.tipe}
-
- router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/${item.id}`)}
- >
-
-
-
-
- {
- setSelectedId(item.id);
- setModalHapus(true);
- }}
- >
-
-
-
-
- ))}
-
-
-
-
- setModalHapus(false)}
- onConfirm={handleHapus}
- text='Apakah anda yakin ingin menghapus hubungan organisasi ini?'
- />
-
- );
-}
-
-
-export default HubunganOrganisasi;
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx
index 7cf73d5d..c1a8eef5 100644
--- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx
@@ -1,7 +1,9 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
-import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
+
+import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
import colors from '@/con/colors';
+import ApiFetch from '@/lib/api-fetch';
import {
Box,
Button,
@@ -12,7 +14,8 @@ import {
Stack,
Text,
TextInput,
- Title
+ Title,
+ Tooltip
} from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
@@ -21,42 +24,26 @@ import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-interface PreviewImage {
- file?: File;
- preview: string;
-}
-
-interface PegawaiFormData {
- namaLengkap: string;
- gelarAkademik: string;
- imageId: string | null;
- tanggalMasuk: string;
- email: string;
- telepon: string;
- alamat: string;
- posisiId: string;
- isActive: boolean;
-}
-
-export default function EditPegawai() {
+export default function EditPegawaiBumDes() {
const router = useRouter();
const { id } = useParams<{ id: string }>();
- const [previewImage, setPreviewImage] = useState(null);
- const stateOrganisasi = useProxy(strukturorganisasiState.pegawai);
- const [formData, setFormData] = useState({
- namaLengkap: "",
- gelarAkademik: "",
- imageId: "",
- tanggalMasuk: "",
- email: "",
- telepon: "",
- alamat: "",
- posisiId: "",
+ const stateOrganisasi = useProxy(stateStrukturBumDes.pegawai);
+
+ const [formData, setFormData] = useState({
+ namaLengkap: '',
+ gelarAkademik: '',
+ imageId: '',
+ tanggalMasuk: '',
+ email: '',
+ telepon: '',
+ alamat: '',
+ posisiId: '',
isActive: true,
});
+ const [previewImage, setPreviewImage] = useState(null);
+ const [file, setFile] = useState(null);
-
- // Format date to YYYY-MM-DD for date input
+ // Format date for
const formatDateForInput = (dateString: string) => {
if (!dateString) return '';
const date = new Date(dateString);
@@ -64,34 +51,29 @@ export default function EditPegawai() {
};
useEffect(() => {
- strukturorganisasiState.posisiOrganisasi.findMany.load();
const loadPegawai = async () => {
try {
+ await stateStrukturBumDes.posisiOrganisasi.findManyAll.load();
+
const data = await stateOrganisasi.edit.load(id);
if (data) {
setFormData({
- namaLengkap: data.namaLengkap || "",
- gelarAkademik: data.gelarAkademik || "",
- imageId: data.imageId || "",
- tanggalMasuk: data.tanggalMasuk || "",
- email: data.email || "",
- telepon: data.telepon || "",
- alamat: data.alamat || "",
- posisiId: data.posisiId || "",
- isActive: data.isActive ?? true, // pakai nullish coalescing
+ namaLengkap: data.namaLengkap || '',
+ gelarAkademik: data.gelarAkademik || '',
+ imageId: data.imageId || '',
+ tanggalMasuk: data.tanggalMasuk || '',
+ email: data.email || '',
+ telepon: data.telepon || '',
+ alamat: data.alamat || '',
+ posisiId: data.posisiId || '',
+ isActive: data.isActive ?? true,
});
- if (data.image?.link) {
- setPreviewImage(data.image.link);
- } else {
- setPreviewImage(null);
- }
+ setPreviewImage(data.image?.link || null);
}
} catch (error) {
- console.error("Error loading pegawai:", error);
- toast.error(
- error instanceof Error ? error.message : "Gagal mengambil data pegawai"
- );
+ console.error('Error loading pegawai:', error);
+ toast.error(error instanceof Error ? error.message : 'Gagal mengambil data pegawai');
}
};
@@ -101,118 +83,109 @@ export default function EditPegawai() {
const handleSubmit = async () => {
try {
if (!formData.namaLengkap.trim()) {
- toast.error('Nama lengkap tidak boleh kosong');
- return;
+ return toast.error('Nama lengkap tidak boleh kosong');
}
- stateOrganisasi.edit.form = {
- namaLengkap: formData.namaLengkap.trim(),
- gelarAkademik: formData.gelarAkademik.trim(),
- imageId: formData.imageId ? formData.imageId.trim() : "",
- tanggalMasuk: formData.tanggalMasuk.trim(),
- email: formData.email.trim(),
- telepon: formData.telepon.trim(),
- alamat: formData.alamat.trim(),
- posisiId: formData.posisiId.trim(),
- isActive: formData.isActive,
- };
-
+ // Update global state only on submit
+ const updatedForm = { ...formData };
-
- if (id && !stateOrganisasi.edit.id) {
- stateOrganisasi.edit.id = id;
+ if (file) {
+ const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
+ const uploaded = res.data?.data;
+ if (!uploaded?.id) return toast.error('Gagal upload gambar');
+ updatedForm.imageId = uploaded.id;
}
+ stateOrganisasi.edit.form = updatedForm;
+ if (id && !stateOrganisasi.edit.id) stateOrganisasi.edit.id = id;
+
const success = await stateOrganisasi.edit.submit();
-
- if (success) {
- router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai");
- }
+ if (success) router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai');
} catch (error) {
- console.error("Error updating pegawai:", error);
- toast.error(error instanceof Error ? error.message : "Gagal memperbarui data pegawai");
+ console.error('Error updating pegawai:', error);
+ toast.error(error instanceof Error ? error.message : 'Gagal memperbarui data pegawai');
}
-
-
-
};
return (
-
-
- router.back()} variant='subtle' color={'blue'}>
-
-
-
+
+
+
+ router.back()} p="xs" radius="md">
+
+
+
+ Edit Data Pegawai PPID
+
-
-
- Edit Data Pegawai
+
+
+ {/* Nama Lengkap */}
setFormData({ ...formData, namaLengkap: e.target.value })}
+ required
/>
+
+ {/* Gelar Akademik */}
setFormData({ ...formData, gelarAkademik: e.target.value })}
/>
-
- Gambar
-
- {
- const file = files[0]; // Hanya ambil file pertama
- if (file) {
- setPreviewImage({
- file,
- preview: URL.createObjectURL(file)
- });
- }
- }}
- maxSize={5 * 1024 ** 2} // 5MB
- accept={{
- 'image/*': ['.jpeg', '.jpg', '.png', '.webp']
- }}
- >
-
-
-
-
-
-
-
-
-
-
-
-
- Drag images here or click to select files
-
-
- Attach as many files as you like, each file should not exceed 5mb
-
-
-
-
- {previewImage && (
+ {/* Foto Profil */}
+
+ Foto Profil
+ {
+ const selectedFile = files[0];
+ if (selectedFile) {
+ setFile(selectedFile);
+ setPreviewImage(URL.createObjectURL(selectedFile));
+ }
+ }}
+ onReject={() => toast.error('File tidak valid, gunakan format gambar')}
+ maxSize={5 * 1024 ** 2}
+ accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
+ radius="md"
+ p="xl"
+ >
+
+
+
+
+
+ Seret gambar atau klik untuk memilih file
+ Maksimal 5MB, format gambar wajib
+
+
+
+
+ {previewImage && (
+
- )}
-
+
+ )}
+
+ {/* Tanggal Masuk */}
setFormData({ ...formData, tanggalMasuk: e.target.value })}
/>
+
+ {/* Email */}
(formData.email = e.currentTarget.value)}
+ onChange={(e) => setFormData({ ...formData, email: e.target.value })}
/>
+
+ {/* Telepon */}
(formData.telepon = e.currentTarget.value)}
+ onChange={(e) => setFormData({ ...formData, telepon: e.target.value })}
/>
+
+ {/* Alamat */}
(formData.alamat = e.currentTarget.value)}
- />
- ({
- value: p.id, // harus string
- label: p.nama,
- })) || []
- }
- value={formData.posisiId}
- onChange={(value) => {
- if (value !== null) {
- setFormData({ ...formData, posisiId: value }); // value harus string
- }
- }}
- />
- {
- setFormData({ ...formData, isActive: val === 'true' });
- }}
+ onChange={(e) => setFormData({ ...formData, alamat: e.target.value })}
/>
+ {/* Posisi */}
+
+ Posisi
+ ({ value: p.id, label: p.nama })) || []}
+ value={formData.posisiId}
+ onChange={(value) => value && setFormData({ ...formData, posisiId: value })}
+ searchable
+ clearable
+ />
+
-
+ {/* Status Pegawai */}
+
+ Status Pegawai
+ setFormData({ ...formData, isActive: val === 'true' })}
+ clearable
+ />
+
+
+ {/* Submit Button */}
+
Simpan
-
+
);
}
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx
index 261a61de..7a0626e4 100644
--- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx
@@ -1,42 +1,56 @@
'use client'
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
-import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
+import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
+
import colors from '@/con/colors';
-import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
+import { Box, Button, Group, Image, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
-import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
+import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
function DetailPegawai() {
- const statePegawai = useProxy(strukturorganisasiState.pegawai)
- const [modalHapus, setModalHapus] = useState(false)
- const [selectedId, setSelectedId] = useState(null)
- const params = useParams()
+ const statePegawai = useProxy(stateStrukturBumDes.pegawai);
+ const [modalHapus, setModalHapus] = useState(false);
+ const [modalNonActive, setModalNonActive] = useState(false);
+ const [selectedId, setSelectedId] = useState(null);
+ const params = useParams();
const router = useRouter();
useShallowEffect(() => {
- statePegawai.findUnique.load(params?.id as string)
- }, [])
+ stateStrukturBumDes.posisiOrganisasi.findMany.load();
+ statePegawai.findUnique.load(params?.id as string);
+ }, []);
const handleHapus = () => {
if (selectedId) {
- statePegawai.delete.byId(selectedId)
- setModalHapus(false)
- setSelectedId(null)
- router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai")
+ statePegawai.delete.byId(selectedId);
+ setModalHapus(false);
+ setSelectedId(null);
+ router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai");
}
- }
+ };
+
+ const handleNonActive = () => {
+ if (selectedId) {
+ statePegawai.nonActive.byId(selectedId);
+ setModalNonActive(false);
+ setSelectedId(null);
+ router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai");
+ }
+ };
if (!statePegawai.findUnique.data) {
return (
-
+
- )
+ );
}
+ const data = statePegawai.findUnique.data;
+
return (
@@ -44,91 +58,125 @@ function DetailPegawai() {
-
-
- Detail Pegawai
-
-
+
+
+
+ Detail Pegawai PPID
+
+
+
+
- Nama Lengkap
- {statePegawai.findUnique.data?.namaLengkap}
-
-
- Gelar Akademik
- {statePegawai.findUnique.data?.gelarAkademik}
-
-
- Image
- {statePegawai.findUnique.data?.image?.link ? (
-
- ) : (
- Tidak ada gambar
- )}
-
-
- Tanggal Masuk
-
- {statePegawai.findUnique.data?.tanggalMasuk
- ? new Date(statePegawai.findUnique.data.tanggalMasuk).toLocaleDateString()
- : "-"}
+ Nama Lengkap
+
+ {data.namaLengkap || '-'} {data.gelarAkademik || ''}
-
- Email
- {statePegawai.findUnique.data?.email}
-
-
- Telepon
- {statePegawai.findUnique.data?.telepon}
-
-
- Alamat
- {statePegawai.findUnique.data?.alamat}
-
-
- Posisi
-
- {statePegawai.findUnique.data?.posisi ? (
-
- {statePegawai.findUnique.data.posisi.nama}
-
- ) : (
-
- Tidak ada posisi
-
- )}
-
-
-
- Aktif
- {statePegawai.findUnique.data?.isActive ? "Ya" : "Tidak"}
-
-
- {
- if (statePegawai.findUnique.data) {
- setSelectedId(statePegawai.findUnique.data.id);
- setModalHapus(true);
- }
+ Posisi
+ {data.posisi?.nama || '-'}
+
+
+
+ Email
+ {data.email || '-'}
+
+
+
+ Telepon
+ {data.telepon || '-'}
+
+
+
+ Alamat
+ {data.alamat || '-'}
+
+
+
+ Tanggal Masuk
+
+ {data.tanggalMasuk ? new Date(data.tanggalMasuk).toLocaleDateString() : '-'}
+
+
+
+
+ Status
+
+ {data.isActive ? 'Aktif' : 'Tidak Aktif'}
+
+
+
+
+ Foto Profil
+ {data.image?.link ? (
+
-
+ loading='lazy'
+ />
+ ) : (
+ Tidak ada foto profil
+ )}
+
+
+ {
+ setSelectedId(data.id || null);
+ setModalNonActive(true);
+ }}
+ variant="light"
+ radius="md"
+ size="md"
+ >
+ {data.isActive ? "Aktif" : "Nonaktif"}
+
+
+
+
+ {
+ setSelectedId(data.id || null);
+ setModalHapus(true);
+ }}
+ variant="light"
+ radius="md"
+ size="md"
+ >
+
+
+
+
{
- if (statePegawai.findUnique.data) {
- router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${statePegawai.findUnique.data.id}/edit`);
- }
- }}
- disabled={!statePegawai.findUnique.data}
- color="green">
+ color="green"
+ onClick={() => router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${data.id}/edit`)}
+ variant="light"
+ radius="md"
+ size="md"
+ >
-
-
+
+
@@ -139,7 +187,15 @@ function DetailPegawai() {
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
- text="Apakah anda yakin ingin menghapus produk ini?"
+ text="Apakah Anda yakin ingin menghapus data pegawai ini?"
+ />
+
+ {/* Modal NonActive */}
+ setModalNonActive(false)}
+ onConfirm={handleNonActive}
+ text="Apakah Anda yakin ingin menonaktifkan pegawai ini?"
/>
);
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx
index 540a3daf..dbf697ee 100644
--- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx
@@ -1,9 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
-import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
+
+import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
-import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core';
+import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -11,17 +12,17 @@ import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-function CreatePegawai() {
+function CreatePegawaiBumDes() {
const router = useRouter();
const [previewImage, setPreviewImage] = useState<{ preview: string; file: File } | null>(null);
- const stateOrganisasi = useProxy(strukturorganisasiState)
+ const stateOrganisasi = useProxy(stateStrukturBumDes.pegawai)
useEffect(() => {
- stateOrganisasi.posisiOrganisasi.findMany.load();
+ stateStrukturBumDes.posisiOrganisasi.findManyAll.load();
resetForm();
}, []);
const resetForm = () => {
- stateOrganisasi.pegawai.create.form = {
+ stateOrganisasi.create.form = {
namaLengkap: "",
gelarAkademik: "",
imageId: "",
@@ -30,7 +31,7 @@ function CreatePegawai() {
telepon: "",
alamat: "",
posisiId: "",
- isActive: true,
+ isActive: true,
};
};
@@ -52,15 +53,15 @@ function CreatePegawai() {
}
// Set status aktif secara otomatis
- stateOrganisasi.pegawai.create.form.isActive = true;
-
+ stateOrganisasi.create.form.isActive = true;
+
// Simpan ID gambar ke form
- stateOrganisasi.pegawai.create.form.imageId = uploaded.id;
-
+ stateOrganisasi.create.form.imageId = uploaded.id;
+
// Submit form
- await stateOrganisasi.pegawai.create.submit();
-
-
+ await stateOrganisasi.create.submit();
+
+
// Reset form dan redirect
resetForm();
toast.success("Data pegawai berhasil ditambahkan");
@@ -72,130 +73,194 @@ function CreatePegawai() {
};
return (
-
-
- router.back()}>
-
-
-
-
-
- Create Pegawai
- (stateOrganisasi.pegawai.create.form.namaLengkap = e.currentTarget.value)}
- />
- (stateOrganisasi.pegawai.create.form.gelarAkademik = e.currentTarget.value)}
- />
-
- Gambar
-
- {
- const file = files[0]; // Hanya ambil file pertama
- if (file) {
- setPreviewImage({
- file,
- preview: URL.createObjectURL(file)
- });
- }
- }}
- maxSize={5 * 1024 ** 2} // 5MB
- accept={{
- 'image/*': ['.jpeg', '.jpg', '.png', '.webp']
- }}
- >
-
-
-
-
-
-
-
-
-
-
+
+
+
+ router.back()} p="xs" radius="md">
+
+
+
+
+ Tambah Pegawai BUMDesa
+
+
-
-
- Drag images here or click to select files
-
-
- Attach as many files as you like, each file should not exceed 5mb
-
-
-
-
- {previewImage && (
+
+
+
+ (stateOrganisasi.create.form.namaLengkap = e.currentTarget.value)}
+ required
+ />
+
+
+ (stateOrganisasi.create.form.gelarAkademik = e.currentTarget.value)}
+ />
+
+
+
+ Foto Profil
+
+ {
+ const file = files[0];
+ if (file) {
+ setPreviewImage({
+ file,
+ preview: URL.createObjectURL(file)
+ });
+ }
+ }}
+ maxSize={5 * 1024 ** 2} // 5MB
+ accept={{
+ 'image/*': ['.jpeg', '.jpg', '.png', '.webp']
+ }}
+ styles={{
+ root: {
+ border: '2px dashed #ced4da',
+ borderRadius: '8px',
+ padding: '20px',
+ textAlign: 'center',
+ cursor: 'pointer',
+ '&:hover': {
+ borderColor: '#228be6',
+ },
+ },
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Seret gambar ke sini atau klik untuk memilih file
+
+
+ Format yang didukung: JPG, PNG, WebP. Maksimal 5MB
+
+
+
+
+
+ {previewImage && (
+
+
+ Preview Gambar
+
- )}
-
+
+ )}
+
+
+ (stateOrganisasi.create.form.tanggalMasuk = e.currentTarget.value)}
+ />
+
+
+
+ (stateOrganisasi.create.form.email = e.currentTarget.value)}
+ />
+
+
+
+ (stateOrganisasi.create.form.telepon = e.currentTarget.value)}
+ />
+
+
+
+ (stateOrganisasi.create.form.alamat = e.currentTarget.value)}
+ />
+
+
+
+
+ Posisi
+
+ ({
+ value: p.id,
+ label: p.nama
+ })) || []}
+ value={stateOrganisasi.create.form.posisiId}
+ onChange={(value) => {
+ if (value) stateOrganisasi.create.form.posisiId = value;
+ }}
+ searchable
+ clearable
+ />
- (stateOrganisasi.pegawai.create.form.tanggalMasuk = e.currentTarget.value)}
- />
- (stateOrganisasi.pegawai.create.form.email = e.currentTarget.value)}
- />
- (stateOrganisasi.pegawai.create.form.telepon = e.currentTarget.value)}
- />
- (stateOrganisasi.pegawai.create.form.alamat = e.currentTarget.value)}
- />
- ({
- value: p.id,
- label: p.nama
- })) || []}
- value={stateOrganisasi.pegawai.create.form.posisiId}
- onChange={(value) => {
- if (value) stateOrganisasi.pegawai.create.form.posisiId = value;
- }}
- searchable
- />
-
- Simpan
-
+
+
+ Simpan
+
+
);
}
-export default CreatePegawai;
+export default CreatePegawaiBumDes;
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx
index 2a140fc7..d56609de 100644
--- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx
@@ -1,33 +1,33 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
-import { Badge, Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, ThemeIcon } from '@mantine/core';
-import { IconCheck, IconDeviceImacCog, IconSearch, IconX } from '@tabler/icons-react';
+import { Badge, Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, ThemeIcon, Title, Tooltip } from '@mantine/core';
+import { IconCheck, IconDeviceImacCog, IconPlus, IconSearch, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
-import { useEffect, useMemo, useState } from 'react';
+import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
-import JudulList from '../../../_com/judulList';
-import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
+import stateStrukturBumDes from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
-function Pegawai() {
+
+function PegawaiBumDes() {
const [search, setSearch] = useState("");
return (
}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
-
+
);
}
-function ListPegawai({ search }: { search: string }) {
- const stateOrganisasi = useProxy(strukturorganisasiState.pegawai);
+function ListPegawaiBumdes({ search }: { search: string }) {
+ const stateOrganisasi = useProxy(stateStrukturBumDes.pegawai);
const router = useRouter();
const {
@@ -39,21 +39,10 @@ function ListPegawai({ search }: { search: string }) {
} = stateOrganisasi.findMany;
useEffect(() => {
- load(page, 10);
- }, [page]);
+ load(page, 10, search);
+ }, [page, search]);
- const filteredData = useMemo(() => {
- if (!data) return [];
- return data.filter(item => {
- const keyword = search.toLowerCase();
- return (
- item.namaLengkap?.toLowerCase().includes(keyword) ||
- item.gelarAkademik?.toLowerCase().includes(keyword) ||
- item.telepon?.toLowerCase().includes(keyword) ||
- item.posisi?.nama?.toLowerCase().includes(keyword)
- );
- });
- }, [data, search]);
+ const filteredData = data || []
// Handle loading state
if (loading || !data) {
@@ -67,29 +56,51 @@ function ListPegawai({ search }: { search: string }) {
if (data.length === 0) {
return (
-
- Tidak ada data pegawai yang tersedia
+
+
+ Daftar Pegawai BUMDesa
+
+ }
+ color="blue"
+ variant="light"
+ onClick={() => router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create')}
+ >
+ Tambah Baru
+
+
+
+
+ Tidak ada data pegawai yang ditemukan
+
);
}
return (
-
-
+
+
+ Daftar Pegawai BUMDesa
+
+ }
+ color="blue"
+ variant="light"
+ onClick={() => router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create')}
+ >
+ Tambah Baru
+
+
+
-
+
- Nama
- Gelar Akademik
- Telepon
- Posisi
- Aktif
- Detail
+ Nama Lengkap
+ Posisi
+ Status
+ Aksi
@@ -106,10 +117,20 @@ function ListPegawai({ search }: { search: string }) {
}) // Aktif di atas
).map((item) => (
- {item.namaLengkap}
- {item.gelarAkademik}
- {item.telepon}
- {item.posisi?.nama}
+
+
+
+ {item.namaLengkap}
+
+
+
+
+
+
+ {item.posisi?.nama || 'Belum diatur'}
+
+
+
@@ -131,8 +152,15 @@ function ListPegawai({ search }: { search: string }) {
- router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${item.id}`)}>
-
+ }
+ onClick={() => router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${item.id}`)}
+ >
+ Detail
@@ -140,21 +168,22 @@ function ListPegawai({ search }: { search: string }) {
+
+ {
+ load(newPage, 10);
+ window.scrollTo(0, 0);
+ }}
+ total={totalPages}
+ withEdges
+ withControls
+ radius="md"
+ />
+
-
- {
- load(newPage, 10);
- window.scrollTo(0, 0);
- }}
- total={totalPages}
- mt="md"
- mb="md"
- />
-
);
}
-export default Pegawai;
+export default PegawaiBumDes;
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/[id]/page.tsx
index 508647a3..0d8f7ed7 100644
--- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/[id]/page.tsx
@@ -1,30 +1,22 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react-hooks/exhaustive-deps */
-'use client'
+'use client';
+
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
-import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
+import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
import colors from '@/con/colors';
-import {
- Box,
- Button,
- Group,
- Paper,
- Stack,
- Text,
- TextInput,
- Title,
- Tooltip,
-} from '@mantine/core';
+import { Box, Button, Group, Paper, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-function EditPosisiOrganisasi() {
+function EditPosisiOrganisasiBumDes() {
const router = useRouter();
const params = useParams();
const id = params?.id as string;
- const stateOrganisasi = useProxy(strukturorganisasiState.posisiOrganisasi);
+ const stateOrganisasi = useProxy(stateStrukturBumDes.posisiOrganisasi);
const [formData, setFormData] = useState({
nama: '',
@@ -32,13 +24,17 @@ function EditPosisiOrganisasi() {
hierarki: 0,
});
- useEffect(() => {
- const loadPosisiOrganisasi = async () => {
- if (!id) return;
+ // Fungsi generik untuk update formData
+ const handleChange = (field: keyof typeof formData, value: any) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+ useEffect(() => {
+ if (!id) return;
+
+ const loadPosisiOrganisasi = async () => {
try {
const data = await stateOrganisasi.edit.load(id);
-
if (data) {
stateOrganisasi.edit.id = id;
setFormData({
@@ -47,8 +43,8 @@ function EditPosisiOrganisasi() {
hierarki: data.hierarki || 0,
});
}
- } catch (error) {
- console.error('Error loading posisi organisasi:', error);
+ } catch (err) {
+ console.error('Error loading posisi organisasi:', err);
toast.error('Gagal memuat data posisi organisasi');
}
};
@@ -57,12 +53,13 @@ function EditPosisiOrganisasi() {
}, [id]);
const handleSubmit = async () => {
- try {
- if (!formData.nama.trim()) {
- toast.error('Nama posisi organisasi tidak boleh kosong');
- return;
- }
+ if (!formData.nama.trim()) {
+ toast.error('Nama posisi organisasi tidak boleh kosong');
+ return;
+ }
+ try {
+ // Update global state hanya saat submit
stateOrganisasi.edit.form = {
nama: formData.nama.trim(),
deskripsi: formData.deskripsi.trim(),
@@ -70,19 +67,17 @@ function EditPosisiOrganisasi() {
};
if (!stateOrganisasi.edit.id) {
- stateOrganisasi.edit.id = id; // fallback
+ stateOrganisasi.edit.id = id;
}
const success = await stateOrganisasi.edit.update();
if (success) {
- toast.success('Posisi organisasi berhasil diperbarui!');
- router.push(
- '/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi'
- );
+ router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi');
}
- } catch (error) {
- console.error('Error updating posisi organisasi:', error);
+ } catch (err) {
+ console.error('Error updating posisi organisasi:', err);
+ // toast error biasanya sudah ada di update
}
};
@@ -90,17 +85,12 @@ function EditPosisiOrganisasi() {
- router.back()}
- p="xs"
- radius="md"
- >
+ router.back()} p="xs" radius="md">
- Edit Posisi Organisasi
+ Edit Posisi Organisasi BUMDes
@@ -114,44 +104,40 @@ function EditPosisiOrganisasi() {
>
- setFormData({ ...formData, nama: e.target.value })
- }
label="Nama Posisi Organisasi"
placeholder="Masukkan nama posisi organisasi"
+ value={formData.nama}
+ onChange={(e) => handleChange('nama', e.target.value)}
required
/>
-
+
Deskripsi
- setFormData({ ...formData, deskripsi: htmlContent })
- }
+ onChange={(html) => handleChange('deskripsi', html)}
/>
- setFormData({
- ...formData,
- hierarki: parseInt(e.target.value) || 0,
- })
- }
label="Hierarki"
- placeholder="Masukkan hierarki"
+ type="number"
+ min={0}
+ placeholder="Contoh: 1 (Angka semakin kecil, posisi semakin tinggi)"
+ value={formData.hierarki}
+ onChange={(e) => {
+ const value = parseInt(e.target.value, 10);
+ handleChange('hierarki', isNaN(value) ? 0 : value);
+ }}
required
/>
-
+
{
- stateOrganisasi.findMany.load();
- }, []);
-
- const resetForm = () => {
- stateOrganisasi.create.form = {
- nama: '',
- deskripsi: '',
- hierarki: 0,
- };
+function CreatePosisiOrganisasiBumDes() {
+ const router = useRouter();
+ const stateOrganisasi = useProxy(stateStrukturBumDes.posisiOrganisasi);
+
+ useEffect(() => {
+ stateOrganisasi.findMany.load();
+ // Initialize form with default values
+ stateOrganisasi.create.form = {
+ nama: "",
+ deskripsi: "",
+ hierarki: 0,
};
-
- const handleSubmit = async () => {
- await stateOrganisasi.create.submit();
- resetForm();
- router.push(
- '/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi'
- );
+
+ return () => {
+ // Clean up form on unmount
+ stateOrganisasi.create.form = {
+ nama: "",
+ deskripsi: "",
+ hierarki: 0,
+ };
};
+ }, []);
- return (
-
- {/* Header Back + Title */}
-
-
- router.back()}
- p="xs"
- radius="md"
- >
-
-
-
-
- Tambah Posisi Organisasi
-
-
+ const handleSubmit = async () => {
+ try {
+ if (!stateOrganisasi.create.form.nama.trim()) {
+ return toast.error('Nama posisi tidak boleh kosong');
+ }
+
+ await stateOrganisasi.create.submit();
+ toast.success('Posisi organisasi berhasil ditambahkan');
+ router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi');
+ } catch (error) {
+ toast.error('Gagal menambahkan posisi organisasi');
+ console.error('Error:', error);
+ }
+ };
- {/* Form Card */}
-
+
+
+ router.back()} p="xs" radius="md">
+
+
+
+
+ Tambah Posisi Organisasi BUMDes
+
+
+
+
+
+ (stateOrganisasi.create.form.nama = e.target.value)}
+ required
+ />
+
+
+
+ Deskripsi
+
+ {
+ stateOrganisasi.create.form.deskripsi = htmlContent;
+ }}
+ />
+
+
+ {
+ const value = parseInt(e.target.value, 10);
+ stateOrganisasi.create.form.hierarki = isNaN(value) ? 0 : value;
+ }}
+ required
+ />
+
+
+
-
- (stateOrganisasi.create.form.nama = e.currentTarget.value)}
- required
- />
-
-
- Deskripsi
- {
- stateOrganisasi.create.form.deskripsi = htmlContent;
- }}
- />
-
-
- {
- const value = parseInt(e.currentTarget.value, 10);
- if (!isNaN(value)) {
- stateOrganisasi.create.form.hierarki = value;
- }
- }}
- required
- />
-
- {/* Action Button */}
-
-
- Simpan
-
-
-
-
-
- );
+ Simpan
+
+
+
+
+
+ );
}
-export default CreatePosisiOrganisasi;
+export default CreatePosisiOrganisasiBumDes;
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/page.tsx
index 33f0cfd0..ea181a8f 100644
--- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/page.tsx
@@ -1,58 +1,37 @@
'use client'
import colors from '@/con/colors';
-import {
- Box,
- Button,
- Center,
- Group,
- Paper,
- Pagination,
- Skeleton,
- Stack,
- Table,
- TableTbody,
- TableTd,
- TableTh,
- TableThead,
- TableTr,
- Text,
- Title,
- Tooltip,
-} from '@mantine/core';
+import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
-import { useShallowEffect } from '@mantine/hooks';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
-import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
+import stateStrukturBumDes from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
-function PosisiOrganisasi() {
+
+function PosisiOrganisasiBumDes() {
const [search, setSearch] = useState("");
return (
- {/* Search Bar */}
}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
-
- {/* List Table */}
-
+
);
}
-function ListPosisiOrganisasi({ search }: { search: string }) {
- const stateOrganisasi = useProxy(strukturorganisasiState.posisiOrganisasi);
+function ListPosisiOrganisasiBumDes({ search }: { search: string }) {
+ const stateOrganisasi = useProxy(stateStrukturBumDes.posisiOrganisasi)
const router = useRouter();
-
- const [modalHapus, setModalHapus] = useState(false);
- const [selectedId, setSelectedId] = useState(null);
+ const [modalHapus, setModalHapus] = useState(false)
+ const [selectedId, setSelectedId] = useState(null)
const {
data,
@@ -66,21 +45,20 @@ function ListPosisiOrganisasi({ search }: { search: string }) {
load(page, 10, search);
}, [page, search]);
- const filteredData = data || [];
-
const handleHapus = async () => {
if (selectedId) {
await stateOrganisasi.delete.byId(selectedId);
- setModalHapus(false);
- setSelectedId(null);
- load(page, 10, search); // refresh
+ setModalHapus(false)
+ setSelectedId(null)
}
- };
+ }
+
+ const filteredData = data || []
if (loading || !data) {
return (
-
+
);
}
@@ -89,71 +67,70 @@ function ListPosisiOrganisasi({ search }: { search: string }) {
- Daftar Posisi Organisasi
-
+ Daftar Posisi Organisasi BumDes
+
}
color="blue"
variant="light"
- onClick={() =>
- router.push(
- '/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/create'
- )
- }
+ onClick={() => router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/create')}
>
Tambah Baru
-
- Nama Posisi
- Hierarki
- Edit
- Hapus
+ Nama Posisi
+ Deskripsi
+ Hierarki
+ Edit
+ Hapus
{filteredData.length > 0 ? (
filteredData.map((item) => (
-
+
{item.nama}
-
- {item.hierarki ?? '-'}
+
+
+
+
-
-
+
+ {item.hierarki || '-'}
+
+
+
- router.push(
- `/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/${item.id}`
- )
- }
+ size="sm"
+ onClick={() => router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/${item.id}`)}
>
-
-
- {
- setSelectedId(item.id);
- setModalHapus(true);
- }}
- >
-
-
-
+
+
+ {
+ setSelectedId(item.id);
+ setModalHapus(true);
+ }}
+ >
+
+
+
))
@@ -170,8 +147,6 @@ function ListPosisiOrganisasi({ search }: { search: string }) {
-
- {/* Pagination */}
-
{/* Modal Hapus */}
setModalHapus(false)}
onConfirm={handleHapus}
- text="Apakah anda yakin ingin menghapus posisi organisasi ini?"
+ text="Apakah anda yakin ingin menghapus posisi organisasi BumDes ini?"
/>
);
}
-export default PosisiOrganisasi;
+export default PosisiOrganisasiBumDes;
diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/struktur-organisasi/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/struktur-organisasi/page.tsx
new file mode 100644
index 00000000..be24d78b
--- /dev/null
+++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/struktur-organisasi/page.tsx
@@ -0,0 +1,133 @@
+/* eslint-disable prefer-const */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable react-hooks/exhaustive-deps */
+'use client'
+import { Box, Center, Image, Loader, Paper, Stack, Text, Tooltip } from '@mantine/core';
+import { IconUsers } from '@tabler/icons-react';
+import { OrganizationChart } from 'primereact/organizationchart';
+import { useEffect } from 'react';
+import { useProxy } from 'valtio/utils';
+import stateStrukturBumDes from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
+
+function StrukturOrganisasiBumDes() {
+ return (
+
+
+
+ );
+}
+
+function ListStrukturOrganisasiBumDes() {
+ const stateOrganisasi = useProxy(stateStrukturBumDes.pegawai);
+
+ useEffect(() => {
+ stateOrganisasi.findMany.load();
+ }, []);
+
+ if (stateOrganisasi.findMany.loading) {
+ return (
+
+
+
+ );
+ }
+
+ if (!stateOrganisasi.findMany.data || stateOrganisasi.findMany.data.length === 0) {
+ return (
+
+
+ Belum ada struktur organisasi yang ditambahkan
+
+ );
+ }
+
+ const posisiMap = new Map();
+
+ const aktifPegawai = stateOrganisasi.findMany.data.filter(p => p.isActive);
+
+ for (const pegawai of aktifPegawai) {
+ const posisiId = pegawai.posisi.id;
+ if (!posisiMap.has(posisiId)) {
+ posisiMap.set(posisiId, {
+ ...pegawai.posisi,
+ pegawaiList: [],
+ children: [],
+ });
+ }
+ posisiMap.get(posisiId)!.pegawaiList.push(pegawai);
+ }
+
+ let root: any[] = [];
+ posisiMap.forEach((posisi) => {
+ if (posisi.parentId) {
+ const parent = posisiMap.get(posisi.parentId);
+ if (parent) {
+ parent.children.push(posisi);
+ }
+ } else {
+ root.push(posisi);
+ }
+ });
+
+ function toOrgChartFormat(node: any): any {
+ return {
+ expanded: true,
+ type: 'person',
+ styleClass: 'p-person',
+ data: {
+ name: node.pegawaiList?.[0]?.namaLengkap || 'Belum ada pegawai',
+ status: node.nama,
+ image: node.pegawaiList?.[0]?.image?.link || '/img/default.png',
+ },
+ children: node.children.map(toOrgChartFormat),
+ };
+ }
+
+ const chartData = root.map(toOrgChartFormat);
+
+ return (
+
+
+
+
+
+ );
+}
+
+function nodeTemplate(node: any) {
+ const imageSrc = node?.data?.image || '/img/default.png';
+ const name = node?.data?.name || 'Tanpa Nama';
+ const status = node?.data?.status || 'Tidak ada deskripsi';
+
+ return (
+
+
+
+
+ {name}
+ {status}
+
+ );
+}
+
+export default StrukturOrganisasiBumDes;
diff --git a/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/[id]/page.tsx
index c00259b1..f1857aec 100644
--- a/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/[id]/page.tsx
@@ -94,7 +94,7 @@ function DetailAjukanIdeInofativDesa() {
Alamat
-
+
@@ -104,12 +104,12 @@ function DetailAjukanIdeInofativDesa() {
Deskripsi
-
+
Masalah
- {data?.masalah || '-'}
+ {data?.masalah || '-'}
diff --git a/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/page.tsx b/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/page.tsx
index 51ba6814..24bdbd06 100644
--- a/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/page.tsx
@@ -4,7 +4,6 @@ import {
Box,
Button,
Center,
- Group,
Pagination,
Paper,
Skeleton,
@@ -16,11 +15,10 @@ import {
TableThead,
TableTr,
Text,
- Title,
- Tooltip,
+ Title
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
-import { IconDeviceImac, IconSearch, IconPlus } from '@tabler/icons-react';
+import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
@@ -72,20 +70,7 @@ function ListAjukanIdeInovatif({ search }: { search: string }) {
return (
-
- Daftar Ide Inovatif
-
- }
- color="blue"
- variant="light"
- onClick={() => router.push('/admin/inovasi/ajukan-ide-inovatif/create')}
- >
- Tambah Baru
-
-
-
-
+ Daftar Ide Inovatif
diff --git a/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/[id]/edit/page.tsx b/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/[id]/edit/page.tsx
index 715e3c0d..05c93b59 100644
--- a/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/[id]/edit/page.tsx
@@ -1,10 +1,21 @@
-'use client'
+'use client';
/* eslint-disable react-hooks/exhaustive-deps */
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import desaDigitalState from '@/app/admin/(dashboard)/_state/inovasi/desa-digital';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
-import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
+import {
+ Box,
+ Button,
+ Group,
+ Image,
+ Paper,
+ Stack,
+ Text,
+ TextInput,
+ Title,
+ Tooltip,
+} from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -12,20 +23,22 @@ import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-function EditPenghargaan() {
- const stateDesaDigital = useProxy(desaDigitalState)
- const router = useRouter()
- const params = useParams()
- const [previewImage, setPreviewImage] = useState(null)
- const [file, setFile] = useState(null)
+function EditDigitalSmartVillage() {
+ const stateDesaDigital = useProxy(desaDigitalState);
+ const router = useRouter();
+ const params = useParams();
+
+ const [previewImage, setPreviewImage] = useState(null);
+ const [file, setFile] = useState(null);
+
const [formData, setFormData] = useState({
- name: stateDesaDigital.findUnique.data?.name || '',
- deskripsi: stateDesaDigital.findUnique.data?.deskripsi || '',
- imageId: stateDesaDigital.findUnique.data?.imageId || '',
- })
+ name: '',
+ deskripsi: '',
+ imageId: '',
+ });
useEffect(() => {
- const loadPenghargaan = async () => {
+ const loadData = async () => {
const id = params?.id as string;
if (!id) return;
@@ -38,136 +51,160 @@ function EditPenghargaan() {
imageId: data.imageId || '',
});
- if (data?.image?.link) {
- setPreviewImage(data.image.link);
- }
+ if (data?.image?.link) setPreviewImage(data.image.link);
}
} catch (error) {
- console.error("Error loading desa digital smart village:", error);
- toast.error("Gagal memuat data desa digital smart village");
+ console.error('Error loading data:', error);
+ toast.error('Gagal memuat data desa digital smart village');
}
};
- loadPenghargaan();
+ loadData();
}, [params?.id]);
const handleSubmit = async () => {
try {
- stateDesaDigital.edit.form = {
- ...stateDesaDigital.edit.form,
- name: formData.name,
- deskripsi: formData.deskripsi,
- imageId: formData.imageId,
- }
+ stateDesaDigital.edit.form = { ...stateDesaDigital.edit.form, ...formData };
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
-
- if (!uploaded?.id) {
- return toast.error("Gagal upload gambar");
- }
+ if (!uploaded?.id) return toast.error('Gagal upload gambar');
stateDesaDigital.edit.form.imageId = uploaded.id;
}
await stateDesaDigital.edit.update();
- toast.success("Desa digital smart village berhasil diperbarui!");
- router.push("/admin/inovasi/desa-digital-smart-village");
+ toast.success('Desa digital smart village berhasil diperbarui!');
+ router.push('/admin/inovasi/desa-digital-smart-village');
} catch (error) {
- console.error("Error updating desa digital smart village:", error);
- toast.error("Terjadi kesalahan saat memperbarui desa digital smart village");
+ console.error('Error updating desa digital:', error);
+ toast.error('Terjadi kesalahan saat memperbarui data');
}
- }
+ };
return (
-
-
- router.back()}>
-
-
-
-
-
- Edit Desa Digital Smart Village
+
+ {/* Header */}
+
+
+ router.back()} p="xs" radius="md">
+
+
+
+
+ Edit Desa Digital Smart Village
+
+
+
+ {/* Form Card */}
+
+
+ {/* Dropzone Upload */}
+
+
+ Gambar Desa Digital
+
+ {
+ const selectedFile = files[0];
+ if (selectedFile) {
+ setFile(selectedFile);
+ setPreviewImage(URL.createObjectURL(selectedFile));
+ }
+ }}
+ onReject={() => toast.error('File tidak valid, gunakan format gambar')}
+ maxSize={5 * 1024 ** 2}
+ accept={{ 'image/*': [] }}
+ radius="md"
+ p="xl"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ Seret gambar atau klik untuk memilih file
+
+
+ Maksimal 5MB, format gambar wajib
+
+
+
+
+
+ {previewImage && (
+
+
+
+ )}
+
+
+ {/* Input Judul */}
setFormData({ ...formData, name: e.target.value })}
- label={Judul }
- placeholder="masukkan judul"
+ required
/>
-
- Gambar
-
- {
- const selectedFile = files[0]; // Ambil file pertama
- if (selectedFile) {
- setFile(selectedFile);
- setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview
- }
- }}
- onReject={() => toast.error('File tidak valid.')}
- maxSize={5 * 1024 ** 2} // Maks 5MB
- accept={{ 'image/*': [] }}
- >
-
-
-
-
-
-
-
-
-
-
-
-
- Drag gambar ke sini atau klik untuk pilih file
-
-
- Maksimal 5MB dan harus format gambar
-
-
-
-
-
- {/* Tampilkan preview kalau ada */}
- {previewImage && (
-
-
-
- )}
-
-
+ {/* Editor Deskripsi */}
- Deskripsi
+
+ Deskripsi
+
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- stateDesaDigital.edit.form.deskripsi = htmlContent;
- }}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
+ }
/>
- Simpan
+ {/* Tombol Simpan */}
+
+
+ Simpan
+
+
);
}
-export default EditPenghargaan;
+export default EditDigitalSmartVillage;
diff --git a/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/[id]/page.tsx
index 1fcbdbeb..c82485e0 100644
--- a/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/[id]/page.tsx
@@ -1,8 +1,18 @@
'use client'
import colors from '@/con/colors';
-import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
+import {
+ Box,
+ Button,
+ Group,
+ Image,
+ Paper,
+ Skeleton,
+ Stack,
+ Text,
+ Tooltip,
+} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
-import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
+import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
@@ -10,95 +20,136 @@ import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import desaDigitalState from '../../../_state/inovasi/desa-digital';
function DetailDesaDigital() {
- const stateDesaDigital = useProxy(desaDigitalState)
+ const stateDesaDigital = useProxy(desaDigitalState);
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState(null);
- const router = useRouter()
- const params = useParams()
+ const router = useRouter();
+ const params = useParams();
useShallowEffect(() => {
- stateDesaDigital.findUnique.load(params?.id as string)
- }, [])
+ stateDesaDigital.findUnique.load(params?.id as string);
+ }, []);
const handleHapus = () => {
if (selectedId) {
- stateDesaDigital.delete.byId(selectedId)
- setModalHapus(false)
- setSelectedId(null)
- router.push("/admin/inovasi/desa-digital-smart-village")
+ stateDesaDigital.delete.byId(selectedId);
+ setModalHapus(false);
+ setSelectedId(null);
+ router.push("/admin/inovasi/desa-digital-smart-village");
}
- }
+ };
if (!stateDesaDigital.findUnique.data) {
return (
-
+
- )
+ );
}
+ const data = stateDesaDigital.findUnique.data;
+
return (
-
-
- router.back()}>
-
-
-
-
-
- Detail Desa Digital Smart Village
- {stateDesaDigital.findUnique.data ? (
-
-
-
- Judul
- {stateDesaDigital.findUnique.data?.name}
-
-
- Deskripsi
-
-
-
- Gambar
-
-
-
+
+ {/* Tombol Kembali */}
+ router.back()}
+ leftSection={ }
+ mb={15}
+ >
+ Kembali
+
+
+ {/* Card Utama */}
+
+
+
+ Detail Desa Digital Smart Village
+
+
+ {/* Sub Card Detail */}
+
+
+
+ Judul
+ {data?.name || '-'}
+
+
+
+ Deskripsi
+
+
+
+
+ Gambar
+ {data?.image?.link ? (
+
+ ) : (
+ Tidak ada gambar
+ )}
+
+
+ {/* Tombol Aksi */}
+
+
{
- if (stateDesaDigital.findUnique.data) {
- setSelectedId(stateDesaDigital.findUnique.data.id);
- setModalHapus(true);
- }
+ setSelectedId(data.id);
+ setModalHapus(true);
}}
- disabled={stateDesaDigital.delete.loading || !stateDesaDigital.findUnique.data}
- color={"red"}
+ variant="light"
+ radius="md"
+ size="md"
>
-
+
+
+
+
{
- if (stateDesaDigital.findUnique.data) {
- router.push(`/admin/inovasi/desa-digital-smart-village/${stateDesaDigital.findUnique.data.id}/edit`);
- }
- }}
- disabled={!stateDesaDigital.findUnique.data}
- color={"green"}
+ color="green"
+ onClick={() => router.push(`/admin/inovasi/desa-digital-smart-village/${data.id}/edit`)}
+ variant="light"
+ radius="md"
+ size="md"
>
-
-
-
- ) : null}
+
+
+
+
- {/* Modal Konfirmasi Hapus */}
+ {/* Modal Konfirmasi */}
setModalHapus(false)}
onConfirm={handleHapus}
- text='Apakah anda yakin ingin menghapus desa digital smart village ini?'
+ text="Apakah Anda yakin ingin menghapus desa digital smart village ini?"
/>
);
diff --git a/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/create/page.tsx b/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/create/page.tsx
index 289651f0..6a00b669 100644
--- a/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/create/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/create/page.tsx
@@ -22,7 +22,7 @@ import CreateEditor from '../../../_com/createEditor';
import desaDigitalState from '../../../_state/inovasi/desa-digital';
import { Dropzone } from '@mantine/dropzone';
-function CreateDesaDigital() {
+export default function CreateDesaDigital() {
const stateDesaDigital = useProxy(desaDigitalState);
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
@@ -44,7 +44,6 @@ function CreateDesaDigital() {
}
try {
- // Upload gambar dulu
const uploadRes = await ApiFetch.api.fileStorage.create.post({
file,
name: file.name,
@@ -55,10 +54,8 @@ function CreateDesaDigital() {
return toast.error('Gagal mengunggah gambar');
}
- // Set imageId ke form
stateDesaDigital.create.form.imageId = uploaded.id;
- // Submit form
const success = await stateDesaDigital.create.create();
if (success) {
resetForm();
@@ -72,10 +69,16 @@ function CreateDesaDigital() {
return (
- {/* Header */}
-
+ {/* Header dengan tombol kembali */}
+
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ style={{ transition: 'background 0.2s ease' }}
+ >
@@ -84,28 +87,32 @@ function CreateDesaDigital() {
- {/* Card */}
+ {/* Card Form */}
-
- {/* Nama */}
+
+ {/* Input Nama */}
(stateDesaDigital.create.form.name = e.target.value)}
- required
+ radius="md"
+ withAsterisk
/>
{/* Deskripsi */}
-
+
Deskripsi
-
- Gambar
+
+ Gambar Desa Digital
{
@@ -134,6 +141,11 @@ function CreateDesaDigital() {
accept={{ 'image/*': [] }}
radius="md"
p="xl"
+ style={{
+ border: '2px dashed #cfd8dc',
+ backgroundColor: '#fafafa',
+ transition: 'background-color 0.2s ease, border-color 0.2s ease',
+ }}
>
@@ -153,15 +165,22 @@ function CreateDesaDigital() {
{/* Preview */}
{previewImage && (
-
+
@@ -170,7 +189,7 @@ function CreateDesaDigital() {
{/* Tombol Submit */}
-
+
{
+ (e.currentTarget.style.transform = 'translateY(-2px)');
+ (e.currentTarget.style.boxShadow =
+ '0 6px 20px rgba(79, 172, 254, 0.5)');
+ }}
+ onMouseLeave={(e) => {
+ (e.currentTarget.style.transform = 'translateY(0)');
+ (e.currentTarget.style.boxShadow =
+ '0 4px 15px rgba(79, 172, 254, 0.4)');
}}
>
Simpan
@@ -189,5 +219,3 @@ function CreateDesaDigital() {
);
}
-
-export default CreateDesaDigital;
diff --git a/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/page.tsx b/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/page.tsx
index f1c1f484..84cb3baf 100644
--- a/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/desa-digital-smart-village/page.tsx
@@ -97,17 +97,21 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
filteredData.map((item) => (
-
- {item.name}
-
+
+
+ {item.name}
+
+
-
+
+
+
(null);
const [file, setFile] = useState(null);
+
const [formData, setFormData] = useState({
- name: stateInfoTekno.findUnique.data?.name || '',
- deskripsi: stateInfoTekno.findUnique.data?.deskripsi || '',
- imageId: stateInfoTekno.findUnique.data?.imageId || '',
+ name: '',
+ deskripsi: '',
+ imageId: '',
});
+ // Load data pertama kali
useEffect(() => {
const id = params?.id as string;
if (!id) return;
- const loadPenghargaan = async () => {
+ const loadData = async () => {
try {
const data = await stateInfoTekno.edit.load(id);
if (data) {
@@ -58,16 +60,19 @@ function EditInfoTeknologiTepatGuna() {
}
};
- loadPenghargaan();
+ loadData();
}, [params?.id]);
+ // Submit form
const handleSubmit = async () => {
try {
+ // sync local β global pas submit
stateInfoTekno.edit.form = {
...stateInfoTekno.edit.form,
...formData,
};
+ // upload file kalau ada
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({
file,
@@ -120,7 +125,7 @@ function EditInfoTeknologiTepatGuna() {
label="Judul"
placeholder="Masukkan judul info teknologi tepat guna"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}
required
/>
@@ -188,10 +193,9 @@ function EditInfoTeknologiTepatGuna() {
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- stateInfoTekno.edit.form.deskripsi = htmlContent;
- }}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
+ }
/>
diff --git a/src/app/admin/(dashboard)/inovasi/info-teknologi-tepat-guna/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/info-teknologi-tepat-guna/[id]/page.tsx
index 4313fcaa..8e9d2dab 100644
--- a/src/app/admin/(dashboard)/inovasi/info-teknologi-tepat-guna/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/info-teknologi-tepat-guna/[id]/page.tsx
@@ -55,7 +55,7 @@ function DetailInfoTeknologiTepatGuna() {
diff --git a/src/app/admin/(dashboard)/inovasi/info-teknologi-tepat-guna/create/page.tsx b/src/app/admin/(dashboard)/inovasi/info-teknologi-tepat-guna/create/page.tsx
index d2d09028..be8b23b7 100644
--- a/src/app/admin/(dashboard)/inovasi/info-teknologi-tepat-guna/create/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/info-teknologi-tepat-guna/create/page.tsx
@@ -94,7 +94,7 @@ function CreateInfoTeknologiTepatGuna() {
{/* Nama */}
{
stateInfoTekno.create.form.name = val.target.value;
}}
diff --git a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/[id]/edit/page.tsx b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/[id]/edit/page.tsx
index 24827165..cce425a1 100644
--- a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/[id]/edit/page.tsx
@@ -27,14 +27,14 @@ function EditKolaborasiInovasi() {
const params = useParams();
const [formData, setFormData] = useState({
- name: kolaborasiState.update.form.name || "",
- deskripsi: kolaborasiState.update.form.deskripsi || "",
- tahun: kolaborasiState.update.form.tahun || "",
- slug: kolaborasiState.update.form.slug || "",
- kolaborator: kolaborasiState.update.form.kolaborator || "",
+ name: "",
+ deskripsi: "",
+ tahun: "",
+ slug: "",
+ kolaborator: "",
});
- // Load data
+ // Load data awal dari server
useEffect(() => {
const loadKolaborasi = async () => {
const id = params?.id as string;
@@ -44,11 +44,11 @@ function EditKolaborasiInovasi() {
const data = await kolaborasiState.update.load(id);
if (data) {
setFormData({
- name: data.name || "",
- deskripsi: data.deskripsi || "",
- tahun: data.tahun || "",
- slug: data.slug || "",
- kolaborator: data.kolaborator || "",
+ name: data.name ?? "",
+ deskripsi: data.deskripsi ?? "",
+ tahun: data.tahun?.toString() ?? "",
+ slug: data.slug ?? "",
+ kolaborator: data.kolaborator ?? "",
});
}
} catch (error) {
@@ -60,6 +60,7 @@ function EditKolaborasiInovasi() {
loadKolaborasi();
}, [params?.id]);
+ // Handler submit β baru update global state
const handleSubmit = async () => {
try {
kolaborasiState.update.form = {
@@ -70,6 +71,7 @@ function EditKolaborasiInovasi() {
slug: formData.slug,
kolaborator: formData.kolaborator,
};
+
await kolaborasiState.update.submit();
toast.success("Kolaborasi inovasi berhasil diperbarui!");
router.push("/admin/inovasi/kolaborasi-inovasi");
@@ -79,6 +81,11 @@ function EditKolaborasiInovasi() {
}
};
+ // Handler input (biar lebih DRY)
+ const handleChange = (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
return (
@@ -105,7 +112,7 @@ function EditKolaborasiInovasi() {
label="Nama Kolaborasi"
placeholder="Masukkan nama kolaborasi"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange("name", e.target.value)}
required
/>
@@ -113,7 +120,7 @@ function EditKolaborasiInovasi() {
label="Deskripsi Singkat"
placeholder="Masukkan deskripsi singkat"
value={formData.slug}
- onChange={(e) => setFormData({ ...formData, slug: e.target.value })}
+ onChange={(e) => handleChange("slug", e.target.value)}
required
/>
@@ -121,7 +128,7 @@ function EditKolaborasiInovasi() {
label="Tahun"
placeholder="Masukkan tahun"
value={formData.tahun}
- onChange={(e) => setFormData({ ...formData, tahun: e.target.value })}
+ onChange={(e) => handleChange("tahun", e.target.value)}
required
/>
@@ -129,7 +136,7 @@ function EditKolaborasiInovasi() {
label="Kolaborator"
placeholder="Masukkan nama kolaborator"
value={formData.kolaborator}
- onChange={(e) => setFormData({ ...formData, kolaborator: e.target.value })}
+ onChange={(e) => handleChange("kolaborator", e.target.value)}
required
/>
@@ -139,10 +146,7 @@ function EditKolaborasiInovasi() {
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- kolaborasiState.update.form.deskripsi = htmlContent;
- }}
+ onChange={(htmlContent) => handleChange("deskripsi", htmlContent)}
/>
diff --git a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/[id]/page.tsx
index cc4d06f5..0d3b0f9a 100644
--- a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/[id]/page.tsx
@@ -81,12 +81,12 @@ function DetailKolaborasiInovasi() {
Deskripsi Singkat
- {data?.slug || '-'}
+ {data?.slug || '-'}
Deskripsi
-
+
diff --git a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/create/page.tsx b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/create/page.tsx
index a45d50ad..8ed78d71 100644
--- a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/create/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/create/page.tsx
@@ -77,7 +77,7 @@ function CreateProgramKreatifDesa() {
Nama Kolaborasi Inovasi}
placeholder="Masukkan nama kolaborasi inovasi"
- value={stateCreate.create.form.name || ''}
+ defaultValue={stateCreate.create.form.name || ''}
onChange={(val) => stateCreate.create.form.name = val.target.value}
required
/>
@@ -106,7 +106,7 @@ function CreateProgramKreatifDesa() {
Kolaborator}
placeholder="Masukkan kolaborator"
- value={stateCreate.create.form.kolaborator || ''}
+ defaultValue={stateCreate.create.form.kolaborator || ''}
onChange={(e) => stateCreate.create.form.kolaborator = e.currentTarget.value}
/>
diff --git a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/mitra-kolaborasi/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/mitra-kolaborasi/[id]/page.tsx
index ac9c1ea2..a1762db1 100644
--- a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/mitra-kolaborasi/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/mitra-kolaborasi/[id]/page.tsx
@@ -33,17 +33,22 @@ function EditMitraKolaborasi() {
const state = useProxy(mitraKolaborasi);
const router = useRouter();
const params = useParams();
+
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
+
+ // Local form state (controlled)
const [formData, setFormData] = useState({
- name: state.update.form.name || '',
- imageId: state.update.form.imageId || '',
+ name: '',
+ imageId: '',
});
+ // Load data ke state lokal sekali saja
useEffect(() => {
- const loadFoto = async () => {
+ const loadData = async () => {
const id = params?.id as string;
if (!id) return;
+
try {
const data = await state.update.load(id);
if (data) {
@@ -51,25 +56,31 @@ function EditMitraKolaborasi() {
name: data.name || '',
imageId: data.imageId || '',
});
+
if (data?.image?.link) {
setPreviewImage(data.image.link);
}
}
} catch (error) {
- console.error('Error loading foto:', error);
- toast.error('Gagal memuat data foto');
+ console.error('Error loading data:', error);
+ toast.error('Gagal memuat data mitra');
}
};
- loadFoto();
+
+ loadData();
}, [params?.id]);
+ const handleChange = (key: string, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [key]: value,
+ }));
+ };
+
const handleSubmit = async () => {
try {
- state.update.form = {
- ...state.update.form,
- name: formData.name,
- imageId: formData.imageId,
- };
+ // upload file jika ada
+ let imageId = formData.imageId;
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({
file,
@@ -79,8 +90,16 @@ function EditMitraKolaborasi() {
if (!uploaded?.id) {
return toast.error('Gagal upload gambar');
}
- state.update.form.imageId = uploaded.id;
+ imageId = uploaded.id;
}
+
+ // update global state hanya saat submit
+ state.update.form = {
+ ...state.update.form,
+ name: formData.name,
+ imageId,
+ };
+
await state.update.update();
toast.success('Mitra berhasil diperbarui!');
router.push('/admin/inovasi/kolaborasi-inovasi/mitra-kolaborasi');
@@ -119,7 +138,7 @@ function EditMitraKolaborasi() {
label="Nama Mitra"
placeholder="Masukkan nama mitra"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange('name', e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/mitra-kolaborasi/create/page.tsx b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/mitra-kolaborasi/create/page.tsx
index 2c9a077c..d7da8e8b 100644
--- a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/mitra-kolaborasi/create/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/mitra-kolaborasi/create/page.tsx
@@ -85,7 +85,7 @@ function CreateMitraKolaborasi() {
(state.create.form.name = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/edit/page.tsx
index 93ffb0ef..80459e66 100644
--- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/edit/page.tsx
@@ -1,9 +1,19 @@
-'use client'
+'use client';
/* eslint-disable react-hooks/exhaustive-deps */
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import layananonlineDesa from '@/app/admin/(dashboard)/_state/inovasi/layanan-online-desa';
import colors from '@/con/colors';
-import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
+import {
+ Box,
+ Button,
+ Group,
+ Paper,
+ Stack,
+ Text,
+ TextInput,
+ Title,
+ Tooltip,
+} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
@@ -11,31 +21,34 @@ import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
function EditJenisLayanan() {
- const state = useProxy(layananonlineDesa.jenisLayanan)
- const router = useRouter()
- const params = useParams()
+ const state = useProxy(layananonlineDesa.jenisLayanan);
+ const router = useRouter();
+ const params = useParams();
+
const [formData, setFormData] = useState({
- nama: state.edit.form.nama,
- deskripsi: state.edit.form.deskripsi,
- })
+ nama: '',
+ deskripsi: '',
+ });
useEffect(() => {
const loadJenisLayanan = async () => {
const id = params?.id as string;
if (!id) return;
+
try {
const data = await state.edit.load(id);
if (data) {
setFormData({
- nama: data.nama,
- deskripsi: data.deskripsi,
+ nama: data.nama ?? '',
+ deskripsi: data.deskripsi ?? '',
});
}
} catch (error) {
- console.error("Error loading jenis layanan:", error);
- toast.error("Gagal memuat data jenis layanan");
+ console.error('Error loading jenis layanan:', error);
+ toast.error('Gagal memuat data jenis layanan');
}
};
+
loadJenisLayanan();
}, [params?.id]);
@@ -43,46 +56,89 @@ function EditJenisLayanan() {
try {
state.edit.form = {
...state.edit.form,
- nama: formData.nama,
- deskripsi: formData.deskripsi,
- }
- await state.edit.update()
- toast.success("Jenis layanan berhasil diperbarui!")
- router.push("/admin/inovasi/layanan-online-desa/jenis-layanan")
+ ...formData,
+ };
+
+ await state.edit.update();
+ toast.success('Jenis layanan berhasil diperbarui!');
+ router.push('/admin/inovasi/layanan-online-desa/jenis-layanan');
} catch (error) {
- console.error("Error updating jenis layanan:", error);
- toast.error("Terjadi kesalahan saat memperbarui jenis layanan");
+ console.error('Error updating jenis layanan:', error);
+ toast.error('Terjadi kesalahan saat memperbarui jenis layanan');
}
- }
+ };
return (
-
-
- router.back()}>
-
-
-
-
-
- Edit Jenis Layanan
+
+ {/* Header */}
+
+
+ router.back()}
+ p="xs"
+ radius="md"
+ >
+
+
+
+
+ Edit Jenis Layanan
+
+
+
+ {/* Form Container */}
+
+
+ {/* Input: Nama Jenis Layanan */}
{
- setFormData({ ...formData, nama: val.target.value });
- }}
- label={Nama Jenis Layanan }
- placeholder="masukkan nama jenis layanan"
+ onChange={(e) =>
+ setFormData((prev) => ({ ...prev, nama: e.target.value }))
+ }
+ required
/>
+
+ {/* Input: Deskripsi (Rich Text Editor) */}
- Deskripsi
+
+ Deskripsi
+
{
- setFormData({ ...formData, deskripsi: htmlContent });
- }}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({
+ ...prev,
+ deskripsi: htmlContent,
+ }))
+ }
/>
- Simpan
+
+ {/* Tombol Submit */}
+
+
+ Simpan
+
+
diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/page.tsx
index 427aaada..41990ab2 100644
--- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/page.tsx
@@ -79,6 +79,7 @@ function DetailJenisLayanan() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data?.deskripsi || '-' }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/create/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/create/page.tsx
index 50d76bc1..b1b5a107 100644
--- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/create/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/create/page.tsx
@@ -69,7 +69,7 @@ function CreateJenisLayanan() {
>
{
statePasar.create.form.nama = val.target.value;
}}
@@ -78,7 +78,7 @@ function CreateJenisLayanan() {
required
/>
{
statePasar.create.form.deskripsi = val.target.value;
}}
diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/[id]/page.tsx
index 6444952c..c64bb102 100644
--- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/[id]/page.tsx
@@ -10,7 +10,7 @@ import {
Stack,
TextInput,
Title,
- Tooltip
+ Tooltip,
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -25,9 +25,10 @@ function EditJenisPengaduan() {
const state = useProxy(layananonlineDesa.jenisPengaduan);
const [formData, setFormData] = useState({
- nama: "",
+ nama: '',
});
+ // Load data sekali aja
useEffect(() => {
const loadJenisPengaduan = async () => {
if (!id) return;
@@ -36,51 +37,58 @@ function EditJenisPengaduan() {
const data = await state.edit.load(id);
if (data) {
- // pastikan id-nya masuk ke state edit
- state.edit.id = id;
+ state.edit.id = id; // inject id ke state global (hanya sekali)
setFormData({
nama: data.nama || '',
});
}
} catch (error) {
- console.error("Error loading jenis pengaduan:", error);
- toast.error("Gagal memuat data jenis pengaduan");
+ console.error('Error loading jenis pengaduan:', error);
+ toast.error('Gagal memuat data jenis pengaduan');
}
};
loadJenisPengaduan();
}, [id]);
+ const handleChange = (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
const handleSubmit = async () => {
+ if (!formData.nama.trim()) {
+ toast.error('Nama jenis pengaduan tidak boleh kosong');
+ return;
+ }
+
+ // Update ke global state HANYA pas submit
+ state.edit.form = {
+ nama: formData.nama.trim(),
+ };
+
+ // Safety fallback kalau ID belum ada
+ if (!state.edit.id) {
+ state.edit.id = id;
+ }
+
try {
- if (!formData.nama.trim()) {
- toast.error('Nama jenis pengaduan tidak boleh kosong');
- return;
- }
-
- state.edit.form = {
- nama: formData.nama.trim(),
- };
-
- // Safety check tambahan: pastikan ID tidak kosong
- if (!state.edit.id) {
- state.edit.id = id; // fallback
- }
-
const success = await state.edit.update();
if (success) {
- router.push("/admin/inovasi/layanan-online-desa/jenis-pengaduan");
+ router.push('/admin/inovasi/layanan-online-desa/jenis-pengaduan');
}
} catch (error) {
- console.error("Error updating jenis pengaduan:", error);
- // toast akan ditampilkan dari fungsi update
+ console.error('Error updating jenis pengaduan:', error);
+ // toast ditangani di dalam state.update
}
};
return (
- {/* Header + tombol back */}
+ {/* Header */}
- {/* Card Form */}
+ {/* Form */}
setFormData({ ...formData, nama: e.target.value })}
+ onChange={(e) => handleChange('nama', e.target.value)}
label="Nama Jenis Pengaduan"
placeholder="Masukkan nama jenis pengaduan"
required
diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/create/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/create/page.tsx
index e3175ede..bb0ae5b9 100644
--- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/create/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/create/page.tsx
@@ -64,7 +64,7 @@ function CreateJenisPengaduan() {
(state.create.form.nama = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/pengaduan-masyarakat/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/pengaduan-masyarakat/[id]/page.tsx
index abcbb91f..a9a9ad3e 100644
--- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/pengaduan-masyarakat/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/pengaduan-masyarakat/[id]/page.tsx
@@ -102,7 +102,7 @@ function DetailPengaduanMasyarakat() {
Deskripsi Pengaduan
-
+
diff --git a/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/[id]/edit/page.tsx
index fc9b335f..5e2a29ff 100644
--- a/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/[id]/edit/page.tsx
@@ -41,6 +41,7 @@ function EditProgramKreatifDesa() {
icon: '',
});
+ // Load data hanya sekali berdasarkan params.id
useEffect(() => {
const loadProgramKreatif = async () => {
const id = params?.id as string;
@@ -50,17 +51,11 @@ function EditProgramKreatifDesa() {
const data = await stateProgramKreatif.update.load(id);
if (data) {
stateProgramKreatif.update.id = id;
- stateProgramKreatif.update.form = {
- name: data.name,
- slug: data.slug,
- deskripsi: data.deskripsi,
- icon: data.icon,
- };
setFormData({
- name: data.name,
- slug: data.slug,
- deskripsi: data.deskripsi,
- icon: data.icon,
+ name: data.name || '',
+ slug: data.slug || '',
+ deskripsi: data.deskripsi || '',
+ icon: data.icon || '',
});
}
} catch (error) {
@@ -72,10 +67,15 @@ function EditProgramKreatifDesa() {
loadProgramKreatif();
}, [params?.id]);
+ const handleChange =
+ (field: keyof FormProgramKreatif) =>
+ (value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
stateProgramKreatif.update.form = {
- ...stateProgramKreatif.update.form,
name: formData.name.trim(),
deskripsi: formData.deskripsi.trim(),
slug: formData.slug.trim(),
@@ -85,7 +85,7 @@ function EditProgramKreatifDesa() {
router.push('/admin/inovasi/program-kreatif-desa');
} catch (error) {
console.error('Error updating program kreatif:', error);
- toast.error('Gagal memuat data program kreatif');
+ toast.error('Gagal menyimpan program kreatif');
}
};
@@ -93,7 +93,12 @@ function EditProgramKreatifDesa() {
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -115,7 +120,7 @@ function EditProgramKreatifDesa() {
label="Nama Program Kreatif Desa"
placeholder="Masukkan nama program kreatif desa"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange('name')(e.target.value)}
required
/>
@@ -123,7 +128,7 @@ function EditProgramKreatifDesa() {
label="Deskripsi Singkat Program Kreatif Desa"
placeholder="Masukkan deskripsi singkat program kreatif desa"
value={formData.slug}
- onChange={(e) => setFormData({ ...formData, slug: e.target.value })}
+ onChange={(e) => handleChange('slug')(e.target.value)}
required
/>
@@ -133,10 +138,7 @@ function EditProgramKreatifDesa() {
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- stateProgramKreatif.update.form.deskripsi = htmlContent;
- }}
+ onChange={handleChange('deskripsi')}
/>
@@ -146,10 +148,7 @@ function EditProgramKreatifDesa() {
{
- setFormData((prev) => ({ ...prev, icon: value }));
- stateProgramKreatif.update.form.icon = value;
- }}
+ onChange={handleChange('icon')}
/>
diff --git a/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/[id]/page.tsx
index 94dd1015..e402bf15 100644
--- a/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/[id]/page.tsx
@@ -86,12 +86,12 @@ function DetailProgramKreatifDesa() {
Deskripsi Singkat
- {data?.slug || '-'}
+ {data?.slug || '-'}
Deskripsi
-
+
diff --git a/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/create/page.tsx b/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/create/page.tsx
index 883529be..b5969bf7 100644
--- a/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/create/page.tsx
+++ b/src/app/admin/(dashboard)/inovasi/program-kreatif-desa/create/page.tsx
@@ -64,7 +64,7 @@ function CreateProgramKreatifDesa() {
Nama Program Kreatif Desa}
placeholder="Masukkan nama program kreatif desa"
- value={stateCreate.create.form.name || ""}
+ defaultValue={stateCreate.create.form.name || ""}
onChange={(e) => (stateCreate.create.form.name = e.currentTarget.value)}
required
/>
@@ -81,7 +81,7 @@ function CreateProgramKreatifDesa() {
Deskripsi Singkat Program Kreatif Desa}
placeholder="Masukkan deskripsi singkat program kreatif desa"
- value={stateCreate.create.form.slug || ""}
+ defaultValue={stateCreate.create.form.slug || ""}
onChange={(e) => (stateCreate.create.form.slug = e.currentTarget.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/edit/page.tsx
index d11e7981..c1a7d88e 100644
--- a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/edit/page.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react-hooks/exhaustive-deps */
"use client";
import {
@@ -40,12 +39,12 @@ function EditKeamananLingkungan() {
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
const [formData, setFormData] = useState({
- name: keamananState.edit.form.name || "",
- deskripsi: keamananState.edit.form.deskripsi || "",
- imageId: keamananState.edit.form.imageId || "",
+ name: "",
+ deskripsi: "",
+ imageId: "",
});
- // Load data by id
+ // Load data sekali pas mount
useEffect(() => {
const loadData = async () => {
const id = params?.id as string;
@@ -71,16 +70,16 @@ function EditKeamananLingkungan() {
};
loadData();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [params?.id]);
+ const handleChange = (field: string, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
- keamananState.edit.form = {
- ...keamananState.edit.form,
- name: formData.name,
- deskripsi: formData.deskripsi,
- imageId: formData.imageId,
- };
+ let imageId = formData.imageId;
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({
@@ -93,9 +92,17 @@ function EditKeamananLingkungan() {
return toast.error("Gagal upload gambar");
}
- keamananState.edit.form.imageId = uploaded.id;
+ imageId = uploaded.id;
}
+ // update global state hanya sekali pas submit
+ keamananState.edit.form = {
+ ...keamananState.edit.form,
+ name: formData.name,
+ deskripsi: formData.deskripsi,
+ imageId,
+ };
+
await keamananState.edit.update();
toast.success("Keamanan Lingkungan berhasil diperbarui!");
router.push("/admin/keamanan/keamanan-lingkungan-pecalang-patwal");
@@ -186,7 +193,7 @@ function EditKeamananLingkungan() {
{previewImage ? (
-
+
) : (
@@ -195,9 +202,7 @@ function EditKeamananLingkungan() {
- setFormData({ ...formData, name: e.target.value })
- }
+ onChange={(e) => handleChange("name", e.target.value)}
label="Judul Keamanan Lingkungan"
placeholder="Masukkan judul"
required
@@ -209,10 +214,7 @@ function EditKeamananLingkungan() {
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- keamananState.edit.form.deskripsi = htmlContent;
- }}
+ onChange={(htmlContent) => handleChange("deskripsi", htmlContent)}
/>
diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/page.tsx
index 50f49d8c..1ce73db9 100644
--- a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/page.tsx
@@ -88,7 +88,7 @@ function DetailKeamananLingkungan() {
Deskripsi
-
+
{/* Aksi */}
diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/create/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/create/page.tsx
index ea5c2604..4ff05394 100644
--- a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/create/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/create/page.tsx
@@ -172,7 +172,7 @@ function CreateKeamananLingkungan() {
{/* Input Nama */}
{
keamananState.create.form.name = val.target.value;
}}
diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx
index f4bef5c6..8fde1a33 100644
--- a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx
@@ -107,12 +107,16 @@ function ListKeamananLingkungan({ search }: { search: string }) {
filteredData.map((item) => (
-
- {item.name}
-
+
+
+ {item.name}
+
+
-
+
+
+
{
const loadKontakDarurat = async () => {
const id = params?.id as string;
@@ -55,14 +56,23 @@ function EditKontakItem() {
loadKontakDarurat();
}, [params?.id]);
+ const handleChange = (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
const handleSubmit = async () => {
try {
+ // Update global state sekali pas submit
kontakState.update.form = {
...kontakState.update.form,
nama: formData.name,
nomorTelepon: formData.nomorTelepon,
icon: formData.icon,
};
+
await kontakState.update.update();
toast.success('Kontak Darurat berhasil diperbarui!');
router.push('/admin/keamanan/kontak-darurat/kontak-darurat-item');
@@ -100,7 +110,7 @@ function EditKontakItem() {
label="Nama Kontak"
placeholder="Masukkan nama kontak"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange('name', e.target.value)}
required
/>
@@ -108,7 +118,7 @@ function EditKontakItem() {
label="Nomor Telepon"
placeholder="Masukkan nomor telepon"
value={formData.nomorTelepon}
- onChange={(e) => setFormData({ ...formData, nomorTelepon: e.target.value })}
+ onChange={(e) => handleChange('nomorTelepon', e.target.value)}
required
/>
@@ -118,10 +128,7 @@ function EditKontakItem() {
{
- setFormData((prev) => ({ ...prev, icon: value }));
- kontakState.update.form.icon = value;
- }}
+ onChange={(value) => handleChange('icon', value)}
/>
diff --git a/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-item/create/page.tsx b/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-item/create/page.tsx
index f318bce5..3b962470 100644
--- a/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-item/create/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-item/create/page.tsx
@@ -65,7 +65,7 @@ function CreateKontakItem() {
{/* Input Nama Kategori */}
{
kontakState.create.form.nama = val.target.value;
}}
@@ -77,7 +77,7 @@ function CreateKontakItem() {
Nomor Telepon Kontak}
placeholder="Masukkan nomor telepon"
- value={kontakState.create.form.nomorTelepon}
+ defaultValue={kontakState.create.form.nomorTelepon}
onChange={(val) => {
kontakState.create.form.nomorTelepon = val.target.value;
}}
diff --git a/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-keamanan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-keamanan/[id]/edit/page.tsx
index 6057918b..7492194c 100644
--- a/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-keamanan/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-keamanan/[id]/edit/page.tsx
@@ -15,41 +15,42 @@ import {
Text,
TextInput,
Title,
- Tooltip
+ Tooltip,
} from "@mantine/core";
-import {
- IconArrowBack
-} from "@tabler/icons-react";
+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 EditKontakDaruratKeamanan() {
- const [isLoading, setIsLoading] = useState(true);
const router = useRouter();
- const kontakState = useProxy(kontakDarurat.kontakDaruratKeamananState);
const params = useParams();
+ const kontakState = useProxy(kontakDarurat.kontakDaruratKeamananState);
+
+ const [isLoading, setIsLoading] = useState(true);
+ // Remove the dependency on data in the initial state
const [formData, setFormData] = useState({
- name: kontakState.update.form.nama || "",
- icon: kontakState.update.form.icon || "",
- kategoriId: kontakState.update.form.kategoriId || [],
+ name: "",
+ icon: "" as IconKey | "",
+ kategoriId: [] as string[], // Initialize as empty array
});
- // Load data
+ // Load data dari backend
useEffect(() => {
const loadData = async () => {
try {
setIsLoading(true);
await kontakDarurat.kontakDaruratItem.findMany.load();
+
const id = params?.id as string;
if (id) {
const data = await kontakState.update.load(id);
if (data) {
setFormData({
name: data.nama || "",
- icon: data.icon || "",
- kategoriId: data.kategoriId || [],
+ icon: (data.icon as IconKey) || "",
+ kategoriId: Array.isArray(data.kategoriId) ? data.kategoriId : [],
});
}
}
@@ -63,6 +64,7 @@ function EditKontakDaruratKeamanan() {
loadData();
}, [params?.id]);
+
// Handle submit
const handleSubmit = async () => {
try {
@@ -72,6 +74,7 @@ function EditKontakDaruratKeamanan() {
icon: formData.icon,
kategoriId: formData.kategoriId,
};
+
await kontakState.update.update();
toast.success("Kontak Darurat berhasil diperbarui!");
router.push("/admin/keamanan/kontak-darurat");
@@ -110,43 +113,54 @@ function EditKontakDaruratKeamanan() {
style={{ border: "1px solid #e0e0e0" }}
>
- {/* Nama kategori */}
+ {/* Nama Kontak */}
setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) =>
+ setFormData((prev) => ({ ...prev, name: e.target.value }))
+ }
label="Nama Kontak Darurat"
placeholder="Masukkan nama kontak darurat"
required
/>
+ {/* MultiSelect */}
setFormData({ ...formData, kategoriId: val })}
+ onChange={(val) =>
+ setFormData((prev) => ({ ...prev, kategoriId: val }))
+ }
label={Kontak Item }
placeholder={isLoading ? "Memuat data..." : "Pilih kontak item"}
data={
Array.isArray(kontakDarurat.kontakDaruratItem.findMany.data)
? kontakDarurat.kontakDaruratItem.findMany.data.map((v) => ({
value: v.id,
- label: v.nama
+ label: v.nama,
}))
: []
}
clearable
searchable
required
- error={!formData.kategoriId.length ? "Pilih minimal satu kategori" : undefined}
+ error={
+ !formData.kategoriId.length
+ ? "Pilih minimal satu kategori"
+ : undefined
+ }
disabled={isLoading}
/>
+ {/* Icon Select */}
- Ikon Program Kreatif Desa
+
+ Ikon Program Kreatif Desa
+
{
- setFormData((prev) => ({ ...prev, icon: value }));
- kontakState.update.form.icon = value;
- }}
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, icon: value }))
+ }
/>
diff --git a/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-keamanan/create/page.tsx b/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-keamanan/create/page.tsx
index 4eb42efb..afdbd448 100644
--- a/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-keamanan/create/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/kontak-darurat/kontak-darurat-keamanan/create/page.tsx
@@ -72,7 +72,7 @@ function CreateKontakDaruratKeamanan() {
{/* Input Nama Kategori */}
{
kontakState.create.form.nama = val.target.value;
}}
diff --git a/src/app/admin/(dashboard)/keamanan/laporan-publik/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/laporan-publik/[id]/edit/page.tsx
index 5d2c2789..f52bd6c1 100644
--- a/src/app/admin/(dashboard)/keamanan/laporan-publik/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/laporan-publik/[id]/edit/page.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import laporanPublikState from '@/app/admin/(dashboard)/_state/keamanan/laporan-publik';
@@ -30,51 +28,59 @@ function EditLaporanPublik() {
const router = useRouter();
const params = useParams();
- const [formData, setFormData] = useState({
- judul: stateLaporan.edit.form.judul || '',
- lokasi: stateLaporan.edit.form.lokasi || '',
- tanggalWaktu: stateLaporan.edit.form.tanggalWaktu || '',
- status: stateLaporan.edit.form.status || '',
- penanganan: stateLaporan.edit.form.penanganan || '',
- kronologi: stateLaporan.edit.form.kronologi || '',
+ const [formData, setFormData] = useState<{
+ judul: string;
+ lokasi: string;
+ tanggalWaktu: string;
+ status: Status;
+ penanganan: string;
+ kronologi: string;
+ }>({
+ judul: '',
+ lokasi: '',
+ tanggalWaktu: '',
+ status: 'Proses', // Default status
+ penanganan: '',
+ kronologi: '',
});
useEffect(() => {
const loadLaporanPublik = async () => {
const id = params?.id as string;
if (!id) return;
-
+
try {
const data = await stateLaporan.edit.load(id);
if (data) {
- setFormData({
- judul: data.judul || '',
- lokasi: data.lokasi || '',
- tanggalWaktu: data.tanggalWaktu || '',
- status: data.status || '',
- penanganan: data.penanganan?.map((p: any) => p.deskripsi)[0] || '',
- kronologi: data.kronologi || '',
- });
+ setFormData((prev) => ({
+ ...prev,
+ judul: data.judul ?? prev.judul,
+ lokasi: data.lokasi ?? prev.lokasi,
+ tanggalWaktu: data.tanggalWaktu ?? prev.tanggalWaktu,
+ status: (data.status as Status) ?? prev.status,
+ penanganan: data.penanganan?.[0]?.deskripsi ?? prev.penanganan,
+ kronologi: data.kronologi ?? prev.kronologi,
+ }));
}
} catch (error) {
- console.error('Error loading laporan publik:', error);
+ console.error("Error loading laporan publik:", error);
toast.error("Gagal mengambil data laporan publik");
}
};
-
+
loadLaporanPublik();
- }, [params?.id]);
+ }, [params?.id, stateLaporan.edit]);
+
+
+ const handleChange = (field: string, value: string | Status) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
const handleSubmit = async () => {
try {
stateLaporan.edit.form = {
...stateLaporan.edit.form,
- judul: formData.judul,
- lokasi: formData.lokasi,
- tanggalWaktu: formData.tanggalWaktu,
- status: formData.status,
- penanganan: formData.penanganan,
- kronologi: formData.kronologi,
+ ...formData,
};
await stateLaporan.edit.update();
@@ -112,7 +118,7 @@ function EditLaporanPublik() {
setFormData({ ...formData, judul: e.target.value })}
+ onChange={(e) => handleChange('judul', e.target.value)}
label={Judul Laporan Publik }
placeholder="Masukkan judul laporan publik"
required
@@ -120,7 +126,7 @@ function EditLaporanPublik() {
setFormData({ ...formData, lokasi: e.target.value })}
+ onChange={(e) => handleChange('lokasi', e.target.value)}
label={Lokasi Laporan Publik }
placeholder="Masukkan lokasi laporan publik"
required
@@ -129,15 +135,20 @@ function EditLaporanPublik() {
- setFormData({ ...formData, tanggalWaktu: val ? val.toString() : '' })
- }
+ onChange={(value: string | null) => {
+ if (value) {
+ const date = new Date(value);
+ handleChange('tanggalWaktu', date.toISOString());
+ } else {
+ handleChange('tanggalWaktu', '');
+ }
+ }}
required
/>
setFormData({ ...formData, status: e as Status })}
+ onChange={(val) => handleChange('status', val as Status)}
label={Status Laporan Publik }
placeholder="Pilih status laporan publik"
data={[
@@ -152,10 +163,7 @@ function EditLaporanPublik() {
Kronologi Laporan Publik
{
- setFormData((prev) => ({ ...prev, kronologi: htmlContent }));
- stateLaporan.edit.form.kronologi = htmlContent;
- }}
+ onChange={(htmlContent) => handleChange('kronologi', htmlContent)}
/>
@@ -163,10 +171,7 @@ function EditLaporanPublik() {
Penanganan Laporan Publik
{
- setFormData((prev) => ({ ...prev, penanganan: htmlContent }));
- stateLaporan.edit.form.penanganan = htmlContent;
- }}
+ onChange={(htmlContent) => handleChange('penanganan', htmlContent)}
/>
diff --git a/src/app/admin/(dashboard)/keamanan/laporan-publik/[id]/page.tsx b/src/app/admin/(dashboard)/keamanan/laporan-publik/[id]/page.tsx
index 396c59df..b5f83ee9 100644
--- a/src/app/admin/(dashboard)/keamanan/laporan-publik/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/laporan-publik/[id]/page.tsx
@@ -77,7 +77,7 @@ function DetailLaporanPublik() {
Judul Laporan Publik
- {data.judul || '-'}
+ {data.judul || '-'}
@@ -91,7 +91,7 @@ function DetailLaporanPublik() {
Lokasi
- {data.lokasi || '-'}
+ {data.lokasi || '-'}
@@ -121,7 +121,7 @@ function DetailLaporanPublik() {
Kronologi
-
+
@@ -133,6 +133,7 @@ function DetailLaporanPublik() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: item.deskripsi || '-' }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
))
diff --git a/src/app/admin/(dashboard)/keamanan/laporan-publik/create/page.tsx b/src/app/admin/(dashboard)/keamanan/laporan-publik/create/page.tsx
index 7df96049..994af691 100644
--- a/src/app/admin/(dashboard)/keamanan/laporan-publik/create/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/laporan-publik/create/page.tsx
@@ -63,7 +63,7 @@ function CreateLaporanPublik() {
>
(stateLaporan.create.form.judul = e.target.value)}
label={Judul Laporan Publik }
placeholder="Masukkan judul laporan publik"
@@ -71,7 +71,7 @@ function CreateLaporanPublik() {
/>
(stateLaporan.create.form.lokasi = e.target.value)}
label={Lokasi Laporan Publik }
placeholder="Masukkan lokasi laporan publik"
@@ -91,7 +91,7 @@ function CreateLaporanPublik() {
/>
(stateLaporan.create.form.kronologi = e.target.value)}
label={Kronologi Laporan Publik }
placeholder="Masukkan kronologi laporan publik"
diff --git a/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/edit/page.tsx
index 42ab7cc9..de2e39aa 100644
--- a/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/edit/page.tsx
@@ -10,9 +10,10 @@ import {
Group,
Paper,
Stack,
+ Text,
TextInput,
Title,
- Tooltip
+ Tooltip,
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -32,6 +33,7 @@ function EditPencegahanKriminalitas() {
linkVideo: '',
});
+ // load data hanya sekali pas id berubah
useEffect(() => {
const loadKriminalitas = async () => {
const id = params?.id as string;
@@ -41,10 +43,10 @@ function EditPencegahanKriminalitas() {
const data = await kriminalitasState.update.load(id);
if (data) {
setFormData({
- judul: data.judul || '',
- deskripsi: data.deskripsi || '',
- deskripsiSingkat: data.deskripsiSingkat || '',
- linkVideo: data.linkVideo || '',
+ judul: data.judul ?? '',
+ deskripsi: data.deskripsi ?? '',
+ deskripsiSingkat: data.deskripsiSingkat ?? '',
+ linkVideo: data.linkVideo ?? '',
});
}
} catch (error) {
@@ -58,6 +60,12 @@ function EditPencegahanKriminalitas() {
const embedLink = convertYoutubeUrlToEmbed(formData.linkVideo);
+ const handleChange =
+ (field: keyof typeof formData) =>
+ (e: React.ChangeEvent) => {
+ setFormData((prev) => ({ ...prev, [field]: e.target.value }));
+ };
+
const handleSubmit = async () => {
const converted = convertYoutubeUrlToEmbed(formData.linkVideo);
if (!converted) {
@@ -66,16 +74,13 @@ function EditPencegahanKriminalitas() {
}
try {
- // Update the form data first
+ // update global state saat submit
kriminalitasState.update.form = {
- ...kriminalitasState.update.form,
judul: formData.judul,
deskripsi: formData.deskripsi,
deskripsiSingkat: formData.deskripsiSingkat,
linkVideo: formData.linkVideo,
};
-
- // Set the ID and then call update
kriminalitasState.update.id = params?.id as string;
await kriminalitasState.update.update();
@@ -120,19 +125,21 @@ function EditPencegahanKriminalitas() {
label="Judul"
placeholder="Masukkan judul"
value={formData.judul}
- onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
+ onChange={handleChange('judul')}
required
/>
-
- setFormData({ ...formData, deskripsiSingkat: e.target.value })
- }
- required
- />
+
+
+ Deskripsi
+
+
+ setFormData((prev) => ({ ...prev, deskripsiSingkat: val }))
+ }
+ />
+
@@ -141,7 +148,7 @@ function EditPencegahanKriminalitas() {
- setFormData({ ...formData, deskripsi: val })
+ setFormData((prev) => ({ ...prev, deskripsi: val }))
}
/>
@@ -151,9 +158,7 @@ function EditPencegahanKriminalitas() {
label="Link Video YouTube"
placeholder="https://www.youtube.com/watch?v=abc123"
value={formData.linkVideo}
- onChange={(e) =>
- setFormData({ ...formData, linkVideo: e.currentTarget.value })
- }
+ onChange={handleChange('linkVideo')}
required
/>
{embedLink && (
diff --git a/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/page.tsx b/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/page.tsx
index 72371193..46ffedf0 100644
--- a/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/page.tsx
@@ -75,7 +75,7 @@ function DetailPencegahanKriminalitas() {
Deskripsi Singkat
{data?.deskripsiSingkat ? (
-
+
) : (
Tidak ada deskripsi singkat
)}
@@ -84,7 +84,7 @@ function DetailPencegahanKriminalitas() {
Deskripsi
{data?.deskripsi ? (
-
+
) : (
Tidak ada deskripsi
)}
diff --git a/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/create/page.tsx b/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/create/page.tsx
index 69f06504..89b189fc 100644
--- a/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/create/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/create/page.tsx
@@ -82,7 +82,7 @@ function CreatePencegahanKriminalitas() {
{
kriminalitasState.create.form.judul = e.currentTarget.value;
}}
@@ -90,15 +90,17 @@ function CreatePencegahanKriminalitas() {
/>
{/* Deskripsi Singkat */}
- {
- kriminalitasState.create.form.deskripsiSingkat = e.currentTarget.value;
- }}
- required
- />
+
+
+ Deskripsi Singkat
+
+ {
+ kriminalitasState.create.form.deskripsiSingkat = val;
+ }}
+ />
+
{/* Deskripsi Panjang */}
@@ -117,7 +119,7 @@ function CreatePencegahanKriminalitas() {
setLink(e.currentTarget.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/page.tsx b/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/page.tsx
index 45db4790..be9eefc9 100644
--- a/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/page.tsx
@@ -105,9 +105,11 @@ function ListPencegahanKriminalitas({ search }: { search: string }) {
data.map((item) => (
-
+
+
{item.judul}
+
diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx
index 4605c3be..bf826e3d 100644
--- a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx
@@ -52,50 +52,36 @@ function EditPolsekTerdekat() {
layananPolsekId: "",
});
- useEffect(() => {
- const loadPolsekTerdekat = async () => {
- const id = params?.id as string;
- if (!id) return;
-
- try {
- const data = await polsekState.edit.load(id);
- if (data) {
- setFormData({
- nama: data.nama || "",
- jarakKeDesa: data.jarakKeDesa || "",
- alamat: data.alamat || "",
- nomorTelepon: data.nomorTelepon || "",
- jamOperasional: data.jamOperasional || "",
- embedMapUrl: data.embedMapUrl || "",
- namaTempatMaps: data.namaTempatMaps || "",
- alamatMaps: data.alamatMaps || "",
- linkPetunjukArah: data.linkPetunjukArah || "",
- layananPolsekId: data.layananPolsekId || "",
- });
+ // load data untuk form edit
+ useEffect(() => {
+ const loadPolsekTerdekat = async () => {
+ const id = params?.id as string;
+ if (!id) return;
+
+ try {
+ const data = await polsekState.edit.load(id);
+ if (data) {
+ setFormData({
+ nama: data.nama || "",
+ jarakKeDesa: data.jarakKeDesa || "",
+ alamat: data.alamat || "",
+ nomorTelepon: data.nomorTelepon || "",
+ jamOperasional: data.jamOperasional || "",
+ embedMapUrl: data.embedMapUrl || "",
+ namaTempatMaps: data.namaTempatMaps || "",
+ alamatMaps: data.alamatMaps || "",
+ linkPetunjukArah: data.linkPetunjukArah || "",
+ layananPolsekId: data.layananPolsekId || "",
+ });
+ }
+ } catch (error) {
+ console.error("Error loading polsek terdekat:", error);
+ toast.error("Gagal memuat data polsek terdekat");
}
- } catch (error) {
- console.error("Error loading polsek terdekat:", error);
- toast.error("Gagal memuat data polsek terdekat");
- }
- };
-
- loadPolsekTerdekat();
- }, [params?.id]);
-
- const handleSubmit = async () => {
- try {
- polsekState.edit.form = {
- ...polsekState.edit.form,
- ...formData,
};
- await polsekState.edit.update();
- toast.success("Polsek terdekat berhasil diperbarui!");
- router.push("/admin/keamanan/polsek-terdekat");
- } catch (error) {
- console.error("Error updating polsek terdekat:", error);
- toast.error("Gagal memperbarui data polsek terdekat");
- }
- };
+
+ loadPolsekTerdekat();
+ }, [params?.id]);
const fetchLayanan = async () => {
try {
@@ -198,6 +184,22 @@ function EditPolsekTerdekat() {
fetchLayanan();
}, []);
+ const handleChange = (field: string, value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
+ const handleSubmit = async () => {
+ try {
+ polsekState.edit.form = { ...formData }; // update global state hanya di sini
+ await polsekState.edit.update();
+ toast.success("Polsek terdekat berhasil diperbarui!");
+ router.push("/admin/keamanan/polsek-terdekat");
+ } catch (error) {
+ console.error("Error updating polsek terdekat:", error);
+ toast.error("Gagal memperbarui data polsek terdekat");
+ }
+ };
+
return (
{/* Modal Tambah */}
@@ -211,7 +213,7 @@ function EditPolsekTerdekat() {
setNamaLayananBaru(e.currentTarget.value)}
/>
Simpan
@@ -229,7 +231,7 @@ function EditPolsekTerdekat() {
setNamaLayananUpdate(e.currentTarget.value)}
/>
- setFormData({ ...formData, nama: val.target.value })
- }
+ onChange={(e) => handleChange("nama", e.currentTarget.value)}
label="Nama Polsek Terdekat"
placeholder="Masukkan nama Polsek Terdekat"
required
/>
- setFormData({ ...formData, jarakKeDesa: val.target.value })
- }
+ onChange={(e) => handleChange("jarakKeDesa", e.currentTarget.value)}
label="Jarak Polsek Terdekat"
- placeholder="Masukkan jarak Polsek Terdekat"
/>
- setFormData({ ...formData, alamat: val.target.value })
- }
+ onChange={(e) => handleChange("alamat", e.currentTarget.value)}
label="Alamat Polsek Terdekat"
- placeholder="Masukkan alamat Polsek Terdekat"
/>
- setFormData({ ...formData, nomorTelepon: val.target.value })
- }
+ onChange={(e) => handleChange("nomorTelepon", e.currentTarget.value)}
label="Nomor Telepon"
- placeholder="Masukkan nomor telepon Polsek Terdekat"
/>
- setFormData({ ...formData, jamOperasional: val.target.value })
- }
+ onChange={(e) => handleChange("jamOperasional", e.currentTarget.value)}
label="Jam Operasional"
- placeholder="Masukkan jam operasional Polsek Terdekat"
/>
- setFormData({ ...formData, embedMapUrl: val.target.value })
- }
+ onChange={(e) => handleChange("embedMapUrl", e.currentTarget.value)}
label="Embed Map URL"
- placeholder="Masukkan embed map url"
/>
- setFormData({ ...formData, namaTempatMaps: val.target.value })
- }
+ onChange={(e) => handleChange("namaTempatMaps", e.currentTarget.value)}
label="Nama Tempat Maps"
- placeholder="Masukkan nama tempat di maps"
/>
- setFormData({ ...formData, alamatMaps: val.target.value })
- }
+ onChange={(e) => handleChange("alamatMaps", e.currentTarget.value)}
label="Alamat Maps"
- placeholder="Masukkan alamat di maps"
/>
- setFormData({ ...formData, linkPetunjukArah: val.target.value })
- }
+ onChange={(e) => handleChange("linkPetunjukArah", e.currentTarget.value)}
label="Link Petunjuk Arah"
- placeholder="Masukkan link petunjuk arah"
/>
- {/* Dropdown Layanan */}
{
- polsekState.create.form.layananPolsekId = val || "";
- }}
+ value={formData.layananPolsekId}
+ onChange={(val) => handleChange("layananPolsekId", val || "")}
/>
Alamat Polsek
- {data?.alamat || "-"}
+ {data?.alamat || "-"}
{/* Nomor */}
@@ -125,19 +125,19 @@ function DetailPolsekTerdekat() {
{/* Nama Tempat Maps */}
Nama Tempat Maps
- {data?.namaTempatMaps || "-"}
+ {data?.namaTempatMaps || "-"}
{/* Alamat Maps */}
Alamat Maps
- {data?.alamatMaps || "-"}
+ {data?.alamatMaps || "-"}
{/* Link Petunjuk Arah */}
- Link Petunjuk Arah
-
+ Link Petunjuk Arah
+
setNamaLayananBaru(e.currentTarget.value)}
/>
Simpan
@@ -148,59 +148,59 @@ function CreatePolsekTerdekat() {
>
(polsekState.create.form.nama = val.target.value)}
label={Nama Polsek Terdekat }
placeholder="Masukkan nama Polsek Terdekat"
required
/>
(polsekState.create.form.jarakKeDesa = val.target.value)}
label={Jarak Polsek Terdekat }
placeholder="Masukkan jarak Polsek Terdekat"
required
/>
(polsekState.create.form.alamat = val.target.value)}
label={Alamat Polsek Terdekat }
placeholder="Masukkan alamat Polsek Terdekat"
required
/>
(polsekState.create.form.nomorTelepon = val.target.value)}
label={Nomor Telepon Polsek Terdekat }
placeholder="Masukkan nomor telepon Polsek Terdekat"
required
/>
(polsekState.create.form.jamOperasional = val.target.value)}
label={Jam Operasional Polsek Terdekat }
placeholder="Masukkan jam operasional Polsek Terdekat"
/>
(polsekState.create.form.embedMapUrl = val.target.value)}
label={Embed Map URL }
placeholder="Masukkan embed map url"
/>
(polsekState.create.form.namaTempatMaps = val.target.value)}
label={Nama Tempat Maps }
placeholder="Masukkan nama tempat maps"
/>
(polsekState.create.form.alamatMaps = val.target.value)}
label={Alamat Maps }
placeholder="Masukkan alamat maps"
/>
(polsekState.create.form.linkPetunjukArah = val.target.value)}
label={Link Petunjuk Arah }
placeholder="Masukkan link petunjuk arah"
diff --git a/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/edit/page.tsx
index 270aec6c..2de3db48 100644
--- a/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/edit/page.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react-hooks/exhaustive-deps */
"use client";
import {
@@ -39,9 +38,9 @@ function EditTipsKeamanan() {
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
const [formData, setFormData] = useState({
- judul: keamananState.update.form.judul || "",
- deskripsi: keamananState.update.form.deskripsi || "",
- imageId: keamananState.update.form.imageId || "",
+ judul: "",
+ deskripsi: "",
+ imageId: "",
});
// Load data saat pertama kali
@@ -70,14 +69,12 @@ function EditTipsKeamanan() {
};
loadData();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [params?.id]);
const handleSubmit = async () => {
try {
- keamananState.update.form = {
- ...keamananState.update.form,
- ...formData,
- };
+ let imageId = formData.imageId;
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({
@@ -87,10 +84,14 @@ function EditTipsKeamanan() {
const uploaded = res.data?.data;
if (!uploaded?.id) return toast.error("Gagal upload gambar");
-
- keamananState.update.form.imageId = uploaded.id;
+ imageId = uploaded.id;
}
+ keamananState.update.form = {
+ ...formData,
+ imageId,
+ };
+
await keamananState.update.update();
toast.success("Tips Keamanan berhasil diperbarui!");
router.push("/admin/keamanan/tips-keamanan");
@@ -105,7 +106,12 @@ function EditTipsKeamanan() {
{/* Header */}
- router.back()} p="xs" radius="md">
+ router.back()}
+ p="xs"
+ radius="md"
+ >
@@ -137,7 +143,9 @@ function EditTipsKeamanan() {
setPreviewImage(URL.createObjectURL(selectedFile));
}
}}
- onReject={() => toast.error("File tidak valid, gunakan format gambar")}
+ onReject={() =>
+ toast.error("File tidak valid, gunakan format gambar")
+ }
maxSize={5 * 1024 ** 2}
accept={{ "image/*": [] }}
radius="md"
@@ -145,7 +153,11 @@ function EditTipsKeamanan() {
>
-
+
@@ -200,7 +212,9 @@ function EditTipsKeamanan() {
label="Nama Tips Keamanan"
placeholder="Masukkan nama tips keamanan"
value={formData.judul}
- onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
+ onChange={(e) =>
+ setFormData((prev) => ({ ...prev, judul: e.target.value }))
+ }
required
/>
@@ -211,10 +225,9 @@ function EditTipsKeamanan() {
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- keamananState.update.form.deskripsi = htmlContent;
- }}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
+ }
/>
diff --git a/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/page.tsx b/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/page.tsx
index 0b8ec263..61bb32c9 100644
--- a/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/page.tsx
@@ -68,7 +68,7 @@ function DetailTipsKeamanan() {
Nama Tips Keamanan
- {data.judul || '-'}
+ {data.judul || '-'}
@@ -76,6 +76,7 @@ function DetailTipsKeamanan() {
diff --git a/src/app/admin/(dashboard)/keamanan/tips-keamanan/create/page.tsx b/src/app/admin/(dashboard)/keamanan/tips-keamanan/create/page.tsx
index c3227392..6f82636a 100644
--- a/src/app/admin/(dashboard)/keamanan/tips-keamanan/create/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/tips-keamanan/create/page.tsx
@@ -138,7 +138,7 @@ function CreateKeamananLingkungan() {
(stateKeamanan.create.form.judul = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/keamanan/tips-keamanan/page.tsx b/src/app/admin/(dashboard)/keamanan/tips-keamanan/page.tsx
index c01ec070..7305d94c 100644
--- a/src/app/admin/(dashboard)/keamanan/tips-keamanan/page.tsx
+++ b/src/app/admin/(dashboard)/keamanan/tips-keamanan/page.tsx
@@ -99,12 +99,16 @@ function ListTipsKeamanan({ search }: { search: string }) {
filteredData.map((item) => (
-
- {item.judul}
-
+
+
+ {item.judul}
+
+
-
+
+
+
(null);
const [file, setFile] = useState(null);
const [formData, setFormData] = useState({
- title: stateArtikelKesehatan.edit.form.title,
- content: stateArtikelKesehatan.edit.form.content,
- imageId: stateArtikelKesehatan.edit.form.imageId,
- introduction: { content: stateArtikelKesehatan.edit.form.introduction?.content },
- symptom: {
- title: stateArtikelKesehatan.edit.form.symptom?.title,
- content: stateArtikelKesehatan.edit.form.symptom?.content
- },
- prevention: {
- title: stateArtikelKesehatan.edit.form.prevention?.title,
- content: stateArtikelKesehatan.edit.form.prevention?.content
- },
- firstAid: {
- title: stateArtikelKesehatan.edit.form.firstAid?.title,
- content: stateArtikelKesehatan.edit.form.firstAid?.content
- },
- mythVsFact: {
- title: stateArtikelKesehatan.edit.form.mythVsFact?.title,
- mitos: stateArtikelKesehatan.edit.form.mythVsFact?.mitos,
- fakta: stateArtikelKesehatan.edit.form.mythVsFact?.fakta
- },
- doctorSign: {
- content: stateArtikelKesehatan.edit.form.doctorSign?.content
- }
+ title: '',
+ content: '',
+ imageId: '',
+ introduction: { content: '' },
+ symptom: { title: '', content: '' },
+ prevention: { title: '', content: '' },
+ firstAid: { title: '', content: '' },
+ mythVsFact: { title: '', mitos: '', fakta: '' },
+ doctorSign: { content: '' },
});
+ // Load data artikel
useEffect(() => {
const loadArtikelKesehatan = async () => {
const id = params?.id as string;
@@ -78,74 +64,89 @@ function EditArtikelKesehatan() {
try {
await stateArtikelKesehatan.edit.load(id);
const { form } = stateArtikelKesehatan.edit;
- if (form) {
- setFormData({
- title: form.title,
- content: form.content,
- introduction: { content: form.introduction?.content || '' },
- imageId: form.imageId,
- symptom: {
- title: form.symptom?.title || '',
- content: form.symptom?.content || ''
- },
- prevention: {
- title: form.prevention?.title || '',
- content: form.prevention?.content || ''
- },
- firstAid: {
- title: form.firstAid?.title || '',
- content: form.firstAid?.content || ''
- },
- mythVsFact: {
- title: form.mythVsFact?.title || '',
- mitos: form.mythVsFact?.mitos || '',
- fakta: form.mythVsFact?.fakta || ''
- },
- doctorSign: {
- content: form.doctorSign?.content || ''
- }
- });
+ if (!form) return;
- if (form?.imageId) {
- setPreviewImage(`${process.env.NEXT_PUBLIC_API_URL}/file/${form.imageId}`);
- }
+ setFormData({
+ title: form.title || '',
+ content: form.content || '',
+ imageId: form.imageId || '',
+ introduction: { content: form.introduction?.content || '' },
+ symptom: { title: form.symptom?.title || '', content: form.symptom?.content || '' },
+ prevention: { title: form.prevention?.title || '', content: form.prevention?.content || '' },
+ firstAid: { title: form.firstAid?.title || '', content: form.firstAid?.content || '' },
+ mythVsFact: {
+ title: form.mythVsFact?.title || '',
+ mitos: form.mythVsFact?.mitos || '',
+ fakta: form.mythVsFact?.fakta || '',
+ },
+ doctorSign: { content: form.doctorSign?.content || '' },
+ });
+
+ if (form.imageId) {
+ setPreviewImage(`${process.env.NEXT_PUBLIC_API_URL}/file/${form.imageId}`);
}
} catch (error) {
- console.error("Error loading artikel kesehatan:", error);
- toast.error("Gagal memuat data artikel kesehatan");
+ console.error('Error loading artikel kesehatan:', error);
+ toast.error('Gagal memuat data artikel kesehatan');
}
};
+
loadArtikelKesehatan();
}, [params?.id]);
+ const handleFileChange = (files: File[]) => {
+ const selectedFile = files[0];
+ if (!selectedFile) return;
+ setFile(selectedFile);
+ setPreviewImage(URL.createObjectURL(selectedFile));
+ };
+
const handleSubmit = async () => {
try {
+ // Copy formData ke global state
stateArtikelKesehatan.edit.form = { ...formData };
+
+ // Upload gambar kalau ada
if (file) {
- const res = await ApiFetch.api.fileStorage.create.post({
- file,
- name: file.name,
- });
+ const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
-
- if (!uploaded?.id) {
- return toast.error("Gagal upload gambar");
- }
-
+ if (!uploaded?.id) return toast.error('Gagal upload gambar');
stateArtikelKesehatan.edit.form.imageId = uploaded.id;
}
const success = await stateArtikelKesehatan.edit.submit();
if (success) {
- toast.success("Artikel kesehatan berhasil diperbarui!");
- router.push("/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan");
+ toast.success('Artikel kesehatan berhasil diperbarui!');
+ router.push('/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan');
}
} catch (error) {
- console.error("Error updating artikel kesehatan:", error);
- toast.error(error instanceof Error ? error.message : "Gagal memperbarui data artikel kesehatan");
+ console.error('Error updating artikel kesehatan:', error);
+ toast.error(error instanceof Error ? error.message : 'Gagal memperbarui data artikel kesehatan');
}
};
+ const InputText = ({
+ label,
+ value,
+ onChange,
+ placeholder,
+ required,
+ }: {
+ label: string;
+ value: string;
+ onChange: (v: string) => void;
+ placeholder?: string;
+ required?: boolean;
+ }) => (
+ onChange(e.target.value)}
+ required={required}
+ />
+ );
+
return (
{/* Header */}
@@ -170,34 +171,31 @@ function EditArtikelKesehatan() {
style={{ border: '1px solid #e0e0e0' }}
>
- setFormData(prev => ({ ...prev, title: e.target.value }))}
+ onChange={(value) => setFormData((prev) => ({ ...prev, title: value }))}
+ placeholder="Masukkan judul artikel"
required
/>
+
+ {/* Gambar */}
- Gambar Berita
+ Gambar Artikel Kesehatan
{
- const selectedFile = files[0];
- if (selectedFile) {
- setFile(selectedFile);
- setPreviewImage(URL.createObjectURL(selectedFile));
- }
- }}
- onReject={() => toast.error("File tidak valid, gunakan format gambar")}
+ onDrop={handleFileChange}
+ onReject={() => toast.error('File tidak valid, gunakan format gambar')}
maxSize={5 * 1024 ** 2}
- accept={{ "image/*": [] }}
+ accept={{ 'image/*': [] }}
radius="md"
p="xl"
>
-
+
@@ -215,64 +213,57 @@ function EditArtikelKesehatan() {
-
{previewImage && (
-
+
)}
- setFormData(prev => ({ ...prev, content: e.target.value }))}
+ onChange={(value) => setFormData((prev) => ({ ...prev, content: value }))}
+ placeholder="Masukkan deskripsi artikel"
required
/>
-
- setFormData(prev => ({
- ...prev,
- introduction: { ...prev.introduction, content: e.target.value }
- }))
- }
- />
+ {/* Pendahuluan */}
+
+ Pendahuluan
+
+ setFormData((prev) => ({ ...prev, introduction: { ...prev.introduction, content: value } }))
+ }
+ />
+
{/* Gejala */}
Gejala
-
- setFormData(prev => ({
- ...prev,
- symptom: { ...prev.symptom, title: e.target.value }
- }))
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, symptom: { ...prev.symptom, title: value } }))
}
/>
- setFormData(prev => ({
- ...prev,
- symptom: { ...prev.symptom, content: e }
- }))
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, symptom: { ...prev.symptom, content: value } }))
}
/>
@@ -281,23 +272,17 @@ function EditArtikelKesehatan() {
{/* Pencegahan */}
Pencegahan
-
- setFormData(prev => ({
- ...prev,
- prevention: { ...prev.prevention, title: e.target.value }
- }))
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, prevention: { ...prev.prevention, title: value } }))
}
/>
- setFormData(prev => ({
- ...prev,
- prevention: { ...prev.prevention, content: e }
- }))
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, prevention: { ...prev.prevention, content: value } }))
}
/>
@@ -305,23 +290,17 @@ function EditArtikelKesehatan() {
{/* Pertolongan Pertama */}
Pertolongan Pertama
-
- setFormData(prev => ({
- ...prev,
- firstAid: { ...prev.firstAid, title: e.target.value }
- }))
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, firstAid: { ...prev.firstAid, title: value } }))
}
/>
- setFormData(prev => ({
- ...prev,
- firstAid: { ...prev.firstAid, content: e }
- }))
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, firstAid: { ...prev.firstAid, content: value } }))
}
/>
@@ -329,48 +308,36 @@ function EditArtikelKesehatan() {
{/* Mitos vs Fakta */}
Mitos vs Fakta
-
- setFormData(prev => ({
- ...prev,
- mythVsFact: { ...prev.mythVsFact, title: e.target.value }
- }))
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, mythVsFact: { ...prev.mythVsFact, title: value } }))
}
/>
Mitos
- setFormData(prev => ({
- ...prev,
- mythVsFact: { ...prev.mythVsFact, mitos: e }
- }))
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, mythVsFact: { ...prev.mythVsFact, mitos: value } }))
}
/>
Fakta
- setFormData(prev => ({
- ...prev,
- mythVsFact: { ...prev.mythVsFact, fakta: e }
- }))
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, mythVsFact: { ...prev.mythVsFact, fakta: value } }))
}
/>
- {/* Kapan harus ke dokter */}
+ {/* Dokter */}
Kapan Harus Ke Dokter
- setFormData(prev => ({
- ...prev,
- doctorSign: { ...prev.doctorSign, content: e }
- }))
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, doctorSign: { content: value } }))
}
/>
@@ -384,7 +351,7 @@ function EditArtikelKesehatan() {
style={{
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
color: '#fff',
- boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)'
+ boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
}}
>
Simpan
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/page.tsx
index 59f980e2..e03b527f 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/page.tsx
@@ -103,13 +103,13 @@ function DetailArtikelKesehatan() {
{/* Deskripsi */}
Deskripsi
-
+
{/* Pendahuluan */}
Pendahuluan
-
+
{/* Gejala */}
@@ -118,7 +118,7 @@ function DetailArtikelKesehatan() {
Judul
{data.symptom?.title}
Deskripsi
-
+
{/* Pencegahan */}
@@ -127,7 +127,7 @@ function DetailArtikelKesehatan() {
Judul
{data.prevention?.title}
Deskripsi
-
+
{/* Pertolongan Pertama */}
@@ -136,7 +136,7 @@ function DetailArtikelKesehatan() {
Judul
{data.firstaid?.title}
Deskripsi
-
+
{/* Mitos vs Fakta */}
@@ -145,15 +145,15 @@ function DetailArtikelKesehatan() {
Judul
{data.mythvsfact?.title}
Mitos
-
+
Fakta
-
+
{/* Kapan ke Dokter */}
Kapan Harus ke Dokter
-
+
{/* Aksi */}
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/create/page.tsx
index 6072684c..7289cd05 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/create/page.tsx
@@ -115,7 +115,7 @@ function CreateArtikelKesehatan() {
- Gambar Berita
+ Gambar Artikel Kesehatan
{
@@ -163,11 +163,11 @@ function CreateArtikelKesehatan() {
)}
-
+
{
stateArtikelKesehatan.create.form.title = e.target.value;
}}
@@ -176,22 +176,21 @@ function CreateArtikelKesehatan() {
{
stateArtikelKesehatan.create.form.content = e.target.value;
}}
required
/>
- {
- stateArtikelKesehatan.create.form.introduction.content = e.target.value;
- }}
- />
-
+
+ Pendahuluan
+ {
+ stateArtikelKesehatan.create.form.introduction.content = e;
+ }}
+ />
+
{/* Gejala */}
Gejala
@@ -200,7 +199,7 @@ function CreateArtikelKesehatan() {
label={"Judul Gejala"}
required
placeholder="Masukkan judul gejala penyakit"
- value={stateArtikelKesehatan.create.form.symptom.title}
+ defaultValue={stateArtikelKesehatan.create.form.symptom.title}
onChange={(e) => {
stateArtikelKesehatan.create.form.symptom.title = e.target.value;
}}
@@ -224,7 +223,7 @@ function CreateArtikelKesehatan() {
label={"Judul Pencegahan"}
required
placeholder="Masukkan judul"
- value={stateArtikelKesehatan.create.form.prevention.title}
+ defaultValue={stateArtikelKesehatan.create.form.prevention.title}
onChange={(e) => {
stateArtikelKesehatan.create.form.prevention.title = e.target.value;
}}
@@ -245,7 +244,7 @@ function CreateArtikelKesehatan() {
label={"Judul Pertolongan Pertama"}
required
placeholder="Masukkan judul"
- value={stateArtikelKesehatan.create.form.firstAid.title}
+ defaultValue={stateArtikelKesehatan.create.form.firstAid.title}
onChange={(e) => {
stateArtikelKesehatan.create.form.firstAid.title = e.target.value;
}}
@@ -266,7 +265,7 @@ function CreateArtikelKesehatan() {
label={"Judul Mitos dan Fakta"}
required
placeholder="Masukkan judul"
- value={stateArtikelKesehatan.create.form.mythVsFact.title}
+ defaultValue={stateArtikelKesehatan.create.form.mythVsFact.title}
onChange={(e) => {
stateArtikelKesehatan.create.form.mythVsFact.title = e.target.value;
}}
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/page.tsx
index 2d1902a0..961c6630 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/page.tsx
@@ -89,26 +89,26 @@ function ListArtikelKesehatan({ search }: { search: string }) {
- Judul
- Konten
- Aksi
+ Judul
+ Konten
+ Aksi
{filteredData.length > 0 ? (
filteredData.map((item) => (
-
+
{item.title}
-
+
{item.content}
-
+
({
- name: stateFasilitasKesehatan.edit.form.name || '',
- informasiUmum: {
- fasilitas: stateFasilitasKesehatan.edit.form.informasiUmum?.fasilitas || '',
- alamat: stateFasilitasKesehatan.edit.form.informasiUmum?.alamat || '',
- jamOperasional: stateFasilitasKesehatan.edit.form.informasiUmum?.jamOperasional || '',
- },
- layananUnggulan: {
- content: stateFasilitasKesehatan.edit.form.layananUnggulan?.content || '',
- },
- dokterdanTenagaMedis: {
- name: stateFasilitasKesehatan.edit.form.dokterdanTenagaMedis?.name || '',
- specialist: stateFasilitasKesehatan.edit.form.dokterdanTenagaMedis?.specialist || '',
- jadwal: stateFasilitasKesehatan.edit.form.dokterdanTenagaMedis?.jadwal || '',
- },
- fasilitasPendukung: {
- content: stateFasilitasKesehatan.edit.form.fasilitasPendukung?.content || '',
- },
- prosedurPendaftaran: {
- content: stateFasilitasKesehatan.edit.form.prosedurPendaftaran?.content || '',
- },
- tarifDanLayanan: {
- layanan: stateFasilitasKesehatan.edit.form.tarifDanLayanan?.layanan || '',
- tarif: stateFasilitasKesehatan.edit.form.tarifDanLayanan?.tarif || '',
- },
+ name: '',
+ informasiUmum: { fasilitas: '', alamat: '', jamOperasional: '' },
+ layananUnggulan: { content: '' },
+ dokterdanTenagaMedis: { name: '', specialist: '', jadwal: '' },
+ fasilitasPendukung: { content: '' },
+ prosedurPendaftaran: { content: '' },
+ tarifDanLayanan: { layanan: '', tarif: '' },
});
+ // Helper untuk update nested state
+ const updateForm = (
+ key: K,
+ value: FasilitasKesehatanFormBase[K]
+ ) => setFormData(prev => ({ ...prev, [key]: value }));
+
+ const updateNested = <
+ K extends keyof FasilitasKesehatanFormBase,
+ N extends keyof FasilitasKesehatanFormBase[K]
+ >(key: K, nestedKey: N, value: FasilitasKesehatanFormBase[K][N]) =>
+ setFormData(prev => ({
+ ...prev,
+ [key]: { ...prev[key] as object, [nestedKey]: value },
+ }));
+
+ // Load data
useEffect(() => {
- const loadFasilitasKesehatan = async () => {
+ const load = async () => {
const id = params?.id as string;
if (!id) return;
-
try {
- await stateFasilitasKesehatan.edit.load(id);
- const { form } = stateFasilitasKesehatan.edit;
- if (form) {
- setFormData({
- name: form.name,
- informasiUmum: {
- fasilitas: form.informasiUmum?.fasilitas || '',
- alamat: form.informasiUmum?.alamat || '',
- jamOperasional: form.informasiUmum?.jamOperasional || '',
- },
- layananUnggulan: { content: form.layananUnggulan?.content || '' },
- dokterdanTenagaMedis: {
- name: form.dokterdanTenagaMedis?.name || '',
- specialist: form.dokterdanTenagaMedis?.specialist || '',
- jadwal: form.dokterdanTenagaMedis?.jadwal || '',
- },
- fasilitasPendukung: { content: form.fasilitasPendukung?.content || '' },
- prosedurPendaftaran: { content: form.prosedurPendaftaran?.content || '' },
- tarifDanLayanan: {
- layanan: form.tarifDanLayanan?.layanan || '',
- tarif: form.tarifDanLayanan?.tarif || '',
- },
- });
- }
- } catch (error) {
- console.error("Error loading fasilitas kesehatan:", error);
- toast.error("Gagal memuat data fasilitas kesehatan");
+ await state.edit.load(id);
+ const form = state.edit.form;
+ if (form) setFormData(form as FasilitasKesehatanFormBase);
+ } catch (err) {
+ console.error(err);
+ toast.error('Gagal memuat data fasilitas kesehatan');
}
};
- loadFasilitasKesehatan();
+ load();
}, [params?.id]);
+ // Submit
const handleSubmit = async () => {
try {
- stateFasilitasKesehatan.edit.form = {
- ...stateFasilitasKesehatan.edit.form,
- ...formData,
- };
- const success = await stateFasilitasKesehatan.edit.submit();
+ state.edit.form = { ...state.edit.form, ...formData };
+ const success = await state.edit.submit();
if (success) {
- toast.success("Fasilitas kesehatan berhasil diperbarui!");
- router.push("/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan");
+ toast.success('Fasilitas kesehatan berhasil diperbarui!');
+ router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan');
}
- } catch (error) {
- console.error("Error updating fasilitas kesehatan:", error);
- toast.error("Terjadi kesalahan saat memperbarui data fasilitas kesehatan");
+ } catch (err) {
+ console.error(err);
+ toast.error('Terjadi kesalahan saat memperbarui data fasilitas kesehatan');
}
};
@@ -157,144 +132,103 @@ function EditFasilitasKesehatan() {
label="Nama Fasilitas Kesehatan"
placeholder="Masukkan nama fasilitas kesehatan"
value={formData.name}
- onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
+ onChange={(e) => updateForm('name', e.target.value)}
required
/>
{/* Informasi Umum */}
- Informasi Umum
+
+ Informasi Umum
+
- setFormData(prev => ({
- ...prev,
- informasiUmum: { ...prev.informasiUmum, fasilitas: e.target.value },
- }))
- }
+ onChange={(e) => updateNested('informasiUmum', 'fasilitas', e.target.value)}
/>
- setFormData(prev => ({
- ...prev,
- informasiUmum: { ...prev.informasiUmum, alamat: e.target.value },
- }))
- }
+ onChange={(e) => updateNested('informasiUmum', 'alamat', e.target.value)}
/>
- setFormData(prev => ({
- ...prev,
- informasiUmum: { ...prev.informasiUmum, jamOperasional: e.target.value },
- }))
- }
+ onChange={(e) => updateNested('informasiUmum', 'jamOperasional', e.target.value)}
/>
{/* Layanan Unggulan */}
- Layanan Unggulan
+
+ Layanan Unggulan
+
- setFormData(prev => ({
- ...prev,
- layananUnggulan: { content: e },
- }))
- }
+ onChange={(v) => updateNested('layananUnggulan', 'content', v)}
/>
{/* Dokter dan Tenaga Medis */}
- Dokter dan Tenaga Medis
+
+ Dokter dan Tenaga Medis
+
- setFormData(prev => ({
- ...prev,
- dokterdanTenagaMedis: { ...prev.dokterdanTenagaMedis, name: e.target.value },
- }))
- }
+ onChange={(e) => updateNested('dokterdanTenagaMedis', 'name', e.target.value)}
/>
- setFormData(prev => ({
- ...prev,
- dokterdanTenagaMedis: { ...prev.dokterdanTenagaMedis, specialist: e.target.value },
- }))
+ updateNested('dokterdanTenagaMedis', 'specialist', e.target.value)
}
/>
- setFormData(prev => ({
- ...prev,
- dokterdanTenagaMedis: { ...prev.dokterdanTenagaMedis, jadwal: e.target.value },
- }))
- }
+ onChange={(e) => updateNested('dokterdanTenagaMedis', 'jadwal', e.target.value)}
/>
{/* Fasilitas Pendukung */}
- Fasilitas Pendukung
+
+ Fasilitas Pendukung
+
- setFormData(prev => ({
- ...prev,
- fasilitasPendukung: { content: e },
- }))
- }
+ onChange={(v) => updateNested('fasilitasPendukung', 'content', v)}
/>
{/* Prosedur Pendaftaran */}
- Prosedur Pendaftaran
+
+ Prosedur Pendaftaran
+
- setFormData(prev => ({
- ...prev,
- prosedurPendaftaran: { content: e },
- }))
- }
+ onChange={(v) => updateNested('prosedurPendaftaran', 'content', v)}
/>
{/* Tarif dan Layanan */}
- Tarif dan Layanan
+
+ Tarif dan Layanan
+
- setFormData(prev => ({
- ...prev,
- tarifDanLayanan: { ...prev.tarifDanLayanan, tarif: e.target.value },
- }))
- }
+ onChange={(e) => updateNested('tarifDanLayanan', 'tarif', e.target.value)}
/>
- setFormData(prev => ({
- ...prev,
- tarifDanLayanan: { ...prev.tarifDanLayanan, layanan: e.target.value },
- }))
- }
+ onChange={(e) => updateNested('tarifDanLayanan', 'layanan', e.target.value)}
/>
@@ -304,7 +238,7 @@ function EditFasilitasKesehatan() {
onClick={handleSubmit}
radius="md"
size="md"
- loading={stateFasilitasKesehatan.edit.loading}
+ loading={state.edit.loading}
style={{
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
color: '#fff',
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/page.tsx
index 190eb675..d6640e65 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/page.tsx
@@ -88,24 +88,24 @@ function DetailFasilitasKesehatan() {
Fasilitas
{data.informasiumum?.fasilitas || '-'}
Alamat
- {data.informasiumum?.alamat || '-'}
+ {data.informasiumum?.alamat || '-'}
Jam Operasional
{data.informasiumum?.jamOperasional || '-'}
Layanan Unggulan
-
+
Fasilitas Pendukung
-
+
Prosedur Pendaftaran
-
+
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create/page.tsx
index 7d4338c6..530039a9 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create/page.tsx
@@ -92,7 +92,7 @@ function CreateFasilitasKesehatan() {
(stateFasilitasKesehatan.create.form.name = e.target.value)}
required
/>
@@ -103,21 +103,21 @@ function CreateFasilitasKesehatan() {
(stateFasilitasKesehatan.create.form.informasiUmum.fasilitas = e.target.value)}
required
/>
(stateFasilitasKesehatan.create.form.informasiUmum.alamat = e.target.value)}
required
/>
(stateFasilitasKesehatan.create.form.informasiUmum.jamOperasional = e.target.value)}
required
/>
@@ -138,21 +138,21 @@ function CreateFasilitasKesehatan() {
(stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name = e.target.value)}
required
/>
(stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist = e.target.value)}
required
/>
(stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal = e.target.value)}
required
/>
@@ -182,14 +182,14 @@ function CreateFasilitasKesehatan() {
(stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif = e.target.value)}
required
/>
(stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/create/page.tsx
index 3e33d305..71bf934b 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/create/page.tsx
@@ -40,7 +40,7 @@ function CreateDokter() {
Nama Dokter}
placeholder="masukkan nama dokter"
- value={createState.create.create.form.name}
+ defaultValue={createState.create.create.form.name}
onChange={(e) => {
createState.create.create.form.name = e.target.value;
}}
@@ -49,7 +49,7 @@ function CreateDokter() {
Specialist}
placeholder="masukkan specialist"
- value={createState.create.create.form.specialist}
+ defaultValue={createState.create.create.form.specialist}
onChange={(e) => {
createState.create.create.form.specialist = e.target.value;
}}
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/page.tsx
index 9c9e3f1e..8c554c6a 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/page.tsx
@@ -127,7 +127,9 @@ function ListFasilitasKesehatan({ search }: { search: string }) {
- {item.tarifdanlayanan?.layanan || '-'}
+
+ {item.tarifdanlayanan?.layanan || '-'}
+
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/edit/page.tsx
index c5f9bc20..f6501312 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/edit/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
+
import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
import colors from '@/con/colors';
import {
@@ -24,13 +25,14 @@ function EditGrafikHasilKepuasan() {
const params = useParams();
const [formData, setFormData] = useState({
- nama: editState.update.form.nama || '',
- tanggal: editState.update.form.tanggal || '',
- jenisKelamin: editState.update.form.jenisKelamin || '',
- alamat: editState.update.form.alamat || '',
- penyakit: editState.update.form.penyakit || '',
+ nama: '',
+ tanggal: '',
+ jenisKelamin: '',
+ alamat: '',
+ penyakit: '',
});
+ // Load data once
useEffect(() => {
const loadData = async () => {
const id = params?.id as string;
@@ -38,17 +40,15 @@ function EditGrafikHasilKepuasan() {
try {
const data = await editState.update.load(id);
- if (data) {
- setFormData({
- nama: data.nama || '',
- tanggal: data.tanggal || '',
- jenisKelamin: data.jenisKelamin || '',
- alamat: data.alamat || '',
- penyakit: data.penyakit || '',
- });
- }
- } catch (error) {
- console.error("Error loading grafik hasil kepuasan:", error);
+ if (data) setFormData({
+ nama: data.nama || '',
+ tanggal: data.tanggal || '',
+ jenisKelamin: data.jenisKelamin || '',
+ alamat: data.alamat || '',
+ penyakit: data.penyakit || '',
+ });
+ } catch (err) {
+ console.error("Error loading grafik hasil kepuasan:", err);
toast.error("Gagal memuat data grafik hasil kepuasan");
}
};
@@ -56,17 +56,19 @@ function EditGrafikHasilKepuasan() {
loadData();
}, [params?.id]);
+ // Generic handler for controlled inputs
+ const handleChange = (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
- editState.update.form = {
- ...editState.update.form,
- ...formData,
- };
+ editState.update.form = { ...editState.update.form, ...formData };
await editState.update.submit();
toast.success('Grafik hasil kepuasan berhasil diperbarui!');
router.push('/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan');
- } catch (error) {
- console.error('Error updating grafik hasil kepuasan:', error);
+ } catch (err) {
+ console.error('Error updating grafik hasil kepuasan:', err);
toast.error('Terjadi kesalahan saat memperbarui grafik hasil kepuasan');
}
};
@@ -100,44 +102,17 @@ function EditGrafikHasilKepuasan() {
style={{ border: '1px solid #e0e0e0' }}
>
- setFormData({ ...formData, nama: e.target.value })}
- label="Nama"
- placeholder="Masukkan nama"
- required
- />
- setFormData({ ...formData, tanggal: e.target.value })}
- label="Tanggal"
- placeholder="Masukkan tanggal"
- required
- />
-
- setFormData({ ...formData, jenisKelamin: e.target.value })
- }
- label="Jenis Kelamin"
- placeholder="Masukkan jenis kelamin"
- required
- />
- setFormData({ ...formData, alamat: e.target.value })}
- label="Alamat"
- placeholder="Masukkan alamat"
- required
- />
- setFormData({ ...formData, penyakit: e.target.value })}
- label="Penyakit"
- placeholder="Masukkan penyakit"
- required
- />
+ {(['nama','tanggal','jenisKelamin','alamat','penyakit'] as const).map((field) => (
+ handleChange(field, e.target.value)}
+ type={field === 'tanggal' ? 'date' : 'text'}
+ label={field === 'jenisKelamin' ? 'Jenis Kelamin' : field.charAt(0).toUpperCase() + field.slice(1)}
+ placeholder={`Masukkan ${field}`}
+ required
+ />
+ ))}
(stateGrafikKepuasan.create.form.nama = e.target.value)}
required
/>
@@ -80,28 +80,28 @@ function CreateGrafikHasilKepuasanMasyarakat() {
type="date"
label="Tanggal"
placeholder="Masukkan tanggal"
- value={stateGrafikKepuasan.create.form.tanggal}
+ defaultValue={stateGrafikKepuasan.create.form.tanggal}
onChange={(e) => (stateGrafikKepuasan.create.form.tanggal = e.target.value)}
required
/>
(stateGrafikKepuasan.create.form.jenisKelamin = e.target.value)}
required
/>
(stateGrafikKepuasan.create.form.alamat = e.target.value)}
required
/>
(stateGrafikKepuasan.create.form.penyakit = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/page.tsx
index 16ab706f..1897aba3 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/page.tsx
@@ -223,7 +223,7 @@ function ListGrafikHasilKepuasanMasyarakat({ search }: { search: string }) {
{/* Chart */}
-
+
Grafik Hasil Kepuasan Masyarakat
{mounted && diseaseChartData.length > 0 ? (
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/edit/page.tsx
index a6aacc37..de1b5922 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/edit/page.tsx
@@ -4,17 +4,7 @@
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import jadwalKegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import colors from '@/con/colors';
-import {
- Box,
- Button,
- Group,
- Paper,
- Stack,
- Text,
- TextInput,
- Title,
- Tooltip,
-} from '@mantine/core';
+import { Box, Button, Group, Paper, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
@@ -29,46 +19,49 @@ interface JadwalKegiatanFormBase {
waktu: string;
lokasi: string;
};
- deskripsiJadwalKegiatan: {
- deskripsi: string;
- };
- layananJadwalKegiatan: {
- content: string;
- };
- syaratKetentuanJadwalKegiatan: {
- content: string;
- };
- dokumenJadwalKegiatan: {
- content: string;
- };
+ deskripsiJadwalKegiatan: { deskripsi: string };
+ layananJadwalKegiatan: { content: string };
+ syaratKetentuanJadwalKegiatan: { content: string };
+ dokumenJadwalKegiatan: { content: string };
}
+const emptyForm = (): JadwalKegiatanFormBase => ({
+ content: '',
+ informasiJadwalKegiatan: { name: '', tanggal: '', waktu: '', lokasi: '' },
+ deskripsiJadwalKegiatan: { deskripsi: '' },
+ layananJadwalKegiatan: { content: '' },
+ syaratKetentuanJadwalKegiatan: { content: '' },
+ dokumenJadwalKegiatan: { content: '' },
+});
+
function EditJadwalKegiatan() {
const stateJadwalKegiatan = useProxy(jadwalKegiatanState);
const router = useRouter();
const params = useParams();
- const [formData, setFormData] = useState({
- content: stateJadwalKegiatan.edit.form.content || '',
- informasiJadwalKegiatan: {
- name: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.name || '',
- tanggal: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.tanggal || '',
- waktu: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.waktu || '',
- lokasi: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.lokasi || '',
- },
- deskripsiJadwalKegiatan: {
- deskripsi: stateJadwalKegiatan.edit.form.deskripsiJadwalKegiatan?.deskripsi || '',
- },
- layananJadwalKegiatan: {
- content: stateJadwalKegiatan.edit.form.layananJadwalKegiatan?.content || '',
- },
- syaratKetentuanJadwalKegiatan: {
- content: stateJadwalKegiatan.edit.form.syaratKetentuanJadwalKegiatan?.content || '',
- },
- dokumenJadwalKegiatan: {
- content: stateJadwalKegiatan.edit.form.dokumenJadwalKegiatan?.content || '',
- },
- });
+ const [formData, setFormData] = useState(emptyForm());
+
+ // Helper untuk update nested state
+ const updateNested = <
+ K extends keyof JadwalKegiatanFormBase,
+ N extends keyof JadwalKegiatanFormBase[K]
+ >(
+ key: K,
+ subKey: N,
+ value: string
+ ) => {
+ setFormData(prev => ({
+ ...prev,
+ [key]: {
+ ...(prev[key] as Record),
+ [subKey]: value
+ }
+ }));
+ };
+
+ const updateSimple = (key: keyof JadwalKegiatanFormBase, value: string) => {
+ setFormData(prev => ({ ...prev, [key]: value }));
+ };
useEffect(() => {
const loadJadwalKegiatan = async () => {
@@ -80,25 +73,17 @@ function EditJadwalKegiatan() {
const { form } = stateJadwalKegiatan.edit;
if (form) {
setFormData({
- content: form.content,
+ content: form.content || '',
informasiJadwalKegiatan: {
name: form.informasiJadwalKegiatan?.name || '',
tanggal: form.informasiJadwalKegiatan?.tanggal || '',
waktu: form.informasiJadwalKegiatan?.waktu || '',
lokasi: form.informasiJadwalKegiatan?.lokasi || '',
},
- deskripsiJadwalKegiatan: {
- deskripsi: form.deskripsiJadwalKegiatan?.deskripsi || '',
- },
- layananJadwalKegiatan: {
- content: form.layananJadwalKegiatan?.content || '',
- },
- syaratKetentuanJadwalKegiatan: {
- content: form.syaratKetentuanJadwalKegiatan?.content || '',
- },
- dokumenJadwalKegiatan: {
- content: form.dokumenJadwalKegiatan?.content || '',
- },
+ deskripsiJadwalKegiatan: { deskripsi: form.deskripsiJadwalKegiatan?.deskripsi || '' },
+ layananJadwalKegiatan: { content: form.layananJadwalKegiatan?.content || '' },
+ syaratKetentuanJadwalKegiatan: { content: form.syaratKetentuanJadwalKegiatan?.content || '' },
+ dokumenJadwalKegiatan: { content: form.dokumenJadwalKegiatan?.content || '' },
});
}
} catch (error) {
@@ -111,16 +96,7 @@ function EditJadwalKegiatan() {
const handleSubmit = async () => {
try {
- stateJadwalKegiatan.edit.form = {
- ...stateJadwalKegiatan.edit.form,
- content: formData.content,
- informasiJadwalKegiatan: { ...formData.informasiJadwalKegiatan },
- deskripsiJadwalKegiatan: { ...formData.deskripsiJadwalKegiatan },
- layananJadwalKegiatan: { ...formData.layananJadwalKegiatan },
- syaratKetentuanJadwalKegiatan: { ...formData.syaratKetentuanJadwalKegiatan },
- dokumenJadwalKegiatan: { ...formData.dokumenJadwalKegiatan }
- };
-
+ stateJadwalKegiatan.edit.form = { ...stateJadwalKegiatan.edit.form, ...formData };
const success = await stateJadwalKegiatan.edit.submit();
if (success) {
toast.success("Jadwal kegiatan berhasil diperbarui!");
@@ -161,7 +137,7 @@ function EditJadwalKegiatan() {
label="Nama Jadwal Kegiatan"
placeholder="Masukkan nama jadwal kegiatan"
value={formData.content}
- onChange={(e) => setFormData((prev) => ({ ...prev, content: e.target.value }))}
+ onChange={(e) => updateSimple('content', e.target.value)}
/>
{/* Deskripsi */}
@@ -169,36 +145,22 @@ function EditJadwalKegiatan() {
Deskripsi Jadwal Kegiatan
setFormData((prev) => ({
- ...prev,
- deskripsiJadwalKegiatan: { deskripsi: val }
- }))}
+ onChange={(val) => updateNested('deskripsiJadwalKegiatan', 'deskripsi', val)}
/>
{/* Informasi Jadwal */}
Informasi Jadwal Kegiatan
- setFormData((prev) => ({
- ...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, name: e.target.value }
- }))}
- />
- setFormData((prev) => ({
- ...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, tanggal: e.target.value }
- }))}
- />
- setFormData((prev) => ({
- ...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, waktu: e.target.value }
- }))}
- />
- setFormData((prev) => ({
- ...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, lokasi: e.target.value }
- }))}
- />
+ {(['name', 'tanggal', 'waktu', 'lokasi'] as const).map((field) => (
+ updateNested('informasiJadwalKegiatan', field, e.target.value)}
+ />
+ ))}
{/* Layanan */}
@@ -206,10 +168,7 @@ function EditJadwalKegiatan() {
Layanan Jadwal Kegiatan
setFormData((prev) => ({
- ...prev,
- layananJadwalKegiatan: { content: val }
- }))}
+ onChange={(val) => updateNested('layananJadwalKegiatan', 'content', val)}
/>
@@ -218,10 +177,7 @@ function EditJadwalKegiatan() {
Syarat dan Ketentuan
setFormData((prev) => ({
- ...prev,
- syaratKetentuanJadwalKegiatan: { content: val }
- }))}
+ onChange={(val) => updateNested('syaratKetentuanJadwalKegiatan', 'content', val)}
/>
@@ -230,10 +186,7 @@ function EditJadwalKegiatan() {
Dokumen Yang Perlu Dibawa
setFormData((prev) => ({
- ...prev,
- dokumenJadwalKegiatan: { content: val }
- }))}
+ onChange={(val) => updateNested('dokumenJadwalKegiatan', 'content', val)}
/>
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/page.tsx
index 45a1aade..383cc54c 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/page.tsx
@@ -83,31 +83,31 @@ function DetailJadwalKegiatan() {
Waktu
{data.informasijadwalkegiatan.waktu || '-'}
Lokasi
- {data.informasijadwalkegiatan.lokasi || '-'}
+ {data.informasijadwalkegiatan.lokasi || '-'}
{/* Deskripsi */}
Deskripsi
-
+
{/* Layanan */}
Layanan
-
+
{/* Syarat Ketentuan */}
Syarat Ketentuan
-
+
{/* Dokumen */}
Dokumen
-
+
{/* Aksi */}
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/create/page.tsx
index d847720d..55121d4a 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/create/page.tsx
@@ -87,7 +87,7 @@ function CreateJadwalKegiatan() {
{
stateJadwalKegiatan.create.form.content = e.target.value;
}}
@@ -110,7 +110,7 @@ function CreateJadwalKegiatan() {
label="Nama"
required
placeholder="Masukkan nama"
- value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.name}
+ defaultValue={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.name}
onChange={(e) => {
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.name = e.target.value;
}}
@@ -119,7 +119,7 @@ function CreateJadwalKegiatan() {
type="date"
required
label="Tanggal"
- value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.tanggal}
+ defaultValue={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.tanggal}
onChange={(e) => {
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.tanggal = e.target.value;
}}
@@ -128,7 +128,7 @@ function CreateJadwalKegiatan() {
label="Waktu"
required
placeholder="Masukkan waktu"
- value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.waktu}
+ defaultValue={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.waktu}
onChange={(e) => {
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.waktu = e.target.value;
}}
@@ -137,7 +137,7 @@ function CreateJadwalKegiatan() {
label="Lokasi"
required
placeholder="Masukkan lokasi"
- value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.lokasi}
+ defaultValue={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.lokasi}
onChange={(e) => {
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.lokasi = e.target.value;
}}
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/edit/page.tsx
index e05848c5..ac5e2805 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/edit/page.tsx
@@ -1,16 +1,17 @@
/* eslint-disable react-hooks/exhaustive-deps */
-'use client'
+'use client';
+
import persentaseKelahiranKematian from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
import colors from '@/con/colors';
import {
- Box,
- Button,
- Group,
- Paper,
- Stack,
- TextInput,
- Title,
- Tooltip
+ Box,
+ Button,
+ Group,
+ Paper,
+ Stack,
+ TextInput,
+ Title,
+ Tooltip,
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -18,146 +19,130 @@ import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-
function EditKelahiran() {
- const editState = useProxy(persentaseKelahiranKematian.kelahiran);
- const router = useRouter();
- const params = useParams();
+ const editState = useProxy(persentaseKelahiranKematian.kelahiran);
+ const router = useRouter();
+ const params = useParams();
+ const [formData, setFormData] = useState({
+ nama: '',
+ tanggal: '',
+ jenisKelamin: '',
+ alamat: '',
+ });
- const [formData, setFormData] = useState({
- nama: editState.edit.form.nama || '',
- tanggal: editState.edit.form.tanggal || '',
- jenisKelamin: editState.edit.form.jenisKelamin || '',
- alamat: editState.edit.form.alamat || '',
- });
+ // Load data saat mount atau params.id berubah
+ useEffect(() => {
+ const loadKelahiran = async () => {
+ const id = params?.id as string;
+ if (!id) return;
+ try {
+ const data = await editState.edit.load(id);
+ if (data) setFormData({
+ nama: data.nama || '',
+ tanggal: data.tanggal || '',
+ jenisKelamin: data.jenisKelamin || '',
+ alamat: data.alamat || ''
+ });
+ } catch (error) {
+ console.error('Error loading data kelahiran:', error);
+ toast.error('Gagal memuat data kelahiran');
+ }
+ };
- useEffect(() => {
- const loadKelahiran = async () => {
- const id = params?.id as string;
- if (!id) return;
+ loadKelahiran();
+ }, [params?.id]);
+ const handleChange = (key: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({ ...prev, [key]: value }));
+ };
- try {
- const data = await editState.edit.load(id);
- if (data) {
- setFormData({
- nama: data.nama || '',
- tanggal: data.tanggal || '',
- jenisKelamin: data.jenisKelamin || '',
- alamat: data.alamat || '',
- });
- }
- } catch (error) {
- console.error('Error loading data kelahiran:', error);
- toast.error('Gagal memuat data kelahiran');
- }
- };
+ const handleSubmit = async () => {
+ try {
+ // Update global state hanya saat submit
+ editState.edit.form = { ...editState.edit.form, ...formData };
+ await editState.edit.update();
+ toast.success('Data kelahiran berhasil diperbarui!');
+ router.push('/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran');
+ } catch (error) {
+ console.error('Error updating data kelahiran:', error);
+ toast.error('Terjadi kesalahan saat memperbarui data kelahiran');
+ }
+ };
+ return (
+
+ {/* Header */}
+
+
+ router.back()} p="xs" radius="md">
+
+
+
+
+ Edit Data Kelahiran
+
+
- loadKelahiran();
- }, [params?.id]);
+ {/* Form */}
+
+
+ handleChange('nama', e.target.value)}
+ label="Nama"
+ placeholder="Masukkan nama"
+ required
+ />
+ handleChange('tanggal', e.target.value)}
+ label="Tanggal"
+ placeholder="Masukkan tanggal"
+ required
+ />
+ handleChange('jenisKelamin', e.target.value)}
+ label="Jenis Kelamin"
+ placeholder="Masukkan jenis kelamin"
+ required
+ />
+ handleChange('alamat', e.target.value)}
+ label="Alamat"
+ placeholder="Masukkan alamat"
+ required
+ />
-
- const handleSubmit = async () => {
- try {
- editState.edit.form = {
- ...editState.edit.form,
- nama: formData.nama,
- tanggal: formData.tanggal,
- jenisKelamin: formData.jenisKelamin,
- alamat: formData.alamat,
- };
-
-
- await editState.edit.update();
- toast.success('Data kelahiran berhasil diperbarui!');
- router.push(
- '/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran'
- );
- } catch (error) {
- console.error('Error updating data kelahiran:', error);
- toast.error('Terjadi kesalahan saat memperbarui data kelahiran');
- }
- };
-
-
- return (
-
- {/* Header */}
-
-
- router.back()} p="xs" radius="md">
-
-
-
-
- Edit Data Kelahiran
-
-
-
-
- {/* Form */}
-
-
- setFormData({ ...formData, nama: e.target.value })}
- label="Nama"
- placeholder="Masukkan nama"
- required
- />
- setFormData({ ...formData, tanggal: e.target.value })}
- label="Tanggal"
- placeholder="Masukkan tanggal"
- required
- />
- setFormData({ ...formData, jenisKelamin: e.target.value })}
- label="Jenis Kelamin"
- placeholder="Masukkan jenis kelamin"
- required
- />
- setFormData({ ...formData, alamat: e.target.value })}
- label="Alamat"
- placeholder="Masukkan alamat"
- required
- />
-
-
-
-
- Simpan
-
-
-
-
-
- );
+
+
+ Simpan
+
+
+
+
+
+ );
}
-
-export default EditKelahiran;
\ No newline at end of file
+export default EditKelahiran;
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/page.tsx
index 2ce02278..18a2e587 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/page.tsx
@@ -105,7 +105,7 @@ function DetailKelahiran() {
Alamat
- {data.alamat || '-'}
+ {data.alamat || '-'}
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/create/page.tsx
index e668ac72..b8476a66 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/create/page.tsx
@@ -74,7 +74,7 @@ function CreateKelahiran() {
Nama}
placeholder="Masukkan nama"
- value={createState.create.form.nama}
+ defaultValue={createState.create.form.nama}
onChange={(e) => (createState.create.form.nama = e.target.value)}
required
/>
@@ -82,21 +82,21 @@ function CreateKelahiran() {
type="date"
label={Tanggal }
placeholder="Masukkan tanggal"
- value={createState.create.form.tanggal}
+ defaultValue={createState.create.form.tanggal}
onChange={(e) => (createState.create.form.tanggal = e.target.value)}
required
/>
Jenis Kelamin}
placeholder="Masukkan jenis kelamin"
- value={createState.create.form.jenisKelamin}
+ defaultValue={createState.create.form.jenisKelamin}
onChange={(e) => (createState.create.form.jenisKelamin = e.target.value)}
required
/>
Alamat}
placeholder="Masukkan alamat"
- value={createState.create.form.alamat}
+ defaultValue={createState.create.form.alamat}
onChange={(e) => (createState.create.form.alamat = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/edit/page.tsx
index 510c60f3..9f7ada02 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/edit/page.tsx
@@ -1,18 +1,19 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
+
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import persentaseKelahiranKematian from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
import colors from '@/con/colors';
import {
- Box,
- Button,
- Group,
- Paper,
- Stack,
- Text,
- TextInput,
- Title,
- Tooltip,
+ Box,
+ Button,
+ Group,
+ Paper,
+ Stack,
+ Text,
+ TextInput,
+ Title,
+ Tooltip,
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -20,160 +21,149 @@ import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-
function EditKematian() {
- const editState = useProxy(persentaseKelahiranKematian.kematian);
- const router = useRouter();
- const params = useParams();
+ const editState = useProxy(persentaseKelahiranKematian.kematian);
+ const router = useRouter();
+ const params = useParams();
+ const [formData, setFormData] = useState({
+ nama: '',
+ tanggal: '',
+ jenisKelamin: '',
+ alamat: '',
+ penyebab: '',
+ });
- const [formData, setFormData] = useState({
- nama: editState.edit.form.nama || '',
- tanggal: editState.edit.form.tanggal || '',
- jenisKelamin: editState.edit.form.jenisKelamin || '',
- alamat: editState.edit.form.alamat || '',
- penyebab: editState.edit.form.penyebab || '',
- });
+ // Load data saat mount
+ useEffect(() => {
+ const loadData = async () => {
+ const id = params?.id as string;
+ if (!id) return;
+ try {
+ const data = await editState.edit.load(id);
+ if (data) {
+ setFormData({
+ nama: data.nama || '',
+ tanggal: data.tanggal || '',
+ jenisKelamin: data.jenisKelamin || '',
+ alamat: data.alamat || '',
+ penyebab: data.penyebab || '',
+ });
+ }
+ } catch (error) {
+ console.error('Error loading data kematian:', error);
+ toast.error('Gagal memuat data kematian');
+ }
+ };
- useEffect(() => {
- const loadData = async () => {
- const id = params?.id as string;
- if (!id) return;
+ loadData();
+ }, [params?.id]);
+ const handleChange = (key: keyof typeof formData, value: string) => {
+ setFormData(prev => ({ ...prev, [key]: value }));
+ };
- try {
- const data = await editState.edit.load(id);
- if (data) {
- setFormData({
- nama: data.nama || '',
- tanggal: data.tanggal || '',
- jenisKelamin: data.jenisKelamin || '',
- alamat: data.alamat || '',
- penyebab: data.penyebab || '',
- });
- }
- } catch (error) {
- console.error('Error loading data kematian:', error);
- toast.error('Gagal memuat data kematian');
- }
- };
+ const handleSubmit = async () => {
+ try {
+ // Update global state saat submit
+ editState.edit.form = { ...editState.edit.form, ...formData };
+ await editState.edit.update();
+ toast.success('Data kematian berhasil diperbarui!');
+ router.push(
+ '/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian'
+ );
+ } catch (error) {
+ console.error('Error updating data kematian:', error);
+ toast.error('Terjadi kesalahan saat memperbarui data kematian');
+ }
+ };
+ return (
+
+ {/* Header */}
+
+
+ router.back()} p="xs" radius="md">
+
+
+
+
+ Edit Data Kematian
+
+
- loadData();
- }, [params?.id]);
+ {/* Form Card */}
+
+
+ handleChange('nama', e.target.value)}
+ required
+ />
+ handleChange('tanggal', e.target.value)}
+ required
+ />
- const handleSubmit = async () => {
- try {
- editState.edit.form = { ...editState.edit.form, ...formData };
- await editState.edit.update();
- toast.success('Data kematian berhasil diperbarui!');
- router.push(
- '/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian'
- );
- } catch (error) {
- console.error('Error updating data kematian:', error);
- toast.error('Terjadi kesalahan saat memperbarui data kematian');
- }
- };
+ handleChange('jenisKelamin', e.target.value)}
+ required
+ />
+ handleChange('alamat', e.target.value)}
+ required
+ />
- return (
-
- {/* Header dengan tombol back */}
-
-
- router.back()} p="xs" radius="md">
-
-
-
-
- Edit Data Kematian
-
-
+
+
+ Penyebab
+
+ handleChange('penyebab', htmlContent)}
+ />
+
-
- {/* Card Form */}
-
-
- setFormData({ ...formData, nama: e.target.value })}
- required
- />
-
-
- setFormData({ ...formData, tanggal: e.target.value })}
- required
- />
-
-
- setFormData({ ...formData, jenisKelamin: e.target.value })}
- required
- />
-
-
- setFormData({ ...formData, alamat: e.target.value })}
- required
- />
-
-
-
-
- Penyebab
-
- {
- setFormData((prev) => ({ ...prev, penyebab: htmlContent }));
- editState.edit.form.penyebab = htmlContent;
- }}
- />
-
-
-
-
-
- Simpan
-
-
-
-
-
- );
+
+
+ Simpan
+
+
+
+
+
+ );
}
-
-export default EditKematian;
\ No newline at end of file
+export default EditKematian;
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/page.tsx
index eeb7913b..41b4fa96 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/page.tsx
@@ -102,13 +102,13 @@ function DetailKematian() {
Alamat
- {data?.alamat || '-'}
+ {data?.alamat || '-'}
Penyebab
-
+
diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/create/page.tsx
index f9477dd5..2f1c92b7 100644
--- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/create/page.tsx
@@ -83,7 +83,7 @@ function CreateKematian() {
(createState.create.form.nama = e.target.value)}
required
/>
@@ -91,21 +91,21 @@ function CreateKematian() {
type="date"
label="Tanggal"
placeholder="Masukkan tanggal"
- value={createState.create.form.tanggal}
+ defaultValue={createState.create.form.tanggal}
onChange={(e) => (createState.create.form.tanggal = e.target.value)}
required
/>
(createState.create.form.jenisKelamin = e.target.value)}
required
/>
(createState.create.form.alamat = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/edit/page.tsx
index 8d330f3c..6d751dd5 100644
--- a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/edit/page.tsx
@@ -19,7 +19,7 @@ import {
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect, useState } from 'react';
+import { useEffect, useState, ChangeEvent } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
@@ -28,15 +28,21 @@ function EditInfoWabahPenyakit() {
const router = useRouter();
const params = useParams();
+ const [formData, setFormData] = useState({
+ name: '',
+ deskripsiSingkat: '',
+ deskripsi: '',
+ imageId: '',
+ });
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
- const [formData, setFormData] = useState({
- name: infoWabahPenyakitState.edit.form.name || '',
- deskripsiSingkat: infoWabahPenyakitState.edit.form.deskripsiSingkat || '',
- deskripsi: infoWabahPenyakitState.edit.form.deskripsiLengkap || '',
- imageId: infoWabahPenyakitState.edit.form.imageId || '',
- });
+ // Helper untuk update field formData
+ const updateField = (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
+ // Load data edit
useEffect(() => {
const loadInfoWabahPenyakit = async () => {
const id = params?.id as string;
@@ -52,12 +58,10 @@ function EditInfoWabahPenyakit() {
imageId: data.imageId || '',
});
- if (data?.image?.link) {
- setPreviewImage(data.image.link);
- }
+ if (data.image?.link) setPreviewImage(data.image.link);
}
} catch (error) {
- console.error('Error loading info wabah penyakit:', error);
+ console.error(error);
toast.error('Gagal memuat data info wabah penyakit');
}
};
@@ -67,34 +71,43 @@ function EditInfoWabahPenyakit() {
const handleSubmit = async () => {
try {
+ let uploadedImageId = formData.imageId;
+
+ // Upload file kalau ada
+ if (file) {
+ const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
+ const uploaded = res.data?.data;
+
+ if (!uploaded?.id) return toast.error('Gagal upload gambar');
+ uploadedImageId = uploaded.id;
+ }
+
+ // Update global state
infoWabahPenyakitState.edit.form = {
...infoWabahPenyakitState.edit.form,
name: formData.name,
deskripsiSingkat: formData.deskripsiSingkat,
deskripsiLengkap: formData.deskripsi,
- imageId: formData.imageId,
+ imageId: uploadedImageId,
};
- if (file) {
- const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
- const uploaded = res.data?.data;
-
- if (!uploaded?.id) {
- return toast.error('Gagal upload gambar');
- }
-
- infoWabahPenyakitState.edit.form.imageId = uploaded.id;
- }
-
await infoWabahPenyakitState.edit.update();
toast.success('Info wabah penyakit berhasil diperbarui!');
router.push('/admin/kesehatan/info-wabah-penyakit');
} catch (error) {
- console.error('Error updating info wabah penyakit:', error);
+ console.error(error);
toast.error('Terjadi kesalahan saat memperbarui info wabah penyakit');
}
};
+ const handleDrop = (files: File[]) => {
+ const selectedFile = files[0];
+ if (selectedFile) {
+ setFile(selectedFile);
+ setPreviewImage(URL.createObjectURL(selectedFile));
+ }
+ };
+
return (
{/* Header */}
@@ -121,19 +134,21 @@ function EditInfoWabahPenyakit() {
setFormData({ ...formData, name: e.target.value })}
+ onChange={(e: ChangeEvent) => updateField('name', e.target.value)}
label="Judul"
placeholder="Masukkan judul"
required
/>
- setFormData({ ...formData, deskripsiSingkat: e.target.value })}
- label="Deskripsi Singkat"
- placeholder="Masukkan deskripsi singkat"
- required
- />
+
+
+ Deskripsi Singkat
+
+ updateField('deskripsiSingkat', val)}
+ />
+
@@ -141,7 +156,7 @@ function EditInfoWabahPenyakit() {
setFormData({ ...formData, deskripsi: val })}
+ onChange={(val) => updateField('deskripsi', val)}
/>
@@ -150,13 +165,7 @@ function EditInfoWabahPenyakit() {
Gambar
{
- const selectedFile = files[0];
- if (selectedFile) {
- setFile(selectedFile);
- setPreviewImage(URL.createObjectURL(selectedFile));
- }
- }}
+ onDrop={handleDrop}
onReject={() => toast.error('File tidak valid.')}
maxSize={5 * 1024 ** 2}
accept={{ 'image/*': [] }}
diff --git a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/page.tsx
index 9e96bd7f..03fda087 100644
--- a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/page.tsx
@@ -84,7 +84,7 @@ function DetailInfoWabahPenyakit() {
Deskripsi Singkat
- {data.deskripsiSingkat || '-'}
+
@@ -93,6 +93,7 @@ function DetailInfoWabahPenyakit() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsiLengkap }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/create/page.tsx
index 0e0b6255..06a0d39c 100644
--- a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/create/page.tsx
@@ -91,7 +91,7 @@ function CreateInfoWabahPenyakit() {
>
{
infoWabahPenyakitState.create.form.name = val.target.value;
}}
@@ -100,15 +100,15 @@ function CreateInfoWabahPenyakit() {
required
/>
- {
- infoWabahPenyakitState.create.form.deskripsiSingkat = val.target.value;
- }}
- label={Deskripsi Singkat }
- placeholder="Masukkan deskripsi singkat"
- required
- />
+
+ Deskripsi Singkat
+ {
+ infoWabahPenyakitState.create.form.deskripsiSingkat = val;
+ }}
+ />
+
Deskripsi
diff --git a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/page.tsx b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/page.tsx
index aaa20847..e4215822 100644
--- a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/page.tsx
@@ -111,9 +111,7 @@ function ListInfoWabahPenyakit({ search }: { search: string }) {
-
- {item.deskripsiSingkat}
-
+
diff --git a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/edit/page.tsx
index c8562fe0..8ede9489 100644
--- a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/edit/page.tsx
@@ -1,5 +1,4 @@
-/* eslint-disable react-hooks/exhaustive-deps */
-'use client'
+'use client';
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import kontakDarurat from '@/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat';
import colors from '@/con/colors';
@@ -23,7 +22,6 @@ import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-
function EditKontakDarurat() {
const kontakDaruratState = useProxy(kontakDarurat);
const router = useRouter();
@@ -32,11 +30,14 @@ function EditKontakDarurat() {
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
const [formData, setFormData] = useState({
- name: kontakDaruratState.edit.form.name || '',
- deskripsi: kontakDaruratState.edit.form.deskripsi || '',
- imageId: kontakDaruratState.edit.form.imageId || '',
+ name: '',
+ deskripsi: '',
+ imageId: '',
+ whatsapp: '',
});
+ const [loading, setLoading] = useState(true);
+ // Load data sekali saat mount
useEffect(() => {
const loadKontakDarurat = async () => {
const id = params?.id as string;
@@ -49,41 +50,40 @@ function EditKontakDarurat() {
name: data.name || '',
deskripsi: data.deskripsi || '',
imageId: data.imageId || '',
+ whatsapp: data.whatsapp || '',
});
-
- if (data?.image?.link) {
- setPreviewImage(data.image.link);
- }
+ if (data?.image?.link) setPreviewImage(data.image.link);
}
} catch (error) {
console.error("Error loading kontak darurat:", error);
toast.error("Gagal memuat data kontak darurat");
+ } finally {
+ setLoading(false);
}
};
loadKontakDarurat();
- }, [params?.id]);
+ }, [params?.id, kontakDaruratState.edit]);
const handleSubmit = async () => {
try {
- kontakDaruratState.edit.form = {
- ...kontakDaruratState.edit.form,
- name: formData.name,
- deskripsi: formData.deskripsi,
- imageId: formData.imageId,
- };
+ let imageId = formData.imageId;
+ // Upload file baru jika ada
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
-
- if (!uploaded?.id) {
- return toast.error("Gagal upload gambar");
- }
-
- kontakDaruratState.edit.form.imageId = uploaded.id;
+ if (!uploaded?.id) return toast.error("Gagal upload gambar");
+ imageId = uploaded.id;
}
+ // Update global state sekaligus submit
+ kontakDaruratState.edit.form = {
+ ...kontakDaruratState.edit.form,
+ ...formData,
+ imageId,
+ };
+
await kontakDaruratState.edit.update();
toast.success("Kontak darurat berhasil diperbarui!");
router.push("/admin/kesehatan/kontak-darurat");
@@ -93,9 +93,10 @@ function EditKontakDarurat() {
}
};
+ if (loading) return Loading... ;
+
return (
- {/* Header */}
router.back()} p="xs" radius="md">
@@ -107,7 +108,6 @@ function EditKontakDarurat() {
- {/* Form */}
+ {/* Controlled Input */}
setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
label="Judul"
placeholder="Masukkan judul"
required
/>
+ setFormData(prev => ({ ...prev, whatsapp: e.target.value }))}
+ label="Whatsapp"
+ placeholder="Masukkan whatsapp"
+ required
+ />
+
Deskripsi
setFormData({ ...formData, deskripsi: val })}
+ onChange={(val) => setFormData(prev => ({ ...prev, deskripsi: val }))}
/>
diff --git a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/page.tsx
index a1088693..628338cb 100644
--- a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/page.tsx
@@ -72,12 +72,18 @@ function DetailKontakDarurat() {
{data.name || '-'}
+
+ Whatsapp
+ {data.whatsapp || '-'}
+
+
Deskripsi
diff --git a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/create/page.tsx
index d1767f6a..65a84277 100644
--- a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/create/page.tsx
@@ -38,6 +38,7 @@ function CreateKontakDarurat() {
name: '',
deskripsi: '',
imageId: '',
+ whatsapp: '',
};
setPreviewImage(null);
setFile(null);
@@ -96,7 +97,7 @@ function CreateKontakDarurat() {
>
{
kontakDaruratState.create.form.name = val.target.value;
}}
@@ -105,6 +106,17 @@ function CreateKontakDarurat() {
required
/>
+ {
+ kontakDaruratState.create.form.whatsapp = val.target.value;
+ }}
+ label={Whatsapp }
+ placeholder="Masukkan whatsapp"
+ required
+ />
+
Deskripsi
(null);
const [file, setFile] = useState(null);
- const [formData, setFormData] = useState({
- name: penangananDaruratState.edit.form.name || '',
- deskripsi: penangananDaruratState.edit.form.deskripsi || '',
- imageId: penangananDaruratState.edit.form.imageId || '',
- })
+ const [loading, setLoading] = useState(true);
+ // Load data satu kali saat component mount
useEffect(() => {
- const loadPenangananDarurat = async () => {
+ const loadData = async () => {
const id = params?.id as string;
if (!id) return;
@@ -48,49 +52,66 @@ function EditPenangananDarurat() {
name: data.name || '',
deskripsi: data.deskripsi || '',
imageId: data.imageId || '',
- })
+ });
- if (data?.image?.link) {
+ if (data.image?.link) {
setPreviewImage(data.image.link);
}
}
- } catch (error) {
- console.error('Error loading penanganan darurat:', error);
+ } catch (err) {
+ console.error('Error loading penanganan darurat:', err);
toast.error('Gagal memuat data penanganan darurat');
+ } finally {
+ setLoading(false);
}
}
- loadPenangananDarurat();
- }, [params?.id])
+ loadData();
+ }, [params?.id]);
+
+ const handleChange = (key: keyof typeof formData, value: string) => {
+ setFormData(prev => ({ ...prev, [key]: value }));
+ };
+
+ const handleDrop = (files: File[]) => {
+ const selected = files[0];
+ if (!selected) return;
+
+ setFile(selected);
+ setPreviewImage(URL.createObjectURL(selected));
+ };
const handleSubmit = async () => {
try {
- penangananDaruratState.edit.form = {
- ...penangananDaruratState.edit.form,
- name: formData.name,
- deskripsi: formData.deskripsi,
- imageId: formData.imageId,
- }
+ let imageId = formData.imageId;
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
- if (!uploaded?.id) {
- return toast.error("Gagal upload gambar");
- }
+ if (!uploaded?.id) return toast.error("Gagal upload gambar");
- penangananDaruratState.edit.form.imageId = uploaded.id;
+ imageId = uploaded.id;
}
+ // update global state sekali saat submit
+ penangananDaruratState.edit.form = {
+ ...penangananDaruratState.edit.form,
+ name: formData.name,
+ deskripsi: formData.deskripsi,
+ imageId,
+ };
+
await penangananDaruratState.edit.update();
toast.success("Penanganan darurat berhasil diperbarui!");
router.push("/admin/kesehatan/penanganan-darurat");
- } catch (error) {
- console.error("Error updating penanganan darurat:", error);
+ } catch (err) {
+ console.error("Error updating penanganan darurat:", err);
toast.error("Gagal memperbarui data penanganan darurat");
}
- }
+ };
+
+ if (loading) return Loading... ;
return (
@@ -118,7 +139,7 @@ function EditPenangananDarurat() {
setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange('name', e.target.value)}
label="Judul"
placeholder="Masukkan judul"
required
@@ -128,20 +149,14 @@ function EditPenangananDarurat() {
Deskripsi
setFormData({ ...formData, deskripsi: val })}
+ onChange={(val) => handleChange('deskripsi', val)}
/>
Gambar
{
- const selectedFile = files[0];
- if (selectedFile) {
- setFile(selectedFile);
- setPreviewImage(URL.createObjectURL(selectedFile));
- }
- }}
+ onDrop={handleDrop}
onReject={() => toast.error('File tidak valid.')}
maxSize={5 * 1024 ** 2}
accept={{ 'image/*': [] }}
diff --git a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/page.tsx
index c7f11f9b..36967755 100644
--- a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/page.tsx
@@ -78,6 +78,7 @@ function DetailPenangananDarurat() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/create/page.tsx
index 89dc64ff..f28e0a92 100644
--- a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/create/page.tsx
@@ -99,7 +99,7 @@ function CreatePenangananDarurat() {
Judul}
placeholder="Masukkan judul"
- value={penangananDaruratState.create.form.name}
+ defaultValue={penangananDaruratState.create.form.name}
onChange={(val) => {
penangananDaruratState.create.form.name = val.target.value;
}}
diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx
index 606f2538..1a47aefa 100644
--- a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx
@@ -1,20 +1,21 @@
/* eslint-disable react-hooks/exhaustive-deps */
-'use client'
+'use client';
+
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import posyandustate from '@/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
import {
- Box,
- Button,
- Group,
- Image,
- Paper,
- Stack,
- Text,
- TextInput,
- Title,
- Tooltip,
+ Box,
+ Button,
+ Group,
+ Image,
+ Paper,
+ Stack,
+ Text,
+ TextInput,
+ Title,
+ Tooltip,
} from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
@@ -23,234 +24,211 @@ import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-
function EditPosyandu() {
- const statePosyandu = useProxy(posyandustate);
- const router = useRouter();
- const params = useParams();
+ const statePosyandu = useProxy(posyandustate);
+ const router = useRouter();
+ const params = useParams();
+ const [previewImage, setPreviewImage] = useState(null);
+ const [file, setFile] = useState(null);
+ const [formData, setFormData] = useState({
+ name: '',
+ nomor: '',
+ deskripsi: '',
+ imageId: '',
+ jadwalPelayanan: '',
+ });
- const [previewImage, setPreviewImage] = useState(null);
- const [file, setFile] = useState(null);
- const [formData, setFormData] = useState({
- name: statePosyandu.edit.form.name || '',
- nomor: statePosyandu.edit.form.nomor || '',
- deskripsi: statePosyandu.edit.form.deskripsi || '',
- imageId: statePosyandu.edit.form.imageId || '',
- jadwalPelayanan: statePosyandu.edit.form.jadwalPelayanan || '',
- });
+ // Load data posyandu
+ useEffect(() => {
+ const loadPosyandu = async () => {
+ const id = params?.id as string;
+ if (!id) return;
+ try {
+ const data = await statePosyandu.edit.load(id);
+ if (data) {
+ setFormData({
+ name: data.name || '',
+ nomor: data.nomor || '',
+ deskripsi: data.deskripsi || '',
+ imageId: data.imageId || '',
+ jadwalPelayanan: data.jadwalPelayanan || '',
+ });
+ if (data?.image?.link) setPreviewImage(data.image.link);
+ }
+ } catch (error) {
+ console.error('Error loading posyandu:', error);
+ toast.error('Gagal memuat data posyandu');
+ }
+ };
+ loadPosyandu();
+ }, [params?.id]);
- useEffect(() => {
- const loadPosyandu = async () => {
- const id = params?.id as string;
- if (!id) return;
+ const handleSubmit = async () => {
+ try {
+ // Update global state hanya saat submit
+ const updatedForm = { ...statePosyandu.edit.form, ...formData };
+ // Upload file jika ada
+ if (file) {
+ const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
+ const uploaded = res.data?.data;
- try {
- const data = await statePosyandu.edit.load(id);
- if (data) {
- setFormData({
- name: data.name || '',
- nomor: data.nomor || '',
- deskripsi: data.deskripsi || '',
- imageId: data.imageId || '',
- jadwalPelayanan: data.jadwalPelayanan || '',
- });
+ if (!uploaded?.id) return toast.error('Gagal upload gambar');
+ updatedForm.imageId = uploaded.id;
+ }
+ statePosyandu.edit.form = updatedForm;
+ await statePosyandu.edit.update();
- if (data?.image?.link) {
- setPreviewImage(data.image.link);
- }
- }
- } catch (error) {
- console.error("Error loading posyandu:", error);
- toast.error("Gagal memuat data posyandu");
- }
- };
- loadPosyandu();
- }, [params?.id]);
+ toast.success('Posyandu berhasil diperbarui!');
+ router.push('/admin/kesehatan/posyandu');
+ } catch (error) {
+ console.error('Error updating posyandu:', error);
+ toast.error('Terjadi kesalahan saat memperbarui posyandu');
+ }
+ };
+ return (
+
+ {/* Tombol Back */}
+
+
+ router.back()} p="xs" radius="md">
+
+
+
+
+ Edit Posyandu
+
+
- const handleSubmit = async () => {
- try {
- statePosyandu.edit.form = {
- ...statePosyandu.edit.form,
- ...formData,
- };
+ {/* Card utama */}
+
+
+ {/* Upload Gambar */}
+
+
+ Gambar Posyandu
+
+ {
+ const selectedFile = files[0];
+ if (selectedFile) {
+ setFile(selectedFile);
+ setPreviewImage(URL.createObjectURL(selectedFile));
+ }
+ }}
+ onReject={() => toast.error('File tidak valid, gunakan format gambar')}
+ maxSize={5 * 1024 ** 2}
+ accept={{ 'image/*': [] }}
+ radius="md"
+ p="xl"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ Seret gambar atau klik untuk memilih file
+
+
+ Maksimal 5MB, format gambar wajib
+
+
+
+
+ {previewImage && (
+
+
+
+ )}
+
- if (file) {
- const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
- const uploaded = res.data?.data;
+ {/* Input Form */}
+ setFormData({ ...formData, name: e.target.value })}
+ required
+ />
+ setFormData({ ...formData, nomor: e.target.value })}
+ required
+ />
- if (!uploaded?.id) {
- return toast.error("Gagal upload gambar");
- }
+
+
+ Deskripsi Posyandu
+
+ setFormData({ ...formData, deskripsi: htmlContent })}
+ />
+
+
+
+ Jadwal Pelayanan
+
+
+ setFormData({ ...formData, jadwalPelayanan: htmlContent })
+ }
+ />
+
- statePosyandu.edit.form.imageId = uploaded.id;
- }
-
-
- await statePosyandu.edit.update();
- toast.success("Posyandu berhasil diperbarui!");
- router.push("/admin/kesehatan/posyandu");
- } catch (error) {
- console.error("Error updating posyandu:", error);
- toast.error("Terjadi kesalahan saat memperbarui posyandu");
- }
- };
-
-
- return (
-
- {/* Tombol Back */}
-
-
- router.back()} p="xs" radius="md">
-
-
-
-
- Edit Posyandu
-
-
-
-
- {/* Card utama */}
-
-
- {/* Upload Gambar */}
-
-
- Gambar Posyandu
-
- {
- const selectedFile = files[0];
- if (selectedFile) {
- setFile(selectedFile);
- setPreviewImage(URL.createObjectURL(selectedFile));
- }
- }}
- onReject={() => toast.error('File tidak valid, gunakan format gambar')}
- maxSize={5 * 1024 ** 2}
- accept={{ 'image/*': [] }}
- radius="md"
- p="xl"
- >
-
-
-
-
-
-
-
-
-
-
-
-
- Seret gambar atau klik untuk memilih file
-
-
- Maksimal 5MB, format gambar wajib
-
-
-
-
-
-
- {previewImage && (
-
-
-
- )}
-
-
-
- {/* Input Form */}
- setFormData({ ...formData, name: e.target.value })}
- required
- />
-
-
- setFormData({ ...formData, nomor: e.target.value })}
- required
- />
-
-
-
- Deskripsi Posyandu
- {
- setFormData({ ...formData, deskripsi: htmlContent });
- statePosyandu.edit.form.deskripsi = htmlContent;
- }}
- />
-
-
-
-
- Jadwal Pelayanan
- {
- setFormData({ ...formData, jadwalPelayanan: htmlContent });
- statePosyandu.edit.form.jadwalPelayanan = htmlContent;
- }}
- />
-
-
-
- {/* Tombol Submit */}
-
-
- Simpan
-
-
-
-
-
- );
+ {/* Tombol Submit */}
+
+
+ Simpan
+
+
+
+
+
+ );
}
-
-export default EditPosyandu;
\ No newline at end of file
+export default EditPosyandu;
diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx
index 3629f800..75af2397 100644
--- a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx
@@ -93,6 +93,7 @@ function DetailPosyandu() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi || '-' }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -103,6 +104,7 @@ function DetailPosyandu() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.jadwalPelayanan || '-' }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx
index ad5047be..9542eefe 100644
--- a/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx
@@ -155,14 +155,14 @@ function CreatePosyandu() {
(statePosyandu.create.form.name = e.target.value)}
required
/>
(statePosyandu.create.form.nomor = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/edit/page.tsx
index 97b20c03..1f0dcbec 100644
--- a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/edit/page.tsx
@@ -1,5 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
-'use client'
+'use client';
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import programKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/program-kesehatan/programKesehatan';
import colors from '@/con/colors';
@@ -31,73 +31,70 @@ function EditProgramKesehatan() {
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
const [formData, setFormData] = useState({
- name: programKesehatanState.edit.form.name || '',
- deskripsiSingkat: programKesehatanState.edit.form.deskripsiSingkat || '',
- deskripsi: programKesehatanState.edit.form.deskripsi || '',
- imageId: programKesehatanState.edit.form.imageId || '',
+ name: '',
+ deskripsiSingkat: '',
+ deskripsi: '',
+ imageId: '',
});
+ // Load data awal
useEffect(() => {
- const loadProgramKesehatan = async () => {
+ const loadData = async () => {
const id = params?.id as string;
if (!id) return;
try {
const data = await programKesehatanState.edit.load(id);
- if (data) {
- setFormData({
- name: data.name || '',
- deskripsiSingkat: data.deskripsiSingkat || '',
- deskripsi: data.deskripsi || '',
- imageId: data.imageId || '',
- });
+ if (!data) return;
- if (data?.image?.link) {
- setPreviewImage(data.image.link);
- }
- }
- } catch (error) {
- console.error('Error loading program kesehatan:', error);
+ setFormData({
+ name: data.name || '',
+ deskripsiSingkat: data.deskripsiSingkat || '',
+ deskripsi: data.deskripsi || '',
+ imageId: data.imageId || '',
+ });
+
+ if (data?.image?.link) setPreviewImage(data.image.link);
+ } catch (err) {
+ console.error(err);
toast.error('Gagal memuat data program kesehatan');
}
};
- loadProgramKesehatan();
+ loadData();
}, [params?.id]);
+ // Handler input controlled
+ const handleChange = (key: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({ ...prev, [key]: value }));
+ };
+
+ // Submit form
const handleSubmit = async () => {
try {
- programKesehatanState.edit.form = {
- ...programKesehatanState.edit.form,
- name: formData.name,
- deskripsiSingkat: formData.deskripsiSingkat,
- deskripsi: formData.deskripsi,
- imageId: formData.imageId,
- };
+ const updatedForm = { ...programKesehatanState.edit.form, ...formData };
+ // Upload file kalau ada
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
+ if (!uploaded?.id) return toast.error('Gagal upload gambar');
- if (!uploaded?.id) {
- return toast.error('Gagal upload gambar');
- }
-
- programKesehatanState.edit.form.imageId = uploaded.id;
+ updatedForm.imageId = uploaded.id;
}
+ programKesehatanState.edit.form = updatedForm;
await programKesehatanState.edit.update();
toast.success('Program kesehatan berhasil diperbarui!');
router.push('/admin/kesehatan/program-kesehatan');
- } catch (error) {
- console.error('Error updating program kesehatan:', error);
+ } catch (err) {
+ console.error(err);
toast.error('Terjadi kesalahan saat memperbarui program kesehatan');
}
};
return (
- {/* Header dengan tombol back */}
router.back()} p="xs" radius="md">
@@ -109,7 +106,6 @@ function EditProgramKesehatan() {
- {/* Card Form */}
- setFormData({ ...formData, name: e.target.value })}
- label="Judul"
- placeholder="Masukkan judul"
- required
- />
+ {[
+ { label: 'Judul', key: 'name', placeholder: 'Masukkan judul' },
+ ].map((field) => (
+ handleChange(field.key as keyof typeof formData, e.target.value)}
+ required
+ />
+ ))}
- setFormData({ ...formData, deskripsiSingkat: e.target.value })}
- label="Deskripsi Singkat"
- placeholder="Masukkan deskripsi singkat"
- required
- />
+
+
+ Deskripsi Singkat
+
+ handleChange('deskripsiSingkat', val)}
+ />
+
@@ -141,7 +144,7 @@ function EditProgramKesehatan() {
setFormData({ ...formData, deskripsi: val })}
+ onChange={(val) => handleChange('deskripsi', val)}
/>
@@ -152,10 +155,9 @@ function EditProgramKesehatan() {
{
const selectedFile = files[0];
- if (selectedFile) {
- setFile(selectedFile);
- setPreviewImage(URL.createObjectURL(selectedFile));
- }
+ if (!selectedFile) return;
+ setFile(selectedFile);
+ setPreviewImage(URL.createObjectURL(selectedFile));
}}
onReject={() => toast.error('File tidak valid.')}
maxSize={5 * 1024 ** 2}
diff --git a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/page.tsx
index aae520f5..5a36bca6 100644
--- a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/page.tsx
@@ -73,12 +73,12 @@ function DetailProgramKesehatan() {
Deskripsi Singkat
- {data?.deskripsiSingkat || '-'}
+
Deskripsi
-
+
diff --git a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/create/page.tsx
index b98f19ed..35d642df 100644
--- a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/create/page.tsx
@@ -92,7 +92,7 @@ function CreateProgramKesehatan() {
>
{
programKesehatanState.create.form.name = val.target.value;
}}
@@ -101,15 +101,17 @@ function CreateProgramKesehatan() {
required
/>
- {
- programKesehatanState.create.form.deskripsiSingkat = val.target.value;
- }}
- label="Deskripsi Singkat"
- placeholder="Masukkan deskripsi singkat"
- required
- />
+
+
+ Deskripsi Singkat
+
+ {
+ programKesehatanState.create.form.deskripsiSingkat = val;
+ }}
+ />
+
diff --git a/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/edit/page.tsx
index 6b37fb05..9dd41e85 100644
--- a/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/edit/page.tsx
@@ -54,20 +54,11 @@ function EditPuskesmas() {
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
const [formData, setFormData] = useState({
- name: statePuskesmas.edit.form.name || '',
- alamat: statePuskesmas.edit.form.alamat || '',
- jam: {
- workDays: statePuskesmas.edit.form.jam?.workDays || '',
- weekDays: statePuskesmas.edit.form.jam?.weekDays || '',
- holiday: statePuskesmas.edit.form.jam?.holiday || '',
- },
- kontak: {
- kontakPuskesmas: statePuskesmas.edit.form.kontak?.kontakPuskesmas || '',
- email: statePuskesmas.edit.form.kontak?.email || '',
- facebook: statePuskesmas.edit.form.kontak?.facebook || '',
- kontakUGD: statePuskesmas.edit.form.kontak?.kontakUGD || '',
- },
- imageId: statePuskesmas.edit.form.imageId || '',
+ name: '',
+ alamat: '',
+ jam: { workDays: '', weekDays: '', holiday: '' },
+ kontak: { kontakPuskesmas: '', email: '', facebook: '', kontakUGD: '' },
+ imageId: '',
});
useEffect(() => {
diff --git a/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/page.tsx
index e2f8bef6..2384ae36 100644
--- a/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/page.tsx
@@ -73,7 +73,7 @@ function DetailPuskesmas() {
Alamat
- {data?.alamat || '-'}
+ {data?.alamat || '-'}
diff --git a/src/app/admin/(dashboard)/kesehatan/puskesmas/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/puskesmas/create/page.tsx
index ff69d499..871b1235 100644
--- a/src/app/admin/(dashboard)/kesehatan/puskesmas/create/page.tsx
+++ b/src/app/admin/(dashboard)/kesehatan/puskesmas/create/page.tsx
@@ -100,40 +100,40 @@ function CreatePuskesmas() {
(statePuskesmas.create.form.name = e.target.value)}
required
/>
(statePuskesmas.create.form.alamat = e.target.value)}
required
/>
(statePuskesmas.create.form.jam.workDays = e.target.value)}
/>
(statePuskesmas.create.form.jam.weekDays = e.target.value)}
/>
(statePuskesmas.create.form.jam.holiday = e.target.value)}
/>
(statePuskesmas.create.form.kontak.kontakPuskesmas = e.target.value)
}
@@ -141,19 +141,19 @@ function CreatePuskesmas() {
(statePuskesmas.create.form.kontak.email = e.target.value)}
/>
(statePuskesmas.create.form.kontak.facebook = e.target.value)}
/>
(statePuskesmas.create.form.kontak.kontakUGD = e.target.value)}
/>
diff --git a/src/app/admin/(dashboard)/landing-page/apbdes/[id]/edit/page.tsx b/src/app/admin/(dashboard)/landing-page/apbdes/[id]/edit/page.tsx
index c9ed189e..4327ad4e 100644
--- a/src/app/admin/(dashboard)/landing-page/apbdes/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/apbdes/[id]/edit/page.tsx
@@ -28,20 +28,21 @@ function EditAPBDes() {
const router = useRouter();
const params = useParams();
+ const [formData, setFormData] = useState({
+ name: '',
+ jumlah: '',
+ imageId: '',
+ fileId: ''
+ });
+
const [previewImage, setPreviewImage] = useState(null);
const [previewDoc, setPreviewDoc] = useState(null);
const [imageFile, setImageFile] = useState(null);
const [docFile, setDocFile] = useState(null);
- const [formData, setFormData] = useState({
- name: apbdesState.edit.form.name || '',
- jumlah: apbdesState.edit.form.jumlah || '',
- imageId: apbdesState.edit.form.imageId || '',
- fileId: apbdesState.edit.form.fileId || ''
- });
- // Load APBDes data by id
+ // Load data on mount
useEffect(() => {
- const loadAPBDes = async () => {
+ const loadData = async () => {
const id = params?.id as string;
if (!id) return;
@@ -54,62 +55,58 @@ function EditAPBDes() {
imageId: data.imageId || '',
fileId: data.fileId || ''
});
-
- if (data.image?.link) setPreviewImage(data.image.link);
- if (data.file?.link) setPreviewDoc(data.file.link);
+ setPreviewImage(data.image?.link || null);
+ setPreviewDoc(data.file?.link || null);
}
- } catch (error) {
- console.error('Error loading APBDes:', error);
+ } catch (err) {
+ console.error(err);
toast.error('Gagal memuat data APBDes');
}
};
- loadAPBDes();
+ loadData();
}, [params?.id]);
+ // Generic Dropzone handler
+ const handleDrop = (fileType: 'image' | 'doc') => (files: File[]) => {
+ const file = files[0];
+ if (!file) return;
+
+ if (fileType === 'image') {
+ setImageFile(file);
+ setPreviewImage(URL.createObjectURL(file));
+ } else {
+ setDocFile(file);
+ setPreviewDoc(URL.createObjectURL(file));
+ }
+ };
+
const handleSubmit = async () => {
try {
- // Update global state with form data
- apbdesState.edit.form = {
- ...apbdesState.edit.form,
- ...formData,
+ // Update global state with local form data first
+ apbdesState.edit.form = { ...apbdesState.edit.form, ...formData };
+
+ // Helper function for uploading file
+ const uploadFile = async (file: File | null) => {
+ if (!file) return null;
+ const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
+ const uploaded = res.data?.data;
+ if (!uploaded?.id) throw new Error('Upload gagal');
+ return uploaded.id;
};
- // Upload new image if exists
- if (imageFile) {
- const res = await ApiFetch.api.fileStorage.create.post({
- file: imageFile,
- name: imageFile.name
- });
- const uploaded = res.data?.data;
+ // Upload files if selected
+ const uploadedImageId = await uploadFile(imageFile);
+ const uploadedDocId = await uploadFile(docFile);
- if (!uploaded?.id) {
- return toast.error('Gagal upload gambar');
- }
-
- apbdesState.edit.form.imageId = uploaded.id;
- }
-
- // Upload new document if exists
- if (docFile) {
- const res = await ApiFetch.api.fileStorage.create.post({
- file: docFile,
- name: docFile.name
- });
- const uploaded = res.data?.data;
-
- if (!uploaded?.id) {
- return toast.error('Gagal upload dokumen');
- }
-
- apbdesState.edit.form.fileId = uploaded.id;
- }
+ if (uploadedImageId) apbdesState.edit.form.imageId = uploadedImageId;
+ if (uploadedDocId) apbdesState.edit.form.fileId = uploadedDocId;
await apbdesState.edit.update();
toast.success('APBDes berhasil diperbarui!');
router.push('/admin/landing-page/apbdes');
- } catch (error) {
- console.error('Error updating APBDes:', error);
+ } catch (err) {
+ console.error(err);
toast.error('Terjadi kesalahan saat memperbarui APBDes');
}
};
@@ -127,15 +124,9 @@ function EditAPBDes() {
-
+
+ {/* Controlled Inputs */}
+ {/* Image Dropzone */}
-
- Gambar APBDes
-
+ Gambar APBDes
{
- const selectedFile = files[0];
- if (selectedFile) {
- setImageFile(selectedFile);
- setPreviewImage(URL.createObjectURL(selectedFile));
- }
- }}
+ onDrop={handleDrop('image')}
onReject={() => toast.error('File tidak valid, gunakan format gambar')}
maxSize={5 * 1024 ** 2}
accept={{ 'image/*': [] }}
@@ -171,57 +155,29 @@ function EditAPBDes() {
p="xl"
>
-
-
-
-
-
-
-
-
-
+
+
+
-
- Seret gambar atau klik untuk memilih file
-
-
- Maksimal 5MB, format gambar wajib
-
+ Seret gambar atau klik untuk memilih file
+ Maksimal 5MB, format gambar wajib
-
{previewImage && (
-
+
)}
+ {/* Document Dropzone */}
-
- Dokumen APBDes
-
+ Dokumen APBDes
{
- const selectedFile = files[0];
- if (selectedFile) {
- setDocFile(selectedFile);
- setPreviewDoc(URL.createObjectURL(selectedFile));
- }
- }}
+ onDrop={handleDrop('doc')}
onReject={() => toast.error('File tidak valid, gunakan format dokumen')}
- maxSize={10 * 1024 ** 2} // 10MB
+ maxSize={10 * 1024 ** 2}
accept={{
'application/pdf': ['.pdf'],
'application/msword': ['.doc'],
@@ -233,40 +189,19 @@ function EditAPBDes() {
p="xl"
>
-
-
-
-
-
-
-
-
-
+
+
+
-
- Seret dokumen atau klik untuk memilih file
-
-
- Maksimal 10MB, format PDF/DOC/DOCX/XLS/XLSX
-
+ Seret dokumen atau klik untuk memilih file
+ Maksimal 10MB, format PDF/DOC/DOCX/XLS/XLSX
-
{previewDoc && (
-
- Dokumen terpilih: {docFile?.name || 'Dokumen'}
-
- }
- size="sm"
- >
+ Dokumen terpilih: {docFile?.name || 'Dokumen'}
+ } size="sm">
Lihat Dokumen
diff --git a/src/app/admin/(dashboard)/landing-page/apbdes/create/page.tsx b/src/app/admin/(dashboard)/landing-page/apbdes/create/page.tsx
index 80af1a3f..1925e0a5 100644
--- a/src/app/admin/(dashboard)/landing-page/apbdes/create/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/apbdes/create/page.tsx
@@ -218,14 +218,14 @@ function CreateAPBDes() {
(stateAPBDes.create.form.name = e.target.value)}
required
/>
(stateAPBDes.create.form.jumlah = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/[id]/page.tsx b/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/[id]/page.tsx
index 5e22c5fe..48af5204 100644
--- a/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/[id]/page.tsx
@@ -1,11 +1,12 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
+
import korupsiState from '@/app/admin/(dashboard)/_state/landing-page/desa-anti-korupsi';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, TextInput, Title, Tooltip } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect, useState } from 'react';
+import { useEffect, useState, useCallback } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
@@ -15,28 +16,25 @@ export default function EditKategoriDesaAntiKorupsi() {
const id = params?.id as string;
const stateKategori = useProxy(korupsiState.kategoriDesaAntiKorupsi);
- const [formData, setFormData] = useState({
- name: "",
- });
+ // state lokal untuk form
+ const [formData, setFormData] = useState({ name: '' });
const [isLoading, setIsLoading] = useState(false);
+ // load data kategori saat mount atau id berubah
useEffect(() => {
+ if (!id) return;
+
const loadKategori = async () => {
- if (!id) return;
setIsLoading(true);
-
try {
const data = await stateKategori.edit.load(id);
-
if (data) {
stateKategori.edit.id = id;
- setFormData({
- name: data.name || '',
- });
+ setFormData({ name: data.name || '' });
}
- } catch (error) {
- console.error("Error loading kategori desa anti korupsi:", error);
- toast.error("Gagal memuat data kategori desa anti korupsi");
+ } catch (err) {
+ console.error(err);
+ toast.error('Gagal memuat data kategori desa anti korupsi');
} finally {
setIsLoading(false);
}
@@ -45,36 +43,40 @@ export default function EditKategoriDesaAntiKorupsi() {
loadKategori();
}, [id]);
- const handleSubmit = async () => {
- if (!formData.name.trim()) {
- return toast.error('Nama kategori tidak boleh kosong');
- }
+ // handler controlled input
+ const handleChange = useCallback(
+ (field: keyof typeof formData, value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ },
+ []
+ );
+
+ // submit form
+ const handleSubmit = useCallback(async () => {
+ if (!formData.name.trim()) return toast.error('Nama kategori tidak boleh kosong');
try {
setIsLoading(true);
- stateKategori.edit.form = {
- name: formData.name.trim(),
- };
- if (!stateKategori.edit.id) {
- stateKategori.edit.id = id;
- }
+ // update global state hanya saat submit
+ stateKategori.edit.form = { name: formData.name.trim() };
+ if (!stateKategori.edit.id) stateKategori.edit.id = id;
await stateKategori.edit.update();
toast.success('Kategori berhasil diperbarui');
- router.push("/admin/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi");
- } catch (error) {
- console.error("Error updating kategori desa anti korupsi:", error);
- toast.error(error instanceof Error ? error.message : 'Gagal memperbarui kategori');
+ router.push('/admin/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi');
+ } catch (err) {
+ console.error(err);
+ toast.error(err instanceof Error ? err.message : 'Gagal memperbarui kategori');
} finally {
setIsLoading(false);
}
- };
+ }, [formData.name, id, router, stateKategori.edit]);
return (
-
+
router.back()} p="xs" radius="md">
@@ -96,8 +98,8 @@ export default function EditKategoriDesaAntiKorupsi() {
setFormData({ ...formData, name: e.target.value })}
+ value={formData.name} // controlled
+ onChange={(e) => handleChange('name', e.currentTarget.value)}
required
disabled={isLoading}
/>
diff --git a/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/create/page.tsx b/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/create/page.tsx
index 2ecb3429..15e3645c 100644
--- a/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/create/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/create/page.tsx
@@ -57,7 +57,7 @@ export default function CreateKategoriDesaAntiKorupsi() {
(stateKategori.create.form.name = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/[id]/edit/page.tsx b/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/[id]/edit/page.tsx
index 85592ca7..d60eb719 100644
--- a/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/[id]/edit/page.tsx
@@ -1,18 +1,19 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
+
import { Box, Button, Group, Paper, Select, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
import { IconArrowBack, IconFile, IconUpload, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect, useState } from 'react';
+import { useEffect, useState, ChangeEvent } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
+import { Dropzone } from '@mantine/dropzone';
+import { useShallowEffect } from '@mantine/hooks';
import colors from '@/con/colors';
import korupsiState from '@/app/admin/(dashboard)/_state/landing-page/desa-anti-korupsi';
import ApiFetch from '@/lib/api-fetch';
-import { Dropzone } from '@mantine/dropzone';
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
-import { useShallowEffect } from '@mantine/hooks';
interface FormDesaAntiKorupsi {
name: string;
@@ -23,12 +24,9 @@ interface FormDesaAntiKorupsi {
export default function EditDesaAntiKorupsi() {
const desaAntiKorupsiState = useProxy(korupsiState.desaAntikorupsi);
- const [previewFile, setPreviewFile] = useState(null);
- const [file, setFile] = useState(null);
- const [isLoading, setIsLoading] = useState(false);
const params = useParams();
const router = useRouter();
-
+
const [formData, setFormData] = useState({
name: '',
deskripsi: '',
@@ -36,10 +34,16 @@ export default function EditDesaAntiKorupsi() {
fileId: '',
});
+ const [previewFile, setPreviewFile] = useState(null);
+ const [file, setFile] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+
+ // Load kategori
useShallowEffect(() => {
korupsiState.kategoriDesaAntiKorupsi.findMany.load();
}, []);
+ // Load data existing
useEffect(() => {
const loadDesaAntiKorupsi = async () => {
const id = params?.id as string;
@@ -47,29 +51,21 @@ export default function EditDesaAntiKorupsi() {
try {
const data = await desaAntiKorupsiState.edit.load(id);
- if (data) {
- desaAntiKorupsiState.edit.id = id;
+ if (!data) return;
- desaAntiKorupsiState.edit.form = {
- name: data.name,
- deskripsi: data.deskripsi,
- kategoriId: data.kategoriId,
- fileId: data.fileId,
- };
+ desaAntiKorupsiState.edit.id = id;
+ desaAntiKorupsiState.edit.form = { ...data };
- setFormData({
- name: data.name,
- deskripsi: data.deskripsi,
- kategoriId: data.kategoriId,
- fileId: data.fileId,
- });
+ setFormData({
+ name: data.name,
+ deskripsi: data.deskripsi,
+ kategoriId: data.kategoriId,
+ fileId: data.fileId,
+ });
- if (data?.file?.link) {
- setPreviewFile(data.file.link);
- }
- }
- } catch (error) {
- console.error('Error loading data:', error);
+ if (data.file?.link) setPreviewFile(data.file.link);
+ } catch (err) {
+ console.error(err);
toast.error('Gagal memuat data Desa Anti Korupsi');
}
};
@@ -77,42 +73,47 @@ export default function EditDesaAntiKorupsi() {
loadDesaAntiKorupsi();
}, [params?.id]);
+ // Generic handler input
+ const handleInputChange = (key: keyof FormDesaAntiKorupsi) => (e: ChangeEvent | string | null) => {
+ const value = typeof e === 'string' ? e : e?.target?.value ?? '';
+ setFormData((prev) => ({ ...prev, [key]: value || '' }));
+ };
+
+ // Special handler for Select component
+ const handleSelectChange = (key: keyof FormDesaAntiKorupsi) => (value: string | null) => {
+ setFormData((prev) => ({ ...prev, [key]: value || '' }));
+ };
+
+ const handleDrop = (files: File[]) => {
+ if (!files.length) return;
+ const selectedFile = files[0];
+ setFile(selectedFile);
+ setPreviewFile(URL.createObjectURL(selectedFile));
+ };
+
const handleSubmit = async () => {
- if (!formData.name) {
- return toast.warn('Masukkan judul dokumen');
- }
- if (!formData.kategoriId) {
- return toast.warn('Pilih kategori dokumen');
- }
+ if (!formData.name) return toast.warn('Masukkan judul dokumen');
+ if (!formData.kategoriId) return toast.warn('Pilih kategori dokumen');
setIsLoading(true);
try {
- // Update global state with form data
- desaAntiKorupsiState.edit.form = {
- ...desaAntiKorupsiState.edit.form,
- ...formData,
- kategoriId: formData.kategoriId || '',
- };
+ // Update global state
+ desaAntiKorupsiState.edit.form = { ...desaAntiKorupsiState.edit.form, ...formData };
- // Upload new file if exists
+ // Upload file jika ada
if (file) {
- const res = await ApiFetch.api.fileStorage.create.post({
- file,
- name: file.name
- });
+ const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
+ if (!uploaded?.id) throw new Error('Gagal mengunggah dokumen');
- if (!uploaded?.id) {
- throw new Error('Gagal mengunggah dokumen');
- }
desaAntiKorupsiState.edit.form.fileId = uploaded.id;
}
await desaAntiKorupsiState.edit.update();
toast.success('Data berhasil diperbarui');
router.push('/admin/landing-page/desa-anti-korupsi/list-desa-anti-korupsi');
- } catch (error) {
- console.error('Error updating data:', error);
+ } catch (err) {
+ console.error(err);
toast.error('Terjadi kesalahan saat memperbarui data');
} finally {
setIsLoading(false);
@@ -145,7 +146,7 @@ export default function EditDesaAntiKorupsi() {
label="Judul Dokumen"
placeholder="Masukkan judul dokumen"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={handleInputChange('name')}
required
/>
@@ -153,23 +154,18 @@ export default function EditDesaAntiKorupsi() {
Deskripsi
- setFormData({ ...formData, deskripsi: val })}
- />
+
setFormData({ ...formData, kategoriId: val || '' })}
- data={
- korupsiState.kategoriDesaAntiKorupsi.findMany.data?.map((v) => ({
- value: v.id,
- label: v.name,
- })) || []
- }
+ onChange={handleSelectChange('kategoriId')}
+ data={korupsiState.kategoriDesaAntiKorupsi.findMany.data?.map((v) => ({
+ value: v.id,
+ label: v.name,
+ })) || []}
required
searchable
clearable
@@ -180,13 +176,7 @@ export default function EditDesaAntiKorupsi() {
Dokumen
{
- const selectedFile = files[0];
- if (selectedFile) {
- setFile(selectedFile);
- setPreviewFile(URL.createObjectURL(selectedFile));
- }
- }}
+ onDrop={handleDrop}
onReject={() => toast.error('File tidak valid, gunakan format dokumen')}
maxSize={5 * 1024 ** 2}
accept={{
@@ -229,12 +219,7 @@ export default function EditDesaAntiKorupsi() {
width: '100%',
}}
>
-
+
)}
@@ -259,4 +244,4 @@ export default function EditDesaAntiKorupsi() {
);
-}
\ No newline at end of file
+}
diff --git a/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/[id]/page.tsx b/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/[id]/page.tsx
index d72d0ac3..919eb39d 100644
--- a/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/[id]/page.tsx
@@ -83,7 +83,8 @@ export default function DetailKegiatanDesa() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi || '-' }}
- style={{ lineHeight: 1.6 }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal", lineHeight: 1.6 }}
+
/>
diff --git a/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/create/page.tsx b/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/create/page.tsx
index ae196e20..1756842b 100644
--- a/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/create/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/create/page.tsx
@@ -165,7 +165,7 @@ export default function CreateDesaAntiKorupsi() {
(stateKorupsi.create.form.name = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/[id]/edit/page.tsx b/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/[id]/edit/page.tsx
index 58e82afc..98785c88 100644
--- a/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/[id]/edit/page.tsx
@@ -1,10 +1,21 @@
'use client'
/* eslint-disable react-hooks/exhaustive-deps */
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useState, useCallback } from 'react';
import { useRouter, useParams } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import colors from '@/con/colors';
-import { Box, Button, Group, Paper, Stack, Title, TextInput, Text, Select, Tooltip } from '@mantine/core';
+import {
+ Box,
+ Button,
+ Group,
+ Paper,
+ Stack,
+ Title,
+ TextInput,
+ Text,
+ Select,
+ Tooltip,
+} from '@mantine/core';
import { IconArrowBack, IconDeviceFloppy } from '@tabler/icons-react';
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan';
import { toast } from 'react-toastify';
@@ -18,64 +29,93 @@ interface FormResponden {
}
function EditResponden() {
- const router = useRouter()
- const params = useParams() as { id: string }
- const state = useProxy(indeksKepuasanState.responden)
- const id = params.id
+ const router = useRouter();
+ const params = useParams() as { id: string };
+ const state = useProxy(indeksKepuasanState.responden);
+ const id = params.id;
+
const [formData, setFormData] = useState({
name: '',
tanggal: '',
jenisKelaminId: '',
ratingId: '',
kelompokUmurId: '',
- })
- useEffect(() => {
+ });
+
+ // Helper untuk load pilihan select
+ const loadSelectOptions = useCallback(() => {
indeksKepuasanState.jenisKelaminResponden.findMany.load();
indeksKepuasanState.pilihanRatingResponden.findMany.load();
indeksKepuasanState.kelompokUmurResponden.findMany.load();
+ }, []);
- const loadResponden = async () => {
- const id = params?.id as string;
- if (!id) return;
+ // Load data responden
+ const loadResponden = useCallback(async () => {
+ if (!id) return;
- try {
- const data = await state.update.load(id);
- if (data) {
- state.update.id = id;
+ try {
+ const data = await state.update.load(id);
+ if (!data) return;
- state.update.form = {
- name: data.name,
- tanggal: data.tanggal,
- jenisKelaminId: data.jenisKelaminId,
- ratingId: data.ratingId,
- kelompokUmurId: data.kelompokUmurId,
- };
-
- setFormData({
- name: data.name,
- tanggal: data.tanggal,
- jenisKelaminId: data.jenisKelaminId,
- ratingId: data.ratingId,
- kelompokUmurId: data.kelompokUmurId,
- });
-
-
- }
- } catch (error) {
- console.error("Error loading program penghijauan:", error);
- toast.error("Gagal memuat data program penghijauan");
- }
+ setFormData({
+ name: data.name,
+ tanggal: data.tanggal,
+ jenisKelaminId: data.jenisKelaminId,
+ ratingId: data.ratingId,
+ kelompokUmurId: data.kelompokUmurId,
+ });
+ } catch (error) {
+ console.error("Error loading responden:", error);
+ toast.error("Gagal memuat data responden");
}
+ }, [id]);
+ useEffect(() => {
+ loadSelectOptions();
loadResponden();
- }, [params?.id]);
+ }, [loadSelectOptions, loadResponden]);
const handleSubmit = async () => {
state.update.id = id;
- state.update.form = { ...formData }; // <-- sinkronisasi manual
+ state.update.form = { ...formData }; // sinkronisasi manual
await state.update.submit();
- router.push('/admin/landing-page/indeks-kepuasan-masyarakat/responden')
- }
+ router.push('/admin/landing-page/indeks-kepuasan-masyarakat/responden');
+ };
+
+ // Reusable Select component
+ const ControlledSelect = ({
+ label,
+ value,
+ onChange,
+ options,
+ error,
+ placeholder = 'Pilih',
+ loading = false,
+ }: {
+ label: string;
+ value: string;
+ onChange: (val: string) => void;
+ options: { value: string; label: string }[];
+ error?: string;
+ placeholder?: string;
+ loading?: boolean;
+ }) => {
+ return (
+ {label}}
+ value={value}
+ onChange={(val) => onChange(val || '')}
+ data={options}
+ placeholder={placeholder}
+ disabled={loading}
+ clearable
+ searchable
+ required
+ radius="md"
+ error={error}
+ />
+ );
+ };
return (
@@ -100,125 +140,62 @@ function EditResponden() {
>
- Nama Responden
-
- }
- type='text'
+ label="Nama Responden"
placeholder="Masukkan nama responden"
value={formData.name}
- onChange={(val) => {
- setFormData({
- ...formData,
- name: val.currentTarget.value
- })
- }}
+ onChange={(e) => setFormData({ ...formData, name: e.currentTarget.value })}
radius="md"
required
/>
- Tanggal
-
- }
+ label="Tanggal"
type="date"
- placeholder='Pilih tanggal'
value={formData.tanggal ? new Date(formData.tanggal).toISOString().split('T')[0] : ''}
- onChange={(e) => {
- const selectedDate = e.currentTarget.value;
- setFormData({
- ...formData,
- tanggal: selectedDate,
- });
- }}
+ onChange={(e) => setFormData({ ...formData, tanggal: e.currentTarget.value })}
radius="md"
required
/>
-
- Jenis Kelamin
-
- }
- placeholder="Pilih jenis kelamin"
- value={formData.jenisKelaminId}
- onChange={(val) => setFormData({ ...formData, jenisKelaminId: val || "" })}
- data={
- (indeksKepuasanState.jenisKelaminResponden.findMany.data || [])
- .filter(Boolean)
- .map((v) => ({
- value: v.id || '',
- label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
- }))
- }
- disabled={indeksKepuasanState.jenisKelaminResponden.findMany.loading}
- clearable
- searchable
- required
- radius="md"
- error={!formData.jenisKelaminId ? "Pilih jenis kelamin" : undefined}
- />
- setFormData({ ...formData, ratingId: val || "" })}
- label={
-
- Rating
-
- }
- placeholder='Pilih rating'
- data={
- (indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
- .filter(Boolean)
- .map((v) => ({
- value: v.id || '',
- label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
- }))
- }
- disabled={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
- clearable
- searchable
- required
- radius="md"
- error={!formData.ratingId ? "Pilih rating" : undefined}
- />
- setFormData({ ...formData, kelompokUmurId: val || "" })}
- label={Kelompok Umur }
- placeholder='Pilih kelompok umur'
- data={
- (indeksKepuasanState.kelompokUmurResponden.findMany.data || [])
- .filter(Boolean)
- .map((v) => ({
- value: v.id || '',
- label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
- }))
- }
- disabled={indeksKepuasanState.kelompokUmurResponden.findMany.loading}
- clearable
- searchable
- required
- error={!formData.kelompokUmurId ? "Pilih kelompok umur" : undefined}
+ setFormData({ ...formData, jenisKelaminId: val })}
+ options={(indeksKepuasanState.jenisKelaminResponden.findMany.data || [])
+ .filter(Boolean)
+ .map((v) => ({ value: v.id || '', label: v.name || 'Tanpa Nama' }))}
+ loading={indeksKepuasanState.jenisKelaminResponden.findMany.loading}
+ error={!formData.jenisKelaminId ? 'Pilih jenis kelamin' : undefined}
/>
-
+
+ setFormData({ ...formData, ratingId: val })}
+ options={(indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
+ .filter(Boolean)
+ .map((v) => ({ value: v.id || '', label: v.name || 'Tanpa Nama' }))}
+ loading={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
+ error={!formData.ratingId ? 'Pilih rating' : undefined}
+ />
+
+ setFormData({ ...formData, kelompokUmurId: val })}
+ options={(indeksKepuasanState.kelompokUmurResponden.findMany.data || [])
+ .filter(Boolean)
+ .map((v) => ({ value: v.id || '', label: v.name || 'Tanpa Nama' }))}
+ loading={indeksKepuasanState.kelompokUmurResponden.findMany.loading}
+ error={!formData.kelompokUmurId ? 'Pilih kelompok umur' : undefined}
+ />
+
- router.back()}
- >
+ router.back()}>
Batal
- }
- onClick={handleSubmit}
+ onClick={handleSubmit}
loading={state.update.loading}
color={colors['blue-button']}
>
diff --git a/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/[id]/page.tsx b/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/[id]/page.tsx
index e82733d0..7bb65dc6 100644
--- a/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/[id]/page.tsx
@@ -26,7 +26,7 @@ export default function DetailResponden() {
stateDetail.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
- router.push("/admin/ppid/ikm-desa-darmasaba/responden")
+ router.push("/admin/landing-page/indeks-kepuasan-masyarakat/responden")
}
}
diff --git a/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/create/page.tsx b/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/create/page.tsx
index 9feb87e7..7056c869 100644
--- a/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/create/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/indeks-kepuasan-masyarakat/responden/create/page.tsx
@@ -64,7 +64,7 @@ function RespondenCreate() {
label="Nama"
type='text'
placeholder="masukkan nama"
- value={stategrafikBerdasarkanResponden.create.form.name}
+ defaultValue={stategrafikBerdasarkanResponden.create.form.name}
onChange={(val) => {
stategrafikBerdasarkanResponden.create.form.name = val.currentTarget.value;
}}
@@ -73,7 +73,7 @@ function RespondenCreate() {
label="Tanggal"
type="date"
placeholder="masukkan tanggal"
- value={stategrafikBerdasarkanResponden.create.form.tanggal}
+ defaultValue={stategrafikBerdasarkanResponden.create.form.tanggal}
onChange={(val) => {
stategrafikBerdasarkanResponden.create.form.tanggal = val.currentTarget.value;
}}
diff --git a/src/app/admin/(dashboard)/landing-page/prestasi-desa/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/landing-page/prestasi-desa/_lib/layoutTabs.tsx
index ce64fdd6..53da55c1 100644
--- a/src/app/admin/(dashboard)/landing-page/prestasi-desa/_lib/layoutTabs.tsx
+++ b/src/app/admin/(dashboard)/landing-page/prestasi-desa/_lib/layoutTabs.tsx
@@ -2,7 +2,7 @@
'use client'
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
-import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
+import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
import { IconCategory, IconListDetails } from '@tabler/icons-react';
import colors from '@/con/colors';
@@ -58,6 +58,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
radius="lg"
keepMounted={false}
>
+
))}
+
{tabs.map((tab, i) => (
{
- const loadKategoriprestasi = async () => {
- if (!id) return;
+ if (!id) return;
+ const loadKategori = async () => {
+ setLoading(true);
try {
const data = await stateKategori.edit.load(id);
-
if (data) {
- // pastikan id-nya masuk ke state edit
stateKategori.edit.id = id;
- setFormData({
- name: data.name || '',
- });
+ setFormData({ name: data.name || '' });
}
- } catch (error) {
- console.error("Error loading kategori prestasi desa:", error);
- toast.error("Gagal memuat data kategori prestasi desa");
+ } catch (err) {
+ console.error(err);
+ toast.error('Gagal memuat data kategori prestasi desa');
+ } finally {
+ setLoading(false);
}
};
- loadKategoriprestasi();
+ loadKategori();
}, [id]);
+ // Submit: update global state hanya saat submit
const handleSubmit = async () => {
+ if (!formData.name.trim()) {
+ toast.error('Nama kategori prestasi tidak boleh kosong');
+ return;
+ }
+
try {
- if (!formData.name.trim()) {
- toast.error('Nama kategori prestasi tidak boleh kosong');
- return;
- }
-
- stateKategori.edit.form = {
- name: formData.name.trim(),
- };
-
- // Safety check tambahan: pastikan ID tidak kosong
- if (!stateKategori.edit.id) {
- stateKategori.edit.id = id; // fallback
- }
+ stateKategori.edit.form = { name: formData.name.trim() };
+ stateKategori.edit.id ||= id; // fallback jika id belum ada
const success = await stateKategori.edit.update();
-
if (success) {
- router.push("/admin/landing-page/prestasi-desa/kategori-prestasi-desa");
+ toast.success('Kategori prestasi berhasil diperbarui');
+ router.push('/admin/landing-page/prestasi-desa/kategori-prestasi-desa');
}
- } catch (error) {
- console.error("Error updating kategori prestasi desa:", error);
- // toast akan ditampilkan dari fungsi update
+ } catch (err) {
+ console.error(err);
+ toast.error('Gagal memperbarui kategori prestasi desa');
}
};
@@ -97,6 +92,7 @@ function EditKategoriPrestasi() {
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required
+ disabled={loading}
/>
@@ -104,6 +100,7 @@ function EditKategoriPrestasi() {
onClick={handleSubmit}
radius="md"
size="md"
+ loading={loading}
style={{
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
color: '#fff',
diff --git a/src/app/admin/(dashboard)/landing-page/prestasi-desa/kategori-prestasi-desa/create/page.tsx b/src/app/admin/(dashboard)/landing-page/prestasi-desa/kategori-prestasi-desa/create/page.tsx
index da45b56b..c03a0ba6 100644
--- a/src/app/admin/(dashboard)/landing-page/prestasi-desa/kategori-prestasi-desa/create/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/prestasi-desa/kategori-prestasi-desa/create/page.tsx
@@ -54,7 +54,7 @@ function CreateKategoriPrestasi() {
(stateKategori.create.form.name = val.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/[id]/edit/page.tsx
index 4ba12632..b68990ac 100644
--- a/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/[id]/edit/page.tsx
@@ -1,19 +1,17 @@
/* eslint-disable react-hooks/exhaustive-deps */
-'use client'
+'use client';
import { useProxy } from 'valtio/utils';
-
-import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
-import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
-import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
+import { useParams, useRouter } from 'next/navigation';
+import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
+import { Dropzone } from '@mantine/dropzone';
+import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
+import { toast } from 'react-toastify';
import colors from '@/con/colors';
-
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import prestasiState from '@/app/admin/(dashboard)/_state/landing-page/prestasi-desa';
import ApiFetch from '@/lib/api-fetch';
-import { Dropzone } from '@mantine/dropzone';
-import { toast } from 'react-toastify';
interface FormPrestasiDesa {
name: string;
@@ -22,31 +20,31 @@ interface FormPrestasiDesa {
imageId: string;
}
-function EditPrestasiDesa() {
- const editState = useProxy(prestasiState.prestasiDesa)
- const [previewFile, setPreviewFile] = useState(null);
- const [file, setFile] = useState(null);
- const params = useParams()
- const router = useRouter()
+export default function EditPrestasiDesa() {
+ const editState = useProxy(prestasiState.prestasiDesa);
const [formData, setFormData] = useState({
name: '',
deskripsi: '',
kategoriId: '',
imageId: '',
- })
+ });
+ const [previewFile, setPreviewFile] = useState(null);
+ const [file, setFile] = useState(null);
+ const params = useParams();
+ const router = useRouter();
+ // Load kategori & prestasi desa
useEffect(() => {
- prestasiState.kategoriPrestasi.findMany.load()
- const loadDesaAntiKorupsi = async () => {
+ prestasiState.kategoriPrestasi.findMany.load();
+
+ const loadPrestasi = async () => {
const id = params?.id as string;
if (!id) return;
try {
const data = await editState.edit.load(id);
if (data) {
- // β¬οΈ FIX PENTING: tambahkan ini
editState.edit.id = id;
-
editState.edit.form = {
name: data.name,
deskripsi: data.deskripsi,
@@ -61,51 +59,37 @@ function EditPrestasiDesa() {
imageId: data.imageId,
});
- if (data?.image?.link) {
- setPreviewFile(data.image.link)
- }
+ if (data.image?.link) setPreviewFile(data.image.link);
}
} catch (error) {
- console.error("Error loading prestasi desa:", error);
- toast.error("Gagal memuat data prestasi desa");
+ console.error('Error loading prestasi desa:', error);
+ toast.error('Gagal memuat data prestasi desa');
}
- }
+ };
- loadDesaAntiKorupsi();
+ loadPrestasi();
}, [params?.id]);
-
const handleSubmit = async () => {
-
try {
- // Update global state with form data
- editState.edit.form = {
- ...editState.edit.form,
- name: formData.name,
- deskripsi: formData.deskripsi,
- kategoriId: formData.kategoriId || '',
- imageId: formData.imageId // Keep existing imageId if not changed
- };
-
- // Jika ada file baru, upload
+ // Jika ada file baru, upload dulu
+ let imageId = formData.imageId;
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
-
- if (!uploaded?.id) {
- return toast.error("Gagal upload gambar");
- }
-
- // Update imageId in global state
- editState.edit.form.imageId = uploaded.id;
+ if (!uploaded?.id) return toast.error('Gagal upload gambar');
+ imageId = uploaded.id;
}
+ // Update global state sekaligus
+ editState.edit.form = { ...formData, imageId };
await editState.edit.update();
- toast.success("prestasi desa berhasil diperbarui!");
- router.push("/admin/landing-page/prestasi-desa/list-prestasi-desa");
+
+ toast.success('Prestasi desa berhasil diperbarui!');
+ router.push('/admin/landing-page/prestasi-desa/list-prestasi-desa');
} catch (error) {
- console.error("Error updating prestasi desa:", error);
- toast.error("Terjadi kesalahan saat memperbarui prestasi desa");
+ console.error('Error updating prestasi desa:', error);
+ toast.error('Terjadi kesalahan saat memperbarui prestasi desa');
}
};
@@ -117,71 +101,35 @@ function EditPrestasiDesa() {
-
- Edit Prestasi Desa
-
+ Edit Prestasi Desa
-
+
{
- setFormData({
- ...formData,
- name: val.target.value
- });
- }}
+ onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required
/>
-
- Deskripsi
-
- {
- setFormData({
- ...formData,
- deskripsi: val
- });
- }}
- />
+ Deskripsi
+ setFormData({ ...formData, deskripsi: val })} />
{
- setFormData({
- ...formData,
- kategoriId: val ?? ""
- });
- }}
- data={
- prestasiState.kategoriPrestasi.findMany.data?.map((v) => ({
- value: v.id,
- label: v.name,
- })) || []
- }
+ onChange={(val) => setFormData({ ...formData, kategoriId: val ?? '' })}
+ data={prestasiState.kategoriPrestasi.findMany.data?.map((v) => ({ value: v.id, label: v.name })) || []}
required
/>
-
- Gambar Prestasi
-
+ Gambar Prestasi
{
const selectedFile = files[0];
@@ -197,22 +145,12 @@ function EditPrestasiDesa() {
p="xl"
>
-
-
-
-
-
-
-
-
-
+
+
+
-
- Seret gambar atau klik untuk memilih file
-
-
- Maksimal 5MB, format gambar wajib
-
+ Seret gambar atau klik untuk memilih file
+ Maksimal 5MB, format gambar wajib
@@ -224,7 +162,7 @@ function EditPrestasiDesa() {
alt="Preview Gambar"
radius="md"
style={{ maxHeight: 220, objectFit: 'contain', border: `1px solid ${colors['blue-button']}` }}
- loading='lazy'
+ loading="lazy"
/>
)}
@@ -249,6 +187,3 @@ function EditPrestasiDesa() {
);
}
-
-
-export default EditPrestasiDesa;
\ No newline at end of file
diff --git a/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/[id]/page.tsx b/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/[id]/page.tsx
index 97f0f67a..d7c3ecba 100644
--- a/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/[id]/page.tsx
@@ -82,6 +82,7 @@ function DetailPrestasiDesa() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: detailState.findUnique.data?.deskripsi || '-' }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/create/page.tsx b/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/create/page.tsx
index 25025d19..8462f93d 100644
--- a/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/create/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/prestasi-desa/list-prestasi-desa/create/page.tsx
@@ -141,7 +141,7 @@ function CreatePrestasiDesa() {
(stateCreate.create.form.name = e.target.value)}
required
/>
@@ -170,12 +170,11 @@ function CreatePrestasiDesa() {
required
/>
-
+
router.back()}
- style={{ marginRight: 'auto' }}
>
Batal
diff --git a/src/app/admin/(dashboard)/landing-page/profile/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/landing-page/profile/_lib/layoutTabs.tsx
index d2dd5ab9..24595d4b 100644
--- a/src/app/admin/(dashboard)/landing-page/profile/_lib/layoutTabs.tsx
+++ b/src/app/admin/(dashboard)/landing-page/profile/_lib/layoutTabs.tsx
@@ -66,7 +66,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
return (
- Profil Desa
+ Profile Desa
(null);
const [file, setFile] = useState(null);
const [formData, setFormData] = useState({
- name: stateMediaSosial.update.form.name || '',
- iconUrl: stateMediaSosial.update.form.iconUrl || '',
- imageId: stateMediaSosial.update.form.imageId || '',
+ name: '',
+ iconUrl: '',
+ imageId: '',
});
+ // Load data by ID
useEffect(() => {
const id = params?.id as string;
if (!id) return;
- const loadMediaSosial = async () => {
+ const loadData = async () => {
try {
const data = await stateMediaSosial.update.load(id);
@@ -59,11 +60,16 @@ function EditMediaSosial() {
}
};
- loadMediaSosial();
+ loadData();
}, [params?.id]);
+ const handleChange = (field: string, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
+ // update global state hanya saat submit
stateMediaSosial.update.form = { ...stateMediaSosial.update.form, ...formData };
if (file) {
@@ -85,7 +91,10 @@ function EditMediaSosial() {
};
return (
-
+
router.back()} p="xs" radius="md">
@@ -106,6 +115,7 @@ function EditMediaSosial() {
style={{ border: '1px solid #e0e0e0' }}
>
+ {/* Upload Gambar */}
Gambar Media Sosial
@@ -151,26 +161,32 @@ function EditMediaSosial() {
src={previewImage}
alt="Preview Gambar"
radius="md"
- style={{ maxHeight: 220, objectFit: 'contain', border: `1px solid ${colors['blue-button']}` }}
+ style={{
+ maxHeight: 220,
+ objectFit: 'contain',
+ border: `1px solid ${colors['blue-button']}`,
+ }}
loading="lazy"
/>
)}
+ {/* Nama Media Sosial */}
setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleChange('name', e.target.value)}
required
/>
+ {/* Link Media Sosial */}
setFormData({ ...formData, iconUrl: e.target.value })}
+ onChange={(e) => handleChange('iconUrl', e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/landing-page/profile/media-sosial/[id]/page.tsx b/src/app/admin/(dashboard)/landing-page/profile/media-sosial/[id]/page.tsx
index 71597f9d..c1ed2c0b 100644
--- a/src/app/admin/(dashboard)/landing-page/profile/media-sosial/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/profile/media-sosial/[id]/page.tsx
@@ -52,9 +52,7 @@ function DetailMediaSosial() {
(stateMediaSosial.create.form.name = e.target.value)}
required
/>
(stateMediaSosial.create.form.iconUrl = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/landing-page/profile/pejabat-desa/[id]/page.tsx b/src/app/admin/(dashboard)/landing-page/profile/pejabat-desa/[id]/page.tsx
index c02906ee..9b7c4a70 100644
--- a/src/app/admin/(dashboard)/landing-page/profile/pejabat-desa/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/profile/pejabat-desa/[id]/page.tsx
@@ -1,7 +1,10 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
+
import colors from '@/con/colors';
-import { Alert, Box, Button, Center, Group, Image, Paper, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
+import {
+ Alert, Box, Button, Center, Group, Image,
+ Paper, Stack, Text, TextInput, Title, Tooltip
+} from '@mantine/core';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
@@ -17,7 +20,14 @@ function EditPejabatDesa() {
const params = useParams();
const router = useRouter();
- // UI States
+ // Local form state
+ const [formData, setFormData] = useState({
+ name: '',
+ position: '',
+ imageId: null as string | null,
+ });
+
+ // UI states
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
@@ -34,11 +44,24 @@ function EditPejabatDesa() {
try {
const profileData = await profileLandingPageState.pejabatDesa.findUnique.load(id);
- profileLandingPageState.pejabatDesa.edit.initialize(profileData);
+ if (profileData) {
+ // Initialize form data
+ setFormData({
+ name: profileData.name || '',
+ position: profileData.position || '',
+ imageId: profileData.imageId || null,
+ });
- if (profileData && profileData.image?.link) {
- setPreviewImage(profileData.image.link);
+ // Initialize edit state with profile data
+ profileLandingPageState.pejabatDesa.edit.initialize({
+ ...profileData,
+ imageId: profileData.imageId || ''
+ });
+
+ if (profileData.image?.link) {
+ setPreviewImage(profileData.image.link);
+ }
}
} catch (error) {
console.error("Error loading profile:", error);
@@ -47,16 +70,15 @@ function EditPejabatDesa() {
};
loadData();
-
- return () => {
- profileLandingPageState.pejabatDesa.edit.reset(); // cleanup form
- };
+ return () => profileLandingPageState.pejabatDesa.edit.reset();
}, [params?.id, router]);
- const handleFieldChange = (field: string, value: string) => {
- profileLandingPageState.pejabatDesa.edit.updateField(field as any, value);
+ // Handle input change
+ const handleChange = (field: string, value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
};
+ // Handle file change
const handleFileChange = (newFile: File | null) => {
if (!newFile) {
setFile(null);
@@ -72,15 +94,17 @@ function EditPejabatDesa() {
reader.readAsDataURL(newFile);
};
+ // Submit form
const handleSubmit = async () => {
- if (isSubmitting || !profileLandingPageState.pejabatDesa.edit.form.name.trim()) {
+ if (isSubmitting || !formData.name.trim()) {
toast.error("Nama wajib diisi");
return;
}
setIsSubmitting(true);
-
try {
+ let imageId = formData.imageId;
+
// Upload file jika ada
if (file) {
const uploadResponse = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
@@ -90,13 +114,23 @@ function EditPejabatDesa() {
toast.error("Gagal upload gambar");
return;
}
-
- profileLandingPageState.pejabatDesa.edit.form.imageId = uploaded.id;
+ imageId = uploaded.id;
}
- // Submit form
- const success = await profileLandingPageState.pejabatDesa.edit.submit();
+ // Ensure we have the ID from the URL
+ const id = params?.id as string;
+ if (!id) {
+ throw new Error("ID tidak ditemukan");
+ }
+ // Update global state with the ID and form data
+ profileLandingPageState.pejabatDesa.edit.id = id;
+ profileLandingPageState.pejabatDesa.edit.form = {
+ ...formData,
+ imageId: imageId || '', // Ensure imageId is always a string
+ };
+
+ const success = await profileLandingPageState.pejabatDesa.edit.submit();
if (success) {
toast.success("Berhasil menyimpan perubahan");
router.push("/admin/landing-page/profile/pejabat-desa");
@@ -109,11 +143,9 @@ function EditPejabatDesa() {
}
};
- const handleBack = () => {
- router.back();
- };
+ const handleBack = () => router.back();
- // Loading state
+ // Loading
if (allState.edit.loading) {
return (
@@ -124,7 +156,7 @@ function EditPejabatDesa() {
);
}
- // Error state
+ // Error
if (allState.edit.error) {
return (
@@ -146,7 +178,7 @@ function EditPejabatDesa() {
- router.back()} p="xs" radius="md">
+
@@ -170,74 +202,70 @@ function EditPejabatDesa() {
Nama Perbekel}
placeholder="Masukkan nama perbekel"
- value={allState.edit.form.name}
- onChange={(e) => handleFieldChange('name', e.currentTarget.value)}
- error={!allState.edit.form.name && "Nama wajib diisi"}
+ value={formData.name}
+ onChange={(e) => handleChange('name', e.currentTarget.value)}
+ error={!formData.name && "Nama wajib diisi"}
/>
{/* Posisi Field */}
Posisi}
placeholder="Masukkan posisi"
- value={allState.edit.form.position}
- onChange={(e) => handleFieldChange('position', e.currentTarget.value)}
- error={!allState.edit.form.position && "Posisi wajib diisi"}
+ value={formData.position}
+ onChange={(e) => handleChange('position', e.currentTarget.value)}
+ error={!formData.position && "Posisi wajib diisi"}
/>
{/* File Upload */}
Gambar
-
- handleFileChange(files[0])}
- onReject={() => toast.error('File tidak valid.')}
- maxSize={5 * 1024 ** 2} // Maks 5MB
- accept={{ 'image/*': [] }}
- >
-
-
-
-
-
-
-
-
-
-
+ handleFileChange(files[0])}
+ onReject={() => toast.error('File tidak valid.')}
+ maxSize={5 * 1024 ** 2}
+ accept={{ 'image/*': [] }}
+ >
+
+
+
+
+
+
+
+
+
+
-
-
- Drag gambar ke sini atau klik untuk pilih file
-
-
- Maksimal 5MB dan harus format gambar
-
-
-
-
+
+
+ Drag gambar ke sini atau klik untuk pilih file
+
+
+ Maksimal 5MB dan harus format gambar
+
+
+
+
- {/* Tampilkan preview kalau ada */}
- {previewImage && (
-
-
-
- )}
-
-
+ {previewImage && (
+
+
+
+ )}
- {/* Preview Gambar */}
+ {/* Preview */}
Preview Gambar
{previewImage ? (
@@ -252,13 +280,13 @@ function EditPejabatDesa() {
)}
- {/* Submit Button */}
+ {/* Submit */}
{isSubmitting ? 'Menyimpan...' : 'Simpan Perubahan'}
@@ -278,4 +306,4 @@ function EditPejabatDesa() {
);
}
-export default EditPejabatDesa;
\ No newline at end of file
+export default EditPejabatDesa;
diff --git a/src/app/admin/(dashboard)/landing-page/profile/program-inovasi/[id]/edit/page.tsx b/src/app/admin/(dashboard)/landing-page/profile/program-inovasi/[id]/edit/page.tsx
index 4d72ce2b..d4b44eb3 100644
--- a/src/app/admin/(dashboard)/landing-page/profile/program-inovasi/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/profile/program-inovasi/[id]/edit/page.tsx
@@ -31,10 +31,10 @@ function EditProgramInovasi() {
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
const [formData, setFormData] = useState({
- name: stateProgramInovasi.update.form.name || "",
- description: stateProgramInovasi.update.form.description || "",
- imageId: stateProgramInovasi.update.form.imageId || "",
- link: stateProgramInovasi.update.form.link || "",
+ name: "",
+ description: "",
+ imageId: "",
+ link: "",
})
useEffect(() => {
@@ -51,9 +51,12 @@ function EditProgramInovasi() {
imageId: data.imageId || "",
link: data.link || ""
});
- // Tampilkan preview gambar
+
+ // Preview image
if (data.image?.link) {
setPreviewImage(data.image.link);
+ } else {
+ setPreviewImage(null);
}
}
} catch (error) {
@@ -69,24 +72,25 @@ function EditProgramInovasi() {
const handleSubmit = async () => {
try {
+ // Upload file kalau ada file baru
+ let imageId = formData.imageId;
+ if (file) {
+ const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
+ const uploaded = res.data?.data;
+ if (!uploaded?.id) {
+ return toast.error("Gagal upload gambar");
+ }
+ imageId = uploaded.id;
+ }
+
+ // Update global state form (baru di sini)
stateProgramInovasi.update.form = {
...stateProgramInovasi.update.form,
name: formData.name,
description: formData.description,
- imageId: formData.imageId,
+ imageId,
link: formData.link,
}
- if (file) {
- const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
- const uploaded = res.data?.data;
-
- if (!uploaded?.id) {
- return toast.error("Gagal upload gambar");
- }
-
- // Update imageId in global state
- stateProgramInovasi.update.form.imageId = uploaded.id;
- }
await stateProgramInovasi.update.update();
toast.success("Program Inovasi berhasil diperbarui!");
@@ -170,7 +174,9 @@ function EditProgramInovasi() {
)}
+
- Deskripsi
+ Deskripsi
{
- setFormData((prev) => ({ ...prev, description: htmlContent }));
- stateProgramInovasi.update.form.description = htmlContent;
- }}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({ ...prev, description: htmlContent }))
+ }
/>
Deskripsi
-
+
diff --git a/src/app/admin/(dashboard)/landing-page/profile/program-inovasi/create/page.tsx b/src/app/admin/(dashboard)/landing-page/profile/program-inovasi/create/page.tsx
index e214827a..de402dd6 100644
--- a/src/app/admin/(dashboard)/landing-page/profile/program-inovasi/create/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/profile/program-inovasi/create/page.tsx
@@ -144,7 +144,7 @@ function CreateProgramInovasi() {
(stateProgramInovasi.create.form.name = e.target.value)}
required
/>
@@ -162,7 +162,7 @@ function CreateProgramInovasi() {
(stateProgramInovasi.create.form.link = e.target.value)}
/>
diff --git a/src/app/admin/(dashboard)/landing-page/sdgs-desa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/landing-page/sdgs-desa/[id]/edit/page.tsx
index b3003d71..dd65bf5a 100644
--- a/src/app/admin/(dashboard)/landing-page/sdgs-desa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/sdgs-desa/[id]/edit/page.tsx
@@ -14,7 +14,7 @@ import {
TextInput,
Title,
Tooltip,
- Image
+ Image,
} from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { IconArrowBack, IconDeviceFloppy, IconPhoto, IconUpload, IconX } from "@tabler/icons-react";
@@ -23,37 +23,35 @@ import { useEffect, useState } from "react";
import { toast } from "react-toastify";
import { useProxy } from "valtio/utils";
-function EditKolaborasiInovasi() {
+export default function EditKolaborasiInovasi() {
const sdgsState = useProxy(sdgsDesa);
const router = useRouter();
const params = useParams();
+ const [formData, setFormData] = useState({
+ name: "",
+ jumlah: "",
+ imageId: "",
+ });
const [previewImage, setPreviewImage] = useState(null);
const [file, setFile] = useState(null);
- const [formData, setFormData] = useState({
- name: sdgsState.edit.form.name || '',
- jumlah: sdgsState.edit.form.jumlah || '',
- imageId: sdgsState.edit.form.imageId || ''
- });
- // Load sdgs desa by id saat pertama kali
+ // Load data sdgs desa by id
useEffect(() => {
const loadKolaborasi = async () => {
const id = params?.id as string;
if (!id) return;
try {
- const data = await sdgsState.edit.load(id); // akses langsung, bukan dari proxy
+ const data = await sdgsState.edit.load(id);
if (data) {
setFormData({
- name: data.name || '',
- jumlah: data.jumlah || '',
- imageId: data.imageId || '',
+ name: data.name || "",
+ jumlah: data.jumlah || "",
+ imageId: data.imageId || "",
});
- if (data.image) {
- if (data?.image?.link) {
- setPreviewImage(data.image.link);
- }
+ if (data.image?.link) {
+ setPreviewImage(data.image.link);
}
}
} catch (error) {
@@ -65,31 +63,26 @@ function EditKolaborasiInovasi() {
loadKolaborasi();
}, [params?.id]);
+ const handleInputChange = (field: keyof typeof formData, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
-
try {
- // edit global state with form data
- sdgsState.edit.form = {
- ...sdgsState.edit.form,
- name: formData.name,
- jumlah: formData.jumlah,
- imageId: formData.imageId // Keep existing imageId if not changed
- };
+ let imageId = formData.imageId;
- // Jika ada file baru, upload
+ // Upload file baru jika ada
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
-
- if (!uploaded?.id) {
- return toast.error("Gagal upload gambar");
- }
-
- // edit imageId in global state
- sdgsState.edit.form.imageId = uploaded.id;
+ if (!uploaded?.id) return toast.error("Gagal upload gambar");
+ imageId = uploaded.id;
}
+ // Update global state hanya saat submit
+ sdgsState.edit.form = { ...sdgsState.edit.form, ...formData, imageId };
await sdgsState.edit.update();
+
toast.success("sdgs desa berhasil diperbarui!");
router.push("/admin/landing-page/sdgs-desa");
} catch (error) {
@@ -99,11 +92,11 @@ function EditKolaborasiInovasi() {
};
return (
-
+
router.back()} p="xs" radius="md">
-
+
@@ -112,12 +105,12 @@ function EditKolaborasiInovasi() {
@@ -132,15 +125,15 @@ function EditKolaborasiInovasi() {
setPreviewImage(URL.createObjectURL(selectedFile));
}
}}
- onReject={() => toast.error('File tidak valid, gunakan format gambar')}
+ onReject={() => toast.error("File tidak valid, gunakan format gambar")}
maxSize={5 * 1024 ** 2}
- accept={{ 'image/*': [] }}
+ accept={{ "image/*": [] }}
radius="md"
p="xl"
>
-
+
@@ -160,12 +153,12 @@ function EditKolaborasiInovasi() {
{previewImage && (
-
+
@@ -176,7 +169,7 @@ function EditKolaborasiInovasi() {
label="Nama Sdgs Desa"
placeholder="Masukkan nama Sdgs Desa"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleInputChange("name", e.target.value)}
required
/>
@@ -184,7 +177,7 @@ function EditKolaborasiInovasi() {
label="Jumlah"
placeholder="Masukkan jumlah"
value={formData.jumlah}
- onChange={(e) => setFormData({ ...formData, jumlah: e.target.value })}
+ onChange={(e) => handleInputChange("jumlah", e.target.value)}
required
type="number"
/>
@@ -197,9 +190,9 @@ function EditKolaborasiInovasi() {
radius="md"
size="md"
style={{
- background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
- color: '#fff',
- boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
+ background: `linear-gradient(135deg, ${colors["blue-button"]}, #4facfe)`,
+ color: "#fff",
+ boxShadow: "0 4px 15px rgba(79, 172, 254, 0.4)",
}}
>
Simpan
@@ -210,5 +203,3 @@ function EditKolaborasiInovasi() {
);
}
-
-export default EditKolaborasiInovasi;
diff --git a/src/app/admin/(dashboard)/landing-page/sdgs-desa/create/page.tsx b/src/app/admin/(dashboard)/landing-page/sdgs-desa/create/page.tsx
index 0acf75f9..0b1b6517 100644
--- a/src/app/admin/(dashboard)/landing-page/sdgs-desa/create/page.tsx
+++ b/src/app/admin/(dashboard)/landing-page/sdgs-desa/create/page.tsx
@@ -159,7 +159,7 @@ function CreateSDGsDesa() {
}
placeholder="Masukkan jumlah"
- value={stateSDGSDesa.create.form.jumlah}
+ defaultValue={stateSDGSDesa.create.form.jumlah}
onChange={(val) => {
stateSDGSDesa.create.form.jumlah = val.target.value;
}}
@@ -167,14 +167,6 @@ function CreateSDGsDesa() {
min={0}
radius="md"
/>
- {
- console.log(val.target.value)
- }}
-
- />
-
{
- const loadProgramKreatif = async () => {
+ const loadData = async () => {
const id = params?.id as string;
if (!id) return;
@@ -67,14 +69,6 @@ function EditDataLingkunganDesa() {
const data = await stateDataLingkunganDesa.update.load(id);
if (data) {
stateDataLingkunganDesa.update.id = id;
-
- stateDataLingkunganDesa.update.form = {
- name: data.name,
- deskripsi: data.deskripsi,
- jumlah: data.jumlah,
- icon: data.icon,
- };
-
setFormData({
name: data.name,
deskripsi: data.deskripsi,
@@ -88,13 +82,14 @@ function EditDataLingkunganDesa() {
}
};
- loadProgramKreatif();
+ loadData();
}, [params?.id]);
const handleSubmit = async () => {
try {
+ // Update global state hanya saat submit
stateDataLingkunganDesa.update.form = {
- ...stateDataLingkunganDesa.update.form,
+ ...formData,
name: formData.name.trim(),
deskripsi: formData.deskripsi.trim(),
jumlah: formData.jumlah.trim(),
@@ -135,11 +130,8 @@ function EditDataLingkunganDesa() {
value={formData.name}
label={Nama Data Lingkungan Desa }
placeholder="Masukkan nama data lingkungan desa"
- onChange={(val) =>
- setFormData({
- ...formData,
- name: val.target.value,
- })
+ onChange={(e) =>
+ setFormData((prev) => ({ ...prev, name: e.target.value }))
}
required
/>
@@ -148,11 +140,8 @@ function EditDataLingkunganDesa() {
value={formData.jumlah}
label={Jumlah Data Lingkungan Desa }
placeholder="Masukkan jumlah data lingkungan desa"
- onChange={(val) =>
- setFormData({
- ...formData,
- jumlah: val.target.value,
- })
+ onChange={(e) =>
+ setFormData((prev) => ({ ...prev, jumlah: e.target.value }))
}
required
/>
@@ -163,10 +152,9 @@ function EditDataLingkunganDesa() {
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- stateDataLingkunganDesa.update.form.deskripsi = htmlContent;
- }}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
+ }
/>
@@ -176,10 +164,9 @@ function EditDataLingkunganDesa() {
{
- setFormData((prev) => ({ ...prev, icon: value }));
- stateDataLingkunganDesa.update.form.icon = value;
- }}
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, icon: value }))
+ }
/>
@@ -202,5 +189,3 @@ function EditDataLingkunganDesa() {
);
}
-
-export default EditDataLingkunganDesa;
diff --git a/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/[id]/page.tsx b/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/[id]/page.tsx
index 5f1d6095..4e985dce 100644
--- a/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/[id]/page.tsx
@@ -131,7 +131,7 @@ function DetailDataLingkunganDesa() {
Deskripsi
-
+
{/* Action Buttons */}
diff --git a/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/create/page.tsx b/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/create/page.tsx
index 54dd409d..c2bff29e 100644
--- a/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/create/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/create/page.tsx
@@ -69,7 +69,7 @@ function CreateDataLingkunganDesa() {
Nama Data Lingkungan Desa}
placeholder="Masukkan nama data lingkungan desa"
- value={stateCreate.create.form.name || ''}
+ defaultValue={stateCreate.create.form.name || ''}
onChange={(val) => (stateCreate.create.form.name = val.target.value)}
required
/>
@@ -86,7 +86,7 @@ function CreateDataLingkunganDesa() {
Jumlah Data Lingkungan Desa}
placeholder="Masukkan jumlah data lingkungan desa"
- value={stateCreate.create.form.jumlah || ''}
+ defaultValue={stateCreate.create.form.jumlah || ''}
onChange={(e) => (stateCreate.create.form.jumlah = e.currentTarget.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/page.tsx b/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/page.tsx
index acc56ac6..b7addeaf 100644
--- a/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/data-lingkungan-desa/page.tsx
@@ -115,7 +115,7 @@ function ListDataLingkunganDesa({ search }: { search: string }) {
Daftar Data Lingkungan Desa
- } color="blue" variant="light" onClick={() => router.push('/admin/lingkungan/data-lingkungan/create')}>
+ } color="blue" variant="light" onClick={() => router.push('/admin/lingkungan/data-lingkungan-desa/create')}>
Tambah Baru
diff --git a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba/edit/page.tsx
index e43c4f1e..78447cc6 100644
--- a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba/edit/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba/edit/page.tsx
@@ -1,4 +1,5 @@
'use client'
+
import stateEdukasiLingkungan from '@/app/admin/(dashboard)/_state/lingkungan/edukasi-lingkungan';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, Title } from '@mantine/core';
@@ -10,48 +11,67 @@ import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
const EdukasiLingkunganTextEditor = dynamic(
- () => import('../../_lib/edukasiLingkunganTextEditor').then(mod => mod.EdukasiLingkunganTextEditor),
+ () =>
+ import('../../_lib/edukasiLingkunganTextEditor').then(
+ (mod) => mod.EdukasiLingkunganTextEditor
+ ),
{ ssr: false }
);
-function EditContohKegiatanDesaDarmasaba() {
+export default function EditContohKegiatanDesaDarmasaba() {
const router = useRouter();
- const contohEdukasiState = useProxy(stateEdukasiLingkungan.stateContohEdukasiLingkungan);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ const contohEdukasiState = useProxy(
+ stateEdukasiLingkungan.stateContohEdukasiLingkungan
+ );
+ // state lokal untuk form
+ const [formData, setFormData] = useState({
+ judul: '',
+ deskripsi: '',
+ });
+
+ // load data awal
useShallowEffect(() => {
if (!contohEdukasiState.findById.data) {
contohEdukasiState.findById.initialize();
}
}, []);
+ // update state lokal saat data global sudah ada
useEffect(() => {
if (contohEdukasiState.findById.data) {
- setJudul(contohEdukasiState.findById.data.judul ?? '');
- setContent(contohEdukasiState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: contohEdukasiState.findById.data.judul ?? '',
+ deskripsi: contohEdukasiState.findById.data.deskripsi ?? '',
+ });
}
}, [contohEdukasiState.findById.data]);
- const submit = () => {
+ // handler perubahan input
+ const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
+ // submit update
+ const handleSubmit = () => {
if (contohEdukasiState.findById.data) {
- contohEdukasiState.findById.data.judul = judul;
- contohEdukasiState.findById.data.deskripsi = content;
- contohEdukasiState.update.save(contohEdukasiState.findById.data);
+ const updatedData = {
+ ...contohEdukasiState.findById.data,
+ judul: formData.judul,
+ deskripsi: formData.deskripsi,
+ };
+ contohEdukasiState.update.save(updatedData);
}
- router.push('/admin/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba');
+ router.push(
+ '/admin/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba'
+ );
};
return (
{/* Header */}
- router.back()}
- p="xs"
- radius="md"
- >
+ router.back()} p="xs" radius="md">
@@ -75,8 +95,8 @@ function EditContohKegiatanDesaDarmasaba() {
handleChange('judul', value)}
+ initialContent={formData.judul}
/>
@@ -86,14 +106,14 @@ function EditContohKegiatanDesaDarmasaba() {
handleChange('deskripsi', value)}
+ initialContent={formData.deskripsi}
/>
);
}
-
-export default EditContohKegiatanDesaDarmasaba;
diff --git a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba/page.tsx b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba/page.tsx
index 1e13f6df..a7b7a396 100644
--- a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba/page.tsx
@@ -57,6 +57,7 @@ function Page() {
fw={600}
c="black"
dangerouslySetInnerHTML={{ __html: listContohEdukasi.findById.data.judul }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -66,6 +67,7 @@ function Page() {
c="dimmed"
lineClamp={10}
dangerouslySetInnerHTML={{ __html: listContohEdukasi.findById.data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan/edit/page.tsx
index ad78350d..6f363765 100644
--- a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan/edit/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan/edit/page.tsx
@@ -14,30 +14,43 @@ const EdukasiLingkunganTextEditor = dynamic(
{ ssr: false }
);
-function EditMateriEdukasiYangDiberikan() {
+export default function EditMateriEdukasiYangDiberikan() {
const router = useRouter();
const materiEdukasiState = useProxy(stateEdukasiLingkungan.stateMateriEdukasiLingkungan);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ // State lokal gabungan untuk form
+ const [formData, setFormData] = useState({ judul: '', content: '' });
+
+ // Initialize data kalau belum ada
useShallowEffect(() => {
if (!materiEdukasiState.findById.data) {
materiEdukasiState.findById.initialize();
}
}, []);
+ // Set formData saat data tersedia
useEffect(() => {
if (materiEdukasiState.findById.data) {
- setJudul(materiEdukasiState.findById.data.judul ?? '');
- setContent(materiEdukasiState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: materiEdukasiState.findById.data.judul ?? '',
+ content: materiEdukasiState.findById.data.deskripsi ?? '',
+ });
}
}, [materiEdukasiState.findById.data]);
+ // Handler perubahan form
+ const handleChange = (field: 'judul' | 'content', value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
const submit = () => {
if (materiEdukasiState.findById.data) {
- materiEdukasiState.findById.data.judul = judul;
- materiEdukasiState.findById.data.deskripsi = content;
- materiEdukasiState.update.save(materiEdukasiState.findById.data);
+ const updatedData = {
+ ...materiEdukasiState.findById.data,
+ judul: formData.judul,
+ deskripsi: formData.content,
+ };
+ materiEdukasiState.update.save(updatedData);
}
router.push('/admin/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan');
};
@@ -66,14 +79,22 @@ function EditMateriEdukasiYangDiberikan() {
Judul
-
+ handleChange('judul', val)}
+ initialContent={formData.judul}
+ />
Konten
-
+ handleChange('content', val)}
+ initialContent={formData.content}
+ />
@@ -96,5 +117,3 @@ function EditMateriEdukasiYangDiberikan() {
);
}
-
-export default EditMateriEdukasiYangDiberikan;
diff --git a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan/page.tsx b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan/page.tsx
index 330a50ca..942d3d78 100644
--- a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan/page.tsx
@@ -53,6 +53,7 @@ function Page() {
fw={600}
c="black"
dangerouslySetInnerHTML={{ __html: listMateriEdukasi.findById.data.judul }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -62,6 +63,7 @@ function Page() {
c="dimmed"
lineClamp={10}
dangerouslySetInnerHTML={{ __html: listMateriEdukasi.findById.data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan/edit/page.tsx
index b196c099..0c60469d 100644
--- a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan/edit/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan/edit/page.tsx
@@ -1,4 +1,5 @@
'use client'
+
import stateEdukasiLingkungan from '@/app/admin/(dashboard)/_state/lingkungan/edukasi-lingkungan';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, Title } from '@mantine/core';
@@ -14,30 +15,44 @@ const EdukasiLingkunganTextEditor = dynamic(
{ ssr: false }
);
-function EditTujuanEdukasiLingkungan() {
+export default function EditTujuanEdukasiLingkungan() {
const router = useRouter();
const tujuanEdukasiState = useProxy(stateEdukasiLingkungan.stateTujuanEdukasi);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ // State lokal untuk form, gak tergantung global state
+ const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
+
+ // Initialize global state
useShallowEffect(() => {
if (!tujuanEdukasiState.findById.data) {
tujuanEdukasiState.findById.initialize();
}
}, []);
+ // Sync initial values dari global state ke form lokal
useEffect(() => {
if (tujuanEdukasiState.findById.data) {
- setJudul(tujuanEdukasiState.findById.data.judul ?? '');
- setContent(tujuanEdukasiState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: tujuanEdukasiState.findById.data.judul ?? '',
+ deskripsi: tujuanEdukasiState.findById.data.deskripsi ?? '',
+ });
}
}, [tujuanEdukasiState.findById.data]);
+ // Handler input
+ const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
const submit = () => {
if (tujuanEdukasiState.findById.data) {
- tujuanEdukasiState.findById.data.judul = judul;
- tujuanEdukasiState.findById.data.deskripsi = content;
- tujuanEdukasiState.update.save(tujuanEdukasiState.findById.data);
+ // Update global state hanya saat submit
+ const updatedData = {
+ ...tujuanEdukasiState.findById.data,
+ judul: formData.judul,
+ deskripsi: formData.deskripsi,
+ };
+ tujuanEdukasiState.update.save(updatedData);
}
router.push('/admin/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan');
};
@@ -73,8 +88,8 @@ function EditTujuanEdukasiLingkungan() {
handleChange('judul', value)}
+ initialContent={formData.judul}
/>
@@ -84,8 +99,8 @@ function EditTujuanEdukasiLingkungan() {
handleChange('deskripsi', value)}
+ initialContent={formData.deskripsi}
/>
@@ -109,5 +124,3 @@ function EditTujuanEdukasiLingkungan() {
);
}
-
-export default EditTujuanEdukasiLingkungan;
diff --git a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan/page.tsx b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan/page.tsx
index 49fd7dd5..48b88aa8 100644
--- a/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan/page.tsx
@@ -53,6 +53,7 @@ function Page() {
fw={600}
c='black'
dangerouslySetInnerHTML={{ __html: listTujuanEdukasi.findById.data.judul }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -62,6 +63,7 @@ function Page() {
c="dimmed"
lineClamp={10}
dangerouslySetInnerHTML={{ __html: listTujuanEdukasi.findById.data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/gotong-royong/kategori-kegiatan/[id]/page.tsx b/src/app/admin/(dashboard)/lingkungan/gotong-royong/kategori-kegiatan/[id]/page.tsx
index f6b81aa7..dd2e34a7 100644
--- a/src/app/admin/(dashboard)/lingkungan/gotong-royong/kategori-kegiatan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/gotong-royong/kategori-kegiatan/[id]/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
-'use client'
+'use client';
+
import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
@@ -15,53 +16,59 @@ function EditKategoriKegiatan() {
const id = params?.id as string;
const stateKategori = useProxy(gotongRoyongState.kategoriKegiatan);
- const [formData, setFormData] = useState({
- nama: '',
- });
+ const [formData, setFormData] = useState({ nama: '' });
+ const [loading, setLoading] = useState(true);
+ // Load data once
useEffect(() => {
- const loadKategorikegiatan = async () => {
- if (!id) return;
+ if (!id) return;
+ const loadKategori = async () => {
try {
const data = await stateKategori.edit.load(id);
-
if (data) {
stateKategori.edit.id = id;
- setFormData({
- nama: data.nama || '',
- });
+ setFormData({ nama: data.nama || '' });
}
- } catch (error) {
- console.error('Error loading kategori kegiatan:', error);
+ } catch (err) {
+ console.error('Error loading kategori kegiatan:', err);
toast.error('Gagal memuat data kategori kegiatan');
+ } finally {
+ setLoading(false);
}
};
- loadKategorikegiatan();
+ loadKategori();
}, [id]);
- const handleSubmit = async () => {
- try {
- if (!formData.nama.trim()) {
- toast.error('Nama kategori kegiatan tidak boleh kosong');
- return;
- }
+ const handleChange = (field: string, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
- stateKategori.edit.form = { nama: formData.nama.trim() };
+ const handleSubmit = async () => {
+ const trimmedNama = formData.nama.trim();
+ if (!trimmedNama) {
+ toast.error('Nama kategori kegiatan tidak boleh kosong');
+ return;
+ }
+
+ try {
+ stateKategori.edit.form = { nama: trimmedNama };
if (!stateKategori.edit.id) stateKategori.edit.id = id;
const success = await stateKategori.edit.update();
-
if (success) {
toast.success('Kategori kegiatan berhasil diperbarui!');
router.push('/admin/lingkungan/gotong-royong/kategori-kegiatan');
}
- } catch (error) {
- console.error('Error updating kategori kegiatan:', error);
+ } catch (err) {
+ console.error('Error updating kategori kegiatan:', err);
+ toast.error('Gagal memperbarui kategori kegiatan');
}
};
+ if (loading) return Loading... ;
+
return (
@@ -86,7 +93,7 @@ function EditKategoriKegiatan() {
setFormData({ ...formData, nama: e.target.value })}
+ onChange={(e) => handleChange('nama', e.target.value)}
label={Nama Kategori Kegiatan }
placeholder="Masukkan nama kategori kegiatan"
required
diff --git a/src/app/admin/(dashboard)/lingkungan/gotong-royong/kategori-kegiatan/create/page.tsx b/src/app/admin/(dashboard)/lingkungan/gotong-royong/kategori-kegiatan/create/page.tsx
index 3930a195..46d44b95 100644
--- a/src/app/admin/(dashboard)/lingkungan/gotong-royong/kategori-kegiatan/create/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/gotong-royong/kategori-kegiatan/create/page.tsx
@@ -53,7 +53,7 @@ function CreateKategoriKegiatan() {
>
(stateKategori.create.form.nama = val.target.value)}
label={Nama Kategori Kegiatan }
placeholder="Masukkan nama kategori kegiatan"
diff --git a/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/[id]/edit/page.tsx
index 73545702..15b5c48a 100644
--- a/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/[id]/edit/page.tsx
@@ -4,7 +4,19 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
-import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
+import {
+ Box,
+ Button,
+ Group,
+ Image,
+ Paper,
+ Select,
+ Stack,
+ Text,
+ TextInput,
+ Title,
+ Tooltip,
+} from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -23,12 +35,10 @@ interface FormKegiatanDesa {
kategoriKegiatanId: string;
}
-function EditGotongRoyong() {
- const kegiatanDesaState = useProxy(gotongRoyongState.kegiatanDesa)
- const params = useParams()
- const router = useRouter()
- const [previewImage, setPreviewImage] = useState(null);
- const [file, setFile] = useState(null);
+export default function EditKegiatanDesa() {
+ const kegiatanDesaState = useProxy(gotongRoyongState.kegiatanDesa);
+ const params = useParams();
+ const router = useRouter();
const [formData, setFormData] = useState({
judul: '',
@@ -39,16 +49,19 @@ function EditGotongRoyong() {
partisipan: 0,
imageId: '',
kategoriKegiatanId: '',
- })
+ });
+ const [file, setFile] = useState(null);
+ const [previewImage, setPreviewImage] = useState(null);
const formatDateForInput = (dateString: string) => {
if (!dateString) return '';
- const date = new Date(dateString);
- return date.toISOString().split('T')[0];
+ return new Date(dateString).toISOString().split('T')[0];
};
+ // Load kategori & data kegiatan
useEffect(() => {
- const loadKegiatanDesa = async () => {
+ const loadData = async () => {
+ gotongRoyongState.kategoriKegiatan.findMany.load();
const id = params?.id as string;
if (!id) return;
@@ -65,43 +78,48 @@ function EditGotongRoyong() {
imageId: data.imageId || '',
kategoriKegiatanId: data.kategoriKegiatanId || '',
});
+ if (data.imageId) {
+ // Optional: bisa fetch URL image dari backend
+ setPreviewImage(`/api/file/${data.imageId}`);
+ }
}
} catch (error) {
- console.error("Error loading kegiatan desa:", error);
- toast.error("Gagal memuat data kegiatan desa");
+ console.error(error);
+ toast.error('Gagal memuat data kegiatan desa');
}
- }
- gotongRoyongState.kategoriKegiatan.findMany.load()
- loadKegiatanDesa();
+ };
+ loadData();
}, [params?.id]);
const handleSubmit = async () => {
try {
- kegiatanDesaState.edit.form = {
- ...kegiatanDesaState.edit.form,
- judul: formData.judul.trim(),
- deskripsiSingkat: formData.deskripsiSingkat.trim(),
- deskripsiLengkap: formData.deskripsiLengkap.trim(),
- tanggal: new Date(formData.tanggal.trim()),
- lokasi: formData.lokasi.trim(),
- partisipan: formData.partisipan,
- imageId: formData.imageId,
- kategoriKegiatanId: formData.kategoriKegiatanId,
- }
+ let imageId = formData.imageId;
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
- if (!uploaded?.id) return toast.error("Gagal upload gambar");
- kegiatanDesaState.edit.form.imageId = uploaded.id;
+ if (!uploaded?.id) return toast.error('Gagal upload gambar');
+ imageId = uploaded.id;
}
- await kegiatanDesaState.edit.update()
- toast.success("Kegiatan desa berhasil diperbarui!")
- router.push("/admin/lingkungan/gotong-royong/kegiatan-desa");
+
+ kegiatanDesaState.edit.form = {
+ judul: formData.judul.trim(),
+ deskripsiSingkat: formData.deskripsiSingkat.trim(),
+ deskripsiLengkap: formData.deskripsiLengkap.trim(),
+ tanggal: new Date(formData.tanggal),
+ lokasi: formData.lokasi.trim(),
+ partisipan: formData.partisipan,
+ imageId,
+ kategoriKegiatanId: formData.kategoriKegiatanId,
+ };
+
+ await kegiatanDesaState.edit.update();
+ toast.success('Kegiatan desa berhasil diperbarui!');
+ router.push('/admin/lingkungan/gotong-royong/kegiatan-desa');
} catch (error) {
- console.error("Error updating kegiatan desa:", error);
- toast.error("Terjadi kesalahan saat memperbarui kegiatan desa");
+ console.error(error);
+ toast.error('Terjadi kesalahan saat memperbarui kegiatan desa');
}
- }
+ };
return (
@@ -132,13 +150,13 @@ function EditGotongRoyong() {
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
required
/>
- Deskripsi Singkat Kegiatan Desa}
- placeholder="masukkan deskripsi singkat kegiatan desa"
- onChange={(e) => setFormData({ ...formData, deskripsiSingkat: e.target.value })}
- required
- />
+
+ Deskripsi Singkat Kegiatan Desa
+ setFormData(prev => ({ ...prev, deskripsiSingkat: htmlContent }))}
+ />
+
({
@@ -148,22 +166,16 @@ function EditGotongRoyong() {
value={formData.kategoriKegiatanId}
onChange={(val) => setFormData({ ...formData, kategoriKegiatanId: val ?? '' })}
/>
-
Deskripsi Lengkap Kegiatan Desa
{
- setFormData((prev) => ({ ...prev, deskripsiLengkap: htmlContent }));
- kegiatanDesaState.edit.form.deskripsiLengkap = htmlContent;
- }}
+ onChange={(htmlContent) => setFormData(prev => ({ ...prev, deskripsiLengkap: htmlContent }))}
/>
-
Tanggal Kegiatan Desa}
- placeholder="masukkan tanggal kegiatan desa"
type="date"
+ label={Tanggal Kegiatan Desa }
value={formatDateForInput(formData.tanggal)}
onChange={(e) => setFormData({ ...formData, tanggal: e.target.value })}
/>
@@ -187,11 +199,10 @@ function EditGotongRoyong() {
Gambar Kegiatan Desa
{
- const selectedFile = files[0];
- if (selectedFile) {
- setFile(selectedFile);
- setPreviewImage(URL.createObjectURL(selectedFile));
- }
+ const selected = files[0];
+ if (!selected) return;
+ setFile(selected);
+ setPreviewImage(URL.createObjectURL(selected));
}}
onReject={() => toast.error('File tidak valid.')}
maxSize={5 * 1024 ** 2}
@@ -248,5 +259,3 @@ function EditGotongRoyong() {
);
}
-
-export default EditGotongRoyong;
diff --git a/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/[id]/page.tsx b/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/[id]/page.tsx
index 42855333..11090d90 100644
--- a/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/[id]/page.tsx
@@ -85,13 +85,13 @@ function DetailKegiatanDesa() {
{/* Deskripsi Singkat */}
Deskripsi Singkat
- {data.deskripsiSingkat || '-'}
+
{/* Deskripsi Lengkap */}
Deskripsi
-
+
{/* Kategori */}
@@ -109,7 +109,7 @@ function DetailKegiatanDesa() {
{/* Lokasi */}
Lokasi
- {data.lokasi || '-'}
+ {data.lokasi || '-'}
{/* Gambar */}
diff --git a/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/create/page.tsx b/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/create/page.tsx
index 0ebb868c..2c4fbc49 100644
--- a/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/create/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/gotong-royong/kegiatan-desa/create/page.tsx
@@ -155,21 +155,23 @@ function CreateKegiatanDesa() {
(stateKegiatanDesa.create.form.judul = e.target.value)}
required
/>
- (stateKegiatanDesa.create.form.deskripsiSingkat = e.target.value)}
- required
- />
+
+
+ Deskripsi Singkat
+
+ (stateKegiatanDesa.create.form.deskripsiSingkat = val)}
+ />
+
{
const value = Number(e.target.value);
if (value >= 0) {
@@ -184,7 +186,7 @@ function CreateKegiatanDesa() {
label="Tanggal"
type="date"
placeholder="Contoh: 2022-01-01"
- value={
+ defaultValue={
stateKegiatanDesa.create.form.tanggal
? stateKegiatanDesa.create.form.tanggal.toISOString().split('T')[0]
: ''
@@ -198,7 +200,7 @@ function CreateKegiatanDesa() {
(stateKegiatanDesa.create.form.lokasi = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/bentuk-konservasi-berdasarkan-adat/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/bentuk-konservasi-berdasarkan-adat/edit/page.tsx
index 4008f466..aca07a0c 100644
--- a/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/bentuk-konservasi-berdasarkan-adat/edit/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/bentuk-konservasi-berdasarkan-adat/edit/page.tsx
@@ -17,9 +17,11 @@ const KonservasiAdatBaliTextEditor = dynamic(
function EditBentukKonservasiBerdasarkanAdat() {
const router = useRouter();
const bentukKonservasiState = useProxy(stateKonservasiAdatBali.stateBentukKonservasiBerdasarkanAdat);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ // Gabung semua field form jadi satu object
+ const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
+
+ // Initialize data dari global state
useShallowEffect(() => {
if (!bentukKonservasiState.findById.data) {
bentukKonservasiState.findById.initialize();
@@ -28,16 +30,22 @@ function EditBentukKonservasiBerdasarkanAdat() {
useEffect(() => {
if (bentukKonservasiState.findById.data) {
- setJudul(bentukKonservasiState.findById.data.judul ?? '');
- setContent(bentukKonservasiState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: bentukKonservasiState.findById.data.judul ?? '',
+ deskripsi: bentukKonservasiState.findById.data.deskripsi ?? '',
+ });
}
}, [bentukKonservasiState.findById.data]);
+ const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
const submit = () => {
if (bentukKonservasiState.findById.data) {
- bentukKonservasiState.findById.data.judul = judul;
- bentukKonservasiState.findById.data.deskripsi = content;
- bentukKonservasiState.update.save(bentukKonservasiState.findById.data);
+ // Update global state cuma pas submit
+ const updatedData = { ...bentukKonservasiState.findById.data, ...formData };
+ bentukKonservasiState.update.save(updatedData);
}
router.push('/admin/lingkungan/konservasi-adat-bali/bentuk-konservasi-berdasarkan-adat');
};
@@ -46,12 +54,7 @@ function EditBentukKonservasiBerdasarkanAdat() {
{/* Header */}
- router.back()}
- p="xs"
- radius="md"
- >
+ router.back()} p="xs" radius="md">
@@ -75,8 +78,8 @@ function EditBentukKonservasiBerdasarkanAdat() {
handleChange('judul', value)}
+ initialContent={formData.judul}
/>
@@ -86,8 +89,8 @@ function EditBentukKonservasiBerdasarkanAdat() {
handleChange('deskripsi', value)}
+ initialContent={formData.deskripsi}
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/bentuk-konservasi-berdasarkan-adat/page.tsx b/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/bentuk-konservasi-berdasarkan-adat/page.tsx
index a9b8330d..72b62a5f 100644
--- a/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/bentuk-konservasi-berdasarkan-adat/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/bentuk-konservasi-berdasarkan-adat/page.tsx
@@ -64,6 +64,7 @@ function Page() {
dangerouslySetInnerHTML={{
__html: listBentukKonservasiBerdasarkanAdat.findById.data.judul,
}}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -75,6 +76,7 @@ function Page() {
dangerouslySetInnerHTML={{
__html: listBentukKonservasiBerdasarkanAdat.findById.data.deskripsi,
}}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/filosofi-tri-hita-karana/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/filosofi-tri-hita-karana/edit/page.tsx
index 0ec3cd14..3ed20d1e 100644
--- a/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/filosofi-tri-hita-karana/edit/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/filosofi-tri-hita-karana/edit/page.tsx
@@ -1,4 +1,5 @@
'use client'
+
import stateKonservasiAdatBali from '@/app/admin/(dashboard)/_state/lingkungan/konservasi-adat-bali';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, Title } from '@mantine/core';
@@ -20,26 +21,35 @@ const KonservasiAdatBaliTextEditor = dynamic(
function EditFilosofiTriHitaKarana() {
const router = useRouter();
const filosofiTriHitaState = useProxy(stateKonservasiAdatBali.stateFilosofiTriHita);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ // Local state form
+ const [formData, setFormData] = useState({ judul: '', content: '' });
+
+ // Load data dari global state kalau belum ada
useShallowEffect(() => {
if (!filosofiTriHitaState.findById.data) {
filosofiTriHitaState.findById.initialize();
}
}, []);
+ // Set formData dari global state saat data tersedia
useEffect(() => {
if (filosofiTriHitaState.findById.data) {
- setJudul(filosofiTriHitaState.findById.data.judul ?? '');
- setContent(filosofiTriHitaState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: filosofiTriHitaState.findById.data.judul ?? '',
+ content: filosofiTriHitaState.findById.data.deskripsi ?? '',
+ });
}
}, [filosofiTriHitaState.findById.data]);
- const submit = () => {
+ const handleChange = (field: 'judul' | 'content', value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
+ const handleSubmit = () => {
if (filosofiTriHitaState.findById.data) {
- filosofiTriHitaState.findById.data.judul = judul;
- filosofiTriHitaState.findById.data.deskripsi = content;
+ filosofiTriHitaState.findById.data.judul = formData.judul;
+ filosofiTriHitaState.findById.data.deskripsi = formData.content;
filosofiTriHitaState.update.save(filosofiTriHitaState.findById.data);
}
router.push('/admin/lingkungan/konservasi-adat-bali/filosofi-tri-hita-karana');
@@ -49,12 +59,7 @@ function EditFilosofiTriHitaKarana() {
{/* Header */}
- router.back()}
- p="xs"
- radius="md"
- >
+ router.back()} p="xs" radius="md">
@@ -78,8 +83,8 @@ function EditFilosofiTriHitaKarana() {
handleChange('judul', val)}
+ initialContent={formData.judul}
/>
@@ -89,14 +94,14 @@ function EditFilosofiTriHitaKarana() {
handleChange('content', val)}
+ initialContent={formData.content}
/>
@@ -66,6 +67,7 @@ function Page() {
c="dimmed"
lineClamp={10}
dangerouslySetInnerHTML={{ __html: listFilosofi.findById.data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/nilai-konservasi-adat/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/nilai-konservasi-adat/edit/page.tsx
index 6f6271a8..1df50f0c 100644
--- a/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/nilai-konservasi-adat/edit/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/nilai-konservasi-adat/edit/page.tsx
@@ -17,26 +17,36 @@ const KonservasiAdatBaliTextEditor = dynamic(
function EditNilaiKonservasiAdat() {
const router = useRouter();
const nilaiKonservasiState = useProxy(stateKonservasiAdatBali.stateNilaiKonservasiAdat);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ // state lokal untuk form
+ const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
+
+ // load data awal
useShallowEffect(() => {
if (!nilaiKonservasiState.findById.data) {
nilaiKonservasiState.findById.initialize();
}
}, []);
+ // sync state lokal saat data backend siap
useEffect(() => {
if (nilaiKonservasiState.findById.data) {
- setJudul(nilaiKonservasiState.findById.data.judul ?? '');
- setContent(nilaiKonservasiState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: nilaiKonservasiState.findById.data.judul ?? '',
+ deskripsi: nilaiKonservasiState.findById.data.deskripsi ?? '',
+ });
}
}, [nilaiKonservasiState.findById.data]);
+ const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
const submit = () => {
if (nilaiKonservasiState.findById.data) {
- nilaiKonservasiState.findById.data.judul = judul;
- nilaiKonservasiState.findById.data.deskripsi = content;
+ // update global state saat submit
+ nilaiKonservasiState.findById.data.judul = formData.judul;
+ nilaiKonservasiState.findById.data.deskripsi = formData.deskripsi;
nilaiKonservasiState.update.save(nilaiKonservasiState.findById.data);
}
router.push('/admin/lingkungan/konservasi-adat-bali/nilai-konservasi-adat');
@@ -46,12 +56,7 @@ function EditNilaiKonservasiAdat() {
{/* Header */}
- router.back()}
- p="xs"
- radius="md"
- >
+ router.back()} p="xs" radius="md">
@@ -75,8 +80,8 @@ function EditNilaiKonservasiAdat() {
handleChange('judul', val)}
+ initialContent={formData.judul}
/>
@@ -86,8 +91,8 @@ function EditNilaiKonservasiAdat() {
handleChange('deskripsi', val)}
+ initialContent={formData.deskripsi}
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/nilai-konservasi-adat/page.tsx b/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/nilai-konservasi-adat/page.tsx
index 644e8d90..32893221 100644
--- a/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/nilai-konservasi-adat/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/konservasi-adat-bali/nilai-konservasi-adat/page.tsx
@@ -55,6 +55,7 @@ function Page() {
fw={600}
c="black"
dangerouslySetInnerHTML={{ __html: listNilaiKonservasiAdat.findById.data.judul }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -64,6 +65,7 @@ function Page() {
c="dimmed"
lineClamp={10}
dangerouslySetInnerHTML={{ __html: listNilaiKonservasiAdat.findById.data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/edit/page.tsx
index 49034b5c..56c9035d 100644
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/edit/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
+
import pengelolaanSampahState from '@/app/admin/(dashboard)/_state/lingkungan/pengelolaan-sampah';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
@@ -13,9 +14,9 @@ import { useProxy } from 'valtio/utils';
const LeafletMapEdit = dynamic(() => import('@/app/admin/(dashboard)/_com/leafletMapEdit'), { ssr: false });
function EditKeteranganBankSampahTerdekat() {
- const keteranganState = useProxy(pengelolaanSampahState.keteranganSampah)
+ const keteranganState = useProxy(pengelolaanSampahState.keteranganSampah);
const router = useRouter();
- const params = useParams()
+ const params = useParams();
const [markerPosition, setMarkerPosition] = useState<{ lat: number; lng: number } | null>(null);
const [formData, setFormData] = useState({
@@ -24,10 +25,11 @@ function EditKeteranganBankSampahTerdekat() {
namaTempatMaps: '',
lat: 0,
lng: 0,
- })
+ });
+ // Load data ketika component mount
useEffect(() => {
- const loadKeteranganBankSampahTerdekat = async () => {
+ const loadKeterangan = async () => {
const id = params?.id as string;
if (!id) return;
@@ -35,14 +37,8 @@ function EditKeteranganBankSampahTerdekat() {
const data = await keteranganState.edit.load(id);
if (data) {
keteranganState.edit.id = id;
- keteranganState.edit.form = {
- name: data.name,
- alamat: data.alamat,
- namaTempatMaps: data.namaTempatMaps,
- lat: data.lat,
- lng: data.lng,
- };
+ // Update local formData dan markerPosition
setFormData({
name: data.name,
alamat: data.alamat,
@@ -50,51 +46,47 @@ function EditKeteranganBankSampahTerdekat() {
lat: data.lat,
lng: data.lng,
});
-
setMarkerPosition({ lat: data.lat, lng: data.lng });
}
} catch (error) {
- console.error("Error loading pengelolaan sampah:", error);
- toast.error("Gagal memuat data pengelolaan sampah");
+ console.error(error);
+ toast.error('Gagal memuat data pengelolaan sampah');
}
- }
+ };
- loadKeteranganBankSampahTerdekat();
+ loadKeterangan();
}, [params?.id]);
+ const handleInputChange = (field: keyof typeof formData, value: string | number) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
- if (!formData.name.trim()) {
- return toast.error('Nama bank sampah harus diisi');
- }
- if (!formData.alamat.trim()) {
- return toast.error('Alamat harus diisi');
- }
- if (!formData.namaTempatMaps.trim()) {
- return toast.error('Nama tempat di Maps harus diisi');
- }
- if (!markerPosition) {
- return toast.error('Silakan pilih lokasi di peta');
- }
+ if (!formData.name.trim()) return toast.error('Nama bank sampah harus diisi');
+ if (!formData.alamat.trim()) return toast.error('Alamat harus diisi');
+ if (!formData.namaTempatMaps.trim()) return toast.error('Nama tempat di Maps harus diisi');
+ if (!markerPosition) return toast.error('Silakan pilih lokasi di peta');
+ // Update global state hanya saat submit
keteranganState.edit.form = {
- ...keteranganState.edit.form,
name: formData.name.trim(),
alamat: formData.alamat.trim(),
namaTempatMaps: formData.namaTempatMaps.trim(),
lat: markerPosition.lat,
lng: markerPosition.lng,
};
-
+
await keteranganState.edit.update();
toast.success('Data bank sampah berhasil diperbarui');
keteranganState.findUnique.data = null;
router.push("/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat");
} catch (error) {
- console.error("Error updating pengelolaan sampah:", error);
+ console.error(error);
toast.error(error instanceof Error ? error.message : 'Gagal memperbarui data bank sampah');
}
- }
+ };
+
return (
@@ -121,44 +113,34 @@ function EditKeteranganBankSampahTerdekat() {
label="Nama Bank Sampah"
placeholder="Masukkan nama bank sampah"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={(e) => handleInputChange('name', e.target.value)}
required
/>
-
setFormData({ ...formData, alamat: e.target.value })}
+ onChange={(e) => handleInputChange('alamat', e.target.value)}
required
/>
-
setFormData({ ...formData, namaTempatMaps: e.target.value })}
+ onChange={(e) => handleInputChange('namaTempatMaps', e.target.value)}
required
/>
-
- Pilih Lokasi di Peta
-
-
- Klik pada peta untuk menandai lokasi
-
+ Pilih Lokasi di Peta
+ Klik pada peta untuk menandai lokasi
{
setMarkerPosition(pos);
- setFormData(prev => ({
- ...prev,
- lat: pos.lat,
- lng: pos.lng,
- }));
+ handleInputChange('lat', pos.lat);
+ handleInputChange('lng', pos.lng);
}}
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/page.tsx
index 417cd170..195861d8 100644
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/page.tsx
@@ -78,12 +78,12 @@ function DetailKeteranganBankSampahTerdekat() {
Alamat
- {keteranganState.findUnique.data?.alamat}
+ {keteranganState.findUnique.data?.alamat}
Nama Tempat di Maps
- {keteranganState.findUnique.data?.namaTempatMaps}
+ {keteranganState.findUnique.data?.namaTempatMaps}
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/create/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/create/page.tsx
index 883c89bf..ad8ae867 100644
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/create/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/create/page.tsx
@@ -76,7 +76,7 @@ function CreateKeteranganBankSampahTerdekat() {
(keteranganState.create.form.name = e.target.value)}
required
/>
@@ -84,7 +84,7 @@ function CreateKeteranganBankSampahTerdekat() {
(keteranganState.create.form.alamat = e.target.value)}
required
/>
@@ -92,7 +92,7 @@ function CreateKeteranganBankSampahTerdekat() {
(keteranganState.create.form.namaTempatMaps = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/[id]/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/[id]/page.tsx
index e0427dc8..683c410a 100644
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/[id]/page.tsx
@@ -17,15 +17,16 @@ interface FormProgramKreatif {
type IconKey = 'ekowisata' | 'kompetisi' | 'wisata' | 'ekonomi' | 'sampah' | 'truck' | 'scale' | 'clipboard' | 'trash';
-
function EditProgramKreatifDesa() {
const stateSampah = useProxy(pengelolaanSampahState.pengelolaanSampah)
const params = useParams()
const router = useRouter();
+
+ // State lokal untuk form controlled
const [formData, setFormData] = useState({
name: '',
icon: '',
- })
+ });
useEffect(() => {
const loadProgramKreatif = async () => {
@@ -35,14 +36,7 @@ function EditProgramKreatifDesa() {
try {
const data = await stateSampah.update.load(id);
if (data) {
- // β¬οΈ FIX PENTING: tambahkan ini
- stateSampah.update.id = id;
-
- stateSampah.update.form = {
- name: data.name,
- icon: data.icon,
- };
-
+ stateSampah.update.id = id; // simpan id di global state
setFormData({
name: data.name,
icon: data.icon,
@@ -52,21 +46,19 @@ function EditProgramKreatifDesa() {
console.error("Error loading pengelolaan sampah:", error);
toast.error("Gagal memuat data pengelolaan sampah");
}
- }
+ };
loadProgramKreatif();
}, [params?.id]);
-
-
const handleSubmit = async () => {
try {
+ // Update global state HANYA saat submit
stateSampah.update.form = {
- ...stateSampah.update.form,
name: formData.name.trim(),
icon: formData.icon.trim(),
};
-
+
await stateSampah.update.submit();
toast.success('Data pengelolaan sampah berhasil diperbarui!');
router.push("/admin/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah");
@@ -74,17 +66,13 @@ function EditProgramKreatifDesa() {
console.error("Error updating pengelolaan sampah:", error);
toast.error(error instanceof Error ? error.message : "Gagal memperbarui data pengelolaan sampah");
}
- }
+ };
+
return (
- router.back()}
- p="xs"
- radius="md"
- >
+ router.back()} p="xs" radius="md">
@@ -106,14 +94,7 @@ function EditProgramKreatifDesa() {
label="Nama Pengelolaan Sampah"
placeholder="Masukkan nama pengelolaan sampah"
value={formData.name}
- onChange={(e) => {
- const value = e.target.value;
- setFormData(prev => ({
- ...prev,
- name: value
- }));
- stateSampah.update.form.name = value;
- }}
+ onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
required
/>
@@ -123,10 +104,7 @@ function EditProgramKreatifDesa() {
{
- setFormData(prev => ({ ...prev, icon: value }));
- stateSampah.update.form.icon = value;
- }}
+ onChange={(value) => setFormData(prev => ({ ...prev, icon: value }))}
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/create/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/create/page.tsx
index 563befe5..a413905b 100644
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/create/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/create/page.tsx
@@ -61,7 +61,7 @@ function CreatePengelolaanSampahBankSampah() {
(stateCreate.create.form.name = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/program-penghijauan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/program-penghijauan/[id]/edit/page.tsx
index 274c1d16..141f920f 100644
--- a/src/app/admin/(dashboard)/lingkungan/program-penghijauan/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/program-penghijauan/[id]/edit/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
-'use client'
+'use client';
+
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import SelectIconProgramEdit from '@/app/admin/(dashboard)/_com/selectIconEdit';
import programPenghijauanState from '@/app/admin/(dashboard)/_state/lingkungan/program-penghijauan';
@@ -50,13 +51,14 @@ function EditProgramPenghijauan() {
const [formData, setFormData] = useState({
name: '',
- deskripsi: '',
judul: '',
+ deskripsi: '',
icon: '',
});
+ // Load data program penghijauan
useEffect(() => {
- const loadProgramPenghijauan = async () => {
+ const loadProgram = async () => {
const id = params?.id as string;
if (!id) return;
@@ -64,14 +66,6 @@ function EditProgramPenghijauan() {
const data = await stateProgramPenghijauan.update.load(id);
if (data) {
stateProgramPenghijauan.update.id = id;
-
- stateProgramPenghijauan.update.form = {
- name: data.name,
- judul: data.judul,
- deskripsi: data.deskripsi,
- icon: data.icon,
- };
-
setFormData({
name: data.name,
judul: data.judul,
@@ -85,19 +79,21 @@ function EditProgramPenghijauan() {
}
};
- loadProgramPenghijauan();
+ loadProgram();
}, [params?.id]);
+ // Hanya update global state saat submit
const handleSubmit = async () => {
try {
stateProgramPenghijauan.update.form = {
- ...stateProgramPenghijauan.update.form,
name: formData.name.trim(),
- deskripsi: formData.deskripsi.trim(),
judul: formData.judul.trim(),
+ deskripsi: formData.deskripsi.trim(),
icon: formData.icon.trim(),
};
+
await stateProgramPenghijauan.update.submit();
+ toast.success('Program penghijauan berhasil diperbarui');
router.push('/admin/lingkungan/program-penghijauan');
} catch (error) {
console.error('Error updating program penghijauan:', error);
@@ -107,7 +103,7 @@ function EditProgramPenghijauan() {
return (
- {/* Header dengan back button */}
+ {/* Header */}
- setFormData({
- ...formData,
- name: val.target.value,
- })
+ onChange={(e) =>
+ setFormData((prev) => ({ ...prev, name: e.target.value }))
}
required
/>
@@ -151,11 +144,8 @@ function EditProgramPenghijauan() {
value={formData.judul}
label="Judul Deskripsi Program Penghijauan"
placeholder="Masukkan judul deskripsi program penghijauan"
- onChange={(val) =>
- setFormData({
- ...formData,
- judul: val.target.value,
- })
+ onChange={(e) =>
+ setFormData((prev) => ({ ...prev, judul: e.target.value }))
}
required
/>
@@ -166,10 +156,9 @@ function EditProgramPenghijauan() {
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- stateProgramPenghijauan.update.form.deskripsi = htmlContent;
- }}
+ onChange={(htmlContent) =>
+ setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
+ }
/>
@@ -179,10 +168,9 @@ function EditProgramPenghijauan() {
{
- setFormData((prev) => ({ ...prev, icon: value }));
- stateProgramPenghijauan.update.form.icon = value;
- }}
+ onChange={(value) =>
+ setFormData((prev) => ({ ...prev, icon: value }))
+ }
/>
diff --git a/src/app/admin/(dashboard)/lingkungan/program-penghijauan/[id]/page.tsx b/src/app/admin/(dashboard)/lingkungan/program-penghijauan/[id]/page.tsx
index 44df2081..a2857cfc 100644
--- a/src/app/admin/(dashboard)/lingkungan/program-penghijauan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/program-penghijauan/[id]/page.tsx
@@ -112,7 +112,7 @@ function DetailProgramPenghijauan() {
Deskripsi
-
+
{/* Tombol aksi */}
diff --git a/src/app/admin/(dashboard)/lingkungan/program-penghijauan/create/page.tsx b/src/app/admin/(dashboard)/lingkungan/program-penghijauan/create/page.tsx
index a787090d..5b8e1ce5 100644
--- a/src/app/admin/(dashboard)/lingkungan/program-penghijauan/create/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/program-penghijauan/create/page.tsx
@@ -69,7 +69,7 @@ function CreateProgramPenghijauan() {
Nama Program Penghijauan}
placeholder="Masukkan nama program penghijauan"
- value={stateCreate.create.form.name || ''}
+ defaultValue={stateCreate.create.form.name || ''}
onChange={(e) => (stateCreate.create.form.name = e.target.value)}
required
/>
@@ -86,7 +86,7 @@ function CreateProgramPenghijauan() {
Judul Deskripsi Program Penghijauan}
placeholder="Masukkan judul deskripsi program penghijauan"
- value={stateCreate.create.form.judul || ''}
+ defaultValue={stateCreate.create.form.judul || ''}
onChange={(e) => (stateCreate.create.form.judul = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/_lib/layoutTabs.tsx
index 553daf17..2f2d3997 100644
--- a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/_lib/layoutTabs.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/_lib/layoutTabs.tsx
@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
-import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
+import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
import { IconSchool, IconStar } from '@tabler/icons-react';
@@ -58,36 +58,42 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
radius="lg"
keepMounted={false}
>
-
+
- {tabs.map((tab, i) => (
-
-
+ {tabs.map((tab, i) => (
+
- {tab.label}
-
-
- ))}
-
+
+ {tab.label}
+
+
+ ))}
+
+
{tabs.map((tab, i) => (
Nama Lengkap
- {data.namaLengkap || '-'}
+ {data.namaLengkap || '-'}
NIK
@@ -75,7 +75,7 @@ function DetailBeasiswaPendaftar() {
Tempat Lahir
- {data.tempatLahir || '-'}
+ {data.tempatLahir || '-'}
Tanggal Lahir
@@ -97,11 +97,11 @@ function DetailBeasiswaPendaftar() {
Alamat KTP
- {data.alamatKTP || '-'}
+ {data.alamatKTP || '-'}
Alamat Domisili
- {data.alamatDomisili || '-'}
+ {data.alamatDomisili || '-'}
No HP
diff --git a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/[id]/page.tsx b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/[id]/page.tsx
index 17188b39..3a745c50 100644
--- a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/[id]/page.tsx
@@ -97,7 +97,7 @@ function EditProgramKreatifDesa() {
{
const value = e.target.value;
setFormData(prev => ({
diff --git a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/create/page.tsx b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/create/page.tsx
index d0859b4b..f7fb5f93 100644
--- a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/create/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/create/page.tsx
@@ -61,7 +61,7 @@ function CreateKeunggulanProgram() {
(stateCreate.create.form.judul = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/page.tsx b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/page.tsx
index 4d8bf0d4..f2f18216 100644
--- a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/page.tsx
@@ -99,22 +99,22 @@ function ListKeunggulanProgram({ search }: { search: string }) {
- Nama Keunggulan Program
- Deskripsi
- Edit
- Delete
+ Nama Keunggulan Program
+ Deskripsi
+ Edit
+ Delete
{filteredData.length > 0 ? (
filteredData.map((item) => (
-
+
{item.judul}
-
+
-
+
-
+
-
+
- {tabs.map((tab, i) => (
-
-
+ {tabs.map((tab, i) => (
+
- {tab.label}
-
-
- ))}
-
+
+ {tab.label}
+
+
+ ))}
+
+
{tabs.map((tab, i) => (
{
if (editState.findById.data) {
- setJudul(editState.findById.data.judul ?? '');
- setContent(editState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: editState.findById.data.judul ?? '',
+ deskripsi: editState.findById.data.deskripsi ?? ''
+ });
}
}, [editState.findById.data]);
+ const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
- if (!judul.trim()) {
+ if (!formData.judul.trim()) {
toast.error('Judul wajib diisi');
return;
}
@@ -61,9 +68,13 @@ function EditFasilitasYangDisediakan() {
setIsSubmitting(true);
try {
if (editState.findById.data) {
- editState.findById.data.judul = judul;
- editState.findById.data.deskripsi = content;
- await editState.update.save(editState.findById.data);
+ // Update global state hanya saat submit
+ const payload = {
+ ...editState.findById.data,
+ judul: formData.judul,
+ deskripsi: formData.deskripsi
+ };
+ await editState.update.save(payload);
toast.success('Berhasil menyimpan perubahan');
router.push('/admin/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan');
@@ -78,7 +89,6 @@ function EditFasilitasYangDisediakan() {
const handleBack = () => router.back();
- // Loading state
if (editState.findById.loading) {
return (
@@ -123,9 +133,9 @@ function EditFasilitasYangDisediakan() {
Judul}
placeholder="Masukkan judul fasilitas"
- value={judul}
- onChange={(e) => setJudul(e.currentTarget.value)}
- error={!judul && 'Judul wajib diisi'}
+ value={formData.judul}
+ onChange={(e) => handleChange('judul', e.currentTarget.value)}
+ error={!formData.judul && 'Judul wajib diisi'}
/>
{/* Deskripsi */}
@@ -133,8 +143,8 @@ function EditFasilitasYangDisediakan() {
Deskripsi
handleChange('deskripsi', value)}
+ initialContent={formData.deskripsi}
/>
@@ -144,7 +154,7 @@ function EditFasilitasYangDisediakan() {
bg={colors['blue-button']}
onClick={handleSubmit}
loading={isSubmitting || editState.update.loading}
- disabled={!judul}
+ disabled={!formData.judul}
>
{isSubmitting ? 'Menyimpan...' : 'Simpan Perubahan'}
diff --git a/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan/page.tsx b/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan/page.tsx
index 6e129fa0..b568cefa 100644
--- a/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan/page.tsx
@@ -81,6 +81,7 @@ function Page() {
ta="justify"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal/edit/page.tsx
index b1ca2730..5e871dea 100644
--- a/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal/edit/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal/edit/page.tsx
@@ -1,4 +1,5 @@
'use client'
+
import stateBimbinganBelajarDesa from '@/app/admin/(dashboard)/_state/pendidikan/bimbingan-belajar-desa';
import colors from '@/con/colors';
import {
@@ -33,27 +34,33 @@ function EditLokasiDanJadwal() {
const router = useRouter();
const editState = useProxy(stateBimbinganBelajarDesa.lokasiDanJadwalState);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ // state lokal untuk form, tidak langsung merubah global state
+ const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
const [isSubmitting, setIsSubmitting] = useState(false);
- // load data sekali
+ // Load data sekali
useShallowEffect(() => {
if (!editState.findById.data) {
editState.findById.initialize();
}
}, []);
- // isi state ketika data loaded
+ // Isi state lokal setelah data global ter-load
useEffect(() => {
if (editState.findById.data) {
- setJudul(editState.findById.data.judul ?? '');
- setContent(editState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: editState.findById.data.judul ?? '',
+ deskripsi: editState.findById.data.deskripsi ?? ''
+ });
}
}, [editState.findById.data]);
+ const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
- if (!judul.trim()) {
+ if (!formData.judul.trim()) {
toast.error('Judul wajib diisi');
return;
}
@@ -61,9 +68,13 @@ function EditLokasiDanJadwal() {
setIsSubmitting(true);
try {
if (editState.findById.data) {
- editState.findById.data.judul = judul;
- editState.findById.data.deskripsi = content;
- await editState.update.save(editState.findById.data);
+ // update global state hanya saat submit
+ const updatedData = {
+ ...editState.findById.data,
+ judul: formData.judul,
+ deskripsi: formData.deskripsi
+ };
+ await editState.update.save(updatedData);
toast.success('Berhasil menyimpan perubahan');
router.push('/admin/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal');
@@ -78,7 +89,6 @@ function EditLokasiDanJadwal() {
const handleBack = () => router.back();
- // loading state
if (editState.findById.loading) {
return (
@@ -92,15 +102,10 @@ function EditLokasiDanJadwal() {
return (
- {/* Header dengan tombol kembali */}
+ {/* Header */}
-
+
@@ -125,9 +130,9 @@ function EditLokasiDanJadwal() {
Judul}
placeholder="Masukkan judul lokasi/jadwal"
- value={judul}
- onChange={(e) => setJudul(e.currentTarget.value)}
- error={!judul && 'Judul wajib diisi'}
+ value={formData.judul}
+ onChange={(e) => handleChange('judul', e.currentTarget.value)}
+ error={!formData.judul && 'Judul wajib diisi'}
/>
{/* Deskripsi Field */}
@@ -135,8 +140,8 @@ function EditLokasiDanJadwal() {
Deskripsi
handleChange('deskripsi', value)}
+ initialContent={formData.deskripsi}
/>
@@ -146,7 +151,7 @@ function EditLokasiDanJadwal() {
bg={colors['blue-button']}
onClick={handleSubmit}
loading={isSubmitting || editState.update.loading}
- disabled={!judul}
+ disabled={!formData.judul}
>
{isSubmitting ? 'Menyimpan...' : 'Simpan Perubahan'}
diff --git a/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal/page.tsx b/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal/page.tsx
index 360465dd..f9de4d6a 100644
--- a/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal/page.tsx
@@ -71,6 +71,7 @@ function Page() {
fw="bold"
c={colors['blue-button']}
dangerouslySetInnerHTML={{ __html: data.judul }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -81,6 +82,7 @@ function Page() {
ta="justify"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/tujuan-program/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/tujuan-program/edit/page.tsx
index 2b318d89..55a0d1c6 100644
--- a/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/tujuan-program/edit/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/tujuan-program/edit/page.tsx
@@ -1,4 +1,5 @@
'use client'
+
import stateBimbinganBelajarDesa from '@/app/admin/(dashboard)/_state/pendidikan/bimbingan-belajar-desa';
import colors from '@/con/colors';
import {
@@ -33,26 +34,31 @@ function EditTujuanProgram() {
const router = useRouter();
const editState = useProxy(stateBimbinganBelajarDesa.stateTujuanProgram);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ // gabung judul & content jadi formData
+ const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
const [isSubmitting, setIsSubmitting] = useState(false);
- // load data once
+ // load data sekali
useShallowEffect(() => {
- if (!editState.findById.data) {
- editState.findById.initialize();
- }
+ if (!editState.findById.data) editState.findById.initialize();
}, []);
+ // sync data dari global state ke local formData sekali setelah load
useEffect(() => {
if (editState.findById.data) {
- setJudul(editState.findById.data.judul ?? '');
- setContent(editState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: editState.findById.data.judul ?? '',
+ deskripsi: editState.findById.data.deskripsi ?? '',
+ });
}
}, [editState.findById.data]);
+ const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
- if (!judul.trim()) {
+ if (!formData.judul.trim()) {
toast.error('Judul wajib diisi');
return;
}
@@ -60,8 +66,9 @@ function EditTujuanProgram() {
setIsSubmitting(true);
try {
if (editState.findById.data) {
- editState.findById.data.judul = judul;
- editState.findById.data.deskripsi = content;
+ // update global state hanya saat submit
+ editState.findById.data.judul = formData.judul;
+ editState.findById.data.deskripsi = formData.deskripsi;
await editState.update.save(editState.findById.data);
toast.success('Berhasil menyimpan perubahan');
@@ -117,18 +124,20 @@ function EditTujuanProgram() {
Judul}
placeholder="Masukkan judul program"
- value={judul}
- onChange={(e) => setJudul(e.currentTarget.value)}
- error={!judul && 'Judul wajib diisi'}
+ value={formData.judul}
+ onChange={(e) => handleChange('judul', e.currentTarget.value)}
+ error={!formData.judul && 'Judul wajib diisi'}
/>
{/* Deskripsi Field */}
- Deskripsi
+
+ Deskripsi
+
handleChange('deskripsi', value)}
/>
@@ -138,7 +147,7 @@ function EditTujuanProgram() {
bg={colors['blue-button']}
onClick={handleSubmit}
loading={isSubmitting || editState.update.loading}
- disabled={!judul}
+ disabled={!formData.judul}
>
{isSubmitting ? 'Menyimpan...' : 'Simpan Perubahan'}
diff --git a/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/tujuan-program/page.tsx b/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/tujuan-program/page.tsx
index 30544096..45b1c4db 100644
--- a/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/tujuan-program/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/bimbingan-belajar-desa/tujuan-program/page.tsx
@@ -93,6 +93,7 @@ function Page() {
ta="justify"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/data-pendidikan/[id]/page.tsx b/src/app/admin/(dashboard)/pendidikan/data-pendidikan/[id]/page.tsx
index edaad7da..114d6c66 100644
--- a/src/app/admin/(dashboard)/pendidikan/data-pendidikan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/data-pendidikan/[id]/page.tsx
@@ -5,7 +5,7 @@ import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, TextInput, Title, Tooltip } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import dataPendidikan from '../../../_state/pendidikan/data-pendidikan';
@@ -15,23 +15,35 @@ export default function EditDataPendidikan() {
const stateDPM = useProxy(dataPendidikan);
const id = params.id;
+ // state lokal untuk form
+ const [formData, setFormData] = useState({
+ name: '',
+ jumlah: '',
+ });
+
// Load data saat mount
useEffect(() => {
if (id) {
stateDPM.findUnique.load(id).then(() => {
const data = stateDPM.findUnique.data;
if (data) {
- stateDPM.update.form = {
+ setFormData({
name: data.name || '',
jumlah: data.jumlah || '',
- };
+ });
}
});
}
}, [id]);
+ const handleChange = (field: 'name' | 'jumlah', value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
+ // update global state hanya saat submit
stateDPM.update.id = id;
+ stateDPM.update.form = { ...formData };
await stateDPM.update.submit();
router.push('/admin/pendidikan/data-pendidikan');
};
@@ -59,8 +71,8 @@ export default function EditDataPendidikan() {
(stateDPM.update.form.name = e.currentTarget.value)}
+ value={formData.name}
+ onChange={(e) => handleChange('name', e.currentTarget.value)}
radius="md"
required
/>
@@ -68,8 +80,8 @@ export default function EditDataPendidikan() {
label="Jumlah Peserta"
type="number"
placeholder="Masukkan jumlah peserta"
- value={stateDPM.update.form.jumlah}
- onChange={(e) => (stateDPM.update.form.jumlah = e.currentTarget.value)}
+ value={formData.jumlah}
+ onChange={(e) => handleChange('jumlah', e.currentTarget.value)}
radius="md"
required
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/data-pendidikan/create/page.tsx b/src/app/admin/(dashboard)/pendidikan/data-pendidikan/create/page.tsx
index 602849f4..52f8e1f7 100644
--- a/src/app/admin/(dashboard)/pendidikan/data-pendidikan/create/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/data-pendidikan/create/page.tsx
@@ -51,7 +51,7 @@ export default function CreateDataPendidikan() {
(stateDPM.create.form.name = e.currentTarget.value)}
radius="md"
required
@@ -59,7 +59,7 @@ export default function CreateDataPendidikan() {
(stateDPM.create.form.jumlah = e.currentTarget.value)}
radius="md"
type="number"
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/_lib/layoutTabs.tsx
index e0493f56..e7991d96 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/_lib/layoutTabs.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/_lib/layoutTabs.tsx
@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
-import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
+import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
import { IconBuilding, IconChalkboard, IconMicroscope, IconSchool } from '@tabler/icons-react';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
@@ -72,30 +72,36 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
radius="lg"
keepMounted={false}
>
-
+
- {tabs.map((tab, i) => (
-
-
- {tab.label}
-
-
- ))}
-
+ >
+ {tabs.map((tab, i) => (
+
+
+ {tab.label}
+
+
+ ))}
+
+
{tabs.map((tab, i) => (
{children}
@@ -121,4 +128,3 @@ export default LayoutTabs;
-
\ No newline at end of file
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/jenjang-pendidikan/[id]/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/jenjang-pendidikan/[id]/page.tsx
index ff1ce12c..02b1003b 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/jenjang-pendidikan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/jenjang-pendidikan/[id]/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
+
import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud';
import colors from '@/con/colors';
import {
@@ -22,64 +23,62 @@ function EditJenjangPendidikan() {
const router = useRouter();
const params = useParams();
const id = params?.id as string;
+
const stateJenjang = useProxy(infoSekolahPaud.jenjangPendidikan);
- const [formData, setFormData] = useState({
- nama: "",
- });
+ const [formData, setFormData] = useState({ nama: '' });
+ const [loading, setLoading] = useState(true);
+ // Load data sekali saat component mount
useEffect(() => {
- const loadJenjangPendidikan = async () => {
- if (!id) return;
+ if (!id) return;
+ const loadJenjang = async () => {
try {
const data = await stateJenjang.edit.load(id);
-
if (data) {
- stateJenjang.edit.id = id;
- setFormData({
- nama: data.nama || '',
- });
+ stateJenjang.edit.id = id; // tetap simpan di global state
+ setFormData({ nama: data.nama || '' });
}
} catch (error) {
- console.error("Error loading jenjang pendidikan:", error);
- toast.error("Gagal memuat data jenjang pendidikan");
+ console.error('Error loading jenjang pendidikan:', error);
+ toast.error('Gagal memuat data jenjang pendidikan');
+ } finally {
+ setLoading(false);
}
};
- loadJenjangPendidikan();
+ loadJenjang();
}, [id]);
+ const handleChange = (field: string, value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
+ if (!formData.nama.trim()) {
+ toast.error('Nama jenjang pendidikan tidak boleh kosong');
+ return;
+ }
+
try {
- if (!formData.nama.trim()) {
- toast.error('Nama jenjang pendidikan tidak boleh kosong');
- return;
- }
-
- stateJenjang.edit.form = {
- nama: formData.nama.trim(),
- };
-
- if (!stateJenjang.edit.id) {
- stateJenjang.edit.id = id;
- }
+ stateJenjang.edit.form = { nama: formData.nama.trim() };
+ if (!stateJenjang.edit.id) stateJenjang.edit.id = id;
const success = await stateJenjang.edit.update();
-
if (success) {
- toast.success("Jenjang pendidikan berhasil diperbarui!");
- router.push("/admin/pendidikan/info-sekolah/jenjang-pendidikan");
+ toast.success('Jenjang pendidikan berhasil diperbarui!');
+ router.push('/admin/pendidikan/info-sekolah/jenjang-pendidikan');
}
} catch (error) {
- console.error("Error updating jenjang pendidikan:", error);
- toast.error("Terjadi kesalahan saat memperbarui jenjang pendidikan");
+ console.error('Error updating jenjang pendidikan:', error);
+ toast.error('Terjadi kesalahan saat memperbarui jenjang pendidikan');
}
};
return (
- {/* Header Back + Title */}
+ {/* Header */}
router.back()} p="xs" radius="md">
@@ -91,7 +90,7 @@ function EditJenjangPendidikan() {
- {/* Form Container */}
+ {/* Form */}
setFormData({ ...formData, nama: e.target.value })}
+ onChange={(e) => handleChange('nama', e.target.value)}
required
+ disabled={loading}
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/jenjang-pendidikan/create/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/jenjang-pendidikan/create/page.tsx
index 62b33467..fe8b30d5 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/jenjang-pendidikan/create/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/jenjang-pendidikan/create/page.tsx
@@ -74,7 +74,7 @@ function CreateJenjangPendidikan() {
(stateJenjang.create.form.nama = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/lembaga/[id]/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/lembaga/[id]/edit/page.tsx
index 620d13e1..e644a878 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/lembaga/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/lembaga/[id]/edit/page.tsx
@@ -31,6 +31,7 @@ export default function EditLembaga() {
jenjangId: '',
});
+ // Load jenjang pendidikan dan data lembaga
useEffect(() => {
infoSekolahPaud.jenjangPendidikan.findMany.load();
@@ -46,12 +47,17 @@ export default function EditLembaga() {
}
}, [id]);
+ const handleChange = (field: 'nama' | 'jenjangId', value: string) => {
+ setForm((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
if (!form.nama || !form.jenjangId) {
toast.warn('Nama dan jenjang pendidikan harus diisi');
return;
}
+ // Update global state hanya saat submit
state.edit.id = id;
state.edit.form = form;
@@ -65,7 +71,7 @@ export default function EditLembaga() {
return (
- {/* Header dengan back button */}
+ {/* Header */}
- {/* Card Form */}
+ {/* Form Card */}
setForm({ ...form, nama: e.currentTarget.value })}
+ onChange={(e) => handleChange('nama', e.currentTarget.value)}
required
/>
@@ -111,7 +117,7 @@ export default function EditLembaga() {
})) || []
}
value={form.jenjangId}
- onChange={(val) => setForm({ ...form, jenjangId: val || '' })}
+ onChange={(val) => handleChange('jenjangId', val || '')}
required
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/lembaga/create/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/lembaga/create/page.tsx
index 972d32da..cbf52bca 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/lembaga/create/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/lembaga/create/page.tsx
@@ -66,7 +66,7 @@ function CreateLembaga() {
>
{
stateLembaga.create.form.nama = val.target.value;
}}
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/[id]/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/[id]/edit/page.tsx
index abdf4f34..2cf22993 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/[id]/edit/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
+
import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud';
import colors from '@/con/colors';
import {
@@ -25,21 +26,22 @@ interface FormPengajar {
}
function EditPengajar() {
- const pengajarState = useProxy(infoSekolahPaud.pengajar)
- const params = useParams()
- const router = useRouter()
+ const pengajarState = useProxy(infoSekolahPaud.pengajar);
+ const params = useParams();
+ const router = useRouter();
const [formData, setFormData] = useState({
nama: '',
lembagaId: ''
- })
+ });
useEffect(() => {
- const loadPengajar = async () => {
+ const loadData = async () => {
const id = params?.id as string;
if (!id) return;
try {
+ infoSekolahPaud.lembagaPendidikan.findMany.load();
const data = await pengajarState.edit.load(id);
if (data) {
setFormData({
@@ -47,30 +49,35 @@ function EditPengajar() {
lembagaId: data.lembagaId || '',
});
}
- } catch (error) {
- console.error("Error loading pengajar:", error);
+ } catch (err) {
+ console.error(err);
toast.error("Gagal memuat data pengajar");
}
- }
- infoSekolahPaud.lembagaPendidikan.findMany.load()
- loadPengajar();
+ };
+
+ loadData();
}, [params?.id]);
+ const handleChange = (field: keyof FormPengajar, value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
+ // update global state hanya saat submit
pengajarState.edit.form = {
...pengajarState.edit.form,
nama: formData.nama.trim(),
- lembagaId: formData.lembagaId.trim(),
- }
- await pengajarState.edit.update()
- toast.success("Data pengajar berhasil diperbarui!")
+ lembagaId: formData.lembagaId.trim()
+ };
+ await pengajarState.edit.update();
+ toast.success("Data pengajar berhasil diperbarui!");
router.push("/admin/pendidikan/info-sekolah/pengajar");
- } catch (error) {
- console.error("Error updating pengajar:", error);
+ } catch (err) {
+ console.error(err);
toast.error("Terjadi kesalahan saat memperbarui pengajar");
}
- }
+ };
return (
@@ -100,7 +107,7 @@ function EditPengajar() {
label="Nama Pengajar"
placeholder="Masukkan nama pengajar"
value={formData.nama}
- onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
+ onChange={(e) => handleChange('nama', e.target.value)}
required
/>
@@ -112,7 +119,7 @@ function EditPengajar() {
label: k.nama
})) ?? []}
value={formData.lembagaId}
- onChange={(val) => setFormData({ ...formData, lembagaId: val ?? '' })}
+ onChange={(val) => handleChange('lembagaId', val ?? '')}
required
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/[id]/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/[id]/page.tsx
index 770bf166..f97d19c4 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/[id]/page.tsx
@@ -73,7 +73,7 @@ function DetailPengajar() {
Lembaga
- {data.lembaga?.nama || '-'}
+ {data.lembaga?.nama || '-'}
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/create/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/create/page.tsx
index 61abd8ef..22e683e7 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/create/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/pengajar/create/page.tsx
@@ -74,7 +74,7 @@ function CreatePengajar() {
Nama}
placeholder="Masukkan nama pengajar"
- value={stateCreate.create.form.nama}
+ defaultValue={stateCreate.create.form.nama}
onChange={(e) => (stateCreate.create.form.nama = e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/[id]/edit/page.tsx
index ff84fd8f..96818201 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/[id]/edit/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
+
import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud';
import colors from '@/con/colors';
import {
@@ -35,6 +36,7 @@ function EditSiswa() {
lembagaId: '',
});
+ // Load data siswa
useEffect(() => {
const loadSiswa = async () => {
const id = params?.id as string;
@@ -53,10 +55,20 @@ function EditSiswa() {
toast.error('Gagal memuat data siswa');
}
};
+
infoSekolahPaud.lembagaPendidikan.findMany.load();
loadSiswa();
}, [params?.id]);
+ // Handler update local formData
+ const handleChange = (field: keyof FormSiswa, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
+ // Submit ke global state
const handleSubmit = async () => {
try {
siswaState.edit.form = {
@@ -75,7 +87,7 @@ function EditSiswa() {
return (
- {/* Header dengan tombol kembali */}
+ {/* Header */}
router.back()} p="xs" radius="md">
@@ -87,7 +99,7 @@ function EditSiswa() {
- {/* Card Form */}
+ {/* Form Card */}
Nama Siswa}
placeholder="Masukkan nama siswa"
value={formData.nama}
- onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
+ onChange={(e) => handleChange('nama', e.target.value)}
required
/>
@@ -115,7 +127,7 @@ function EditSiswa() {
})) ?? []
}
value={formData.lembagaId}
- onChange={(val) => setFormData({ ...formData, lembagaId: val ?? '' })}
+ onChange={(val) => handleChange('lembagaId', val ?? '')}
required
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/[id]/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/[id]/page.tsx
index 56502739..519d2590 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/[id]/page.tsx
@@ -75,7 +75,7 @@ function DetailSiswa() {
Lembaga
- {data?.lembaga?.nama || '-'}
+ {data?.lembaga?.nama || '-'}
diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/create/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/create/page.tsx
index 7dd841a5..9ebe3f57 100644
--- a/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/create/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah/siswa/create/page.tsx
@@ -67,7 +67,7 @@ function CreateSiswa() {
>
{
stateCreate.create.form.nama = val.target.value;
}}
diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/layoutTabs.tsx
index b436082f..e83af8ac 100644
--- a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/layoutTabs.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/layoutTabs.tsx
@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
-import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
+import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
import { IconSchool, IconMapPin, IconBook2 } from '@tabler/icons-react';
@@ -66,36 +66,42 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
radius="lg"
keepMounted={false}
>
-
+
- {tabs.map((tab, i) => (
-
-
+ {tabs.map((tab, i) => (
+
- {tab.label}
-
-
- ))}
-
+
+ {tab.label}
+
+
+ ))}
+
+
{tabs.map((tab, i) => (
{
if (editState.findById.data) {
- setJudul(editState.findById.data.judul ?? '');
- setContent(editState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: editState.findById.data.judul ?? '',
+ content: editState.findById.data.deskripsi ?? '',
+ });
}
}, [editState.findById.data]);
+ const handleChange = (field: 'judul' | 'content', value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
- if (!judul.trim()) {
+ if (!formData.judul.trim()) {
toast.error('Judul wajib diisi');
return;
}
@@ -61,11 +66,15 @@ function EditJenisProgramYangDiselenggarakan() {
setIsSubmitting(true);
try {
if (editState.findById.data) {
- editState.findById.data.judul = judul;
- editState.findById.data.deskripsi = content;
- await editState.update.save(editState.findById.data);
+ const updatedData = {
+ ...editState.findById.data,
+ judul: formData.judul,
+ deskripsi: formData.content,
+ };
+ await editState.update.save(updatedData);
toast.success('Berhasil menyimpan perubahan');
+
router.push(
'/admin/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan'
);
@@ -117,17 +126,17 @@ function EditJenisProgramYangDiselenggarakan() {
Judul}
placeholder="Masukkan judul program"
- value={judul}
- onChange={(e) => setJudul(e.currentTarget.value)}
- error={!judul && 'Judul wajib diisi'}
+ value={formData.judul}
+ onChange={(e) => handleChange('judul', e.currentTarget.value)}
+ error={!formData.judul && 'Judul wajib diisi'}
/>
Deskripsi
handleChange('content', value)}
+ initialContent={formData.content}
/>
@@ -136,7 +145,7 @@ function EditJenisProgramYangDiselenggarakan() {
bg={colors['blue-button']}
onClick={handleSubmit}
loading={isSubmitting || editState.update.loading}
- disabled={!judul}
+ disabled={!formData.judul}
>
{isSubmitting ? 'Menyimpan...' : 'Simpan Perubahan'}
diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/page.tsx
index 010ef66b..d7afd755 100644
--- a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/page.tsx
@@ -83,6 +83,7 @@ function Page() {
fw="bold"
c={colors['blue-button']}
dangerouslySetInnerHTML={{ __html: data.judul }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -93,6 +94,7 @@ function Page() {
ta="justify"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/edit/page.tsx
index c04903e3..7fe96497 100644
--- a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/edit/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/edit/page.tsx
@@ -1,4 +1,5 @@
'use client'
+
import pendidikanNonFormalState from '@/app/admin/(dashboard)/_state/pendidikan/pendidikan-non-formal';
import colors from '@/con/colors';
import {
@@ -33,8 +34,11 @@ function EditTempatKegiatan() {
const router = useRouter();
const editState = useProxy(pendidikanNonFormalState.stateTempatKegiatan);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ // state lokal form
+ const [formData, setFormData] = useState({
+ judul: '',
+ deskripsi: '',
+ });
const [isSubmitting, setIsSubmitting] = useState(false);
// load data pertama kali
@@ -44,16 +48,22 @@ function EditTempatKegiatan() {
}
}, []);
- // sync state dengan data hasil fetch
+ // sync state lokal ketika data fetch selesai
useEffect(() => {
if (editState.findById.data) {
- setJudul(editState.findById.data.judul ?? '');
- setContent(editState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: editState.findById.data.judul ?? '',
+ deskripsi: editState.findById.data.deskripsi ?? '',
+ });
}
}, [editState.findById.data]);
+ const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
- if (!judul.trim()) {
+ if (!formData.judul.trim()) {
toast.error('Judul wajib diisi');
return;
}
@@ -61,10 +71,13 @@ function EditTempatKegiatan() {
setIsSubmitting(true);
try {
if (editState.findById.data) {
- editState.findById.data.judul = judul;
- editState.findById.data.deskripsi = content;
- await editState.update.save(editState.findById.data);
+ const updatedData = {
+ ...editState.findById.data,
+ judul: formData.judul,
+ deskripsi: formData.deskripsi,
+ };
+ await editState.update.save(updatedData);
toast.success('Berhasil menyimpan perubahan');
router.push(
'/admin/pendidikan/pendidikan-non-formal/tempat-kegiatan'
@@ -117,9 +130,9 @@ function EditTempatKegiatan() {
Judul}
placeholder="Masukkan judul tempat kegiatan"
- value={judul}
- onChange={(e) => setJudul(e.currentTarget.value)}
- error={!judul && 'Judul wajib diisi'}
+ value={formData.judul}
+ onChange={(e) => handleChange('judul', e.currentTarget.value)}
+ error={!formData.judul && 'Judul wajib diisi'}
/>
@@ -128,8 +141,8 @@ function EditTempatKegiatan() {
handleChange('deskripsi', value)}
+ initialContent={formData.deskripsi}
/>
@@ -138,7 +151,7 @@ function EditTempatKegiatan() {
bg={colors['blue-button']}
onClick={handleSubmit}
loading={isSubmitting || editState.update.loading}
- disabled={!judul}
+ disabled={!formData.judul}
>
{isSubmitting ? 'Menyimpan...' : 'Simpan Perubahan'}
diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/page.tsx
index 49c6ce6f..d56908cf 100644
--- a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/page.tsx
@@ -71,6 +71,7 @@ function Page() {
fw="bold"
c={colors['blue-button']}
dangerouslySetInnerHTML={{ __html: data.judul }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -81,6 +82,7 @@ function Page() {
ta="justify"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/edit/page.tsx
index 35adde5f..fe91defd 100644
--- a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/edit/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/edit/page.tsx
@@ -1,4 +1,5 @@
'use client'
+
import pendidikanNonFormalState from '@/app/admin/(dashboard)/_state/pendidikan/pendidikan-non-formal';
import colors from '@/con/colors';
import {
@@ -30,41 +31,49 @@ function EditTujuanProgram() {
const router = useRouter();
const editState = useProxy(pendidikanNonFormalState.stateTujuanPendidikanNonFormal);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
const [isSubmitting, setIsSubmitting] = useState(false);
- // load data pertama kali
+ // Load data pertama kali
useShallowEffect(() => {
if (!editState.findById.data) {
editState.findById.initialize();
}
}, []);
- // sync data hasil fetch ke local state
+ // Sync hasil fetch ke local state (sekali)
useEffect(() => {
if (editState.findById.data) {
- setJudul(editState.findById.data.judul ?? '');
- setContent(editState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: editState.findById.data.judul ?? '',
+ deskripsi: editState.findById.data.deskripsi ?? ''
+ });
}
}, [editState.findById.data]);
+ const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
- if (!judul.trim()) {
+ if (!formData.judul.trim()) {
toast.error('Judul wajib diisi');
return;
}
+ if (!editState.findById.data) return;
+
setIsSubmitting(true);
try {
- if (editState.findById.data) {
- editState.findById.data.judul = judul;
- editState.findById.data.deskripsi = content;
- await editState.update.save(editState.findById.data);
+ const payload = {
+ ...editState.findById.data,
+ judul: formData.judul,
+ deskripsi: formData.deskripsi
+ };
- toast.success('Berhasil menyimpan perubahan');
- router.push('/admin/pendidikan/pendidikan-non-formal/tujuan-program');
- }
+ await editState.update.save(payload);
+ toast.success('Berhasil menyimpan perubahan');
+ router.push('/admin/pendidikan/pendidikan-non-formal/tujuan-program');
} catch (err) {
console.error('Error saving:', err);
toast.error('Gagal menyimpan data');
@@ -88,16 +97,13 @@ function EditTujuanProgram() {
return (
- {/* Back Button + Title */}
-
- Edit Tujuan Program
-
+ Edit Tujuan Program
Judul}
placeholder="Masukkan judul program"
- value={judul}
- onChange={(e) => setJudul(e.currentTarget.value)}
- error={!judul && 'Judul wajib diisi'}
+ value={formData.judul}
+ onChange={(e) => handleChange('judul', e.currentTarget.value)}
+ error={!formData.judul && 'Judul wajib diisi'}
/>
Deskripsi
handleChange('deskripsi', value)}
+ initialContent={formData.deskripsi}
/>
@@ -131,7 +137,7 @@ function EditTujuanProgram() {
bg={colors['blue-button']}
onClick={handleSubmit}
loading={isSubmitting || editState.update.loading}
- disabled={!judul}
+ disabled={!formData.judul}
>
{isSubmitting ? 'Menyimpan...' : 'Simpan Perubahan'}
diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/page.tsx
index dd045c94..dcc6470e 100644
--- a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/page.tsx
@@ -85,6 +85,7 @@ function Page() {
fw="bold"
c={colors['blue-button']}
dangerouslySetInnerHTML={{ __html: data.judul }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -95,6 +96,7 @@ function Page() {
ta="justify"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx
index 17674fea..3954de64 100644
--- a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx
@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
-import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
+import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
-import { IconBook2, IconCategory } from '@tabler/icons-react';
+import { IconBook2, IconCategory, IconUser } from '@tabler/icons-react';
function LayoutTabs({ children }: { children: React.ReactNode }) {
const router = useRouter();
@@ -25,6 +25,13 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
icon: ,
tooltip: "Atur kategori untuk buku digital",
},
+ {
+ label: "Peminjam",
+ value: "peminjam",
+ href: "/admin/pendidikan/perpustakaan-digital/peminjam",
+ icon: ,
+ tooltip: "Data Peminjam Buku",
+ },
];
const currentTab = tabs.find(tab => tab.href === pathname);
@@ -58,36 +65,42 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
radius="lg"
keepMounted={false}
>
-
+
- {tabs.map((tab, i) => (
-
-
+ {tabs.map((tab, i) => (
+
- {tab.label}
-
-
- ))}
-
+
+ {tab.label}
+
+
+ ))}
+
+
{tabs.map((tab, i) => (
(null);
const [file, setFile] = useState(null);
- const [formData, setFormData] = useState({
- judul: editState.update.form.judul || "",
- deskripsi: editState.update.form.deskripsi || "",
- imageId: editState.update.form.imageId || "",
- kategoriId: editState.update.form.kategoriId || "",
- })
+ // Load kategori & data awal
useEffect(() => {
perpustakaanDigitalState.kategoriBuku.findMany.load();
- const loadDataPerpustakaan = async () => {
- const id = params?.id as string;
+
+ const loadData = async () => {
+ const id = Array.isArray(params?.id) ? params.id[0] : params?.id;
if (!id) return;
try {
const data = await editState.update.load(id);
- if (data) {
- setFormData({
- judul: data.judul || "",
- deskripsi: data.deskripsi || "",
- imageId: data.imageId || "",
- kategoriId: data.kategoriId || "",
- });
- if (data.image?.link) setPreviewImage(data.image.link);
- }
- } catch (error) {
- console.error("Error loading data perpustakaan:", error);
- toast.error(error instanceof Error ? error.message : "Gagal mengambil data perpustakaan");
- }
- }
+ if (!data) return;
- loadDataPerpustakaan();
+ setFormData({
+ judul: data.judul || '',
+ deskripsi: data.deskripsi || '',
+ kategoriId: data.kategoriId || '',
+ imageId: data.imageId || '',
+ });
+
+ if (data.image?.link) setPreviewImage(data.image.link);
+ } catch (error) {
+ console.error(error);
+ toast.error(error instanceof Error ? error.message : 'Gagal memuat data perpustakaan');
+ }
+ };
+
+ loadData();
}, [params?.id]);
+ const handleInputChange = (field: string, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
- editState.update.form = { ...editState.update.form, ...formData }
+ // Update global state hanya pas submit
+ editState.update.form = { ...editState.update.form, ...formData };
+ // Upload file jika ada
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
- if (!uploaded?.id) return toast.error("Gagal upload gambar");
+ if (!uploaded?.id) return toast.error('Gagal upload gambar');
editState.update.form.imageId = uploaded.id;
}
await editState.update.update();
- toast.success("Data perpustakaan berhasil diperbarui!");
- router.push("/admin/pendidikan/perpustakaan-digital/data-perpustakaan");
+ toast.success('Data perpustakaan berhasil diperbarui!');
+ router.push('/admin/pendidikan/perpustakaan-digital/data-perpustakaan');
} catch (error) {
- console.error("Error updating data perpustakaan:", error);
- toast.error("Terjadi kesalahan saat memperbarui data perpustakaan");
+ console.error(error);
+ toast.error('Terjadi kesalahan saat memperbarui data perpustakaan');
}
};
@@ -86,18 +98,26 @@ function EditPerpustakaanDigital() {
- {/* Form Paper */}
-
+
{/* Dropzone Image */}
- Gambar
+
+ Gambar
+
{
- const selectedFile = files[0];
- if (selectedFile) {
- setFile(selectedFile);
- setPreviewImage(URL.createObjectURL(selectedFile));
+ const selected = files[0];
+ if (selected) {
+ setFile(selected);
+ setPreviewImage(URL.createObjectURL(selected));
}
}}
onReject={() => toast.error('File tidak valid.')}
@@ -117,8 +137,12 @@ function EditPerpustakaanDigital() {
- Seret gambar atau klik untuk memilih file
- Maksimal 5MB, format gambar wajib
+
+ Seret gambar atau klik untuk memilih file
+
+
+ Maksimal 5MB, format gambar wajib
+
@@ -141,27 +165,29 @@ function EditPerpustakaanDigital() {
label="Judul"
placeholder="Masukkan judul"
value={formData.judul}
- onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
+ onChange={(e) => handleInputChange('judul', e.target.value)}
required
/>
{/* Deskripsi */}
- Deskripsi
- setFormData({ ...formData, deskripsi: val })} />
+
+ Deskripsi
+
+ handleInputChange('deskripsi', val)} />
{/* Kategori */}
setFormData({ ...formData, kategoriId: val || "" })}
+ onChange={(val) => handleInputChange('kategoriId', val || '')}
label="Kategori"
- placeholder='Pilih kategori'
- data={perpustakaanDigitalState.kategoriBuku.findMany.data?.map(v => ({ value: v.id, label: v.name })) || []}
+ placeholder="Pilih kategori"
+ data={perpustakaanDigitalState.kategoriBuku.findMany.data?.map((v) => ({ value: v.id, label: v.name })) || []}
clearable
searchable
required
- error={!formData.kategoriId ? "Pilih kategori" : undefined}
+ error={!formData.kategoriId ? 'Pilih kategori' : undefined}
/>
{/* Submit Button */}
diff --git a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/data-perpustakaan/[id]/page.tsx b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/data-perpustakaan/[id]/page.tsx
index c1039731..437c2da4 100644
--- a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/data-perpustakaan/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/data-perpustakaan/[id]/page.tsx
@@ -72,7 +72,7 @@ function DetailDataPerpustakaan() {
Deskripsi
-
+
diff --git a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/data-perpustakaan/create/page.tsx b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/data-perpustakaan/create/page.tsx
index edcce751..00cfed3d 100644
--- a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/data-perpustakaan/create/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/data-perpustakaan/create/page.tsx
@@ -80,7 +80,7 @@ function CreateDataPerpustakaan() {
Judul}
placeholder='Masukkan judul'
- value={createState.create.form.judul}
+ defaultValue={createState.create.form.judul}
onChange={(val) => {
createState.create.form.judul = val.target.value;
}}
diff --git a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/kategori-buku/[id]/page.tsx b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/kategori-buku/[id]/page.tsx
index d7f49ded..627e023d 100644
--- a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/kategori-buku/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/kategori-buku/[id]/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
+
import perpustakaanDigitalState from '@/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, TextInput, Title, Tooltip } from '@mantine/core';
@@ -14,9 +15,8 @@ function EditKategoriBuku() {
const router = useRouter();
const params = useParams();
- const [formData, setFormData] = useState({
- name: editState.update.form.name || '',
- });
+ const [formData, setFormData] = useState({ name: '' });
+ const [loading, setLoading] = useState(true);
useEffect(() => {
const loadKategori = async () => {
@@ -26,21 +26,26 @@ function EditKategoriBuku() {
try {
const data = await editState.update.load(id);
if (data) {
- setFormData({
- name: data.name || '',
- });
+ setFormData({ name: data.name || '' });
}
} catch (error) {
console.error('Error loading kategori buku:', error);
toast.error('Gagal memuat data kategori buku');
+ } finally {
+ setLoading(false);
}
};
loadKategori();
}, [params?.id]);
+ const handleChange = (e: React.ChangeEvent) => {
+ setFormData((prev) => ({ ...prev, name: e.target.value }));
+ };
+
const handleSubmit = async () => {
try {
+ // Update global state hanya saat submit
editState.update.form = { ...editState.update.form, name: formData.name };
await editState.update.update();
toast.success('Kategori Buku berhasil diperbarui!');
@@ -77,8 +82,9 @@ function EditKategoriBuku() {
label="Nama Kategori Buku"
placeholder="Masukkan nama kategori buku"
value={formData.name}
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+ onChange={handleChange}
required
+ disabled={loading}
/>
@@ -91,6 +97,7 @@ function EditKategoriBuku() {
color: '#fff',
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
}}
+ disabled={loading || !formData.name.trim()}
>
Simpan
diff --git a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/kategori-buku/create/page.tsx b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/kategori-buku/create/page.tsx
index 0eb655aa..73ce3608 100644
--- a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/kategori-buku/create/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/kategori-buku/create/page.tsx
@@ -49,7 +49,7 @@ function CreateKategoriBuku() {
Nama Kategori Buku}
placeholder='Masukkan nama kategori buku'
- value={createState.create.form.name}
+ defaultValue={createState.create.form.name}
onChange={(val) => {
createState.create.form.name = val.target.value;
}}
diff --git a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/[id]/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/[id]/edit/page.tsx
new file mode 100644
index 00000000..d0ce3fb8
--- /dev/null
+++ b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/[id]/edit/page.tsx
@@ -0,0 +1,254 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+'use client'
+import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
+import perpustakaanDigitalState from '@/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital';
+
+import colors from '@/con/colors';
+import {
+ Box,
+ Button,
+ Group,
+ Paper,
+ Select,
+ Stack,
+ Text,
+ TextInput,
+ Title,
+ Tooltip,
+} from '@mantine/core';
+import { DateInput } from '@mantine/dates';
+import { useShallowEffect } from '@mantine/hooks';
+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';
+
+export type Status = "Dipinjam" | "Dikembalikan" | "Terlambat" | "Dibatalkan";
+
+function EditPeminjam() {
+ const stateEdit = useProxy(perpustakaanDigitalState.peminjamanBuku);
+ const router = useRouter();
+ const params = useParams();
+
+ const [formData, setFormData] = useState<{
+ nama: string;
+ noTelp: string;
+ alamat: string;
+ bukuId: string;
+ buku?: {
+ id: string;
+ judul: string;
+ };
+ tanggalPinjam: string;
+ batasKembali: string;
+ tanggalKembali: string;
+ status: Status;
+ catatan: string;
+ }>({
+ nama: '',
+ noTelp: '',
+ alamat: '',
+ bukuId: '',
+ tanggalPinjam: '',
+ batasKembali: '',
+ tanggalKembali: '',
+ status: 'Dipinjam', // Default status
+ catatan: '',
+ });
+
+ useShallowEffect(() => {
+ perpustakaanDigitalState.dataPerpustakaan.findManyAll.load()
+ })
+
+ useEffect(() => {
+ const loadPeminjam = async () => {
+ const id = params?.id as string;
+ if (!id) return;
+
+ try {
+ const data = await stateEdit.update.load(id);
+ if (data) {
+ setFormData((prev) => ({
+ ...prev,
+ nama: data.nama ?? prev.nama,
+ noTelp: data.noTelp ?? prev.noTelp,
+ alamat: data.alamat ?? prev.alamat,
+ bukuId: data.bukuId ?? prev.bukuId,
+ buku: data.buku ? {
+ id: data.buku.id,
+ judul: data.buku.judul
+ } : undefined,
+ tanggalPinjam: data.tanggalPinjam ?? prev.tanggalPinjam,
+ batasKembali: data.batasKembali ?? prev.batasKembali,
+ tanggalKembali: data.tanggalKembali ?? prev.tanggalKembali,
+ status: (data.status as Status) ?? prev.status,
+ catatan: data.catatan ?? prev.catatan,
+ }));
+ }
+ } catch (error) {
+ console.error("Error loading peminjam:", error);
+ toast.error("Gagal mengambil data peminjam");
+ }
+ };
+
+ loadPeminjam();
+ }, [params?.id]);
+
+
+ const handleChange = (field: string, value: string | Status) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
+ const handleSubmit = async () => {
+ try {
+ stateEdit.update.form = {
+ ...stateEdit.update.form,
+ ...formData,
+ };
+
+ await stateEdit.update.update();
+ toast.success("Peminjam berhasil diperbarui!");
+ router.push("/admin/pendidikan/perpustakaan-digital/peminjam");
+ } catch (error) {
+ console.error("Error updating peminjam:", error);
+ toast.error("Terjadi kesalahan saat memperbarui peminjam");
+ }
+ };
+
+ return (
+
+ {/* Header */}
+
+
+ router.back()} p="xs" radius="md">
+
+
+
+
+ Edit Peminjam Buku
+
+
+
+ {/* Card */}
+
+
+ handleChange('nama', e.target.value)}
+ label={Nama Peminjam }
+ placeholder="Masukkan nama peminjam"
+ required
+ />
+
+ handleChange('noTelp', e.target.value)}
+ label={No Telp Peminjam }
+ placeholder="Masukkan no telp peminjam"
+ required
+ />
+
+ handleChange('alamat', e.target.value)}
+ label={Alamat Peminjam }
+ placeholder="Masukkan alamat peminjam"
+ required
+ />
+
+
+ Buku
+ ({ value: p.id, label: p.judul })) || []}
+ value={formData.bukuId}
+ onChange={(value) => value && setFormData({ ...formData, bukuId: value })}
+ searchable
+ clearable
+ />
+
+
+ {
+ perpustakaanDigitalState.peminjamanBuku.update.form.tanggalPinjam =
+ date ? new Date(date).toISOString() : '';
+ }}
+ label={Tanggal Pinjam }
+ placeholder="Masukkan tanggal pinjam"
+ required
+ />
+
+ {
+ perpustakaanDigitalState.peminjamanBuku.update.form.tanggalKembali =
+ date ? new Date(date).toISOString() : '';
+ }}
+ label={Tanggal Kembali }
+ placeholder="Masukkan tanggal kembali"
+ required
+ />
+
+ {
+ perpustakaanDigitalState.peminjamanBuku.update.form.batasKembali =
+ date ? new Date(date).toISOString() : '';
+ }}
+ label={Batas Kembali }
+ placeholder="Masukkan batas kembali"
+ required
+ />
+
+ handleChange('status', val as Status)}
+ label={Status }
+ placeholder="Pilih status"
+ data={[
+ { value: "Dipinjam", label: "Dipinjam" },
+ { value: "Dikembalikan", label: "Dikembalikan" },
+ { value: "Terlambat", label: "Terlambat" },
+ { value: "Dibatalkan", label: "Dibatalkan" },
+ ]}
+ required
+ />
+
+
+ Catatan
+ handleChange('catatan', htmlContent)}
+ />
+
+
+
+
+ Simpan
+
+
+
+
+
+ );
+}
+
+export default EditPeminjam;
diff --git a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/[id]/page.tsx b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/[id]/page.tsx
new file mode 100644
index 00000000..ed599d33
--- /dev/null
+++ b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/[id]/page.tsx
@@ -0,0 +1,237 @@
+'use client';
+import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
+import perpustakaanDigitalState from '@/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital';
+import colors from '@/con/colors';
+import {
+ Badge,
+ Box,
+ Button,
+ Group,
+ Paper,
+ Skeleton,
+ Stack,
+ Text,
+ Tooltip
+} from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useState } from 'react';
+import { useProxy } from 'valtio/utils';
+
+
+function DetailDataPeminjaman() {
+ const stateDetail = useProxy(perpustakaanDigitalState.peminjamanBuku);
+ const [modalHapus, setModalHapus] = useState(false);
+ const [selectedId, setSelectedId] = useState(null);
+ const params = useParams();
+ const router = useRouter();
+
+ useShallowEffect(() => {
+ stateDetail.findUnique.load(params?.id as string);
+ }, []);
+
+ const data = stateDetail.findUnique.data;
+
+ const handleHapus = () => {
+ if (selectedId) {
+ stateDetail.delete.delete(selectedId);
+ setModalHapus(false);
+ setSelectedId(null);
+ router.push('/admin/pendidikan/perpustakaan-digital/peminjam');
+ }
+ };
+
+ const renderStatusBadge = (status: string) => {
+ const normalized = status?.toUpperCase();
+
+ switch (normalized) {
+ case 'DIPINJAM':
+ return Dipinjam ;
+ case 'DIKEMBALIKAN':
+ return Dikembalikan ;
+ case 'TERLAMBAT':
+ return Terlambat ;
+ case 'DIBATALKAN':
+ return Dibatalkan ;
+ default:
+ return Tidak diketahui ;
+ }
+ };
+
+ if (!data) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ router.back()}
+ leftSection={ }
+ mb={15}
+ >
+ Kembali
+
+
+
+
+
+ Detail Data Peminjam Buku
+
+
+
+
+
+
+ Nama
+
+
+ {data.nama || '-'}
+
+
+
+
+
+ No Telp
+
+
+ {data.noTelp || '-'}
+
+
+
+ {/* πΉ Editable Status */}
+
+
+
+
+ Status
+
+ {renderStatusBadge(data.status || '')}
+
+
+
+
+
+
+ Alamat
+
+
+ {data.alamat || '-'}
+
+
+
+
+
+ Buku
+
+
+ {data.buku?.judul || '-'}
+
+
+
+
+
+ Tanggal Pinjam
+
+
+ {data.tanggalPinjam
+ ? new Date(data.tanggalPinjam).toLocaleDateString('id-ID')
+ : '-'}
+
+
+
+
+
+ Tanggal Kembali
+
+
+ {data.tanggalKembali
+ ? new Date(data.tanggalKembali).toLocaleDateString('id-ID')
+ : '-'}
+
+
+
+
+
+ Batas Kembali
+
+
+ {data.batasKembali
+ ? new Date(data.batasKembali).toLocaleDateString('id-ID')
+ : '-'}
+
+
+
+
+
+ Catatan
+
+
+
+
+
+
+ {
+ setSelectedId(data.id);
+ setModalHapus(true);
+ }}
+ variant="light"
+ radius="md"
+ size="md"
+ disabled={stateDetail.delete.loading}
+ >
+
+
+
+
+
+
+ router.push(`/admin/pendidikan/perpustakaan-digital/peminjam/${data.id}/edit`)
+ }
+ variant="light"
+ radius="md"
+ size="md"
+ >
+
+
+
+
+
+
+
+
+
+ setModalHapus(false)}
+ onConfirm={handleHapus}
+ text="Apakah Anda yakin ingin menghapus data ini?"
+ />
+
+ );
+}
+
+export default DetailDataPeminjaman;
diff --git a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/page.tsx b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/page.tsx
new file mode 100644
index 00000000..89d8505a
--- /dev/null
+++ b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/page.tsx
@@ -0,0 +1,157 @@
+'use client';
+import colors from '@/con/colors';
+import {
+ Badge,
+ Box,
+ Button,
+ Center,
+ Pagination,
+ Paper,
+ Skeleton,
+ Stack,
+ Table,
+ TableTbody,
+ TableTd,
+ TableTh,
+ TableThead,
+ TableTr,
+ Text,
+ Title,
+} from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
+import { useRouter } from 'next/navigation';
+import { useState } from 'react';
+import { useProxy } from 'valtio/utils';
+import HeaderSearch from '../../../_com/header';
+import perpustakaanDigitalState from '../../../_state/pendidikan/perpustakaan-digital';
+
+function PeminjamBuku() {
+ const [search, setSearch] = useState('');
+ return (
+
+ }
+ value={search}
+ onChange={(e) => setSearch(e.currentTarget.value)}
+ />
+
+
+ );
+}
+
+function ListPeminjamBuku({ search }: { search: string }) {
+ const statePeminjam = useProxy(perpustakaanDigitalState.peminjamanBuku);
+ const router = useRouter();
+
+ const { data, page, totalPages, loading, load } = statePeminjam.findMany;
+
+ useShallowEffect(() => {
+ load(page, 10, search);
+ }, [page, search]);
+
+ const filteredData = data || [];
+
+ // πΉ Fungsi helper untuk badge status
+ const renderStatusBadge = (status: string) => {
+ const normalized = status?.toUpperCase();
+
+ switch (normalized) {
+ case 'DIPINJAM':
+ return Dipinjam ;
+ case 'DIKEMBALIKAN':
+ return Dikembalikan ;
+ case 'TERLAMBAT':
+ return Terlambat ;
+ case 'DIBATALKAN':
+ return Dibatalkan ;
+ default:
+ return Tidak diketahui ;
+ }
+ };
+
+ if (loading || !data) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+ Daftar Peminjam Buku
+
+
+
+
+ No
+ Nama Peminjam
+ Status
+ Detail
+
+
+
+ {filteredData.length > 0 ? (
+ filteredData.map((item, index) => (
+
+ {index + 1}
+
+ {item.nama}
+
+
+ {renderStatusBadge(item.status)}
+
+
+
+ router.push(`/admin/pendidikan/perpustakaan-digital/peminjam/${item.id}`)
+ }
+ >
+
+
+
+
+ ))
+ ) : (
+
+
+
+
+ Tidak ada data Peminjam buku yang cocok
+
+
+
+
+ )}
+
+
+
+
+
+ {totalPages > 1 && (
+
+ {
+ load(newPage, 10);
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ }}
+ total={totalPages}
+ mt="md"
+ mb="md"
+ color="blue"
+ radius="md"
+ />
+
+ )}
+
+ );
+}
+
+export default PeminjamBuku;
diff --git a/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/_lib/layoutTabs.tsx
index d60d95cd..9c76abe1 100644
--- a/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/_lib/layoutTabs.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/_lib/layoutTabs.tsx
@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
-import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
+import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
import { IconSchool, IconTarget } from '@tabler/icons-react';
@@ -11,13 +11,6 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const tabs = [
- {
- label: "Program Unggulan",
- value: "program-unggulan",
- href: "/admin/pendidikan/program-pendidikan-anak/program-unggulan",
- icon: ,
- tooltip: "Lihat dan kelola program unggulan pendidikan anak",
- },
{
label: "Tujuan Program",
value: "tujuan-program",
@@ -25,6 +18,13 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
icon: ,
tooltip: "Atur tujuan program pendidikan anak",
},
+ {
+ label: "Program Unggulan",
+ value: "program-unggulan",
+ href: "/admin/pendidikan/program-pendidikan-anak/program-unggulan",
+ icon: ,
+ tooltip: "Lihat dan kelola program unggulan pendidikan anak",
+ }
];
const currentTab = tabs.find(tab => tab.href === pathname);
@@ -59,36 +59,42 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
radius="lg"
keepMounted={false}
>
-
+
- {tabs.map((tab, i) => (
-
-
+ {tabs.map((tab, i) => (
+
- {tab.label}
-
-
- ))}
-
+
+ {tab.label}
+
+
+ ))}
+
+
{tabs.map((tab, i) => (
({
+ judul: '',
+ deskripsi: ''
+ });
const [isSubmitting, setIsSubmitting] = useState(false);
// load data once
useShallowEffect(() => {
- if (!editState.findById.data) {
- editState.findById.initialize();
- }
+ if (!editState.findById.data) editState.findById.initialize();
}, []);
+ // populate formData saat data global tersedia
useEffect(() => {
if (editState.findById.data) {
- setJudul(editState.findById.data.judul ?? '');
- setContent(editState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: editState.findById.data.judul ?? '',
+ deskripsi: editState.findById.data.deskripsi ?? ''
+ });
}
}, [editState.findById.data]);
+ const handleChange = useCallback(
+ (field: keyof FormData, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ },
+ []
+ );
+
const handleSubmit = async () => {
- if (!judul.trim()) {
+ if (!formData.judul.trim()) {
toast.error('Judul wajib diisi');
return;
}
@@ -60,9 +77,13 @@ function EditTujuanProgram() {
setIsSubmitting(true);
try {
if (editState.findById.data) {
- editState.findById.data.judul = judul;
- editState.findById.data.deskripsi = content;
- await editState.update.save(editState.findById.data);
+ // update global state only on submit
+ const updatedData = {
+ ...editState.findById.data,
+ judul: formData.judul,
+ deskripsi: formData.deskripsi
+ };
+ await editState.update.save(updatedData);
toast.success('Berhasil menyimpan perubahan');
router.push('/admin/pendidikan/program-pendidikan-anak/program-unggulan');
@@ -77,7 +98,6 @@ function EditTujuanProgram() {
const handleBack = () => router.back();
- // loading state
if (editState.findById.loading) {
return (
@@ -93,12 +113,7 @@ function EditTujuanProgram() {
-
+
@@ -122,18 +137,20 @@ function EditTujuanProgram() {
Judul}
placeholder="Masukkan judul program"
- value={judul}
- onChange={(e) => setJudul(e.currentTarget.value)}
- error={!judul && 'Judul wajib diisi'}
+ value={formData.judul}
+ onChange={(e) => handleChange('judul', e.currentTarget.value)}
+ error={!formData.judul && 'Judul wajib diisi'}
/>
{/* Deskripsi Field */}
- Deskripsi
+
+ Deskripsi
+
handleChange('deskripsi', value)}
+ initialContent={formData.deskripsi}
/>
@@ -143,7 +160,7 @@ function EditTujuanProgram() {
bg={colors['blue-button']}
onClick={handleSubmit}
loading={isSubmitting || editState.update.loading}
- disabled={!judul}
+ disabled={!formData.judul}
>
{isSubmitting ? 'Menyimpan...' : 'Simpan Perubahan'}
diff --git a/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/program-unggulan/page.tsx b/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/program-unggulan/page.tsx
index 3bb0c725..57a19af4 100644
--- a/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/program-unggulan/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/program-unggulan/page.tsx
@@ -71,6 +71,7 @@ function Page() {
fw="bold"
c={colors['blue-button']}
dangerouslySetInnerHTML={{ __html: data.judul }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -81,6 +82,7 @@ function Page() {
ta="justify"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/tujuan-program/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/tujuan-program/edit/page.tsx
index d2b0ca22..6929f9f2 100644
--- a/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/tujuan-program/edit/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/tujuan-program/edit/page.tsx
@@ -11,7 +11,7 @@ import {
Text,
TextInput,
Title,
- Tooltip
+ Tooltip,
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack } from '@tabler/icons-react';
@@ -30,8 +30,8 @@ function EditTujuanProgram() {
const router = useRouter();
const editState = useProxy(stateProgramPendidikanAnak.stateTujuanProgram);
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ // Menggunakan satu state lokal untuk form
+ const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
const [isSubmitting, setIsSubmitting] = useState(false);
// Load data pertama kali
@@ -41,16 +41,22 @@ function EditTujuanProgram() {
}
}, []);
- // Sync state dengan data hasil fetch
+ // Sync hanya sekali saat data fetch berhasil
useEffect(() => {
if (editState.findById.data) {
- setJudul(editState.findById.data.judul ?? '');
- setContent(editState.findById.data.deskripsi ?? '');
+ setFormData({
+ judul: editState.findById.data.judul ?? '',
+ deskripsi: editState.findById.data.deskripsi ?? '',
+ });
}
}, [editState.findById.data]);
+ const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
- if (!judul.trim()) {
+ if (!formData.judul.trim()) {
toast.error('Judul wajib diisi');
return;
}
@@ -58,9 +64,12 @@ function EditTujuanProgram() {
setIsSubmitting(true);
try {
if (editState.findById.data) {
- editState.findById.data.judul = judul;
- editState.findById.data.deskripsi = content;
- await editState.update.save(editState.findById.data);
+ const updatedData = {
+ ...editState.findById.data,
+ judul: formData.judul,
+ deskripsi: formData.deskripsi,
+ };
+ await editState.update.save(updatedData);
toast.success('Berhasil menyimpan perubahan');
router.push('/admin/pendidikan/program-pendidikan-anak/tujuan-program');
@@ -112,17 +121,17 @@ function EditTujuanProgram() {
Judul}
placeholder="Masukkan judul program"
- value={judul}
- onChange={(e) => setJudul(e.currentTarget.value)}
- error={!judul && 'Judul wajib diisi'}
+ value={formData.judul}
+ onChange={(e) => handleChange('judul', e.currentTarget.value)}
+ error={!formData.judul && 'Judul wajib diisi'}
/>
Deskripsi
handleChange('deskripsi', value)}
+ initialContent={formData.deskripsi}
/>
@@ -131,7 +140,7 @@ function EditTujuanProgram() {
bg={colors['blue-button']}
onClick={handleSubmit}
loading={isSubmitting || editState.update.loading}
- disabled={!judul}
+ disabled={!formData.judul}
>
{isSubmitting ? 'Menyimpan...' : 'Simpan Perubahan'}
diff --git a/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/tujuan-program/page.tsx b/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/tujuan-program/page.tsx
index 63562537..d003433d 100644
--- a/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/tujuan-program/page.tsx
+++ b/src/app/admin/(dashboard)/pendidikan/program-pendidikan-anak/tujuan-program/page.tsx
@@ -71,6 +71,7 @@ function Page() {
fw="bold"
c={colors['blue-button']}
dangerouslySetInnerHTML={{ __html: data.judul }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -81,6 +82,7 @@ function Page() {
ta="justify"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/edit/page.tsx
index c61cca2a..8a7b9a43 100644
--- a/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/edit/page.tsx
@@ -1,5 +1,6 @@
-'use client';
/* eslint-disable react-hooks/exhaustive-deps */
+'use client';
+
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import daftarInformasiPublik from '@/app/admin/(dashboard)/_state/ppid/daftar_informasi_publik/daftarInformasiPublik';
import colors from '@/con/colors';
@@ -17,15 +18,15 @@ interface FormDaftarInformasi {
}
function EditDaftarInformasiPublik() {
- const daftarInformasi = useProxy(daftarInformasiPublik)
- const router = useRouter()
- const params = useParams()
+ const daftarInformasi = useProxy(daftarInformasiPublik);
+ const router = useRouter();
+ const params = useParams();
const [formData, setFormData] = useState({
jenisInformasi: '',
deskripsi: '',
tanggal: '',
- })
+ });
const formatDateForInput = (dateString: string) => {
if (!dateString) return '';
@@ -33,6 +34,7 @@ function EditDaftarInformasiPublik() {
return date.toISOString().split('T')[0];
};
+ // Load data once on mount / when ID changes
useEffect(() => {
const loadDaftarInformasi = async () => {
const id = params?.id as string;
@@ -48,14 +50,18 @@ function EditDaftarInformasiPublik() {
});
}
} catch (error) {
- console.error("Error loading daftar informasi:", error);
- toast.error("Gagal memuat data daftar informasi");
+ console.error('Error loading daftar informasi:', error);
+ toast.error('Gagal memuat data daftar informasi');
}
- }
+ };
loadDaftarInformasi();
}, [params?.id]);
+ const handleChange = (field: keyof FormDaftarInformasi, value: string) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
+
const handleSubmit = async () => {
try {
daftarInformasi.edit.form = {
@@ -63,14 +69,14 @@ function EditDaftarInformasiPublik() {
jenisInformasi: formData.jenisInformasi.trim(),
deskripsi: formData.deskripsi.trim(),
tanggal: formData.tanggal.trim(),
- }
- await daftarInformasi.edit.update()
- router.push("/admin/ppid/daftar-informasi-publik-desa-darmasaba");
+ };
+ await daftarInformasi.edit.update();
+ router.push('/admin/ppid/daftar-informasi-publik-desa-darmasaba');
} catch (error) {
- console.error("Error updating berita:", error);
- toast.error("Terjadi kesalahan saat memperbarui berita");
+ console.error('Error updating berita:', error);
+ toast.error('Terjadi kesalahan saat memperbarui berita');
}
- }
+ };
return (
@@ -98,12 +104,7 @@ function EditDaftarInformasiPublik() {
label="Jenis Informasi"
placeholder="Masukkan jenis informasi"
value={formData.jenisInformasi}
- onChange={(val) => {
- setFormData({
- ...formData,
- jenisInformasi: val.target.value
- });
- }}
+ onChange={(e) => handleChange('jenisInformasi', e.target.value)}
required
/>
@@ -113,10 +114,7 @@ function EditDaftarInformasiPublik() {
{
- setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
- daftarInformasi.edit.form.deskripsi = htmlContent;
- }}
+ onChange={(htmlContent) => handleChange('deskripsi', htmlContent)}
/>
@@ -125,12 +123,7 @@ function EditDaftarInformasiPublik() {
label="Tanggal Publikasi"
placeholder="Pilih tanggal publikasi"
value={formatDateForInput(formData.tanggal)}
- onChange={(val) => {
- setFormData({
- ...formData,
- tanggal: val.target.value
- });
- }}
+ onChange={(e) => handleChange('tanggal', e.target.value)}
required
/>
diff --git a/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/page.tsx b/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/page.tsx
index dab10166..ac4f92d8 100644
--- a/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/page.tsx
@@ -63,7 +63,7 @@ function DetailDaftarInformasiPublik() {
Detail Informasi Publik
-
+
Jenis Informasi
@@ -88,6 +88,7 @@ function DetailDaftarInformasiPublik() {
c="dimmed"
dangerouslySetInnerHTML={{ __html: data.deskripsi || '-' }}
className="prose max-w-none"
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/create/page.tsx b/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/create/page.tsx
index 626f0020..aadfe5d0 100644
--- a/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/create/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/daftar-informasi-publik-desa-darmasaba/create/page.tsx
@@ -68,7 +68,7 @@ export default function CreateDaftarInformasi() {
{
daftarInformasi.create.form.jenisInformasi = e.target.value;
}}
@@ -96,7 +96,7 @@ export default function CreateDaftarInformasi() {
{
daftarInformasi.create.form.tanggal = e.target.value;
}}
diff --git a/src/app/admin/(dashboard)/ppid/dasar-hukum/edit/page.tsx b/src/app/admin/(dashboard)/ppid/dasar-hukum/edit/page.tsx
index 5ca62fce..28fcaef5 100644
--- a/src/app/admin/(dashboard)/ppid/dasar-hukum/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/dasar-hukum/edit/page.tsx
@@ -9,37 +9,44 @@ import stateDasarHukumPPID from '../../../_state/ppid/dasar_hukum/dasarHukum';
import { useRouter } from 'next/navigation';
import { IconArrowBack } from '@tabler/icons-react';
-const PPIDTextEditor = dynamic(() => import('../../_com/PPIDTextEditor').then(mod => mod.PPIDTextEditor), {
- ssr: false,
-});
+const PPIDTextEditor = dynamic(
+ () => import('../../_com/PPIDTextEditor').then(mod => mod.PPIDTextEditor),
+ { ssr: false }
+);
function EditDasarHukum() {
- const router = useRouter()
- const dasarHukumState = useProxy(stateDasarHukumPPID)
- const [judul, setJudul] = useState('');
- const [content, setContent] = useState('');
+ const router = useRouter();
+ const dasarHukumState = useProxy(stateDasarHukumPPID);
+ // Gabungkan judul & content jadi satu state lokal
+ const [formData, setFormData] = useState({ judul: '', content: '' });
+
+ // Load data awal sekali
useShallowEffect(() => {
if (!dasarHukumState.findById.data) {
- dasarHukumState.findById.initialize(); // biar masuk ke `findFirst` route kamu
+ dasarHukumState.findById.initialize();
}
}, []);
+ // Set state lokal saat data global sudah tersedia
useEffect(() => {
if (dasarHukumState.findById.data) {
- setJudul(dasarHukumState.findById.data.judul ?? '')
- setContent(dasarHukumState.findById.data.content ?? '')
+ setFormData({
+ judul: dasarHukumState.findById.data.judul ?? '',
+ content: dasarHukumState.findById.data.content ?? '',
+ });
}
- }, [dasarHukumState.findById.data])
+ }, [dasarHukumState.findById.data]);
- const submit = () => {
+ const handleSubmit = () => {
if (dasarHukumState.findById.data) {
- dasarHukumState.findById.data.judul = judul;
- dasarHukumState.findById.data.content = content;
- dasarHukumState.update.save(dasarHukumState.findById.data)
+ // Update global state hanya saat submit
+ const updated = { ...dasarHukumState.findById.data, ...formData };
+ dasarHukumState.update.save(updated);
}
- router.push('/admin/ppid/dasar-hukum')
- }
+ router.push('/admin/ppid/dasar-hukum');
+ };
+
return (
@@ -69,12 +76,12 @@ function EditDasarHukum() {
setFormData(prev => ({ ...prev, judul: value }))}
/>
-
+
Konten
@@ -82,15 +89,15 @@ function EditDasarHukum() {
setFormData(prev => ({ ...prev, content: value }))}
/>
@@ -78,11 +79,7 @@ function Page() {
diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/_lib/layoutTabs.tsx
index c6b40858..1cd27336 100644
--- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/_lib/layoutTabs.tsx
+++ b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/_lib/layoutTabs.tsx
@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
import colors from '@/con/colors';
-import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
+import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core';
import { IconChartBar, IconUsers } from '@tabler/icons-react';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
@@ -49,41 +49,49 @@ function LayoutTabsIKM({ children }: { children: React.ReactNode }) {
IKM Desa Darmasaba
-
- {tabs.map((e, i) => (
-
-
+
+ {tabs.map((e, i) => (
+
- {e.label}
-
-
- ))}
-
+
+ {e.label}
+
+
+ ))}
+
+
{tabs.map((e, i) => (
{/* Konten dummy, bisa diganti tergantung routing */}
diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/[id]/edit/page.tsx
index 2be25c2d..808be9ac 100644
--- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/[id]/edit/page.tsx
@@ -1,13 +1,13 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
-/* eslint-disable react-hooks/exhaustive-deps */
-import React, { useEffect, useState } from 'react';
-import { useRouter, useParams } from 'next/navigation';
-import { useProxy } from 'valtio/utils';
-import colors from '@/con/colors';
-import { Box, Button, Group, Paper, Stack, Title, TextInput, Text, Select, Tooltip } from '@mantine/core';
-import { IconArrowBack, IconDeviceFloppy } from '@tabler/icons-react';
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan';
+import colors from '@/con/colors';
+import { Box, Button, Group, Paper, Select, Stack, TextInput, Title, Tooltip } from '@mantine/core';
+import { IconArrowBack, IconDeviceFloppy } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useEffect, useState, ChangeEvent } from 'react';
import { toast } from 'react-toastify';
+import { useProxy } from 'valtio/utils';
interface FormResponden {
name: string;
@@ -18,24 +18,35 @@ interface FormResponden {
}
function EditResponden() {
- const router = useRouter()
- const params = useParams() as { id: string }
- const state = useProxy(indeksKepuasanState.responden)
- const id = params.id
+ const router = useRouter();
+ const params = useParams() as { id: string };
+ const state = useProxy(indeksKepuasanState.responden);
+ const id = params.id;
+
const [formData, setFormData] = useState({
name: '',
tanggal: '',
jenisKelaminId: '',
ratingId: '',
kelompokUmurId: '',
- })
+ });
+
+ // Helper untuk membuat data Select dari state
+ const mapSelectData = (data: any[] | null | undefined) =>
+ (data || [])
+ .filter(Boolean)
+ .map((v: any) => ({
+ value: v.id || '',
+ label: typeof v.name === 'string' ? v.name : 'Tanpa Nama',
+ }));
+
useEffect(() => {
+ // Load opsi dropdown
indeksKepuasanState.jenisKelaminResponden.findMany.load();
indeksKepuasanState.pilihanRatingResponden.findMany.load();
indeksKepuasanState.kelompokUmurResponden.findMany.load();
const loadResponden = async () => {
- const id = params?.id as string;
if (!id) return;
try {
@@ -43,39 +54,35 @@ function EditResponden() {
if (data) {
state.update.id = id;
- state.update.form = {
- name: data.name,
- tanggal: data.tanggal,
- jenisKelaminId: data.jenisKelaminId,
- ratingId: data.ratingId,
- kelompokUmurId: data.kelompokUmurId,
- };
-
+ // **formData lokal tetap controlled**, tidak overwrite tiap render
setFormData({
- name: data.name,
- tanggal: data.tanggal,
- jenisKelaminId: data.jenisKelaminId,
- ratingId: data.ratingId,
- kelompokUmurId: data.kelompokUmurId,
+ name: data.name || '',
+ tanggal: data.tanggal || '',
+ jenisKelaminId: data.jenisKelaminId || '',
+ ratingId: data.ratingId || '',
+ kelompokUmurId: data.kelompokUmurId || '',
});
-
-
}
} catch (error) {
- console.error("Error loading program penghijauan:", error);
- toast.error("Gagal memuat data program penghijauan");
+ console.error("Error loading responden:", error);
+ toast.error("Gagal memuat data responden");
}
- }
+ };
loadResponden();
- }, [params?.id]);
+ }, [id, state.update]);
+
+ const handleChange = (field: keyof FormResponden) => (e: ChangeEvent | string | null) => {
+ const value = typeof e === 'string' || e === null ? e || '' : e.currentTarget.value;
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ };
const handleSubmit = async () => {
state.update.id = id;
- state.update.form = { ...formData }; // <-- sinkronisasi manual
+ state.update.form = { ...formData }; // hanya update global state saat submit
await state.update.submit();
- router.push('/admin/ppid/ikm-desa-darmasaba/responden')
- }
+ router.push('/admin/ppid/ikm-desa-darmasaba/responden');
+ };
return (
@@ -100,60 +107,28 @@ function EditResponden() {
>
- Nama Responden
-
- }
- type='text'
+ label="Nama Responden"
placeholder="Masukkan nama responden"
value={formData.name}
- onChange={(val) => {
- setFormData({
- ...formData,
- name: val.currentTarget.value
- })
- }}
+ onChange={handleChange('name')}
radius="md"
required
/>
- Tanggal
-
- }
+ label="Tanggal"
type="date"
- placeholder='Pilih tanggal'
value={formData.tanggal ? new Date(formData.tanggal).toISOString().split('T')[0] : ''}
- onChange={(e) => {
- const selectedDate = e.currentTarget.value;
- setFormData({
- ...formData,
- tanggal: selectedDate,
- });
- }}
+ onChange={handleChange('tanggal')}
radius="md"
required
/>
+
- Jenis Kelamin
-
- }
+ label="Jenis Kelamin"
placeholder="Pilih jenis kelamin"
value={formData.jenisKelaminId}
- onChange={(val) => setFormData({ ...formData, jenisKelaminId: val || "" })}
- data={
- (indeksKepuasanState.jenisKelaminResponden.findMany.data || [])
- .filter(Boolean)
- .map((v) => ({
- value: v.id || '',
- label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
- }))
- }
+ onChange={handleChange('jenisKelaminId')}
+ data={mapSelectData(indeksKepuasanState.jenisKelaminResponden.findMany.data)}
disabled={indeksKepuasanState.jenisKelaminResponden.findMany.loading}
clearable
searchable
@@ -161,24 +136,13 @@ function EditResponden() {
radius="md"
error={!formData.jenisKelaminId ? "Pilih jenis kelamin" : undefined}
/>
+
setFormData({ ...formData, ratingId: val || "" })}
- label={
-
- Rating
-
- }
- placeholder='Pilih rating'
- data={
- (indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
- .filter(Boolean)
- .map((v) => ({
- value: v.id || '',
- label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
- }))
- }
+ onChange={handleChange('ratingId')}
+ data={mapSelectData(indeksKepuasanState.pilihanRatingResponden.findMany.data)}
disabled={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
clearable
searchable
@@ -188,37 +152,26 @@ function EditResponden() {
/>
setFormData({ ...formData, kelompokUmurId: val || "" })}
- label={Kelompok Umur }
- placeholder='Pilih kelompok umur'
- data={
- (indeksKepuasanState.kelompokUmurResponden.findMany.data || [])
- .filter(Boolean)
- .map((v) => ({
- value: v.id || '',
- label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
- }))
- }
+ onChange={handleChange('kelompokUmurId')}
+ data={mapSelectData(indeksKepuasanState.kelompokUmurResponden.findMany.data)}
disabled={indeksKepuasanState.kelompokUmurResponden.findMany.loading}
clearable
searchable
required
+ radius="md"
error={!formData.kelompokUmurId ? "Pilih kelompok umur" : undefined}
/>
-
+
- router.back()}
- >
+ router.back()}>
Batal
- }
- onClick={handleSubmit}
+ onClick={handleSubmit}
loading={state.update.loading}
color={colors['blue-button']}
>
diff --git a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/create/page.tsx b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/create/page.tsx
index 9feb87e7..7056c869 100644
--- a/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/create/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/ikm-desa-darmasaba/responden/create/page.tsx
@@ -64,7 +64,7 @@ function RespondenCreate() {
label="Nama"
type='text'
placeholder="masukkan nama"
- value={stategrafikBerdasarkanResponden.create.form.name}
+ defaultValue={stategrafikBerdasarkanResponden.create.form.name}
onChange={(val) => {
stategrafikBerdasarkanResponden.create.form.name = val.currentTarget.value;
}}
@@ -73,7 +73,7 @@ function RespondenCreate() {
label="Tanggal"
type="date"
placeholder="masukkan tanggal"
- value={stategrafikBerdasarkanResponden.create.form.tanggal}
+ defaultValue={stategrafikBerdasarkanResponden.create.form.tanggal}
onChange={(val) => {
stategrafikBerdasarkanResponden.create.form.tanggal = val.currentTarget.value;
}}
diff --git a/src/app/admin/(dashboard)/ppid/permohonan-informasi-publik/[id]/page.tsx b/src/app/admin/(dashboard)/ppid/permohonan-informasi-publik/[id]/page.tsx
new file mode 100644
index 00000000..29bf3335
--- /dev/null
+++ b/src/app/admin/(dashboard)/ppid/permohonan-informasi-publik/[id]/page.tsx
@@ -0,0 +1,95 @@
+'use client'
+import colors from '@/con/colors';
+import { Box, Button, Paper, Skeleton, Stack, Text } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconArrowBack } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useProxy } from 'valtio/utils';
+import statepermohonanInformasiPublikForm from '../../../_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik';
+
+function DetailPermohonanInformasiPublik() {
+ const state = useProxy(statepermohonanInformasiPublikForm.statepermohonanInformasiPublik)
+ const router = useRouter()
+ const params = useParams()
+
+ useShallowEffect(() => {
+ state.findUnique.load(params?.id as string)
+ }, [params?.id])
+
+ if (!state.findUnique.data) {
+ return (
+
+
+
+ )
+ }
+
+ const data = state.findUnique.data;
+
+ return (
+
+ router.back()}
+ leftSection={ }
+ mb={15}
+ >
+ Kembali
+
+
+
+
+
+ Detail Informasi Publik
+
+
+
+
+
+ Nama
+ {data.name || '-'}
+
+
+ NIK
+ {data.nik || '-'}
+
+
+ Telepon
+ {data.notelp || '-'}
+
+
+
+ Email
+ {data.email || '-'}
+
+
+
+ Jenis Informasi
+ {data.jenisInformasiDiminta?.name || '-'}
+
+
+
+ Cara Akses Informasi
+ {data.caraMemperolehInformasi?.name || '-'}
+
+
+
+ Cara Akses Salinan Informasi
+ {data.caraMemperolehSalinanInformasi?.name || '-'}
+
+
+
+
+
+
+ );
+}
+
+export default DetailPermohonanInformasiPublik;
diff --git a/src/app/admin/(dashboard)/ppid/permohonan-informasi-publik/page.tsx b/src/app/admin/(dashboard)/ppid/permohonan-informasi-publik/page.tsx
index 9dd16103..8aa329ce 100644
--- a/src/app/admin/(dashboard)/ppid/permohonan-informasi-publik/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/permohonan-informasi-publik/page.tsx
@@ -1,13 +1,15 @@
'use client'
-import { useProxy } from 'valtio/utils'
-import { useShallowEffect } from '@mantine/hooks'
-import { Box, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Title, Text, Group, Tooltip, Badge } from '@mantine/core'
-import { IconInfoCircle, IconUser, IconMail, IconPhone, IconId } from '@tabler/icons-react'
-import statepermohonanInformasiPublikForm from '../../_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik'
import colors from '@/con/colors'
+import { Box, Button, Group, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core'
+import { useShallowEffect } from '@mantine/hooks'
+import { IconDeviceImacCog, IconId, IconInfoCircle, IconPhone, IconUser } from '@tabler/icons-react'
+import { useRouter } from 'next/navigation'
+import { useProxy } from 'valtio/utils'
+import statepermohonanInformasiPublikForm from '../../_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik'
function Page() {
const permohonanInformasiPublikState = useProxy(statepermohonanInformasiPublikForm)
+ const router = useRouter()
useShallowEffect(() => {
permohonanInformasiPublikState.statepermohonanInformasiPublik.findMany.load()
@@ -56,10 +58,7 @@ function Page() {
Nama
NIK
Telepon
- Email
- Jenis Informasi
- Cara Akses Informasi
- Salinan Informasi
+ Detail
@@ -67,27 +66,31 @@ function Page() {
{index + 1}
-
+
{item.name}
-
-
- {item.nik}
- {item.notelp}
- {item.email}
-
-
- {item.jenisInformasiDiminta?.name || '-'}
-
+
-
- {item.caraMemperolehInformasi?.name || '-'}
-
+
+ {item.nik}
+
-
- {item.caraMemperolehSalinanInformasi?.name || '-'}
-
+
+ {item.notelp}
+
+
+
+ }
+ onClick={() => router.push(`/admin/ppid/permohonan-informasi-publik/${item.id}`)}
+ >
+ Detail
+
))}
diff --git a/src/app/admin/(dashboard)/ppid/permohonan-keberatan-informasi-publik/[id]/page.tsx b/src/app/admin/(dashboard)/ppid/permohonan-keberatan-informasi-publik/[id]/page.tsx
new file mode 100644
index 00000000..0e4e95e8
--- /dev/null
+++ b/src/app/admin/(dashboard)/ppid/permohonan-keberatan-informasi-publik/[id]/page.tsx
@@ -0,0 +1,82 @@
+'use client'
+import colors from '@/con/colors';
+import { Box, Button, Paper, Skeleton, Stack, Text } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconArrowBack } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useProxy } from 'valtio/utils';
+import statePermohonanKeberatan from '../../../_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi'
+
+
+function DetailPermohonanKeberatanInformasiPublik() {
+ const state = useProxy(statePermohonanKeberatan)
+ const router = useRouter()
+ const params = useParams()
+
+ useShallowEffect(() => {
+ state.findUnique.load(params?.id as string)
+ }, [params?.id])
+
+ if (!state.findUnique.data) {
+ return (
+
+
+
+ )
+ }
+
+ const data = state.findUnique.data;
+
+ return (
+
+ router.back()}
+ leftSection={ }
+ mb={15}
+ >
+ Kembali
+
+
+
+
+
+ Detail Informasi Publik
+
+
+
+
+
+ Nama
+ {data.name || '-'}
+
+
+ Telepon
+ {data.notelp || '-'}
+
+
+
+ Email
+ {data.email || '-'}
+
+
+
+ Alasan
+
+
+
+
+
+
+
+ );
+}
+
+export default DetailPermohonanKeberatanInformasiPublik;
diff --git a/src/app/admin/(dashboard)/ppid/permohonan-keberatan-informasi-publik/page.tsx b/src/app/admin/(dashboard)/ppid/permohonan-keberatan-informasi-publik/page.tsx
index 660cfb48..2d651a3e 100644
--- a/src/app/admin/(dashboard)/ppid/permohonan-keberatan-informasi-publik/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/permohonan-keberatan-informasi-publik/page.tsx
@@ -1,13 +1,15 @@
'use client'
import colors from '@/con/colors'
-import { Box, Group, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core'
+import { Box, Button, Group, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core'
import { useShallowEffect } from '@mantine/hooks'
-import { IconInfoCircle, IconMail, IconMessage, IconPhone, IconUser } from '@tabler/icons-react'
+import { IconDeviceImacCog, IconInfoCircle, IconMail, IconPhone, IconUser } from '@tabler/icons-react'
import { useProxy } from 'valtio/utils'
import statePermohonanKeberatan from '../../_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi'
+import { useRouter } from 'next/navigation'
function Page() {
const listState = useProxy(statePermohonanKeberatan)
+ const router = useRouter()
useShallowEffect(() => {
listState.findMany.load()
}, [])
@@ -55,7 +57,7 @@ function Page() {
Nama
Email
Telepon
- Alasan Keberatan
+ Detail
@@ -74,21 +76,16 @@ function Page() {
{item.notelp || '-'}
- ]*>?/gm, '')}>
- ]*>?/gm, '').substring(0, 50) + '...' :
- '-'
- }}
- />
-
+
}
+ onClick={() => router.push(`/admin/ppid/permohonan-keberatan-informasi-publik/${item.id}`)}
+ >
+ Detail
+
))}
diff --git a/src/app/admin/(dashboard)/ppid/profile-ppid/page.tsx b/src/app/admin/(dashboard)/ppid/profile-ppid/page.tsx
index 79dab2db..a7bc90e9 100644
--- a/src/app/admin/(dashboard)/ppid/profile-ppid/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/profile-ppid/page.tsx
@@ -97,25 +97,25 @@ function Page() {
Biodata
-
+
Riwayat Karir
-
+
Pengalaman Organisasi
-
+
Program Kerja Unggulan
-
+
diff --git a/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/[id]/edit/page.tsx
index b7dd46aa..c09fd141 100644
--- a/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/[id]/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/[id]/edit/page.tsx
@@ -23,27 +23,26 @@ import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
-
export default function EditPegawaiPPID() {
const router = useRouter();
const { id } = useParams<{ id: string }>();
+ const stateOrganisasi = useProxy(stateStrukturPPID.pegawai);
+
+ const [formData, setFormData] = useState({
+ namaLengkap: '',
+ gelarAkademik: '',
+ imageId: '',
+ tanggalMasuk: '',
+ email: '',
+ telepon: '',
+ alamat: '',
+ posisiId: '',
+ isActive: true,
+ });
const [previewImage, setPreviewImage] = useState
(null);
const [file, setFile] = useState(null);
- const stateOrganisasi = useProxy(stateStrukturPPID.pegawai);
- const [formData, setFormData] = useState({
- namaLengkap: stateOrganisasi.edit.form.namaLengkap,
- gelarAkademik: stateOrganisasi.edit.form.gelarAkademik,
- imageId: stateOrganisasi.edit.form.imageId,
- tanggalMasuk: stateOrganisasi.edit.form.tanggalMasuk,
- email: stateOrganisasi.edit.form.email,
- telepon: stateOrganisasi.edit.form.telepon,
- alamat: stateOrganisasi.edit.form.alamat,
- posisiId: stateOrganisasi.edit.form.posisiId,
- isActive: stateOrganisasi.edit.form.isActive,
- });
-
- // Format date to YYYY-MM-DD for date input
+ // Format date for
const formatDateForInput = (dateString: string) => {
if (!dateString) return '';
const date = new Date(dateString);
@@ -53,36 +52,29 @@ export default function EditPegawaiPPID() {
useEffect(() => {
const loadPegawai = async () => {
try {
- await stateStrukturPPID.posisiOrganisasi.findMany.load(1, 0); // ambil semua data
+ await stateStrukturPPID.posisiOrganisasi.findManyAll.load();
+
const data = await stateOrganisasi.edit.load(id);
if (data) {
setFormData({
- namaLengkap: data.namaLengkap || "",
- gelarAkademik: data.gelarAkademik || "",
- imageId: data.imageId || "",
- tanggalMasuk: data.tanggalMasuk || "",
- email: data.email || "",
- telepon: data.telepon || "",
- alamat: data.alamat || "",
- posisiId: data.posisiId || "",
- isActive: data.isActive ?? true, // pakai nullish coalescing
+ namaLengkap: data.namaLengkap || '',
+ gelarAkademik: data.gelarAkademik || '',
+ imageId: data.imageId || '',
+ tanggalMasuk: data.tanggalMasuk || '',
+ email: data.email || '',
+ telepon: data.telepon || '',
+ alamat: data.alamat || '',
+ posisiId: data.posisiId || '',
+ isActive: data.isActive ?? true,
});
- if (data.image?.link) {
- setPreviewImage(data.image.link);
- } else {
- setPreviewImage(null);
- }
+ setPreviewImage(data.image?.link || null);
}
} catch (error) {
- console.error("Error loading pegawai:", error);
- toast.error(
- error instanceof Error ? error.message : "Gagal mengambil data pegawai"
- );
+ console.error('Error loading pegawai:', error);
+ toast.error(error instanceof Error ? error.message : 'Gagal mengambil data pegawai');
}
};
- console.log('Current posisiId:', formData.posisiId);
- console.log('Available positions:', stateStrukturPPID.posisiOrganisasi.findMany.data?.map(p => p.id));
loadPegawai();
}, [id]);
@@ -90,50 +82,28 @@ export default function EditPegawaiPPID() {
const handleSubmit = async () => {
try {
if (!formData.namaLengkap.trim()) {
- toast.error('Nama lengkap tidak boleh kosong');
- return;
+ return toast.error('Nama lengkap tidak boleh kosong');
}
- stateOrganisasi.edit.form = {
- namaLengkap: formData.namaLengkap.trim(),
- gelarAkademik: formData.gelarAkademik.trim(),
- imageId: formData.imageId || "",
- tanggalMasuk: formData.tanggalMasuk.trim(),
- email: formData.email.trim(),
- telepon: formData.telepon.trim(),
- alamat: formData.alamat.trim(),
- posisiId: formData.posisiId.trim(),
- isActive: formData.isActive,
- };
+ // Update global state only on submit
+ const updatedForm = { ...formData };
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
-
- if (!uploaded?.id) {
- return toast.error("Gagal upload gambar");
- }
-
- // Update imageId in global state
- stateOrganisasi.edit.form.imageId = uploaded.id;
+ if (!uploaded?.id) return toast.error('Gagal upload gambar');
+ updatedForm.imageId = uploaded.id;
}
- if (id && !stateOrganisasi.edit.id) {
- stateOrganisasi.edit.id = id;
- }
+ stateOrganisasi.edit.form = updatedForm;
+ if (id && !stateOrganisasi.edit.id) stateOrganisasi.edit.id = id;
const success = await stateOrganisasi.edit.submit();
-
- if (success) {
- router.push("/admin/ppid/struktur-ppid/pegawai");
- }
+ if (success) router.push('/admin/ppid/struktur-ppid/pegawai');
} catch (error) {
- console.error("Error updating pegawai:", error);
- toast.error(error instanceof Error ? error.message : "Gagal memperbarui data pegawai");
+ console.error('Error updating pegawai:', error);
+ toast.error(error instanceof Error ? error.message : 'Gagal memperbarui data pegawai');
}
-
-
-
};
return (
@@ -144,9 +114,7 @@ export default function EditPegawaiPPID() {
-
- Edit Data Pegawai PPID
-
+ Edit Data Pegawai PPID
+ {/* Nama Lengkap */}
+ setFormData({ ...formData, namaLengkap: e.target.value })}
+ required
+ />
+
+ {/* Gelar Akademik */}
+ setFormData({ ...formData, gelarAkademik: e.target.value })}
+ />
+
+ {/* Foto Profil */}
-
- Nama Lengkap
-
- setFormData({ ...formData, namaLengkap: e.target.value })}
- required
- />
-
-
-
- Gelar Akademik
-
- setFormData({ ...formData, gelarAkademik: e.target.value })}
- />
-
-
-
- Foto Profil
-
+ Foto Profil
{
const selectedFile = files[0];
@@ -198,22 +161,12 @@ export default function EditPegawaiPPID() {
p="xl"
>
-
-
-
-
-
-
-
-
-
+
+
+
-
- Seret gambar atau klik untuk memilih file
-
-
- Maksimal 5MB, format gambar wajib
-
+ Seret gambar atau klik untuk memilih file
+ Maksimal 5MB, format gambar wajib
@@ -225,86 +178,74 @@ export default function EditPegawaiPPID() {
alt="Preview Gambar"
radius="md"
style={{ maxHeight: 220, objectFit: 'contain', border: `1px solid ${colors['blue-button']}` }}
- loading='lazy'
+ loading="lazy"
/>
)}
+
+ {/* Tanggal Masuk */}
+ setFormData({ ...formData, tanggalMasuk: e.target.value })}
+ />
+
+ {/* Email */}
+ setFormData({ ...formData, email: e.target.value })}
+ />
+
+ {/* Telepon */}
+ setFormData({ ...formData, telepon: e.target.value })}
+ />
+
+ {/* Alamat */}
+ setFormData({ ...formData, alamat: e.target.value })}
+ />
+
+ {/* Posisi */}
-
- Tanggal Masuk
-
- setFormData({ ...formData, tanggalMasuk: e.target.value })}
- />
-
-
-
- Email
-
- setFormData({ ...formData, email: e.target.value })}
- />
-
-
-
- Telepon
-
- setFormData({ ...formData, telepon: e.target.value })}
- />
-
-
-
- Alamat
-
- setFormData({ ...formData, alamat: e.target.value })}
- />
-
-
-
- Posisi
-
+ Posisi
({
- value: p.id,
- label: p.nama
- })) || []}
+ data={stateStrukturPPID.posisiOrganisasi.findManyAll.data?.map(p => ({ value: p.id, label: p.nama })) || []}
value={formData.posisiId}
onChange={(value) => value && setFormData({ ...formData, posisiId: value })}
searchable
clearable
/>
+
+ {/* Status Pegawai */}
-
- Status Pegawai
-
+ Status Pegawai
{
- setFormData({ ...formData, isActive: val === 'true' });
- }}
+ onChange={(val) => setFormData({ ...formData, isActive: val === 'true' })}
clearable
/>
+ {/* Submit Button */}
(null);
const params = useParams();
const router = useRouter();
@@ -30,6 +31,15 @@ function DetailPegawai() {
}
};
+ const handleNonActive = () => {
+ if (selectedId) {
+ statePegawai.nonActive.byId(selectedId);
+ setModalNonActive(false);
+ setSelectedId(null);
+ router.push("/admin/ppid/struktur-ppid/pegawai");
+ }
+ };
+
if (!statePegawai.findUnique.data) {
return (
@@ -64,7 +74,7 @@ function DetailPegawai() {
Nama Lengkap
-
+
{data.namaLengkap || '-'} {data.gelarAkademik || ''}
@@ -86,7 +96,7 @@ function DetailPegawai() {
Alamat
- {data.alamat || '-'}
+ {data.alamat || '-'}
@@ -125,6 +135,19 @@ function DetailPegawai() {
)}
+ {
+ setSelectedId(data.id || null);
+ setModalNonActive(true);
+ }}
+ variant="light"
+ radius="md"
+ size="md"
+ >
+ {data.isActive ? "Aktif" : "Nonaktif"}
+
+
+
+ {/* Modal NonActive */}
+ setModalNonActive(false)}
+ onConfirm={handleNonActive}
+ text="Apakah Anda yakin ingin menonaktifkan pegawai ini?"
+ />
);
}
diff --git a/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/create/page.tsx b/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/create/page.tsx
index 75f09c72..38c8e0db 100644
--- a/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/create/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/create/page.tsx
@@ -16,7 +16,7 @@ function CreatePegawaiPPID() {
const [previewImage, setPreviewImage] = useState<{ preview: string; file: File } | null>(null);
const stateOrganisasi = useProxy(stateStrukturPPID.pegawai)
useEffect(() => {
- stateStrukturPPID.posisiOrganisasi.findMany.load(1, 0);
+ stateStrukturPPID.posisiOrganisasi.findManyAll.load();
resetForm();
}, []);
@@ -94,23 +94,19 @@ function CreatePegawaiPPID() {
>
-
- Nama Lengkap
-
(stateOrganisasi.create.form.namaLengkap = e.currentTarget.value)}
required
/>
-
- Gelar Akademik
-
(stateOrganisasi.create.form.gelarAkademik = e.currentTarget.value)}
/>
@@ -189,47 +185,39 @@ function CreatePegawaiPPID() {
)}
-
- Tanggal Masuk
-
(stateOrganisasi.create.form.tanggalMasuk = e.currentTarget.value)}
/>
-
- Email
-
(stateOrganisasi.create.form.email = e.currentTarget.value)}
/>
-
- Nomor Telepon
-
(stateOrganisasi.create.form.telepon = e.currentTarget.value)}
/>
-
- Alamat
-
(stateOrganisasi.create.form.alamat = e.currentTarget.value)}
/>
@@ -240,7 +228,7 @@ function CreatePegawaiPPID() {
({
+ data={stateStrukturPPID.posisiOrganisasi.findManyAll.data?.map(p => ({
value: p.id,
label: p.nama
})) || []}
diff --git a/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/page.tsx b/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/page.tsx
index 247bca71..994b8628 100644
--- a/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/struktur-ppid/pegawai/page.tsx
@@ -103,18 +103,7 @@ function ListPegawaiPPID({ search }: { search: string }) {
- {(() => {
- console.log('Rendering table with items:', stateOrganisasi.findMany.data);
- return null;
- })()}
- {([...filteredData]
- .sort((a, b) => {
- if (a.isActive === b.isActive) {
- return a.namaLengkap.localeCompare(b.namaLengkap); // kalau status sama, urut nama
- }
- return Number(b.isActive) - Number(a.isActive); // aktif duluan
- }) // Aktif di atas
- ).map((item) => (
+ {filteredData.map((item) => (
diff --git a/src/app/admin/(dashboard)/ppid/struktur-ppid/posisi-organisasi/[id]/page.tsx b/src/app/admin/(dashboard)/ppid/struktur-ppid/posisi-organisasi/[id]/page.tsx
index fa100eb4..c07319ca 100644
--- a/src/app/admin/(dashboard)/ppid/struktur-ppid/posisi-organisasi/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/struktur-ppid/posisi-organisasi/[id]/page.tsx
@@ -1,5 +1,7 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
+
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID';
import colors from '@/con/colors';
@@ -17,20 +19,23 @@ function EditPosisiOrganisasiPPID() {
const stateOrganisasi = useProxy(stateStrukturPPID.posisiOrganisasi);
const [formData, setFormData] = useState({
- nama: "",
- deskripsi: "",
+ nama: '',
+ deskripsi: '',
hierarki: 0,
});
- useEffect(() => {
- const loadPosisiOrganisasi = async () => {
- if (!id) return;
+ // Fungsi generik untuk update formData
+ const handleChange = (field: keyof typeof formData, value: any) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+ useEffect(() => {
+ if (!id) return;
+
+ const loadPosisiOrganisasi = async () => {
try {
const data = await stateOrganisasi.edit.load(id);
-
if (data) {
- // pastikan id-nya masuk ke state edit
stateOrganisasi.edit.id = id;
setFormData({
nama: data.nama || '',
@@ -38,9 +43,9 @@ function EditPosisiOrganisasiPPID() {
hierarki: data.hierarki || 0,
});
}
- } catch (error) {
- console.error("Error loading posisi organisasi:", error);
- toast.error("Gagal memuat data posisi organisasi");
+ } catch (err) {
+ console.error('Error loading posisi organisasi:', err);
+ toast.error('Gagal memuat data posisi organisasi');
}
};
@@ -48,31 +53,31 @@ function EditPosisiOrganisasiPPID() {
}, [id]);
const handleSubmit = async () => {
- try {
- if (!formData.nama.trim()) {
- toast.error('Nama posisi organisasi tidak boleh kosong');
- return;
- }
+ if (!formData.nama.trim()) {
+ toast.error('Nama posisi organisasi tidak boleh kosong');
+ return;
+ }
+ try {
+ // Update global state hanya saat submit
stateOrganisasi.edit.form = {
nama: formData.nama.trim(),
deskripsi: formData.deskripsi.trim(),
hierarki: formData.hierarki,
};
- // Safety check tambahan: pastikan ID tidak kosong
if (!stateOrganisasi.edit.id) {
- stateOrganisasi.edit.id = id; // fallback
+ stateOrganisasi.edit.id = id;
}
const success = await stateOrganisasi.edit.update();
if (success) {
- router.push("/admin/ppid/struktur-ppid/posisi-organisasi");
+ router.push('/admin/ppid/struktur-ppid/posisi-organisasi');
}
- } catch (error) {
- console.error("Error updating posisi organisasi:", error);
- // toast akan ditampilkan dari fungsi update
+ } catch (err) {
+ console.error('Error updating posisi organisasi:', err);
+ // toast error biasanya sudah ada di update
}
};
@@ -102,22 +107,20 @@ function EditPosisiOrganisasiPPID() {
label="Nama Posisi Organisasi"
placeholder="Masukkan nama posisi organisasi"
value={formData.nama}
- onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
+ onChange={(e) => handleChange('nama', e.target.value)}
required
/>
-
+
Deskripsi
{
- setFormData({ ...formData, deskripsi: htmlContent });
- }}
+ onChange={(html) => handleChange('deskripsi', html)}
/>
-
+
{
const value = parseInt(e.target.value, 10);
- setFormData({ ...formData, hierarki: isNaN(value) ? 0 : value });
+ handleChange('hierarki', isNaN(value) ? 0 : value);
}}
required
/>
diff --git a/src/app/admin/(dashboard)/ppid/struktur-ppid/posisi-organisasi/create/page.tsx b/src/app/admin/(dashboard)/ppid/struktur-ppid/posisi-organisasi/create/page.tsx
index 495ea1bc..942e723e 100644
--- a/src/app/admin/(dashboard)/ppid/struktur-ppid/posisi-organisasi/create/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/struktur-ppid/posisi-organisasi/create/page.tsx
@@ -73,7 +73,7 @@ function CreatePosisiOrganisasiPPID() {
(stateOrganisasi.create.form.nama = e.target.value)}
required
/>
@@ -95,7 +95,7 @@ function CreatePosisiOrganisasiPPID() {
type="number"
min={0}
placeholder="Contoh: 1 (Angka semakin kecil, posisi semakin tinggi)"
- value={stateOrganisasi.create.form.hierarki || ''}
+ defaultValue={stateOrganisasi.create.form.hierarki || ''}
onChange={(e) => {
const value = parseInt(e.target.value, 10);
stateOrganisasi.create.form.hierarki = isNaN(value) ? 0 : value;
diff --git a/src/app/admin/(dashboard)/ppid/struktur-ppid/struktur-organisasi/page.tsx b/src/app/admin/(dashboard)/ppid/struktur-ppid/struktur-organisasi/page.tsx
index fa46cef3..b331cfe4 100644
--- a/src/app/admin/(dashboard)/ppid/struktur-ppid/struktur-organisasi/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/struktur-ppid/struktur-organisasi/page.tsx
@@ -21,10 +21,10 @@ function ListStrukturOrganisasiPPID() {
const stateOrganisasi = useProxy(stateStrukturPPID.pegawai);
useEffect(() => {
- stateOrganisasi.findMany.load();
+ stateOrganisasi.findManyAll.load();
}, []);
- if (stateOrganisasi.findMany.loading) {
+ if (stateOrganisasi.findManyAll.loading) {
return (
@@ -32,7 +32,7 @@ function ListStrukturOrganisasiPPID() {
);
}
- if (!stateOrganisasi.findMany.data || stateOrganisasi.findMany.data.length === 0) {
+ if (!stateOrganisasi.findManyAll.data || stateOrganisasi.findManyAll.data.length === 0) {
return (
@@ -42,7 +42,10 @@ function ListStrukturOrganisasiPPID() {
}
const posisiMap = new Map();
- for (const pegawai of stateOrganisasi.findMany.data) {
+
+ const aktifPegawai = stateOrganisasi.findManyAll.data?.filter(p => p.isActive);
+
+ for (const pegawai of aktifPegawai) {
const posisiId = pegawai.posisi.id;
if (!posisiMap.has(posisiId)) {
posisiMap.set(posisiId, {
diff --git a/src/app/admin/(dashboard)/ppid/visi-misi-ppid/edit/page.tsx b/src/app/admin/(dashboard)/ppid/visi-misi-ppid/edit/page.tsx
index a3495b9c..0bf1df96 100644
--- a/src/app/admin/(dashboard)/ppid/visi-misi-ppid/edit/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/visi-misi-ppid/edit/page.tsx
@@ -10,13 +10,14 @@ import stateVisiMisiPPID from '../../../_state/ppid/visi_misi_ppid/visimisiPPID'
import MisiPPID from '../misiPPID/misi-PPID';
import VisiPPID from '../visiPPID/visi-PPID';
-
function VisiMisiPPIDEdit() {
- const router = useRouter()
+ const router = useRouter();
const visiMisi = useProxy(stateVisiMisiPPID);
- const [draftVisi, setDraftVisi] = useState('');
- const [draftMisi, setDraftMisi] = useState('');
+ // Gabung state lokal menjadi satu object
+ const [formData, setFormData] = useState({ visi: '', misi: '' });
+
+ // Initialize global data ke state lokal saat pertama load
useShallowEffect(() => {
if (!visiMisi.findById.data) {
visiMisi.findById.initialize();
@@ -25,19 +26,25 @@ function VisiMisiPPIDEdit() {
useEffect(() => {
if (visiMisi.findById.data) {
- setDraftVisi(visiMisi.findById.data.visi ?? '');
- setDraftMisi(visiMisi.findById.data.misi ?? '');
+ setFormData({
+ visi: visiMisi.findById.data.visi ?? '',
+ misi: visiMisi.findById.data.misi ?? '',
+ });
}
}, [visiMisi.findById.data]);
+ const handleChange = (key: 'visi' | 'misi', value: string) => {
+ setFormData(prev => ({ ...prev, [key]: value }));
+ };
+
const submit = () => {
if (visiMisi.findById.data) {
- // update nilai di state global hanya saat submit
- visiMisi.findById.data.visi = draftVisi;
- visiMisi.findById.data.misi = draftMisi;
+ // update nilai global hanya saat submit
+ visiMisi.findById.data.visi = formData.visi;
+ visiMisi.findById.data.misi = formData.misi;
visiMisi.update.save(visiMisi.findById.data);
}
- router.push('/admin/ppid/visi-misi-ppid')
+ router.push('/admin/ppid/visi-misi-ppid');
};
return (
@@ -63,11 +70,11 @@ function VisiMisiPPIDEdit() {
>
-
+ handleChange('visi', value)} />
-
+
-
+ handleChange('misi', value)} />
diff --git a/src/app/admin/(dashboard)/ppid/visi-misi-ppid/page.tsx b/src/app/admin/(dashboard)/ppid/visi-misi-ppid/page.tsx
index ea659743..3ae5c332 100644
--- a/src/app/admin/(dashboard)/ppid/visi-misi-ppid/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/visi-misi-ppid/page.tsx
@@ -82,11 +82,7 @@ function VisiMisiPPIDList() {
@@ -99,11 +95,7 @@ function VisiMisiPPIDList() {
diff --git a/src/app/admin/(dashboard)/user&role/role/[id]/page.tsx b/src/app/admin/(dashboard)/user&role/role/[id]/page.tsx
index fbe12b85..35bfc56c 100644
--- a/src/app/admin/(dashboard)/user&role/role/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/user&role/role/[id]/page.tsx
@@ -1,62 +1,71 @@
-/* eslint-disable react-hooks/exhaustive-deps */
'use client'
+
import colors from '@/con/colors';
import { Box, Button, Group, MultiSelect, Paper, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
-import { useEffect, useState } from 'react';
+import { useEffect, useState, useCallback } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
import user from '../../../_state/user/user-state';
function EditRole() {
- const stateRole = useProxy(user.roleState)
+ const stateRole = useProxy(user.roleState);
const router = useRouter();
const params = useParams();
+ // Controlled local state
const [formData, setFormData] = useState({
- name: stateRole.update.form.name || "",
- permissions: stateRole.update.form.permissions || [],
- })
+ name: '',
+ permissions: [] as string[],
+ });
+
+ // Load role data
+ const loadRole = useCallback(async (id: string) => {
+ try {
+ const data = await stateRole.update.load(id);
+ if (data) {
+ setFormData({
+ name: data.name || '',
+ permissions: data.permissions || [],
+ });
+ }
+ } catch (error) {
+ console.error('Error loading role:', error);
+ toast.error(error instanceof Error ? error.message : 'Gagal mengambil data role');
+ }
+ }, [stateRole.update]);
useEffect(() => {
- stateRole.findMany.load();
- const loadRole = async () => {
- const id = params?.id as string;
- if (!id) return;
+ stateRole.findMany.load(); // Load permissions/options
+ const id = params?.id as string;
+ if (id) loadRole(id);
+ }, [params?.id, loadRole, stateRole.findMany]);
- try {
- const data = await stateRole.update.load(id);
- if (data) {
- setFormData({
- name: data.name || "",
- permissions: data.permissions || [],
- });
- }
- } catch (error) {
- console.error("Error loading role:", error);
- toast.error(
- error instanceof Error ? error.message : "Gagal mengambil data role"
- );
- }
+ // Submit handler
+ const handleSubmit = async () => {
+ if (!formData.name.trim()) {
+ toast.error('Nama role tidak boleh kosong');
+ return;
+ }
+ if (!formData.permissions.length) {
+ toast.error('Pilih minimal satu permission');
+ return;
}
- loadRole();
- }, [params?.id]);
-
- const handleSubmit = async () => {
try {
+ // Update global state only on submit
stateRole.update.form = {
...stateRole.update.form,
name: formData.name,
permissions: formData.permissions,
- }
+ };
await stateRole.update.update();
- toast.success("Role berhasil diperbarui!");
- router.push("/admin/user&role/role");
+ toast.success('Role berhasil diperbarui!');
+ router.push('/admin/user&role/role');
} catch (error) {
- console.error("Error updating role:", error);
- toast.error("Terjadi kesalahan saat memperbarui role");
+ console.error('Error updating role:', error);
+ toast.error('Terjadi kesalahan saat memperbarui role');
}
};
@@ -76,47 +85,48 @@ function EditRole() {
-
- Edit Role
+
setFormData({ ...formData, name: e.target.value })}
- label={Nama Role }
- placeholder='Masukkan nama role'
+ label={Nama Role }
+ placeholder="Masukkan nama role"
/>
setFormData({ ...formData, permissions: val })}
- label={Permission }
- placeholder='Pilih permission'
+ label={Permission }
+ placeholder="Pilih permission"
data={
stateRole.findMany.data?.map((v) => ({
- value: v.id, // Make sure this is using the ID
- label: v.name
+ value: v.id,
+ label: v.name,
})) || []
}
clearable
searchable
required
- error={!formData.permissions.length ? "Pilih minimal satu permission" : undefined}
+ error={!formData.permissions.length ? 'Pilih minimal satu permission' : undefined}
/>
- Submit
+
+ Submit
+
diff --git a/src/app/admin/(dashboard)/user&role/role/create/page.tsx b/src/app/admin/(dashboard)/user&role/role/create/page.tsx
index eb28e6bb..7b342f2c 100644
--- a/src/app/admin/(dashboard)/user&role/role/create/page.tsx
+++ b/src/app/admin/(dashboard)/user&role/role/create/page.tsx
@@ -66,7 +66,7 @@ export default function CreateRole() {
(stateRole.create.form.name = e.target.value)}
required
/>
diff --git a/src/app/admin/_com/list_PageAdmin.tsx b/src/app/admin/_com/list_PageAdmin.tsx
index 64a808f0..4ddba6d9 100644
--- a/src/app/admin/_com/list_PageAdmin.tsx
+++ b/src/app/admin/_com/list_PageAdmin.tsx
@@ -225,7 +225,7 @@ export const navBar = [
{
id: "Ekonomi_3",
name: "Struktur Organisasi Dan Sk Pengurus Bumdesa",
- path: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi"
+ path: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai"
},
{
id: "Ekonomi_4",
@@ -377,22 +377,5 @@ export const navBar = [
path: "/admin/pendidikan/data-pendidikan"
}
]
- },
- {
- id: "User & Role",
- name: "User & Role",
- path: "",
- children: [
- {
- id: "User",
- name: "User",
- path: "/admin/user&role/user"
- },
- {
- id: "Role",
- name: "Role",
- path: "/admin/user&role/role"
- },
- ]
- },
+ }
]
diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx
index bcc49834..e2396825 100644
--- a/src/app/admin/layout.tsx
+++ b/src/app/admin/layout.tsx
@@ -132,7 +132,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
{
- router.push("/login");
+ router.push("/darmasaba");
}}
color={colors["blue-button"]}
radius="xl"
diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx
index 41d2707d..203da169 100644
--- a/src/app/admin/page.tsx
+++ b/src/app/admin/page.tsx
@@ -1,7 +1,36 @@
-import ProgramInovasi from "./(dashboard)/landing-page/profile/program-inovasi/page";
+'use client';
+
+import dynamic from 'next/dynamic';
+import { useEffect, useState } from 'react';
+
+// Dynamically import the components with SSR disabled to prevent hydration issues
+const LayoutTabs = dynamic(
+ () => import('./(dashboard)/landing-page/profile/_lib/layoutTabs'),
+ { ssr: false }
+);
+
+const ProgramInovasi = dynamic(
+ () => import('./(dashboard)/landing-page/profile/program-inovasi/page'),
+ { ssr: false }
+);
export default function Page() {
- return(
-
- )
+ const [mounted, setMounted] = useState(false);
+
+ // This ensures the component is only rendered on the client
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ if (!mounted) {
+ return null; // or return a loading state
+ }
+
+ return (
+
+ )
}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/desa/berita/kategori-berita/findMany.ts b/src/app/api/[[...slugs]]/_lib/desa/berita/kategori-berita/findMany.ts
index 3c8d9c09..bffbbffc 100644
--- a/src/app/api/[[...slugs]]/_lib/desa/berita/kategori-berita/findMany.ts
+++ b/src/app/api/[[...slugs]]/_lib/desa/berita/kategori-berita/findMany.ts
@@ -27,7 +27,7 @@ async function kategoriBeritaFindMany(context: Context) {
where,
skip,
take: limit,
- orderBy: { createdAt: 'desc' },
+ orderBy: { createdAt: 'asc' },
}),
prisma.kategoriBerita.count({ where }),
]);
diff --git a/src/app/api/[[...slugs]]/_lib/desa/layanan/ajukan_permohonan/findUnique.ts b/src/app/api/[[...slugs]]/_lib/desa/layanan/ajukan_permohonan/findUnique.ts
index 1eb4db95..99e6f442 100644
--- a/src/app/api/[[...slugs]]/_lib/desa/layanan/ajukan_permohonan/findUnique.ts
+++ b/src/app/api/[[...slugs]]/_lib/desa/layanan/ajukan_permohonan/findUnique.ts
@@ -1,9 +1,7 @@
import prisma from "@/lib/prisma";
-export default async function findUniqueAjukanPermohonan(request: Request) {
- const url = new URL(request.url);
- const pathSegments = url.pathname.split("/");
- const id = pathSegments[pathSegments.length - 1];
+export default async function findUniqueAjukanPermohonan({ params }: { params: { id: string } }) {
+ const { id } = params;
if (!id) {
return Response.json(
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/index.ts
index 2fb84a21..05e511c6 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/index.ts
@@ -3,7 +3,6 @@ import PasarDesa from "./pasar-desa";
import LowonganKerja from "./lowongan-kerja";
import ProgramKemiskinan from "./program-kemiskinan";
import KategoriProduk from "./pasar-desa/kategori-produk";
-import StrukturOrganisasi from "./struktur-organisasi";
import GrafikUsiaKerjaYangMenganggur from "./usia-kerja-yang-menganggur";
import GrafikMenganggurBerdasarkanPendidikan from "./usia-kerja-yang-menganggur/pengangguran-berdasrkan-pendidikan";
import JumlahPendudukMiskin from "./jumlah-penduduk-miskin";
@@ -11,6 +10,7 @@ import SektorUnggulanDesa from "./sektor-unggulan-desa";
import DemografiPekerjaan from "./demografi-pekerjaan";
import JumlahPengangguran from "./jumlah-pengangguran";
import PendapatanAsliDesa from "./pendapatan-asli-desa";
+import StrukturOrganisasi from "./struktur-bumdes";
const Ekonomi = new Elysia({
prefix: "/api/ekonomi",
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/jumlah-pengangguran/detail-data-pengangguran/findByMonthYear.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/jumlah-pengangguran/detail-data-pengangguran/findByMonthYear.ts
index af787cd4..c733ae0b 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/jumlah-pengangguran/detail-data-pengangguran/findByMonthYear.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/jumlah-pengangguran/detail-data-pengangguran/findByMonthYear.ts
@@ -1,8 +1,8 @@
import prisma from "@/lib/prisma";
-import { Context } from "elysia";
-export default async function findByMonthYear(context: Context) {
- const { month, year } = context.params as { month: string; year: string };
+
+export default async function findByMonthYear({ params }: { params: { month: string; year: number } }) {
+ const { month, year } = params;
if (!month || !year) {
return {
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/jumlah-pengangguran/detail-data-pengangguran/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/jumlah-pengangguran/detail-data-pengangguran/index.ts
index 0f982095..60603b04 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/jumlah-pengangguran/detail-data-pengangguran/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/jumlah-pengangguran/detail-data-pengangguran/index.ts
@@ -40,7 +40,7 @@ const DetailDataPengangguran = new Elysia({
percentageChange: t.Optional(t.Number()),
}),
})
- .delete("/:id", detailDataPengangguranDelete, {
+ .delete("/del/:id", detailDataPengangguranDelete, {
params: t.Object({
id: t.String(),
}),
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/create.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/create.ts
index 43680db9..d94ba148 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/create.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/create.ts
@@ -9,6 +9,7 @@ type FormCreate = {
gaji: string;
deskripsi: string;
kualifikasi: string;
+ notelp: string;
}
export default async function lowonganKerjaCreate(context: Context) {
@@ -23,6 +24,7 @@ export default async function lowonganKerjaCreate(context: Context) {
gaji: body.gaji,
deskripsi: body.deskripsi,
kualifikasi: body.kualifikasi,
+ notelp: body.notelp,
},
});
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/index.ts
index ecd6e88c..ed69d8d2 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/index.ts
@@ -15,6 +15,7 @@ const LowonganKerja = new Elysia({prefix: "/lowongankerja", tags: ["Ekonomi/Lowo
gaji: t.String(),
deskripsi: t.String(),
kualifikasi: t.String(),
+ notelp: t.String(),
})
})
.get("/find-many", lowonganKerjaFindMany)
@@ -35,6 +36,7 @@ const LowonganKerja = new Elysia({prefix: "/lowongankerja", tags: ["Ekonomi/Lowo
gaji: t.String(),
deskripsi: t.String(),
kualifikasi: t.String(),
+ notelp: t.String(),
})
})
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/updt.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/updt.ts
index 40dbaf76..53cb1c78 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/updt.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/lowongan-kerja/updt.ts
@@ -9,6 +9,7 @@ type FormUpdate = {
gaji: string;
deskripsi: string;
kualifikasi: string;
+ notelp: string;
}
export default async function lowonganKerjaUpdate(context: Context){
@@ -16,7 +17,7 @@ export default async function lowonganKerjaUpdate(context: Context){
const id = context.params?.id;
const body = context.body as FormUpdate;
- const { posisi, namaPerusahaan, lokasi, tipePekerjaan, gaji, deskripsi, kualifikasi } = body;
+ const { posisi, namaPerusahaan, lokasi, tipePekerjaan, gaji, deskripsi, kualifikasi, notelp } = body;
if (!id) {
return Response.json({
@@ -46,6 +47,7 @@ export default async function lowonganKerjaUpdate(context: Context){
gaji,
deskripsi,
kualifikasi,
+ notelp,
},
});
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/create.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/create.ts
index 4751abcc..7ddd3be7 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/create.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/create.ts
@@ -7,7 +7,9 @@ type FormCreate = {
alamatUsaha: string;
imageId: string;
rating: number;
- kategoriId: string[]; // Array of KategoriProduk IDs
+ kategoriId: string[];
+ kontak: string;
+ // Array of KategoriProduk IDs
};
export default async function pasarDesaCreate(context: Context) {
@@ -28,7 +30,9 @@ export default async function pasarDesaCreate(context: Context) {
alamatUsaha: body.alamatUsaha,
imageId: body.imageId,
rating: Number(body.rating),
- kategoriProdukId: body.kategoriId[0], // Use the first category as the main one
+ kategoriProdukId: body.kategoriId[0],
+ kontak: body.kontak
+ // Use the first category as the main one
},
});
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/index.ts
index dc8e413e..6731abb2 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/index.ts
@@ -37,6 +37,7 @@ const PasarDesa = new Elysia({
imageId: t.String(),
rating: t.Number(),
kategoriId: t.Array(t.String()),
+ kontak: t.String(),
}),
}
)
@@ -79,6 +80,7 @@ const PasarDesa = new Elysia({
imageId: t.String(),
rating: t.Number(),
kategoriId: t.Array(t.String()),
+ kontak: t.String(),
}),
}
);
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/findManyAll.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/findManyAll.ts
new file mode 100644
index 00000000..3373f148
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/findManyAll.ts
@@ -0,0 +1,49 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+// /api/berita/findManyAll.ts
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+async function kategoriProdukFindManyAll(context: Context) {
+ // Ambil query search (opsional)
+ const search2 = (context.query.search as string) || "";
+
+ // Buat where clause
+ const where: any = { isActive: true };
+
+ if (search2) {
+ where.OR = [
+ { nama: { contains: search2, mode: "insensitive" } },
+ {
+ KategoriToPasar: {
+ some: {
+ kategori: {
+ nama: { contains: search2, mode: "insensitive" },
+ },
+ },
+ },
+ },
+ ];
+ }
+
+ try {
+ const data = await prisma.kategoriProduk.findMany({
+ where,
+ orderBy: { createdAt: "desc" },
+ });
+
+ return {
+ success: true,
+ message: "Berhasil ambil semua kategori produk",
+ data,
+ total: data.length,
+ };
+ } catch (e) {
+ console.error("Error di findManyAll:", e);
+ return {
+ success: false,
+ message: "Gagal mengambil data kategori produk",
+ };
+ }
+}
+
+export default kategoriProdukFindManyAll;
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/index.ts
index c8729849..5777098b 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/index.ts
@@ -5,12 +5,14 @@ import kategoriProdukDelete from "./del";
import kategoriProdukCreate from "./create";
import kategoriProdukUpdate from "./updt";
import { t } from "elysia";
+import kategoriProdukFindManyAll from "./findManyAll";
const KategoriProduk = new Elysia({
prefix: "/kategoriproduk",
tags: ["Ekonomi/Kategori Produk"],
})
.get("/find-many", kategoriProdukFindMany)
+ .get("/find-many-all", kategoriProdukFindManyAll)
.get("/:id", async (context) => {
const response = await kategoriProdukFindUnique(context);
return response;
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/updt.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/updt.ts
index 71e2417d..7acac5cd 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/updt.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/updt.ts
@@ -9,6 +9,7 @@ type FormUpdate = {
imageId: string;
rating: number;
kategoriId: string[]; // Array of KategoriProduk IDs
+ kontak: string;
};
export default async function pasarDesaUpdate(context: Context) {
@@ -31,6 +32,7 @@ export default async function pasarDesaUpdate(context: Context) {
alamatUsaha: body.alamatUsaha,
imageId: body.imageId,
rating: Number(body.rating),
+ kontak: body.kontak
},
});
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/find-by-id.ts
new file mode 100644
index 00000000..b88c1c62
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/find-by-id.ts
@@ -0,0 +1,51 @@
+import prisma from "@/lib/prisma";
+
+export default async function strukturBumDesFindById(request: Request) {
+ const url = new URL(request.url);
+ const pathSegments = url.pathname.split('/');
+ const id = pathSegments[pathSegments.length - 1];
+
+ if (!id) {
+ return Response.json({
+ success: false,
+ message: "ID tidak boleh kosong",
+ }, { status: 400 });
+ }
+
+ try {
+ if (typeof id !== 'string') {
+ return Response.json({
+ success: false,
+ message: "ID tidak valid",
+ }, { status: 400 });
+ }
+
+ const data = await prisma.strukturBumDes.findUnique({
+ where: { id },
+ include: {
+ image: true,
+ }
+ });
+
+ if (!data) {
+ return Response.json({
+ success: false,
+ message: "Data tidak ditemukan",
+ }, { status: 404 });
+ }
+
+ return Response.json({
+ success: true,
+ message: "Berhasil mengambil data berdasarkan ID",
+ data,
+ }, { status: 200 });
+ } catch (e) {
+ console.error("Find by ID error:", e);
+ return Response.json({
+ success: false,
+ message: "Gagal mengambil data: " + (e instanceof Error ? e.message : 'Unknown error'),
+ }, {
+ status: 500,
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/index.ts
new file mode 100644
index 00000000..4317d3bd
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/index.ts
@@ -0,0 +1,29 @@
+import Elysia, { t } from "elysia";
+import PosisiOrganisasi from "./posisi-organisasi";
+import Pegawai from "./pegawai";
+import strukturBumDesFindById from "./find-by-id";
+import strukturBumDesUpdate from "./update";
+
+
+const StrukturOrganisasi = new Elysia({
+ prefix: "/struktur-organisasi",
+ tags: ["Ekonomi/Struktur Organisasi"],
+})
+.get("/:id", async (context) => {
+ const response = await strukturBumDesFindById(new Request(context.request))
+ return response
+})
+.use(PosisiOrganisasi)
+.use(Pegawai)
+.put("/:id", async (context) => {
+ const response = await strukturBumDesUpdate(context)
+ return response
+}, {
+ body: t.Object({
+ name: t.String(),
+ imageId: t.String(),
+ })
+})
+
+
+export default StrukturOrganisasi;
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/create.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/create.ts
similarity index 97%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/create.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/create.ts
index 824771db..8619492c 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/create.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/create.ts
@@ -32,7 +32,7 @@ export default async function pegawaiCreate(context: Context) {
}
try {
- const pegawai = await prisma.pegawai.create({
+ const pegawai = await prisma.pegawaiBumDes.create({
data: {
namaLengkap: body.namaLengkap,
gelarAkademik: body.gelarAkademik,
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/del.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/del.ts
similarity index 74%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/del.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/del.ts
index a2b3b7de..52a204fd 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/del.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/del.ts
@@ -13,17 +13,13 @@ export default async function pegawaiDelete(context: Context) {
}
try {
- const deleted = await prisma.pegawai.update({
- where: { id },
- data: {
- isActive: false, // soft delete
- updatedAt: new Date(),
- },
+ const deleted = await prisma.pegawaiBumDes.delete({
+ where: { id },
});
return {
success: true,
- message: "Pegawai berhasil di-nonaktifkan",
+ message: "Pegawai berhasil dihapus",
data: deleted,
};
} catch (error: any) {
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findMany.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findMany.ts
similarity index 93%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findMany.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findMany.ts
index b5bf9570..aa7a6e53 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findMany.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findMany.ts
@@ -9,7 +9,7 @@ export default async function pegawaiFindMany(context: Context) {
try {
const [data, total] = await Promise.all([
- prisma.pegawai.findMany({
+ prisma.pegawaiBumDes.findMany({
where: { isActive: true },
include: {
posisi: true,
@@ -19,7 +19,7 @@ export default async function pegawaiFindMany(context: Context) {
take: limit,
orderBy: { createdAt: 'desc' },
}),
- prisma.pegawai.count({
+ prisma.pegawaiBumDes.count({
where: { isActive: true }
})
]);
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findUnique.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findUnique.ts
similarity index 93%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findUnique.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findUnique.ts
index 4c2c4884..8b00abd6 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findUnique.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findUnique.ts
@@ -13,7 +13,7 @@ export default async function pegawaiFindUnique(context: Context) {
}
try {
- const pegawai = await prisma.pegawai.findUnique({
+ const pegawai = await prisma.pegawaiBumDes.findUnique({
where: { id },
include: {
posisi: true,
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/index.ts
similarity index 96%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/index.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/index.ts
index cb969630..cecf97cc 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/index.ts
@@ -7,7 +7,7 @@ import pegawaiUpdate from "./updt";
const Pegawai = new Elysia({
prefix: "/pegawai",
- tags: ["Ekonomi/Struktur Organisasi/Pegawai"],
+ tags: ["Ekonomi/Struktur BUMDes/Pegawai"],
})
// β
Find all
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/updt.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/updt.ts
similarity index 96%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/updt.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/updt.ts
index 4f9b54fa..b7f8929f 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/updt.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/updt.ts
@@ -28,7 +28,7 @@ export default async function pegawaiUpdate(context: Context) {
body.isActive = String(body.isActive).toLowerCase() === 'true';
try {
- const updated = await prisma.pegawai.update({
+ const updated = await prisma.pegawaiBumDes.update({
where: { id: body.id },
data: {
namaLengkap: body.namaLengkap,
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/create.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/create.ts
similarity index 92%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/create.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/create.ts
index 10b3a994..69aa2578 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/create.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/create.ts
@@ -18,7 +18,7 @@ export default async function posisiOrganisasiCreate(context: Context) {
}
try {
- const posisiOrganisasi = await prisma.posisiOrganisasi.create({
+ const posisiOrganisasi = await prisma.posisiOrganisasiBumDes.create({
data: {
nama: body.nama,
deskripsi: body.deskripsi,
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/del.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/del.ts
similarity index 70%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/del.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/del.ts
index 0560b115..07373122 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/del.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/del.ts
@@ -16,7 +16,7 @@ export default async function posisiOrganisasiDelete(context: Context) {
try {
// Check if the position exists first
- const existing = await prisma.posisiOrganisasi.findUnique({
+ const existing = await prisma.posisiOrganisasiBumDes.findUnique({
where: { id },
});
@@ -31,7 +31,7 @@ export default async function posisiOrganisasiDelete(context: Context) {
}
// Check if there are any pegawai associated with this position
- const pegawaiCount = await prisma.pegawai.count({
+ const pegawaiCount = await prisma.pegawaiBumDes.count({
where: { posisiId: id },
});
@@ -45,28 +45,8 @@ export default async function posisiOrganisasiDelete(context: Context) {
);
}
- // Check if this position is used in any hubungan organisasi
- const hubunganCount = await prisma.hubunganOrganisasi.count({
- where: {
- OR: [
- { atasanId: id },
- { bawahanId: id },
- ],
- },
- });
-
- if (hubunganCount > 0) {
- return new Response(
- JSON.stringify({
- success: false,
- message: "Tidak dapat menghapus posisi yang masih terdaftar dalam struktur organisasi",
- }),
- { status: 400, headers: { "Content-Type": "application/json" } }
- );
- }
-
// If all checks pass, delete the position
- const deleted = await prisma.posisiOrganisasi.delete({
+ const deleted = await prisma.posisiOrganisasiBumDes.delete({
where: { id },
});
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findMany.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findMany.ts
similarity index 93%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findMany.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findMany.ts
index ea81b455..bef2f638 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findMany.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findMany.ts
@@ -23,13 +23,13 @@ export default async function posisiOrganisasiFindMany(context: Context) {
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
- prisma.posisiOrganisasi.findMany({
+ prisma.posisiOrganisasiBumDes.findMany({
where,
skip,
take: limit,
orderBy: { createdAt: "asc" },
}),
- prisma.posisiOrganisasi.count({ where }),
+ prisma.posisiOrganisasiBumDes.count({ where }),
]);
return {
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findManyAll.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findManyAll.ts
new file mode 100644
index 00000000..a41d5c73
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findManyAll.ts
@@ -0,0 +1,45 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+// /api/posisi-organisasi/findManyAll.ts
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+async function posisiOrganisasiFindManyAll(context: Context) {
+ const search = (context.query.search as string) || "";
+
+ // filter default
+ const where: any = { isActive: true };
+
+ if (search) {
+ where.OR = [
+ { nama: { contains: search, mode: "insensitive" } },
+ { deskripsi: { contains: search, mode: "insensitive" } },
+ ];
+ }
+
+ try {
+ const data = await prisma.posisiOrganisasiPPID.findMany({
+ where,
+ orderBy: { hierarki: "asc" },
+ });
+
+ return {
+ success: true,
+ message: "Berhasil mengambil semua data posisi organisasi",
+ data: data.map((item: any) => ({
+ id: item.id,
+ nama: item.nama,
+ deskripsi: item.deskripsi,
+ hierarki: item.hierarki,
+ })),
+ total: data.length,
+ };
+ } catch (e) {
+ console.error("Find many all error:", e);
+ return {
+ success: false,
+ message: "Gagal mengambil data posisi organisasi",
+ };
+ }
+}
+
+export default posisiOrganisasiFindManyAll;
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findUnique.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findUnique.ts
similarity index 94%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findUnique.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findUnique.ts
index 13092469..eefd821d 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findUnique.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findUnique.ts
@@ -21,7 +21,7 @@ export default async function posisiOrganisasiFindUnique(context: Context) {
}
}
- const data = await prisma.posisiOrganisasi.findUnique({
+ const data = await prisma.posisiOrganisasiBumDes.findUnique({
where: { id },
});
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/index.ts
similarity index 90%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/index.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/index.ts
index 4338204d..eba36b3e 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/index.ts
@@ -4,6 +4,7 @@ import posisiOrganisasiFindUnique from "./findUnique";
import posisiOrganisasiCreate from "./create";
import posisiOrganisasiUpdate from "./updt";
import posisiOrganisasiDelete from "./del";
+import posisiOrganisasiFindManyAll from "./findManyAll";
const PosisiOrganisasi = new Elysia({
prefix: "/posisi-organisasi",
@@ -11,6 +12,7 @@ const PosisiOrganisasi = new Elysia({
})
.get("/find-many", posisiOrganisasiFindMany)
+.get("/find-many-all", posisiOrganisasiFindManyAll)
.get("/:id", async (context) => {
const response = await posisiOrganisasiFindUnique(context);
return response;
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/updt.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/updt.ts
similarity index 88%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/updt.ts
rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/updt.ts
index e3639460..c5768081 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/updt.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/updt.ts
@@ -20,7 +20,7 @@ export default async function posisiOrganisasiUpdate(context: Context) {
}
try {
- await prisma.posisiOrganisasi.update({
+ await prisma.posisiOrganisasiBumDes.update({
where: { id },
data: {
nama: body.nama,
@@ -29,7 +29,7 @@ export default async function posisiOrganisasiUpdate(context: Context) {
},
});
- const updated = await prisma.posisiOrganisasi.findUnique({
+ const updated = await prisma.posisiOrganisasiBumDes.findUnique({
where: { id },
});
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/update.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/update.ts
new file mode 100644
index 00000000..e8991b81
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/update.ts
@@ -0,0 +1,116 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+import path from "path";
+import fs from "fs/promises";
+import { Prisma } from "@prisma/client";
+
+type FormUpdate = Prisma.StrukturBumDesGetPayload<{
+ select: {
+ id: true;
+ name: true;
+ imageId: true;
+ };
+}>;
+
+export default async function strukturBumDesUpdate(context: Context) {
+try {
+ const id = context.params?.id as string;
+ const body = (await context.body) as Omit;
+
+ const { name, imageId } = 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.strukturBumDes.findUnique({
+ where: {
+ id
+ },
+ include: {
+ image: true,
+ }
+ })
+
+ if (!existing) {
+ return new Response(
+ JSON.stringify({
+ success: false,
+ message: "Data tidak ditemukan",
+ }),
+ {
+ status: 404,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ }
+ )
+ }
+
+ if (existing.imageId !== imageId) {
+ const oldImage = existing.image;
+ if (oldImage) {
+ try {
+ const filePath = path.join(oldImage.path, oldImage.name);
+ await fs.unlink(filePath);
+ await prisma.fileStorage.delete({
+ where: { id: oldImage.id },
+ })
+ } catch (error) {
+ console.error("Gagal hapus gambar lama:", error);
+ }
+ }
+ }
+
+ const updated = await prisma.strukturBumDes.update({
+ where: {
+ id
+ },
+ data: {
+ name,
+ imageId,
+ }
+ })
+
+ return new Response(
+ JSON.stringify({
+ success: true,
+ message: "Struktur BumDes Berhasil Dibuat",
+ data: updated,
+ }),
+ {
+ status: 200,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ }
+ )
+
+} catch (error) {
+ console.error("Error updating struktur BumDes:", error);
+ return new Response(
+ JSON.stringify({
+ success: false,
+ message: "Terjadi kesalahan saat mengupdate struktur BumDes",
+ }),
+ {
+ status: 500,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ }
+ )
+}
+}
+
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/create.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/create.ts
deleted file mode 100644
index db964952..00000000
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/create.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-import prisma from "@/lib/prisma";
-import { Context } from "elysia";
-
-type FormCreateHubunganOrganisasi = {
- atasanId: string;
- bawahanId: string;
- tipe?: string;
-};
-
-export default async function hubunganOrganisasiCreate(context: Context) {
- const body = await context.body as FormCreateHubunganOrganisasi;
-
- // Validasi minimal
- if (!body || !body.atasanId || !body.bawahanId) {
- return {
- success: false,
- message: "atasanId dan bawahanId wajib diisi",
- };
- }
-
- try {
- const data = await prisma.hubunganOrganisasi.create({
- data: {
- atasanId: body.atasanId,
- bawahanId: body.bawahanId,
- tipe: body.tipe,
- },
- });
-
- return {
- success: true,
- message: "Berhasil membuat hubungan organisasi",
- data,
- };
- } catch (error: any) {
- if (error.code === "P2002") {
- return {
- success: false,
- message: "Hubungan antara atasan dan bawahan sudah ada",
- };
- }
-
- console.error("Error create hubungan organisasi:", error);
- return {
- success: false,
- message: "Gagal membuat hubungan organisasi",
- error: error.message,
- };
- }
-}
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findMany.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findMany.ts
deleted file mode 100644
index 4de4200d..00000000
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findMany.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-import prisma from "@/lib/prisma";
-
-export default async function hubunganOrganisasiFindMany() {
- try {
- const data = await prisma.hubunganOrganisasi.findMany({
- include: {
- atasan: true,
- bawahan: true,
- },
- orderBy: {
- atasanId: "asc",
- },
- });
-
- return {
- success: true,
- data,
- };
- } catch (error: any) {
- console.error("Error findMany hubungan organisasi:", error);
- return {
- success: false,
- message: "Gagal mengambil data hubungan organisasi",
- error: error.message,
- };
- }
-}
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findUnique.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findUnique.ts
deleted file mode 100644
index d8d16cba..00000000
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findUnique.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-import prisma from "@/lib/prisma";
-import { Context } from "elysia";
-
-export default async function hubunganOrganisasiFindUnique(context: Context) {
- const { id } = context.params as { id: string };
-
- if (!id) {
- return {
- success: false,
- message: "ID hubungan organisasi wajib diisi",
- };
- }
-
- try {
- const data = await prisma.hubunganOrganisasi.findUnique({
- where: { id },
- include: {
- atasan: true,
- bawahan: true,
- },
- });
-
- if (!data) {
- return {
- success: false,
- message: "Data hubungan organisasi tidak ditemukan",
- };
- }
-
- return {
- success: true,
- data,
- };
- } catch (error: any) {
- console.error("Error findUnique hubungan organisasi:", error);
- return {
- success: false,
- message: "Gagal mengambil data",
- error: error.message,
- };
- }
-}
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/index.ts
deleted file mode 100644
index 994bdd4a..00000000
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/index.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import Elysia, { t } from "elysia";
-import hubunganOrganisasiFindMany from "./findMany";
-import hubunganOrganisasiFindUnique from "./findUnique";
-import hubunganOrganisasiCreate from "./create";
-import hubunganOrganisasiUpdate from "./updt";
-import hubunganOrganisasiDelete from "./del";
-
-
-const HubunganOrganisasi = new Elysia({
- prefix: "/hubungan-organisasi",
- tags: ["Ekonomi/Struktur Organisasi/Hubungan Organisasi"],
-})
-
- // π GET /find-many
- .get("/find-many", hubunganOrganisasiFindMany)
-
- // π GET /:id
- .get("/:id", async (context) => {
- return await hubunganOrganisasiFindUnique(context);
- })
-
- // β POST /create
- .post("/create", hubunganOrganisasiCreate, {
- body: t.Object({
- atasanId: t.String(),
- bawahanId: t.String(),
- tipe: t.Optional(t.String()),
- }),
- })
-
- // βοΈ PUT /:id
- .put( "/:id",
- async (context) => {
- const response = await hubunganOrganisasiUpdate(context);
- return response;
- }, {
- body: t.Object({
- atasanId: t.Optional(t.String()),
- bawahanId: t.Optional(t.String()),
- tipe: t.Optional(t.String()),
- }),
- })
-
- // β DELETE /del/:id
- .delete("/del/:id", hubunganOrganisasiDelete);
-
-export default HubunganOrganisasi;
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/updt.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/updt.ts
deleted file mode 100644
index 3d3491cd..00000000
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/updt.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import prisma from "@/lib/prisma";
-import { Context } from "elysia";
-
-/* eslint-disable @typescript-eslint/no-explicit-any */
-type FormUpdateHubungan = {
- atasanId?: string;
- bawahanId?: string;
- tipe?: string;
-};
-
-export default async function hubunganOrganisasiUpdate(context: Context) {
- const body = await context.body as Omit;
- const id = context.params?.id;
-
- if (!id) {
- return {
- success: false,
- message: "ID wajib ada di URL",
- };
- }
-
- try {
- const updated = await prisma.hubunganOrganisasi.update({
- where: { id },
- data: {
- atasanId: body.atasanId,
- bawahanId: body.bawahanId,
- tipe: body.tipe,
- },
- });
-
- return {
- success: true,
- message: "Hubungan organisasi berhasil diupdate",
- data: updated,
- };
- } catch (error: any) {
- if (error.code === "P2002") {
- return {
- success: false,
- message: "Relasi atasan-bawahan sudah ada",
- };
- }
-
- console.error("Error update hubungan organisasi:", error);
- return {
- success: false,
- message: "Gagal update data hubungan organisasi",
- error: error.message,
- };
- }
-}
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/index.ts
deleted file mode 100644
index b0ae7175..00000000
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/index.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import Elysia from "elysia";
-import PosisiOrganisasi from "./posisi-organisasi";
-import Pegawai from "./pegawai";
-import HubunganOrganisasi from "./hubungan-organisasi";
-
-const StrukturOrganisasi = new Elysia({
- prefix: "/struktur-organisasi",
- tags: ["Ekonomi/Struktur Organisasi"],
-})
-.use(PosisiOrganisasi)
-.use(Pegawai)
-.use(HubunganOrganisasi)
-
-export default StrukturOrganisasi;
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/img.ts b/src/app/api/[[...slugs]]/_lib/img.ts
index 4752afbf..84cab823 100644
--- a/src/app/api/[[...slugs]]/_lib/img.ts
+++ b/src/app/api/[[...slugs]]/_lib/img.ts
@@ -23,7 +23,9 @@ async function img({
// Validasi ekstensi file
if (![".jpg", ".jpeg", ".png"].includes(ext)) {
console.warn(`Ekstensi file tidak didukung: ${ext}`);
- return new Response(await fs.readFile(noImage), {
+ const buffer = await fs.readFile(noImage);
+ const uint8Array = new Uint8Array(buffer);
+ return new Response(new Blob([uint8Array], { type: 'image/jpeg' }), {
headers: { "Content-Type": "image/jpeg" },
});
}
@@ -43,7 +45,8 @@ async function img({
.resize(size || metadata.width) // Gunakan size jika diberikan, jika tidak gunakan width asli
.toBuffer();
- return new Response(resizedImageBuffer, {
+ const uint8Array = new Uint8Array(resizedImageBuffer);
+ return new Response(new Blob([uint8Array], { type: 'image/jpeg' }), {
headers: {
"Cache-Control": "public, max-age=3600, stale-while-revalidate=600",
"Content-Type": "image/jpeg",
@@ -52,7 +55,9 @@ async function img({
} catch (error) {
console.error(`Gagal memproses file: ${name}`, error);
// Jika file tidak ditemukan atau gagal diproses, kembalikan default image
- return new Response(await fs.readFile(noImage), {
+ const buffer = await fs.readFile(noImage);
+ const uint8Array = new Uint8Array(buffer);
+ return new Response(new Blob([uint8Array], { type: 'image/jpeg' }), {
headers: { "Content-Type": "image/jpeg" },
});
}
diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/findUnique.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/findUnique.ts
index bb0009a7..0e00fcb4 100644
--- a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/findUnique.ts
+++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/findUnique.ts
@@ -1,9 +1,7 @@
import prisma from "@/lib/prisma";
-export default async function pengaduanMasyarakatFindUnique(request: Request) {
- const url = new URL(request.url);
- const pathSegments = url.pathname.split("/");
- const id = pathSegments[pathSegments.length - 1];
+export default async function pengaduanMasyarakatFindUnique({ params }: { params: { id: string } }) {
+ const { id } = params;
if (!id) {
return Response.json({
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/kontak-darurat-keamanan/findMany.ts b/src/app/api/[[...slugs]]/_lib/keamanan/kontak-darurat-keamanan/findMany.ts
index 6fa611d6..4b5944c9 100644
--- a/src/app/api/[[...slugs]]/_lib/keamanan/kontak-darurat-keamanan/findMany.ts
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/kontak-darurat-keamanan/findMany.ts
@@ -23,6 +23,7 @@ export default async function kontakDaruratKeamananFindMany(context: Context) {
try {
const [data, total] = await Promise.all([
prisma.kontakDaruratKeamanan.findMany({
+ where,
include: {
kontakItems: {
include: {
diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/kontak-darurat-keamanan/kontak-item/findMany.ts b/src/app/api/[[...slugs]]/_lib/keamanan/kontak-darurat-keamanan/kontak-item/findMany.ts
index 2580ff95..add090ca 100644
--- a/src/app/api/[[...slugs]]/_lib/keamanan/kontak-darurat-keamanan/kontak-item/findMany.ts
+++ b/src/app/api/[[...slugs]]/_lib/keamanan/kontak-darurat-keamanan/kontak-item/findMany.ts
@@ -25,6 +25,7 @@ export default async function kontakItemFindMany(context: Context) {
skip,
take: limit,
orderBy: { createdAt: "desc" },
+ where,
}),
prisma.kontakItem.count({ where }),
]);
diff --git a/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/create.ts b/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/create.ts
index 8c5be87d..2e4ccdea 100644
--- a/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/create.ts
+++ b/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/create.ts
@@ -7,6 +7,7 @@ type FormCreate = Prisma.KontakDaruratGetPayload<{
name: true;
deskripsi: true;
imageId: true;
+ whatsapp: true;
};
}>;
export default async function kontakDaruratCreate(context: Context) {
@@ -17,6 +18,7 @@ export default async function kontakDaruratCreate(context: Context) {
name: body.name,
deskripsi: body.deskripsi,
imageId: body.imageId,
+ whatsapp: body.whatsapp,
}
})
return {
diff --git a/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/index.ts b/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/index.ts
index a5162b83..44c43af2 100644
--- a/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/index.ts
@@ -14,6 +14,7 @@ const KontakDarurat = new Elysia({
name: t.String(),
deskripsi: t.String(),
imageId: t.String(),
+ whatsapp: t.String(),
})
})
.get("/find-many", kontakDaruratFindMany)
@@ -33,6 +34,7 @@ const KontakDarurat = new Elysia({
name: t.String(),
deskripsi: t.String(),
imageId: t.String(),
+ whatsapp: t.String(),
})
}
)
diff --git a/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/updt.ts b/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/updt.ts
index bc9d7922..f61c6ffc 100644
--- a/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/updt.ts
+++ b/src/app/api/[[...slugs]]/_lib/kesehatan/kontak-darurat/updt.ts
@@ -10,6 +10,7 @@ type FormUpdate = Prisma.KontakDaruratGetPayload<{
name: true;
deskripsi: true;
imageId: true;
+ whatsapp: true;
}
}>
export default async function kontakDaruratUpdate(context: Context) {
@@ -21,6 +22,7 @@ export default async function kontakDaruratUpdate(context: Context) {
name,
deskripsi,
imageId,
+ whatsapp,
} = body;
if(!id) {
@@ -75,6 +77,7 @@ export default async function kontakDaruratUpdate(context: Context) {
name,
deskripsi,
imageId,
+ whatsapp,
}
})
diff --git a/src/app/api/[[...slugs]]/_lib/lingkungan/gotong-royong/findFirst.ts b/src/app/api/[[...slugs]]/_lib/lingkungan/gotong-royong/findFirst.ts
index 2d9a19f7..f7887819 100644
--- a/src/app/api/[[...slugs]]/_lib/lingkungan/gotong-royong/findFirst.ts
+++ b/src/app/api/[[...slugs]]/_lib/lingkungan/gotong-royong/findFirst.ts
@@ -9,7 +9,7 @@ export default async function KegiatanDesaFindFirst(context: Context) {
if (kategori) {
where.kategoriKegiatan = {
- name: { equals: kategori, mode: 'insensitive' }
+ nama: { equals: kategori, mode: 'insensitive' }
};
}
diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/findMany.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/findMany.ts
index d4b446b6..6eee2e52 100644
--- a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/findMany.ts
+++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/findMany.ts
@@ -66,9 +66,6 @@ async function lembagaPendidikanFindMany(context: Context) {
})
]);
- console.log('Fetched data count:', data.length);
- console.log('Total count:', total);
-
return {
success: true,
message: "Success fetch lembaga pendidikan with pagination",
diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/data-perpustakaan/findManyAll.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/data-perpustakaan/findManyAll.ts
new file mode 100644
index 00000000..c8ad693f
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/data-perpustakaan/findManyAll.ts
@@ -0,0 +1,48 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+export default async function dataPerpustakaanFindManyAll(context: Context) {
+ const search = (context.query.search as string) || "";
+ const isActiveParam = context.query.isActive;
+
+ // Buat where clause dinamis
+ const where: any = {};
+
+ if (isActiveParam !== undefined) {
+ where.isActive = isActiveParam === "true";
+ }
+
+ if (search) {
+ where.OR = [
+ { judul: { contains: search, mode: "insensitive" } },
+ { deskripsi: { contains: search, mode: "insensitive" } },
+ ];
+ }
+
+ try {
+ const data = await prisma.dataPerpustakaan.findMany({
+ where,
+ include: {
+ kategori: true,
+ image: true,
+ },
+ orderBy: { createdAt: "desc" },
+ });
+
+ return {
+ success: true,
+ message: "Success fetch all data perpustakaan (non-paginated)",
+ total: data.length,
+ data,
+ };
+ } catch (error) {
+ console.error("Find many all error:", error);
+ return {
+ success: false,
+ message: "Failed fetch all data perpustakaan",
+ total: 0,
+ data: [],
+ };
+ }
+}
diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/data-perpustakaan/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/data-perpustakaan/index.ts
index 768f4bba..4ae625a1 100644
--- a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/data-perpustakaan/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/data-perpustakaan/index.ts
@@ -4,6 +4,7 @@ import dataPerpustakaanDelete from "./del";
import dataPerpustakaanFindMany from "./findMany";
import dataPerpustakaanFindUnique from "./findUnique";
import dataPerpustakaanUpdate from "./updt";
+import dataPerpustakaanFindManyAll from "./findManyAll";
const DataPerpustakaan = new Elysia({
prefix: "/dataperpustakaan",
@@ -18,7 +19,7 @@ const DataPerpustakaan = new Elysia({
kategoriId: t.String(),
}),
})
-
+ .get("/findManyAll", dataPerpustakaanFindManyAll)
.get("/findMany", dataPerpustakaanFindMany)
.get("/:id", async (context) => {
const response = await dataPerpustakaanFindUnique(
diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/index.ts
index c2abe457..dfe063e3 100644
--- a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/index.ts
@@ -1,12 +1,14 @@
import Elysia from "elysia";
import DataPerpustakaan from "./data-perpustakaan";
import KategoriBuku from "./kategori-buku";
+import PeminjamanBuku from "./peminjaman";
const PerpustakaanDigital = new Elysia({
prefix: "/perpustakaandigital",
tags: ["Pendidikan / Perpustakaan Digital"],
})
.use(DataPerpustakaan)
- .use(KategoriBuku);
+ .use(KategoriBuku)
+ .use(PeminjamanBuku);
export default PerpustakaanDigital;
diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/create.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/create.ts
new file mode 100644
index 00000000..82dda9e2
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/create.ts
@@ -0,0 +1,40 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+type FormCreate = {
+ nama: string;
+ noTelp: string;
+ alamat: string;
+ bukuId: string;
+ tanggalPinjam: string;
+ batasKembali: string;
+ tanggalKembali?: string;
+ catatan?: string;
+}
+
+export default async function peminjamanBukuCreate(context: Context) {
+ const body = (await context.body) as FormCreate;
+
+ try {
+ const result = await prisma.peminjamanBuku.create({
+ data: {
+ nama: body.nama,
+ noTelp: body.noTelp,
+ alamat: body.alamat,
+ bukuId: body.bukuId,
+ tanggalPinjam: body.tanggalPinjam,
+ batasKembali: body.batasKembali,
+ tanggalKembali: body.tanggalKembali,
+ catatan: body.catatan,
+ },
+ });
+ return {
+ success: true,
+ message: "Berhasil membuat peminjaman buku",
+ data: result,
+ };
+ } catch (error) {
+ console.error("Error creating peminjaman buku:", error);
+ throw new Error("Gagal membuat peminjaman buku: " + (error as Error).message);
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/del.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/del.ts
new file mode 100644
index 00000000..4b38efd7
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/del.ts
@@ -0,0 +1,16 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+export default async function peminjamanBukuDelete(context: Context) {
+ const id = context.params.id as string;
+
+ await prisma.peminjamanBuku.delete({
+ where: { id },
+ });
+
+ return {
+ status: 200,
+ success: true,
+ message: "Success delete peminjaman buku",
+ };
+}
diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/findMany.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/findMany.ts
new file mode 100644
index 00000000..1de38808
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/findMany.ts
@@ -0,0 +1,71 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+async function peminjamanBukuFindMany(context: Context) {
+ // Ambil parameter dari query
+ const page = Number(context.query.page) || 1;
+ const limit = Number(context.query.limit) || 10;
+ const search = (context.query.search as string) || '';
+ const skip = (page - 1) * limit;
+
+ // Buat where clause
+ const where: any = {};
+
+ // Tambahkan pencarian (jika ada)
+ if (search) {
+ where.OR = [
+ { catatan: { contains: search, mode: 'insensitive' } },
+ { nama: { contains: search, mode: 'insensitive' } },
+ { buku: { judul: { contains: search, mode: 'insensitive' } } },
+ { noTelp: { contains: search, mode: 'insensitive' } },
+ { alamat: { contains: search, mode: 'insensitive' } },
+ ];
+ }
+
+ try {
+ // Ambil data dan total count secara paralel
+ const [data, total] = await Promise.all([
+ prisma.peminjamanBuku.findMany({
+ where,
+ include: {
+ buku: {
+ select: {
+ id: true,
+ judul: true,
+ kategoriId: true,
+ createdAt: true,
+ updatedAt: true,
+ deletedAt: true,
+ isActive: true,
+ imageId: true,
+ deskripsi: true
+ }
+ },
+ },
+ skip,
+ take: limit,
+ orderBy: { tanggalPinjam: 'desc' },
+ }),
+ prisma.peminjamanBuku.count({ where }),
+ ]);
+
+ return {
+ success: true,
+ message: "Berhasil ambil data peminjaman buku dengan pagination",
+ data,
+ page,
+ limit,
+ total,
+ totalPages: Math.ceil(total / limit),
+ };
+ } catch (e) {
+ console.error("Error di peminjamanBukuFindMany:", e);
+ return {
+ success: false,
+ message: "Gagal mengambil data peminjaman buku",
+ };
+ }
+}
+
+export default peminjamanBukuFindMany;
diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/findUnique.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/findUnique.ts
new file mode 100644
index 00000000..971ac0ff
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/findUnique.ts
@@ -0,0 +1,49 @@
+import prisma from "@/lib/prisma";
+
+export default async function peminjamanBukuFindUnique(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.peminjamanBuku.findUnique({
+ where: { id },
+ include: {
+ buku: true,
+ },
+ });
+
+ if (!data) {
+ return {
+ success: false,
+ message: "Data not found",
+ }
+ }
+
+ return {
+ success: true,
+ message: "Success get data peminjaman buku",
+ data,
+ }
+ } catch (error) {
+ console.error("Find by ID error:", error);
+ return {
+ success: false,
+ message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'),
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/index.ts
new file mode 100644
index 00000000..28b0d753
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/index.ts
@@ -0,0 +1,48 @@
+import Elysia, { t } from "elysia";
+import peminjamanBukuCreate from "./create";
+import peminjamanBukuDelete from "./del";
+import peminjamanBukuFindMany from "./findMany";
+import peminjamanBukuFindUnique from "./findUnique";
+import peminjamanBukuUpdate from "./updt";
+
+const PeminjamanBuku = new Elysia({
+ prefix: "/peminjamanbuku",
+ tags: ["Pendidikan / Perpustakaan Digital / Peminjaman Buku"],
+})
+
+ .post("/create", peminjamanBukuCreate, {
+ body: t.Object({
+ nama: t.String(),
+ noTelp: t.String(),
+ alamat: t.String(),
+ bukuId: t.String(),
+ tanggalPinjam: t.String(),
+ batasKembali: t.String(),
+ tanggalKembali: t.String(),
+ catatan: t.String()
+ }),
+ })
+
+ .get("/findMany", peminjamanBukuFindMany)
+ .get("/:id", async (context) => {
+ const response = await peminjamanBukuFindUnique(
+ new Request(context.request)
+ );
+ return response;
+ })
+ .put("/:id", peminjamanBukuUpdate, {
+ body: t.Object({
+ nama: t.String(),
+ noTelp: t.String(),
+ alamat: t.String(),
+ bukuId: t.String(),
+ tanggalPinjam: t.String(),
+ batasKembali: t.String(),
+ tanggalKembali: t.String(),
+ catatan: t.String(),
+ status: t.String()
+ }),
+ })
+ .delete("/del/:id", peminjamanBukuDelete);
+
+export default PeminjamanBuku;
diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/updt.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/updt.ts
new file mode 100644
index 00000000..0954d054
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/pendidikan/perpustakaan-digital/peminjaman/updt.ts
@@ -0,0 +1,49 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+type FormUpdate = {
+ nama: string;
+ noTelp: string;
+ alamat: string;
+ bukuId: string;
+ tanggalPinjam: string;
+ batasKembali: string;
+ tanggalKembali?: string;
+ catatan?: string;
+ status?: 'Dipinjam' | 'Dikembalikan' | 'Terlambat' | 'Dibatalkan';
+};
+
+export default async function dataPerpustakaanUpdate(context: Context) {
+ const body = (await context.body) as FormUpdate;
+ const id = context.params.id as string;
+
+ try {
+ const result = await prisma.peminjamanBuku.update({
+ where: { id },
+ data: {
+ nama: body.nama,
+ noTelp: body.noTelp,
+ alamat: body.alamat,
+ bukuId: body.bukuId,
+ tanggalPinjam: body.tanggalPinjam,
+ batasKembali: body.batasKembali,
+ tanggalKembali: body.tanggalKembali,
+ catatan: body.catatan,
+ status: body.status,
+ },
+ include: {
+ buku: true,
+ }
+ });
+ return {
+ success: true,
+ message: "Berhasil mengupdate data peminjaman buku",
+ data: result,
+ };
+ } catch (error) {
+ console.error("Error updating data peminjaman buku:", error);
+ throw new Error(
+ "Gagal mengupdate data peminjaman buku: " + (error as Error).message
+ );
+ }
+}
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/findUnique.ts b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/findUnique.ts
new file mode 100644
index 00000000..62e9db6a
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/findUnique.ts
@@ -0,0 +1,52 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+export default async function permohonanInformasiPublikFindUnique(context: Context) {
+ const url = new URL(context.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.permohonanInformasiPublik.findUnique({
+ where: { id },
+ include: {
+ jenisInformasiDiminta: true,
+ caraMemperolehInformasi: true,
+ caraMemperolehSalinanInformasi: true,
+ }
+ });
+
+ if (!data) {
+ return {
+ success: false,
+ message: "Permohonan informasi publik tidak ditemukan",
+ }
+ }
+
+ return {
+ success: true,
+ message: "Success find permohonan informasi publik",
+ data,
+ }
+ } catch (error) {
+ console.error("Find by ID error:", error);
+ return {
+ success: false,
+ message: "Gagal mengambil permohonan informasi publik: " + (error instanceof Error ? error.message : 'Unknown error'),
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/index.ts b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/index.ts
index 3a7a764b..3a198317 100644
--- a/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/index.ts
@@ -4,6 +4,7 @@ import permohonanInformasiPublikFindMany from "./find-many";
import jenisInformasiFindMany from "./jenisInformasi";
import memperolehInformasiFindMany from "./memperolehInformasi";
import salinanInformasiFindMany from "./salinanInformasi";
+import permohonanInformasiPublikFindUnique from "./findUnique";
const PermohonanInformasiPublik = new Elysia({
prefix: "/permohonaninformasipublik",
@@ -12,6 +13,10 @@ const PermohonanInformasiPublik = new Elysia({
.get("/jenisInformasi/find-many", jenisInformasiFindMany)
.get("/memperolehInformasi/find-many", memperolehInformasiFindMany)
.get("/salinanInformasi/find-many", salinanInformasiFindMany)
+ .get("/:id", async (context) => {
+ const response = await permohonanInformasiPublikFindUnique(context);
+ return response;
+ })
.get("/find-many", permohonanInformasiPublikFindMany)
.post("/create", permohonanInformasiPublikCreate, {
body: t.Object({
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/findUnique.ts b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/findUnique.ts
new file mode 100644
index 00000000..97ce38c4
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/findUnique.ts
@@ -0,0 +1,47 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+export default async function permohonanKeberatanInformasiPublikFindUnique(context: Context) {
+ const url = new URL(context.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.formulirPermohonanKeberatan.findUnique({
+ where: { id },
+ });
+
+ if (!data) {
+ return {
+ success: false,
+ message: "Permohonan keberatan informasi publik tidak ditemukan",
+ }
+ }
+
+ return {
+ success: true,
+ message: "Success find permohonan keberatan informasi publik",
+ data,
+ }
+ } catch (error) {
+ console.error("Find by ID error:", error);
+ return {
+ success: false,
+ message: "Gagal mengambil permohonan keberatan informasi publik: " + (error instanceof Error ? error.message : 'Unknown error'),
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/index.ts b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/index.ts
index 4d77d9c7..1b4e7650 100644
--- a/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/index.ts
@@ -1,12 +1,14 @@
import Elysia, { t } from "elysia";
import permohonanKeberatanInformasiPublikCreate from "./create";
import permohonanKeberatanInformasiPublikFindMany from "./find-many";
+import permohonanKeberatanInformasiPublikFindUnique from "./findUnique";
const PermohonanKeberatanInformasiPublik = new Elysia({
prefix: "/permohonankeberataninformasipublik",
tags: ["PPID/Permohonan Keberatan Informasi Publik"],
})
.get("/find-many", permohonanKeberatanInformasiPublikFindMany)
+ .get("/:id", permohonanKeberatanInformasiPublikFindUnique)
.post("/create", permohonanKeberatanInformasiPublikCreate, {
body: t.Object({
name: t.String(),
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/del.ts b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/del.ts
index 6074ad65..cec58438 100644
--- a/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/del.ts
+++ b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/del.ts
@@ -13,17 +13,13 @@ export default async function pegawaiDelete(context: Context) {
}
try {
- const deleted = await prisma.pegawaiPPID.update({
+ const deleted = await prisma.pegawaiPPID.delete({
where: { id },
- data: {
- isActive: false, // soft delete
- updatedAt: new Date(),
- },
});
return {
success: true,
- message: "Pegawai berhasil di-nonaktifkan",
+ message: "Pegawai berhasil dihapus",
data: deleted,
};
} catch (error: any) {
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/findMany.ts b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/findMany.ts
index 7c2f377c..67d677ff 100644
--- a/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/findMany.ts
+++ b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/findMany.ts
@@ -2,61 +2,67 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
-// Di findMany.ts
export default async function pegawaiFindMany(context: Context) {
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || "";
const skip = (page - 1) * limit;
-
- // Buat where clause
const isActiveParam = context.query.isActive;
+ // where clause dinamis
const where: any = {};
if (isActiveParam !== undefined) {
where.isActive = isActiveParam === "true";
}
- // Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ namaLengkap: { contains: search, mode: "insensitive" } },
{ alamat: { contains: search, mode: "insensitive" } },
+ { posisi: { nama: { contains: search, mode: "insensitive" } } },
];
}
try {
- const [data, total] = await Promise.all([
+ // Ambil semua data terlebih dahulu (tanpa pagination)
+ const [allData, total] = await Promise.all([
prisma.pegawaiPPID.findMany({
where,
include: {
posisi: true,
image: true,
},
- skip,
- take: limit,
- orderBy: { posisi: { hierarki: "asc" } },
- }),
- prisma.pegawaiPPID.count({
- where,
}),
+ prisma.pegawaiPPID.count({ where }),
]);
+ // Sort manual berdasarkan hierarki posisi
+ const sortedData = allData.sort((a, b) => {
+ // Sort berdasarkan hierarki terlebih dahulu
+ if (a.posisi.hierarki !== b.posisi.hierarki) {
+ return a.posisi.hierarki - b.posisi.hierarki;
+ }
+ // Jika hierarki sama, sort berdasarkan nama posisi
+ return a.posisi.nama.localeCompare(b.posisi.nama);
+ });
+
+ // Lakukan pagination manual setelah sorting
+ const paginatedData = sortedData.slice(skip, skip + limit);
const totalPages = Math.ceil(total / limit);
return {
success: true,
- message: "Success fetch pegawai with pagination",
- data,
+ message: "Success fetch pegawai with hierarchy order",
+ data: paginatedData,
page,
totalPages,
total,
};
- } catch (e) {
- console.error("Find many paginated error:", e);
+ } catch (error) {
+ console.error("Find many pegawai error:", error);
return {
success: false,
- message: "Failed fetch pegawai with pagination",
+ message: "Failed fetch pegawai",
data: [],
page: 1,
totalPages: 1,
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/findManyAll.ts b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/findManyAll.ts
new file mode 100644
index 00000000..0f0bdccd
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/findManyAll.ts
@@ -0,0 +1,48 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+export default async function pegawaiFindManyAll(context: Context) {
+ const search = (context.query.search as string) || "";
+ const isActiveParam = context.query.isActive;
+
+ // Buat where clause dinamis
+ const where: any = {};
+
+ if (isActiveParam !== undefined) {
+ where.isActive = isActiveParam === "true";
+ }
+
+ if (search) {
+ where.OR = [
+ { namaLengkap: { contains: search, mode: "insensitive" } },
+ { alamat: { contains: search, mode: "insensitive" } },
+ ];
+ }
+
+ try {
+ const data = await prisma.pegawaiPPID.findMany({
+ where,
+ include: {
+ posisi: true,
+ image: true,
+ },
+ orderBy: { posisi: { hierarki: "asc" } },
+ });
+
+ return {
+ success: true,
+ message: "Success fetch all pegawai (non-paginated)",
+ total: data.length,
+ data,
+ };
+ } catch (error) {
+ console.error("Find many all error:", error);
+ return {
+ success: false,
+ message: "Failed fetch all pegawai",
+ total: 0,
+ data: [],
+ };
+ }
+}
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/index.ts b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/index.ts
index 7d7ae02a..f4f9e7a5 100644
--- a/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/index.ts
@@ -2,8 +2,10 @@ import Elysia, { t } from "elysia";
import pegawaiFindMany from "./findMany";
import pegawaiFindUnique from "./findUnique";
import pegawaiCreate from "./create";
-import pegawaiDelete from "./del";
+import pegawaiNonActive from "./nonActive";
import pegawaiUpdate from "./updt";
+import pegawaiDelete from "./del";
+import pegawaiFindManyAll from "./findManyAll";
const Pegawai = new Elysia({
@@ -14,6 +16,9 @@ const Pegawai = new Elysia({
// β
Find all
.get("/find-many", pegawaiFindMany)
+ // β
Find all (non-paginated)
+ .get("/find-many-all", pegawaiFindManyAll)
+
// β
Find by ID
.get("/:id", async (context) => {
const response = await pegawaiFindUnique(context);
@@ -58,6 +63,7 @@ const Pegawai = new Elysia({
)
// β
Delete
+ .delete("/non-active/:id", pegawaiNonActive)
.delete("/del/:id", pegawaiDelete);
export default Pegawai;
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/del.ts b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/nonActive.ts
similarity index 51%
rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/del.ts
rename to src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/nonActive.ts
index c65bcf67..0398f39b 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/del.ts
+++ b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/pegawai/nonActive.ts
@@ -2,31 +2,35 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
-export default async function hubunganOrganisasiDelete(context: Context) {
+export default async function pegawaiNonActive(context: Context) {
const { id } = context.params as { id: string };
if (!id) {
return {
success: false,
- message: "ID wajib diisi",
+ message: "ID pegawai tidak ditemukan",
};
}
try {
- const deleted = await prisma.hubunganOrganisasi.delete({
+ const deleted = await prisma.pegawaiPPID.update({
where: { id },
+ data: {
+ isActive: false, // soft delete
+ updatedAt: new Date(),
+ },
});
return {
success: true,
- message: "Hubungan organisasi berhasil dihapus",
+ message: "Pegawai berhasil di-nonaktifkan",
data: deleted,
};
} catch (error: any) {
- console.error("Error delete hubungan organisasi:", error);
+ console.error("Error nonaktifkan pegawai:", error);
return {
success: false,
- message: "Gagal menghapus hubungan organisasi",
+ message: "Gagal nonaktifkan pegawai",
error: error.message,
};
}
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/posisi-organisasi/findManyAll.ts b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/posisi-organisasi/findManyAll.ts
new file mode 100644
index 00000000..a41d5c73
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/posisi-organisasi/findManyAll.ts
@@ -0,0 +1,45 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+// /api/posisi-organisasi/findManyAll.ts
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+async function posisiOrganisasiFindManyAll(context: Context) {
+ const search = (context.query.search as string) || "";
+
+ // filter default
+ const where: any = { isActive: true };
+
+ if (search) {
+ where.OR = [
+ { nama: { contains: search, mode: "insensitive" } },
+ { deskripsi: { contains: search, mode: "insensitive" } },
+ ];
+ }
+
+ try {
+ const data = await prisma.posisiOrganisasiPPID.findMany({
+ where,
+ orderBy: { hierarki: "asc" },
+ });
+
+ return {
+ success: true,
+ message: "Berhasil mengambil semua data posisi organisasi",
+ data: data.map((item: any) => ({
+ id: item.id,
+ nama: item.nama,
+ deskripsi: item.deskripsi,
+ hierarki: item.hierarki,
+ })),
+ total: data.length,
+ };
+ } catch (e) {
+ console.error("Find many all error:", e);
+ return {
+ success: false,
+ message: "Gagal mengambil data posisi organisasi",
+ };
+ }
+}
+
+export default posisiOrganisasiFindManyAll;
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/posisi-organisasi/index.ts b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/posisi-organisasi/index.ts
index 0637c5d2..84de5eaa 100644
--- a/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/posisi-organisasi/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/ppid/struktur_ppid/posisi-organisasi/index.ts
@@ -4,6 +4,7 @@ import posisiOrganisasiFindUnique from "./findUnique";
import posisiOrganisasiCreate from "./create";
import posisiOrganisasiUpdate from "./updt";
import posisiOrganisasiDelete from "./del";
+import posisiOrganisasiFindManyAll from "./findManyAll";
const PosisiOrganisasi = new Elysia({
prefix: "/posisiorganisasi",
@@ -11,6 +12,7 @@ const PosisiOrganisasi = new Elysia({
})
.get("/find-many", posisiOrganisasiFindMany)
+.get("/find-many-all", posisiOrganisasiFindManyAll)
.get("/:id", async (context) => {
const response = await posisiOrganisasiFindUnique(context);
return response;
diff --git a/src/app/api/[[...slugs]]/_lib/search/findMany.ts b/src/app/api/[[...slugs]]/_lib/search/findMany.ts
new file mode 100644
index 00000000..2a782fd6
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/search/findMany.ts
@@ -0,0 +1,1970 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { Context } from "elysia";
+import prisma from "@/lib/prisma";
+
+export default async function searchFindMany(context: Context) {
+ const { query, page = 1, limit = 10, type } = context.query as any;
+
+ // Convert to numbers
+ const pageNum = parseInt(String(page), 10) || 1;
+ const limitNum = parseInt(String(limit), 10) || 10;
+ const skip = (pageNum - 1) * limitNum;
+
+ if (!query || query.trim() === "") {
+ return { data: [], nextPage: null };
+ }
+
+ // π kalau type dikirim β cari spesifik modul
+ //========================================= MENU LANDING PAGE ========================================= //
+ //========================================= PROFILE ========================================= //
+ if (type === "pejabatdesa") {
+ const data = await prisma.pejabatDesa.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return {
+ data: data.map((b) => ({ type: "pejabatdesa", ...b })),
+ nextPage: data.length < limitNum ? null : pageNum + 1,
+ };
+ }
+
+ if (type === "programinovasi") {
+ const data = await prisma.programInovasi.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "mediasosial") {
+ const data = await prisma.mediaSosial.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ //========================================= DESA ANTI KORUPSI ========================================= //
+
+ if (type === "desaantikorupsi") {
+ const data = await prisma.desaAntiKorupsi.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ //========================================= SDGS Desa ========================================= //
+ if (type === "sdgsdesa") {
+ const data = await prisma.sdgsDesa.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ //========================================= APBDes ========================================= //
+ if (type === "apbdes") {
+ const data = await prisma.aPBDes.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ //========================================= PRESTASI DESA ========================================= //
+ if (type === "prestasidesa") {
+ const data = await prisma.prestasiDesa.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ //========================================= INDEKS KEPUASAAN MASYARAKAT ========================================= //
+ if (type === "responden") {
+ const data = await prisma.responden.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ //========================================= MENU PPID ========================================= //
+ //========================================= STRUKTUR PPID ========================================= //
+ if (type === "strukturppid") {
+ const data = await prisma.strukturPPID.findMany({
+ where: {
+ PegawaiPPID: { namaLengkap: { contains: query, mode: "insensitive" } },
+ },
+ include: {
+ PosisiOrganisasiPPID: true,
+ PegawaiPPID: true,
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= VISI MISI PPID ========================================= //
+ if (type === "visimisippid") {
+ const data = await prisma.visiMisiPPID.findMany({
+ where: {
+ visi: { contains: query, mode: "insensitive" },
+ misi: { contains: query, mode: "insensitive" },
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ //
+
+ // ========================================= DASAR HUKUM PPID ========================================= //
+ if (type === "dasarhukumppid") {
+ const data = await prisma.dasarHukumPPID.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PROFILE PPID ========================================= //
+ if (type === "profileppid") {
+ const data = await prisma.profilePPID.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= DAFTAR INFORMASI PUBLIK ========================================= //
+ if (type === "daftarinformasipublik") {
+ const data = await prisma.daftarInformasiPublik.findMany({
+ where: { jenisInformasi: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ //=========================================PERMOHONAN INFORMASI PUBLIK========================= //
+ if (type === "permohonaninformasipublik") {
+ const data = await prisma.permohonanInformasiPublik.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ //=========================================PERMOHONAN INFORMASI KEBERATAN PUBLIK========================= //
+ if (type === "permohonaninformasikeberatanpublik") {
+ const data = await prisma.formulirPermohonanKeberatan.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= IKM ========================================= //
+ if (type === "ikm") {
+ const data = await prisma.indeksKepuasanMasyarakat.findMany({
+ where: { label: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= MENU DESA ========================================= //
+ // ========================================= PROFILE DESA ========================================= //
+ if (type === "sejarahdesa") {
+ const data = await prisma.sejarahDesa.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "visimisidesa") {
+ const data = await prisma.visiMisiDesa.findMany({
+ where: { visi: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "lambangdesa") {
+ const data = await prisma.lambangDesa.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "maskotdesa") {
+ const data = await prisma.maskotDesa.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "profilperbekel") {
+ const data = await prisma.profilPerbekel.findMany({
+ where: { biodata: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "perbekeldarmasaba") {
+ const data = await prisma.perbekelDariMasaKeMasa.findMany({
+ where: { nama: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= BERITA ========================================= //
+ if (type === "berita") {
+ const data = await prisma.berita.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "kategoriBerita") {
+ const data = await prisma.kategoriBerita.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= POTENSI DESA ========================================= //
+ if (type === "potensi") {
+ const data = await prisma.potensiDesa.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PENGUMUMAN ========================================= //
+ if (type === "pengumuman") {
+ const data = await prisma.pengumuman.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= GALLERY ========================================= //
+ if (type === "galleryFoto") {
+ const data = await prisma.galleryFoto.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "galleryVideo") {
+ const data = await prisma.galleryVideo.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= LAYANAN DESA ========================================= //
+ if (type === "pelayananSuratKeterangan") {
+ const data = await prisma.pelayananSuratKeterangan.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "pelayananPerizinanBerusaha") {
+ const data = await prisma.pelayananPerizinanBerusaha.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "pelayananTelunjukSaktiDesa") {
+ const data = await prisma.pelayananTelunjukSaktiDesa.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "pelayananPendudukNonPermanen") {
+ const data = await prisma.pelayananPendudukNonPermanen.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PENGHARGAAN ========================================= //
+ if (type === "penghargaan") {
+ const data = await prisma.penghargaan.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= MENU KESEHATAN ========================================= //
+ // ========================================= POSYANDU ========================================= //
+ if (type === "posyandu") {
+ const data = await prisma.posyandu.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+ // ========================================= DATA KESEHATAN WARGA ========================================= //
+ // ========================================= FASILITAS KESEHATAN ========================================= //
+ if (type === "fasilitasKesehatan") {
+ const data = await prisma.fasilitasKesehatan.findMany({
+ where: {
+ name: { contains: query, mode: "insensitive" },
+ informasiumum: { fasilitas: { contains: query, mode: "insensitive" } },
+ layananunggulan: { content: { contains: query, mode: "insensitive" } },
+ dokterdantenagamedis: {
+ name: { contains: query, mode: "insensitive" },
+ },
+ fasilitaspendukung: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ prosedurpendaftaran: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ tarifdanlayanan: { layanan: { contains: query, mode: "insensitive" } },
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= JADWAL KEGIATAN ========================================= //
+ if (type === "jadwalKegiatan") {
+ const data = await prisma.jadwalKegiatan.findMany({
+ where: {
+ content: { contains: query, mode: "insensitive" },
+ informasijadwalkegiatan: {
+ name: { contains: query, mode: "insensitive" },
+ },
+ deskripsijadwalkegiatan: {
+ deskripsi: { contains: query, mode: "insensitive" },
+ },
+ layananjadwalkegiatan: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ syaratketentuanjadwalkegiatan: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ dokumenjadwalkegiatan: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ pendaftaranjadwalkegiatan: {
+ name: { contains: query, mode: "insensitive" },
+ },
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= ARTIKEL KESEHATAN ========================================= //
+ if (type === "artikelKesehatan") {
+ const data = await prisma.artikelKesehatan.findMany({
+ where: {
+ title: { contains: query, mode: "insensitive" },
+ content: { contains: query, mode: "insensitive" },
+ introduction: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ symptom: {
+ title: { contains: query, mode: "insensitive" },
+ },
+ prevention: {
+ title: { contains: query, mode: "insensitive" },
+ },
+ firstaid: {
+ title: { contains: query, mode: "insensitive" },
+ },
+ mythvsfact: {
+ title: { contains: query, mode: "insensitive" },
+ },
+ doctorsign: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PUSKESMAS ========================================= //
+ if (type === "puskesmas") {
+ const data = await prisma.puskesmas.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PROGRAM KESEHATAN ========================================= //
+ if (type === "programKesehatan") {
+ const data = await prisma.programKesehatan.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PENANGANAN DARURAT ========================================= //
+ if (type === "penangananDarurat") {
+ const data = await prisma.penangananDarurat.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= KONTAK DARURAT ========================================= //
+ if (type === "kontakDarurat") {
+ const data = await prisma.kontakDarurat.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= INFO WABAH PENYAKIT ========================================= //
+ if (type === "infoWabahPenyakit") {
+ const data = await prisma.infoWabahPenyakit.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= MENU KEAMANAN ========================================= //
+ // ========================================= KEAMANAN LINGKUNGAN ========================================= //
+ if (type === "keamananLingkungan") {
+ const data = await prisma.keamananLingkungan.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= POLSEK TERDEKAT ========================================= //
+ if (type === "polsekTerdekat") {
+ const data = await prisma.polsekTerdekat.findMany({
+ where: { nama: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= KONTAK DARURAT ========================================= //
+ if (type === "kontakDaruratKeamanan") {
+ const data = await prisma.kontakDaruratKeamanan.findMany({
+ where: { nama: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PENCEGAHAN KRIMINALITAS ========================================= //
+ if (type === "pencegahanKriminalitas") {
+ const data = await prisma.pencegahanKriminalitas.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= LAPORAN PUBLIK ========================================= //
+ if (type === "laporanPublik") {
+ const data = await prisma.laporanPublik.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= TIPS KEAMANAN ========================================= //
+ if (type === "tipsKeamanan") {
+ const data = await prisma.menuTipsKeamanan.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= MENU EKONOMI ========================================= //
+ // ========================================= PASAR DESA ========================================= //
+ if (type === "pasarDesa") {
+ const data = await prisma.pasarDesa.findMany({
+ where: { nama: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= LOWONGAN KERJA LOKAL ========================================= //
+ if (type === "lowonganKerjaLokal") {
+ const data = await prisma.lowonganPekerjaan.findMany({
+ where: {
+ OR: [
+ { posisi: { contains: query, mode: "insensitive" } },
+ { namaPerusahaan: { contains: query, mode: "insensitive" } },
+ { lokasi: { contains: query, mode: "insensitive" } },
+ { tipePekerjaan: { contains: query, mode: "insensitive" } },
+ { gaji: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } },
+ { kualifikasi: { contains: query, mode: "insensitive" } },
+ { notelp: { contains: query, mode: "insensitive" } },
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+ // ========================================= STRUKTUR ORGANISASI ========================================= //
+ if (type === "strukturOrganisasi") {
+ const data = await prisma.strukturBumDes.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { PosisiOrganisasiBumDes: { nama: { contains: query, mode: "insensitive" } } },
+ { PegawaiBumDes: { namaLengkap: { contains: query, mode: "insensitive" } } },
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= JUMLAH PENDUDUK USIA KERJA YANG MENGANGGUR ========================================= //
+ if (type === "jumlahPendudukUsiaKerjaYangMenganggurUsia") {
+ const data = await prisma.grafikMenganggurBerdasarkanUsia.findMany({
+ where: {
+ OR: [
+ { usia18_25: { contains: query, mode: "insensitive" } },
+ { usia26_35: { contains: query, mode: "insensitive" } },
+ { usia36_45: { contains: query, mode: "insensitive" } },
+ { usia46_keatas: { contains: query, mode: "insensitive" } },
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= JUMLAH PENDUDUK USIA KERJA YANG MENGANGGUR ========================================= //
+ if (type === "jumlahPendudukUsiaKerjaYangMenganggurPendidikan") {
+ const data = await prisma.grafikMenganggurBerdasarkanPendidikan.findMany({
+ where: {
+ OR: [
+ { SD: { contains: query, mode: "insensitive" } },
+ { SMP: { contains: query, mode: "insensitive" } },
+ { SMA: { contains: query, mode: "insensitive" } },
+ { D3: { contains: query, mode: "insensitive" } },
+ { S1: { contains: query, mode: "insensitive" } },
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= JUMLAH PENDUDUK MISKIN ========================================= //
+ if (type === "jumlahPendudukMiskin") {
+ const data = await prisma.grafikJumlahPendudukMiskin.findMany({
+ where: {
+ OR: [
+ // Convert year to string for partial matching
+ ...(isNaN(Number(query)) ? [] : [
+ { year: { equals: Number(query) } }
+ ]),
+ // Convert totalPoorPopulation to string for partial matching
+ ...(isNaN(Number(query)) ? [] : [
+ { totalPoorPopulation: { equals: Number(query) } }
+ ])
+ ].filter(Boolean), // Remove any empty arrays from the spread
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+ // ========================================= PROGRAM KEMISKINAN ========================================= //
+ if (type === "programKemiskinan") {
+ const data = await prisma.programKemiskinan.findMany({
+ where: {
+ OR: [
+ { nama: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } },
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+
+ // ========================================= SEKTOR UNGGULAN DESA ========================================= //
+ if (type === "sektorUnggulanDesa") {
+ const data = await prisma.sektorUnggulanDesa.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { description: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= DEMOGRAFI PEKERJAAN ========================================= //
+ if (type === "demografiPekerjaan") {
+ const data = await prisma.dataDemografiPekerjaan.findMany({
+ where: { pekerjaan: { contains: query, mode: "insensitive" } },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= MENU INOVASI ========================================= //
+// ========================================= DESA DIGITAL / SMART VILLAGE ========================================= //
+
+ if (type === "desaDigital") {
+ const data = await prisma.desaDigital.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PROGRAM KREATIF ========================================= //
+
+ if (type === "programKreatif") {
+ const data = await prisma.programKreatif.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { slug: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= KOLABORASI INOVASI ========================================= //
+
+ if (type === "kolaborasiInovasi") {
+ const data = await prisma.kolaborasiInovasi.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { slug: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } },
+ { kolaborator: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "mitraKolaborasi") {
+ const data = await prisma.mitraKolaborasi.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= INFO TEKHNOLOGI TEPAT GUNA ========================================= //
+
+ if (type === "infoTekno") {
+ const data = await prisma.infoTekno.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= LINGKUNGAN ========================================= //
+// ========================================= PENGELOLAAN SAMPAH ========================================= //
+
+ if (type === "pengelolaanSampah") {
+ const data = await prisma.pengelolaanSampah.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "keteranganBankSampahTerdekat") {
+ const data = await prisma.keteranganBankSampahTerdekat.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { alamat: { contains: query, mode: "insensitive" } },
+ { namaTempatMaps: { contains: query, mode: "insensitive" } },
+ { linkPetunjukArah: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PORGRAM PENGHIJAUAN ========================================= //
+
+ if (type === "programPenghijauan") {
+ const data = await prisma.programPenghijauan.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= DATA LINGKUNGAN DESA ========================================= //
+
+ if (type === "dataLingkunganDesa") {
+ const data = await prisma.dataLingkunganDesa.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= GOTONG ROYONG ========================================= //
+
+ if (type === "gotongRoyong") {
+ const data = await prisma.kegiatanDesa.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsiSingkat: { contains: query, mode: "insensitive" } },
+ { deskripsiLengkap: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= EDUKASI LINGKUNGAN ========================================= //
+
+ if (type === "tujuanEdukasiLingkungan") {
+ const data = await prisma.tujuanEdukasiLingkungan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "materiEdukasiLingkungan") {
+ const data = await prisma.materiEdukasiLingkungan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "contohEdukasiLingkungan") {
+ const data = await prisma.contohEdukasiLingkungan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= KONSERVASI ADAT BALI ========================================= //
+
+ if (type === "filosofiTriHita") {
+ const data = await prisma.filosofiTriHita.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "bentukKonservasiBerdasarkanAdat") {
+ const data = await prisma.bentukKonservasiBerdasarkanAdat.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "nilaiKonservasiAdat") {
+ const data = await prisma.nilaiKonservasiAdat.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= MENU PENDIDIKAN ========================================= //
+// ========================================= INFO SEKOLAH & PAUD ========================================= //
+
+ if (type === "jenjangPendidikan") {
+ const data = await prisma.jenjangPendidikan.findMany({
+ where: {
+ OR: [
+ { nama: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "lembaga") {
+ const data = await prisma.lembaga.findMany({
+ where: {
+ OR: [
+ { nama: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "siswa") {
+ const data = await prisma.siswa.findMany({
+ where: {
+ OR: [
+ { nama: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "pengajar") {
+ const data = await prisma.pengajar.findMany({
+ where: {
+ OR: [
+ { nama: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= BEASISWA DESA ========================================= //
+ if (type === "keunggulanProgram") {
+ const data = await prisma.keunggulanProgram.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PROGRAM PENDIDIKAN ANAK ========================================= //
+ if (type === "tujuanProgram") {
+ const data = await prisma.tujuanProgram.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "programUnggulan") {
+ const data = await prisma.programUnggulan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "lokasiJadwalBimbinganBelajarDesa") {
+ const data = await prisma.lokasiJadwalBimbinganBelajarDesa.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "fasilitasBimbinganBelajarDesa") {
+ const data = await prisma.fasilitasBimbinganBelajarDesa.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PENDIDIKAN NON FORMAL ========================================= //
+ if (type === "tujuanPendidikanNonFormal") {
+ const data = await prisma.tujuanPendidikanNonFormal.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "tempatKegiatan") {
+ const data = await prisma.tempatKegiatan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ if (type === "jenisProgramYangDiselenggarakan") {
+ const data = await prisma.jenisProgramYangDiselenggarakan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= PERPUSTAKAAN ========================================= //
+ if (type === "dataPerpustakaan") {
+ const data = await prisma.dataPerpustakaan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // ========================================= DATA PENDIDIKAN ========================================= //
+ if (type === "dataPendidikan") {
+ const data = await prisma.dataPendidikan.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { jumlah: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ skip,
+ take: limitNum,
+ });
+ return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
+ }
+
+ // π GLOBAL SEARCH β cari di beberapa modul sekaligus
+ const [
+ pejabatdesa,
+ programinovasi,
+ mediasosial,
+ desaantikorupsi,
+ sdgsdesa,
+ apbdes,
+ prestasidesa,
+ responden,
+ strukturppid,
+ visimisippid,
+ dasarhukumppid,
+ profileppid,
+ daftarinformasipublik,
+ permohonaninformasipublik,
+ permohonaninformasikeberatanpublik,
+ ikm,
+ sejarahdesa,
+ visimisidesa,
+ lambangdesa,
+ maskotdesa,
+ profilperbekel,
+ perbekeldarmasaba,
+ berita,
+ kategoriBerita,
+ potensi,
+ pengumuman,
+ galleryFoto,
+ galleryVideo,
+ pelayananSuratKeterangan,
+ pelayananPerizinanBerusaha,
+ pelayananTelunjukSaktiDesa,
+ pelayananPendudukNonPermanen,
+ penghargaan,
+ posyandu,
+ fasilitasKesehatan,
+ jadwalKegiatan,
+ artikelKesehatan,
+ puskesmas,
+ programKesehatan,
+ penangananDarurat,
+ kontakDarurat,
+ infoWabahPenyakit,
+ keamananLingkungan,
+ polsekTerdekat,
+ kontakDaruratKeamanan,
+ pencegahanKriminalitas,
+ laporanPublik,
+ tipsKeamanan,
+ pasarDesa,
+ lowonganKerjaLokal,
+ strukturOrganisasi,
+ jumlahPendudukUsiaKerjaYangMenganggurUsia,
+ jumlahPendudukUsiaKerjaYangMenganggurPendidikan,
+ jumlahPendudukMiskin,
+ programKemiskinan,
+ sektorUnggulanDesa,
+ demografiPekerjaan,
+ desaDigital,
+ programKreatif,
+ kolaborasiInovasi,
+ mitraKolaborasi,
+ infoTekno,
+ pengelolaanSampah,
+ keteranganBankSampahTerdekat,
+ programPenghijauan,
+ dataLingkunganDesa,
+ gotongRoyong,
+ tujuanEdukasiLingkungan,
+ materiEdukasiLingkungan,
+ contohEdukasiLingkungan,
+ filosofiTriHita,
+ bentukKonservasiBerdasarkanAdat,
+ nilaiKonservasiAdat,
+ jenjangPendidikan,
+ lembaga,
+ siswa,
+ pengajar,
+ keunggulanProgram,
+ tujuanProgram,
+ programUnggulan,
+ lokasiJadwalBimbinganBelajarDesa,
+ fasilitasBimbinganBelajarDesa,
+ tujuanPendidikanNonFormal,
+ tempatKegiatan,
+ jenisProgramYangDiselenggarakan,
+ dataPerpustakaan,
+ dataPendidikan
+
+ ] = await Promise.all([
+ prisma.pejabatDesa.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.programInovasi.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.mediaSosial.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.desaAntiKorupsi.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.sdgsDesa.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.aPBDes.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.prestasiDesa.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.responden.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ // β
FIXED
+ prisma.strukturPPID.findMany({
+ where: {
+ PegawaiPPID: { namaLengkap: { contains: query, mode: "insensitive" } },
+ },
+ include: { PegawaiPPID: true },
+ take: limitNum,
+ }),
+ prisma.visiMisiPPID.findMany({
+ where: {
+ OR: [
+ { visi: { contains: query, mode: "insensitive" } },
+ { misi: { contains: query, mode: "insensitive" } },
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.dasarHukumPPID.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.profilePPID.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.daftarInformasiPublik.findMany({
+ where: { jenisInformasi: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.permohonanInformasiPublik.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.formulirPermohonanKeberatan.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.indeksKepuasanMasyarakat.findMany({
+ where: { label: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.sejarahDesa.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.visiMisiDesa.findMany({
+ where: {
+ OR: [
+ { visi: { contains: query, mode: "insensitive" } },
+ { misi: { contains: query, mode: "insensitive" } },
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.lambangDesa.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.maskotDesa.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.profilPerbekel.findMany({
+ where: { biodata: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.perbekelDariMasaKeMasa.findMany({
+ where: { nama: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.berita.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.kategoriBerita.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.potensiDesa.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.pengumuman.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.galleryFoto.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.galleryVideo.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.pelayananSuratKeterangan.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.pelayananPerizinanBerusaha.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.pelayananTelunjukSaktiDesa.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.pelayananPendudukNonPermanen.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.penghargaan.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.posyandu.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.fasilitasKesehatan.findMany({
+ where: {
+ name: { contains: query, mode: "insensitive" },
+ informasiumum: { fasilitas: { contains: query, mode: "insensitive" } },
+ layananunggulan: { content: { contains: query, mode: "insensitive" } },
+ dokterdantenagamedis: {
+ name: { contains: query, mode: "insensitive" },
+ },
+ fasilitaspendukung: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ prosedurpendaftaran: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ tarifdanlayanan: { layanan: { contains: query, mode: "insensitive" } },
+ },
+ take: limitNum,
+ }),
+ prisma.jadwalKegiatan.findMany({
+ where: {
+ content: { contains: query, mode: "insensitive" },
+ informasijadwalkegiatan: {
+ name: { contains: query, mode: "insensitive" },
+ },
+ deskripsijadwalkegiatan: {
+ deskripsi: { contains: query, mode: "insensitive" },
+ },
+ layananjadwalkegiatan: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ syaratketentuanjadwalkegiatan: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ dokumenjadwalkegiatan: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ pendaftaranjadwalkegiatan: {
+ name: { contains: query, mode: "insensitive" },
+ },
+ },
+ take: limitNum,
+ }),
+ prisma.artikelKesehatan.findMany({
+ where: {
+ title: { contains: query, mode: "insensitive" },
+ content: { contains: query, mode: "insensitive" },
+ introduction: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ symptom: {
+ title: { contains: query, mode: "insensitive" },
+ },
+ prevention: {
+ title: { contains: query, mode: "insensitive" },
+ },
+ firstaid: {
+ title: { contains: query, mode: "insensitive" },
+ },
+ mythvsfact: {
+ title: { contains: query, mode: "insensitive" },
+ },
+ doctorsign: {
+ content: { contains: query, mode: "insensitive" },
+ },
+ },
+ take: limitNum,
+ }),
+ prisma.puskesmas.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.programKesehatan.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.penangananDarurat.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.kontakDarurat.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.infoWabahPenyakit.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.keamananLingkungan.findMany({
+ where: { name: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.polsekTerdekat.findMany({
+ where: { nama: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.kontakDaruratKeamanan.findMany({
+ where: { nama: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.pencegahanKriminalitas.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.laporanPublik.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.menuTipsKeamanan.findMany({
+ where: { judul: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.pasarDesa.findMany({
+ where: { nama: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.lowonganPekerjaan.findMany({
+ where: { posisi: { contains: query, mode: "insensitive" } },
+ take: limitNum,
+ }),
+ prisma.strukturBumDes.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { PosisiOrganisasiBumDes: { nama: { contains: query, mode: "insensitive" } } },
+ { PegawaiBumDes: { namaLengkap: { contains: query, mode: "insensitive" } } },
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.grafikMenganggurBerdasarkanUsia.findMany({
+ where: {
+ OR: [
+ { usia18_25: { contains: query, mode: "insensitive" } },
+ { usia26_35: { contains: query, mode: "insensitive" } },
+ { usia36_45: { contains: query, mode: "insensitive" } },
+ { usia46_keatas: { contains: query, mode: "insensitive" } },
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.grafikMenganggurBerdasarkanPendidikan.findMany({
+ where: {
+ OR: [
+ { SD: { contains: query, mode: "insensitive" } },
+ { SMP: { contains: query, mode: "insensitive" } },
+ { SMA: { contains: query, mode: "insensitive" } },
+ { D3: { contains: query, mode: "insensitive" } },
+ { S1: { contains: query, mode: "insensitive" } },
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.grafikJumlahPendudukMiskin.findMany({
+ where: {
+ OR: [
+ ...(isNaN(Number(query)) ? [] : [
+ { year: { equals: Number(query) } },
+ { totalPoorPopulation: { equals: Number(query) } }
+ ])
+ ].filter(Boolean),
+ },
+ take: limitNum,
+ }),
+ prisma.programKemiskinan.findMany({
+ where: {
+ OR: [
+ { nama: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } },
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.sektorUnggulanDesa.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { description: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.dataDemografiPekerjaan.findMany({
+ where: {
+ pekerjaan: { contains: query, mode: "insensitive" }
+ },
+ take: limitNum,
+ }),
+ prisma.desaDigital.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.programKreatif.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { slug: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.kolaborasiInovasi.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { slug: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } },
+ { kolaborator: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.mitraKolaborasi.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.infoTekno.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.pengelolaanSampah.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.keteranganBankSampahTerdekat.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { alamat: { contains: query, mode: "insensitive" } },
+ { namaTempatMaps: { contains: query, mode: "insensitive" } },
+ { linkPetunjukArah: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.programPenghijauan.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.dataLingkunganDesa.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.kegiatanDesa.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsiSingkat: { contains: query, mode: "insensitive" } },
+ { deskripsiLengkap: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.tujuanEdukasiLingkungan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.materiEdukasiLingkungan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.contohEdukasiLingkungan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.filosofiTriHita.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.bentukKonservasiBerdasarkanAdat.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.nilaiKonservasiAdat.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.jenjangPendidikan.findMany({
+ where: {
+ OR: [
+ { nama: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.lembaga.findMany({
+ where: {
+ OR: [
+ { nama: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.siswa.findMany({
+ where: {
+ OR: [
+ { nama: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.pengajar.findMany({
+ where: {
+ OR: [
+ { nama: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.keunggulanProgram.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.tujuanProgram.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.programUnggulan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.lokasiJadwalBimbinganBelajarDesa.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.fasilitasBimbinganBelajarDesa.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.tujuanPendidikanNonFormal.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.tempatKegiatan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.jenisProgramYangDiselenggarakan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.dataPerpustakaan.findMany({
+ where: {
+ OR: [
+ { judul: { contains: query, mode: "insensitive" } },
+ { deskripsi: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ }),
+ prisma.dataPendidikan.findMany({
+ where: {
+ OR: [
+ { name: { contains: query, mode: "insensitive" } },
+ { jumlah: { contains: query, mode: "insensitive" } }
+ ],
+ },
+ take: limitNum,
+ })
+ ]);
+
+ return {
+ data: [
+ ...pejabatdesa.map((b) => ({ type: "pejabatdesa", ...b })),
+ ...programinovasi.map((b) => ({ type: "programinovasi", ...b })),
+ ...mediasosial.map((b) => ({ type: "mediaSosial", ...b })),
+ ...desaantikorupsi.map((b) => ({ type: "desaantikorupsi", ...b })),
+ ...sdgsdesa.map((b) => ({ type: "sdgsdesa", ...b })),
+ ...apbdes.map((b) => ({ type: "apbdes", ...b })),
+ ...prestasidesa.map((b) => ({ type: "prestasidesa", ...b })),
+ ...responden.map((b) => ({ type: "responden", ...b })),
+ ...strukturppid.map((b) => ({ type: "strukturppid", ...b })),
+ ...visimisippid.map((b) => ({ type: "visimisippid", ...b })),
+ ...dasarhukumppid.map((b) => ({ type: "dasarhukumppid", ...b })),
+ ...profileppid.map((b) => ({ type: "profileppid", ...b })),
+ ...daftarinformasipublik.map((b) => ({
+ type: "daftarinformasipublik",
+ ...b,
+ })),
+ ...permohonaninformasipublik.map((b) => ({
+ type: "permohonaninformasipublik",
+ ...b,
+ })),
+ ...permohonaninformasikeberatanpublik.map((b) => ({
+ type: "permohonaninformasikeberatanpublik",
+ ...b,
+ })),
+ ...ikm.map((b) => ({ type: "ikm", ...b })),
+ ...sejarahdesa.map((b) => ({ type: "sejarahdesa", ...b })),
+ ...visimisidesa.map((b) => ({ type: "visimisidesa", ...b })),
+ ...lambangdesa.map((b) => ({ type: "lambangdesa", ...b })),
+ ...maskotdesa.map((b) => ({ type: "maskotdesa", ...b })),
+ ...profilperbekel.map((b) => ({ type: "profilperbekel", ...b })),
+ ...perbekeldarmasaba.map((b) => ({ type: "perbekeldarmasaba", ...b })),
+ ...berita.map((b) => ({ type: "berita", ...b })),
+ ...kategoriBerita.map((b) => ({ type: "kategoriBerita", ...b })),
+ ...potensi.map((b) => ({ type: "potensi", ...b })),
+ ...pengumuman.map((b) => ({ type: "pengumuman", ...b })),
+ ...galleryFoto.map((b) => ({ type: "galleryFoto", ...b })),
+ ...galleryVideo.map((b) => ({ type: "galleryVideo", ...b })),
+ ...pelayananSuratKeterangan.map((b) => ({
+ type: "pelayananSuratKeterangan",
+ ...b,
+ })),
+ ...pelayananPerizinanBerusaha.map((b) => ({
+ type: "pelayananPerizinanBerusaha",
+ ...b,
+ })),
+ ...pelayananTelunjukSaktiDesa.map((b) => ({
+ type: "pelayananTelunjukSaktiDesa",
+ ...b,
+ })),
+ ...pelayananPendudukNonPermanen.map((b) => ({
+ type: "pelayananPendudukNonPermanen",
+ ...b,
+ })),
+ ...penghargaan.map((b) => ({ type: "penghargaan", ...b })),
+ ...posyandu.map((b) => ({ type: "posyandu", ...b })),
+ ...fasilitasKesehatan.map((b) => ({ type: "fasilitasKesehatan", ...b })),
+ ...jadwalKegiatan.map((b) => ({ type: "jadwalKegiatan", ...b })),
+ ...artikelKesehatan.map((b) => ({ type: "artikelKesehatan", ...b })),
+ ...puskesmas.map((b) => ({ type: "puskesmas", ...b })),
+ ...programKesehatan.map((b) => ({ type: "programKesehatan", ...b })),
+ ...penangananDarurat.map((b) => ({ type: "penangananDarurat", ...b })),
+ ...kontakDarurat.map((b) => ({ type: "kontakDarurat", ...b })),
+ ...infoWabahPenyakit.map((b) => ({ type: "infoWabahPenyakit", ...b })),
+ ...keamananLingkungan.map((b) => ({ type: "keamananLingkungan", ...b })),
+ ...polsekTerdekat.map((b) => ({ type: "polsekTerdekat", ...b })),
+ ...kontakDaruratKeamanan.map((b) => ({ type: "kontakDaruratKeamanan", ...b })),
+ ...pencegahanKriminalitas.map((b) => ({ type: "pencegahanKriminalitas", ...b })),
+ ...laporanPublik.map((b) => ({ type: "laporanPublik", ...b })),
+ ...tipsKeamanan.map((b) => ({ type: "tipsKeamanan", ...b })),
+ ...pasarDesa.map((b) => ({ type: "pasarDesa", ...b })),
+ ...lowonganKerjaLokal.map((b) => ({ type: "lowonganKerjaLokal", ...b })),
+ ...strukturOrganisasi.map((b) => ({ type: "strukturOrganisasi", ...b })),
+ ...jumlahPendudukUsiaKerjaYangMenganggurUsia.map((b) => ({ type: "jumlahPendudukUsiaKerjaYangMenganggurUsia", ...b })),
+ ...jumlahPendudukUsiaKerjaYangMenganggurPendidikan.map((b) => ({ type: "jumlahPendudukUsiaKerjaYangMenganggurPendidikan", ...b })),
+ ...jumlahPendudukMiskin.map((b) => ({ type: "jumlahPendudukMiskin", ...b })),
+ ...programKemiskinan.map((b) => ({ type: "programKemiskinan", ...b })),
+ ...sektorUnggulanDesa.map((b) => ({ type: "sektorUnggulanDesa", ...b })),
+ ...demografiPekerjaan.map((b) => ({ type: "demografiPekerjaan", ...b })),
+ ...desaDigital.map((b) => ({ type: "desaDigital", ...b })),
+ ...programKreatif.map((b) => ({ type: "programKreatif", ...b })),
+ ...kolaborasiInovasi.map((b) => ({ type: "kolaborasiInovasi", ...b })),
+ ...mitraKolaborasi.map((b) => ({ type: "mitraKolaborasi", ...b })),
+ ...infoTekno.map((b) => ({ type: "infoTekno", ...b })),
+ ...pengelolaanSampah.map((b) => ({ type: "pengelolaanSampah", ...b })),
+ ...keteranganBankSampahTerdekat.map((b) => ({ type: "keteranganBankSampahTerdekat", ...b })),
+ ...programPenghijauan.map((b) => ({ type: "programPenghijauan", ...b })),
+ ...dataLingkunganDesa.map((b) => ({ type: "dataLingkunganDesa", ...b })),
+ ...gotongRoyong.map((b) => ({ type: "gotongRoyong", ...b })),
+ ...tujuanEdukasiLingkungan.map((b) => ({ type: "tujuanEdukasiLingkungan", ...b })),
+ ...materiEdukasiLingkungan.map((b) => ({ type: "materiEdukasiLingkungan", ...b })),
+ ...contohEdukasiLingkungan.map((b) => ({ type: "contohEdukasiLingkungan", ...b })),
+ ...filosofiTriHita.map((b) => ({ type: "filosofiTriHita", ...b })),
+ ...bentukKonservasiBerdasarkanAdat.map((b) => ({ type: "bentukKonservasiBerdasarkanAdat", ...b })),
+ ...nilaiKonservasiAdat.map((b) => ({ type: "nilaiKonservasiAdat", ...b })),
+ ...jenjangPendidikan.map((b) => ({ type: "jenjangPendidikan", ...b })),
+ ...lembaga.map((b) => ({ type: "lembaga", ...b })),
+ ...siswa.map((b) => ({ type: "siswa", ...b })),
+ ...pengajar.map((b) => ({ type: "pengajar", ...b })),
+ ...keunggulanProgram.map((b) => ({ type: "keunggulanProgram", ...b })),
+ ...tujuanProgram.map((b) => ({ type: "tujuanProgram", ...b })),
+ ...programUnggulan.map((b) => ({ type: "programUnggulan", ...b })),
+ ...tujuanPendidikanNonFormal.map((b) => ({ type: "tujuanPendidikanNonFormal", ...b })),
+ ...fasilitasBimbinganBelajarDesa.map((b) => ({ type: "fasilitasBimbinganBelajarDesa", ...b })),
+ ...lokasiJadwalBimbinganBelajarDesa.map((b) => ({ type: "lokasiJadwalBimbinganBelajarDesa", ...b })),
+ ...tempatKegiatan.map((b) => ({ type: "tempatKegiatan", ...b })),
+ ...jenisProgramYangDiselenggarakan.map((b) => ({ type: "jenisProgramYangDiselenggarakan", ...b })),
+ ...dataPerpustakaan.map((b) => ({ type: "dataPerpustakaan", ...b })),
+ ...dataPendidikan.map((b) => ({ type: "dataPendidikan", ...b })),
+
+ ],
+ nextPage: null, // bisa dibuat lebih kompleks kalau perlu
+ };
+}
diff --git a/src/app/api/[[...slugs]]/_lib/search/index.ts b/src/app/api/[[...slugs]]/_lib/search/index.ts
new file mode 100644
index 00000000..83910fab
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/search/index.ts
@@ -0,0 +1,10 @@
+import Elysia from "elysia";
+import searchFindMany from "./findMany";
+
+const Search = new Elysia({
+ prefix: "/api/search",
+ tags: ["Search"],
+})
+ .get("/findMany", searchFindMany);
+
+export default Search;
diff --git a/src/app/api/[[...slugs]]/_lib/search/searchState.ts b/src/app/api/[[...slugs]]/_lib/search/searchState.ts
new file mode 100644
index 00000000..42fa0cff
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/search/searchState.ts
@@ -0,0 +1,162 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+'use client';
+import { proxy } from 'valtio';
+import { debounce } from 'lodash';
+import ApiFetch from '@/lib/api-fetch';
+
+interface SearchResult {
+ type?: string;
+ id: string | number;
+ title?: string;
+ [key: string]: any;
+}
+
+const searchState = proxy({
+ query: '',
+ page: 1,
+ limit: 10,
+ type: '', // kosong = global search
+ results: [] as SearchResult[],
+ nextPage: null as number | null,
+ loading: false,
+
+ async fetch() {
+ if (!searchState.query) {
+ searchState.results = [];
+ return;
+ }
+
+ searchState.loading = true;
+
+ try {
+ const res = await ApiFetch.api.search.findMany.get({
+ query: {
+ query: searchState.query,
+ page: searchState.page,
+ limit: searchState.limit,
+ type: searchState.type,
+ },
+ });
+
+ console.log("Search API Response:", res);
+ const rawItems = res.data?.data || [];
+ const parsedItems = structuredClone(rawItems); // β
penting!
+
+ console.log("β
Parsed items:", parsedItems);
+
+ if (searchState.page === 1) {
+ searchState.results = parsedItems;
+ } else {
+ searchState.results.push(...parsedItems);
+ }
+
+ console.log("Search results render:", searchState.results);
+
+
+ searchState.nextPage = res.data?.nextPage || null;
+ } catch (error) {
+ console.error("Search fetch error:", error);
+ } finally {
+ searchState.loading = false;
+ }
+ },
+
+ async next() {
+ if (!searchState.nextPage || searchState.loading) return;
+ searchState.page = searchState.nextPage;
+ await searchState.fetch();
+ },
+});
+
+// π debounce-nya tetap kita export biar bisa dipanggil manual
+export const debouncedFetch = debounce(() => {
+ searchState.page = 1;
+ searchState.fetch();
+}, 500);
+
+export default searchState;
+
+
+// 'use client';
+// import { proxy, subscribe } from 'valtio';
+// import { debounce } from 'lodash';
+// import ApiFetch from '@/lib/api-fetch';
+
+// interface SearchResult {
+// type?: string;
+// id: string | number;
+// title?: string;
+// [key: string]: any;
+// }
+
+// const searchState = proxy({
+// query: '',
+// page: 1,
+// limit: 10,
+// type: '', // kosong = global search
+// results: [] as SearchResult[],
+// nextPage: null as number | null,
+// loading: false,
+
+// // --- fetch utama ---
+// async fetch() {
+// if (!searchState.query.trim()) {
+// // π§Ή kalau query kosong, kosongin data dan stop
+// searchState.results = [];
+// searchState.nextPage = null;
+// searchState.loading = false;
+// return;
+// }
+
+// searchState.loading = true;
+
+// try {
+// const res = await ApiFetch.api.search.findMany.get({
+// query: {
+// query: searchState.query,
+// page: searchState.page,
+// limit: searchState.limit,
+// type: searchState.type,
+// },
+// });
+
+// const newData = res.data?.data || [];
+
+// // Kalau ini page pertama, replace data
+// if (searchState.page === 1) {
+// searchState.results = newData;
+// } else {
+// // Kalau page berikutnya, append data
+// searchState.results = [...searchState.results, ...newData];
+// }
+
+// searchState.nextPage = res.data?.nextPage || null;
+// } catch (err) {
+// console.error('Search fetch error:', err);
+// } finally {
+// searchState.loading = false;
+// }
+// },
+
+// // --- load next page (infinite scroll) ---
+// async next() {
+// if (!searchState.nextPage || searchState.loading) return;
+// searchState.page = searchState.nextPage;
+// await searchState.fetch();
+// },
+// });
+
+// // --- debounce agar gak fetch tiap ketik ---
+// const debouncedFetch = debounce(() => {
+// // reset pagination setiap query berubah
+// searchState.page = 1;
+// searchState.fetch();
+// }, 500);
+
+// // --- auto trigger setiap query berubah ---
+// subscribe(searchState, () => {
+// // kalau query berubah, jalankan debounce fetch
+// debouncedFetch();
+// });
+
+// export default searchState;
diff --git a/src/app/api/[[...slugs]]/route.ts b/src/app/api/[[...slugs]]/route.ts
index 0ccdc5bd..ed4e2f1b 100644
--- a/src/app/api/[[...slugs]]/route.ts
+++ b/src/app/api/[[...slugs]]/route.ts
@@ -25,6 +25,7 @@ import LandingPage from "./_lib/landing_page";
import Pendidikan from "./_lib/pendidikan";
import User from "./_lib/user";
import Role from "./_lib/user/role";
+import Search from "./_lib/search";
const ROOT = process.cwd();
@@ -95,6 +96,7 @@ const ApiServer = new Elysia()
.use(Pendidikan)
.use(User)
.use(Role)
+ .use(Search)
.onError(({ code }) => {
if (code === "NOT_FOUND") {
diff --git a/src/app/api/subscribe/route.ts b/src/app/api/subscribe/route.ts
new file mode 100644
index 00000000..ce360546
--- /dev/null
+++ b/src/app/api/subscribe/route.ts
@@ -0,0 +1,54 @@
+import { NextResponse } from 'next/server';
+import nodemailer from 'nodemailer';
+
+export async function POST(request: Request) {
+ try {
+ const { email } = await request.json();
+
+ // Input validation
+ if (!email) {
+ return NextResponse.json(
+ { success: false, message: 'Email is required' },
+ { status: 400 }
+ );
+ }
+
+ // Email regex validation
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!emailRegex.test(email)) {
+ return NextResponse.json(
+ { success: false, message: 'Invalid email format' },
+ { status: 400 }
+ );
+ }
+
+ // Configure nodemailer
+ const transporter = nodemailer.createTransport({
+ service: 'gmail',
+ auth: {
+ user: process.env.EMAIL_USER,
+ pass: process.env.EMAIL_PASS,
+ },
+ });
+
+ // Send email
+ await transporter.sendMail({
+ from: `"Tim Info" <${process.env.EMAIL_USER}>`,
+ to: email,
+ subject: 'β
Berhasil Berlangganan!',
+ html: `Terima kasih telah berlangganan info terbaru dari kami!
`,
+ });
+
+ return NextResponse.json({
+ success: true,
+ message: 'Subscription successful! Please check your email.',
+ });
+
+ } catch (error) {
+ console.error('Error in subscribe API:', error);
+ return NextResponse.json(
+ { success: false, message: 'Internal server error' },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/app/coba/page.tsx b/src/app/coba/page.tsx
index 903aa6fe..fe89eae3 100644
--- a/src/app/coba/page.tsx
+++ b/src/app/coba/page.tsx
@@ -1,83 +1,52 @@
import colors from '@/con/colors';
-import { Stack, Box, Container, Grid, GridCol, Group, Paper, TextInput, Text, Image, Flex, Button } from '@mantine/core';
-import { IconCalendar, IconMapPin, IconSearch, IconUsersGroup } from '@tabler/icons-react';
+import { Stack, Container, Box, List, ListItem, Text, Image } from '@mantine/core';
import React from 'react';
+import BackButton from '../darmasaba/(pages)/desa/layanan/_com/BackButto';
-import Link from 'next/link';
function Page() {
return (
-
- {/* Header */}
-
-
-
- Program Gotong Royong
+
+
+
+
+
+ Bumdes Pudak Mesari
-
- Desa Darmasaba
+
+ Informasi dan Pelayanan Administrasi Digital
-
+
+
-
- {/* Tabs Menu */}
-
-
-
-
-
-
- Semua
-
-
- {['Kebersihan', 'Infrastruktur', 'Sosial', 'Lingkungan'].map((kategori) => (
-
-
- {kategori}
-
-
- ))}
-
-
-
- }
- w="100%"
- />
-
-
-
-
-
- Membangun Fasilitas Desa
-
-
- Sosial
-
-
-
- Program Pembangunan Fasilitas Desa Maju, Masyarakat Sejahtera.
-
-
-
- 1 April 2025
-
-
-
- Banjar Desa Darmasaba
-
-
-
- 30 Partisipan
-
- Deskripsi :Β Program pembangunan Pura sebagai pusat spiritual dan budaya desa, melibatkan gotong royong masyarakat dalam pembangunan struktur utama serta ornamen tradisional.
-
- Daftar Sebagai Relawan
-
-
-
+
+
+ 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.
+
+
+ Potensi dan Peran BUMDes Pudak Mesari:
+
+
+
+ Pengembangan Usaha Mikro dan Kecil: BUMDes Pudak Mesari menyediakan layanan bagi pelaku usaha mikro dan kecil di desa, seperti penyediaan konsumsi dan snack kotak untuk berbagai acara.
+
+
+ Pengelolaan Sampah Berbasis Masyarakat: Melalui kolaborasi dengan komunitas pemuda peduli lingkungan, BUMDes Pudak Mesari aktif dalam pengelolaan sampah berbasis masyarakat.
+
+
+ Peningkatan Kapasitas dan Transparansi: Untuk memastikan pengelolaan yang akuntabel, BUMDes Pudak Mesari rutin mengadakan rapat koordinasi dan pendampingan penyusunan laporan pertanggungjawaban.
+
+
+ Kolaborasi Internasional: Desa Darmasaba, melalui BUMDes Pudak Mesari, menerima kunjungan dari tim Osaki Jepang untuk memperkuat pengelolaan sampah dan lingkungan.
+
+
+
+ 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.
+
);
diff --git a/src/app/darmasaba/(pages)/desa/berita/[kategori]/Content.tsx b/src/app/darmasaba/(pages)/desa/berita/[kategori]/Content.tsx
index 350d3032..8871dc52 100644
--- a/src/app/darmasaba/(pages)/desa/berita/[kategori]/Content.tsx
+++ b/src/app/darmasaba/(pages)/desa/berita/[kategori]/Content.tsx
@@ -75,7 +75,7 @@ export default function Content({ kategori }: { kategori: string }) {
{featured.kategoriBerita?.name || kategori}
{featured.judul}
- {featured.deskripsi}
+
@@ -135,9 +135,9 @@ export default function Content({ kategori }: { kategori: string }) {
{item.kategoriBerita?.name || kategori}
{item.judul}
- {item.deskripsi}
+
-
+
{new Date(item.createdAt).toLocaleDateString('id-ID', {
day: 'numeric',
month: 'short',
diff --git a/src/app/darmasaba/(pages)/desa/berita/[kategori]/[id]/page.tsx b/src/app/darmasaba/(pages)/desa/berita/[kategori]/[id]/page.tsx
index 29dcac13..031608aa 100644
--- a/src/app/darmasaba/(pages)/desa/berita/[kategori]/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/berita/[kategori]/[id]/page.tsx
@@ -62,11 +62,23 @@ function Page() {
Informasi dan Pelayanan Administrasi Digital
-
+
-
+
diff --git a/src/app/darmasaba/(pages)/desa/berita/[kategori]/page.tsx b/src/app/darmasaba/(pages)/desa/berita/[kategori]/page.tsx
index 517a69a5..54ba6b26 100644
--- a/src/app/darmasaba/(pages)/desa/berita/[kategori]/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/berita/[kategori]/page.tsx
@@ -1,4 +1,3 @@
-// src/app/darmasaba/(pages)/desa/berita/[kategori]/page.tsx
import { Suspense } from "react";
import Content from "./Content";
diff --git a/src/app/darmasaba/(pages)/desa/berita/_lib/layoutTabs.tsx b/src/app/darmasaba/(pages)/desa/berita/_lib/layoutTabs.tsx
index f5b100ec..c32d9dd7 100644
--- a/src/app/darmasaba/(pages)/desa/berita/_lib/layoutTabs.tsx
+++ b/src/app/darmasaba/(pages)/desa/berita/_lib/layoutTabs.tsx
@@ -1,3 +1,152 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+// 'use client'
+// import colors from '@/con/colors';
+// import { Box, Group, Stack, Tabs, TabsList, TabsTab, Text, TextInput } from '@mantine/core';
+// import { IconSearch } from '@tabler/icons-react';
+// import { usePathname, useRouter, useSearchParams } from 'next/navigation';
+// import React, { useEffect, useState } from 'react';
+// import BackButton from '../../layanan/_com/BackButto';
+
+// type HeaderSearchProps = {
+// placeholder?: string;
+// searchIcon?: React.ReactNode;
+// value?: string;
+// onChange?: (event: React.ChangeEvent) => void;
+// children?: React.ReactNode;
+// };
+
+// function LayoutTabsBerita({
+// children,
+// placeholder = "pencarian",
+// searchIcon =
+// }: HeaderSearchProps) {
+// const router = useRouter();
+// const pathname = usePathname();
+// const searchParams = useSearchParams();
+
+// const activeTab = pathname.split('/').pop() || 'semua';
+// const initialSearch = searchParams.get('search') || '';
+// const [searchValue, setSearchValue] = useState(initialSearch);
+// const [searchTimeout, setSearchTimeout] = useState(null);
+
+// const [activeTabState, setActiveTabState] = useState(activeTab);
+// useEffect(() => {
+// setActiveTabState(activeTab);
+// }, [activeTab]);
+
+// useEffect(() => {
+// return () => {
+// if (searchTimeout !== null) clearTimeout(searchTimeout);
+// };
+// }, [searchTimeout]);
+
+// const handleSearchChange = (event: React.ChangeEvent) => {
+// const value = event.target.value;
+// setSearchValue(value);
+
+// if (searchTimeout !== null) clearTimeout(searchTimeout);
+
+// const newTimeout = window.setTimeout(() => {
+// const params = new URLSearchParams(searchParams.toString());
+
+// if (value) params.set('search', value);
+// else params.delete('search');
+
+// if (params.toString() !== searchParams.toString()) {
+// router.push(`/darmasaba/desa/berita/${activeTab}?${params.toString()}`);
+// }
+// }, 500);
+
+// setSearchTimeout(newTimeout);
+// };
+
+// const tabs = [
+// { label: "Semua", value: "semua", href: "/darmasaba/desa/berita/semua" },
+// { label: "Budaya", value: "budaya", href: "/darmasaba/desa/berita/budaya" },
+// { label: "Pemerintahan", value: "pemerintahan", href: "/darmasaba/desa/berita/pemerintahan" },
+// { label: "Ekonomi", value: "ekonomi", href: "/darmasaba/desa/berita/ekonomi" },
+// { label: "Pembangunan", value: "pembangunan", href: "/darmasaba/desa/berita/pembangunan" },
+// { label: "Sosial", value: "sosial", href: "/darmasaba/desa/berita/sosial" },
+// { label: "Teknologi", value: "teknologi", href: "/darmasaba/desa/berita/teknologi" },
+// ];
+
+// const handleTabChange = (value: string | null) => {
+// if (!value) return;
+// const tab = tabs.find(t => t.value === value);
+// if (tab) {
+// const params = new URLSearchParams(searchParams.toString());
+// router.push(`/darmasaba/desa/berita/${value}${params.toString() ? `?${params.toString()}` : ''}`);
+// }
+// };
+
+// return (
+//
+// {/* Header */}
+//
+//
+//
+
+//
+//
+//
+//
+// Portal Berita Darmasaba
+//
+//
+// Temukan berbagai potensi dan keunggulan yang dimiliki Desa Darmasaba
+//
+//
+//
+//
+//
+//
+//
+
+//
+//
+// {/* SCROLLABLE TABS */}
+//
+//
+// {tabs.map((tab, index) => (
+// router.push(tab.href)}
+// style={{
+// flex: '0 0 auto', // Prevent shrinking
+// minWidth: 100, // optional: makes them touch-friendly
+// textAlign: 'center'
+// }}
+// >
+// {tab.label}
+//
+// ))}
+//
+//
+//
+
+// {children}
+//
+//
+// );
+// }
+
+// export default LayoutTabsBerita;
+
+
'use client'
import colors from '@/con/colors';
import { Box, Group, Stack, Tabs, TabsList, TabsTab, Text, TextInput } from '@mantine/core';
@@ -5,39 +154,32 @@ import { IconSearch } from '@tabler/icons-react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import React, { useEffect, useState } from 'react';
import BackButton from '../../layanan/_com/BackButto';
+import { useProxy } from 'valtio/utils';
+import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
-type HeaderSearchProps = {
- placeholder?: string;
- searchIcon?: React.ReactNode;
- value?: string;
- onChange?: (event: React.ChangeEvent) => void;
- children?: React.ReactNode;
-};
-
-function LayoutTabsBerita({
- children,
- placeholder = "pencarian",
- searchIcon =
-}: HeaderSearchProps) {
+function LayoutTabsBerita({ children }: { children: React.ReactNode }) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
- const activeTab = pathname.split('/').pop() || 'semua';
- const initialSearch = searchParams.get('search') || '';
- const [searchValue, setSearchValue] = useState(initialSearch);
- const [searchTimeout, setSearchTimeout] = useState(null);
+ const kategoriState = useProxy(stateDashboardBerita.kategoriBerita);
+ // tab aktif dari url
+ const activeTab = pathname.split('/').pop() || 'semua';
const [activeTabState, setActiveTabState] = useState(activeTab);
+
+ useEffect(() => {
+ kategoriState.findMany.load(); // ambil kategori dari DB
+ }, []);
+
useEffect(() => {
setActiveTabState(activeTab);
}, [activeTab]);
- useEffect(() => {
- return () => {
- if (searchTimeout !== null) clearTimeout(searchTimeout);
- };
- }, [searchTimeout]);
+ // search
+ const initialSearch = searchParams.get('search') || '';
+ const [searchValue, setSearchValue] = useState(initialSearch);
+ const [searchTimeout, setSearchTimeout] = useState(null);
const handleSearchChange = (event: React.ChangeEvent) => {
const value = event.target.value;
@@ -47,26 +189,23 @@ function LayoutTabsBerita({
const newTimeout = window.setTimeout(() => {
const params = new URLSearchParams(searchParams.toString());
-
if (value) params.set('search', value);
else params.delete('search');
- if (params.toString() !== searchParams.toString()) {
- router.push(`/darmasaba/desa/berita/${activeTab}?${params.toString()}`);
- }
+ router.push(`/darmasaba/desa/berita/${activeTab}${params.toString() ? `?${params.toString()}` : ''}`);
}, 500);
setSearchTimeout(newTimeout);
};
+ // --- tabs dinamis ---
const tabs = [
{ label: "Semua", value: "semua", href: "/darmasaba/desa/berita/semua" },
- { label: "Budaya", value: "budaya", href: "/darmasaba/desa/berita/budaya" },
- { label: "Pemerintahan", value: "pemerintahan", href: "/darmasaba/desa/berita/pemerintahan" },
- { label: "Ekonomi", value: "ekonomi", href: "/darmasaba/desa/berita/ekonomi" },
- { label: "Pembangunan", value: "pembangunan", href: "/darmasaba/desa/berita/pembangunan" },
- { label: "Sosial", value: "sosial", href: "/darmasaba/desa/berita/sosial" },
- { label: "Teknologi", value: "teknologi", href: "/darmasaba/desa/berita/teknologi" },
+ ...(kategoriState.findMany.data || []).map((kat: any) => ({
+ label: kat.name,
+ value: kat.name.toLowerCase(),
+ href: `/darmasaba/desa/berita/${kat.name.toLowerCase()}`
+ }))
];
const handleTabChange = (value: string | null) => {
@@ -74,7 +213,7 @@ function LayoutTabsBerita({
const tab = tabs.find(t => t.value === value);
if (tab) {
const params = new URLSearchParams(searchParams.toString());
- router.push(`/darmasaba/desa/berita/${value}${params.toString() ? `?${params.toString()}` : ''}`);
+ router.push(`${tab.href}${params.toString() ? `?${params.toString()}` : ''}`);
}
};
@@ -88,18 +227,16 @@ function LayoutTabsBerita({
-
+
Portal Berita Darmasaba
-
- Temukan berbagai potensi dan keunggulan yang dimiliki Desa Darmasaba
-
+ Temukan berbagai potensi dan keunggulan yang dimiliki Desa Darmasaba
}
w="100%"
value={searchValue}
onChange={handleSearchChange}
@@ -108,6 +245,7 @@ function LayoutTabsBerita({
+ {/* TABS */}
- {/* SCROLLABLE TABS */}
{tabs.map((tab, index) => (
@@ -124,8 +261,8 @@ function LayoutTabsBerita({
value={tab.value}
onClick={() => router.push(tab.href)}
style={{
- flex: '0 0 auto', // Prevent shrinking
- minWidth: 100, // optional: makes them touch-friendly
+ flex: '0 0 auto',
+ minWidth: 100,
textAlign: 'center'
}}
>
diff --git a/src/app/darmasaba/(pages)/desa/berita/semua/page.tsx b/src/app/darmasaba/(pages)/desa/berita/semua/page.tsx
index c6aaf3f2..f37d9a93 100644
--- a/src/app/darmasaba/(pages)/desa/berita/semua/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/berita/semua/page.tsx
@@ -82,9 +82,7 @@ function Semua() {
{featuredData.kategoriBerita?.name || 'Berita'}
{featuredData.judul}
-
- {featuredData.deskripsi}
-
+
@@ -146,10 +144,10 @@ function Semua() {
{item.judul}
- {item.deskripsi}
+
-
+
{new Date(item.createdAt).toLocaleDateString('id-ID', {
day: 'numeric',
month: 'short',
diff --git a/src/app/darmasaba/(pages)/desa/galery/foto/Content.tsx b/src/app/darmasaba/(pages)/desa/galery/foto/Content.tsx
index 97d8cb08..6daa122e 100644
--- a/src/app/darmasaba/(pages)/desa/galery/foto/Content.tsx
+++ b/src/app/darmasaba/(pages)/desa/galery/foto/Content.tsx
@@ -6,122 +6,83 @@ import { useCallback, useEffect, useState } from 'react';
import ApiFetch from '@/lib/api-fetch';
interface FileItem {
- id: string;
- name: string;
- link: string;
- realName: string;
- createdAt: string | Date;
- category: string;
- path: string;
- mimeType: string;
- }
-
- export default function FotoContent() {
- const [files, setFiles] = useState([]);
- const [loading, setLoading] = useState(true);
- const [search, setSearch] = useState('');
- const [page, setPage] = useState(1);
- const [totalPages, setTotalPages] = useState(1);
-
- // Handle search and pagination changes
- const loadData = useCallback((pageNum: number, searchTerm: string) => {
+ id: string;
+ name: string;
+ link: string;
+ realName: string;
+ createdAt: string | Date;
+ category: string;
+ path: string;
+ mimeType: string;
+}
+
+export default function FotoContent() {
+ const [files, setFiles] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [search, setSearch] = useState('');
+ const [page, setPage] = useState(1);
+ const [totalPages, setTotalPages] = useState(1);
+ const limit = 9; // β
ambil 12 data per page
+
+ const loadData = useCallback(async (pageNum: number, searchTerm: string) => {
setLoading(true);
- // Using the load function from the component's scope
- const loadFn = async () => {
- try {
- const response = await ApiFetch.api.fileStorage.findMany.get({
- query: {
- category: 'image',
- page: pageNum.toString(),
- limit: '10',
- ...(searchTerm && { search: searchTerm })
- }
- });
+ try {
+ const query: Record = {
+ category: 'image',
+ page: pageNum.toString(),
+ limit: limit.toString(),
+ };
+ if (searchTerm) query.search = searchTerm;
- if (response.status === 200 && response.data) {
- setFiles(response.data.data || []);
- setTotalPages(response.data.meta?.totalPages || 1);
- } else {
- setFiles([]);
- }
- } catch (err) {
- console.error('Load error:', err);
+ const response = await ApiFetch.api.fileStorage.findMany.get({ query });
+
+ if (response.status === 200 && response.data) {
+ setFiles(response.data.data || []);
+ setTotalPages(response.data.meta?.totalPages || 1);
+ } else {
setFiles([]);
- } finally {
- setLoading(false);
}
- };
-
- loadFn();
+ } catch (err) {
+ console.error('Load error:', err);
+ setFiles([]);
+ } finally {
+ setLoading(false);
+ }
}, []);
- // Initial load and URL change handler
+ // β
Initial load + update when URL/search changes
useEffect(() => {
const handleRouteChange = () => {
const urlParams = new URLSearchParams(window.location.search);
const urlSearch = urlParams.get('search') || '';
const urlPage = parseInt(urlParams.get('page') || '1');
-
setSearch(urlSearch);
setPage(urlPage);
loadData(urlPage, urlSearch);
};
- // Handle search updates from the search bar
const handleSearchUpdate = (e: Event) => {
const { search } = (e as CustomEvent).detail;
-
setSearch(search);
- setPage(1); // Reset to first page on new search
+ setPage(1);
loadData(1, search);
};
- // Initial load
handleRouteChange();
-
- // Set up event listeners
window.addEventListener('popstate', handleRouteChange);
window.addEventListener('searchUpdate', handleSearchUpdate as EventListener);
-
- // Cleanup
+
return () => {
window.removeEventListener('popstate', handleRouteChange);
window.removeEventListener('searchUpdate', handleSearchUpdate as EventListener);
};
}, [loadData]);
- // β
Fetch data
+ // β
Update when page/search changes
useEffect(() => {
- const fetchFiles = async () => {
- setLoading(true);
- try {
- const query: Record = {
- category: 'image',
- page: page.toString(),
- limit: '10',
- };
- if (search) query.search = search;
+ loadData(page, search);
+ }, [page, search, loadData]);
- const response = await ApiFetch.api.fileStorage.findMany.get({ query });
-
- if (response.status === 200 && response.data) {
- setFiles(response.data.data || []);
- setTotalPages(response.data.meta?.totalPages || 1);
- } else {
- setFiles([]);
- }
- } catch (err) {
- console.error('Fetch error:', err);
- setFiles([]);
- } finally {
- setLoading(false);
- }
- };
-
- if (page > 0) fetchFiles(); // jangan fetch jika page belum valid
- }, [search, page]);
-
- // β
Update URL
const updateURL = (newSearch: string, newPage: number) => {
const url = new URL(window.location.href);
if (newSearch) url.searchParams.set('search', newSearch);
@@ -148,7 +109,14 @@ interface FileItem {
{files.map((file) => (
-
+
-
-
-
- {file.realName || file.name}
-
-
- {new Date(file.createdAt).toLocaleDateString('id-ID', {
- day: 'numeric',
- month: 'long',
- year: 'numeric',
- })}
-
-
-
+
+
+ {file.realName || file.name}
+
+
+ {new Date(file.createdAt).toLocaleDateString('id-ID', {
+ day: 'numeric',
+ month: 'long',
+ year: 'numeric',
+ })}
+
+
))}
@@ -181,4 +147,4 @@ interface FileItem {
);
-}
\ No newline at end of file
+}
diff --git a/src/app/darmasaba/(pages)/desa/galery/video/Content.tsx b/src/app/darmasaba/(pages)/desa/galery/video/Content.tsx
index c36d069d..903a9c90 100644
--- a/src/app/darmasaba/(pages)/desa/galery/video/Content.tsx
+++ b/src/app/darmasaba/(pages)/desa/galery/video/Content.tsx
@@ -128,6 +128,7 @@ export default function VideoContent() {
ta="justify"
fz="sm"
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
diff --git a/src/app/darmasaba/(pages)/desa/layanan/[id]/page.tsx b/src/app/darmasaba/(pages)/desa/layanan/[id]/page.tsx
index df8ac6d0..a2c4e9bc 100644
--- a/src/app/darmasaba/(pages)/desa/layanan/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/layanan/[id]/page.tsx
@@ -107,6 +107,7 @@ function Page() {
fz={{ base: "md", md: "lg" }}
c="dark.7"
ta="justify"
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
{data.image2?.link && (
@@ -145,24 +146,24 @@ function Page() {
Ajukan Permohonan
Nama }
- placeholder="masukkan nama"
+ placeholder="Masukkan nama"
onChange={(val) => (stateCreate.create.form.nama = val.target.value)}
/>
NIK }
- placeholder="masukkan NIK"
+ placeholder="Masukkan NIK"
onChange={(val) => (stateCreate.create.form.nik = val.target.value)}
/>
Alamat }
- placeholder="masukkan alamat"
+ placeholder="Masukkan alamat"
onChange={(val) => (stateCreate.create.form.alamat = val.target.value)}
/>
Nomor KK}
- placeholder="masukkan Nomor KK"
+ placeholder="Masukkan Nomor KK"
onChange={(val) => (stateCreate.create.form.nomorKk = val.target.value)}
/>
-
Simpan
diff --git a/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananPendudukNonPermanent.tsx b/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananPendudukNonPermanent.tsx
index 5cda8f40..c35a976b 100644
--- a/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananPendudukNonPermanent.tsx
+++ b/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananPendudukNonPermanent.tsx
@@ -16,7 +16,7 @@ function PelayananPendudukNonPermanent() {
const loadData = async () => {
try {
setLoading(true);
- await state.pelayananPendudukNonPermanen.findById.load('1');
+ await state.pelayananPendudukNonPermanen.findById.load('edit');
} catch (error) {
console.error('Gagal memuat data:', error);
} finally {
@@ -52,6 +52,7 @@ function PelayananPendudukNonPermanent() {
ta="justify"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data?.deskripsi }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
) : (
Deskripsi belum tersedia.
diff --git a/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananPerizinanBerusaha.tsx b/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananPerizinanBerusaha.tsx
index 3507063c..08f63752 100644
--- a/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananPerizinanBerusaha.tsx
+++ b/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananPerizinanBerusaha.tsx
@@ -7,34 +7,52 @@ import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
function PelayananPerizinanBerusaha() {
- const state = useProxy(stateLayananDesa)
- const [loading, setLoading] = useState(false)
- const [active, setActive] = useState(1);
- const nextStep = () => setActive((current) => (current < 6 ? current + 1 : current));
- const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
+ const state = useProxy(stateLayananDesa);
+ const [loading, setLoading] = useState(false);
+ const [active, setActive] = useState(0);
+
+ const totalSteps = 6;
+
+ const nextStep = () => {
+ if (active < totalSteps - 1) {
+ setActive(active + 1);
+ } else if (active === totalSteps - 1) {
+ setActive(totalSteps); // Mark as completed
+ }
+ };
+
+ const prevStep = () => {
+ if (active > 0) {
+ setActive(active - 1);
+ }
+ };
useEffect(() => {
const loadData = async () => {
try {
setLoading(true);
- await state.pelayananPerizinanBerusaha.findById.load('1')
+ await state.pelayananPerizinanBerusaha.findById.load('edit');
} catch (error) {
console.error('Gagal memuat data:', error);
} finally {
setLoading(false);
}
- }
- loadData()
- }, [])
+ };
+ loadData();
+ }, []);
const data = state.pelayananPerizinanBerusaha.findById.data;
-
+
if (!data && !loading) {
return (
- Belum ada informasi layanan yang tersedia
- Kunjungi OSS
+
+ Belum ada informasi layanan yang tersedia
+
+
+ Kunjungi OSS
+
);
@@ -47,72 +65,111 @@ function PelayananPerizinanBerusaha() {
) : (
-
-
-
- Perizinan Berusaha Berbasis Risiko melalui OSS
-
-
- Sistem Online Single Submission (OSS) untuk pendaftaran NIB
-
-
+
+
+
+ Perizinan Berusaha Berbasis Risiko melalui OSS
+
+
+ Sistem Online Single Submission (OSS) untuk pendaftaran NIB
+
+
-
+
-
- Alur pendaftaran NIB:
-
-
- Membuat akun di portal OSS
-
-
- Lengkapi informasi perusahaan, data pemegang saham, dan alamat
-
-
- Menentukan kode KBLI sesuai jenis usaha
-
-
- Unggah akta pendirian, surat izin, dan dokumen wajib lainnya
-
-
- Menunggu verifikasi dan persetujuan dari pihak berwenang
-
-
- Menerima NIB sebagai identitas resmi usaha
-
-
-
-
-
- Proses pendaftaran selesai
-
-
-
-
+
+
+ Alur pendaftaran NIB:
+
+ {
+ if (step <= active) { // Only allow clicking on previous or current steps
+ setActive(step);
+ }
+ }}
+ orientation="vertical"
+ color="blue"
+ radius="md"
+ styles={{
+ step: { padding: '14px 0' },
+ stepBody: { marginLeft: 8 }
+ }}
+ >
+
+ Membuat akun di portal OSS
+
+
+ Lengkapi informasi perusahaan, data pemegang saham, dan alamat
+
+
+ Menentukan kode KBLI sesuai jenis usaha
+
+
+ Unggah akta pendirian, surat izin, dan dokumen wajib lainnya
+
+
+ Menunggu verifikasi dan persetujuan dari pihak berwenang
+
+
+ Menerima NIB sebagai identitas resmi usaha
+
+
+
+
+
+ Proses pendaftaran selesai
+
+
+
+
+ {active < totalSteps && (
- } onClick={prevStep} disabled={active === 0}>
+ }
+ onClick={prevStep}
+ disabled={active === 0}
+ >
Kembali
- } onClick={nextStep}>
- Lanjut
-
-
-
-
- Catatan: Persyaratan dan prosedur dapat berubah sewaktu-waktu. Untuk informasi resmi terbaru, silakan kunjungi situs{" "}
- oss.go.id atau hubungi instansi pemerintah terkait.
-
-
+ {active < totalSteps ? (
+ : null}
+ onClick={nextStep}
+ >
+ {active === totalSteps - 1 ? 'Selesai' : 'Lanjut'}
+
+ ) : (
+ setActive(0)}
+ >
+ Mulai Lagi
+
+ )}
+
+ )}
+
+
+
+ Catatan: Persyaratan dan prosedur dapat berubah sewaktu-waktu. Untuk informasi resmi terbaru, silakan kunjungi situs{' '}
+
+ oss.go.id
+ {' '}
+ atau hubungi instansi pemerintah terkait.
+
+
)}
);
}
-export default PelayananPerizinanBerusaha;
+export default PelayananPerizinanBerusaha;
\ No newline at end of file
diff --git a/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananSuratKeterangan.tsx b/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananSuratKeterangan.tsx
index 819ccdfd..e079820c 100644
--- a/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananSuratKeterangan.tsx
+++ b/src/app/darmasaba/(pages)/desa/layanan/_com/pelayananSuratKeterangan.tsx
@@ -1,52 +1,59 @@
-/* eslint-disable react-hooks/exhaustive-deps */
"use client";
import stateLayananDesa from '@/app/admin/(dashboard)/_state/desa/layananDesa';
import colors from '@/con/colors';
-import { BackgroundImage, Box, Button, Center, Group, SimpleGrid, Skeleton, Stack, Text, Tooltip } from '@mantine/core';
+import { BackgroundImage, Box, Button, Center, Group, Pagination, SimpleGrid, Skeleton, Stack, Text, Tooltip } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
import { IconFileDescription, IconInfoCircle } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
-import React, { useEffect, useMemo, useState } from 'react';
import { useProxy } from 'valtio/utils';
function PelayananSuratKeterangan({ search }: { search: string }) {
- const [loading, setLoading] = useState(false);
const router = useRouter();
const state = useProxy(stateLayananDesa);
- const filteredData = useMemo(() => {
- if (!state.suratKeterangan.findMany.data) return [];
- return state.suratKeterangan.findMany.data.filter((item) => {
- const keyword = search.toLowerCase();
- return item.name?.toLowerCase().includes(keyword);
- });
- }, [state.suratKeterangan.findMany.data, search]);
+ const {
+ data,
+ page,
+ totalPages,
+ loading,
+ load
+ } = state.suratKeterangan.findMany;
- useEffect(() => {
- const loadData = async () => {
- try {
- setLoading(true);
- await state.suratKeterangan.findMany.load();
- } catch (error) {
- console.error('Gagal memuat data:', error);
- } finally {
- setLoading(false);
- }
- };
- loadData();
- }, []);
+ useShallowEffect(() => {
+ load(page, 9, search);
+ }, [page, search]);
+
+ if (loading || !data) {
+ return Array.from({ length: 3 }).map((_, i) => (
+
+ ));
+ }
+
+ if (data?.length === 0) {
+ return (
+
+
+
+
+ Tidak ada layanan surat keterangan yang ditemukan
+
+
+
+ );
+ }
return (
-
+
Layanan Surat Keterangan
-
+
@@ -55,69 +62,68 @@ function PelayananSuratKeterangan({ search }: { search: string }) {
cols={{ base: 1, sm: 2, md: 3 }}
spacing="lg"
>
- {loading ? (
- Array.from({ length: 3 }).map((_, i) => (
-
- ))
- ) : filteredData.length === 0 ? (
-
-
-
-
- Tidak ada layanan surat keterangan yang ditemukan
+ {data?.map((v, k) => (
+
+
+
+
+ {v.name}
-
-
- ) : (
- filteredData.map((v, k) => (
-
-
-
-
+ router.push(`/darmasaba/desa/layanan/${v.id}`)}
+ style={{
+ boxShadow: "0 0 12px rgba(59,130,246,0.6)",
+ transition: "all 0.2s ease",
+ }}
>
- {v.name}
-
-
- router.push(`/darmasaba/desa/layanan/${v.id}`)}
- style={{
- boxShadow: "0 0 12px rgba(59,130,246,0.6)",
- transition: "all 0.2s ease",
- }}
- >
- Lihat Detail
-
-
-
-
- ))
- )}
+ Lihat Detail
+
+
+
+
+ ))}
+
+ {
+ load(newPage, 10);
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ }}
+ total={totalPages}
+ mt="md"
+ mb="md"
+ color="blue"
+ radius="md"
+ />
+
);
}
diff --git a/src/app/darmasaba/(pages)/desa/pengumuman/[name]/[id]/page.tsx b/src/app/darmasaba/(pages)/desa/pengumuman/[name]/[id]/page.tsx
index 037b6327..25bf241a 100644
--- a/src/app/darmasaba/(pages)/desa/pengumuman/[name]/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/pengumuman/[name]/[id]/page.tsx
@@ -42,7 +42,7 @@ function Page() {
-
+
{new Date(detail.data?.createdAt).toLocaleDateString('id-ID', {
weekday: 'long',
diff --git a/src/app/darmasaba/(pages)/desa/pengumuman/page.tsx b/src/app/darmasaba/(pages)/desa/pengumuman/page.tsx
index 218e82bf..f964f519 100644
--- a/src/app/darmasaba/(pages)/desa/pengumuman/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/pengumuman/page.tsx
@@ -243,6 +243,7 @@ function Page() {
lineClamp={4}
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
mb="md"
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
diff --git a/src/app/darmasaba/(pages)/desa/potensi/[id]/page.tsx b/src/app/darmasaba/(pages)/desa/potensi/[id]/page.tsx
index 4b2ef6a2..f5a7cadc 100644
--- a/src/app/darmasaba/(pages)/desa/potensi/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/potensi/[id]/page.tsx
@@ -77,9 +77,7 @@ function Page() {
fallbackSrc="https://placehold.co/800x400?text=Gambar+tidak+tersedia"
loading="lazy"
/>
-
- {state.findUnique.data?.deskripsi || 'Belum ada deskripsi untuk potensi desa ini.'}
-
+
diff --git a/src/app/darmasaba/(pages)/desa/potensi/page.tsx b/src/app/darmasaba/(pages)/desa/potensi/page.tsx
index aa1efa10..5f2e1236 100644
--- a/src/app/darmasaba/(pages)/desa/potensi/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/potensi/page.tsx
@@ -43,7 +43,7 @@ function Page() {
Potensi Desa Darmasaba
-
+
Temukan berbagai potensi unggulan, peluang, dan daya tarik yang menjadikan Desa Darmasaba istimewa.
diff --git a/src/app/darmasaba/(pages)/desa/profile/page.tsx b/src/app/darmasaba/(pages)/desa/profile/page.tsx
index de4f921f..e2f08dd5 100644
--- a/src/app/darmasaba/(pages)/desa/profile/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/profile/page.tsx
@@ -10,31 +10,36 @@ import ProfilPerbekel from './ui/profilPerbekel';
// import LembagaDesa from './ui/lembagaDesa';
import MotoDesa from './ui/motoDesa';
import SemuaPerbekel from './ui/semuaPerbekel';
+import ScrollToTopButton from '@/app/darmasaba/_com/scrollToTopButton';
function Page() {
return (
-
-
-
-
-
-
-
- Profile Desa
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ Profile Desa
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Tombol Scroll ke Atas */}
+
+
);
}
diff --git a/src/app/darmasaba/(pages)/desa/profile/ui/lambangDesa.tsx b/src/app/darmasaba/(pages)/desa/profile/ui/lambangDesa.tsx
index 0622bcc9..baefc403 100644
--- a/src/app/darmasaba/(pages)/desa/profile/ui/lambangDesa.tsx
+++ b/src/app/darmasaba/(pages)/desa/profile/ui/lambangDesa.tsx
@@ -2,7 +2,7 @@
'use client'
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile'
import colors from '@/con/colors'
-import { Box, Center, Image, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core'
+import { Box, Center, Image, Paper, Skeleton, Stack, Text } from '@mantine/core'
import { useEffect } from 'react'
import { useProxy } from 'valtio/utils'
@@ -58,16 +58,14 @@ function LambangDesa() {
borderColor: '#e0e9ff',
}}
>
-
-
diff --git a/src/app/darmasaba/(pages)/desa/profile/ui/maskotDesa.tsx b/src/app/darmasaba/(pages)/desa/profile/ui/maskotDesa.tsx
index da477ecf..1d58f079 100644
--- a/src/app/darmasaba/(pages)/desa/profile/ui/maskotDesa.tsx
+++ b/src/app/darmasaba/(pages)/desa/profile/ui/maskotDesa.tsx
@@ -1,11 +1,11 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
-import { Box, Card, Center, Group, Image, Loader, Paper, Stack, Text, Tooltip } from '@mantine/core';
+import colors from '@/con/colors';
+import { Box, Card, Center, Group, Image, Loader, Paper, Stack, Text } from '@mantine/core';
+import { IconPhoto } from '@tabler/icons-react';
import { useEffect } from 'react';
import { useProxy } from 'valtio/utils';
-import { IconPhoto } from '@tabler/icons-react';
-import colors from '@/con/colors';
function MaskotDesa() {
const state = useProxy(stateProfileDesa.maskotDesa);
@@ -48,13 +48,14 @@ function MaskotDesa() {
ta="justify"
c="dark"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
{data.images.length > 0 ? (
data.images.map((img, index) => (
-
-
))
) : (
diff --git a/src/app/darmasaba/(pages)/desa/profile/ui/profilPerbekel.tsx b/src/app/darmasaba/(pages)/desa/profile/ui/profilPerbekel.tsx
index e59f4d57..887ffdaf 100644
--- a/src/app/darmasaba/(pages)/desa/profile/ui/profilPerbekel.tsx
+++ b/src/app/darmasaba/(pages)/desa/profile/ui/profilPerbekel.tsx
@@ -2,10 +2,10 @@
'use client'
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
import colors from '@/con/colors';
-import { Box, Image, Paper, SimpleGrid, Skeleton, Stack, Text, Divider, Tooltip } from '@mantine/core';
+import { Box, Divider, Image, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
+import { IconBriefcase, IconTargetArrow, IconUser, IconUsers } from '@tabler/icons-react';
import { useEffect } from 'react';
import { useProxy } from 'valtio/utils';
-import { IconUser, IconBriefcase, IconUsers, IconTargetArrow } from '@tabler/icons-react';
function ProfilPerbekel() {
const state = useProxy(stateProfileDesa.profilPerbekel)
@@ -27,10 +27,10 @@ function ProfilPerbekel() {
return (
-
@@ -41,11 +41,11 @@ function ProfilPerbekel() {
-
@@ -70,9 +70,9 @@ function ProfilPerbekel() {
Perbekel Desa Darmasaba
-
@@ -83,58 +83,56 @@ function ProfilPerbekel() {
-
-
-
-
-
- Biodata
-
-
+
+
+
+ Biodata
-
+
+
-
-
-
-
- Pengalaman
-
-
+
+
+
+ Pengalaman
-
+
+
-
@@ -143,25 +141,27 @@ function ProfilPerbekel() {
Pengalaman Organisasi
-
-
+
Program Kerja Unggulan
-
diff --git a/src/app/darmasaba/(pages)/desa/profile/ui/sejarahDesa.tsx b/src/app/darmasaba/(pages)/desa/profile/ui/sejarahDesa.tsx
index 0fab6754..185f4606 100644
--- a/src/app/darmasaba/(pages)/desa/profile/ui/sejarahDesa.tsx
+++ b/src/app/darmasaba/(pages)/desa/profile/ui/sejarahDesa.tsx
@@ -64,7 +64,7 @@ function SejarahDesa() {
fz={{ base: 'md', md: 'lg' }}
lh={1.8}
ta="justify"
- style={{ color: '#2a2a2a' }}
+ style={{ color: '#2a2a2a', wordBreak: "break-word", whiteSpace: "normal" }}
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
/>
diff --git a/src/app/darmasaba/(pages)/desa/profile/ui/semuaPerbekel.tsx b/src/app/darmasaba/(pages)/desa/profile/ui/semuaPerbekel.tsx
index 3ba7b404..46f6594b 100644
--- a/src/app/darmasaba/(pages)/desa/profile/ui/semuaPerbekel.tsx
+++ b/src/app/darmasaba/(pages)/desa/profile/ui/semuaPerbekel.tsx
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
-import { Box, Center, Image, Paper, SimpleGrid, Skeleton, Stack, Text, Title, Tooltip } from '@mantine/core';
+import { Box, Center, Image, Paper, SimpleGrid, Skeleton, Stack, Text, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconUser } from '@tabler/icons-react';
import { useProxy } from 'valtio/utils';
@@ -77,23 +77,17 @@ function SemuaPerbekel() {
-
{v.nama}
-
-
-
+
{v.daerah}
-
-
-
+
{v.periode}
-
diff --git a/src/app/darmasaba/(pages)/desa/profile/ui/visimisiDesa.tsx b/src/app/darmasaba/(pages)/desa/profile/ui/visimisiDesa.tsx
index cf8111f5..b8a15c93 100644
--- a/src/app/darmasaba/(pages)/desa/profile/ui/visimisiDesa.tsx
+++ b/src/app/darmasaba/(pages)/desa/profile/ui/visimisiDesa.tsx
@@ -59,6 +59,7 @@ function VisiMisiDesa() {
fw={500}
lh={1.6}
dangerouslySetInnerHTML={{ __html: data.visi }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
@@ -86,6 +87,7 @@ function VisiMisiDesa() {
fw={500}
lh={1.6}
dangerouslySetInnerHTML={{ __html: data.misi }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
diff --git a/src/app/darmasaba/(pages)/ekonomi/PADesa-pendapatan-asli-desa/page.tsx b/src/app/darmasaba/(pages)/ekonomi/PADesa-pendapatan-asli-desa/page.tsx
index 75553230..de14a1e1 100644
--- a/src/app/darmasaba/(pages)/ekonomi/PADesa-pendapatan-asli-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/PADesa-pendapatan-asli-desa/page.tsx
@@ -1,11 +1,10 @@
'use client'
import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
import colors from '@/con/colors';
-import { Box, Grid, GridCol, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
+import { Box, Grid, GridCol, Paper, SimpleGrid, Stack, Table, Text, Title } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
-import { useShallowEffect } from '@mantine/hooks';
-
function Page() {
const state = useProxy(PendapatanAsliDesa.ApbDesa);
@@ -28,6 +27,9 @@ function Page() {
const totalBelanja = latestApb?.belanja?.reduce((sum, item) => sum + (item?.value || 0), 0) || 0;
const totalPembiayaan = latestApb?.pembiayaan?.reduce((sum, item) => sum + (item?.value || 0), 0) || 0;
+ // Hasil akhir
+ const sisaAnggaran = totalPendapatan - totalBelanja - totalPembiayaan;
+
return (
@@ -44,28 +46,37 @@ function Page() {
Pendapatan
- {PendapatanAsliDesa.pendapatan.findMany.data?.map((item) => (
+ {latestApb?.pendapatan?.map((item) => (
-
+
{item.name}
-
- {new Intl.NumberFormat('id-ID', {
- style: 'currency',
- currency: 'IDR',
- minimumFractionDigits: 0
- }).format(item.value)}
+
+
+ {new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(item.value)}
+
))}
-
+
Total Pendapatan
-
-
+
+
{new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
@@ -81,18 +92,28 @@ function Page() {
Belanja
- {PendapatanAsliDesa.belanja.findMany.data?.map((item) => (
+ {latestApb?.belanja?.map((item) => (
-
+
{item.name}
-
- {new Intl.NumberFormat('id-ID', {
- style: 'currency',
- currency: 'IDR',
- minimumFractionDigits: 0
- }).format(item.value)}
+
+
+ {new Intl.NumberFormat('id-ID', {
+ style: 'currency',
+ currency: 'IDR',
+ minimumFractionDigits: 0
+ }).format(item.value)}
+
@@ -118,18 +139,28 @@ function Page() {
Pembiayaan
- {PendapatanAsliDesa.pembiayaan.findMany.data?.map((item) => (
+ {latestApb?.pembiayaan?.map((item) => (
-
+
{item.name}
-
- {new Intl.NumberFormat('id-ID', {
- style: 'currency',
- currency: 'IDR',
- minimumFractionDigits: 0
- }).format(item.value)}
+
+
+ {new Intl.NumberFormat('id-ID', {
+ style: 'currency',
+ currency: 'IDR',
+ minimumFractionDigits: 0
+ }).format(item.value)}
+
@@ -150,13 +181,54 @@ function Page() {
-
+
+
+ {/* π½ Tambahan Ringkasan Anggaran */}
+
+ Ringkasan Anggaran
+
+
+
+ Keterangan
+ Jumlah
+
+
+
+
+ Total Pendapatan
+
+ {new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(totalPendapatan)}
+
+
+
+ Total Belanja
+
+ {new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(totalBelanja)}
+
+
+
+ Total Pembiayaan
+
+ {new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(totalPembiayaan)}
+
+
+
+ Sisa Anggaran
+ = 0 ? "blue" : "red"}>
+
+ {new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(sisaAnggaran)}
+
+
+
+
+
+
);
}
-export default Page;
+export default Page;
\ No newline at end of file
diff --git a/src/app/darmasaba/(pages)/ekonomi/demografi-pekerjaan/page.tsx b/src/app/darmasaba/(pages)/ekonomi/demografi-pekerjaan/page.tsx
index 18d9dcca..61db456e 100644
--- a/src/app/darmasaba/(pages)/ekonomi/demografi-pekerjaan/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/demografi-pekerjaan/page.tsx
@@ -48,7 +48,7 @@ function Page() {
p={10}
mb={50}
h={400}
- w={150}
+ w={Math.max(data.length * 120, 800)} // auto lebar sesuai jumlah data
data={data.map((item) => ({
id: item.id,
Pekerjaan: item.pekerjaan,
diff --git a/src/app/darmasaba/(pages)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/page.tsx b/src/app/darmasaba/(pages)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/page.tsx
index ce69d06f..c3a8636e 100644
--- a/src/app/darmasaba/(pages)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/page.tsx
@@ -72,52 +72,55 @@ function Page() {
)
}
return (
-
-
+
+
-
+
Jumlah Penduduk Usia Kerja Yang Menganggur
-
+
Pengangguran Berdasarkan Usia
{mounted && donutGrafikNganggurData.length > 0 ? (
-
+
+
+
) : }
-
+
-
+
18-25
-
+
26-35
-
+
36-45
-
+
46+
@@ -127,44 +130,47 @@ function Page() {
Pengangguran Berdasarkan Pendidikan
{mounted2 && donutGrafikNganggurDataPendidikan.length > 0 ? (
-
+
+
+
) : }
-
+
-
+
SD
-
+
SMP
-
+
SMA/SMK
-
+
D3
-
+
S1
diff --git a/src/app/darmasaba/(pages)/ekonomi/jumlah-pengangguran/page.tsx b/src/app/darmasaba/(pages)/ekonomi/jumlah-pengangguran/page.tsx
index a57ffda6..ba8b5920 100644
--- a/src/app/darmasaba/(pages)/ekonomi/jumlah-pengangguran/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/jumlah-pengangguran/page.tsx
@@ -199,7 +199,7 @@ function Page() {
{item.totalUnemployment}
{item.educatedUnemployment}
{item.uneducatedUnemployment}
- {item.percentageChange}
+ {item.percentageChange}%
))}
diff --git a/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/[id]/page.tsx b/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/[id]/page.tsx
new file mode 100644
index 00000000..53d29443
--- /dev/null
+++ b/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/[id]/page.tsx
@@ -0,0 +1,136 @@
+'use client'
+
+import lowonganKerjaState from '@/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja';
+import colors from '@/con/colors';
+import { Box, Button, Center, Group, Paper, Skeleton, Stack, Text } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconArrowBack, IconBrandWhatsapp, IconBriefcase, IconCurrencyDollar, IconMapPin, IconPhone } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useState } from 'react';
+import { useProxy } from 'valtio/utils';
+
+function DetailLowonganKerjaUser() {
+ const state = useProxy(lowonganKerjaState);
+ const router = useRouter();
+ const params = useParams();
+ const [loading, setLoading] = useState(true);
+
+ useShallowEffect(() => {
+ const loadData = async () => {
+ await state.findUnique.load(params?.id as string);
+ setLoading(false);
+ };
+ loadData();
+ }, []);
+
+ const data = state.findUnique.data;
+
+ if (loading || !data) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+ }
+ mb="md"
+ onClick={() => router.back()}
+ >
+ Kembali
+
+
+
+
+ {/* Judul */}
+
+ {data.posisi}
+
+
+ Diposting: {new Date(data.createdAt).toLocaleDateString('id-ID', {
+ day: '2-digit',
+ month: 'long',
+ year: 'numeric'
+ })}
+
+
+ {/* Info Ringkas */}
+
+
+
+ {data.namaPerusahaan}
+
+
+
+ {data.lokasi}
+
+
+
+ {data.notelp}
+
+
+
+ {data.gaji || '-'}
+
+
+
+ {data.tipePekerjaan}
+
+
+
+
+
+ Deskripsi Pekerjaan
+
+
+
+
+
+
+ Kualifikasi
+
+
+
+
+
+ window.open(`https://wa.me/${data.notelp}`, '_blank')}
+ leftSection={ }
+ >
+ Hubungi Sekarang
+
+
+
+
+
+
+ );
+}
+
+export default DetailLowonganKerjaUser;
diff --git a/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx b/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx
index ad4d3b04..00ee1683 100644
--- a/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/lowongan-kerja-lokal/page.tsx
@@ -12,13 +12,13 @@ import BackButton from '../../desa/layanan/_com/BackButto';
const formatCurrency = (value: string | number) => {
// Convert to string if it's a number
const numStr = typeof value === 'number' ? value.toString() : value;
-
+
// Remove all non-digit characters
const digitsOnly = numStr.replace(/\D/g, '');
-
+
// Format with thousand separators
const formatted = digitsOnly.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
-
+
return `Rp.${formatted}`;
};
@@ -103,7 +103,7 @@ function Page() {
- router.push('https://www.whatsapp.com/?lang=id')} bg={colors['blue-button']}>Lamar Sekarang
+ router.push(`/darmasaba/ekonomi/lowongan-kerja-lokal/${v.id}`)}>Detail
)
diff --git a/src/app/darmasaba/(pages)/ekonomi/pasar-desa/[id]/page.tsx b/src/app/darmasaba/(pages)/ekonomi/pasar-desa/[id]/page.tsx
new file mode 100644
index 00000000..c013d69f
--- /dev/null
+++ b/src/app/darmasaba/(pages)/ekonomi/pasar-desa/[id]/page.tsx
@@ -0,0 +1,157 @@
+'use client'
+import colors from '@/con/colors';
+import { Box, Button, Paper, Stack, Text, Image, Skeleton, Group, Badge, Divider } from '@mantine/core';
+import { IconArrowBack, IconMapPin, IconPhone, IconStar } from '@tabler/icons-react';
+import { useRouter, useParams } from 'next/navigation';
+import React from 'react';
+import { useProxy } from 'valtio/utils';
+import { useShallowEffect } from '@mantine/hooks';
+import pasarDesaState from '@/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa';
+
+function DetailProdukPasarUser() {
+ const router = useRouter();
+ const params = useParams();
+ const statePasar = useProxy(pasarDesaState);
+
+ useShallowEffect(() => {
+ statePasar.pasarDesa.findUnique.load(params?.id as string);
+ }, []);
+
+ const data = statePasar.pasarDesa.findUnique.data;
+
+ if (!data) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ {/* Tombol kembali */}
+
+ router.back()}
+ leftSection={ }
+ mb={15}
+ >
+ Kembali ke daftar produk
+
+
+
+
+
+ {/* Gambar Produk */}
+ {data.image?.link ? (
+
+ ) : (
+
+ Tidak ada gambar
+
+ )}
+
+ {/* Detail Produk */}
+
+
+ {data.nama || 'Produk Tanpa Nama'}
+
+
+
+ Rp {data.harga?.toLocaleString('id-ID')}
+
+ {data.rating && (
+
+
+ {data.rating}
+
+ )}
+
+
+
+
+
+ {/* Info Tambahan */}
+
+
+ Kategori
+
+ {data.KategoriToPasar && data.KategoriToPasar.length > 0 ? (
+ data.KategoriToPasar.map((kategori) => (
+
+ {kategori.kategori.nama}
+
+ ))
+ ) : (
+ Tidak ada kategori
+ )}
+
+
+
+ {data.alamatUsaha && (
+
+
+ {data.alamatUsaha}
+
+ )}
+
+ {data.kontak && (
+
+
+ {data.kontak}
+
+ )}
+
+
+
+
+ {/* Deskripsi */}
+
+ Deskripsi Produk
+
+ Tidak ada deskripsi.
+
+
+
+ {/* Tombol Aksi User */}
+ {data.kontak && (
+
+ Hubungi Penjual via WhatsApp
+
+ )}
+
+
+
+ );
+}
+
+export default DetailProdukPasarUser;
diff --git a/src/app/darmasaba/(pages)/ekonomi/pasar-desa/page.tsx b/src/app/darmasaba/(pages)/ekonomi/pasar-desa/page.tsx
index ef19511f..1aef38f4 100644
--- a/src/app/darmasaba/(pages)/ekonomi/pasar-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/pasar-desa/page.tsx
@@ -3,7 +3,7 @@ import pasarDesaState from '@/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pa
import colors from '@/con/colors';
import { Box, Center, Flex, Grid, GridCol, Image, Pagination, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
-import { IconMapPinFilled, IconSearch, IconShoppingCartFilled, IconStarFilled } from '@tabler/icons-react';
+import { IconBrandWhatsapp, IconMapPinFilled, IconSearch, IconStarFilled } from '@tabler/icons-react';
import { motion } from 'motion/react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -25,14 +25,14 @@ function Page() {
} = state.findMany
useShallowEffect(() => {
- pasarDesaState.kategoriProduk.findMany.load()
+ pasarDesaState.kategoriProduk.findManyAll.load()
}, [])
// Filter data based on selected category
const filteredData = selectedCategory
- ? data?.filter(item =>
- item.KategoriToPasar?.some(kategori => kategori.kategoriId === selectedCategory)
- )
+ ? data?.filter(item =>
+ item.KategoriToPasar?.some(kategori => kategori.kategoriId === selectedCategory)
+ )
: data;
useShallowEffect(() => {
@@ -71,8 +71,11 @@ function Page() {
/>
-
- Pasar Desa Online merupakan Media Promosi yang bertujuan untuk membantu warga desa dalam memasarkan dan memperkenalkan produknya kepada masyarakat.
+
+ Pasar Desa Online adalah media promosi untuk membantu warga memasarkan
+
+
+ dan memperkenalkan produk mereka.
@@ -87,7 +90,7 @@ function Page() {
({
+ data={pasarDesaState.kategoriProduk.findManyAll.data?.map((v) => ({
value: v.id,
label: v.nama
})) || []}
@@ -105,7 +108,7 @@ function Page() {
return (
router.push('https://www.whatsapp.com/?lang=id')}
+ onClick={() => router.push(`/darmasaba/ekonomi/pasar-desa/${v.id}`)}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.8 }}
>
@@ -117,7 +120,7 @@ function Page() {
h={200}
w='100%'
style={{ objectFit: 'cover' }}
- loading="lazy"
+ loading="lazy"
/>
{v.nama}
Rp {v.harga.toLocaleString('id-ID')}
@@ -132,7 +135,7 @@ function Page() {
{v.alamatUsaha}
-
+
diff --git a/src/app/darmasaba/(pages)/ekonomi/program-kemiskinan/page.tsx b/src/app/darmasaba/(pages)/ekonomi/program-kemiskinan/page.tsx
index 5522d54e..0cadea32 100644
--- a/src/app/darmasaba/(pages)/ekonomi/program-kemiskinan/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/program-kemiskinan/page.tsx
@@ -107,7 +107,7 @@ function Page() {
return (
{v.nama}
-
+
)
})}
diff --git a/src/app/darmasaba/(pages)/ekonomi/sektor-unggulan-desa/page.tsx b/src/app/darmasaba/(pages)/ekonomi/sektor-unggulan-desa/page.tsx
index b62283cb..63bdd10a 100644
--- a/src/app/darmasaba/(pages)/ekonomi/sektor-unggulan-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/sektor-unggulan-desa/page.tsx
@@ -28,6 +28,32 @@ function Page() {
)
}
+ // Add this check before the return statement
+ if (data.length === 0) {
+ return (
+
+
+
+
+ Sektor Unggulan Desa Darmasaba
+
+
+ Data sektor unggulan belum tersedia
+
+
+
+ );
+ }
+
+ const chartData = data
+ .filter(item => item?.name && typeof item.value === 'number')
+ .map((item) => ({
+ id: item.id,
+ sektor: item.name,
+ Ton: item.value,
+ }));
+
+
return (
@@ -45,27 +71,34 @@ function Page() {
return (
{v.name}
-
+
)
})}
-
- Statistik Sektor Unggulan Darmasaba
- ({
- id: item.id,
- sektor: item.name,
- Ton: item.value,
- }))}
- dataKey="sektor"
- series={[
- { name: 'Ton', color: colors['blue-button'] },
- ]}
- tickLine="y"
- />
-
+
+
+ Statistik Sektor Unggulan Darmasaba
+
+
+
+
+
diff --git a/src/app/darmasaba/(pages)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/page.tsx b/src/app/darmasaba/(pages)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/page.tsx
index 507f0957..5faa6467 100644
--- a/src/app/darmasaba/(pages)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/page.tsx
@@ -1,24 +1,300 @@
-import colors from '@/con/colors';
-import { Stack, Box, Text, Image } from '@mantine/core';
-import React from 'react';
-import BackButton from '../../desa/layanan/_com/BackButto';
+/* eslint-disable @typescript-eslint/no-unused-vars */
+/* eslint-disable react-hooks/exhaustive-deps */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+'use client'
+import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'
+import colors from '@/con/colors'
+import {
+ Box,
+ Button,
+ Card,
+ Center,
+ Container,
+ Group,
+ Image,
+ Loader,
+ Paper,
+ Stack,
+ Text,
+ Title,
+ Tooltip,
+ Transition,
+} from '@mantine/core'
+import { IconRefresh, IconSearch, IconUsers } from '@tabler/icons-react'
+import { OrganizationChart } from 'primereact/organizationchart'
+import { useEffect } from 'react'
+import { useProxy } from 'valtio/utils'
+import BackButton from '../../desa/layanan/_com/BackButto'
-function Page() {
+export default function Page() {
return (
-
-
-
-
-
- Struktur Organisasi dan SK Pengurus BUMDesa
-
-
-
-
+
+
+
+
+
+
+
+ Struktur Organisasi Dan SK Pengurus BumDes
+
+
+ Gambaran visual peran dan pegawai yang ditugaskan. Arahkan kursor
+ untuk melihat detail atau klik node untuk fokus tampilan.
+
-
-
- );
+
+
+
+
+
+ )
}
-export default Page;
+function StrukturOrganisasiBumDes() {
+ const stateOrganisasi: any = useProxy(stateStrukturBumDes.pegawai)
+
+ useEffect(() => {
+ void stateOrganisasi.findMany.load()
+ }, [])
+
+ const isLoading =
+ !stateOrganisasi.findMany.data &&
+ stateOrganisasi.findMany.loading !== false
+
+ if (isLoading) {
+ return (
+
+
+
+ Memuat struktur organisasiβ¦
+
+ Mengambil data pegawai dan posisi. Mohon tunggu sebentar.
+
+
+
+ )
+ }
+
+ if (
+ !stateOrganisasi.findMany.data ||
+ stateOrganisasi.findMany.data.length === 0
+ ) {
+ return (
+
+
+
+
+
+
+
+ Data pegawai belum tersedia
+
+
+ Belum ada data pegawai yang tercatat untuk BumDes. Silakan coba
+ muat ulang atau periksa sumber data.
+
+
+ }
+ variant="gradient"
+ gradient={{ from: 'indigo', to: 'cyan' }}
+ onClick={() => stateOrganisasi.findMany.load()}
+ >
+ Muat Ulang
+
+ }
+ variant="subtle"
+ onClick={() =>
+ stateOrganisasi.findMany.load({ query: { q: '' } })
+ }
+ >
+ Cari Pegawai
+
+
+
+
+
+ )
+ }
+
+ const posisiMap = new Map()
+
+ const aktifPegawai = stateOrganisasi.findMany.data.filter((p: any) => p.isActive);
+
+ for (const pegawai of aktifPegawai) {
+ const posisiId = pegawai.posisi.id;
+ if (!posisiMap.has(posisiId)) {
+ posisiMap.set(posisiId, {
+ ...pegawai.posisi,
+ pegawaiList: [],
+ children: [],
+ });
+ }
+ posisiMap.get(posisiId)!.pegawaiList.push(pegawai);
+ }
+
+ // First, create a map of all unique positions
+const allPositions = new Map();
+aktifPegawai.forEach((pegawai: any) => {
+ if (!allPositions.has(pegawai.posisi.id)) {
+ allPositions.set(pegawai.posisi.id, {
+ ...pegawai.posisi,
+ pegawaiList: [],
+ children: []
+ });
+ }
+});
+
+// Then assign employees to their positions
+aktifPegawai.forEach((pegawai: any) => {
+ const posisi = allPositions.get(pegawai.posisi.id);
+ if (posisi) {
+ posisi.pegawaiList.push(pegawai);
+ }
+});
+
+// Now build the hierarchy
+const root = [];
+for (const [_, posisi] of allPositions) {
+ if (posisi.parentId) {
+ const parent = allPositions.get(posisi.parentId);
+ if (parent) {
+ parent.children.push(posisi);
+ } else {
+ // Only add to root if it's a top-level position
+ if (!posisi.parentId) {
+ root.push(posisi);
+ }
+ }
+ } else {
+ root.push(posisi);
+ }
+}
+ function toOrgChartFormat(node: any): any {
+ return {
+ expanded: true,
+ type: 'person',
+ styleClass: 'p-person',
+ data: {
+ name: node.pegawaiList?.[0]?.namaLengkap || 'Belum ditugaskan',
+ title: node.nama || 'Tanpa jabatan',
+ image: node.pegawaiList?.[0]?.image?.link || '/img/default.png',
+ description: node.deskripsi || '',
+ positionId: node.id || null,
+ },
+ children: node.children?.map(toOrgChartFormat) || [],
+ }
+ }
+
+ const chartData = root.map(toOrgChartFormat)
+
+ return (
+
+
+
+
+
+ )
+}
+
+function nodeTemplate(node: any) {
+ const imageSrc = node?.data?.image || '/img/default.png'
+ const name = node?.data?.name || 'Tanpa Nama'
+ const title = node?.data?.title || 'Tanpa Jabatan'
+ const description = node?.data?.description || ''
+
+ return (
+
+ {(styles) => (
+
+
+ {name}
+
+ {title}
+
+
+ {description || 'Belum ada deskripsi.'}
+
+
+ {
+ const id = node?.data?.positionId
+ if (id && (window as any).scrollTo) {
+ ;(window as any).scrollTo({ top: 0, behavior: 'smooth' })
+ }
+ }}
+ >
+ Kembali
+
+
+
+ )}
+
+ )
+}
+
+
diff --git a/src/app/darmasaba/(pages)/inovasi/desa-digital-smart-village/page.tsx b/src/app/darmasaba/(pages)/inovasi/desa-digital-smart-village/page.tsx
index ecaf2a91..ef1465f7 100644
--- a/src/app/darmasaba/(pages)/inovasi/desa-digital-smart-village/page.tsx
+++ b/src/app/darmasaba/(pages)/inovasi/desa-digital-smart-village/page.tsx
@@ -56,7 +56,8 @@ function Page() {
/>
- Mewujudkan Desa Darmasaba sebagai pusat inovasi digital yang memberdayakan masyarakat, meningkatkan kesejahteraan, dan menciptakan peluang ekonomi berbasis teknologi.
+ Menjadikan Desa Darmasaba pusat inovasi digital untuk pemberdayaan masyarakat
+ dan peningkatan ekonomi berbasis teknologi.
@@ -73,7 +74,7 @@ function Page() {
{v.name}
-
+
)
diff --git a/src/app/darmasaba/(pages)/inovasi/info-teknologi-tepat-guna/page.tsx b/src/app/darmasaba/(pages)/inovasi/info-teknologi-tepat-guna/page.tsx
index 7bda4660..6a36f0f2 100644
--- a/src/app/darmasaba/(pages)/inovasi/info-teknologi-tepat-guna/page.tsx
+++ b/src/app/darmasaba/(pages)/inovasi/info-teknologi-tepat-guna/page.tsx
@@ -74,7 +74,7 @@ function Page() {
{v.name}
-
+
)
diff --git a/src/app/darmasaba/(pages)/inovasi/kolaborasi-inovasi/page.tsx b/src/app/darmasaba/(pages)/inovasi/kolaborasi-inovasi/page.tsx
index d7ff33fa..7181199b 100644
--- a/src/app/darmasaba/(pages)/inovasi/kolaborasi-inovasi/page.tsx
+++ b/src/app/darmasaba/(pages)/inovasi/kolaborasi-inovasi/page.tsx
@@ -46,7 +46,7 @@ function Page() {
useShallowEffect(() => {
mitraState.findMany.load(page, 10);
- load(page, 10, search, selectedYear || '');
+ load(page, 10, search, selectedYear ? `year:${selectedYear}` : '');
}, [page, search, selectedYear]);
const mitraData = mitraState.findMany.data || [];
diff --git a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/informasi-desa/page.tsx b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/informasi-desa/page.tsx
index 5dc168ec..9538a594 100644
--- a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/informasi-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/informasi-desa/page.tsx
@@ -57,7 +57,7 @@ function InformasiDesa() {
{dataBerita.kategoriBerita?.name} β’ {dayjs(dataBerita.createdAt).fromNow()}
{dataBerita.judul}
-
+
@@ -104,7 +104,7 @@ function InformasiDesa() {
{dataPengumuman.judul}
{dataPengumuman.CategoryPengumuman?.name} β’ {dayjs(dataPengumuman.createdAt).fromNow()}
-
+
diff --git a/src/app/darmasaba/(pages)/inovasi/program-kreatif-desa/[id]/page.tsx b/src/app/darmasaba/(pages)/inovasi/program-kreatif-desa/[id]/page.tsx
index 67fc2549..3d97e426 100644
--- a/src/app/darmasaba/(pages)/inovasi/program-kreatif-desa/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/inovasi/program-kreatif-desa/[id]/page.tsx
@@ -84,6 +84,7 @@ function Page() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: data?.deskripsi || '-' }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
diff --git a/src/app/darmasaba/(pages)/inovasi/program-kreatif-desa/page.tsx b/src/app/darmasaba/(pages)/inovasi/program-kreatif-desa/page.tsx
index cbe86a05..b8af8a7a 100644
--- a/src/app/darmasaba/(pages)/inovasi/program-kreatif-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/inovasi/program-kreatif-desa/page.tsx
@@ -1,14 +1,14 @@
'use client'
+import { IconKey, IconMapper } from '@/app/admin/(dashboard)/_com/iconMap';
import programKreatifState from '@/app/admin/(dashboard)/_state/inovasi/program-kreatif';
import colors from '@/con/colors';
-import { Box, Button, Center, Group, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
+import { Box, Button, Center, Grid, GridCol, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
+import { IconSearch } from '@tabler/icons-react';
import { useTransitionRouter } from 'next-view-transitions';
-import React, { useState } from 'react';
+import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
-import { IconSearch } from '@tabler/icons-react';
-import { IconKey, IconMapper } from '@/app/admin/(dashboard)/_com/iconMap';
// const data = [
// {
@@ -75,17 +75,23 @@ function Page() {
-
-
- Program Kreatif Desa
-
- }
- value={search}
- onChange={(e) => setSearch(e.currentTarget.value)}
- />
-
+
+
+
+ Program Kreatif Desa
+
+
+
+ setSearch(e.target.value)}
+ leftSection={ }
+ w={{ base: "50%", md: "100%" }}
+ />
+
+
diff --git a/src/app/darmasaba/(pages)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx b/src/app/darmasaba/(pages)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx
index b8846f90..b4abcb7e 100644
--- a/src/app/darmasaba/(pages)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx
@@ -1,7 +1,7 @@
'use client'
import keamananLingkunganState from '@/app/admin/(dashboard)/_state/keamanan/keamanan-lingkungan';
import colors from '@/con/colors';
-import { Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
+import { Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Spoiler, Stack, Text, TextInput } from '@mantine/core';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconSearch } from '@tabler/icons-react';
import { useState } from 'react';
@@ -11,6 +11,7 @@ import BackButton from '../../desa/layanan/_com/BackButto';
function Page() {
const state = useProxy(keamananLingkunganState)
+ const [expandedMap, setExpandedMap] = useState>({});
const [search, setSearch] = useState('')
const [debouncedSearch] = useDebouncedValue(search, 500); // 500ms delay
const {
@@ -25,6 +26,13 @@ function Page() {
load(page, 3, debouncedSearch)
}, [page, debouncedSearch])
+ const toggleExpanded = (index: number, value: boolean) => {
+ setExpandedMap((prev) => ({
+ ...prev,
+ [index]: value,
+ }));
+ };
+
if (loading || !data) {
return (
@@ -48,7 +56,7 @@ function Page() {
setSearch(e.target.value)}
leftSection={ }
@@ -56,8 +64,8 @@ function Page() {
/>
-
- Keamanan dan ketertiban lingkungan di Desa Darmasaba dijaga melalui peran aktif Pecalang dan Patwal (Patroli Pengawal). Mereka bertugas memastikan desa tetap aman, tertib, dan kondusif bagi seluruh warga.
+
+ Pecalang dan Patwal (Patroli Pengawal) bertugas memastikan desa tetap aman, tertib, dan kondusif bagi seluruh warga.
@@ -80,7 +88,22 @@ function Page() {
{v.name}
-
+
+ Show more
+
+ }
+ hideLabel={
+
+ Hide details
+
+ }
+ expanded={expandedMap[k] || false}
+ onExpandedChange={(val) => toggleExpanded(k, val)}
+ >
+
+
@@ -89,7 +112,7 @@ function Page() {
})}
-
+
+
+
+
+
+
+
+ Kontak Darurat
+
+
+ Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung.
+
+
+ setSearch(e.target.value)}
+ leftSection={ }
+ w={{ base: "100%", md: "25%" }}
+ />
+
+
+
+
+
+
+
+
+
+
+ Nomor Darurat Utama
+
+ 112
+
+
+
+
+
+
+ Tidak ada kontak darurat yang ditemukan
+
+
+ );
+ }
+
return (
@@ -49,10 +97,12 @@ function Page() {
}
+ radius={"lg"}
+ placeholder='Cari Kontak Darurat'
value={search}
- onChange={(e) => setSearch(e.currentTarget.value)}
+ onChange={(e) => setSearch(e.target.value)}
+ leftSection={ }
+ w={{ base: "50%", md: "100%" }}
/>
@@ -76,57 +126,63 @@ function Page() {
{/* Layanan Darurat */}
{data.map((item) => (
-
-
-
- {item.icon && (
-
- )}
-
-
- {item.nama}
-
-
+ (
-
-
-
- {kontak.kontakItem?.icon && (
-
- )}
+ p="lg"
+ radius="md"
+ bg={colors['white-trans-1']}
+ >
+
+
+ {item.icon && (
+
+ )}
+
+
+ {item.nama}
+
+
+
+ {/* Kontak Items */}
+ {item.kontakItems?.map((kontak) => (
+
+
+
+ {kontak.kontakItem?.icon && (
+
+ )}
+
+ {kontak.kontakItem?.nama}
+
+
- {kontak.kontakItem?.nama}
+ {kontak.kontakItem?.nomorTelepon}
-
- {kontak.kontakItem?.nomorTelepon}
-
-
-
- ))}
-
+
+ ))}
+
+
))}
diff --git a/src/app/darmasaba/(pages)/keamanan/laporan-publik/[id]/page.tsx b/src/app/darmasaba/(pages)/keamanan/laporan-publik/[id]/page.tsx
index 0b9242eb..fbf8a42d 100644
--- a/src/app/darmasaba/(pages)/keamanan/laporan-publik/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/laporan-publik/[id]/page.tsx
@@ -120,7 +120,7 @@ function Page() {
Kronologi
-
+
@@ -142,6 +142,7 @@ function Page() {
fz="sm"
c="dimmed"
dangerouslySetInnerHTML={{ __html: item.deskripsi || '-' }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
))}
diff --git a/src/app/darmasaba/(pages)/keamanan/laporan-publik/page.tsx b/src/app/darmasaba/(pages)/keamanan/laporan-publik/page.tsx
index dd209346..57b83ab5 100644
--- a/src/app/darmasaba/(pages)/keamanan/laporan-publik/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/laporan-publik/page.tsx
@@ -1,15 +1,15 @@
'use client'
+import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
import laporanPublikState from '@/app/admin/(dashboard)/_state/keamanan/laporan-publik';
import colors from '@/con/colors';
-import { Box, Button, Center, ColorSwatch, Flex, Group, Modal, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
+import { Box, Button, Center, ColorSwatch, Flex, Group, Modal, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import { DateTimePicker } from '@mantine/dates';
import { useDebouncedValue, useDisclosure, useShallowEffect } from '@mantine/hooks';
-import { IconArrowRight, IconPlus } from '@tabler/icons-react';
+import { IconArrowRight, IconPlus, IconSearch } from '@tabler/icons-react';
+import { useTransitionRouter } from 'next-view-transitions';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
-import { useTransitionRouter } from 'next-view-transitions';
-import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
function Page() {
const [search, setSearch] = useState("");
@@ -53,14 +53,17 @@ function Page() {
return (
-
+
setSearch(e.currentTarget.value)}
- />
-
+ radius={"lg"}
+ placeholder='Cari Laporan Publik'
+ value={search}
+ onChange={(e) => setSearch(e.target.value)}
+ leftSection={ }
+ w={{ base: "100%", md: "30%" }}
+ />
+
@@ -115,7 +118,7 @@ function Page() {
return (
- {v.judul}
+ {v.judul}
{v.tanggalWaktu
? new Date(v.tanggalWaktu).toLocaleString('id-ID')
@@ -130,6 +133,7 @@ function Page() {
fz="md"
c="dimmed"
dangerouslySetInnerHTML={{ __html: item.deskripsi || '-' }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
))
@@ -191,7 +195,7 @@ function Page() {
>
(stateLaporan.create.form.judul = e.target.value)}
label={Judul Laporan Publik }
placeholder="Masukkan judul laporan publik"
@@ -199,7 +203,7 @@ function Page() {
/>
(stateLaporan.create.form.lokasi = e.target.value)}
label={Lokasi Laporan Publik }
placeholder="Masukkan lokasi laporan publik"
@@ -208,7 +212,7 @@ function Page() {
Tanggal Laporan Publik}
- value={
+ defaultValue={
stateLaporan.create.form.tanggalWaktu
? new Date(stateLaporan.create.form.tanggalWaktu)
: null
diff --git a/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/[id]/page.tsx b/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/[id]/page.tsx
index 6d1ff9a1..a88f5bac 100644
--- a/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/[id]/page.tsx
@@ -45,7 +45,7 @@ function DetailPencegahanKriminalitas() {
const data = kriminalitasState.findUnique.data;
return (
-
+
Deskripsi Singkat
{data?.deskripsiSingkat ? (
-
+
) : (
Belum ada deskripsi singkat
)}
@@ -86,7 +86,7 @@ function DetailPencegahanKriminalitas() {
Deskripsi Lengkap
{data?.deskripsi ? (
-
+
) : (
Belum ada deskripsi
)}
diff --git a/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/page.tsx b/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/page.tsx
index 5882e8cf..b859cf1e 100644
--- a/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/page.tsx
@@ -2,7 +2,7 @@
'use client'
import pencegahanKriminalitasState from '@/app/admin/(dashboard)/_state/keamanan/pencegahan-kriminalitas';
import colors from '@/con/colors';
-import { Box, Button, Center, Flex, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
+import { Box, Button, Center, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowRight } from '@tabler/icons-react';
import { useTransitionRouter } from 'next-view-transitions';
@@ -41,13 +41,44 @@ function Page() {
)
}
+ if (data.length === 0) {
+ return (
+
+
+
+ Pencegahan Kriminalitas
+
+
+ Keamanan Komunitas & Pencegahan Kriminal
+
+
+
+
+
+ Program Keamanan Berjalan
+
+
+
+ Tidak ada data pencegahan kriminalitas yang cocok
+
+
+
+
+
+ )
+ }
+
return (
Pencegahan Kriminalitas
-
+
Keamanan Komunitas & Pencegahan Kriminal
@@ -61,32 +92,63 @@ function Page() {
Program Keamanan Berjalan
- {data.length > 0 ? (
- data.map((item) => (
- router.push(`/darmasaba/keamanan/pencegahan-kriminalitas/${item.id}`)}>
-
-
+
+ {data.length > 0 ? (
+ data.map((item) => (
+
+ router.push(`/darmasaba/keamanan/pencegahan-kriminalitas/${item.id}`)
+ }
+ onMouseEnter={(e) =>
+ (e.currentTarget.style.backgroundColor = '#1a3e7a')
+ }
+ onMouseLeave={(e) =>
+ (e.currentTarget.style.backgroundColor = colors['blue-button'])
+ }
+ >
+
{item.judul}
-
-
-
-
- ))
- ) : (
-
- Tidak ada data pencegahan kriminalitas yang cocok
-
- )}
+
+
+ ))
+ ) : (
+ Tidak ada data pencegahan kriminalitas yang cocok
+ )}
+
}
- onClick={() => router.push(`/darmasaba/keamanan/pencegahan-kriminalitas/program-lainnya`)}
+ variant="outline"
+ color="blue"
+ rightSection={ }
+ styles={{
+ root: {
+ fontWeight: 600,
+ borderWidth: 2,
+ },
+ }}
+ onClick={() =>
+ router.push(
+ `/darmasaba/keamanan/pencegahan-kriminalitas/program-lainnya`
+ )
+ }
>
Jelajahi Program Lainnya
@@ -112,9 +174,7 @@ function Page() {
{findFirst.data?.judul}
-
- {findFirst.data?.deskripsiSingkat}
-
+
) : null}
diff --git a/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/program-lainnya/page.tsx b/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/program-lainnya/page.tsx
index 39908b59..caa91d39 100644
--- a/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/program-lainnya/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/pencegahan-kriminalitas/program-lainnya/page.tsx
@@ -21,12 +21,23 @@ import pencegahanKriminalitasState from '@/app/admin/(dashboard)/_state/keamanan
import { useShallowEffect } from '@mantine/hooks';
import { useState } from 'react';
import HeaderSearch from '@/app/admin/(dashboard)/_com/header';
+import { IconArrowLeft } from '@tabler/icons-react';
function PencegahanKriminalitas() {
const [search, setSearch] = useState("");
-
+ const router = useRouter();
return (
-
+
+
+ router.back()}
+ leftSection={ }
+ >
+ Kembali
+
+
diff --git a/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/page.tsx b/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/page.tsx
index dcfb262e..3111e04f 100644
--- a/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/page.tsx
@@ -20,10 +20,41 @@ function Page() {
} = state;
useEffect(() => {
- if (!data && !loading) {
- load();
- }
- }, [data, loading]);
+ load();
+ }, []);
+
+ // kalau masih loading
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ // kalau data kosong
+ if (!data) {
+ return (
+
+
+
+
+
+
+ Kantor Polisi Terdekat
+
+
+ Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung
+
+
+
+
+ Data Polsek tidak ada
+
+
+
+ );
+ }
return (
diff --git a/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/semua-polsek/page.tsx b/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/semua-polsek/page.tsx
index 47886d21..1ccaa2f0 100644
--- a/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/semua-polsek/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/polsek-terdekat/semua-polsek/page.tsx
@@ -8,10 +8,12 @@ import React, { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../../desa/layanan/_com/BackButto';
import { useRouter } from 'next/navigation';
+import { useDebouncedValue } from '@mantine/hooks';
function Page() {
const state = useProxy(polsekTerdekatState);
const [search, setSearch] = useState('');
+ const [debouncedSearch] = useDebouncedValue(search, 500); // 500ms delay
const router = useRouter()
const {
@@ -23,8 +25,8 @@ function Page() {
} = state.findMany;
useShallowEffect(() => {
- load(page, 3, search)
- }, [page, search])
+ load(page, 3, debouncedSearch)
+ }, [page, debouncedSearch])
if (loading || !data) {
return (
diff --git a/src/app/darmasaba/(pages)/keamanan/tips-keamanan/page.tsx b/src/app/darmasaba/(pages)/keamanan/tips-keamanan/page.tsx
index 6adbdfe3..e4709396 100644
--- a/src/app/darmasaba/(pages)/keamanan/tips-keamanan/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/tips-keamanan/page.tsx
@@ -82,7 +82,7 @@ function Page() {
{v.judul}
-
+
diff --git a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/[id]/page.tsx b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/[id]/page.tsx
index c989a0be..7fe14d54 100644
--- a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/[id]/page.tsx
@@ -78,28 +78,25 @@ function Page() {
Pendahuluan
-
- {state.findUnique.data.introduction?.content}
-
+
- Kenali Gejala DBD
+ {state.findUnique.data.symptom?.title}
- {state.findUnique.data.symptom?.title}
-
+
{state.findUnique.data.prevention?.title}
-
+
{state.findUnique.data.firstaid?.title}
-
+
@@ -117,10 +114,10 @@ function Page() {
{state.findUnique.data?.mythvsfact ? (
-
+
-
+
) : (
diff --git a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/page.tsx b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/page.tsx
index abcb1889..defb85c2 100644
--- a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/page.tsx
@@ -1,7 +1,7 @@
'use client'
import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import colors from '@/con/colors';
-import { Anchor, Box, Card, Divider, Group, Image, Loader, Paper, Stack, Text, Title, Tooltip } from '@mantine/core';
+import { Box, Button, Card, Divider, Group, Image, Loader, Paper, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconCalendar, IconChevronRight } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -28,9 +28,9 @@ function ArtikelKesehatanPage() {
-
+
Artikel Kesehatan
-
+
{state.findMany.data.length === 0 ? (
@@ -51,7 +51,7 @@ function ArtikelKesehatanPage() {
onMouseLeave={(e) => (e.currentTarget.style.transform = 'translateY(0)')}
>
-
+
{item.title}
@@ -64,18 +64,17 @@ function ArtikelKesehatanPage() {
{item.content}
-
-
+ }
onClick={() => router.push(`/darmasaba/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/${item.id}`)}
- variant="light"
- c={colors['blue-button']}
>
-
- Baca Selengkapnya
-
-
-
-
+ Baca Selengkapnya
+
+
))
diff --git a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/[id]/page.tsx b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/[id]/page.tsx
index 8c1500ab..6aa24dfd 100644
--- a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/[id]/page.tsx
@@ -3,9 +3,9 @@
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
-import { ActionIcon, Anchor, AspectRatio, Badge, Box, Button, Card, Chip, CopyButton, Divider, Grid, Group, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, ThemeIcon, Title, Tooltip } from '@mantine/core';
+import { ActionIcon, AspectRatio, Badge, Box, Button, Card, CopyButton, Divider, Grid, Group, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, ThemeIcon, Title, Tooltip } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
-import { IconBrandWhatsapp, IconCheck, IconCopy, IconDeviceLandlinePhone, IconHeart, IconInfoCircle, IconMail, IconMapPin, IconMoodEmpty, IconSearch, IconStethoscope, IconUser, IconUsersGroup, IconWallet } from '@tabler/icons-react';
+import { IconBrandWhatsapp, IconCheck, IconCopy, IconDeviceLandlinePhone, IconHeart, IconInfoCircle, IconMail, IconMapPin, IconMoodEmpty, IconSearch, IconUser } from '@tabler/icons-react';
import { useParams } from 'next/navigation';
import { useMemo } from 'react';
import { useProxy } from 'valtio/utils';
@@ -39,10 +39,9 @@ function Page() {
const nama = data?.name || 'Fasilitas Kesehatan';
const prosedur = data?.prosedurpendaftaran.content || '';
- console.log("Prosedur:", data?.prosedurpendaftaran);
const alamat = data?.informasiumum?.alamat || '-';
const jam = data?.informasiumum?.jamOperasional || '-';
- const layananUnggulan = data?.layananunggulan || '';
+ const layananUnggulan = data?.layananunggulan?.content || '';
const tenaga = data?.dokterdantenagamedis || null;
const fasilitasPendukungHtml = data?.fasilitaspendukung?.content || '';
const tarif = (data?.tarifdanlayanan as TarifDanLayanan) || null;
@@ -150,11 +149,6 @@ function Page() {
-
- }>Layanan Medis
- }>Ramah Keluarga
- }>Pembayaran Non-Tunai
-
@@ -183,7 +177,7 @@ function Page() {
Layanan Unggulan
{layananUnggulan ? (
-
+
) : (
@@ -211,7 +205,6 @@ function Page() {
} component="a" href={`https://wa.me/${kontak.whatsapp.replace(/\D/g, '')}`} target="_blank" aria-label="Hubungi WhatsApp">WhatsApp
} component="a" href={`mailto:${kontak.email}`} aria-label="Kirim Email">Email
- Kunjungi situs resmi
@@ -247,20 +240,13 @@ function Page() {
-
-
-
-
-
-
-
-
+
Fasilitas Pendukung
{fasilitasPendukungHtml ? (
-
+
) : (
@@ -271,8 +257,7 @@ function Page() {
)}
-
-
+
Layanan & Tarif
@@ -310,17 +295,18 @@ function Page() {
)}
+
-
+
Prosedur Pendaftaran
{prosedur ? (
-
+
) : (
Belum ada prosedur pendaftaran
)}
diff --git a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/page.tsx b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/page.tsx
index f3043dcc..19cf6095 100644
--- a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/page.tsx
@@ -1,11 +1,11 @@
'use client'
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
-import { Anchor, Badge, Box, Card, Divider, Group, Paper, Skeleton, Stack, Text } from '@mantine/core';
+import { Badge, Box, Button, Card, Divider, Group, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
+import { IconChevronRight, IconClock, IconMapPin } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
-import { IconMapPin, IconClock, IconArrowRight } from '@tabler/icons-react';
function FasilitasKesehatanPage() {
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
@@ -36,72 +36,73 @@ function FasilitasKesehatanPage() {
- {state.findMany.data.length === 0 ? (
-
-
- Belum ada fasilitas kesehatan yang tersedia
-
-
- ) : (
- state.findMany.data.map((item) => (
- {
- (e.currentTarget as HTMLElement).style.transform = 'translateY(-4px)';
- (e.currentTarget as HTMLElement).style.boxShadow = '0 8px 20px rgba(0,0,0,0.08)';
- }}
- onMouseLeave={(e) => {
- (e.currentTarget as HTMLElement).style.transform = 'translateY(0px)';
- (e.currentTarget as HTMLElement).style.boxShadow = '0 4px 12px rgba(0,0,0,0.05)';
- }}
- >
-
-
-
- {item.name}
-
-
- Aktif
-
-
-
-
-
- {item.informasiumum.alamat}
-
-
-
-
-
- {item.informasiumum.jamOperasional}
-
-
-
- router.push(
- `/darmasaba/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/${item.id}`
- )
- }
- c={colors['blue-button']}
- fz="sm"
- fw={600}
- style={{ display: 'inline-flex', alignItems: 'center', gap: '4px' }}
- >
- Lihat Detail
-
-
-
-
- ))
- )}
+ {state.findMany.data.length === 0 ? (
+
+
+ Belum ada fasilitas kesehatan yang tersedia
+
+
+ ) : (
+ state.findMany.data.map((item) => (
+ {
+ (e.currentTarget as HTMLElement).style.transform = 'translateY(-4px)';
+ (e.currentTarget as HTMLElement).style.boxShadow = '0 8px 20px rgba(0,0,0,0.08)';
+ }}
+ onMouseLeave={(e) => {
+ (e.currentTarget as HTMLElement).style.transform = 'translateY(0px)';
+ (e.currentTarget as HTMLElement).style.boxShadow = '0 4px 12px rgba(0,0,0,0.05)';
+ }}
+ >
+
+
+
+ {item.name}
+
+
+ Aktif
+
+
+
+
+
+ {item.informasiumum.alamat}
+
+
+
+
+
+ {item.informasiumum.jamOperasional}
+
+
+
+ }
+ onClick={() =>
+ router.push(
+ `/darmasaba/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/${item.id}`
+ )
+ }
+ >
+ Lihat Detail
+
+
+
+
+ ))
+ )}
diff --git a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/[id]/page.tsx b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/[id]/page.tsx
index ac54f30d..f5af70e8 100644
--- a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/[id]/page.tsx
@@ -4,14 +4,16 @@ import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import {
Box,
+ Button,
Divider,
Group,
+ Modal,
Paper,
Skeleton,
Stack,
Text
} from '@mantine/core';
-import { useShallowEffect } from '@mantine/hooks';
+import { useDisclosure, useShallowEffect } from '@mantine/hooks';
import { IconMail, IconPhone, IconUser } from '@tabler/icons-react';
import { useParams } from 'next/navigation';
import { useProxy } from 'valtio/utils';
@@ -21,6 +23,7 @@ import CreatePendaftaran from '../create/page';
function Page() {
const params = useParams();
const state = useProxy(jadwalkegiatanState);
+ const [opened, { open, close }] = useDisclosure(false);
useShallowEffect(() => {
state.findUnique.load(params?.id as string);
@@ -66,28 +69,38 @@ function Page() {
Deskripsi Kegiatan
-
+
Layanan yang Tersedia
-
+
Syarat & Ketentuan
-
+
Dokumen yang Perlu Dibawa
-
+
-
+
+ Pendaftaran Kegiatan
+
+
+ Buat Pendaftaran
+
+
+
+
+
+
diff --git a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/page.tsx b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/page.tsx
index da57f97c..c805b546 100644
--- a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/page.tsx
@@ -53,7 +53,11 @@ function JadwalKegiatanPage() {
{item.informasijadwalkegiatan.name}
- {item.informasijadwalkegiatan.tanggal}
+ {new Date(item.informasijadwalkegiatan.tanggal).toLocaleDateString('id-ID', {
+ day: '2-digit',
+ month: 'long',
+ year: 'numeric'
+ })}
@@ -69,9 +73,9 @@ function JadwalKegiatanPage() {
-
+
}
@@ -80,14 +84,6 @@ function JadwalKegiatanPage() {
`/darmasaba/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/${item.id}`
)
}
- styles={{
- root: {
- background: colors['blue-button'],
- color: 'white',
- boxShadow: '0 0 12px rgba(0, 123, 255, 0.4)',
- transition: 'all 0.2s ease',
- },
- }}
>
Lihat Detail & Daftar
diff --git a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/page.tsx b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/page.tsx
index 31c1ee63..7000f374 100644
--- a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/page.tsx
@@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import colors from '@/con/colors';
import { BarChart as MantineBarChart } from '@mantine/charts';
-import { Box, Center, ColorSwatch, Flex, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
+import { Box, Center, ColorSwatch, Flex, Paper, SimpleGrid, Skeleton, Stack, Text, Title } from '@mantine/core';
import { useEffect, useState } from 'react';
import BackButton from '../../desa/layanan/_com/BackButto';
@@ -107,20 +107,7 @@ function Page() {
-
-
-
- Angka Kematian
-
-
-
-
-
- Angka Kelahiran
-
-
-
-
+ Data Kematian dan Kelahiran
{chartData.length === 0 ? (
Belum ada data yang tersedia untuk ditampilkan
@@ -150,6 +137,20 @@ function Page() {
>
)}
+
+
+
+ Angka Kematian
+
+
+
+
+
+ Angka Kelahiran
+
+
+
+
@@ -163,11 +164,11 @@ function Page() {
}}
>
{/* Fasilitas Kesehatan */}
-
+
{/* Jadwal Kegiatan */}
-
+
{/* Artikel Kesehatan */}
-
+
diff --git a/src/app/darmasaba/(pages)/kesehatan/info-wabah-penyakit/[id]/page.tsx b/src/app/darmasaba/(pages)/kesehatan/info-wabah-penyakit/[id]/page.tsx
new file mode 100644
index 00000000..706a8011
--- /dev/null
+++ b/src/app/darmasaba/(pages)/kesehatan/info-wabah-penyakit/[id]/page.tsx
@@ -0,0 +1,87 @@
+'use client'
+import infoWabahPenyakit from '@/app/admin/(dashboard)/_state/kesehatan/info-wabah-penyakit/infoWabahPenyakit';
+import colors from '@/con/colors';
+import { Box, Button, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconArrowBack } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useProxy } from 'valtio/utils';
+
+
+function DetailInfoWabahPenyakitUser() {
+ const state = useProxy(infoWabahPenyakit);
+ const router = useRouter();
+ const params = useParams();
+
+ useShallowEffect(() => {
+ state.findUnique.load(params?.id as string);
+ }, []);
+
+ if (!state.findUnique.data) {
+ return (
+
+
+
+ );
+ }
+
+ const data = state.findUnique.data;
+
+ return (
+
+ {/* Tombol Back */}
+
+ router.back()}
+ leftSection={ }
+ mb={15}
+ >
+ Kembali
+
+
+
+ {/* Wrapper Detail */}
+
+
+ {/* Judul */}
+
+ {data.name || 'Kontak Darurat'}
+
+
+ {/* Gambar */}
+ {data.image?.link && (
+
+ )}
+
+ {/* Deskripsi */}
+
+ Deskripsi
+
+
+
+
+
+ );
+}
+
+export default DetailInfoWabahPenyakitUser;
diff --git a/src/app/darmasaba/(pages)/kesehatan/info-wabah-penyakit/page.tsx b/src/app/darmasaba/(pages)/kesehatan/info-wabah-penyakit/page.tsx
index 0ac6874c..e65feec2 100644
--- a/src/app/darmasaba/(pages)/kesehatan/info-wabah-penyakit/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/info-wabah-penyakit/page.tsx
@@ -2,10 +2,14 @@
import infoWabahPenyakit from '@/app/admin/(dashboard)/_state/kesehatan/info-wabah-penyakit/infoWabahPenyakit';
import colors from '@/con/colors';
import {
+ Badge,
Box,
+ Button,
Center,
+ Divider,
Grid,
GridCol,
+ Group,
Image,
Pagination,
Paper,
@@ -13,27 +17,25 @@ import {
Skeleton,
Stack,
Text,
- TextInput,
- Badge,
- HoverCard,
- Divider,
- Group,
+ TextInput
} from '@mantine/core';
-import { useShallowEffect } from '@mantine/hooks';
-import { IconSearch, IconInfoCircle } from '@tabler/icons-react';
+import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
+import { IconInfoCircle, IconSearch } from '@tabler/icons-react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
+import { useTransitionRouter } from 'next-view-transitions';
function Page() {
const state = useProxy(infoWabahPenyakit);
+ const router = useTransitionRouter();
const [search, setSearch] = useState('');
-
+ const [debouncedSearch] = useDebouncedValue(search, 500)
const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => {
- load(page, 3, search);
- }, [page, search]);
+ load(page, 3, debouncedSearch);
+ }, [page, debouncedSearch]);
if (loading || !data) {
return (
@@ -59,7 +61,7 @@ function Page() {
>
Informasi Wabah & Penyakit
-
+
Dapatkan informasi terbaru mengenai wabah dan penyakit yang sedang
diawasi.
@@ -82,7 +84,7 @@ function Page() {
-
+
Tidak ada data yang cocok dengan pencarian Anda.
@@ -99,17 +101,35 @@ function Page() {
bg={colors['white-trans-1']}
style={{
transition: 'transform 200ms ease, box-shadow 200ms ease',
+ display: 'flex',
+ flexDirection: 'column',
}}
>
-
-
+ {/* Gambar */}
+
+ w="100%"
+ style={{
+ overflow: 'hidden',
+ borderRadius: '8px',
+ }}
+ >
+
+
+
+ {/* Judul dan badge */}
{v.name}
@@ -118,52 +138,60 @@ function Page() {
Wabah
+
- Diposting: 12 Februari 2025 Β· Dinas Kesehatan
+ Diposting:{' '}
+ {new Date(v.createdAt).toLocaleDateString('id-ID', {
+ day: '2-digit',
+ month: 'long',
+ year: 'numeric',
+ })}
+
-
- {v.deskripsiSingkat}
-
-
-
-
- Lihat detail lengkap
-
-
-
-
-
-
+
+ {/* Bagian deskripsi dan tombol */}
+
+
+
+
+ router.push(`/darmasaba/kesehatan/info-wabah-penyakit/${v.id}`)
+ }
+ >
+ Selengkapnya
+
+
+
))}
+
)}
-
-
- load(newPage)}
- total={totalPages}
- radius="xl"
- size="md"
- mt="lg"
- />
-
+
+
+ load(newPage)}
+ total={totalPages}
+ radius="xl"
+ size="md"
+ mt="lg"
+ />
+
);
diff --git a/src/app/darmasaba/(pages)/kesehatan/kontak-darurat/page.tsx b/src/app/darmasaba/(pages)/kesehatan/kontak-darurat/page.tsx
index aa19062a..b07c1ea5 100644
--- a/src/app/darmasaba/(pages)/kesehatan/kontak-darurat/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/kontak-darurat/page.tsx
@@ -1,9 +1,9 @@
'use client'
-import { useState } from 'react';
-import { useProxy } from 'valtio/utils';
-import { useShallowEffect } from '@mantine/hooks';
+import kontakDarurat from '@/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat';
+import colors from '@/con/colors';
import {
Box,
+ Button,
Center,
Grid,
GridCol,
@@ -15,23 +15,24 @@ import {
Stack,
Text,
TextInput,
- Tooltip,
- Badge,
+ Tooltip
} from '@mantine/core';
-import { IconSearch, IconPhone } from '@tabler/icons-react';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconBrandWhatsapp, IconSearch } from '@tabler/icons-react';
+import { useState } from 'react';
+import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
-import kontakDarurat from '@/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat';
-import colors from '@/con/colors';
+import { useDebouncedValue } from '@mantine/hooks';
function Page() {
const state = useProxy(kontakDarurat);
const [search, setSearch] = useState('');
-
+ const [debouncedSearch] = useDebouncedValue(search, 500)
const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => {
- load(page, 6, search);
- }, [page, search]);
+ load(page, 3, debouncedSearch);
+ }, [page, debouncedSearch]);
if (loading || !data) {
return (
@@ -100,29 +101,44 @@ function Page() {
}}
>
-
+
+ (e.currentTarget.style.transform = 'scale(1.05)')}
+ onMouseLeave={(e) => (e.currentTarget.style.transform = 'scale(1)')}
+ />
+
+
{v.name}
-
+
- }
+
- Panggil Sekarang
-
+ leftSection={ }
+ component="a"
+ href={`https://wa.me/${v.whatsapp.replace(/\D/g, '')}`}
+ target="_blank"
+ aria-label="Hubungi WhatsApp"
+ >WhatsApp
))}
@@ -130,22 +146,20 @@ function Page() {
)}
- {totalPages > 1 && (
-
- load(newPage, 6, search)}
- total={totalPages}
- radius="xl"
- size="md"
- styles={{
- control: {
- borderRadius: '999px',
- },
- }}
- />
-
- )}
+
+ load(newPage, 3, search)}
+ total={totalPages}
+ size="lg"
+ radius="xl"
+ styles={{
+ control: {
+ border: `1px solid ${colors['blue-button']}`,
+ },
+ }}
+ />
+
);
}
diff --git a/src/app/darmasaba/(pages)/kesehatan/penanganan-darurat/[id]/page.tsx b/src/app/darmasaba/(pages)/kesehatan/penanganan-darurat/[id]/page.tsx
new file mode 100644
index 00000000..399d7f4f
--- /dev/null
+++ b/src/app/darmasaba/(pages)/kesehatan/penanganan-darurat/[id]/page.tsx
@@ -0,0 +1,86 @@
+'use client';
+import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penanganan-darurat/penangananDarurat';
+import colors from '@/con/colors';
+import { Box, Button, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconArrowBack } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useProxy } from 'valtio/utils';
+
+function DetailPenangananDaruratUser() {
+ const state = useProxy(penangananDarurat);
+ const router = useRouter();
+ const params = useParams();
+
+ useShallowEffect(() => {
+ state.findUnique.load(params?.id as string);
+ }, []);
+
+ if (!state.findUnique.data) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ const data = state.findUnique.data;
+
+ return (
+
+ {/* Tombol Back */}
+
+ router.back()}
+ leftSection={ }
+ mb={20}
+ >
+ Kembali
+
+
+
+ {/* Wrapper Detail */}
+
+
+
+ {data.name || 'Penanganan Darurat'}
+
+
+ {data.image?.link && (
+
+ )}
+
+
+
+
+
+
+
+ );
+}
+
+export default DetailPenangananDaruratUser;
diff --git a/src/app/darmasaba/(pages)/kesehatan/penanganan-darurat/page.tsx b/src/app/darmasaba/(pages)/kesehatan/penanganan-darurat/page.tsx
index 17b83e42..303462ae 100644
--- a/src/app/darmasaba/(pages)/kesehatan/penanganan-darurat/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/penanganan-darurat/page.tsx
@@ -2,8 +2,8 @@
import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penanganan-darurat/penangananDarurat'
import colors from '@/con/colors'
import {
- Badge,
Box,
+ Button,
Center,
Grid,
GridCol,
@@ -17,7 +17,7 @@ import {
TextInput,
Tooltip
} from '@mantine/core'
-import { useShallowEffect } from '@mantine/hooks'
+import { useDebouncedValue, useShallowEffect } from '@mantine/hooks'
import { IconSearch } from '@tabler/icons-react'
import { useState } from 'react'
import { useProxy } from 'valtio/utils'
@@ -26,12 +26,12 @@ import BackButton from '../../desa/layanan/_com/BackButto'
function Page() {
const state = useProxy(penangananDarurat)
const [search, setSearch] = useState('')
-
+ const [debouncedSearch] = useDebouncedValue(search, 500)
const { data, page, totalPages, loading, load } = state.findMany
useShallowEffect(() => {
- load(page, 6, search)
- }, [page, search])
+ load(page, 3, debouncedSearch)
+ }, [page, debouncedSearch])
if (loading || !data) {
return (
@@ -97,19 +97,43 @@ function Page() {
shadow="sm"
withBorder
bg={colors['white-trans-1']}
- style={{ transition: 'all 0.3s ease' }}
+ style={{
+ transition: 'all 0.3s ease',
+ transform: 'translateY(0)',
+ }}
+ onMouseEnter={(e) => (e.currentTarget.style.transform = 'translateY(-5px)')}
+ onMouseLeave={(e) => (e.currentTarget.style.transform = 'translateY(0)')}
>
-
+
+ (e.currentTarget.style.transform = 'scale(1.05)')}
+ onMouseLeave={(e) => (e.currentTarget.style.transform = 'scale(1)')}
+ />
+
+
+
+
+ Lihat Detail
+
-
- Darurat
-
))}
@@ -140,22 +170,24 @@ function Page() {
)}
- {totalPages > 1 && (
-
- load(newPage, 6, search)}
- total={totalPages}
- size="lg"
- radius="xl"
- styles={{
- control: {
- border: `1px solid ${colors['blue-button']}`,
- },
- }}
- />
-
- )}
+
+ load(newPage, 3, search)}
+ total={totalPages}
+ size="lg"
+ radius="xl"
+ styles={{
+ control: {
+ border: `1px solid ${colors['blue-button']}`,
+ transition: 'all 0.3s ease',
+ '&:hover': { backgroundColor: colors['blue-button'], color: 'white' },
+ },
+ }}
+
+ />
+
+
)
}
diff --git a/src/app/darmasaba/(pages)/kesehatan/posyandu/[id]/page.tsx b/src/app/darmasaba/(pages)/kesehatan/posyandu/[id]/page.tsx
new file mode 100644
index 00000000..1cf5b958
--- /dev/null
+++ b/src/app/darmasaba/(pages)/kesehatan/posyandu/[id]/page.tsx
@@ -0,0 +1,121 @@
+'use client';
+
+import colors from '@/con/colors';
+import { Button, Center, Flex, Group, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconArrowBack, IconCalendar, IconInfoCircle, IconPhone } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useProxy } from 'valtio/utils';
+import posyanduState from '@/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu';
+
+export default function DetailPosyanduUser() {
+ const statePosyandu = useProxy(posyanduState);
+ const params = useParams();
+ const router = useRouter();
+
+ useShallowEffect(() => {
+ statePosyandu.findUnique.load(params?.id as string);
+ }, []);
+
+ if (!statePosyandu.findUnique.data) {
+ return (
+
+
+
+ );
+ }
+
+ const data = statePosyandu.findUnique.data;
+
+ return (
+
+ {/* Tombol Kembali */}
+
+ router.back()}
+ leftSection={ }
+ mb="sm"
+ c={colors['blue-button']}
+ >
+ Kembali
+
+
+
+ {/* Konten utama */}
+
+
+ {/* Header */}
+
+ {data.name || 'Posyandu Desa'}
+
+
+ {/* Gambar */}
+ {data.image?.link ? (
+
+
+
+ ) : (
+
+
+ Tidak ada gambar
+
+
+ )}
+
+ {/* Info utama */}
+
+
+
+
+ {data.nomor || 'Nomor tidak tersedia'}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/darmasaba/(pages)/kesehatan/posyandu/page.tsx b/src/app/darmasaba/(pages)/kesehatan/posyandu/page.tsx
index 4f80a440..1048dcb2 100644
--- a/src/app/darmasaba/(pages)/kesehatan/posyandu/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/posyandu/page.tsx
@@ -1,22 +1,25 @@
'use client'
import posyandustate from "@/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu";
import colors from "@/con/colors";
-import { Badge, Box, Center, Flex, Group, Image, List, ListItem, Pagination, Paper, SimpleGrid, Skeleton, Spoiler, Stack, Text, TextInput } from "@mantine/core";
-import { useShallowEffect } from "@mantine/hooks";
+import { Badge, Box, Button, Center, Flex, Group, Image, List, ListItem, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from "@mantine/core";
+import { useDebouncedValue, useShallowEffect } from "@mantine/hooks";
import { IconCalendar, IconInfoCircle, IconPhone, IconSearch } from "@tabler/icons-react";
import { useState } from "react";
import { useProxy } from "valtio/utils";
import BackButton from "../../desa/layanan/_com/BackButto";
+import { useTransitionRouter } from "next-view-transitions";
export default function Page() {
const state = useProxy(posyandustate);
const [search, setSearch] = useState("");
+ const [debouncedSearch] = useDebouncedValue(search, 500); // 500ms delay
+ const router = useTransitionRouter()
const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => {
- load(page, 6, search);
- }, [page, search]);
+ load(page, 6, debouncedSearch);
+ }, [page, debouncedSearch]);
if (loading || !data) {
return (
@@ -26,6 +29,36 @@ export default function Page() {
);
}
+ if (data.length === 0) {
+ return (
+
+
+
+
+
+ Posyandu Desa Darmasaba
+
+ }
+ w={{ base: "100%", md: "35%" }}
+ value={search}
+ onChange={(e) => setSearch(e.currentTarget.value)}
+ />
+
+
+
+ );
+ }
+
return (
@@ -101,31 +134,41 @@ export default function Page() {
loading="lazy"
/>
-
-
-
- {v.nomor || "Tidak tersedia"}
-
+
+
+
+
+ {v.nomor || "Tidak tersedia"}
+
+
-
-
-
- Jadwal:{" "}
-
-
+
+
+
+
+
+ Jadwal: {" "}
+
+
+
-
+
+
+
-
+
+ router.push(`/darmasaba/kesehatan/posyandu/${v.id}`)}>Detail
))}
diff --git a/src/app/darmasaba/(pages)/kesehatan/program-kesehatan/[id]/page.tsx b/src/app/darmasaba/(pages)/kesehatan/program-kesehatan/[id]/page.tsx
index e40a6088..1d387b83 100644
--- a/src/app/darmasaba/(pages)/kesehatan/program-kesehatan/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/program-kesehatan/[id]/page.tsx
@@ -64,6 +64,7 @@ function Page() {
fz="md"
lh={1.6}
dangerouslySetInnerHTML={{ __html: state.findUnique.data.deskripsi }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
diff --git a/src/app/darmasaba/(pages)/kesehatan/program-kesehatan/page.tsx b/src/app/darmasaba/(pages)/kesehatan/program-kesehatan/page.tsx
index 6b58c9d6..f7ab0887 100644
--- a/src/app/darmasaba/(pages)/kesehatan/program-kesehatan/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/program-kesehatan/page.tsx
@@ -30,7 +30,7 @@ import BackButton from "../../desa/layanan/_com/BackButto";
import { useProxy } from "valtio/utils";
import programKesehatan from "@/app/admin/(dashboard)/_state/kesehatan/program-kesehatan/programKesehatan";
import { useState } from "react";
-import { useShallowEffect } from "@mantine/hooks";
+import { useDebouncedValue, useShallowEffect } from "@mantine/hooks";
import { useRouter } from "next/navigation";
const manfaatProgram = [
@@ -58,11 +58,12 @@ export default function Page() {
const state = useProxy(programKesehatan);
const router = useRouter();
const [search, setSearch] = useState("");
+ const [debouncedSearch] = useDebouncedValue(search, 500); // 500ms delay
const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => {
- load(page, 3, search);
- }, [page, search]);
+ load(page, 3, debouncedSearch);
+ }, [page, debouncedSearch]);
if (loading || !data) {
return (
@@ -87,7 +88,7 @@ export default function Page() {
>
Program Kesehatan Desa
-
+
Temukan berbagai program kesehatan untuk mendukung kualitas hidup
masyarakat Darmasaba.
@@ -125,14 +126,37 @@ export default function Page() {
className="hover-scale"
>
-
+
+
+ (e.currentTarget.style.transform = 'scale(1.05)')}
+ onMouseLeave={(e) => (e.currentTarget.style.transform = 'scale(1)')}
+ />
+
+
+
+
+
@@ -154,13 +179,13 @@ export default function Page() {
{v.createdAt
? new Date(v.createdAt).toLocaleDateString(
- "id-ID",
- {
- day: "numeric",
- month: "long",
- year: "numeric",
- }
- )
+ "id-ID",
+ {
+ day: "numeric",
+ month: "long",
+ year: "numeric",
+ }
+ )
: "Tanggal tidak tersedia"}
@@ -224,7 +249,7 @@ export default function Page() {
>
Manfaat Program Kesehatan
-
+
Program kesehatan Desa Darmasaba berperan penting dalam meningkatkan
kesejahteraan dan kualitas hidup warganya.
@@ -254,7 +279,7 @@ export default function Page() {
{v.title}
-
+
{v.desc}
diff --git a/src/app/darmasaba/(pages)/kesehatan/puskesmas/page.tsx b/src/app/darmasaba/(pages)/kesehatan/puskesmas/page.tsx
index 2e324920..02bf84c7 100644
--- a/src/app/darmasaba/(pages)/kesehatan/puskesmas/page.tsx
+++ b/src/app/darmasaba/(pages)/kesehatan/puskesmas/page.tsx
@@ -43,7 +43,7 @@ function Page() {
Daftar Puskesmas
-
+
Temukan informasi lengkap mengenai layanan, kontak, dan lokasi Puskesmas Darmasaba
diff --git a/src/app/darmasaba/(pages)/lingkungan/data-lingkungan-desa/page.tsx b/src/app/darmasaba/(pages)/lingkungan/data-lingkungan-desa/page.tsx
index b40ea743..595a35a7 100644
--- a/src/app/darmasaba/(pages)/lingkungan/data-lingkungan-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/data-lingkungan-desa/page.tsx
@@ -71,8 +71,11 @@ function Page() {
onChange={(e) => setSearch(e.currentTarget.value)}
/>
-
- Desa Darmasaba menjaga dan mengembangkan lingkungan demi kesejahteraan warganya. Fokus utama meliputi penghijauan, pengelolaan sampah, dan perlindungan kawasan hijau.
+
+ Desa Darmasaba menjaga dan mengembangkan lingkungan demi kesejahteraan warganya.
+
+
+ Fokus utama meliputi penghijauan, pengelolaan sampah, dan perlindungan kawasan hijau.
@@ -104,7 +107,7 @@ function Page() {
Β± {item.jumlah}
-
+
{item.name}
diff --git a/src/app/darmasaba/(pages)/lingkungan/edukasi-lingkungan/component/edukasiCard.tsx b/src/app/darmasaba/(pages)/lingkungan/edukasi-lingkungan/component/edukasiCard.tsx
new file mode 100644
index 00000000..e3c91c50
--- /dev/null
+++ b/src/app/darmasaba/(pages)/lingkungan/edukasi-lingkungan/component/edukasiCard.tsx
@@ -0,0 +1,66 @@
+// Create a new component: components/EdukasiCard.tsx
+'use client';
+
+import { Box, Paper, Stack, Text, Tooltip } from '@mantine/core';
+import { ReactNode } from 'react';
+
+interface EdukasiCardProps {
+ icon: ReactNode;
+ title: string;
+ description: string;
+ color?: string;
+}
+
+export function EdukasiCard({ icon, title, description, color = '#1e88e5' }: EdukasiCardProps) {
+ return (
+
+
+
+
+ {icon}
+
+
+ {title}
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/app/darmasaba/(pages)/lingkungan/edukasi-lingkungan/page.tsx b/src/app/darmasaba/(pages)/lingkungan/edukasi-lingkungan/page.tsx
index aa559a6d..99e5c4cf 100644
--- a/src/app/darmasaba/(pages)/lingkungan/edukasi-lingkungan/page.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/edukasi-lingkungan/page.tsx
@@ -1,91 +1,104 @@
-'use client'
-import colors from '@/con/colors';
-import { Box, List, ListItem, Paper, SimpleGrid, Stack, Text, Tooltip } from '@mantine/core';
+'use client';
+
+import { Box, Container, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
import { IconLeaf, IconPlant2, IconRecycle } from '@tabler/icons-react';
+import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
-const data = [
- {
- id: 1,
- title: 'Tujuan Edukasi Lingkungan',
- icon: ,
- listDeskripsi: [
- 'Meningkatkan kesadaran masyarakat akan pentingnya lingkungan bersih dan sehat',
- 'Mendorong partisipasi warga dalam pengelolaan sampah, penghijauan, dan konservasi',
- 'Mengurangi dampak negatif kegiatan manusia terhadap lingkungan',
- 'Membentuk generasi muda peduli isu-isu lingkungan',
- ],
- },
- {
- id: 2,
- title: 'Materi Edukasi yang Diberikan',
- icon: ,
- listDeskripsi: [
- 'Pengelolaan sampah: pilah organik & anorganik',
- 'Pencegahan pencemaran lingkungan (air, udara, tanah)',
- 'Pemanfaatan lahan hijau dan penghijauan desa',
- 'Daur ulang dan kreativitas dari sampah',
- 'Bahaya pembakaran sampah sembarangan',
- ],
- },
- {
- id: 3,
- title: 'Contoh Kegiatan di Desa Darmasaba',
- icon: ,
- listDeskripsi: [
- 'Pelatihan membuat kompos dari sampah rumah tangga',
- 'Gerakan "Jumat Bersih" rutin',
- 'Workshop pembuatan ecobrick',
- 'Lomba kebersihan antar banjar',
- 'Sosialisasi lingkungan di sekolah dan posyandu',
- ],
- },
-];
+import stateEdukasiLingkungan from '@/app/admin/(dashboard)/_state/lingkungan/edukasi-lingkungan';
+import colors from '@/con/colors';
+import { EdukasiCard } from './component/edukasiCard';
-function Page() {
+function LoadingSkeleton() {
return (
-
+
+ {[1, 2, 3].map((item) => (
+
+ ))}
+
+ );
+}
+
+export default function EdukasiLingkunganPage() {
+ const tujuan = useProxy(stateEdukasiLingkungan.stateTujuanEdukasi.findById);
+ const materi = useProxy(stateEdukasiLingkungan.stateMateriEdukasiLingkungan.findById);
+ const contoh = useProxy(stateEdukasiLingkungan.stateContohEdukasiLingkungan.findById);
+
+ useShallowEffect(() => {
+ tujuan.load('edit');
+ materi.load('edit');
+ contoh.load('edit');
+ }, []);
+
+ const isLoading = tujuan.loading || !tujuan.data ||
+ materi.loading || !materi.data ||
+ contoh.loading || !contoh.data;
+
+ if (isLoading) {
+ return (
+
+
+
+
+ );
+ }
+
+ return (
+
-
-
+
+
Edukasi Lingkungan
-
+
Program edukasi ini membimbing masyarakat untuk peduli dan bertanggung jawab terhadap alam,
meningkatkan kesehatan, kenyamanan, dan keberlanjutan hidup bersama.
-
+
-
-
- {data.map((item) => (
-
-
-
-
-
- {item.icon}
-
- {item.title}
-
-
-
-
-
- {item.listDeskripsi.map((desc, idx) => (
- {desc}
- ))}
-
-
-
- ))}
+
+
+ }
+ title={tujuan.data?.judul || ''}
+ description={tujuan.data?.deskripsi || ''}
+ color={colors['blue-button']}
+ />
+
+ }
+ title={materi.data?.judul || ''}
+ description={materi.data?.deskripsi || ''}
+ color={colors['blue-button']}
+ />
+
+ }
+ title={contoh.data?.judul || ''}
+ description={contoh.data?.deskripsi || ''}
+ color={colors['blue-button']}
+ />
-
+
);
-}
-
-export default Page;
+}
\ No newline at end of file
diff --git a/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/[id]/page.tsx b/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/[id]/page.tsx
index 788730e3..d267fcee 100644
--- a/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/[id]/page.tsx
@@ -70,7 +70,7 @@ function Page() {
-
+
diff --git a/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/content.tsx b/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/content.tsx
index 1c38fa7d..58ab46f1 100644
--- a/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/content.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/content.tsx
@@ -1,5 +1,9 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
+import { useEffect, useState } from 'react';
+import { useProxy } from 'valtio/utils';
+import { motion, AnimatePresence } from 'framer-motion';
+import { Transition } from '@mantine/core';
import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
import {
Badge,
@@ -23,12 +27,11 @@ import {
} from '@mantine/core';
import { IconArrowRight, IconCalendar } from '@tabler/icons-react';
import { useTransitionRouter } from 'next-view-transitions';
-import { useEffect, useState } from 'react';
-import { useProxy } from 'valtio/utils';
export default function Content({ kategori }: { kategori: string }) {
const router = useTransitionRouter();
const [page, setPage] = useState(1);
+ const [animateKey, setAnimateKey] = useState(0);
const state = useProxy(gotongRoyongState.kegiatanDesa);
const featuredState = useProxy(gotongRoyongState.kegiatanDesa.findFirst);
@@ -37,119 +40,178 @@ export default function Content({ kategori }: { kategori: string }) {
const paginatedNews = state.findMany.data || [];
const totalPages = state.findMany.totalPages || 1;
- // Load data
+ // Load data awal
useEffect(() => {
gotongRoyongState.kegiatanDesa.findFirst.load(kategori);
}, [kategori]);
+ // Load daftar berita
useEffect(() => {
state.findMany.load(page, 3, '', kategori);
+ setAnimateKey((prev) => prev + 1); // trigger animasi halus saat page berubah
}, [page, kategori]);
+ // Tampilan kosong
+ if (!featuredState.loading && !featured) {
+ return (
+
+
+ Belum Ada Data Gotong Royong
+ Tidak ada data gotong royong yang tersedia saat ini.
+
+
+ );
+ }
+
return (
{/* === Gotong Royong Utama === */}
- {featuredState.loading ? (
-
- ) : featured ? (
-
- Gotong Royong Utama
-
-
-
-
-
-
-
-
-
- {featured.kategoriKegiatan?.nama || kategori}
-
-
{featured.judul}
- {featured.deskripsiLengkap}
-
-
-
-
-
- {new Date(featured.createdAt).toLocaleDateString('id-ID', {
- day: 'numeric',
- month: 'long',
- year: 'numeric',
- })}
-
-
- }
- onClick={() => router.push(`/darmasaba/lingkungan/gotong-royong/${kategori}/${featured.id}`)}
- >
- Baca Selengkapnya
-
-
-
-
-
-
-
- ) : null}
+
+ {(styles) => (
+
+ {featured ? (
+
+ Gotong Royong Utama
+
+
+
+
+
+
+
+
+
+ {featured.kategoriKegiatan?.nama || kategori}
+
+
{featured.judul}
+
+
+
+
+
+
+ {new Date(featured.createdAt).toLocaleDateString('id-ID', {
+ day: 'numeric',
+ month: 'long',
+ year: 'numeric',
+ })}
+
+
+ }
+ onClick={() =>
+ router.push(`/darmasaba/lingkungan/gotong-royong/${kategori}/${featured.id}`)
+ }
+ >
+ Baca Selengkapnya
+
+
+
+
+
+
+
+ ) : (
+
+ )}
+
+ )}
+
- {/* === Daftar Gotong Royong === */}
+ {/* === Daftar Gotong Royong (Pagination + Fade-in Halus) === */}
Daftar Gotong Royong
- {state.findMany.loading ? (
-
- {Array(3).fill(0).map((_, i) => (
-
- ))}
-
- ) : paginatedNews.length === 0 ? (
- Belum ada gotong royong di kategori "{kategori}".
- ) : (
-
- {paginatedNews.map((item) => (
- router.push(`/darmasaba/lingkungan/gotong-royong/${kategori}/${item.id}`)}
- style={{ cursor: 'pointer' }}
- >
-
-
-
-
- {item.kategoriKegiatan?.nama || kategori}
-
- {item.judul}
-
-
-
- {new Date(item.createdAt).toLocaleDateString('id-ID', {
- day: 'numeric',
- month: 'short',
- year: 'numeric',
- })}
-
- Baca Selengkapnya
-
-
- ))}
-
- )}
+
+
+ {state.findMany.loading ? (
+
+ {Array(3)
+ .fill(0)
+ .map((_, i) => (
+
+ ))}
+
+ ) : paginatedNews.length === 0 ? (
+
+
+ Tidak Ada Data
+ Belum ada data gotong royong yang tersedia.
+
+
+ ) : (
+
+ {paginatedNews.map((item) => (
+ router.push(`/darmasaba/lingkungan/gotong-royong/${kategori}/${item.id}`)}
+ style={{ cursor: 'pointer' }}
+ >
+
+
+
+
+ {item.kategoriKegiatan?.nama || kategori}
+
+
+ {item.judul}
+
+
+
+
+ {new Date(item.createdAt).toLocaleDateString('id-ID', {
+ day: 'numeric',
+ month: 'short',
+ year: 'numeric',
+ })}
+
+ Baca Selengkapnya
+
+
+ ))}
+
+ )}
+
+
{/* Pagination */}
@@ -166,4 +228,4 @@ export default function Content({ kategori }: { kategori: string }) {
);
-}
\ No newline at end of file
+}
diff --git a/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/page.tsx b/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/page.tsx
index 3c40cc0c..fe20be98 100644
--- a/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/page.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/gotong-royong/[kategori]/page.tsx
@@ -1,4 +1,3 @@
-// src/app/darmasaba/(pages)/desa/berita/[kategori]/page.tsx
import { Suspense } from "react";
import Content from "./content";
diff --git a/src/app/darmasaba/(pages)/lingkungan/gotong-royong/_lib/layoutTabs.tsx b/src/app/darmasaba/(pages)/lingkungan/gotong-royong/_lib/layoutTabs.tsx
index 21371f49..1ef77765 100644
--- a/src/app/darmasaba/(pages)/lingkungan/gotong-royong/_lib/layoutTabs.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/gotong-royong/_lib/layoutTabs.tsx
@@ -1,113 +1,73 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
'use client'
+import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
import colors from '@/con/colors';
-import { Box, Container, Grid, GridCol, Stack, Tabs, TabsList, TabsTab, Text, TextInput } from '@mantine/core';
+import { Box, Group, Stack, Tabs, TabsList, TabsTab, Text, TextInput } from '@mantine/core';
import { IconSearch } from '@tabler/icons-react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import React, { useEffect, useState } from 'react';
+import { useProxy } from 'valtio/utils';
import BackButton from '../../../desa/layanan/_com/BackButto';
-
-type HeaderSearchProps = {
- placeholder?: string;
- searchIcon?: React.ReactNode;
- value?: string;
- onChange?: (event: React.ChangeEvent) => void;
- children?: React.ReactNode;
-};
-
-function LayoutTabsGotongRoyong({
- children,
- placeholder = "pencarian",
- searchIcon =
-}: HeaderSearchProps) {
+function LayoutTabsGotongRoyong({ children }: { children: React.ReactNode }) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
-
- // Get active tab from URL path
+
+ const kategoriState = useProxy(gotongRoyongState.kategoriKegiatan);
+
+ // tab aktif dari url
const activeTab = pathname.split('/').pop() || 'semua';
-
- // Get initial search value from URL
- const initialSearch = searchParams.get('search') || '';
- const [searchValue, setSearchValue] = useState(initialSearch);
- const [searchTimeout, setSearchTimeout] = useState(null);
-
- // Update active tab state when pathname changes
const [activeTabState, setActiveTabState] = useState(activeTab);
+
+ useEffect(() => {
+ kategoriState.findMany.load(); // ambil kategori dari DB
+ }, []);
+
useEffect(() => {
setActiveTabState(activeTab);
}, [activeTab]);
- // Clean up timeouts on unmount
- useEffect(() => {
- return () => {
- if (searchTimeout !== null) {
- clearTimeout(searchTimeout);
- }
- };
- }, [searchTimeout]);
+ // search
+ const initialSearch = searchParams.get('search') || '';
+ const [searchValue, setSearchValue] = useState(initialSearch);
+ const [searchTimeout, setSearchTimeout] = useState(null);
- // Handle search input change with debounce
const handleSearchChange = (event: React.ChangeEvent) => {
const value = event.target.value;
setSearchValue(value);
-
- // Clear previous timeout
- if (searchTimeout !== null) {
- clearTimeout(searchTimeout);
- }
-
- // Set new timeout
+
+ if (searchTimeout !== null) clearTimeout(searchTimeout);
+
const newTimeout = window.setTimeout(() => {
const params = new URLSearchParams(searchParams.toString());
-
- if (value) {
- params.set('search', value);
- } else {
- params.delete('search');
- }
-
- // Only update URL if the search value has actually changed
- if (params.toString() !== searchParams.toString()) {
- router.push(`/darmasaba/lingkungan/gotong-royong/${activeTab}?${params.toString()}`);
- }
- }, 500); // 500ms debounce delay
-
+ if (value) params.set('search', value);
+ else params.delete('search');
+
+ router.push(`/darmasaba/lingkungan/gotong-royong/${activeTab}${params.toString() ? `?${params.toString()}` : ''}`);
+ }, 500);
+
setSearchTimeout(newTimeout);
};
+
+ // --- tabs dinamis ---
const tabs = [
- {
- label: "Semua",
- value: "semua",
- href: "/darmasaba/lingkungan/gotong-royong/semua"
- },
- {
- label: "Kebersihan",
- value: "kebersihan",
- href: "/darmasaba/lingkungan/gotong-royong/kebersihan"
- },
- {
- label: "Infrastruktur",
- value: "infrastruktur",
- href: "/darmasaba/lingkungan/gotong-royong/infrastruktur"
- },
- {
- label: "Sosial",
- value: "sosial",
- href: "/darmasaba/lingkungan/gotong-royong/sosial"
- },
- {
- label: "Lingkungan",
- value: "lingkungan",
- href: "/darmasaba/lingkungan/gotong-royong/lingkungan"
- }
+ { label: "Semua", value: "semua", href: "/darmasaba/lingkungan/gotong-royong/semua" },
+ ...(kategoriState.findMany.data || []).map((kat: any) => ({
+ label: kat.nama,
+ value: kat.nama.toLowerCase(),
+ href: `/darmasaba/lingkungan/gotong-royong/${kat.nama.toLowerCase()}`
+ }))
];
+
const handleTabChange = (value: string | null) => {
if (!value) return;
const tab = tabs.find(t => t.value === value);
if (tab) {
const params = new URLSearchParams(searchParams.toString());
- router.push(`/darmasaba/lingkungan/gotong-royong/${value}${params.toString() ? `?${params.toString()}` : ''}`);
+ router.push(`${tab.href}${params.toString() ? `?${params.toString()}` : ''}`);
}
};
@@ -117,17 +77,29 @@ function LayoutTabsGotongRoyong({
-
-
-
- Gotong Royong Desa Darmasaba
-
-
- Gotong royong rutin dilakukan oleh warga desa untuk meningkatkan kualitas hidup dan kesejahteraan masyarakat Desa Darmasaba
-
-
-
+
+
+
+
+ Portal Gotong royong Darmasaba
+
+ Temukan berbagai kegiatan lingkungan yang dimiliki Desa Darmasaba
+
+
+ }
+ w="100%"
+ value={searchValue}
+ onChange={handleSearchChange}
+ />
+
+
+
+
+ {/* TABS */}
-
-
-
- {tabs.map((tab, index) => (
- router.push(tab.href)}
- >
- {tab.label}
-
- ))}
-
-
-
-
-
-
+
+
+ {tabs.map((tab, index) => (
+ router.push(tab.href)}
+ style={{
+ flex: '0 0 auto',
+ minWidth: 100,
+ textAlign: 'center'
+ }}
+ >
+ {tab.label}
+
+ ))}
+
+
{children}
@@ -168,4 +133,4 @@ function LayoutTabsGotongRoyong({
);
}
-export default LayoutTabsGotongRoyong;
\ No newline at end of file
+export default LayoutTabsGotongRoyong;
diff --git a/src/app/darmasaba/(pages)/lingkungan/gotong-royong/semua/page.tsx b/src/app/darmasaba/(pages)/lingkungan/gotong-royong/semua/page.tsx
index 8f6748cf..785e5b92 100644
--- a/src/app/darmasaba/(pages)/lingkungan/gotong-royong/semua/page.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/gotong-royong/semua/page.tsx
@@ -1,175 +1,282 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react-hooks/exhaustive-deps */
-'use client'
+'use client';
+
import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
-import { Badge, Box, Button, Card, Center, Container, Divider, Flex, Grid, GridCol, Group, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, Title } from '@mantine/core';
+import {
+ Badge,
+ Box,
+ Button,
+ Card,
+ Center,
+ Container,
+ Divider,
+ Flex,
+ Grid,
+ GridCol,
+ Group,
+ Image,
+ Pagination,
+ Paper,
+ SimpleGrid,
+ Skeleton,
+ Stack,
+ Text,
+ Title,
+ Transition,
+} from '@mantine/core';
import { IconArrowRight, IconCalendar } from '@tabler/icons-react';
-import { useTransitionRouter } from 'next-view-transitions';
-import { useSearchParams } from 'next/navigation';
-import { useEffect, useState } from 'react';
+import { motion } from 'framer-motion';
+import { useRouter, useSearchParams } from 'next/navigation';
+import { useEffect } from 'react';
import { useProxy } from 'valtio/utils';
-function Page() {
+export default function Page() {
const searchParams = useSearchParams();
- const router = useTransitionRouter();
+ const router = useRouter();
- // Parameter URL
const search = searchParams.get('search') || '';
- const currentPage = parseInt(searchParams.get('page') || '1');
- const [page, setPage] = useState(currentPage);
+ const page = parseInt(searchParams.get('page') || '1');
- // Gunakan proxy untuk state
const state = useProxy(gotongRoyongState.kegiatanDesa);
- const featured = useProxy(gotongRoyongState.kegiatanDesa.findFirst); // β
Berita utama
+ const featured = useProxy(gotongRoyongState.kegiatanDesa.findFirst);
const loadingGrid = state.findMany.loading;
const loadingFeatured = featured.loading;
- // Load berita utama (hanya sekali)
+ // Load featured data once on component mount
useEffect(() => {
- if (!featured.data && !loadingFeatured) {
- gotongRoyongState.kegiatanDesa.findFirst.load();
+ let mounted = true;
+
+ const loadFeatured = async () => {
+ try {
+ if (!featured.data && !loadingFeatured) {
+ await gotongRoyongState.kegiatanDesa.findFirst.load();
+ }
+ } catch (error) {
+ console.error('Error loading featured data:', error);
+ }
+ };
+
+ if (mounted) {
+ loadFeatured();
}
- }, [featured.data, loadingFeatured]);
- // Load berita terbaru (untuk grid) saat page/search berubah
+ return () => {
+ mounted = false;
+ };
+ }, []); // Empty dependency array to run only once on mount
+
useEffect(() => {
- const limit = 3; // Sesuaikan dengan tampilan grid
- state.findMany.load(page, limit, search);
+ let mounted = true;
+
+ const loadData = async () => {
+ try {
+ const limit = 3;
+ await state.findMany.load(page, limit, search);
+ } catch (error) {
+ console.error('Error loading data:', error);
+ }
+ };
+
+ if (mounted) {
+ loadData();
+ }
+
+ return () => {
+ mounted = false;
+ };
}, [page, search]);
- // Update URL saat page berubah
- useEffect(() => {
- const url = new URLSearchParams();
+ const handlePageChange = (newPage: number) => {
+ const url = new URLSearchParams(searchParams.toString());
if (search) url.set('search', search);
- if (page > 1) url.set('page', page.toString());
- router.replace(`?${url.toString()}`);
- }, [page, search]);
+ if (newPage > 1) url.set('page', newPage.toString());
+ else url.delete('page');
+
+ // Use push instead of replace to keep browser history
+ router.push(`?${url.toString()}`, { scroll: false });
+ };
const featuredData = featured.data;
const paginatedNews = state.findMany.data || [];
const totalPages = state.findMany.totalPages || 1;
- return (
-
-
- {/* === Gotong royong Utama (Tetap) === */}
- {loadingFeatured ? (
-
- ) : featuredData ? (
-
- Gotong royong Utama
-
-
-
-
-
-
-
-
-
- {featuredData.kategoriKegiatan?.nama || 'Gotong royong'}
-
-
{featuredData.judul}
-
- {featuredData.deskripsiSingkat}
-
-
-
-
-
-
- {new Date(featuredData.createdAt).toLocaleDateString('id-ID', {
- day: 'numeric',
- month: 'long',
- year: 'numeric'
- })}
-
-
- }
- onClick={() => router.push(`/darmasaba/lingkungan/gotong-royong/${featuredData.kategoriKegiatan?.nama}/${featuredData.id}`)}
- >
- Baca Selengkapnya
-
-
-
-
-
-
-
- ) : null}
+ // Animasi transisi halus tapi tetap instant load
+ const MotionBox = motion(Box as any);
- {/* === Gotong royong Terbaru (Berubah Saat Pagination) === */}
+ // fallback kosong
+ if (!loadingGrid && !loadingFeatured && paginatedNews.length === 0) {
+ return (
+
+ Belum Ada Data Gotong Royong
+ Tidak ada data gotong royong yang tersedia saat ini.
+
+ );
+ }
+
+ return (
+
+
+ {/* === Gotong Royong Utama === */}
+
+ {(styles) =>
+ featuredData ? (
+
+ Gotong royong Utama
+
+
+
+
+
+
+
+
+
+ {featuredData.kategoriKegiatan?.nama || 'Gotong royong'}
+
+
{featuredData.judul}
+
+
+
+
+
+
+ {new Date(featuredData.createdAt).toLocaleDateString('id-ID', {
+ day: 'numeric',
+ month: 'long',
+ year: 'numeric',
+ })}
+
+
+ }
+ onClick={() =>
+ router.push(
+ `/darmasaba/lingkungan/gotong-royong/${featuredData.kategoriKegiatan?.nama}/${featuredData.id}`
+ )
+ }
+ >
+ Baca Selengkapnya
+
+
+
+
+
+
+
+ ) : (
+
+ )
+ }
+
+
+ {/* === Gotong royong Terbaru === */}
Gotong royong Terbaru
- {loadingGrid ? (
-
- {Array(3).fill(0).map((_, i) => (
-
- ))}
-
- ) : paginatedNews.length === 0 ? (
- Tidak ada gotong royong ditemukan.
- ) : (
-
- {paginatedNews.map((item) => (
-
-
-
-
+
+ {(styles) =>
+ loadingGrid ? (
+
+ {Array(3)
+ .fill(0)
+ .map((_, i) => (
+
+ ))}
+
+ ) : paginatedNews.length === 0 ? (
+
+ Tidak ada gotong royong ditemukan.
+
+ ) : (
+
+
+ {paginatedNews.map((item) => (
+
+
+
+
-
- {item.kategoriKegiatan?.nama || 'Gotong royong'}
-
+
+ {item.kategoriKegiatan?.nama || 'Gotong royong'}
+
- {item.judul}
+
+ {item.judul}
+
- {item.deskripsiSingkat}
+
-
-
- {new Date(item.createdAt).toLocaleDateString('id-ID', {
- day: 'numeric',
- month: 'short',
- year: 'numeric'
- })}
-
-
- } onClick={() => router.push(`/darmasaba/lingkungan/gotong-royong/${item.kategoriKegiatan?.nama}/${item.id}`)}>Baca Selengkapnya
-
-
- ))}
-
- )}
+
+
+ {new Date(item.createdAt).toLocaleDateString('id-ID', {
+ day: 'numeric',
+ month: 'short',
+ year: 'numeric',
+ })}
+
- {/* Pagination hanya untuk berita terbaru */}
+ }
+ onClick={() =>
+ router.push(
+ `/darmasaba/lingkungan/gotong-royong/${item.kategoriKegiatan?.nama}/${item.id}`
+ )
+ }
+ >
+ Baca Selengkapnya
+
+
+
+ ))}
+
+
+ )
+ }
+
+
+ {/* Pagination */}
-
+
);
}
-
-export default Page;
-
diff --git a/src/app/darmasaba/(pages)/lingkungan/konservasi-adat-bali/page.tsx b/src/app/darmasaba/(pages)/lingkungan/konservasi-adat-bali/page.tsx
index 73360da3..718cb23e 100644
--- a/src/app/darmasaba/(pages)/lingkungan/konservasi-adat-bali/page.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/konservasi-adat-bali/page.tsx
@@ -1,47 +1,30 @@
+'use client'
+import stateKonservasiAdatBali from '@/app/admin/(dashboard)/_state/lingkungan/konservasi-adat-bali';
import colors from '@/con/colors';
-import { Box, Center, List, ListItem, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
+import { Box, Center, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
-const data = [
- {
- id: 1,
- title: 'Filosofi Tri Hita Karana',
- listDeskripsi: (
-
- Parahyangan: Hubungan manusia dengan Tuhan yang dijaga penuh kesadaran spiritual
- Pawongan: Harmoni dan kerja sama antar manusia dalam masyarakat
- Palemahan: Pelestarian lingkungan dan hubungan manusia dengan alam
-
- ),
- },
- {
- id: 2,
- title: 'Bentuk Konservasi Berdasarkan Adat',
- listDeskripsi: (
-
- Pelestarian Hutan Adat seperti Alas Pala Sangeh dan Wana Kerthi
- Subak: Sistem irigasi tradisional yang menekankan kebersamaan dan keberlanjutan
- Hari Raya Tumpek Uduh: Perayaan untuk menghormati pohon dan tumbuhan
- Perarem & Awig-Awig: Aturan adat untuk menjaga lingkungan dari kerusakan
- Ritual penyucian alam seperti Melasti dan Piodalan Segara
-
- ),
- },
- {
- id: 3,
- title: 'Nilai Konservasi Adat',
- listDeskripsi: (
-
- Menjaga keseimbangan ekosistem dan lingkungan hidup
- Melestarikan spiritualitas lokal dan kesucian alam
- Meningkatkan kesadaran kolektif untuk hidup selaras dengan alam
- Menjamin keberlanjutan sumber daya alam untuk generasi mendatang
-
- ),
- },
-];
-
function Page() {
+ const filosofi = useProxy(stateKonservasiAdatBali.stateFilosofiTriHita.findById)
+ const nilai = useProxy(stateKonservasiAdatBali.stateNilaiKonservasiAdat.findById)
+ const bentuk = useProxy(stateKonservasiAdatBali.stateBentukKonservasiBerdasarkanAdat.findById)
+
+ useShallowEffect(() => {
+ filosofi.load('edit')
+ nilai.load('edit')
+ bentuk.load('edit')
+ }, [])
+
+ if (filosofi.loading || !filosofi.data || nilai.loading || !nilai.data || bentuk.loading || !bentuk.data) {
+ return (
+
+
+
+ );
+ }
+
return (
@@ -56,24 +39,99 @@ function Page() {
-
- {data.map((item) => (
+
+ {/* Filsosofi */}
+
-
+
- {item.title}
-
+ {filosofi.data?.judul}
+
- {item.listDeskripsi}
+
- ))}
+
+ {/* Nilai */}
+
+
+
+
+
+ {nilai.data?.judul}
+
+
+
+
+
+
+ {/* Bentuk */}
+
+
+
+
+
+ {bentuk.data?.judul}
+
+
+
+
+
+
diff --git a/src/app/darmasaba/(pages)/lingkungan/pengelolaan-sampah-bank-sampah/page.tsx b/src/app/darmasaba/(pages)/lingkungan/pengelolaan-sampah-bank-sampah/page.tsx
index ee92f96d..48d4baf9 100644
--- a/src/app/darmasaba/(pages)/lingkungan/pengelolaan-sampah-bank-sampah/page.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/pengelolaan-sampah-bank-sampah/page.tsx
@@ -1,10 +1,10 @@
'use client'
import pengelolaanSampahState from '@/app/admin/(dashboard)/_state/lingkungan/pengelolaan-sampah';
import colors from '@/con/colors';
-import { Box, Flex, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
-import { useShallowEffect } from '@mantine/hooks';
-import { Icon, IconChartLine, IconClipboardTextFilled, IconLeaf, IconRecycle, IconScale, IconSearch, IconTent, IconTrashFilled, IconTrophy, IconTruckFilled } from '@tabler/icons-react';
-import React from 'react';
+import { Box, Center, Flex, Group, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
+import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
+import { Icon, IconChartLine, IconClipboardTextFilled, IconLeaf, IconRecycle, IconRoute, IconScale, IconSearch, IconTent, IconTrashFilled, IconTrophy, IconTruckFilled } from '@tabler/icons-react';
+import React, { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
import dynamic from 'next/dynamic';
@@ -20,20 +20,26 @@ function Page() {
const state = useProxy(pengelolaanSampahState.pengelolaanSampah)
const state2 = useProxy(pengelolaanSampahState.keteranganSampah)
+ const [search, setSearch] = useState('')
+ const [debouncedSearch] = useDebouncedValue(search, 500);
+
const {
data,
- load
+ load,
+
} = state.findMany
const {
data: data2,
- load: load2
+ load: load2,
+ page,
+ totalPages,
} = state2.findMany
useShallowEffect(() => {
load()
- load2()
- }, [])
+ load2(page, 3, debouncedSearch)
+ }, [page, debouncedSearch])
const iconMap: Record = {
ekowisata: IconLeaf,
@@ -104,8 +110,10 @@ function Page() {
px={{ base: 70, md: 150 }}
leftSection={ }
placeholder='Cari Bank Sampah Terdekat'
+ value={search}
+ onChange={(e) => setSearch(e.target.value)}
/>
-
+
{/* Left side - List of bank locations */}
@@ -114,26 +122,42 @@ function Page() {
{data2?.map((v, k) => (
- {v.namaTempatMaps}
- {v.alamat}
- {v.lat && v.lng ? (
-
- π Buka di Google Maps
-
- ) : (
- Koordinat belum tersedia
- )}
+
+
+ {v.namaTempatMaps}
+ {v.alamat}
+
+
+
+ Rute
+
+
+ {v.lat && v.lng ? (
+
+ π Lihat Peta Lebih Besar
+
+ ) : (
+ Koordinat belum tersedia
+ )}
))}
+
+ load(newPage)} // ini penting!
+ total={totalPages}
+ my="md"
+ />
+
-
+
{/* Right side - Single map showing all locations */}
diff --git a/src/app/darmasaba/(pages)/lingkungan/program-penghijauan/[id]/page.tsx b/src/app/darmasaba/(pages)/lingkungan/program-penghijauan/[id]/page.tsx
index 1922f7d1..1e33d357 100644
--- a/src/app/darmasaba/(pages)/lingkungan/program-penghijauan/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/program-penghijauan/[id]/page.tsx
@@ -100,6 +100,7 @@ function Page() {
lh={1.7}
ta="justify"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
) : (
diff --git a/src/app/darmasaba/(pages)/lingkungan/program-penghijauan/page.tsx b/src/app/darmasaba/(pages)/lingkungan/program-penghijauan/page.tsx
index 025a08fc..e10c2dfc 100644
--- a/src/app/darmasaba/(pages)/lingkungan/program-penghijauan/page.tsx
+++ b/src/app/darmasaba/(pages)/lingkungan/program-penghijauan/page.tsx
@@ -66,8 +66,11 @@ function Page() {
/>
-
- Mari berpartisipasi menanam dan merawat pohon untuk menciptakan lingkungan hijau, sehat, dan seimbang bagi seluruh warga desa.
+
+ Mari berpartisipasi menanam dan merawat pohon untuk menciptakan lingkungan hijau,
+
+
+ sehat, dan seimbang bagi seluruh warga desa.
diff --git a/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/page.tsx b/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/page.tsx
index dca68008..05d7bcb2 100644
--- a/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/page.tsx
@@ -1,12 +1,13 @@
'use client'
import beasiswaDesaState from '@/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa';
import colors from '@/con/colors';
-import { Box, Button, Center, Group, Image, Modal, Paper, Select, SimpleGrid, Stack, Stepper, StepperStep, Text, TextInput, Title } from '@mantine/core';
-import { useDisclosure } from '@mantine/hooks';
+import { Box, Button, Center, Group, Image, Modal, Pagination, Paper, Select, SimpleGrid, Skeleton, Stack, Stepper, StepperStep, Text, TextInput, Title } from '@mantine/core';
+import { useDisclosure, useShallowEffect } from '@mantine/hooks';
import { IconArrowRight, IconCoin, IconInfoCircle, IconSchool, IconUsers } from '@tabler/icons-react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
+import { useTransitionRouter } from 'next-view-transitions';
const dataBeasiswa = [
{ id: 1, nama: 'Penerima Beasiswa', jumlah: '250+', icon: IconUsers },
@@ -14,15 +15,11 @@ const dataBeasiswa = [
{ id: 3, nama: 'Dana Tersalurkan', jumlah: '1.5M', icon: IconCoin },
];
-const dataProgram = [
- { id: 1, judul: "Pelatihan SoftSkill", deskripsi: "Pengembangan diri untuk mempersiapkan karir masa depan." },
- { id: 2, judul: "Peningkatan Akses Pendidikan", deskripsi: "Memberi kesempatan bagi masyarakat kurang mampu untuk tetap sekolah." },
- { id: 3, judul: "Pendampingan Intensif", deskripsi: "Bimbingan dari mentor berpengalaman untuk mendukung akademik." },
-];
-
function Page() {
const beasiswaDesa = useProxy(beasiswaDesaState.beasiswaPendaftar)
+ const ungggulanDesa = useProxy(beasiswaDesaState.keunggulanProgram)
const [opened, { open, close }] = useDisclosure(false);
+ const router = useTransitionRouter()
const resetForm = () => {
beasiswaDesa.create.form = {
namaLengkap: "",
@@ -41,6 +38,12 @@ function Page() {
};
};
+ const { data, page, totalPages, loading, load } = ungggulanDesa.findMany;
+
+ useShallowEffect(() => {
+ load(page, 3, "");
+ }, [page])
+
const handleSubmit = async () => {
await beasiswaDesa.create.create();
resetForm();
@@ -51,6 +54,14 @@ function Page() {
const nextStep = () => setActive((current) => (current < 5 ? current + 1 : current));
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
+ if (loading || !data) {
+ return (
+
+
+
+ );
+ }
+
return (
@@ -63,20 +74,20 @@ function Page() {
Wujudkan Mimpi Pendidikanmu di Desa Darmasaba
-
+
Program beasiswa untuk mendukung pendidikan berkualitas bagi generasi muda Desa Darmasaba.
} onClick={open}>
Daftar Sekarang
- }>
+ router.push('/darmasaba/pendidikan/beasiswa-desa/pelajari-lebih-lanjut')} size="lg" radius="xl" variant="light" color={colors['blue-button']} rightSection={ }>
Pelajari Lebih Lanjut
-
+
@@ -101,14 +112,29 @@ function Page() {
Keunggulan Program
- {dataProgram.map((v, k) => (
+ {data.map((v, k) => (
{v.judul}
- {v.deskripsi}
+
))}
+
+ {
+ load(newPage, 10);
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ }}
+ total={totalPages}
+ mt="md"
+ mb="md"
+ color="blue"
+ radius="md"
+ />
+
+
Timeline Pendaftaran
@@ -142,66 +168,66 @@ function Page() {
>
- { beasiswaDesa.create.form.namaLengkap = val.target.value }} />
- { beasiswaDesa.create.form.nik = val.target.value }} />
- { beasiswaDesa.create.form.tempatLahir = val.target.value }} />
- { beasiswaDesa.create.form.tanggalLahir = val.target.value }} />
- { if (val) beasiswaDesa.create.form.jenisKelamin = val }} />
- { beasiswaDesa.create.form.kewarganegaraan = val.target.value }} />
- { if (val) beasiswaDesa.create.form.agama = val }} />
- { beasiswaDesa.create.form.alamatKTP = val.target.value }} />
- { beasiswaDesa.create.form.alamatDomisili = val.target.value }} />
- { beasiswaDesa.create.form.noHp = val.target.value }} />
- { beasiswaDesa.create.form.email = val.target.value }} />
- { if (val) beasiswaDesa.create.form.statusPernikahan = val }} />
- { if (val) beasiswaDesa.create.form.ukuranBaju = val }} />
+ { beasiswaDesa.create.form.namaLengkap = val.target.value }} />
+ { beasiswaDesa.create.form.nik = val.target.value }} />
+ { beasiswaDesa.create.form.tempatLahir = val.target.value }} />
+ { beasiswaDesa.create.form.tanggalLahir = val.target.value }} />
+ { if (val) beasiswaDesa.create.form.jenisKelamin = val }} />
+ { beasiswaDesa.create.form.kewarganegaraan = val.target.value }} />
+ { if (val) beasiswaDesa.create.form.agama = val }} />
+ { beasiswaDesa.create.form.alamatKTP = val.target.value }} />
+ { beasiswaDesa.create.form.alamatDomisili = val.target.value }} />
+ { beasiswaDesa.create.form.noHp = val.target.value }} />
+ { beasiswaDesa.create.form.email = val.target.value }} />
+ { if (val) beasiswaDesa.create.form.statusPernikahan = val }} />
+ { if (val) beasiswaDesa.create.form.ukuranBaju = val }} />
Batal
Kirim
diff --git a/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/pelajari-lebih-lanjut/page.tsx b/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/pelajari-lebih-lanjut/page.tsx
new file mode 100644
index 00000000..f4bc8999
--- /dev/null
+++ b/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/pelajari-lebih-lanjut/page.tsx
@@ -0,0 +1,315 @@
+'use client';
+
+import {
+ Box,
+ Button,
+ Container,
+ Group,
+ Modal,
+ Paper,
+ Select,
+ SimpleGrid,
+ Stack,
+ Text,
+ TextInput,
+ Timeline,
+ Title
+} from '@mantine/core';
+import { IconArrowLeft, IconChecklist, IconInfoCircle, IconQuote, IconSchool, IconTimeline, IconUserPlus } from '@tabler/icons-react';
+import { useRouter } from 'next/navigation';
+import { useDisclosure } from '@mantine/hooks';
+import { useProxy } from 'valtio/utils';
+import beasiswaDesaState from '@/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa';
+import colors from '@/con/colors';
+
+
+export default function BeasiswaPage() {
+ const router = useRouter();
+ const beasiswaDesa = useProxy(beasiswaDesaState.beasiswaPendaftar)
+ const [opened, { open, close }] = useDisclosure(false);
+ const resetForm = () => {
+ beasiswaDesa.create.form = {
+ namaLengkap: "",
+ nik: "",
+ tempatLahir: "",
+ tanggalLahir: "",
+ jenisKelamin: "",
+ kewarganegaraan: "",
+ agama: "",
+ alamatKTP: "",
+ alamatDomisili: "",
+ noHp: "",
+ email: "",
+ statusPernikahan: "",
+ ukuranBaju: "",
+ };
+ };
+
+ const handleSubmit = async () => {
+ await beasiswaDesa.create.create();
+ resetForm();
+ close();
+ };
+
+ return (
+
+ {/* Tombol Kembali */}
+
+ }
+ onClick={() => router.back()}
+ mb="lg"
+ >
+ Kembali
+
+
+
+ {/* Hero Section */}
+
+
+
+
+ Program Beasiswa Pendidikan Desa Darmasaba
+
+
+ Program ini bertujuan untuk mendukung pendidikan generasi muda di Desa Darmasaba
+ agar dapat melanjutkan studi ke jenjang lebih tinggi dengan dukungan finansial dan pendampingan.
+
+
+
+
+ {/* Tentang Program */}
+
+
+
+ Tentang Program
+
+
+ Program Beasiswa Desa Darmasaba adalah inisiatif pemerintah desa untuk meningkatkan akses
+ pendidikan bagi siswa berprestasi dan kurang mampu. Melalui program ini, desa memberikan bantuan
+ biaya sekolah, bimbingan akademik, serta pelatihan soft skill bagi peserta terpilih.
+
+
+ {/* Tambahkan info tahun berjalan di sini */}
+
+
+ π
Periode Beasiswa Tahun 2025
+
+
+ Pendaftaran beasiswa dibuka mulai 1 Januari 2025 dan ditutup pada
+ 31 Mei 2025 .
+ Pengumuman hasil seleksi akan diumumkan pada pertengahan Juni 2025 melalui website resmi Desa Darmasaba.
+
+
+
+
+ {/* Syarat dan Ketentuan */}
+
+
+
+ Syarat Pendaftaran
+
+
+
+
+ Domisili Desa Darmasaba
+
+ Peserta harus merupakan warga desa yang berdomisili minimal 2 tahun.
+
+
+
+
+ Nilai Akademik
+
+ Rata-rata nilai raport minimal 80 atau setara.
+
+
+
+
+ Surat Rekomendasi
+
+ Diperlukan surat rekomendasi dari sekolah atau guru wali kelas.
+
+
+
+
+
+ {/* Proses Seleksi */}
+
+
+
+ Proses Seleksi
+
+
+
+
+
+ Calon peserta mengisi formulir pendaftaran dan mengunggah dokumen pendukung.
+
+
+ β° Estimasi waktu: 1 Februari β 31 Mei 2025
+
+
+
+
+
+ Panitia memverifikasi kelengkapan dan validitas berkas.
+
+
+ β° Estimasi waktu: 5β7 hari kerja setelah penutupan pendaftaran
+
+
+
+
+
+ Peserta yang lolos administrasi akan diundang untuk wawancara langsung dengan tim seleksi.
+
+
+ β° Estimasi waktu: 7β10 hari kerja setelah pengumuman seleksi administrasi
+
+
+
+
+
+ Daftar penerima beasiswa diumumkan melalui website resmi Desa Darmasaba.
+
+
+ β° Estimasi waktu: 5 hari kerja setelah tahap wawancara selesai
+
+
+
+
+
+ ποΈ Total estimasi keseluruhan proses: sekitar 3β4 minggu setelah penutupan pendaftaran
+
+
+
+
+ {/* Testimoni */}
+
+
+
+ Cerita Sukses Penerima Beasiswa
+
+
+
+
+
+ βProgram ini sangat membantu saya melanjutkan kuliah di Universitas Udayana. Terima kasih Desa Darmasaba!β
+
+
+ β Ni Kadek Ayu S., Penerima Beasiswa 2024
+
+
+
+
+
+ βSelain bantuan dana, kami juga mendapatkan pelatihan komputer dan bahasa Inggris.β
+
+
+ β I Made Gede A., Penerima Beasiswa 2023
+
+
+
+
+
+ {/* CTA Akhir */}
+
+
+
+ Siap Bergabung dengan Program Ini?
+
+
+ Segera daftar dan wujudkan mimpimu bersama Desa Darmasaba.
+
+
+ Daftar Sekarang
+
+
+
+
+ Formulir Beasiswa
+
+ }
+ >
+
+
+ { beasiswaDesa.create.form.namaLengkap = val.target.value }} />
+ { beasiswaDesa.create.form.nik = val.target.value }} />
+ { beasiswaDesa.create.form.tempatLahir = val.target.value }} />
+ { beasiswaDesa.create.form.tanggalLahir = val.target.value }} />
+ { if (val) beasiswaDesa.create.form.jenisKelamin = val }} />
+ { beasiswaDesa.create.form.kewarganegaraan = val.target.value }} />
+ { if (val) beasiswaDesa.create.form.agama = val }} />
+ { beasiswaDesa.create.form.alamatKTP = val.target.value }} />
+ { beasiswaDesa.create.form.alamatDomisili = val.target.value }} />
+ { beasiswaDesa.create.form.noHp = val.target.value }} />
+ { beasiswaDesa.create.form.email = val.target.value }} />
+ { if (val) beasiswaDesa.create.form.statusPernikahan = val }} />
+ { if (val) beasiswaDesa.create.form.ukuranBaju = val }} />
+
+ Batal
+ Kirim
+
+
+
+
+
+ );
+}
diff --git a/src/app/darmasaba/(pages)/pendidikan/bimbingan-belajar-desa/page.tsx b/src/app/darmasaba/(pages)/pendidikan/bimbingan-belajar-desa/page.tsx
index 85fbf4bf..15b6d004 100644
--- a/src/app/darmasaba/(pages)/pendidikan/bimbingan-belajar-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/bimbingan-belajar-desa/page.tsx
@@ -1,7 +1,7 @@
'use client'
import stateBimbinganBelajarDesa from '@/app/admin/(dashboard)/_state/pendidikan/bimbingan-belajar-desa';
import colors from '@/con/colors';
-import { Box, Paper, SimpleGrid, Skeleton, Stack, Text, Title, Tooltip, Divider, Badge } from '@mantine/core';
+import { Box, Paper, SimpleGrid, Skeleton, Stack, Text, Title, Tooltip, Divider, Badge, Group } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import { IconMapPin, IconCalendarTime, IconBook2 } from '@tabler/icons-react';
@@ -49,47 +49,47 @@ function Page() {
-
-
- Tujuan Program
-
+
-
-
+
+ {stateTujuanProgram.findById.data?.judul}
+
+
+
-
-
- Lokasi & Jadwal
-
+
-
-
+
+ {stateLokasiDanJadwal.findById.data?.judul}
+
+
+
-
-
- Fasilitas
-
+
-
-
+
+ {stateFasilitas.findById.data?.judul}
+
+
+
diff --git a/src/app/darmasaba/(pages)/pendidikan/data-pendidikan/page.tsx b/src/app/darmasaba/(pages)/pendidikan/data-pendidikan/page.tsx
index 0772bd13..8c696b82 100644
--- a/src/app/darmasaba/(pages)/pendidikan/data-pendidikan/page.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/data-pendidikan/page.tsx
@@ -92,7 +92,7 @@ function Page() {
cursor={{ fill: 'var(--mantine-color-gray-1)' }}
/>
-
+
diff --git a/src/app/darmasaba/(pages)/pendidikan/info-sekolah/[jenjangPendidikan]/pengajar/page.tsx b/src/app/darmasaba/(pages)/pendidikan/info-sekolah/[jenjangPendidikan]/pengajar/page.tsx
index 6e67dd47..573ea65e 100644
--- a/src/app/darmasaba/(pages)/pendidikan/info-sekolah/[jenjangPendidikan]/pengajar/page.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/info-sekolah/[jenjangPendidikan]/pengajar/page.tsx
@@ -84,7 +84,7 @@ function Page({ params }: PageProps) {
Nama Pengajar
Nama Lembaga
- Jenjang Pendidikan
+ Mengajar Di Jenjang Pendidikan
@@ -95,7 +95,7 @@ function Page({ params }: PageProps) {
{item.lembaga.jenjangPendidikan?.nama || '-'}
))}
-
+
)}
diff --git a/src/app/darmasaba/(pages)/pendidikan/info-sekolah/_lib/layoutTabs.tsx b/src/app/darmasaba/(pages)/pendidikan/info-sekolah/_lib/layoutTabs.tsx
index cf547483..146953cc 100644
--- a/src/app/darmasaba/(pages)/pendidikan/info-sekolah/_lib/layoutTabs.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/info-sekolah/_lib/layoutTabs.tsx
@@ -1,5 +1,101 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+// 'use client'
+// import colors from '@/con/colors';
+// import {
+// ActionIcon,
+// Box,
+// Button,
+// Container,
+// Group,
+// Paper,
+// Stack,
+// Text,
+// VisuallyHidden
+// } from '@mantine/core';
+// import { IconArrowLeft } from '@tabler/icons-react';
+// import { useRouter, useSearchParams } from 'next/navigation';
+// import React, { useState } from 'react';
+
+// type LayoutSekolahProps = {
+// title?: string;
+// jenjangPendidikanList?: string[];
+// children: React.ReactNode;
+// };
+
+// export default function LayoutSekolah({
+// title = 'Cari Informasi Sekolah',
+// jenjangPendidikanList = ['Semua', 'TK', 'SD', 'SMP', 'SMA'],
+// children,
+// }: LayoutSekolahProps) {
+// const router = useRouter();
+// const searchParams = useSearchParams();
+// const initialJenjangPendidikan = searchParams.get('jenjangPendidikan') || 'Semua';
+
+// const [jenjangPendidikanAktif, setJenjangPendidikanAktif] = useState(initialJenjangPendidikan);
+
+// // Cleanup timeout
+
+
+// // Handle jenjang pendidikan click
+// const handleJenjangPendidikanChange = (k: string) => {
+// // arahkan langsung ke route jenjang pendidikan
+// if (k.toLowerCase() === 'semua') {
+// setJenjangPendidikanAktif(k);
+// router.push(`/darmasaba/pendidikan/info-sekolah/semua`);
+// } else {
+// setJenjangPendidikanAktif(k);
+// router.push(`/darmasaba/pendidikan/info-sekolah/${encodeURIComponent(k.toLowerCase())}`);
+// }
+// };
+
+
+// return (
+//
+//
+//
+// {/* Back Button */}
+// window.history.back()} variant="light" radius="md" size="lg">
+//
+// Kembali
+//
+
+// {/* Search & Filter */}
+//
+//
+// {title}
+
+//
+// Temukan data lengkap mengenai lembaga pendidikan, jumlah siswa terdaftar, dan tenaga pengajar berdasarkan jenjang pendidikan yang tersedia (TK, SD, SMP, SMA). Gunakan tombol di bawah untuk melihat detail sesuai kebutuhanmu.
+//
+//
+// {jenjangPendidikanList.map((k) => {
+// const aktif = k === jenjangPendidikanAktif;
+// return (
+// handleJenjangPendidikanChange(k)}
+// radius="xl"
+// size="sm"
+// variant={aktif ? 'filled' : 'light'}
+// >
+// {k}
+//
+// );
+// })}
+//
+//
+//
+
+// {/* Slot konten */}
+// {children}
+//
+//
+//
+// );
+// }
'use client'
import colors from '@/con/colors';
+ // pastikan path benar
import {
ActionIcon,
Box,
@@ -9,44 +105,48 @@ import {
Paper,
Stack,
Text,
- VisuallyHidden
+ VisuallyHidden,
+ Loader,
} from '@mantine/core';
import { IconArrowLeft } from '@tabler/icons-react';
import { useRouter, useSearchParams } from 'next/navigation';
-import React, { useState } from 'react';
+import { useSnapshot } from 'valtio';
+import React, { useEffect, useState } from 'react';
+import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud';
type LayoutSekolahProps = {
title?: string;
- jenjangPendidikanList?: string[];
children: React.ReactNode;
};
export default function LayoutSekolah({
title = 'Cari Informasi Sekolah',
- jenjangPendidikanList = ['Semua', 'TK', 'SD', 'SMP', 'SMA'],
children,
}: LayoutSekolahProps) {
const router = useRouter();
const searchParams = useSearchParams();
- const initialJenjangPendidikan = searchParams.get('jenjangPendidikan') || 'Semua';
+ const snap = useSnapshot(infoSekolahPaud.jenjangPendidikan);
- const [jenjangPendidikanAktif, setJenjangPendidikanAktif] = useState(initialJenjangPendidikan);
+ const [jenjangPendidikanAktif, setJenjangPendidikanAktif] = useState(
+ searchParams.get('jenjangPendidikan') || 'Semua'
+ );
- // Cleanup timeout
+ // Load jenjang pendidikan dari backend
+ useEffect(() => {
+ if (!snap.findMany.data) infoSekolahPaud.jenjangPendidikan.findMany.load(1, 100);
+ }, []);
-
- // Handle jenjang pendidikan click
- const handleJenjangPendidikanChange = (k: string) => {
- // arahkan langsung ke route jenjang pendidikan
- if (k.toLowerCase() === 'semua') {
- setJenjangPendidikanAktif(k);
- router.push(`/darmasaba/pendidikan/info-sekolah/semua`);
- } else {
- setJenjangPendidikanAktif(k);
- router.push(`/darmasaba/pendidikan/info-sekolah/${encodeURIComponent(k.toLowerCase())}`);
- }
+ const handleJenjangPendidikanChange = (nama: string) => {
+ setJenjangPendidikanAktif(nama);
+ const path =
+ nama.toLowerCase() === 'semua'
+ ? `/darmasaba/pendidikan/info-sekolah/semua`
+ : `/darmasaba/pendidikan/info-sekolah/${encodeURIComponent(nama.toLowerCase())}`;
+ router.push(path);
};
-
+
+ // List tab dari data state
+ const jenjangList = ['Semua', ...(snap.findMany.data?.map((v) => v.nama) || [])];
return (
@@ -61,31 +161,41 @@ export default function LayoutSekolah({
{/* Search & Filter */}
- {title}
-
-
- Temukan data lengkap mengenai lembaga pendidikan, jumlah siswa terdaftar, dan tenaga pengajar berdasarkan jenjang pendidikan yang tersedia (TK, SD, SMP, SMA). Gunakan tombol di bawah untuk melihat detail sesuai kebutuhanmu.
+
+ {title}
-
- {jenjangPendidikanList.map((k) => {
- const aktif = k === jenjangPendidikanAktif;
- return (
- handleJenjangPendidikanChange(k)}
- radius="xl"
- size="sm"
- variant={aktif ? 'filled' : 'light'}
- >
- {k}
-
- );
- })}
-
+
+
+ Temukan data lengkap mengenai lembaga pendidikan, jumlah siswa terdaftar, dan tenaga
+ pengajar berdasarkan jenjang pendidikan (TK, SD, SMP, SMA).
+
+
+ {snap.findMany.loading ? (
+
+
+
+ ) : (
+
+ {jenjangList.map((k) => {
+ const aktif = k === jenjangPendidikanAktif;
+ return (
+ handleJenjangPendidikanChange(k)}
+ radius="xl"
+ size="sm"
+ variant={aktif ? 'filled' : 'light'}
+ >
+ {k}
+
+ );
+ })}
+
+ )}
- {/* Slot konten */}
+ {/* Konten anak */}
{children}
diff --git a/src/app/darmasaba/(pages)/pendidikan/pendidikan-non-formal/page.tsx b/src/app/darmasaba/(pages)/pendidikan/pendidikan-non-formal/page.tsx
index baf99156..4c3b8e45 100644
--- a/src/app/darmasaba/(pages)/pendidikan/pendidikan-non-formal/page.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/pendidikan-non-formal/page.tsx
@@ -1,7 +1,7 @@
'use client'
import pendidikanNonFormalState from '@/app/admin/(dashboard)/_state/pendidikan/pendidikan-non-formal';
import colors from '@/con/colors';
-import { Box, Paper, SimpleGrid, Skeleton, Stack, Text, Title, Tooltip } from '@mantine/core';
+import { Box, Group, Paper, SimpleGrid, Skeleton, Stack, Text, Title, Tooltip } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import { IconMapPin, IconTarget, IconBook2 } from '@tabler/icons-react';
@@ -43,7 +43,7 @@ function Page() {
Pendidikan Non Formal
- Bentuk pendidikan di luar sekolah yang terstruktur, bertujuan memberikan keterampilan, pengetahuan, dan pengembangan karakter masyarakat dari berbagai usia serta latar belakang.
+ Pendidikan non formal merupakan bentuk pendidikan di luar sekolah yang terstruktur, bertujuan untuk memberikan keterampilan, pengetahuan, serta pengembangan karakter masyarakat dari berbagai usia dan latar belakang.
-
-
-
- Tujuan Program
-
-
-
+
+
+
+
+
+
+
+ {stateTujuanPendidikanNonFormal.findById.data?.judul}
+
+
+
-
-
-
- Tempat Kegiatan
-
-
-
+
+
+
+
+
+
+
+ {stateTempatKegiatan.findById.data?.judul}
+
+
+
@@ -95,13 +103,17 @@ function Page() {
withBorder
>
-
-
-
- Jenis Program yang Diselenggarakan
-
-
-
+
+
+
+
+
+
+
+ {stateJenisProgram.findById.data?.judul}
+
+
+
diff --git a/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/[kategoriBuku]/[id]/page.tsx b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/[kategoriBuku]/[id]/page.tsx
new file mode 100644
index 00000000..319f0d64
--- /dev/null
+++ b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/[kategoriBuku]/[id]/page.tsx
@@ -0,0 +1,126 @@
+'use client';
+
+import perpustakaanDigitalState from '@/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital';
+import colors from '@/con/colors';
+import {
+ Badge,
+ Box,
+ Button,
+ Center,
+ Image,
+ Loader,
+ Paper,
+ Stack,
+ Text,
+ Title,
+} from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconArrowLeft, IconBook2 } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useState } from 'react';
+import { useProxy } from 'valtio/utils';
+import ModalPeminjaman from '../../_lib/modalPeminjaman';
+
+export default function DetailBukuUser() {
+ const router = useRouter();
+ const params = useParams();
+ const stateDetail = useProxy(perpustakaanDigitalState.dataPerpustakaan);
+ const [opened, setOpened] = useState(false);
+
+ useShallowEffect(() => {
+ if (params?.id) stateDetail.findUnique.load(params.id as string);
+ }, [params?.id]);
+
+ const data = stateDetail.findUnique.data;
+
+ if (!data) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ {/* Tombol Kembali */}
+ }
+ onClick={() => router.back()}
+ mb="lg"
+ >
+ Kembali
+
+
+
+
+ {/* Cover Buku */}
+
+
+ {/* Judul & Kategori */}
+
+
+ {data.judul}
+
+ {data.kategori?.name && (
+
+ {data.kategori.name}
+
+ )}
+
+
+ {/* Deskripsi Buku */}
+
+
+ Deskripsi Buku
+
+
+
+
+ {/* Tombol Pinjam */}
+ }
+ onClick={() => setOpened(true)}
+ >
+ Pinjam Buku Ini
+
+
+
+
+ {/* Modal Peminjaman */}
+ setOpened(false)}
+ buku={data}
+ />
+
+ );
+}
diff --git a/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/[kategoriBuku]/content.tsx b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/[kategoriBuku]/content.tsx
index 9c41fb3d..349af199 100644
--- a/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/[kategoriBuku]/content.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/[kategoriBuku]/content.tsx
@@ -1,42 +1,52 @@
'use client'
import perpustakaanDigitalState from '@/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital';
import colors from '@/con/colors';
-import { ActionIcon, Box, Center, Group, Image, Paper, SimpleGrid, Skeleton, Spoiler, Stack, Text, Tooltip, Badge } from '@mantine/core';
+import { ActionIcon, Badge, Box, Button, Center, Group, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, Tooltip } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconBook2, IconRefresh } from '@tabler/icons-react';
import { motion } from 'framer-motion';
+import { useTransitionRouter } from 'next-view-transitions';
import { useSearchParams } from 'next/navigation';
import { useCallback, useState } from 'react';
import { useProxy } from 'valtio/utils';
function Content({ kategoriBuku }: { kategoriBuku: string }) {
const state = useProxy(perpustakaanDigitalState);
- const [expandedId, setExpandedId] = useState(null);
const [isLoading, setIsLoading] = useState(true);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [totalPages, setTotalPages] = useState(1);
const searchParams = useSearchParams();
const searchQuery = searchParams.get('search') || '';
+ const router = useTransitionRouter()
const decodedKategoriBuku = decodeURIComponent(kategoriBuku);
- const kategoriFilter = decodedKategoriBuku.toLowerCase() === 'semua' ? '' : decodedKategoriBuku;
- const loadData = useCallback(async (searchQuery: string = '') => {
+ const loadData = useCallback(async (searchQuery: string = '', page: number = 1) => {
try {
setIsLoading(true);
- await state.dataPerpustakaan.findMany.load(1, 100, searchQuery, kategoriFilter);
+ const currentKategoriFilter = decodedKategoriBuku.toLowerCase() === 'semua' ? '' : decodedKategoriBuku;
+ await state.dataPerpustakaan.findMany.load(page, 3, searchQuery, currentKategoriFilter);
+ setCurrentPage(page);
+ setTotalPages(state.dataPerpustakaan.findMany.totalPages);
} finally {
setIsLoading(false);
}
- }, [kategoriFilter, state.dataPerpustakaan.findMany]);
+ }, [state.dataPerpustakaan.findMany, decodedKategoriBuku]);
useShallowEffect(() => {
loadData(searchQuery);
- }, [searchQuery, loadData]);
+ }, [searchQuery, loadData, kategoriBuku]);
const handleRefresh = () => {
loadData();
};
- if (isLoading || !state.dataPerpustakaan.findMany.load || !state.dataPerpustakaan.findMany.data) {
+ const handlePageChange = (newPage: number) => {
+ loadData(searchQuery, newPage);
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ };
+
+ if ((isLoading && !state.dataPerpustakaan.findMany.data) || !state.dataPerpustakaan.findMany.load) {
return (
@@ -57,18 +67,23 @@ function Content({ kategoriBuku }: { kategoriBuku: string }) {
Koleksi Buku
-
-
-
-
-
+
+
+ Halaman {currentPage} dari {totalPages}
+
+
+
+
+
+
+
{!state.dataPerpustakaan.findMany.data || state.dataPerpustakaan.findMany.data.length === 0 ? (
@@ -132,29 +147,23 @@ function Content({ kategoriBuku }: { kategoriBuku: string }) {
)}
-
- Lihat deskripsi
-
- }
- hideLabel={
-
- Sembunyikan deskripsi
-
- }
- expanded={expandedId === v.id}
- onExpandedChange={(isExpanded) => setExpandedId(isExpanded ? v.id : null)}
- >
-
-
+
+ {/* π Tombol Detail */}
+ router.push(`/darmasaba/pendidikan/perpustakaan-digital/${v.kategori?.name}/${v.id}`)}>
+ Lihat Detail
+
@@ -162,6 +171,15 @@ function Content({ kategoriBuku }: { kategoriBuku: string }) {
)}
+
+
+
);
}
diff --git a/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx
index b7b3d489..e61f85ca 100644
--- a/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx
@@ -1,11 +1,24 @@
-'use client'
-import { useEffect, useState } from 'react';
-import { ActionIcon, Box, Flex, Grid, GridCol, Stack, Tabs, TabsList, TabsTab, Text, TextInput } from '@mantine/core';
-import { IconSearch, IconUser } from '@tabler/icons-react';
-import Link from 'next/link';
-import { usePathname, useRouter, useSearchParams } from 'next/navigation';
-import BackButton from '../../../desa/layanan/_com/BackButto';
+'use client';
+
+import perpustakaanDigitalState from '@/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital';
import colors from '@/con/colors';
+import {
+ Box,
+ Grid,
+ GridCol,
+ Stack,
+ Tabs,
+ TabsList,
+ TabsTab,
+ Text,
+ TextInput
+} from '@mantine/core';
+import { IconSearch } from '@tabler/icons-react';
+import { usePathname, useRouter, useSearchParams } from 'next/navigation';
+import { useEffect, useState } from 'react';
+import { useSnapshot } from 'valtio';
+import BackButton from '../../../desa/layanan/_com/BackButto';
+
type LayoutBukuProps = {
placeholder?: string;
@@ -15,7 +28,7 @@ type LayoutBukuProps = {
children?: React.ReactNode;
};
-function LayoutTabs({
+export default function LayoutTabs({
placeholder = 'Cari buku digital...',
searchIcon = ,
children,
@@ -23,6 +36,7 @@ function LayoutTabs({
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
+ const snap = useSnapshot(perpustakaanDigitalState);
const activeTab = pathname.split('/').pop() || 'semua';
const initialSearch = searchParams.get('search') || '';
@@ -30,6 +44,11 @@ function LayoutTabs({
const [searchTimeout, setSearchTimeout] = useState(null);
const [activeTabState, setActiveTabState] = useState(activeTab);
+ // π¦ Ambil kategori buku saat mount
+ useEffect(() => {
+ perpustakaanDigitalState.kategoriBuku.findMany.load();
+ }, []);
+
useEffect(() => {
setActiveTabState(activeTab);
}, [activeTab]);
@@ -51,7 +70,9 @@ function LayoutTabs({
if (value) params.set('search', value);
router.push(
- `/darmasaba/pendidikan/perpustakaan-digital/${activeTab}${params.toString() ? `?${params.toString()}` : ''}`
+ `/darmasaba/pendidikan/perpustakaan-digital/${activeTab}${
+ params.toString() ? `?${params.toString()}` : ''
+ }`
);
};
@@ -63,41 +84,40 @@ function LayoutTabs({
}
};
+ // π© Tabs dinamis berdasarkan kategori dari state
+ const kategoriTabs =
+ snap.kategoriBuku.findMany.data?.map((item) => ({
+ label: item.name,
+ value: item.name.toLowerCase().replace(/\s+/g, '-'),
+ href: `/darmasaba/pendidikan/perpustakaan-digital/${encodeURIComponent(item.name.toLowerCase().replace(/\s+/g, '-'))}`,
+ })) ?? [];
+
const tabs = [
{ label: 'Semua', value: 'semua', href: '/darmasaba/pendidikan/perpustakaan-digital/semua' },
- { label: 'Dokumenter', value: 'dokumenter', href: '/darmasaba/pendidikan/perpustakaan-digital/dokumenter' },
- { label: 'Sayuran', value: 'sayuran', href: '/darmasaba/pendidikan/perpustakaan-digital/sayuran' },
- { label: 'Dongeng', value: 'dongeng', href: '/darmasaba/pendidikan/perpustakaan-digital/dongeng' },
+ ...kategoriTabs,
];
const handleTabChange = (value: string | null) => {
if (!value) return;
const params = new URLSearchParams(searchParams.toString());
- router.push(`/darmasaba/pendidikan/perpustakaan-digital/${value}${params.toString() ? `?${params.toString()}` : ''}`);
+ router.push(
+ `/darmasaba/pendidikan/perpustakaan-digital/${value}${
+ params.toString() ? `?${params.toString()}` : ''
+ }`
+ );
};
return (
-
-
-
-
-
Perpustakaan Digital Darmasaba
+
@@ -116,6 +136,7 @@ function LayoutTabs({
))}
+
+
{children}
);
}
-
-export default LayoutTabs;
diff --git a/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/_lib/modalPeminjaman.tsx b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/_lib/modalPeminjaman.tsx
new file mode 100644
index 00000000..e137e07a
--- /dev/null
+++ b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/_lib/modalPeminjaman.tsx
@@ -0,0 +1,246 @@
+'use client';
+
+import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
+import perpustakaanDigitalState from '@/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital';
+import colors from '@/con/colors';
+import {
+ Badge,
+ Box,
+ Button,
+ Divider,
+ Group,
+ Image,
+ Modal,
+ Stack,
+ Text,
+ TextInput,
+} from '@mantine/core';
+import { DateInput } from '@mantine/dates';
+import { IconArrowRight, IconBook2, IconUser } from '@tabler/icons-react';
+import { useEffect } from 'react';
+import { toast } from 'react-toastify';
+import { useSnapshot } from 'valtio';
+
+export interface ModalPeminjamanProps {
+ opened: boolean;
+ onClose: () => void;
+ buku: {
+ id: string;
+ judul: string;
+ deskripsi?: string;
+ image?: { link?: string };
+ kategori?: { name?: string };
+ } | null;
+}
+
+export default function ModalPeminjaman({
+ opened,
+ onClose,
+ buku,
+}: ModalPeminjamanProps) {
+ const snap = useSnapshot(perpustakaanDigitalState.peminjamanBuku);
+
+ const BATAS_HARI_PINJAM = 4;
+
+ // Reset form setiap modal dibuka
+ useEffect(() => {
+ if (opened && buku) {
+ perpustakaanDigitalState.peminjamanBuku.create.form = {
+ bukuId: buku.id,
+ nama: '',
+ noTelp: '',
+ alamat: '',
+ tanggalPinjam: '',
+ batasKembali: '',
+ tanggalKembali: '',
+ catatan: '',
+ };
+ }
+ }, [opened, buku]);
+
+ const handleSubmit = async () => {
+ if (!buku) return toast.error('Data buku tidak ditemukan');
+ await perpustakaanDigitalState.peminjamanBuku.create.create();
+ onClose();
+ };
+
+ return (
+ Formulir Peminjaman Buku}
+ >
+ {buku ? (
+
+ {/* --- Info Buku --- */}
+
+
+
+
+
+ {buku.judul}
+
+
+ {buku.kategori?.name && (
+
+ {buku.kategori.name}
+
+ )}
+
+
+
+
+
+
+
+ {/* --- Form Input --- */}
+ }
+ value={snap.create.form.nama}
+ onChange={(e) =>
+ (perpustakaanDigitalState.peminjamanBuku.create.form.nama =
+ e.currentTarget.value)
+ }
+ required
+ />
+
+ }
+ value={snap.create.form.noTelp}
+ onChange={(e) =>
+ (perpustakaanDigitalState.peminjamanBuku.create.form.noTelp =
+ e.currentTarget.value)
+ }
+ required
+ />
+
+ }
+ value={snap.create.form.alamat}
+ onChange={(e) =>
+ (perpustakaanDigitalState.peminjamanBuku.create.form.alamat =
+ e.currentTarget.value)
+ }
+ required
+ />
+
+ {/* === OTOMATIS SET BATAS DAN TANGGAL KEMBALI === */}
+ {
+ if (date) {
+ const tanggalPinjam = new Date(date);
+
+ // simpan tanggal pinjam
+ perpustakaanDigitalState.peminjamanBuku.create.form.tanggalPinjam =
+ tanggalPinjam.toISOString();
+
+ // hitung batas +4 hari
+ const batasKembali = new Date(tanggalPinjam);
+ batasKembali.setDate(batasKembali.getDate() + BATAS_HARI_PINJAM);
+
+ // set batas & tanggal kembali otomatis
+ perpustakaanDigitalState.peminjamanBuku.create.form.batasKembali =
+ batasKembali.toISOString();
+ perpustakaanDigitalState.peminjamanBuku.create.form.tanggalKembali =
+ batasKembali.toISOString();
+
+ toast.info(
+ `Batas pengembalian otomatis diset ke ${batasKembali.toLocaleDateString('id-ID')} (+${BATAS_HARI_PINJAM} hari).`
+ );
+ } else {
+ perpustakaanDigitalState.peminjamanBuku.create.form.tanggalPinjam = '';
+ perpustakaanDigitalState.peminjamanBuku.create.form.batasKembali = '';
+ perpustakaanDigitalState.peminjamanBuku.create.form.tanggalKembali = '';
+ }
+ }}
+ required
+ />
+
+
+ Catatan
+
+ (perpustakaanDigitalState.peminjamanBuku.create.form.catatan =
+ val)
+ }
+ />
+
+
+
+
+
+
+ }
+ radius="xl"
+ style={{
+ background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
+ color: '#fff',
+ boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
+ }}
+ >
+ Pinjam Buku
+
+
+ ) : (
+
+ Tidak ada data buku yang dipilih
+
+ )}
+
+ );
+}
diff --git a/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/semua/content.tsx b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/semua/content.tsx
index 40a98660..639bbed0 100644
--- a/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/semua/content.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/semua/content.tsx
@@ -1,45 +1,89 @@
-'use client'
+/* eslint-disable react-hooks/exhaustive-deps */
+'use client';
+
import perpustakaanDigitalState from '@/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital';
import colors from '@/con/colors';
-import { Badge, Box, Center, Group, Image, Paper, SimpleGrid, Skeleton, Spoiler, Stack, Text, Tooltip } from '@mantine/core';
-import { useShallowEffect } from '@mantine/hooks';
-import { motion } from 'framer-motion';
-import { useCallback, useState } from 'react';
-import { useProxy } from 'valtio/utils';
+import {
+ Badge,
+ Box,
+ Button,
+ Center,
+ Group,
+ Image,
+ Paper,
+ SimpleGrid,
+ Skeleton,
+ Stack,
+ Text,
+ Tooltip
+} from '@mantine/core';
import { IconBook2, IconInfoCircle } from '@tabler/icons-react';
+import { Pagination } from '@mantine/core';
+import { motion } from 'framer-motion';
-type ContentProps = {
- searchQuery: string;
-};
+import { useEffect, useState } from 'react';
+import { useSearchParams } from 'next/navigation';
+import { useProxy } from 'valtio/utils';
+import ModalPeminjaman from '../_lib/modalPeminjaman';
+import { useTransitionRouter } from 'next-view-transitions';
-function Content({ searchQuery }: ContentProps) {
+export default function Content() {
const state = useProxy(perpustakaanDigitalState);
- const [expandedId, setExpandedId] = useState(null);
- const [isLoading, setIsLoading] = useState(true);
+ const router = useTransitionRouter()
+ const [opened, setOpened] = useState(false);
+ const searchParams = useSearchParams();
+ const searchQuery = searchParams.get('search') || '';
+ const [currentPage, setCurrentPage] = useState(1);
+
+ const { data: books = [], loading, totalPages } = state.dataPerpustakaan.findMany;
- const loadData = useCallback(
- async (query: string = '') => {
+ const [selectedBook, setSelectedBook] = useState<{
+ id: string;
+ judul: string;
+ deskripsi?: string;
+ image?: { link?: string };
+ kategori?: { name?: string };
+ } | null>(null);
+
+ // Handle data loading and search
+ useEffect(() => {
+ let isMounted = true;
+ const controller = new AbortController();
+
+ const loadData = async () => {
+ if (!isMounted) return;
+
try {
- setIsLoading(true);
- await state.dataPerpustakaan.findMany.load(1, 100, query, '');
+ await state.dataPerpustakaan.findMany.load(
+ currentPage,
+ 3,
+ searchQuery,
+ ''
+ );
} catch (error) {
- console.error('Gagal memuat data:', error);
- } finally {
- setIsLoading(false);
+ if (!controller.signal.aborted) {
+ console.error('Gagal memuat data:', error);
+ }
}
- },
- [state.dataPerpustakaan.findMany]
- );
+ };
- useShallowEffect(() => {
- loadData(searchQuery);
- }, [searchQuery, loadData]);
+ const timer = setTimeout(loadData, 300);
+
+ return () => {
+ isMounted = false;
+ controller.abort();
+ clearTimeout(timer);
+ };
+ }, [searchQuery, currentPage]);
- if (
- isLoading ||
- !state.dataPerpustakaan.findMany.load ||
- !state.dataPerpustakaan.findMany.data
- ) {
+ // Handle page change
+ const handlePageChange = (newPage: number) => {
+ setCurrentPage(newPage);
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ };
+
+ // Loading state
+ if (loading || !books) {
return (
@@ -53,6 +97,7 @@ function Content({ searchQuery }: ContentProps) {
return (
+ {/* πΉ Header */}
@@ -65,100 +110,152 @@ function Content({ searchQuery }: ContentProps) {
- {!state.dataPerpustakaan.findMany.data ||
- state.dataPerpustakaan.findMany.data.length === 0 ? (
+ {/* π Empty State */}
+ {books.length === 0 ? (
-
- Belum ada buku yang tersedia
+
+
+ Belum ada buku yang tersedia
+
) : (
+ // π Daftar Buku
- {state.dataPerpustakaan.findMany.data?.map((v, k) => (
+ {books.map((v) => (
-
-
+
+ {/* πΌ Gambar Buku */}
-
-
-
+
{v.judul}
{v.kategori && (
-
+
{v.kategori.name}
)}
-
- Lihat deskripsi
-
- }
- hideLabel={
-
- Sembunyikan deskripsi
-
- }
- expanded={expandedId === v.id}
- onExpandedChange={(isExpanded) => setExpandedId(isExpanded ? v.id : null)}
- >
-
-
+
+ {/* π Deskripsi */}
+
+
+ {/* π Tombol Detail */}
+ router.push(`/darmasaba/pendidikan/perpustakaan-digital/${v.kategori?.name}/${v.id}`)}>
+ Lihat Detail
+
+
+ {/* π Tombol Peminjaman */}
+ }
+ onClick={() => {
+ setSelectedBook(v);
+ setOpened(true);
+ }}
+ >
+ Peminjaman
+
))}
)}
+
+
+
+
+
+
+ {/* πΈ Modal Peminjaman */}
+ {
+ setOpened(false);
+ setSelectedBook(null);
+ }}
+ buku={selectedBook}
+ />
);
}
-
-export default Content;
diff --git a/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/semua/page.tsx b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/semua/page.tsx
index 0236b8cd..ab7bb140 100644
--- a/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/semua/page.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/perpustakaan-digital/semua/page.tsx
@@ -1,13 +1,13 @@
// src/app/darmasaba/(pages)/desa/berita/[kategori]/page.tsx
import { Suspense } from "react";
-import Content from "./content";
+import Content from "../[kategoriBuku]/content";
export default async function Page() {
return (
Loading...}>
-
+
);
}
\ No newline at end of file
diff --git a/src/app/darmasaba/(pages)/pendidikan/program-pendidikan-anak/page.tsx b/src/app/darmasaba/(pages)/pendidikan/program-pendidikan-anak/page.tsx
index 3dcac57c..cb6b4f90 100644
--- a/src/app/darmasaba/(pages)/pendidikan/program-pendidikan-anak/page.tsx
+++ b/src/app/darmasaba/(pages)/pendidikan/program-pendidikan-anak/page.tsx
@@ -62,11 +62,11 @@ function Page() {
- Tujuan Program
+ {stateTujuan.findById.data?.judul}
-
+
@@ -83,11 +83,11 @@ function Page() {
- Program Unggulan
+ {stateUnggulan.findById.data?.judul}
-
+
diff --git a/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/page.tsx b/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/page.tsx
index 42b22bec..23c2cee6 100644
--- a/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik-desa-darmasaba/[id]/page.tsx
@@ -118,6 +118,7 @@ export default function DetailInformasiPublikUser() {
diff --git a/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik-desa-darmasaba/page.tsx b/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik-desa-darmasaba/page.tsx
index 1465f011..be5934c8 100644
--- a/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik-desa-darmasaba/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik-desa-darmasaba/page.tsx
@@ -125,7 +125,7 @@ function Page() {
-
+
diff --git a/src/app/darmasaba/(pages)/ppid/dasar-hukum/page.tsx b/src/app/darmasaba/(pages)/ppid/dasar-hukum/page.tsx
index fd0653b1..b9d64d1f 100644
--- a/src/app/darmasaba/(pages)/ppid/dasar-hukum/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/dasar-hukum/page.tsx
@@ -42,7 +42,7 @@ function Page() {
>
Dasar Hukum
-
+
Informasi regulasi dan kebijakan resmi yang menjadi dasar hukum
@@ -74,12 +74,11 @@ function Page() {
fw="bold"
fz={{ base: 'lg', md: 'xl' }}
style={{ lineHeight: 1.4 }}
- >
- {item.judul}
-
+ dangerouslySetInnerHTML={{ __html: item.judul }}
+ />
diff --git a/src/app/darmasaba/(pages)/ppid/ikm-desa-darmasaba/page.tsx b/src/app/darmasaba/(pages)/ppid/ikm-desa-darmasaba/page.tsx
index ab39a966..9e134b39 100644
--- a/src/app/darmasaba/(pages)/ppid/ikm-desa-darmasaba/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/ikm-desa-darmasaba/page.tsx
@@ -3,8 +3,8 @@
import indeksKepuasanState from "@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan";
import colors from "@/con/colors";
import { BarChart, PieChart } from '@mantine/charts';
-import { Box, Button, Center, Container, Flex, Group, Modal, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from "@mantine/core";
-import { useDisclosure, useMediaQuery, useShallowEffect } from "@mantine/hooks";
+import { Box, Button, Center, Container, Flex, Modal, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from "@mantine/core";
+import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { useState } from "react";
import { useProxy } from "valtio/utils";
@@ -18,14 +18,13 @@ interface ChartDataItem {
function Kepuasan() {
- const state = useProxy(indeksKepuasanState.responden);
+const state = useProxy(indeksKepuasanState.responden);
const { data, loading } = state.findMany;
const [donutDataJenisKelamin, setDonutDataJenisKelamin] = useState([]);
const [donutDataRating, setDonutDataRating] = useState([]);
const [donutDataKelompokUmur, setDonutDataKelompokUmur] = useState([]);
- const [barChartData, setBarChartData] = useState>([]);
- const [opened, { open, close }] = useDisclosure(false);
- const isMobile = useMediaQuery("(max-width: 768px)");
+ const [barChartData, setBarChartData] = useState>([]);
+ const [opened, { open, close }] = useDisclosure(false)
const resetForm = () => {
state.create.form = {
@@ -122,18 +121,18 @@ function Kepuasan() {
// Convert map to array and sort by date
const barData = Array.from(monthYearMap.entries())
- .map(([key, count]) => {
+ .map(([key, Responden]) => {
const [year, month] = key.split('-');
const monthName = new Date(Number(year), Number(month) - 1, 1)
.toLocaleString('id-ID', { month: 'long' });
return {
month: `${monthName} ${year}`,
- count,
+ Responden,
sortKey: parseInt(`${year}${String(month).padStart(2, '0')}`, 10)
};
})
.sort((a, b) => a.sortKey - b.sortKey)
- .map(({ month, count }) => ({ month, count }));
+ .map(({ month, Responden }) => ({ month, Responden }));
setBarChartData(barData);
}
@@ -141,12 +140,12 @@ function Kepuasan() {
if ((loading && !data) || !data) {
return (
-
-
-
-
-
-
+
+
+
+
+
+
);
@@ -157,10 +156,16 @@ function Kepuasan() {
- Indeks Kepuasan Masyarakat
+ Indeks Kepuasan Masyarakat
+ Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!
- Ajukan Responden
+ Ajukan Responden
@@ -177,10 +182,10 @@ function Kepuasan() {
{/* Chart Jenis Kelamin */}
@@ -215,7 +217,7 @@ function Kepuasan() {
withLabels
withTooltip
labelsType="percent"
- size={200}
+ size={250} // Fixed size in pixels
data={donutDataJenisKelamin}
/>
@@ -254,7 +256,7 @@ function Kepuasan() {
labelsPosition="outside"
labelsType="percent"
withLabelsLine
- size={200}
+ size={250}
data={donutDataRating}
/>
@@ -297,7 +299,7 @@ function Kepuasan() {
labelsPosition="outside"
labelsType="percent"
withLabelsLine
- size={190}
+ size={250}
data={donutDataKelompokUmur}
/>
@@ -330,7 +332,7 @@ function Kepuasan() {
{
state.create.form.name = val.currentTarget.value;
@@ -413,41 +415,57 @@ function Kepuasan() {
);
}
return (
-
-
-
- Indeks Kepuasan Masyarakat
-
-
- Ajukan Responden
-
-
-
+
+
+
+ Indeks Kepuasan Masyarakat
+
+ Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!
+
+ Ajukan Responden
+
-
-
-
-
-
- Pelayanan Terhadap Publik Desa Darmasaba
-
- Total Responden
-
- {state.findMany.total.toLocaleString("id-ID")}
+
+
+
+
+
+
+ Pelayanan Terhadap Publik Desa Darmasaba
+
+
+ Total Responden
+
+ {state.findMany.total.toLocaleString('id-ID')}
-
-
+
+
{/* Chart Jenis Kelamin */}
@@ -457,17 +475,28 @@ function Kepuasan() {
Belum ada data untuk ditampilkan dalam grafik
) : (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ {donutDataJenisKelamin.map((entry) => (
+
+
+ {entry.name}: {entry.value}
+
+ ))}
+
+
)}
@@ -482,18 +511,35 @@ function Kepuasan() {
Belum ada data untuk ditampilkan dalam grafik
) : (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {donutDataRating.map((entry) => (
+
+
+
+ {entry.name}: {entry.value}
+
+
+ ))}
+
+
+
)}
@@ -508,18 +554,35 @@ function Kepuasan() {
Belum ada data untuk ditampilkan dalam grafik
) : (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {donutDataKelompokUmur.map((entry) => (
+
+
+
+ {entry.name}: {entry.value}
+
+
+ ))}
+
+
+
)}
@@ -536,16 +599,16 @@ function Kepuasan() {
label="Nama"
type='text'
placeholder="masukkan nama"
- value={state.create.form.name}
+ defaultValue={state.create.form.name}
onChange={(val) => {
state.create.form.name = val.currentTarget.value;
}}
/>
{
state.create.form.tanggal = val.currentTarget.value;
}}
@@ -554,7 +617,7 @@ function Kepuasan() {
key={"jenisKelamin"}
label={"Jenis Kelamin"}
placeholder={indeksKepuasanState.jenisKelaminResponden.findMany.loading ? 'Memuat...' : 'Pilih jenis kelamin'}
- value={state.create.form.jenisKelaminId || ""}
+ defaultValue={state.create.form.jenisKelaminId || ""}
onChange={(val) => {
state.create.form.jenisKelaminId = val ?? "";
}}
@@ -572,7 +635,7 @@ function Kepuasan() {
key={"rating_responden"}
label={"Rating"}
placeholder={indeksKepuasanState.pilihanRatingResponden.findMany.loading ? 'Memuat...' : 'Pilih rating'}
- value={state.create.form.ratingId || ""}
+ defaultValue={state.create.form.ratingId || ""}
onChange={(val) => {
state.create.form.ratingId = val ?? "";
}}
@@ -590,7 +653,7 @@ function Kepuasan() {
key={"kelompokUmur"}
label={"Kelompok Umur"}
placeholder={indeksKepuasanState.kelompokUmurResponden.findMany.loading ? 'Memuat...' : 'Pilih kelompok umur'}
- value={state.create.form.kelompokUmurId || ""}
+ defaultValue={state.create.form.kelompokUmurId || ""}
onChange={(val) => {
state.create.form.kelompokUmurId = val ?? "";
}}
diff --git a/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/jenis_infromasi/jenisInformasiSelector.tsx b/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/jenis_infromasi/jenisInformasiSelector.tsx
index 3b81266f..95014e2c 100644
--- a/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/jenis_infromasi/jenisInformasiSelector.tsx
+++ b/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/jenis_infromasi/jenisInformasiSelector.tsx
@@ -32,7 +32,7 @@ export default function JenisInformasiSelector({ onChange }: {
return (
({
value: item.id,
diff --git a/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/memperoleh_informasi/memperolehInfromasi.tsx b/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/memperoleh_informasi/memperolehInfromasi.tsx
index e521caf0..e7cad592 100644
--- a/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/memperoleh_informasi/memperolehInfromasi.tsx
+++ b/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/memperoleh_informasi/memperolehInfromasi.tsx
@@ -28,7 +28,7 @@ function MemperolehInformasi({ onChange }: {
return (
({
value: item.id,
diff --git a/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx b/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx
index 66068037..5a47ba67 100644
--- a/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx
@@ -12,10 +12,9 @@ import {
SimpleGrid,
Stack,
Text,
- TextInput,
- Tooltip,
+ TextInput
} from '@mantine/core';
-import { IconDownload, IconSend2 } from '@tabler/icons-react';
+import { IconSend2 } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
@@ -150,23 +149,6 @@ function Page() {
))}
-
-
-
-
- }
- >
- Unduh Tata Cara
-
-
-
-
({
value: item.id,
diff --git a/src/app/darmasaba/(pages)/ppid/permohonan-keberatan-informasi-publik/page.tsx b/src/app/darmasaba/(pages)/ppid/permohonan-keberatan-informasi-publik/page.tsx
index c771b04e..4ef775be 100644
--- a/src/app/darmasaba/(pages)/ppid/permohonan-keberatan-informasi-publik/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/permohonan-keberatan-informasi-publik/page.tsx
@@ -178,7 +178,7 @@ function Page() {
-
-
-
-
-
- Profil PPID Desa Darmasaba
-
-
- {dataArray.map((item) => (
-
-
-
-
-
-
- Pejabat Pengelola Informasi Publik
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {item.name}
-
-
-
-
-
-
-
-
-
-
-
- Biografi
-
-
-
-
-
-
- Riwayat Karir
-
-
-
-
-
-
-
-
-
-
-
- Pengalaman Organisasi
-
-
-
-
-
-
-
-
-
-
-
- Program Unggulan
-
-
-
-
-
-
-
-
+
+
+
+
- ))}
-
+
+
+ Profil PPID Desa Darmasaba
+
+
+ {dataArray.map((item) => (
+
+
+
+
+
+
+
+ Pejabat Pengelola Informasi dan Dokumentasi
+
+
+
+
+
+
+
+
+
+ e.currentTarget.src = "/perbekel.png"}
+ loading="lazy"
+ />
+
+
+ {item.name}
+
+
+
+
+
+
+
+
+
+
+
+ Biografi
+
+
+
+
+
+
+ Riwayat Karir
+
+
+
+
+
+
+
+
+
+
+
+ Pengalaman Organisasi
+
+
+
+
+
+
+
+
+
+
+
+ Program Unggulan
+
+
+
+
+
+
+
+
+
+ ))}
+
+ {/* Tombol Scroll ke Atas */}
+
+
)
}
diff --git a/src/app/darmasaba/(pages)/ppid/struktur-ppid/[id]/page.tsx b/src/app/darmasaba/(pages)/ppid/struktur-ppid/[id]/page.tsx
new file mode 100644
index 00000000..36a02bc3
--- /dev/null
+++ b/src/app/darmasaba/(pages)/ppid/struktur-ppid/[id]/page.tsx
@@ -0,0 +1,157 @@
+'use client';
+import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID';
+import colors from '@/con/colors';
+import {
+ Box,
+ Divider,
+ Group,
+ Image,
+ Paper,
+ Skeleton,
+ Stack,
+ Text,
+ Title,
+} from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
+import { IconArrowBack } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useProxy } from 'valtio/utils';
+
+function DetailPegawaiUser() {
+ const statePegawai = useProxy(stateStrukturPPID.pegawai);
+ const params = useParams();
+ const router = useRouter();
+
+ useShallowEffect(() => {
+ stateStrukturPPID.posisiOrganisasi.findMany.load();
+ statePegawai.findUnique.load(params?.id as string);
+ }, []);
+
+
+ if (!statePegawai.findUnique.data) {
+ return (
+
+
+
+ );
+ }
+
+ const data = statePegawai.findUnique.data;
+
+ return (
+
+ {/* Back button */}
+
+ router.back()}
+ style={{
+ cursor: 'pointer',
+ display: 'flex',
+ alignItems: 'center',
+ gap: 8,
+ }}
+ >
+
+
+ Kembali
+
+
+
+
+
+
+ {/* Foto Profil */}
+
+
+ {/* Nama & Jabatan */}
+
+
+ {data.namaLengkap || '-'} {data.gelarAkademik || ''}
+
+
+ {data.posisi?.nama || 'Posisi tidak tersedia'}
+
+
+
+
+
+
+ {/* Informasi Detail */}
+
+
+
+
+
+
+
+
+
+ );
+}
+
+/* Komponen kecil untuk menampilkan baris informasi */
+function InfoRow({
+ label,
+ value,
+ valueColor,
+ multiline = false,
+}: {
+ label: string;
+ value?: string | null;
+ valueColor?: string;
+ multiline?: boolean;
+}) {
+ return (
+
+
+ {label}
+
+
+ {value || '-'}
+
+
+ );
+}
+
+export default DetailPegawaiUser;
diff --git a/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx b/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx
index 6c3d3b05..17637cb4 100644
--- a/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx
@@ -1,129 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable @typescript-eslint/no-explicit-any */
-// /* eslint-disable react-hooks/exhaustive-deps */
-// /* eslint-disable @typescript-eslint/no-explicit-any */
-// 'use client'
-// import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID';
-// import colors from '@/con/colors';
-// import { Box, Image, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
-// import { OrganizationChart } from 'primereact/organizationchart';
-// import { useEffect } from 'react';
-// import { useProxy } from 'valtio/utils';
-// import BackButton from '../../desa/layanan/_com/BackButto';
-
-// function Page() {
-// return (
-//
-//
-//
-//
-// Struktur PPID
-//
-
-//
-// );
-// }
-
-// function StrukturOrganisasiPPID() {
-// const stateOrganisasi = useProxy(stateStrukturPPID.pegawai)
-
-// useEffect(() => {
-// stateOrganisasi.findMany.load()
-// }, [])
-
-// if (!stateOrganisasi.findMany.data || stateOrganisasi.findMany.data.length === 0) {
-// return (
-//
-//
-//
-// );
-// }
-
-// // Step 1: Group pegawai berdasarkan posisiId
-// const posisiMap = new Map();
-
-// for (const pegawai of stateOrganisasi.findMany.data) {
-// const posisiId = pegawai.posisi.id;
-// if (!posisiMap.has(posisiId)) {
-// posisiMap.set(posisiId, {
-// ...pegawai.posisi,
-// pegawaiList: [],
-// children: []
-// });
-// }
-// posisiMap.get(posisiId)!.pegawaiList.push(pegawai);
-// }
-
-
-// // Step 2: Buat struktur pohon berdasarkan parentId
-// const root: any[] = [];
-
-// posisiMap.forEach((posisi) => {
-// if (posisi.parentId) {
-// const parent = posisiMap.get(posisi.parentId);
-// if (parent) {
-// parent.children.push(posisi);
-// }
-// } else {
-// root.push(posisi);
-// }
-// });
-
-// // Step 3: Ubah struktur ke format OrganizationChart
-// function toOrgChartFormat(node: any): any {
-// return {
-// expanded: true,
-// type: 'person',
-// styleClass: 'p-person',
-// data: {
-// name: node.pegawaiList?.[0]?.namaLengkap || 'Tidak ada pegawai',
-// status: node.nama,
-// image: node.pegawaiList?.[0]?.image?.link || '/img/default.png'
-// },
-// children: node.children.map(toOrgChartFormat)
-// };
-// }
-
-
-// const chartData = root.map(toOrgChartFormat);
-
-// return (
-//
-//
-//
-//
-//
-// );
-// }
-
-
-// function nodeTemplate(node: any) {
-// const imageSrc = node?.data?.image || '/img/default.png';
-// const name = node?.data?.name || 'Tanpa Nama';
-// const status = node?.data?.status || 'Tidak ada deskripsi';
-
-// return (
-//
-//
-//
-// {name}
-// {status}
-//
-//
-// );
-// }
-
-// export default Page;
-
'use client'
+
import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID'
+import ScrollToTopButton from '@/app/darmasaba/_com/scrollToTopButton'
+import colors from '@/con/colors'
import {
Box,
Button,
@@ -136,16 +17,25 @@ import {
Paper,
Stack,
Text,
+ TextInput,
Title,
- Tooltip,
- Transition,
+ Transition
} from '@mantine/core'
-import { IconRefresh, IconSearch, IconUsers } from '@tabler/icons-react'
+import {
+ IconArrowsMaximize,
+ IconArrowsMinimize,
+ IconRefresh,
+ IconSearch,
+ IconUsers,
+ IconZoomIn,
+ IconZoomOut,
+} from '@tabler/icons-react'
+import { debounce } from 'lodash'
+import { useTransitionRouter } from 'next-view-transitions'
import { OrganizationChart } from 'primereact/organizationchart'
-import { useEffect } from 'react'
+import { useEffect, useRef, useState } from 'react'
import { useProxy } from 'valtio/utils'
import BackButton from '../../desa/layanan/_com/BackButto'
-import colors from '@/con/colors'
export default function Page() {
return (
@@ -167,7 +57,6 @@ export default function Page() {
ta="center"
c={colors['blue-button']}
fz={{ base: 28, md: 36, lg: 44 }}
-
>
Struktur Organisasi PPID
@@ -180,20 +69,34 @@ export default function Page() {
+
+ {/* Tombol Scroll ke Atas */}
+
)
}
function StrukturOrganisasiPPID() {
const stateOrganisasi: any = useProxy(stateStrukturPPID.pegawai)
+ const router = useTransitionRouter()
+ const chartContainerRef = useRef(null)
+ const [scale, setScale] = useState(1)
+ const [isFullscreen, setFullscreen] = useState(false)
+ const [searchQuery, setSearchQuery] = useState('')
+
+ // debounce untuk pencarian
+ const debouncedSearch = useRef(
+ debounce((value: string) => {
+ setSearchQuery(value)
+ }, 400)
+ ).current
useEffect(() => {
void stateOrganisasi.findMany.load()
}, [])
const isLoading =
- !stateOrganisasi.findMany.data &&
- stateOrganisasi.findMany.loading !== false
+ !stateOrganisasi.findMany.data && stateOrganisasi.findMany.loading !== false
if (isLoading) {
return (
@@ -209,10 +112,7 @@ function StrukturOrganisasiPPID() {
)
}
- if (
- !stateOrganisasi.findMany.data ||
- stateOrganisasi.findMany.data.length === 0
- ) {
+ if (!stateOrganisasi.findMany.data || stateOrganisasi.findMany.data.length === 0) {
return (
@@ -233,8 +133,7 @@ function StrukturOrganisasiPPID() {
Data pegawai belum tersedia
- Belum ada data pegawai yang tercatat untuk PPID. Silakan coba
- muat ulang atau periksa sumber data.
+ Belum ada data pegawai yang tercatat untuk PPID.
Muat Ulang
- }
- variant="subtle"
- onClick={() =>
- stateOrganisasi.findMany.load({ query: { q: '' } })
- }
- >
- Cari Pegawai
-
@@ -261,8 +151,11 @@ function StrukturOrganisasiPPID() {
)
}
+ // Buat struktur organisasi
const posisiMap = new Map()
- for (const pegawai of stateOrganisasi.findMany.data) {
+ const aktifPegawai = stateOrganisasi.findMany.data.filter((p: any) => p.isActive)
+
+ for (const pegawai of aktifPegawai) {
const posisiId = pegawai.posisi.id
if (!posisiMap.has(posisiId)) {
posisiMap.set(posisiId, {
@@ -278,25 +171,22 @@ function StrukturOrganisasiPPID() {
posisiMap.forEach((posisi) => {
if (posisi.parentId) {
const parent = posisiMap.get(posisi.parentId)
- if (parent) {
- parent.children.push(posisi)
- } else {
- root.push(posisi)
- }
- } else {
- root.push(posisi)
- }
+ if (parent) parent.children.push(posisi)
+ else root.push(posisi)
+ } else root.push(posisi)
})
function toOrgChartFormat(node: any): any {
+ const pegawai = node.pegawaiList?.[0]
return {
expanded: true,
type: 'person',
styleClass: 'p-person',
data: {
- name: node.pegawaiList?.[0]?.namaLengkap || 'Belum ditugaskan',
+ id: pegawai?.id || null,
+ name: pegawai?.namaLengkap || 'Belum ditugaskan',
title: node.nama || 'Tanpa jabatan',
- image: node.pegawaiList?.[0]?.image?.link || '/img/default.png',
+ image: pegawai?.image?.link || '/img/default.png',
description: node.deskripsi || '',
positionId: node.id || null,
},
@@ -304,36 +194,120 @@ function StrukturOrganisasiPPID() {
}
}
- const chartData = root.map(toOrgChartFormat)
+ let chartData = root.map(toOrgChartFormat)
+
+ // π filter by search
+ if (searchQuery) {
+ const filterNodes = (nodes: any[]): any[] =>
+ nodes
+ .map((n) => ({
+ ...n,
+ children: filterNodes(n.children || []),
+ }))
+ .filter(
+ (n) =>
+ n.data.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ n.data.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ n.children.length > 0
+ )
+ chartData = filterNodes(chartData)
+ }
+
+ // π§ fungsi fullscreen
+ const toggleFullscreen = () => {
+ if (!document.fullscreenElement) {
+ chartContainerRef.current?.requestFullscreen()
+ setFullscreen(true)
+ } else {
+ document.exitFullscreen()
+ setFullscreen(false)
+ }
+ }
+
+ // π§ fungsi zoom
+ const handleZoomIn = () => setScale((prev) => Math.min(prev + 0.1, 2))
+ const handleZoomOut = () => setScale((prev) => Math.max(prev - 0.1, 0.5))
+ const resetZoom = () => setScale(1)
return (
-
-
+ {/* π Search + Zoom + Fullscreen controls */}
+
+ }
+ onChange={(e) => debouncedSearch(e.target.value)}
+ />
+
+
+
+
+
+ {/* π Tambahkan indikator zoom di sini */}
+ {/* Floating Zoom Indicator */}
+
+ {Math.round(scale * 100)}%
+
+
+
+
+
+
+
+
+ Reset
+
+
+ :
+ }
+ >
+ {isFullscreen ? 'Keluar' : 'Fullscreen'}
+
+
+
+
+ {/* Chart Container */}
+
nodeTemplate(node, router)}
/>
-
-
+
+
)
}
-function nodeTemplate(node: any) {
+function nodeTemplate(node: any, router: ReturnType) {
const imageSrc = node?.data?.image || '/img/default.png'
const name = node?.data?.name || 'Tanpa Nama'
const title = node?.data?.title || 'Tanpa Jabatan'
const description = node?.data?.description || ''
return (
-
+
{(styles) => (
{name}
@@ -371,26 +345,19 @@ function nodeTemplate(node: any) {
{description || 'Belum ada deskripsi.'}
-
{
- const id = node?.data?.positionId
- if (id && (window as any).scrollTo) {
- ;(window as any).scrollTo({ top: 0, behavior: 'smooth' })
- }
+ const id = node?.data?.id
+ router.push(`/darmasaba/ppid/struktur-ppid/${id}`)
}}
>
- Detail
+ Lihat Detail
-
)}
)
}
-
-
-
diff --git a/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx b/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx
index 5047e88a..85e50080 100644
--- a/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx
@@ -54,12 +54,11 @@ function Page() {
ta="center"
fz={{ base: 28, md: 36 }}
fw={800}
- variant="gradient"
- gradient={{ from: colors['blue-button'], to: 'cyan', deg: 45 }}
+ c={colors['blue-button']}
>
Moto PPID Desa Darmasaba
-
+
Memberikan informasi yang cepat, mudah, tepat, dan transparan
@@ -67,28 +66,31 @@ function Page() {
} />
-
+
Visi PPID
-
+
Misi PPID
diff --git a/src/app/darmasaba/(tambahan)/penghargaan/[id]/page.tsx b/src/app/darmasaba/(tambahan)/penghargaan/[id]/page.tsx
index 28cf98f7..3168a9bc 100644
--- a/src/app/darmasaba/(tambahan)/penghargaan/[id]/page.tsx
+++ b/src/app/darmasaba/(tambahan)/penghargaan/[id]/page.tsx
@@ -85,6 +85,7 @@ function Page() {
fz="md"
lh={1.6}
dangerouslySetInnerHTML={{ __html: state.findUnique.data?.deskripsi || '' }}
+ style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
diff --git a/src/app/darmasaba/(tambahan)/penghargaan/page.tsx b/src/app/darmasaba/(tambahan)/penghargaan/page.tsx
index 948cadbb..7111c9ac 100644
--- a/src/app/darmasaba/(tambahan)/penghargaan/page.tsx
+++ b/src/app/darmasaba/(tambahan)/penghargaan/page.tsx
@@ -2,11 +2,11 @@
'use client';
import penghargaanState from "@/app/admin/(dashboard)/_state/desa/penghargaan";
import colors from "@/con/colors";
-import { Carousel, CarouselSlide } from "@mantine/carousel";
-import { Box, Button, Container, Group, Paper, Stack, Text, useMantineTheme, Skeleton } from "@mantine/core";
+import { Carousel } from "@mantine/carousel";
+import { Box, Button, Container, Group, Paper, Skeleton, Stack, Text, useMantineTheme } from "@mantine/core";
import { useMediaQuery } from "@mantine/hooks";
+import { IconArrowRight, IconAward } from "@tabler/icons-react";
import Autoplay from "embla-carousel-autoplay";
-import { IconAward, IconArrowRight } from "@tabler/icons-react";
import { useTransitionRouter } from "next-view-transitions";
import { useEffect, useRef } from "react";
import { useProxy } from "valtio/utils";
@@ -18,7 +18,8 @@ export default function Page() {
-
+
+
@@ -37,11 +38,10 @@ export default function Page() {
}
function Slider() {
- const height = 500;
- const width = 1200;
const theme = useMantineTheme();
const mobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`);
- const autoplay = useRef(Autoplay({ delay: 3000 }));
+ const tablet = useMediaQuery(`(max-width: ${theme.breakpoints.md})`);
+ const autoplay = useRef(Autoplay({ delay: 3000, stopOnInteraction: false }));
const state = useProxy(penghargaanState);
const router = useTransitionRouter();
@@ -54,7 +54,7 @@ function Slider() {
if (loading) {
return (
-
+
@@ -74,31 +74,49 @@ function Slider() {
}
const slides = data.map((item) => (
-
+
{
+ e.currentTarget.style.transform = "translateY(-4px)";
+ e.currentTarget.style.boxShadow = "0 8px 20px rgba(0,0,0,0.2)";
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.transform = "translateY(0)";
+ e.currentTarget.style.boxShadow = "none";
}}
>
-
+
{item.name}
router.push(`/darmasaba/penghargaan/${item.id}`)}
+ onClick={() =>
+ router.push(`/darmasaba/penghargaan/${item.id}`)
+ }
size="md"
radius="xl"
rightSection={ }
@@ -110,24 +128,83 @@ function Slider() {
-
+
));
return (
-
- {slides}
-
+ 3}
+ draggable={data.length > 1}
+ styles={{
+ root: {
+ position: "relative",
+ },
+ viewport: {
+ overflow: "hidden",
+ },
+ container: {
+ alignItems: "stretch",
+ },
+ control: {
+ zIndex: 20,
+ backgroundColor: "rgba(255,255,255,0.95)",
+ color: colors["blue-button"],
+ border: `2px solid ${colors["blue-button"]}`,
+ width: 46,
+ height: 46,
+ borderRadius: "50%",
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
+ transition: "all 0.2s ease",
+ '&:hover': {
+ backgroundColor: colors["blue-button"],
+ color: "white",
+ transform: "scale(1.1)",
+ },
+ '&[data-inactive]': {
+ opacity: 0,
+ cursor: 'default',
+ },
+ },
+ controls: {
+ position: "absolute",
+ top: mobile ? "70%" : tablet ? "65%" : "60%",
+ transform: "translateY(-50%)",
+ width: mobile ? "100%" : tablet ? "calc(100% + 60px)" : "calc(100% + 100px)",
+ left: mobile ? "0" : tablet ? "-30px" : "-50px",
+ right: mobile ? "0" : tablet ? "-30px" : "-50px",
+ padding: "0",
+ justifyContent: "space-between",
+ zIndex: 30,
+ },
+ }}
+ >
+ {slides}
+
+
);
-}
+}
\ No newline at end of file
diff --git a/src/app/darmasaba/(tambahan)/prestasi-desa/[id]/page.tsx b/src/app/darmasaba/(tambahan)/prestasi-desa/[id]/page.tsx
index 32609b13..3e345157 100644
--- a/src/app/darmasaba/(tambahan)/prestasi-desa/[id]/page.tsx
+++ b/src/app/darmasaba/(tambahan)/prestasi-desa/[id]/page.tsx
@@ -76,7 +76,7 @@ function Page() {
/>
)}
-
+
Dibuat pada: {new Date(state.findUnique.data.createdAt).toLocaleDateString('id-ID')}
diff --git a/src/app/darmasaba/(tambahan)/prestasi-desa/page.tsx b/src/app/darmasaba/(tambahan)/prestasi-desa/page.tsx
index c25ba552..cb4c10d8 100644
--- a/src/app/darmasaba/(tambahan)/prestasi-desa/page.tsx
+++ b/src/app/darmasaba/(tambahan)/prestasi-desa/page.tsx
@@ -91,19 +91,17 @@ function Page() {
diff --git a/src/app/darmasaba/(tambahan)/program-inovasi/[id]/page.tsx b/src/app/darmasaba/(tambahan)/program-inovasi/[id]/page.tsx
index d44d66cf..2eda6f24 100644
--- a/src/app/darmasaba/(tambahan)/program-inovasi/[id]/page.tsx
+++ b/src/app/darmasaba/(tambahan)/program-inovasi/[id]/page.tsx
@@ -88,6 +88,7 @@ function Page() {
mt="sm"
lh={1.7}
dangerouslySetInnerHTML={{ __html: data?.description || '-' }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
diff --git a/src/app/darmasaba/_com/Footer.tsx b/src/app/darmasaba/_com/Footer.tsx
index 9076b82b..4a7c63f7 100644
--- a/src/app/darmasaba/_com/Footer.tsx
+++ b/src/app/darmasaba/_com/Footer.tsx
@@ -1,8 +1,100 @@
'use client'
import { ActionIcon, Anchor, Box, Button, Center, Container, Divider, Flex, Group, Image, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
-import { IconAt, IconBrandFacebook, IconBrandInstagram, IconBrandTwitter, IconBrandWhatsapp } from '@tabler/icons-react';
+import { IconAt, IconBrandFacebook, IconBrandInstagram, IconBrandTiktok, IconBrandYoutube } from '@tabler/icons-react';
+import { useRef } from 'react';
+import { toast } from 'react-toastify';
+
+const sosialMedia = [
+ {
+ title: "Facebook",
+ link: "https://www.facebook.com/DarmasabaDesaku",
+ icon: IconBrandFacebook,
+ },
+ {
+ title: "Instagram",
+ link: "https://www.instagram.com/ddarmasaba/",
+ icon: IconBrandInstagram,
+ },
+ {
+ title: "Youtube",
+ link: "https://www.youtube.com/channel/UCtPw9WOQO7d2HIKzKgel4Xg",
+ icon: IconBrandYoutube,
+ },
+ {
+ title: "Tiktok",
+ link: "https://www.tiktok.com/@desa.darmasaba?is_from_webapp=1&sender_device=pc",
+ icon: IconBrandTiktok,
+ },
+]
+
+const layanandesa = [
+ {
+ title: "Administrasi Kependudukan",
+ link: "/darmasaba/desa/layanan/",
+ },
+ {
+ title: "Layanan Sosial",
+ link: "/darmasaba/ekonomi/program-kemiskinan",
+ },
+ {
+ title: "Pengaduan Masyarakat",
+ link: "/darmasaba/keamanan/laporan-publik",
+ },
+ {
+ title: "Informasi Publik",
+ link: "/darmasaba/ppid/daftar-informasi-publik-desa-darmasaba",
+ },
+]
+
+const tautanPenting = [
+ {
+ title: "Portal Badung",
+ link: "/darmasaba/desa/berita/semua",
+ },
+ {
+ title: "E-Government",
+ link: "/darmasaba/inovasi/desa-digital-smart-village",
+ },
+ {
+ title: "Transparansi",
+ link: "/darmasaba/ppid/daftar-informasi-publik-desa-darmasaba",
+ }
+]
function Footer() {
+
+ const emailRef = useRef(null)
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ const email = emailRef.current?.value.trim();
+ if (!email) return toast.error('Email wajib diisi!');
+
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!emailRegex.test(email)) return toast.error('Format email tidak valid!');
+
+ try {
+ const res = await fetch('/api/subscribe', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ email }),
+ });
+
+ const data = await res.json();
+
+ if (res.ok && data.success) {
+ toast.success('Berhasil! Cek email Anda untuk konfirmasi.');
+ emailRef.current!.value = '';
+ } else {
+ toast.error(data.message || 'Gagal berlangganan.');
+ }
+ } catch (err) {
+ console.error(err);
+ toast.error('Gagal menghubungi server. Coba lagi nanti.');
+ }
+ };
+
+
return (
@@ -46,7 +138,7 @@ function Footer() {
"Desa Kuat, Warga Sejahtera!"
-
+
@@ -64,31 +156,39 @@ function Footer() {
Darmasaba adalah desa budaya yang kaya akan tradisi dan nilai-nilai warisan Bali.
-
-
-
-
+ {sosialMedia.map((item) => (
+
+
+
+ ))}
+
Layanan Desa
- Administrasi Kependudukan
- Layanan Sosial
- Pengaduan Masyarakat
- Informasi Publik
+ {layanandesa.map((item) => (
+ {item.title}
+ ))}
Tautan Penting
- Portal Badung
- E-Government
- Transparansi
- Unduhan
+ {tautanPenting.map((item) => (
+ {item.title}
+ ))}
@@ -101,8 +201,9 @@ function Footer() {
w="70%"
placeholder="Masukkan email Anda"
rightSection={ }
+ ref={emailRef} // ini aja cukup
/>
- Daftar
+ Daftar
diff --git a/src/app/darmasaba/_com/NavBarSearch.tsx b/src/app/darmasaba/_com/NavBarSearch.tsx
index 4585fa6e..8c8d625d 100644
--- a/src/app/darmasaba/_com/NavBarSearch.tsx
+++ b/src/app/darmasaba/_com/NavBarSearch.tsx
@@ -1,27 +1,77 @@
+import { useRef, useState, useEffect } from 'react';
import stateNav from "@/state/state-nav";
-import { Container, Stack, TextInput, Tooltip } from "@mantine/core";
-import { IconSearch } from "@tabler/icons-react";
+import { Container, Stack, ActionIcon, Box } from "@mantine/core";
+import { IconX } from '@tabler/icons-react';
+import GlobalSearch from "./globalSearch";
export function NavbarSearch() {
+ const [isOpen, setIsOpen] = useState(false);
+ const containerRef = useRef(null);
+ const isNavigatingRef = useRef(false);
+
+ // Close when clicking outside
+ useEffect(() => {
+ function handleClickOutside(event: MouseEvent) {
+ const target = event.target as HTMLElement;
+
+ // Jangan close jika klik di search result item (biar handleSelect yang urus)
+ if (target.closest('.search-result-item')) {
+ return;
+ }
+
+ // Close jika klik di luar container
+ if (containerRef.current && !containerRef.current.contains(target)) {
+ setIsOpen(false);
+ stateNav.clear();
+ }
+ }
+
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, []);
+
+ // Reset navigation flag saat component unmount atau route change
+ useEffect(() => {
+ return () => {
+ isNavigatingRef.current = false;
+ };
+ }, []);
+
return (
-
-
-
- }
- />
-
-
-
+
+
+
+
+ {isOpen && (
+ {
+ setIsOpen(false);
+ stateNav.clear();
+ }}
+ style={{
+ position: 'absolute',
+ right: 10,
+ top: '50%',
+ transform: 'translateY(-50%)',
+ zIndex: 1000
+ }}
+ >
+
+
+ )}
+
+
+
+
);
-}
+}
\ No newline at end of file
diff --git a/src/app/darmasaba/_com/Navbar.tsx b/src/app/darmasaba/_com/Navbar.tsx
index 95c14d56..65da725e 100644
--- a/src/app/darmasaba/_com/Navbar.tsx
+++ b/src/app/darmasaba/_com/Navbar.tsx
@@ -5,7 +5,7 @@ import stateNav from "@/state/state-nav";
import { ActionIcon, Box, Burger, Group, Image, Paper, ScrollArea, Stack, Text, Tooltip } from "@mantine/core";
import { IconSquareArrowRight } from "@tabler/icons-react";
import { motion } from "framer-motion";
-import { useRouter } from "next/navigation";
+import { usePathname, useRouter } from "next/navigation";
import { useSnapshot } from "valtio";
import { MenuItem } from "../../../../types/menu-item";
import { NavbarMainMenu } from "./NavbarMainMenu";
@@ -19,14 +19,18 @@ export function Navbar() {
-
+ {/* Desktop navbar (muncul mulai 992px ke atas) */}
+
+
+
-
+ {/* Mobile navbar (muncul di bawah 992px, termasuk iPad Mini) */}
+
-
-
+
+
-
+
+
+
{mobileOpen && (
)}
+
{(item || isSearch) && }
);
@@ -76,35 +95,105 @@ export function Navbar() {
function NavbarMobile({ listNavbar }: { listNavbar: MenuItem[] }) {
const router = useRouter();
+ const pathname = usePathname(); // π untuk cek path aktif
+
+ // fungsi bantu: cek apakah path sekarang sama dengan menu / sub-menu
+ const isActive = (href?: string) => href && pathname.startsWith(href);
+
return (
-
-
- {listNavbar.map((item, k) => (
-
- {
- router.push(item.href);
- stateNav.mobileOpen = false;
- }}
- style={{ cursor: "pointer" }}
- >
-
- {item.name}
-
-
-
- {item.children && (
-
-
-
- )}
-
- ))}
+
+
+ {listNavbar.map((item, k) => {
+ const active = isActive(item.href);
+ return (
+
+ {
+ if (item.href) {
+ router.push(item.href);
+ stateNav.mobileOpen = false;
+ }
+ }}
+ style={{
+ cursor: item.href ? "pointer" : "default",
+ transition: "background 0.15s ease",
+ borderLeft: active ? "4px solid #1e66f5" : "4px solid transparent",
+ }}
+ >
+
+
+ {item.name}
+
+ {item.href && (
+
+ )}
+
+
+
+ {/* Submenu */}
+ {item.children && (
+
+ {item.children.map((child, j) => {
+ const childActive = isActive(child.href);
+ return (
+ {
+ if (child.href) {
+ router.push(child.href);
+ stateNav.mobileOpen = false;
+ }
+ }}
+ style={{
+ cursor: child.href ? "pointer" : "default",
+ opacity: child.href ? 1 : 0.8,
+ borderRadius: "0.5rem",
+ backgroundColor: childActive ? "#e7f0ff" : "transparent",
+ borderLeft: childActive ? "3px solid #1e66f5" : "3px solid transparent",
+ transition: "background 0.15s ease",
+ }}
+ >
+
+ {child.name}
+
+
+
+ );
+ })}
+
+ )}
+
+ );
+ })}
);
}
+
diff --git a/src/app/darmasaba/_com/NavbarMainMenu.tsx b/src/app/darmasaba/_com/NavbarMainMenu.tsx
index 4882b6a0..5d796d23 100644
--- a/src/app/darmasaba/_com/NavbarMainMenu.tsx
+++ b/src/app/darmasaba/_com/NavbarMainMenu.tsx
@@ -2,15 +2,14 @@
import colors from "@/con/colors"
import stateNav from "@/state/state-nav"
-import { ActionIcon, Button, Container, Flex, Image, Stack, Tooltip } from "@mantine/core"
-import { useHover } from "@mantine/hooks"
+import { ActionIcon, Button, Container, Flex, Image, Menu, MenuTarget, Stack, Tooltip } from "@mantine/core"
import { IconSearch, IconUser } from "@tabler/icons-react"
import { useTransitionRouter } from 'next-view-transitions'
+import { usePathname, useRouter } from "next/navigation"
import { useSnapshot } from "valtio"
import { MenuItem } from "../../../../types/menu-item"
import { NavbarSearch } from "./NavBarSearch"
import { NavbarSubMenu } from "./NavbarSubMenu"
-import { useRouter } from "next/navigation"
// contoh state auth (dummy aja dulu, bisa diganti sesuai sistem auth kamu)
const stateAuth = {
@@ -21,12 +20,13 @@ export function NavbarMainMenu({ listNavbar }: { listNavbar: MenuItem[] }) {
const { item, isSearch } = useSnapshot(stateNav)
const router = useTransitionRouter()
const next = useRouter()
+ const pathname = usePathname();
return (
-
+
{listNavbar.map((item, k) => (
-
+ child.href && pathname.startsWith(child.href)))}
+ />
))}
-
+
+
{
next.push("/admin/landing-page/profile/program-inovasi")
@@ -88,27 +93,45 @@ export function NavbarMainMenu({ listNavbar }: { listNavbar: MenuItem[] }) {
)
}
-function MenuItemCom({ item }: { item: MenuItem }) {
- const { ref, hovered } = useHover()
+function MenuItemCom({ item, isActive = false }: { item: MenuItem, isActive?: boolean }) {
const router = useTransitionRouter()
return (
- {
- stateNav.item = item.children || null
- stateNav.isSearch = false
+ {
+ stateNav.item = item.children || null;
+ stateNav.isSearch = false;
}}
- variant="subtle"
- radius="xl"
- onClick={() => {
- router.push(item.href)
- stateNav.clear()
- }}
- fw={500}
>
- {item.name}
-
+
+ {
+ if (item.href) {
+ router.push(item.href);
+ stateNav.clear();
+ }
+ }}
+ styles={{
+ root: {
+ fontWeight: isActive ? 600 : 400,
+ borderBottom: isActive ? `2px solid ${colors['blue-button']}` : 'none',
+ '&:hover': {
+ backgroundColor: 'transparent',
+ }
+ }
+ }}
+ >
+ {item.name}
+
+
+
)
}
diff --git a/src/app/darmasaba/_com/NavbarSubMenu.tsx b/src/app/darmasaba/_com/NavbarSubMenu.tsx
index be7235ba..db816ec0 100644
--- a/src/app/darmasaba/_com/NavbarSubMenu.tsx
+++ b/src/app/darmasaba/_com/NavbarSubMenu.tsx
@@ -7,10 +7,11 @@ import { IconArrowRight } from "@tabler/icons-react";
import { MenuItem } from "../../../../types/menu-item";
import { useTransitionRouter } from "next-view-transitions";
import colors from "@/con/colors";
+import { usePathname } from "next/navigation";
export function NavbarSubMenu({ item }: { item: MenuItem[] | null }) {
const router = useTransitionRouter();
-
+ const pathname = usePathname();
return (
{item.map((link, index) => (
{
+ key={index}
+ variant="subtle"
+ justify="space-between"
+ size="lg"
+ radius="md"
+ color={link.href && pathname.startsWith(link.href) ? 'blue' : 'gray'}
+ onClick={() => {
+ if (link.href) {
router.push(link.href);
stateNav.item = null;
stateNav.isSearch = false;
- }}
- rightSection={ }
- styles={(theme) => ({
- root: {
- background: "transparent",
- color: colors['blue-button'],
- fontWeight: 500,
- transition: "all 0.2s ease",
- "&:hover": {
- background: theme.colors.gray[8],
- boxShadow: `0 0 12px ${theme.colors.blue[6]}55`,
- },
- },
- })}
- >
- {link.name}
-
+ }
+ }}
+ rightSection={ }
+ styles={(theme) => ({
+ root: {
+ background: link.href && pathname.startsWith(link.href) ? theme.colors.blue[0] : 'transparent',
+ color: link.href && pathname.startsWith(link.href) ? theme.colors.blue[7] : colors['blue-button'],
+ fontWeight: link.href && pathname.startsWith(link.href) ? 600 : 500,
+ transition: "all 0.2s ease",
+ "&:hover": {
+ background: link.href && pathname.startsWith(link.href) ? theme.colors.blue[1] : theme.colors.gray[0],
+ }
+ },
+ })}
+ >
+ {link.name}
+
))}
) : (
diff --git a/src/app/darmasaba/_com/globalSearch.tsx b/src/app/darmasaba/_com/globalSearch.tsx
new file mode 100644
index 00000000..fde9645b
--- /dev/null
+++ b/src/app/darmasaba/_com/globalSearch.tsx
@@ -0,0 +1,184 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+'use client';
+
+import searchState, { debouncedFetch } from '@/app/api/[[...slugs]]/_lib/search/searchState';
+import { Box, Center, Loader, Popover, Text, TextInput } from '@mantine/core';
+import { IconX } from '@tabler/icons-react';
+import { useEffect, useState } from 'react';
+import { useSnapshot } from 'valtio';
+import getDetailUrl from './searchUrl';
+
+export default function GlobalSearch() {
+ const snap = useSnapshot(searchState);
+ const [opened, setOpened] = useState(false);
+ const [isNavigating, setIsNavigating] = useState(false);
+
+ // Buka popover saat ada query
+ useEffect(() => {
+ setOpened(!!snap.query);
+ }, [snap.query]);
+
+ // Infinite scroll handler
+ useEffect(() => {
+ const handleScroll = () => {
+ const nearBottom = window.innerHeight + window.scrollY >= document.body.offsetHeight - 200;
+ if (nearBottom && !snap.loading) searchState.next();
+ };
+ window.addEventListener('scroll', handleScroll);
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, [snap.loading]);
+
+ const handleSelect = async (e: React.MouseEvent, item: any) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (isNavigating) return;
+ setIsNavigating(true);
+
+ try {
+ // π₯ pastikan objek udah βdikeluarkanβ dari Proxy valtio
+ const rawItem = JSON.parse(JSON.stringify(item));
+
+ // π₯ pastikan type-nya string murni
+ const type = String(rawItem.type || '').trim().toLowerCase();
+
+ // π₯ panggil getDetailUrl pakai type yang fix
+ let url = getDetailUrl({ ...rawItem, type });
+
+ // kalau hasil undefined atau default, fallback ke link eksternal
+ if (!url || url === '/darmasaba') {
+ if (rawItem.link && rawItem.link.startsWith('http')) {
+ url = rawItem.link;
+ }
+ }
+
+ if (!url) {
+ console.warn('URL tidak ditemukan untuk item:', rawItem);
+ setIsNavigating(false);
+ return;
+ }
+
+ console.log('Navigating to:', url);
+
+ // tutup popover dulu
+ setOpened(false);
+ searchState.query = '';
+ searchState.results = [];
+ searchState.loading = false;
+
+ // kasih delay biar UI nutup dulu
+ await new Promise((r) => setTimeout(r, 100));
+
+ // navigasi
+ if (url.startsWith('http')) {
+ window.location.href = url;
+ } else {
+ window.location.href = url;
+ }
+
+ } catch (err) {
+ console.error('Error saat navigasi:', err);
+ setIsNavigating(false);
+ }
+ };
+
+
+ const clearSearch = () => {
+ searchState.query = '';
+ searchState.results = [];
+ searchState.page = 1;
+ searchState.nextPage = null;
+ setOpened(false);
+ setIsNavigating(false);
+ };
+
+ return (
+
+ {
+ if (!isOpen) clearSearch();
+ setOpened(isOpen);
+ }}
+ width="target"
+ position="bottom"
+ shadow="md"
+ withinPortal
+ radius="md"
+ zIndex={2000}
+ closeOnClickOutside={true}
+ closeOnEscape={true}
+ styles={{
+ dropdown: {
+ zIndex: 2000,
+ borderRadius: 12,
+ overflow: 'hidden',
+ },
+ }}
+ >
+
+ {
+ searchState.query = e.currentTarget.value;
+ debouncedFetch();
+ }}
+ radius="xl"
+ size="md"
+ rightSection={
+ snap.query ? (
+
+ ) : undefined
+ }
+ />
+
+
+
+ {[...snap.results].length > 0 ? (
+ [...snap.results].map((item: any, i: number) => (
+ !isNavigating && (e.currentTarget.style.background = '#f9f9f9')}
+ onMouseLeave={(e) => (e.currentTarget.style.background = 'white')}
+ onClick={(e) => handleSelect(e, item)}
+ >
+
+ {item.name ?? item.nama ?? item.namaPasar ?? item.judul ?? '(Tanpa nama)'}
+
+
+ dari modul: {item.type || '-'}
+
+
+ ))
+ ) : (
+
+ {snap.loading ? : Tidak ada hasil }
+
+ )}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/app/darmasaba/_com/main-page/apbdes/index.tsx b/src/app/darmasaba/_com/main-page/apbdes/index.tsx
index a62b963b..7dc89bb0 100644
--- a/src/app/darmasaba/_com/main-page/apbdes/index.tsx
+++ b/src/app/darmasaba/_com/main-page/apbdes/index.tsx
@@ -34,13 +34,13 @@ function Apbdes() {
const data = (state.findMany.data || []).slice(0, 3)
return (
-
+
-
+
{textHeading.title}
-
+
{textHeading.des}
@@ -117,7 +117,7 @@ function Apbdes() {
)}
-
+
-
+
- Desa Anti Korupsi
+ Desa Anti Korupsi
Desa antikorupsi mendorong pemerintahan jujur dan transparan. Keuangan desa dikelola terbuka dengan melibatkan warga mengawasi anggaran, sehingga digunakan tepat sasaran sesuai kebutuhan.
diff --git a/src/app/darmasaba/_com/main-page/kepuasan/index.tsx b/src/app/darmasaba/_com/main-page/kepuasan/index.tsx
index b5e3e0ca..1cc7a035 100644
--- a/src/app/darmasaba/_com/main-page/kepuasan/index.tsx
+++ b/src/app/darmasaba/_com/main-page/kepuasan/index.tsx
@@ -23,7 +23,7 @@ function Kepuasan() {
const [donutDataJenisKelamin, setDonutDataJenisKelamin] = useState([]);
const [donutDataRating, setDonutDataRating] = useState([]);
const [donutDataKelompokUmur, setDonutDataKelompokUmur] = useState([]);
- const [barChartData, setBarChartData] = useState>([]);
+ const [barChartData, setBarChartData] = useState>([]);
const [opened, { open, close }] = useDisclosure(false)
const resetForm = () => {
@@ -121,18 +121,18 @@ function Kepuasan() {
// Convert map to array and sort by date
const barData = Array.from(monthYearMap.entries())
- .map(([key, count]) => {
+ .map(([key, Responden]) => {
const [year, month] = key.split('-');
const monthName = new Date(Number(year), Number(month) - 1, 1)
.toLocaleString('id-ID', { month: 'long' });
return {
month: `${monthName} ${year}`,
- count,
+ Responden,
sortKey: parseInt(`${year}${String(month).padStart(2, '0')}`, 10)
};
})
.sort((a, b) => a.sortKey - b.sortKey)
- .map(({ month, count }) => ({ month, count }));
+ .map(({ month, Responden }) => ({ month, Responden }));
setBarChartData(barData);
}
@@ -140,7 +140,7 @@ function Kepuasan() {
if ((loading && !data) || !data) {
return (
-
+
@@ -154,11 +154,17 @@ function Kepuasan() {
if (data.length === 0) {
return (
-
+
- Indeks Kepuasan Masyarakat
+ Indeks Kepuasan Masyarakat
- Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!
+ Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!
Ajukan Responden
-
+
@@ -185,7 +191,7 @@ function Kepuasan() {
h={window.innerWidth < 480 ? 200 : 300}
data={barChartData}
dataKey="month"
- series={[{ name: 'count', color: colors['blue-button'] }]}
+ series={[{ name: 'Responden', color: colors['blue-button'] }]}
tickLine="y"
xAxisLabel="Bulan"
yAxisLabel="Jumlah Responden"
@@ -332,7 +338,7 @@ function Kepuasan() {
{
state.create.form.name = val.currentTarget.value;
@@ -416,16 +422,22 @@ function Kepuasan() {
}
return (
-
+
- Indeks Kepuasan Masyarakat
+ Indeks Kepuasan Masyarakat
Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!
Ajukan Responden
-
+
@@ -448,7 +460,7 @@ function Kepuasan() {
h={300}
data={barChartData}
dataKey="month"
- series={[{ name: 'count', color: colors['blue-button'] }]}
+ series={[{ name: 'Responden', color: colors['blue-button'] }]}
tickLine="y"
xAxisLabel="Bulan"
yAxisLabel="Jumlah Responden"
@@ -599,16 +611,16 @@ function Kepuasan() {
label="Nama"
type='text'
placeholder="masukkan nama"
- value={state.create.form.name}
+ defaultValue={state.create.form.name}
onChange={(val) => {
state.create.form.name = val.currentTarget.value;
}}
/>
{
state.create.form.tanggal = val.currentTarget.value;
}}
@@ -617,7 +629,7 @@ function Kepuasan() {
key={"jenisKelamin"}
label={"Jenis Kelamin"}
placeholder={indeksKepuasanState.jenisKelaminResponden.findMany.loading ? 'Memuat...' : 'Pilih jenis kelamin'}
- value={state.create.form.jenisKelaminId || ""}
+ defaultValue={state.create.form.jenisKelaminId || ""}
onChange={(val) => {
state.create.form.jenisKelaminId = val ?? "";
}}
@@ -635,7 +647,7 @@ function Kepuasan() {
key={"rating_responden"}
label={"Rating"}
placeholder={indeksKepuasanState.pilihanRatingResponden.findMany.loading ? 'Memuat...' : 'Pilih rating'}
- value={state.create.form.ratingId || ""}
+ defaultValue={state.create.form.ratingId || ""}
onChange={(val) => {
state.create.form.ratingId = val ?? "";
}}
@@ -653,7 +665,7 @@ function Kepuasan() {
key={"kelompokUmur"}
label={"Kelompok Umur"}
placeholder={indeksKepuasanState.kelompokUmurResponden.findMany.loading ? 'Memuat...' : 'Pilih kelompok umur'}
- value={state.create.form.kelompokUmurId || ""}
+ defaultValue={state.create.form.kelompokUmurId || ""}
onChange={(val) => {
state.create.form.kelompokUmurId = val ?? "";
}}
diff --git a/src/app/darmasaba/_com/main-page/landing-page/ModuleView.tsx b/src/app/darmasaba/_com/main-page/landing-page/ModuleView.tsx
index d8269302..4d67390f 100644
--- a/src/app/darmasaba/_com/main-page/landing-page/ModuleView.tsx
+++ b/src/app/darmasaba/_com/main-page/landing-page/ModuleView.tsx
@@ -6,20 +6,19 @@ import {
Center,
Image,
Paper,
+ ScrollArea,
SimpleGrid,
+ Skeleton,
Stack,
Text,
- Tooltip,
- Skeleton,
- useMantineColorScheme,
- ScrollArea,
+ useMantineColorScheme
} from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
+import { Prisma } from "@prisma/client";
+import { IconPhotoOff } from "@tabler/icons-react";
import { motion } from "framer-motion";
import { useTransitionRouter } from "next-view-transitions";
import { useProxy } from "valtio/utils";
-import { Prisma } from "@prisma/client";
-import { IconPhotoOff } from "@tabler/icons-react";
type ProgramInovasiItem = Prisma.ProgramInovasiGetPayload<{ include: { image: true } }>;
@@ -30,44 +29,42 @@ function ModuleItem({ data }: { data: ProgramInovasiItem }) {
return (
-
- router.push(`/darmasaba/program-inovasi/${data.id}`)}
- p="lg"
- radius="xl"
- shadow="sm"
- role="button"
- tabIndex={0}
- className="cursor-pointer transition-all"
- bg={isDark ? "dark.6" : "white"}
- >
-
- {data.image?.link ? (
-
- ) : (
-
-
-
- Belum ada gambar
-
-
- )}
-
-
-
- {data.name}
-
-
-
-
+ router.push(`/darmasaba/program-inovasi/${data.id}`)}
+ p="lg"
+ radius="xl"
+ shadow="sm"
+ role="button"
+ tabIndex={0}
+ className="cursor-pointer transition-all"
+ bg={isDark ? "dark.6" : "white"}
+ >
+
+ {data.image?.link ? (
+
+ ) : (
+
+
+
+ Belum ada gambar
+
+
+ )}
+
+
+
+ {data.name}
+
+
+
);
}
@@ -113,11 +110,11 @@ function ModuleView() {
viewport: { paddingRight: 8 }, // kasih jarak biar scroll nggak dempet
}}
>
-
- {listImageState.findMany.data?.map((item) => (
-
- ))}
-
+
+ {listImageState.findMany.data?.map((item) => (
+
+ ))}
+
);
}
diff --git a/src/app/darmasaba/_com/main-page/landing-page/ProfileView.tsx b/src/app/darmasaba/_com/main-page/landing-page/ProfileView.tsx
index 5081dcd0..3520b0f9 100644
--- a/src/app/darmasaba/_com/main-page/landing-page/ProfileView.tsx
+++ b/src/app/darmasaba/_com/main-page/landing-page/ProfileView.tsx
@@ -30,17 +30,41 @@ export default function ProfileView({ data }: ProfileViewProps) {
justify="end"
align="end"
pos="relative"
- w={{ base: '100%', md: '40%' }}
- px="xl"
+ w={{
+ base: '100%', // mobile: full width
+ xs: '100%', // small mobile
+ sm: '85%', // tablet: 85%
+ md: '60%', // laptop: 60%
+ lg: '55%', // laptop large: 55%
+ xl: '50%' // extra large (4K): 50%
+ }}
+ px={{ base: 'md', sm: 'lg', md: 'xl', xl: '2xl' }}
+ h={{ base: 'auto', sm: '500px', md: '600px', lg: '650px', xl: '700px' }}
>
{data.image?.link ? (
-
+
+
+
) : (
@@ -49,24 +73,48 @@ export default function ProfileView({ data }: ProfileViewProps) {
)}
-
+
+ {/* Box nama dan jabatan - responsive positioning */}
+
-
+
{data.position || 'Tidak ada jabatan'}
-
+
{data.name}
);
-}
+}
\ No newline at end of file
diff --git a/src/app/darmasaba/_com/main-page/landing-page/index.tsx b/src/app/darmasaba/_com/main-page/landing-page/index.tsx
index 0c43440e..470ecfe4 100644
--- a/src/app/darmasaba/_com/main-page/landing-page/index.tsx
+++ b/src/app/darmasaba/_com/main-page/landing-page/index.tsx
@@ -1,26 +1,27 @@
"use client";
import colors from "@/con/colors";
-import { Prisma } from "@prisma/client";
import {
+ Badge,
Box,
Card,
- Skeleton,
+ Center,
Flex,
Grid,
GridCol,
+ Group,
Image,
Paper,
+ Skeleton,
Stack,
Text,
- Center,
Tooltip,
- Badge,
} from "@mantine/core";
+import { Prisma } from "@prisma/client";
import { IconCalendarTime, IconInfoCircle } from "@tabler/icons-react";
import { useEffect, useState } from "react";
import ModuleView from "./ModuleView";
-import SosmedView from "./SosmedView";
import ProfileView from "./ProfileView";
+import SosmedView from "./SosmedView";
const getDayOfWeek = () => {
const days = ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"];
@@ -120,23 +121,21 @@ function LandingPage() {
}, []);
return (
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
- Bagikan ide, kritik, atau saran Anda untuk mendukung pembangunan desa.
+ Bagikan ide, kritik, atau saran Anda untuk mendukung pembangunan desa.
Semua lebih mudah dengan fitur interaktif yang kami sediakan.
diff --git a/src/app/darmasaba/_com/main-page/layanan/index.tsx b/src/app/darmasaba/_com/main-page/layanan/index.tsx
index b682c2d8..128bc2e3 100644
--- a/src/app/darmasaba/_com/main-page/layanan/index.tsx
+++ b/src/app/darmasaba/_com/main-page/layanan/index.tsx
@@ -30,17 +30,13 @@ const textHeading = {
function Layanan() {
return (
-
-
+
+
-
+
{textHeading.title}
-
+
{textHeading.des}
diff --git a/src/app/darmasaba/_com/main-page/penghargaan/index.tsx b/src/app/darmasaba/_com/main-page/penghargaan/index.tsx
index 9f2d1496..46ef6f5c 100644
--- a/src/app/darmasaba/_com/main-page/penghargaan/index.tsx
+++ b/src/app/darmasaba/_com/main-page/penghargaan/index.tsx
@@ -68,7 +68,7 @@ function Penghargaan() {
variant="gradient"
gradient={{ from: "cyan", to: "blue", deg: 60 }}
>
- Penghargaan & Prestasi Desa
+ Penghargaan Desa
{loading ? (
diff --git a/src/app/darmasaba/_com/main-page/potensi/index.tsx b/src/app/darmasaba/_com/main-page/potensi/index.tsx
index e9b3593c..ff2bf284 100644
--- a/src/app/darmasaba/_com/main-page/potensi/index.tsx
+++ b/src/app/darmasaba/_com/main-page/potensi/index.tsx
@@ -6,6 +6,7 @@ import {
BackgroundImage,
Box,
Button,
+ Container,
Divider,
Group,
Loader,
@@ -48,15 +49,15 @@ function Potensi() {
const data = (state.findMany.data || []).slice(0, 4);
return (
-
-
-
+
+
+
{textHeading.title}
-
+
{textHeading.des}
-
+
{loading ? (
@@ -104,9 +105,7 @@ function Potensi() {
{v.name}
-
- {v.deskripsi}
-
+
diff --git a/src/app/darmasaba/_com/main-page/prestasi/index.tsx b/src/app/darmasaba/_com/main-page/prestasi/index.tsx
index d9222063..13ff8133 100644
--- a/src/app/darmasaba/_com/main-page/prestasi/index.tsx
+++ b/src/app/darmasaba/_com/main-page/prestasi/index.tsx
@@ -35,7 +35,7 @@ function Prestasi() {
-
+
Prestasi Desa
@@ -55,7 +55,7 @@ function Prestasi() {
-
+
{loading ? (
@@ -101,6 +101,8 @@ function Prestasi() {
fz={{ base: "1.5rem", md: "2rem", lg: "2.5rem" }}
ta="center"
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
+ lineClamp={5}
/>
SDGs Desa
-
- SDGs Desa merupakan langkah nyata untuk mewujudkan desa yang maju, inklusif, dan berkelanjutan melalui 17 tujuan pembangunan: dari pengentasan kemiskinan, pendidikan, kesehatan, kesetaraan gender, hingga pelestarian lingkungan.
+
+ SDGs Desa merupakan langkah nyata untuk mewujudkan desa yang maju, inklusif, dan berkelanjutan melalui 17 tujuan pembangunan dari pengentasan kemiskinan, pendidikan, kesehatan, kesetaraan gender, hingga pelestarian lingkungan.
-
+
{sdgsDesa && sdgsDesa.length > 0 ? (
- {sdgsDesa.map((item) => (
-
-
-
-
-
-
+ {sdgsDesa.map((item) => (
+
+
+
+
+
+
+
+ {/* Stack isi teks & angka */}
+
-
+
{item.name}
+
{item.jumlah}
-
- ))}
-
+
+
+ ))}
+
+
) : (
@@ -141,7 +153,7 @@ export default function SDGS() {
href="/darmasaba/sdgs-desa"
radius="xl"
size="lg"
- mt={40}
+ mt="md"
variant="gradient"
gradient={{ from: "#26667F", to: "#124170" }}
style={{ boxShadow: "0 6px 14px rgba(18,65,112,0.25)"}}
diff --git a/src/app/darmasaba/_com/scrollToTopButton.tsx b/src/app/darmasaba/_com/scrollToTopButton.tsx
new file mode 100644
index 00000000..c2b66fe7
--- /dev/null
+++ b/src/app/darmasaba/_com/scrollToTopButton.tsx
@@ -0,0 +1,36 @@
+'use client'
+import { useWindowScroll } from '@mantine/hooks';
+import { ActionIcon, Transition } from '@mantine/core';
+import { IconArrowUp } from '@tabler/icons-react';
+import colors from '@/con/colors';
+
+function ScrollToTopButton() {
+ const [scroll, scrollTo] = useWindowScroll();
+
+ return (
+ 300}
+ transition="slide-up"
+ duration={300}
+ timingFunction="ease"
+ >
+ {(styles) => (
+ scrollTo({ y: 0 })}
+ pos="fixed"
+ bottom={24}
+ right={24}
+ aria-label="Kembali ke atas"
+ >
+
+
+ )}
+
+ );
+}
+export default ScrollToTopButton
\ No newline at end of file
diff --git a/src/app/darmasaba/_com/searchUrl.tsx b/src/app/darmasaba/_com/searchUrl.tsx
new file mode 100644
index 00000000..6350b8dc
--- /dev/null
+++ b/src/app/darmasaba/_com/searchUrl.tsx
@@ -0,0 +1,92 @@
+const getDetailUrl = (item: { type?: string; id: string | number; [key: string]: unknown }) => {
+ const { type, id, kategori } = item;
+ const map: Record string> = {
+ programinovasi: (id) => `/darmasaba/program-inovasi/${id}`,
+ desaantikorupsi: () => '/darmasaba/desa-anti-korupsi',
+ sdgsdesa: () => '/darmasaba/sdgs-desa',
+ apbdes: () => '/darmasaba/apbdes',
+ prestasidesa: () => '/darmasaba/prestasi-desa',
+ pejabatdesa: () => '/darmasaba/ppid/profile-ppid',
+ strukturppid: () => '/darmasaba/ppid/struktur-ppid',
+ visimisippid: () => '/darmasaba/ppid/visi-misi',
+ dasarhukumppid: () => '/darmasaba/ppid/dasar-hukum',
+ profileppid: () => '/darmasaba/ppid/profile',
+ daftarinformasipublik: () => '/darmasaba/ppid/daftar-informasi-publik',
+ perbekeldarmasaba: () => '/darmasaba/desa/profile',
+ berita: (id, kategori) => `/darmasaba/desa/berita/${kategori}/${id}`,
+ pengumuman: (id, kategori) => `/darmasaba/desa/pengumuman/${kategori}/${id}`,
+ sejarahdesa: () => '/darmasaba/desa/profile',
+ visimisidesa: () => '/darmasaba/desa/profile',
+ lambangdesa: () => '/darmasaba/desa/profile',
+ maskotdesa: () => '/darmasaba/desa/profile',
+ profilperbekel: () => '/darmasaba/desa/profile',
+ potensi: () => '/darmasaba/desa/potensi-desa',
+ galleryFoto: () => '/darmasaba/desa/gallery/foto',
+ galleryVideo: () => '/darmasaba/desa/gallery/video',
+ pelayananSuratKeterangan: () => '/darmasaba/desa/layanan',
+ pelayananPerizinanBerusaha: () => '/darmasaba/desa/layanan',
+ pelayananTelunjukSaktiDesa: () => '/darmasaba/desa/layanan',
+ pelayananPendudukNonPermanent: () => '/darmasaba/desa/layanan',
+ penghargaan: () => '/darmasaba/desa/penghargaan',
+ posyandu: (id) => `/darmasaba/kesehatan/posyandu/${id}`,
+ fasilitasKesehatan: () => '/darmasaba/kesehatan/data-kesehatan-warga',
+ jadwalKegiatan: () => '/darmasaba/kesehatan/data-kesehatan-warga',
+ artikelKesehatan: () => '/darmasaba/kesehatan/data-kesehatan-warga',
+ puskesmas: () => '/darmasaba/kesehatan/puskesmas',
+ programKesehatan: () => '/darmasaba/kesehatan/program-kesehatan',
+ penangananDarurat: () => '/darmasaba/kesehatan/penanganan-darurat',
+ kontakDarurat: () => '/darmasaba/kesehatan/kontak-darurat',
+ infoWabahPenyakit: () => '/darmasaba/kesehatan/info-wabah-penyakit',
+ keamananLingkungan: () => '/darmasaba/keamanan/keamanan-lingkungan-pecalang-patwal',
+ polsekTerdekat: () => '/darmasaba/keamanan/polsek-terdekat',
+ kontakDaruratKeamanan: () => '/darmasaba/keamanan/kontak-darurat',
+ pencegahanKriminalitas: () => '/darmasaba/keamanan/pencegahan-kriminalitas',
+ laporanPublik: () => '/darmasaba/keamanan/laporan-publik',
+ tipsKeamanan: () => '/darmasaba/keamanan/tips-keamanan',
+ pasarDesa: () => '/darmasaba/ekonomi/pasar-desa',
+ lowonganKerjaLokal: () => '/darmasaba/ekonomi/lowongan-kerja-lokal',
+ strukturOrganisasi: () => '/darmasaba/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa',
+ jumlahPendudukUsiaKerjaYangMenganggurUsia: () => '/darmasaba/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur',
+ jumlahPendudukUsiaKerjaYangMenganggurPendidikan: () => '/darmasaba/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur',
+ jumlahPendudukMiskin: () => '/darmasaba/ekonomi/jumlah-penduduk-miskin',
+ programKemiskinan: () => '/darmasaba/ekonomi/program-kemiskinan',
+ sektorUnggulanDesa: () => '/darmasaba/ekonomi/sektor-unggulan-desa',
+ demografiPekerjaan: () => '/darmasaba/ekonomi/demografi-pekerjaan',
+ desaDigital: () => '/darmasaba/inovasi/desa-digital-smart-village',
+ programKreatif: () => '/darmasaba/inovasi/program-kreatif-desa',
+ kolaborasiInovasi: () => '/darmasaba/inovasi/kolaborasi-inovasi',
+ mitraKolaborasi: () => '/darmasaba/inovasi/kolaborasi-inovasi',
+ infoTekno: () => '/darmasaba/inovasi/info-teknologi-tepat-guna',
+ pengelolaanSampah: () => '/darmasaba/lingkungan/pengelolaan-sampah-bank-sampah',
+ keteranganBankSampahTerdekat: () => '/darmasaba/lingkungan/pengelolaan-sampah-bank-sampah',
+ programPenghijauan: () => '/darmasaba/lingkungan/program-penghijauan',
+ dataLingkunganDesa: () => '/darmasaba/lingkungan/data-lingkungan-desa',
+ gotongRoyong: (id, kategori) => `/darmasaba/lingkungan/gotong-royong/${kategori}/${id}`,
+ tujuanEdukasiLingkungan: () => '/darmasaba/lingkungan/edukasi-lingkungan',
+ materiEdukasiLingkungan: () => '/darmasaba/lingkungan/edukasi-lingkungan',
+ contohEdukasiLingkungan: () => '/darmasaba/lingkungan/edukasi-lingkungan',
+ filosofiTriHita: () => '/darmasaba/lingkungan/konservasi-adat-bali',
+ bentukKonservasiBerdasarkanAdat: () => '/darmasaba/lingkungan/konservasi-adat-bali',
+ nilaiKonservasiAdat: () => '/darmasaba/lingkungan/konservasi-adat-bali',
+ jenjangPendidikan: () => '/darmasaba/pendidikan/info-sekolah/semua',
+ lembaga: () => '/darmasaba/pendidikan/info-sekolah/semua/lembaga',
+ siswa: () => '/darmasaba/pendidikan/info-sekolah/semua/siswa',
+ pengajar: () => '/darmasaba/pendidikan/info-sekolah/semua/pengajar',
+ keunggulanProgram: () => '/darmasaba/pendidikan/beasiswa-desa',
+ tujuanProgram: () => '/darmasaba/pendidikan/program-pendidikan-anak',
+ programUnggulan: () => '/darmasaba/pendidikan/program-pendidikan-anak',
+ lokasiJadwalBimbinganBelajarDesa: () => '/darmasaba/pendidikan/bimbingan-belajar-desa',
+ fasilitasBimbinganBelajarDesa: () => '/darmasaba/pendidikan/bimbingan-belajar-desa',
+ tujuanPendidikanNonFormal: () => '/darmasaba/pendidikan/pendidikan-non-formal',
+ tempatKegiatan: () => '/darmasaba/pendidikan/pendidikan-non-formal',
+ jenisProgramYangDiselenggarakan: () => '/darmasaba/pendidikan/pendidikan-non-formal',
+ dataPerpustakaan: () => '/darmasaba/pendidikan/perpustakaan-digital/semua',
+ dataPendidikan: () => '/darmasaba/pendidikan/data-pendidikan',
+
+ };
+
+ if (type && map[type]) return map[type](id, kategori as string | undefined);
+ return '/darmasaba';
+};
+
+export default getDetailUrl;
diff --git a/src/app/darmasaba/page.tsx b/src/app/darmasaba/page.tsx
index 092b49cb..e601eb4d 100644
--- a/src/app/darmasaba/page.tsx
+++ b/src/app/darmasaba/page.tsx
@@ -8,23 +8,31 @@ import colors from "@/con/colors";
import SDGS from "./_com/main-page/sdgs";
// import ApiFetch from "@/lib/api-fetch";
-import { Stack } from "@mantine/core";
+import { Box, Stack } from "@mantine/core";
import Apbdes from "./_com/main-page/apbdes";
import Prestasi from "./_com/main-page/prestasi";
+import ScrollToTopButton from "./_com/scrollToTopButton";
export default function Page() {
return (
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Tombol Scroll ke Atas */}
+
+
);
}
diff --git a/src/app/globals.css b/src/app/globals.css
index 873061a9..5c98d487 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -11,7 +11,7 @@
-webkit-backdrop-filter: blur(40px);
backdrop-filter: blur(40px);
position: fixed;
- z-index: 1;
+ z-index: 50;
width: 100%;
height: 100vh;
}
diff --git a/src/con/colors.ts b/src/con/colors.ts
index 16cbba8e..250d01aa 100644
--- a/src/con/colors.ts
+++ b/src/con/colors.ts
@@ -1,6 +1,14 @@
const colors = {
"orange": "#FCAE00",
"blue-button": "#0A4E78",
+ "blue-button-1": "#E5F2FA",
+ "blue-button-2": "#B8DAEF",
+ "blue-button-3": "#8AC1E3",
+ "blue-button-4": "#5DA9D8",
+ "blue-button-5": "#2F91CC",
+ "blue-button-6": "#083F61",
+ "blue-button-7": "#062F49",
+ "blue-button-8": "#041F32",
"blue-button-trans": "#628EC6",
"white-1": "#FBFBFC",
"white-trans-1": "rgba(255, 255, 255, 0.5)",
diff --git a/src/con/navbar-list-menu.ts b/src/con/navbar-list-menu.ts
index 031ccac0..28eaf9c2 100644
--- a/src/con/navbar-list-menu.ts
+++ b/src/con/navbar-list-menu.ts
@@ -2,7 +2,6 @@ const navbarListMenu = [
{
id: "1",
name: "PPID",
- href: "/darmasaba/ppid/profile-ppid",
children: [
{
id: "1.1",
@@ -51,7 +50,6 @@ const navbarListMenu = [
{
id: "2",
name: "Desa",
- href: "/darmasaba/desa/profile",
children: [
{
id: "2.1",
@@ -94,7 +92,6 @@ const navbarListMenu = [
{
id: "3",
name: "Kesehatan",
- href: "/darmasaba/kesehatan/posyandu",
children: [
{
id: "3.1",
@@ -136,7 +133,6 @@ const navbarListMenu = [
{
id: "4",
name: "Keamanan",
- href: "/darmasaba/keamanan/keamanan-lingkungan-pecalang-patwal",
children: [
{
id: "4.1",
@@ -173,7 +169,6 @@ const navbarListMenu = [
{
id: "5",
name: "Ekonomi",
- href: "/darmasaba/ekonomi/pasar-desa",
children: [
{
id: "5.1",
@@ -229,7 +224,6 @@ const navbarListMenu = [
}, {
id: "6",
name: "Inovasi",
- href: "/darmasaba/inovasi/desa-digital-smart-village",
children: [
{
id: "6.1",
@@ -266,7 +260,6 @@ const navbarListMenu = [
}, {
id: "7",
name: "Lingkungan",
- href: "/darmasaba/lingkungan/pengelolaan-sampah-bank-sampah",
children: [
{
id: "7.1",
@@ -302,7 +295,6 @@ const navbarListMenu = [
}, {
id: "8",
name: "Pendidikan",
- href: "/darmasaba/pendidikan/info-sekolah",
children: [
{
id: "8.1",
diff --git a/types/env.d.ts b/types/env.d.ts
index d4dbd193..2e636b1a 100644
--- a/types/env.d.ts
+++ b/types/env.d.ts
@@ -3,6 +3,8 @@ declare namespace NodeJS {
DATABASE_URL?: string;
WIBU_UPLOAD_DIR?: string;
NEXT_PUBLIC_BASE_URL?: string;
+ NEXTAUTH_SECRET?: string;
+ NEXTAUTH_URL?: string;
+ WIBU_DOWNLOAD_DIR?: string;
}
}
-
diff --git a/types/menu-item.ts b/types/menu-item.ts
index dfc957bc..a6ecf87f 100644
--- a/types/menu-item.ts
+++ b/types/menu-item.ts
@@ -1,6 +1,9 @@
export type MenuItem = {
- id: string,
- name: string,
- href: string,
- children?: MenuItem[]
-}
\ No newline at end of file
+ id: string;
+ name: string;
+ href?: string;
+ children?: MenuItem[];
+} & (
+ { href: string; children?: MenuItem[] } |
+ { children: MenuItem[] }
+)
\ No newline at end of file
diff --git a/xx b/xx
new file mode 100755
index 00000000..c4eb993d
--- /dev/null
+++ b/xx
@@ -0,0 +1 @@
+curl -o assets.zip -s https://cld-dkr-makuro-seafile.wibudev.com/f/f425a33d23b54f0c892a/?dl=1
\ No newline at end of file
diff --git a/xx.ts b/xx.ts
new file mode 100644
index 00000000..2a1248a8
--- /dev/null
+++ b/xx.ts
@@ -0,0 +1,4 @@
+import 'colors'
+
+
+console.log("halo".blue)
\ No newline at end of file