From 55433128a91a90a030a5ac5414239a4c7c4af43b Mon Sep 17 00:00:00 2001 From: nico Date: Wed, 16 Jul 2025 00:20:55 +0800 Subject: [PATCH] API & UI Menu Inovasi & Submenu Layan Online Desa 2 tabs --- bun.lockb | Bin 320554 -> 321766 bytes package.json | 2 +- .../migration.sql | 245 +++++++++++ prisma/schema.prisma | 40 ++ .../_state/ekonomi/pasar-desa/pasar-desa.ts | 223 +++++----- .../_state/inovasi/ajukan-ide-inovatif.ts | 123 ++++++ .../_state/inovasi/layanan-online-desa.ts | 403 ++++++++++++++++++ .../inovasi/ajukan-ide-inovatif/[id]/page.tsx | 108 +++++ .../inovasi/ajukan-ide-inovatif/page.tsx | 108 +++-- .../layanan-online-desa/[id]/edit/page.tsx | 42 -- .../inovasi/layanan-online-desa/[id]/page.tsx | 66 --- .../layanan-online-desa/_lib/layoutTabs.tsx | 73 ++++ .../administrasi-online/[id]/page.tsx | 104 +++++ .../administrasi-online/page.tsx | 99 +++++ .../layanan-online-desa/create/page.tsx | 44 -- .../jenis-layanan/[id]/edit/page.tsx | 92 ++++ .../jenis-layanan/[id]/page.tsx | 103 +++++ .../jenis-layanan/create/page.tsx | 70 +++ .../jenis-layanan/page.tsx | 88 ++++ .../inovasi/layanan-online-desa/layout.tsx | 12 + .../inovasi/layanan-online-desa/page.tsx | 56 --- src/app/admin/_com/list_PageAdmin.tsx | 2 +- .../inovasi/ajukan-ide-inovatif/create.ts | 36 ++ .../_lib/inovasi/ajukan-ide-inovatif/del.ts | 34 ++ .../inovasi/ajukan-ide-inovatif/findMany.ts | 19 + .../inovasi/ajukan-ide-inovatif/findUnique.ts | 46 ++ .../_lib/inovasi/ajukan-ide-inovatif/index.ts | 27 ++ .../api/[[...slugs]]/_lib/inovasi/index.ts | 6 +- .../administrasi-online/create.ts | 43 ++ .../administrasi-online/del.ts | 21 + .../administrasi-online/findMany.ts | 43 ++ .../administrasi-online/findUnique.ts | 28 ++ .../administrasi-online/index.ts | 26 ++ .../jenis-layanan/create.ts | 26 ++ .../administrasi-online/jenis-layanan/del.ts | 33 ++ .../jenis-layanan/findMany.ts | 16 + .../jenis-layanan/findUnique.ts | 47 ++ .../jenis-layanan/index.ts | 32 ++ .../administrasi-online/jenis-layanan/updt.ts | 45 ++ .../_lib/inovasi/layanan-online-desa/index.ts | 13 + .../inovasi/ajukan-ide-inovatif/page.tsx | 97 ++++- .../administrasi-online/page.tsx | 117 +++++ .../informasi-desa/page.tsx | 28 ++ .../inovasi/layanan-online-desa/page.tsx | 51 +-- .../pengaduan-masyarakat/page.tsx | 28 ++ 45 files changed, 2566 insertions(+), 399 deletions(-) create mode 100644 prisma/migrations/20250715072239_nico_15_jul_25_1/migration.sql create mode 100644 src/app/admin/(dashboard)/_state/inovasi/ajukan-ide-inovatif.ts create mode 100644 src/app/admin/(dashboard)/_state/inovasi/layanan-online-desa.ts create mode 100644 src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/[id]/page.tsx delete mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/[id]/edit/page.tsx delete mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/[id]/page.tsx create mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/_lib/layoutTabs.tsx create mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/administrasi-online/[id]/page.tsx create mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/administrasi-online/page.tsx delete mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/create/page.tsx create mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/edit/page.tsx create mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/page.tsx create mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/create/page.tsx create mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/page.tsx create mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/layout.tsx delete mode 100644 src/app/admin/(dashboard)/inovasi/layanan-online-desa/page.tsx create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/updt.ts create mode 100644 src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/index.ts create mode 100644 src/app/darmasaba/(pages)/inovasi/layanan-online-desa/administrasi-online/page.tsx create mode 100644 src/app/darmasaba/(pages)/inovasi/layanan-online-desa/informasi-desa/page.tsx create mode 100644 src/app/darmasaba/(pages)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx diff --git a/bun.lockb b/bun.lockb index 4fd1477e6a1089aaf9878c88a12ec2c871c3f647..0da4afd13d1d7090d51b4331ada19f4d0eb4cca3 100755 GIT binary patch delta 50310 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

SG1X5lJgicxf*0wo^qdS|WA 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�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 delta 49876 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

*AAY_^=5?ZW4NM4CB%&b|7a9Kjh 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 - | null, + }> | null, async load() { const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get(); if (res.status === 200) { @@ -335,125 +334,135 @@ const kategoriProduk = proxy({ } }, }, - edit: { - id: "", - form: { ...kategoriProdukDefaultForm }, - loading: false, - - async load(id: string) { - if (!id) { - toast.warn("ID tidak valid"); - return null; + edit: { + id: "", + form: { ...kategoriProdukDefaultForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch(`/api/ekonomi/kategoriproduk/${id}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); } - - try { - const response = await fetch(`/api/ekonomi/kategoriproduk/${id}`, { - method: "GET", + const result = await response.json(); + if (result?.success) { + const data = result.data; + this.id = data.id; + this.form = { + nama: data.nama, + }; + return data; + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading kategori produk:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + + async update() { + const cek = kategoriProdukForm.safeParse(kategoriProduk.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + kategoriProduk.edit.loading = true; + const response = await fetch( + `/api/ekonomi/kategoriproduk/${kategoriProduk.edit.id}`, + { + method: "PUT", headers: { "Content-Type": "application/json", }, - }); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); + body: JSON.stringify({ + nama: kategoriProduk.edit.form.nama, + }), } - const result = await response.json(); - if (result?.success) { - const data = result.data; - this.id = data.id; - this.form = { - nama: data.nama, - }; - return data; - } else { - throw new Error(result?.message || "Gagal memuat data"); - } - } catch (error) { - console.error("Error loading kategori produk:", error); - toast.error( - error instanceof Error ? error.message : "Gagal memuat data" - ); - return null; - } - }, - - async update() { - const cek = kategoriProdukForm.safeParse(kategoriProduk.edit.form); - if (!cek.success) { - const err = `[${cek.error.issues - .map((v) => `${v.path.join(".")}`) - .join("\n")}] required`; - toast.error(err); - return false; - } + ); + + // Clone the response to avoid 'body already read' error + const responseClone = response.clone(); try { - kategoriProduk.edit.loading = true; - const response = await fetch( - `/api/ekonomi/kategoriproduk/${kategoriProduk.edit.id}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - nama: kategoriProduk.edit.form.nama, - }), - } - ); - - // Clone the response to avoid 'body already read' error - const responseClone = response.clone(); - - try { - const result = await response.json(); - - if (!response.ok) { - console.error('Update failed with status:', response.status, 'Response:', result); - throw new Error( - result?.message || `Gagal mengupdate kategori produk (${response.status})` - ); - } - - if (result.success) { - toast.success(result.message || "Berhasil memperbarui kategori produk"); - await kategoriProduk.findMany.load(); // refresh list - return true; - } else { - throw new Error(result.message || "Gagal mengupdate kategori produk"); - } - } catch (error) { - // If JSON parsing fails, try to get the response text for better error messages - try { - const text = await responseClone.text(); - console.error('Error response text:', text); - throw new Error(`Gagal memproses respons dari server: ${text}`); - } catch (textError) { - console.error('Error parsing response as text:', textError); - console.error('Original error:', error); - throw new Error('Gagal memproses respons dari server'); - } + const result = await response.json(); + + if (!response.ok) { + console.error( + "Update failed with status:", + response.status, + "Response:", + result + ); + throw new Error( + result?.message || + `Gagal mengupdate kategori produk (${response.status})` + ); + } + + if (result.success) { + toast.success( + result.message || "Berhasil memperbarui kategori produk" + ); + await kategoriProduk.findMany.load(); // refresh list + return true; + } else { + throw new Error( + result.message || "Gagal mengupdate kategori produk" + ); } } catch (error) { - console.error("Error updating kategori produk:", error); - toast.error( - error instanceof Error - ? error.message - : "Gagal mengupdate kategori produk" - ); - return false; - } finally { - kategoriProduk.edit.loading = false; + // If JSON parsing fails, try to get the response text for better error messages + try { + const text = await responseClone.text(); + console.error("Error response text:", text); + throw new Error(`Gagal memproses respons dari server: ${text}`); + } catch (textError) { + console.error("Error parsing response as text:", textError); + console.error("Original error:", error); + throw new Error("Gagal memproses respons dari server"); + } } - }, - reset() { - kategoriProduk.edit.id = ""; - kategoriProduk.edit.form = { ...kategoriProdukDefaultForm }; - }, + } catch (error) { + console.error("Error updating kategori produk:", error); + toast.error( + error instanceof Error + ? error.message + : "Gagal mengupdate kategori produk" + ); + return false; + } finally { + kategoriProduk.edit.loading = false; + } }, + reset() { + kategoriProduk.edit.id = ""; + kategoriProduk.edit.form = { ...kategoriProdukDefaultForm }; + }, + }, }); const pasarDesaState = proxy({ pasarDesa, - kategoriProduk + kategoriProduk, }); export default pasarDesaState; diff --git a/src/app/admin/(dashboard)/_state/inovasi/ajukan-ide-inovatif.ts b/src/app/admin/(dashboard)/_state/inovasi/ajukan-ide-inovatif.ts new file mode 100644 index 00000000..9421ed13 --- /dev/null +++ b/src/app/admin/(dashboard)/_state/inovasi/ajukan-ide-inovatif.ts @@ -0,0 +1,123 @@ +import ApiFetch from "@/lib/api-fetch"; +import { Prisma } from "@prisma/client"; +import { toast } from "react-toastify"; +import { proxy } from "valtio"; +import { z } from "zod"; + +const templateForm = z.object({ + name: z.string().min(1).max(50), + deskripsi: z.string().min(1).max(5000), + alamat: z.string().min(1).max(5000), + namaIde: z.string().min(1).max(5000), + masalah: z.string().min(1).max(5000), + benefit: z.string().min(1).max(5000), +}); + +const defaultForm = { + name: "", + deskripsi: "", + alamat: "", + namaIde: "", + masalah: "", + benefit: "", +}; + +const ajukanIdeInovatifState = proxy({ + create: { + form: { ...defaultForm }, + loading: false, + async create() { + const cek = templateForm.safeParse(ajukanIdeInovatifState.create.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + + try { + ajukanIdeInovatifState.create.loading = true; + const res = await ApiFetch.api.inovasi.ajukanideinovatif["create"].post( + ajukanIdeInovatifState.create.form + ); + if (res.status === 200) { + ajukanIdeInovatifState.findMany.load(); + return toast.success("Ajukan Ide Inovatif berhasil di kirim"); + } + console.log(res); + return toast.error("failed create"); + } catch (error) { + console.log((error as Error).message); + } finally { + ajukanIdeInovatifState.create.loading = false; + } + }, + }, + findMany: { + data: null as + | Prisma.AjukanIdeInovatifGetPayload<{ + omit: { + isActive: true; + }; + }>[] + | null, + async load() { + const res = await ApiFetch.api.inovasi.ajukanideinovatif["find-many"].get(); + if (res.status === 200) { + ajukanIdeInovatifState.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.AjukanIdeInovatifGetPayload<{ + omit: { + isActive: true; + }; + }> | null, + async load(id: string) { + try { + const res = await fetch(`/api/inovasi/ajukanideinovatif/${id}`); + if (res.ok) { + const data = await res.json(); + ajukanIdeInovatifState.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + ajukanIdeInovatifState.findUnique.data = null; + } + } catch (error) { + console.error("Error loading ajukan ide inovatif:", error); + ajukanIdeInovatifState.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + ajukanIdeInovatifState.delete.loading = true; + const response = await fetch(`/api/inovasi/ajukanideinovatif/del/${id}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }); + const result = await response.json(); + + if (response.ok) { + toast.success(result.message || "Ajukan Ide Inovatif berhasil dihapus"); + await ajukanIdeInovatifState.findMany.load(); + } else { + toast.error(result?.message || "Gagal menghapus ajukan ide inovatif"); + } + } catch (error) { + console.log((error as Error).message); + toast.error("Terjadi kesalahan saat menghapus ajukan ide inovatif"); + } finally { + ajukanIdeInovatifState.delete.loading = false; + } + }, + }, +}); +export default ajukanIdeInovatifState; diff --git a/src/app/admin/(dashboard)/_state/inovasi/layanan-online-desa.ts b/src/app/admin/(dashboard)/_state/inovasi/layanan-online-desa.ts new file mode 100644 index 00000000..9e08ec9d --- /dev/null +++ b/src/app/admin/(dashboard)/_state/inovasi/layanan-online-desa.ts @@ -0,0 +1,403 @@ +import ApiFetch from "@/lib/api-fetch"; +import { Prisma } from "@prisma/client"; +import { toast } from "react-toastify"; +import { proxy } from "valtio"; +import { z } from "zod"; + +// ========================================= ADMINISTRASI ONLINE ========================================= // +const templateAdministrasiOnlineForm = z.object({ + name: z.string().min(1, "Nama minimal 1 karakter"), + alamat: z.string().min(1, "Alamat minimal 1 karakter"), + nomorTelepon: z.string().min(1, "Nomor telepon minimal 1 karakter"), + jenisLayananId: z.string().min(1, "Jenis layanan minimal 1 karakter"), +}); + +const defaultAdministrasiOnlineForm = { + name: "", + alamat: "", + nomorTelepon: "", + jenisLayananId: "", +}; + +const administrasiOnline = proxy({ + create: { + form: { ...defaultAdministrasiOnlineForm }, + loading: false, + async create() { + const cek = templateAdministrasiOnlineForm.safeParse( + administrasiOnline.create.form + ); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + try { + administrasiOnline.create.loading = true; + const res = + await ApiFetch.api.inovasi.layananonlinedesa.administrasionline[ + "create" + ].post(administrasiOnline.create.form); + if (res.status === 200) { + administrasiOnline.findMany.load(); + return toast.success("Data berhasil ditambahkan"); + } + return toast.error("Gagal menambahkan data"); + } catch (error) { + console.log(error); + toast.error("Gagal menambahkan data"); + } finally { + administrasiOnline.create.loading = false; + } + }, + }, + findMany: { + data: null as Array< + Prisma.AdministrasiOnlineGetPayload<{ + include: { + jenisLayanan: true; + }; + }> + > | null, + page: 1, + totalPages: 1, + loading: false, + + async load(page = 1, limit = 10) { + administrasiOnline.findMany.loading = true; + administrasiOnline.findMany.page = page; + try { + const res = + await ApiFetch.api.inovasi.layananonlinedesa.administrasionline[ + "find-many" + ].get({ + query: { + page, + limit, + }, + }); + + if (res.status === 200 && res.data?.success) { + administrasiOnline.findMany.data = res.data.data ?? []; + administrasiOnline.findMany.totalPages = res.data.totalPages ?? 1; + } + } catch (err) { + console.error("Gagal fetch administrasi online paginated:", err); + } finally { + administrasiOnline.findMany.loading = false; + } + }, + }, + findUnique: { + data: null as Prisma.AdministrasiOnlineGetPayload<{ + include: { + jenisLayanan: true; + }; + }> | null, + async load(id: string) { + try { + const res = await fetch( + `/api/inovasi/layananonlinedesa/administrasionline/${id}` + ); + if (res.ok) { + const data = await res.json(); + administrasiOnline.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch administrasi online:", res.statusText); + administrasiOnline.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching administrasi online:", error); + administrasiOnline.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + administrasiOnline.delete.loading = true; + + const response = await fetch( + `/api/inovasi/layananonlinedesa/administrasionline/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success( + result.message || "Administrasi online berhasil dihapus" + ); + await administrasiOnline.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus administrasi online"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus administrasi online"); + } finally { + administrasiOnline.delete.loading = false; + } + }, + }, +}); + +// ========================================= JENIS LAYANAN ========================================= // +const templateJenisLayananForm = z.object({ + nama: z.string().min(1, "Nama minimal 1 karakter"), + deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"), +}); + +const defaultJenisLayananForm = { + nama: "", + deskripsi: "", +}; + +const jenisLayanan = proxy({ + create: { + form: { ...defaultJenisLayananForm }, + loading: false, + async create() { + const cek = templateJenisLayananForm.safeParse(jenisLayanan.create.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + try { + jenisLayanan.create.loading = true; + const res = + await ApiFetch.api.inovasi.layananonlinedesa.administrasionline.jenislayanan[ + "create" + ].post(jenisLayanan.create.form); + if (res.status === 200) { + jenisLayanan.findMany.load(); + return toast.success("Data berhasil ditambahkan"); + } + return toast.error("Gagal menambahkan data"); + } catch (error) { + console.log(error); + toast.error("Gagal menambahkan data"); + } finally { + jenisLayanan.create.loading = false; + } + }, + }, + findMany: { + data: null as Array<{ + id: string; + nama: string; + deskripsi: string; + }> | null, + async load() { + const res = + await ApiFetch.api.inovasi.layananonlinedesa.administrasionline.jenislayanan[ + "find-many" + ].get(); + if (res.status === 200) { + jenisLayanan.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.JenisLayananGetPayload<{ + omit: { isActive: true }; + }> | null, + async load(id: string) { + try { + const res = await fetch( + `/api/inovasi/layananonlinedesa/administrasionline/jenislayanan/${id}` + ); + if (res.ok) { + const data = await res.json(); + jenisLayanan.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + jenisLayanan.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + jenisLayanan.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + jenisLayanan.delete.loading = true; + + const response = await fetch( + `/api/inovasi/layananonlinedesa/administrasionline/jenislayanan/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "Jenis layanan berhasil dihapus"); + await jenisLayanan.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus jenis layanan"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus jenis layanan"); + } finally { + jenisLayanan.delete.loading = false; + } + }, + }, + edit: { + id: "", + form: { ...defaultJenisLayananForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch( + `/api/inovasi/layananonlinedesa/administrasionline/jenislayanan/${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, + deskripsi: data.deskripsi, + }; + return data; + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading jenis layanan:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + + async update() { + const cek = templateJenisLayananForm.safeParse(jenisLayanan.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + jenisLayanan.edit.loading = true; + const response = await fetch( + `/api/inovasi/layananonlinedesa/administrasionline/jenislayanan/${jenisLayanan.edit.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + nama: jenisLayanan.edit.form.nama, + deskripsi: jenisLayanan.edit.form.deskripsi, + }), + } + ); + + // Clone the response to avoid 'body already read' error + const responseClone = response.clone(); + + try { + const result = await response.json(); + + if (!response.ok) { + console.error( + "Update failed with status:", + response.status, + "Response:", + result + ); + throw new Error( + result?.message || + `Gagal mengupdate jenis layanan (${response.status})` + ); + } + + if (result.success) { + toast.success( + result.message || "Berhasil memperbarui jenis layanan" + ); + await jenisLayanan.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal mengupdate jenis layanan"); + } + } catch (error) { + // If JSON parsing fails, try to get the response text for better error messages + try { + const text = await responseClone.text(); + console.error("Error response text:", text); + throw new Error(`Gagal memproses respons dari server: ${text}`); + } catch (textError) { + console.error("Error parsing response as text:", textError); + console.error("Original error:", error); + throw new Error("Gagal memproses respons dari server"); + } + } + } catch (error) { + console.error("Error updating jenis layanan:", error); + toast.error( + error instanceof Error + ? error.message + : "Gagal mengupdate jenis layanan" + ); + return false; + } finally { + jenisLayanan.edit.loading = false; + } + }, + reset() { + jenisLayanan.edit.id = ""; + jenisLayanan.edit.form = { ...defaultJenisLayananForm }; + }, + }, +}); + +const layananonlineDesa = proxy({ + administrasiOnline, + jenisLayanan, +}); + +export default layananonlineDesa; 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 new file mode 100644 index 00000000..f9ea630c --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/[id]/page.tsx @@ -0,0 +1,108 @@ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack, IconX } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; +import ajukanIdeInovatifState from '../../../_state/inovasi/ajukan-ide-inovatif'; + +function DetailAjukanIdeInofativDesa() { + const state = useProxy(ajukanIdeInovatifState) + const [modalHapus, setModalHapus] = useState(false); + const [selectedId, setSelectedId] = useState(null); + const router = useRouter() + const params = useParams() + + useShallowEffect(() => { + state.findUnique.load(params?.id as string) + }, []) + + const handleHapus = () => { + if (selectedId) { + state.delete.byId(selectedId) + setModalHapus(false) + setSelectedId(null) + router.push("/admin/inovasi/ajukan-ide-inovatif") + } + } + + if (!state.findUnique.data) { + return ( + + + + ) + } + + return ( + + + + + + + + Detail Ajukan Ide Inovatif Desa + + + {state.findUnique.data ? ( + + + + Nama + {state.findUnique.data?.name} + + + Alamat + + + + Nama Ide Inovatif + {state.findUnique.data?.namaIde} + + + Deskripsi + + + + Masalah + {state.findUnique.data?.masalah} + + + Benefit + {state.findUnique.data?.benefit} + + + + ) : null} + + + + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text='Apakah anda yakin ingin menghapus ajukan ide inovatif ini?' + /> + + ); +} + +export default DetailAjukanIdeInofativDesa; 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 15c2392a..b2d2bfd3 100644 --- a/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/page.tsx +++ b/src/app/admin/(dashboard)/inovasi/ajukan-ide-inovatif/page.tsx @@ -1,59 +1,91 @@ +'use client' import colors from '@/con/colors'; -import { Box, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Title } from '@mantine/core'; -import { IconSearch } from '@tabler/icons-react'; -import React from 'react'; +import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconDeviceImac, 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 ajukanIdeInovatifState from '../../_state/inovasi/ajukan-ide-inovatif'; -function AjukanIdeInofativ() { +function AjukanIdeInovatif() { + const [search, setSearch] = useState(""); return ( } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} /> - + ); } -function ListAjukanIdeInovatif() { +function ListAjukanIdeInovatif({ search }: { search: string }) { + const state = useProxy(ajukanIdeInovatifState) + const router = useRouter() + useShallowEffect(() => { + state.findMany.load() + }, []) + + const filteredData = (state.findMany.data || []).filter(item => { + const keyword = search.toLowerCase(); + return ( + item.name.toLowerCase().includes(keyword) || + item.deskripsi.toLowerCase().includes(keyword) || + item.alamat.toLowerCase().includes(keyword) || + item.namaIde.toLowerCase().includes(keyword) || + item.masalah.toLowerCase().includes(keyword) || + item.benefit.toLowerCase().includes(keyword) + ); + }); + + if (!state.findMany.data) { + return ( + + + + ) + } return ( - - List Ajukan Ide Inovatif - - - - - No - Nama - Alamat - Nama Ide Inovatif - Deskripsi - Masalah yang ingin diatasi - Benefit - - - - - - 1 - nama - alamat - ide inovatif - deskripsi - masalah - benefit - - - -
-
+ List Ajukan Ide Inovatif + + + + Nama + Alamat + Nama Ide Inovatif + Detail + + + + {filteredData.map((item) => ( + + {item.name} + + + + + + + + + + + ))} + +
- ) + ); } -export default AjukanIdeInofativ; +export default AjukanIdeInovatif; diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/[id]/edit/page.tsx deleted file mode 100644 index 558fd83d..00000000 --- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/[id]/edit/page.tsx +++ /dev/null @@ -1,42 +0,0 @@ -'use client' -import colors from "@/con/colors"; -import { Box, Button, Paper, Stack, Title, TextInput, Group, Text } from "@mantine/core"; -import { IconArrowBack, IconImageInPicture } from "@tabler/icons-react"; -import { useRouter } from "next/navigation"; -// import { KeamananEditor } from "../../../keamanan/_com/keamananEditor"; - -export default function EditLayananOnlineDesa() { - const router = useRouter(); - return ( - - - - - - - - Edit Layanan Online Desa - - Masukkan Image - - - Nama Layanan Online Desa} - placeholder='Masukkan nama LayananOnlineDesa' - /> - - Deskripsi Layanan Online Desa - {/* */} - - - - - - - - ); - } \ No newline at end of file diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/[id]/page.tsx deleted file mode 100644 index ffa68d5e..00000000 --- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/[id]/page.tsx +++ /dev/null @@ -1,66 +0,0 @@ -'use client' -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Flex, Text, Image } from '@mantine/core'; -import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import React from 'react'; -// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; - -function DetailLayananOnlineDesa() { - const router = useRouter(); - return ( - - - - - - - Detail Layanan Online Desa - - - - - Gambar - gambar - - - Nama Layanan Online Desa - Test Judul - - - Deskripsi - Test Deskripsi - - - Konten - Test Konten - - - - - - - - - - - - - {/* Modal Hapus - setModalHapus(false)} - onConfirm={handleHapus} - text="Apakah anda yakin ingin menghapus potensi ini?" - /> */} - - ); -} - -export default DetailLayananOnlineDesa; diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/_lib/layoutTabs.tsx new file mode 100644 index 00000000..760e16a7 --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/_lib/layoutTabs.tsx @@ -0,0 +1,73 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import colors from '@/con/colors'; +import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core'; +import { usePathname, useRouter } from 'next/navigation'; +import React, { useEffect, useState } from 'react'; + +function LayoutTabsLayananOnlineDesa({ children }: { children: React.ReactNode }) { + const router = useRouter() + const pathname = usePathname() + const tabs = [ + { + label: "Administrasi Online", + value: "administrasionline", + href: "/admin/inovasi/layanan-online-desa/administrasi-online" + }, + { + label: "Jenis Layanan", + value: "jenislayanan", + href: "/admin/inovasi/layanan-online-desa/jenis-layanan" + }, + { + label: "Pengaduan Masyarakat", + value: "pengaduanmasyarakat", + href: "/admin/inovasi/layanan-online-desa/pengaduan-masyarakat" + }, + { + label: "Informasi Desa", + value: "informasidesa", + href: "/admin/inovasi/layanan-online-desa/informasi-desa" + } + + ]; + const curentTab = tabs.find(tab => tab.href === pathname) + const [activeTab, setActiveTab] = useState(curentTab?.value || tabs[0].value); + + const handleTabChange = (value: string | null) => { + const tab = tabs.find(t => t.value === value) + if (tab) { + router.push(tab.href) + } + setActiveTab(value) + } + + useEffect(() => { + const match = tabs.find(tab => tab.href === pathname) + if (match) { + setActiveTab(match.value) + } + }, [pathname]) + + return ( + + Layanan Online Desa + + + {tabs.map((e, i) => ( + {e.label} + ))} + + {tabs.map((e, i) => ( + + {/* Konten dummy, bisa diganti tergantung routing */} + <> + + ))} + + {children} + + ); +} + +export default LayoutTabsLayananOnlineDesa; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/administrasi-online/[id]/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/administrasi-online/[id]/page.tsx new file mode 100644 index 00000000..0741e1f5 --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/administrasi-online/[id]/page.tsx @@ -0,0 +1,104 @@ +'use client' +import { useProxy } from 'valtio/utils'; + +import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack, IconTrash } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; + +import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus'; +import layananonlineDesa from '@/app/admin/(dashboard)/_state/inovasi/layanan-online-desa'; +import colors from '@/con/colors'; + + +function DetailAdministrasiOnline() { + const beritaState = useProxy(layananonlineDesa) + const [modalHapus, setModalHapus] = useState(false) + const [selectedId, setSelectedId] = useState(null) + const params = useParams() + const router = useRouter() + + useShallowEffect(() => { + beritaState.administrasiOnline.findUnique.load(params?.id as string) + }, []) + + + const handleHapus = () => { + if (selectedId) { + beritaState.administrasiOnline.delete.byId(selectedId) + setModalHapus(false) + setSelectedId(null) + router.push("/admin/inovasi/layanan-online-desa/administrasi-online") + } + } + + if (!beritaState.administrasiOnline.findUnique.data) { + return ( + + + + ) + } + + return ( + + + + + + + + Detail Administrasi Online + + + {beritaState.administrasiOnline.findUnique.data ? ( + + + + Nama + {beritaState.administrasiOnline.findUnique.data?.name} + + + Alamat + {beritaState.administrasiOnline.findUnique.data?.alamat} + + + Nomor Telepon + {beritaState.administrasiOnline.findUnique.data?.nomorTelepon} + + + Jenis Layanan + {beritaState.administrasiOnline.findUnique.data?.jenisLayanan?.nama} + + + + ) : null} + + + + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text='Apakah anda yakin ingin menghapus administrasi online ini?' + /> + + ); +} + +export default DetailAdministrasiOnline; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/administrasi-online/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/administrasi-online/page.tsx new file mode 100644 index 00000000..f2b24a32 --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/administrasi-online/page.tsx @@ -0,0 +1,99 @@ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Center, Pagination, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Title } from '@mantine/core'; +import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; + +import { useShallowEffect } from '@mantine/hooks'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../../_com/header'; +import layananonlineDesa from '../../../_state/inovasi/layanan-online-desa'; + +function AdministrasiOnline() { + const [search, setSearch] = useState(""); + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListAdministrasiOnline({ search }: { search: string }) { + const listState = useProxy(layananonlineDesa.administrasiOnline) + const router = useRouter(); + const { + data, + page, + totalPages, + loading, + load, + } = listState.findMany; + + useShallowEffect(() => { + load(page, 10); + }, [page]); + + const filteredData = (data || []).filter(item => { + const keyword = search.toLowerCase(); + return ( + item.name.toLowerCase().includes(keyword) || + item.alamat.toLowerCase().includes(keyword) || + item.nomorTelepon.toLowerCase().includes(keyword) + ); + }); + + if (loading || !data) { + return ; + } + + return ( + + + List Administrasi Online + + + + Nama Layanan + Alamat + Nomor Telepon + Detail + + + + {filteredData.map((item) => ( + + {item.name} + {item.alamat} + {item.nomorTelepon} + + + + + ))} + +
+
+

+ load(newPage)} // ini penting! + total={totalPages} + mt="md" + mb="md" + /> +
+ + ); +} + +export default AdministrasiOnline; diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/create/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/create/page.tsx deleted file mode 100644 index a5a78206..00000000 --- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/create/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -'use client' -import colors from '@/con/colors'; -import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; -import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { KeamananEditor } from '../../../keamanan/_com/keamananEditor'; - -function CreateLayananOnlineDesa() { - const router = useRouter(); - return ( - - - - - - - - Create Layanan Online Desa - - Masukkan Image - - - Nama Layanan Online Desa} - placeholder='Masukkan nama LayananOnlineDesa' - /> - - Deskripsi Layanan Online Desa - - - - - - - - - ); -} - -export default CreateLayananOnlineDesa; 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 new file mode 100644 index 00000000..93ffb0ef --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/edit/page.tsx @@ -0,0 +1,92 @@ +'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 { 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 EditJenisLayanan() { + 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, + }) + + 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, + }); + } + } catch (error) { + console.error("Error loading jenis layanan:", error); + toast.error("Gagal memuat data jenis layanan"); + } + }; + loadJenisLayanan(); + }, [params?.id]); + + const handleSubmit = async () => { + 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") + } catch (error) { + console.error("Error updating jenis layanan:", error); + toast.error("Terjadi kesalahan saat memperbarui jenis layanan"); + } + } + + return ( + + + + + + + Edit Jenis Layanan + { + setFormData({ ...formData, nama: val.target.value }); + }} + label={Nama Jenis Layanan} + placeholder="masukkan nama jenis layanan" + /> + + Deskripsi + { + setFormData({ ...formData, deskripsi: htmlContent }); + }} + /> + + + + + + ); +} + +export default EditJenisLayanan; 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 new file mode 100644 index 00000000..52f84e1c --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/[id]/page.tsx @@ -0,0 +1,103 @@ +'use client' +import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus'; +import layananonlineDesa from '@/app/admin/(dashboard)/_state/inovasi/layanan-online-desa'; +import colors from '@/con/colors'; +import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; + +function DetailJenisLayanan() { + const state = useProxy(layananonlineDesa.jenisLayanan) + const [modalHapus, setModalHapus] = useState(false) + const [selectedId, setSelectedId] = useState(null) + const params = useParams() + const router = useRouter() + + useShallowEffect(() => { + state.findUnique.load(params?.id as string) + }, []) + + const handleHapus = () => { + if (selectedId) { + state.delete.byId(selectedId) + setModalHapus(false) + setSelectedId(null) + router.push("/admin/inovasi/layanan-online-desa/jenis-layanan") + } + } + + if (!state.findUnique.data) { + return ( + + + + ) + } + + return ( + + + + + + + Detail Jenis Layanan + {state.findUnique.data ? ( + + + + Nama + {state.findUnique.data?.nama} + + + Deskripsi + + + + + + + + + ) : null} + + + + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text='Apakah anda yakin ingin menghapus jenis layanan ini?' + /> + + ); +} + +export default DetailJenisLayanan; 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 new file mode 100644 index 00000000..4d00dcd7 --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/create/page.tsx @@ -0,0 +1,70 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import layananonlineDesa from '@/app/admin/(dashboard)/_state/inovasi/layanan-online-desa'; +import colors from '@/con/colors'; +import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useEffect } from 'react'; +import { useProxy } from 'valtio/utils'; + +function CreateJenisLayanan() { + const router = useRouter(); + const statePasar = useProxy(layananonlineDesa.jenisLayanan) + + useEffect(() => { + statePasar.findMany.load(); + }, []); + + const resetForm = () => { + statePasar.create.form = { + nama: "", + deskripsi: "", + }; + } + + const handleSubmit = async () => { + await statePasar.create.create(); + resetForm(); + router.push("/admin/inovasi/layanan-online-desa/jenis-layanan") + } + + return ( + + + + + + + + + Create Jenis Layanan + { + statePasar.create.form.nama = val.target.value; + }} + label={Nama Jenis Layanan} + placeholder='Masukkan nama jenis layanan' + /> + { + statePasar.create.form.deskripsi = val.target.value; + }} + label={Deskripsi} + placeholder='Masukkan deskripsi' + /> + + + + + + + + ); +} + +export default CreateJenisLayanan; diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/page.tsx new file mode 100644 index 00000000..3eb5cec3 --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-layanan/page.tsx @@ -0,0 +1,88 @@ +'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 { IconDeviceImac, 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 JudulList from '../../../_com/judulList'; +import layananonlineDesa from '../../../_state/inovasi/layanan-online-desa'; + + +function JenisLayanan() { + const [search, setSearch] = useState("") + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListJenisLayanan({ search }: { search: string }) { + const stateList = useProxy(layananonlineDesa.jenisLayanan) + const router = useRouter() + + useShallowEffect(() => { + stateList.findMany.load() + }, []) + + + const filteredData = (stateList.findMany.data || []).filter(item => { + const keyword = search.toLowerCase(); + return ( + item.nama.toLowerCase().includes(keyword) + ); + }); + + if (!stateList.findMany.data) { + return ( + + + + ) + } + + return ( + + + + + + + Nama Jenis Layanan + Deskripsi + Detail + + + + {filteredData.map((item) => ( + + {item.nama} + {item.deskripsi} + + + + + ))} + +
+
+
+ ); +} + +export default JenisLayanan; diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/layout.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/layout.tsx new file mode 100644 index 00000000..dcb53527 --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/layout.tsx @@ -0,0 +1,12 @@ +'use client' +import LayoutTabsLayananOnlineDesa from "./_lib/layoutTabs"; + +function Layout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} + +export default Layout; diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/page.tsx deleted file mode 100644 index 2db5b2f7..00000000 --- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/page.tsx +++ /dev/null @@ -1,56 +0,0 @@ -'use client' -import colors from '@/con/colors'; -import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; -import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; -import HeaderSearch from '../../_com/header'; -import JudulList from '../../_com/judulList'; -import { useRouter } from 'next/navigation'; - -function LayananOnlineDesa() { - return ( - - } - /> - - - ); -} - -function ListLayananOnlineDesa() { - const router = useRouter(); - return ( - - - - - - - Nama Layanan - Deskripsi - Detail - - - - - Layanan Online Desa 1 - Deskripsi Layanan Online Desa 1 - - - - - -
-
-
- ); -} - -export default LayananOnlineDesa; diff --git a/src/app/admin/_com/list_PageAdmin.tsx b/src/app/admin/_com/list_PageAdmin.tsx index 86eb9f81..3004b84d 100644 --- a/src/app/admin/_com/list_PageAdmin.tsx +++ b/src/app/admin/_com/list_PageAdmin.tsx @@ -276,7 +276,7 @@ export const navBar = [ { id: "Inovasi_2", name: "Layanan Online Desa", - path: "/admin/inovasi/layanan-online-desa" + path: "/admin/inovasi/layanan-online-desa/administrasi-online" }, { id: "Inovasi_3", diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/create.ts b/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/create.ts new file mode 100644 index 00000000..6373f417 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/create.ts @@ -0,0 +1,36 @@ +import prisma from "@/lib/prisma"; +import { Prisma } from "@prisma/client"; +import { Context } from "elysia"; + +type FormCreateAjukanIdeInovatif = Prisma.AjukanIdeInovatifGetPayload<{ + select: { + name: true; + deskripsi: true; + alamat: true; + namaIde: true; + masalah: true; + benefit: true; + } +}> +export default async function ajukanIdeInovatifCreate(context: Context){ + const body = context.body as FormCreateAjukanIdeInovatif; + + await prisma.ajukanIdeInovatif.create({ + data: { + name: body.name, + deskripsi: body.deskripsi, + alamat: body.alamat, + namaIde: body.namaIde, + masalah: body.masalah, + benefit: body.benefit, + } + }) + + return { + success: true, + message: "Success create ajukan ide inovatif", + data: { + ...body, + } + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/del.ts b/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/del.ts new file mode 100644 index 00000000..d56895e7 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/del.ts @@ -0,0 +1,34 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function ajukanIdeInovatifDelete(context: Context) { + const id = context.params?.id as string; + + if (!id) { + return { + status: 400, + body: "ID tidak diberikan", + }; + } + + const ajukanIdeInovatif = await prisma.ajukanIdeInovatif.findUnique({ + where: { id }, + }); + + if (!ajukanIdeInovatif) { + return { + status: 404, + body: "Ajukan ide inovatif tidak ditemukan", + }; + } + + await prisma.ajukanIdeInovatif.delete({ + where: { id }, + }); + + return { + success: true, + message: "Ajukan ide inovatif berhasil dihapus", + status: 200, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/findMany.ts b/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/findMany.ts new file mode 100644 index 00000000..5b1b46fc --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/findMany.ts @@ -0,0 +1,19 @@ +import prisma from "@/lib/prisma"; + +export default async function ajukanIdeInovatifFindMany() { + try { + const data = await prisma.ajukanIdeInovatif.findMany({}); + + return { + success: true, + message: "Success fetch ajukan ide inovatif", + data, + }; + } catch (error) { + console.error("Find many error:", error); + return { + success: false, + message: "Failed fetch ajukan ide inovatif", + }; + } +} diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/findUnique.ts b/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/findUnique.ts new file mode 100644 index 00000000..deda1b60 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/findUnique.ts @@ -0,0 +1,46 @@ +import prisma from "@/lib/prisma"; + +export default async function ajukanIdeInovatifFindUnique(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 ditemukan", + }, {status: 400}); + } + + try { + if (typeof id !== 'string') { + return Response.json({ + success: false, + message: "ID tidak valid", + }, {status: 400}); + } + + const data = await prisma.ajukanIdeInovatif.findUnique({ + where: { id }, + }); + + if (!data) { + return Response.json({ + success: false, + message: "Ajukan ide inovatif tidak ditemukan", + }, {status: 404}); + } + + return Response.json({ + success: true, + message: "Success fetch ajukan ide inovatif by ID", + data, + }, {status: 200}); + } catch (error) { + console.error("Find by ID error:", error); + return Response.json({ + success: false, + message: "Gagal mengambil ajukan ide inovatif: " + (error instanceof Error ? error.message : 'Unknown error'), + }, {status: 500}); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/index.ts b/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/index.ts new file mode 100644 index 00000000..a84de934 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/ajukan-ide-inovatif/index.ts @@ -0,0 +1,27 @@ +import Elysia, { t } from "elysia"; +import ajukanIdeInovatifCreate from "./create"; +import ajukanIdeInovatifFindMany from "./findMany"; +import ajukanIdeInovatifFindUnique from "./findUnique"; +import ajukanIdeInovatifDelete from "./del"; + +const AjukanIdeInovatif = new Elysia({ + prefix: "/ajukanideinovatif", + tags: ["Inovasi/AjukanIde"], +}) + .post("/create", ajukanIdeInovatifCreate, { + body: t.Object({ + name: t.String(), + deskripsi: t.String(), + alamat: t.String(), + namaIde: t.String(), + masalah: t.String(), + benefit: t.String(), + }), + }) + .get("/find-many", ajukanIdeInovatifFindMany) + .get("/:id", async (context) => { + const response = await ajukanIdeInovatifFindUnique(context.request); + return response; + }) + .delete("/del/:id", ajukanIdeInovatifDelete); +export default AjukanIdeInovatif; diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/index.ts b/src/app/api/[[...slugs]]/_lib/inovasi/index.ts index 66a57c55..5abb4978 100644 --- a/src/app/api/[[...slugs]]/_lib/inovasi/index.ts +++ b/src/app/api/[[...slugs]]/_lib/inovasi/index.ts @@ -3,6 +3,8 @@ import DesaDigital from "./desa-digital"; import ProgramKreatif from "./program-kreatif"; import KolaborasiInovasi from "./kolaborasi-inovasi"; import InfoTekno from "./info-teknologi"; +import AjukanIdeInovatif from "./ajukan-ide-inovatif"; +import LayananOnlineDesa from "./layanan-online-desa"; const Inovasi = new Elysia({ prefix: "/api/inovasi", @@ -10,7 +12,9 @@ const Inovasi = new Elysia({ }) .use(DesaDigital) .use(ProgramKreatif) - .use(KolaborasiInovasi) + .use(KolaborasiInovasi) .use(InfoTekno) + .use(AjukanIdeInovatif) + .use(LayananOnlineDesa) export default Inovasi; diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/create.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/create.ts new file mode 100644 index 00000000..1c5b050b --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/create.ts @@ -0,0 +1,43 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + name: string; + alamat: string; + nomorTelepon: string; + jenisLayananId: string; +}; + +export default async function administrasiOnlineCreate(context: Context) { + const body = context.body as FormCreate; + + if (!body.jenisLayananId) { + throw new Error("jenisLayananId wajib diisi"); + } + + try { + // Create langsung data AdministrasiOnline + const result = await prisma.administrasiOnline.create({ + data: { + name: body.name, + alamat: body.alamat, + nomorTelepon: body.nomorTelepon, + jenisLayananId: body.jenisLayananId, // relasi ke JenisLayanan + }, + include: { + jenisLayanan: true, // Include data relasi + }, + }); + + return { + success: true, + message: "Berhasil membuat administrasi online", + data: result, + }; + } catch (error) { + console.error("Error creating administrasi online:", error); + throw new Error( + "Gagal membuat administrasi online: " + (error as Error).message + ); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/del.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/del.ts new file mode 100644 index 00000000..9fc1e0b6 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/del.ts @@ -0,0 +1,21 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function administrasiOnlineDelete(context: Context) { + const { params } = context; + const id = params?.id as string; + + if (!id) { + throw new Error("ID tidak ditemukan dalam parameter"); + } + + const deleted = await prisma.administrasiOnline.delete({ + where: { id }, + }); + + return { + success: true, + message: "Berhasil menghapus administrasi online", + data: deleted, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/findMany.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/findMany.ts new file mode 100644 index 00000000..21728e98 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/findMany.ts @@ -0,0 +1,43 @@ +// /api/berita/findManyPaginated.ts +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function administrasiOnlineFindMany(context: Context) { + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const skip = (page - 1) * limit; + + try { + const [data, total] = await Promise.all([ + prisma.administrasiOnline.findMany({ + where: { isActive: true }, + include: { + jenisLayanan: true, + }, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, // opsional, kalau mau urut berdasarkan waktu + }), + prisma.administrasiOnline.count({ + where: { isActive: true } + }) + ]); + + return { + success: true, + message: "Success fetch administrasi online with pagination", + data, + page, + totalPages: Math.ceil(total / limit), + total, + }; + } catch (e) { + console.error("Find many paginated error:", e); + return { + success: false, + message: "Failed fetch administrasi online with pagination", + }; + } +} + +export default administrasiOnlineFindMany; diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/findUnique.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/findUnique.ts new file mode 100644 index 00000000..e1e5a51e --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/findUnique.ts @@ -0,0 +1,28 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function administrasiOnlineFindUnique(context: Context) { + const { params } = context; + const id = params?.id as string; + + if (!id) { + throw new Error("ID tidak ditemukan dalam parameter"); + } + + const data = await prisma.administrasiOnline.findUnique({ + where: { id }, + include: { + jenisLayanan: true, + }, + }); + + if (!data) { + throw new Error("Administrasi online tidak ditemukan"); + } + + return { + success: true, + message: "Data administrasi online ditemukan", + data, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/index.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/index.ts new file mode 100644 index 00000000..df15f4fa --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/index.ts @@ -0,0 +1,26 @@ +import Elysia from "elysia"; +import JenisLayanan from "./jenis-layanan"; +import administrasiOnlineFindMany from "./findMany"; +import administrasiOnlineFindUnique from "./findUnique"; +import administrasiOnlineDelete from "./del"; +import administrasiOnlineCreate from "./create"; +import { t } from "elysia"; + +const AdministrasiOnline = new Elysia({ + prefix: "/administrasionline", + tags: ["Inovasi/Layanan Online Desa/Administrasi Online"], +}) + .get("/find-many", administrasiOnlineFindMany) + .get("/:id", administrasiOnlineFindUnique) + .delete("/del/:id", administrasiOnlineDelete) + .post("/create", administrasiOnlineCreate, { + body: t.Object({ + name: t.String(), + alamat: t.String(), + nomorTelepon: t.String(), + jenisLayananId: t.String(), + }), + }) + .use(JenisLayanan); + +export default AdministrasiOnline; diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/create.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/create.ts new file mode 100644 index 00000000..ebf6632d --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/create.ts @@ -0,0 +1,26 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + + +export default async function jenisLayananCreate(context: Context) { + const body = context.body as {nama: string, deskripsi: string}; + + if (!body.nama) { + return { + success: false, + message: "Nama is required", + }; + } + + const jenisLayanan = await prisma.jenisLayanan.create({ + data: { + nama: body.nama, + deskripsi: body.deskripsi, + }, + }); + return { + success: true, + message: "Success create jenis layanan", + data: jenisLayanan + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/del.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/del.ts new file mode 100644 index 00000000..d2d26ea5 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/del.ts @@ -0,0 +1,33 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +const jenisLayananDelete = async (context: Context) => { + const id = context.params.id; + if (!id) { + return { + success: false, + message: "ID is required", + } + } + + const jenisLayanan = await prisma.jenisLayanan.delete({ + where: { + id: id, + }, + }) + + if(!jenisLayanan) { + return { + success: false, + message: "Jenis layanan tidak ditemukan", + } + } + + return { + success: true, + message: "Success delete jenis layanan", + data: jenisLayanan, + } +} + +export default jenisLayananDelete diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/findMany.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/findMany.ts new file mode 100644 index 00000000..75b48d95 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/findMany.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import prisma from "@/lib/prisma"; + +export default async function jenisLayananFindMany() { + const data = await prisma.jenisLayanan.findMany(); + return { + success: true, + data: data.map((item: any) => { + return { + id: item.id, + nama: item.nama, + deskripsi: item.deskripsi, + } + }), + }; +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/findUnique.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/findUnique.ts new file mode 100644 index 00000000..658914ed --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/findUnique.ts @@ -0,0 +1,47 @@ +import { Context } from "elysia"; +import prisma from "@/lib/prisma"; + +export default async function jenisLayananFindUnique(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.jenisLayanan.findUnique({ + where: { id }, + }); + + if (!data) { + return { + success: false, + message: "Jenis layanan tidak ditemukan", + } + } + + return { + success: true, + message: "Success find jenis layanan", + data, + } + } catch (error) { + console.error("Find by ID error:", error); + return { + success: false, + message: "Gagal mengambil jenis layanan: " + (error instanceof Error ? error.message : 'Unknown error'), + } + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/index.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/index.ts new file mode 100644 index 00000000..7605c867 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/index.ts @@ -0,0 +1,32 @@ +import Elysia from "elysia"; +import jenisLayananFindMany from "./findMany"; +import jenisLayananFindUnique from "./findUnique"; +import jenisLayananDelete from "./del"; +import jenisLayananCreate from "./create"; +import jenisLayananUpdate from "./updt"; +import { t } from "elysia"; + +const JenisLayanan = new Elysia({ + prefix: "/jenislayanan", + tags: ["Inovasi/Jenis Layanan"], +}) + .get("/find-many", jenisLayananFindMany) + .get("/:id", async (context) => { + const response = await jenisLayananFindUnique(context); + return response; + }) + .delete("/del/:id", jenisLayananDelete) + .post("/create", jenisLayananCreate, { + body: t.Object({ + nama: t.String(), + deskripsi: t.String(), + }), + }) + .put("/:id", jenisLayananUpdate, { + body: t.Object({ + nama: t.String(), + deskripsi: t.String(), + }), + }); + +export default JenisLayanan; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/updt.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/updt.ts new file mode 100644 index 00000000..a10bb288 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/administrasi-online/jenis-layanan/updt.ts @@ -0,0 +1,45 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function jenisLayananUpdate(context: Context) { + const body = context.body as { nama: string, deskripsi: string }; + const id = context.params?.id as string; + + // Validasi ID dan nama + if (!id) { + return { + success: false, + message: "ID is required", + }; + } + + if (!body.nama) { + return { + success: false, + message: "Nama is required", + }; + } + + try { + const jenisLayanan = await prisma.jenisLayanan.update({ + where: { id }, + data: { + nama: body.nama, + deskripsi: body.deskripsi, + }, + }); + + return { + success: true, + message: "Success update jenis layanan", + data: jenisLayanan, + }; + } catch (error) { + console.error("Update error:", error); + return { + success: false, + message: "Gagal update jenis layanan", + error: error instanceof Error ? error.message : String(error), + }; + } +} diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/index.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/index.ts new file mode 100644 index 00000000..5765dca8 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/index.ts @@ -0,0 +1,13 @@ +import Elysia from "elysia"; +import AdministrasiOnline from "./administrasi-online"; + + +const LayananOnlineDesa = new Elysia({ + prefix: "/layananonlinedesa", + tags: ["Inovasi/Layanan Online Desa"], + + + +}).use(AdministrasiOnline); + +export default LayananOnlineDesa; diff --git a/src/app/darmasaba/(pages)/inovasi/ajukan-ide-inovatif/page.tsx b/src/app/darmasaba/(pages)/inovasi/ajukan-ide-inovatif/page.tsx index 8af21830..5aafcf44 100644 --- a/src/app/darmasaba/(pages)/inovasi/ajukan-ide-inovatif/page.tsx +++ b/src/app/darmasaba/(pages)/inovasi/ajukan-ide-inovatif/page.tsx @@ -1,10 +1,40 @@ +'use client' +import ajukanIdeInovatifState from '@/app/admin/(dashboard)/_state/inovasi/ajukan-ide-inovatif'; import colors from '@/con/colors'; -import { Stack, Box, Text, SimpleGrid, Paper, List, ListItem, Flex, ActionIcon } from '@mantine/core'; -import React from 'react'; -import BackButton from '../../desa/layanan/_com/BackButto'; +import { ActionIcon, Box, Button, Flex, List, ListItem, Modal, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core'; +import { useDisclosure } from '@mantine/hooks'; import { IconArrowRight, IconBulbFilled } from '@tabler/icons-react'; +import { useProxy } from 'valtio/utils'; +import BackButton from '../../desa/layanan/_com/BackButto'; +import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor'; function Page() { + const [opened, { open, close }] = useDisclosure(false); + const ideInovatif = useProxy(ajukanIdeInovatifState) + + + const resetForm = () => { + // Reset state di valtio + ideInovatif.create.form = { + name: "", + deskripsi: "", + alamat: "", + namaIde: "", + masalah: "", + benefit: "", + }; + + // Reset state lokal + }; + + const handleSubmit = async () => { + // Submit data berita + await ideInovatif.create.create(); + + // Reset form setelah submit + resetForm(); + close(); + }; return ( @@ -40,7 +70,7 @@ function Page() { - + @@ -49,6 +79,65 @@ function Page() { + + + + + Ajukan Ide Inovatif + Nama} + placeholder="masukkan nama" + onChange={(val) => { + ideInovatif.create.form.name = val.target.value + }} + /> + Alamat} + placeholder="masukkan alamat" + onChange={(val) => { + ideInovatif.create.form.alamat = val.target.value + }} + /> + Nama Ide} + placeholder="masukkan nama ide" + onChange={(val) => { + ideInovatif.create.form.namaIde = val.target.value + }} + /> + + Deskripsi + { + ideInovatif.create.form.deskripsi = htmlContent; + }} + /> + + Masalah} + placeholder="masukkan masalah" + onChange={(val) => { + ideInovatif.create.form.masalah = val.target.value + }} + /> + Benefit} + placeholder="masukkan benefit" + onChange={(val) => { + ideInovatif.create.form.benefit = val.target.value + }} + /> + + + + + ); } diff --git a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/administrasi-online/page.tsx b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/administrasi-online/page.tsx new file mode 100644 index 00000000..ca88dddc --- /dev/null +++ b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/administrasi-online/page.tsx @@ -0,0 +1,117 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' + +import layananonlineDesa from '@/app/admin/(dashboard)/_state/inovasi/layanan-online-desa'; +import colors from '@/con/colors'; +import { + Box, + Button, + Modal, + Paper, + Select, + Stack, + Text, + TextInput, + Title, +} from '@mantine/core'; +import { useDisclosure } from '@mantine/hooks'; +import { IconFileCheckFilled } from '@tabler/icons-react'; +import { motion } from 'framer-motion'; +import { useEffect } from 'react'; +import { useProxy } from 'valtio/utils'; + +function AdministrasiOnline() { + const [opened, { open, close }] = useDisclosure(false); + const state = useProxy(layananonlineDesa); + + useEffect(() => { + // ✅ Panggil load data jenis layanan dari backend + if (!state.jenisLayanan.findMany.data) { + state.jenisLayanan.findMany.load(); + } + }, []); + + const resetForm = () => { + state.administrasiOnline.create.form = { + name: '', + alamat: '', + nomorTelepon: '', + jenisLayananId: '', + }; + }; + + const handleSubmit = async () => { + await state.administrasiOnline.create.create(); + resetForm(); + close(); // Tutup modal setelah submit + }; + + return ( + + + + + + + + + Administrasi Online + + + Pengurusan surat dan dokumen secara digital tanpa perlu datang ke kantor desa + + + + + + + + + Ajukan Administrasi Online + Nama} + placeholder="masukkan nama" + onChange={(val) => (state.administrasiOnline.create.form.name = val.target.value)} + /> + Alamat} + placeholder="masukkan alamat" + onChange={(val) => (state.administrasiOnline.create.form.alamat = val.target.value)} + /> + Nomor Telepon} + placeholder="masukkan nomor telepon" + onChange={(val) => (state.administrasiOnline.create.form.nomorTelepon = val.target.value)} + /> +