diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d51f5430..8adecc3a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -74,28 +74,18 @@ model FileStorage { ProgramKesehatan ProgramKesehatan[] PenangananDarurat PenangananDarurat[] KontakDarurat KontakDarurat[] - - InfoWabahPenyakit InfoWabahPenyakit[] - - KeamananLingkungan KeamananLingkungan[] - - MenuTipsKeamanan MenuTipsKeamanan[] - - Pelapor Pelapor[] - - PasarDesa PasarDesa[] - - KontakDaruratKeamanan KontakDaruratKeamanan[] - - KontakItem KontakItem[] - - Pegawai Pegawai[] - - DesaDigital DesaDigital[] - - KolaborasiInovasi KolaborasiInovasi[] - - InfoTekno InfoTekno[] + InfoWabahPenyakit InfoWabahPenyakit[] + KeamananLingkungan KeamananLingkungan[] + MenuTipsKeamanan MenuTipsKeamanan[] + Pelapor Pelapor[] + PasarDesa PasarDesa[] + KontakDaruratKeamanan KontakDaruratKeamanan[] + KontakItem KontakItem[] + Pegawai Pegawai[] + DesaDigital DesaDigital[] + KolaborasiInovasi KolaborasiInovasi[] + InfoTekno InfoTekno[] + PengaduanMasyarakat PengaduanMasyarakat[] } //========================================= MENU PPID ========================================= // @@ -1405,25 +1395,54 @@ model AjukanIdeInovatif { // ========================================= LAYANAN ONLINE DESA ========================================= // model AdministrasiOnline { - id String @id @default(cuid()) - name String - alamat String - nomorTelepon String - jenisLayanan JenisLayanan @relation(fields: [jenisLayananId], references: [id]) - jenisLayananId String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - deletedAt DateTime @default(now()) - isActive Boolean @default(true) + id String @id @default(cuid()) + name String + alamat String + nomorTelepon String + jenisLayanan JenisLayanan @relation(fields: [jenisLayananId], references: [id]) + jenisLayananId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) } model JenisLayanan { - id String @id @default(uuid()) - nama String - deskripsi String @db.Text - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - deletedAt DateTime @default(now()) - isActive Boolean @default(true) - AdministrasiOnline AdministrasiOnline[] + id String @id @default(uuid()) + nama String + deskripsi String @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) + AdministrasiOnline AdministrasiOnline[] +} + +model PengaduanMasyarakat { + id String @id @default(cuid()) + name String + email String + nomorTelepon String + nik String + judulPengaduan String + lokasiKejadian String + image FileStorage @relation(fields: [imageId], references: [id]) + imageId String + deskripsiPengaduan String @db.Text + jenisPengaduan JenisPengaduan @relation(fields: [jenisPengaduanId], references: [id]) + jenisPengaduanId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} + +model JenisPengaduan { + id String @id @default(uuid()) + nama String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) + PengaduanMasyarakat PengaduanMasyarakat[] } diff --git a/src/app/admin/(dashboard)/_state/inovasi/layanan-online-desa.ts b/src/app/admin/(dashboard)/_state/inovasi/layanan-online-desa.ts index 9e08ec9d..0504c661 100644 --- a/src/app/admin/(dashboard)/_state/inovasi/layanan-online-desa.ts +++ b/src/app/admin/(dashboard)/_state/inovasi/layanan-online-desa.ts @@ -395,9 +395,409 @@ const jenisLayanan = proxy({ }, }); +// ========================================= PENGADUAN MASYARAKAT ========================================= // +const templatePengaduanMasyarakatForm = z.object({ + name: z.string().min(1, "Nama minimal 1 karakter"), + email: z.string().min(1, "Alamat minimal 1 karakter"), + nomorTelepon: z.string().min(1, "Nomor telepon minimal 1 karakter"), + nik: z.string().min(1, "NIK minimal 1 karakter"), + judulPengaduan: z.string().min(1, "Judul pengaduan minimal 1 karakter"), + lokasiKejadian: z.string().min(1, "Lokasi kejadian minimal 1 karakter"), + deskripsiPengaduan: z.string().min(1, "Deskripsi pengaduan minimal 1 karakter"), + jenisPengaduanId: z.string().min(1, "Jenis pengaduan minimal 1 karakter"), + imageId: z.string().min(1, "Image minimal 1 karakter"), +}); + +const defaultPengaduanMasyarakatForm = { + name: "", + email: "", + nomorTelepon: "", + nik: "", + judulPengaduan: "", + lokasiKejadian: "", + deskripsiPengaduan: "", + jenisPengaduanId: "", + imageId: "", +}; + +const pengaduanMasyarakat = proxy({ + create: { + form: { ...defaultPengaduanMasyarakatForm }, + loading: false, + async create() { + const cek = templatePengaduanMasyarakatForm.safeParse( + pengaduanMasyarakat.create.form + ); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + try { + pengaduanMasyarakat.create.loading = true; + const res = + await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat[ + "create" + ].post(pengaduanMasyarakat.create.form); + if (res.status === 200) { + pengaduanMasyarakat.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 { + pengaduanMasyarakat.create.loading = false; + } + }, + }, + findMany: { + data: null as Array< + Prisma.PengaduanMasyarakatGetPayload<{ + include: { + jenisPengaduan: true; + image: true; + }; + }> + > | null, + page: 1, + totalPages: 1, + loading: false, + + async load(page = 1, limit = 10) { + pengaduanMasyarakat.findMany.loading = true; + pengaduanMasyarakat.findMany.page = page; + try { + const res = + await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat[ + "find-many" + ].get({ + query: { + page, + limit, + }, + }); + + if (res.status === 200 && res.data?.success) { + pengaduanMasyarakat.findMany.data = res.data.data ?? []; + pengaduanMasyarakat.findMany.totalPages = res.data.totalPages ?? 1; + } + } catch (err) { + console.error("Gagal fetch pengaduan masyarakat paginated:", err); + } finally { + pengaduanMasyarakat.findMany.loading = false; + } + }, + }, + findUnique: { + data: null as Prisma.PengaduanMasyarakatGetPayload<{ + include: { + jenisPengaduan: true; + image: true; + }; + }> | null, + async load(id: string) { + try { + const res = await fetch( + `/api/inovasi/layananonlinedesa/pengaduanmasyarakat/${id}` + ); + if (res.ok) { + const data = await res.json(); + pengaduanMasyarakat.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch pengaduan masyarakat:", res.statusText); + pengaduanMasyarakat.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching pengaduan masyarakat:", error); + pengaduanMasyarakat.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + pengaduanMasyarakat.delete.loading = true; + + const response = await fetch( + `/api/inovasi/layananonlinedesa/pengaduanmasyarakat/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success( + result.message || "Pengaduan masyarakat berhasil dihapus" + ); + await pengaduanMasyarakat.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus pengaduan masyarakat"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus pengaduan masyarakat"); + } finally { + pengaduanMasyarakat.delete.loading = false; + } + }, + }, +}); +// ========================================= JENIS PENGADUAN ========================================= // +const templateJenisPengaduanForm = z.object({ + nama: z.string().min(1, "Nama minimal 1 karakter"), +}); + +const defaultJenisPengaduanForm = { + nama: "", +}; + +const jenisPengaduan = proxy({ + create: { + form: { ...defaultJenisPengaduanForm }, + loading: false, + async create() { + const cek = templateJenisPengaduanForm.safeParse(jenisPengaduan.create.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + try { + jenisPengaduan.create.loading = true; + const res = + await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat.jenispengaduan[ + "create" + ].post(jenisPengaduan.create.form); + if (res.status === 200) { + jenisPengaduan.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 { + jenisPengaduan.create.loading = false; + } + }, + }, + findMany: { + data: null as Array<{ + id: string; + nama: string; + }> | null, + async load() { + const res = + await ApiFetch.api.inovasi.layananonlinedesa.pengaduanmasyarakat.jenispengaduan[ + "find-many" + ].get(); + if (res.status === 200) { + jenisPengaduan.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.JenisPengaduanGetPayload<{ + omit: { isActive: true }; + }> | null, + async load(id: string) { + try { + const res = await fetch( + `/api/inovasi/layananonlinedesa/pengaduanmasyarakat/jenispengaduan/${id}` + ); + if (res.ok) { + const data = await res.json(); + jenisPengaduan.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + jenisPengaduan.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + jenisPengaduan.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + jenisPengaduan.delete.loading = true; + + const response = await fetch( + `/api/inovasi/layananonlinedesa/pengaduanmasyarakat/jenispengaduan/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "Jenis pengduan berhasil dihapus"); + await jenisPengaduan.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus jenis pengaduan"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus jenis pengaduan"); + } finally { + jenisPengaduan.delete.loading = false; + } + }, + }, + edit: { + id: "", + form: { ...defaultJenisPengaduanForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch( + `/api/inovasi/layananonlinedesa/pengaduanmasyarakat/jenispengaduan/${id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const result = await response.json(); + if (result?.success) { + const data = result.data; + this.id = data.id; + this.form = { + nama: data.nama + }; + return data; + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading jenis pengaduan:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + + async update() { + const cek = templateJenisPengaduanForm.safeParse(jenisPengaduan.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + jenisPengaduan.edit.loading = true; + const response = await fetch( + `/api/inovasi/layananonlinedesa/pengaduanmasyarakat/jenispengaduan/${jenisPengaduan.edit.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + nama: jenisPengaduan.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 jenis pengaduan (${response.status})` + ); + } + + if (result.success) { + toast.success( + result.message || "Berhasil memperbarui jenis pengaduan" + ); + await jenisPengaduan.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal mengupdate jenis pengaduan"); + } + } catch (error) { + // If JSON parsing fails, try to get the response text for better error messages + 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 pengaduan:", error); + toast.error( + error instanceof Error + ? error.message + : "Gagal mengupdate jenis pengaduan" + ); + return false; + } finally { + jenisPengaduan.edit.loading = false; + } + }, + reset() { + jenisPengaduan.edit.id = ""; + jenisPengaduan.edit.form = { ...defaultJenisPengaduanForm }; + }, + }, +}); + + const layananonlineDesa = proxy({ administrasiOnline, jenisLayanan, + pengaduanMasyarakat, + jenisPengaduan, }); export default layananonlineDesa; 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 index 760e16a7..11ee3621 100644 --- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/_lib/layoutTabs.tsx +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/_lib/layoutTabs.tsx @@ -24,6 +24,11 @@ function LayoutTabsLayananOnlineDesa({ children }: { children: React.ReactNode } value: "pengaduanmasyarakat", href: "/admin/inovasi/layanan-online-desa/pengaduan-masyarakat" }, + { + label: "Jenis Pengaduan", + value: "jenispengaduan", + href: "/admin/inovasi/layanan-online-desa/jenis-pengaduan" + }, { label: "Informasi Desa", value: "informasidesa", 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 new file mode 100644 index 00000000..706b33a0 --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/[id]/page.tsx @@ -0,0 +1,98 @@ +/* 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 { useParams, useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; + +function EditJenisPengaduan() { + const router = useRouter(); + const params = useParams(); + const id = params?.id as string; + const state = useProxy(layananonlineDesa.jenisPengaduan); + + const [formData, setFormData] = useState({ + nama: "", + }); + + useEffect(() => { + const loadJenisPengaduan = async () => { + if (!id) return; + + try { + const data = await state.edit.load(id); + + if (data) { + // pastikan id-nya masuk ke state edit + state.edit.id = id; + setFormData({ + nama: data.nama || '', + }); + } + } catch (error) { + console.error("Error loading jenis pengaduan:", error); + toast.error("Gagal memuat data jenis pengaduan"); + } + }; + + loadJenisPengaduan(); + }, [id]); + + const handleSubmit = async () => { + 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"); + } + } catch (error) { + console.error("Error updating jenis pengaduan:", error); + // toast akan ditampilkan dari fungsi update + } + }; + + return ( + + + router.back()} variant='subtle' color={'blue'}> + + + + + + + Edit Jenis Pengaduan + setFormData({ ...formData, nama: e.target.value })} + label={Nama Jenis Pengaduan} + placeholder='Masukkan nama jenis pengaduan' + /> + + Submit + + + + + ); +} + +export default EditJenisPengaduan; 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 new file mode 100644 index 00000000..5a52a3ef --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/create/page.tsx @@ -0,0 +1,61 @@ +/* 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 CreateJenisPengaduan() { + const router = useRouter(); + const state = useProxy(layananonlineDesa.jenisPengaduan) + + useEffect(() => { + state.findMany.load(); + }, []); + + const resetForm = () => { + state.create.form = { + nama: "", + }; + } + + const handleSubmit = async () => { + await state.create.create(); + resetForm(); + router.push("/admin/inovasi/layanan-online-desa/jenis-pengaduan") + } + + return ( + + + + router.back()} variant='subtle' color={'blue'}> + + + + + + + Create Jenis Pengaduan + { + state.create.form.nama = val.target.value; + }} + label={Nama Jenis Pengaduan} + placeholder='Masukkan nama jenis pengaduan' + /> + + Submit + + + + + + ); +} + +export default CreateJenisPengaduan; diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/page.tsx new file mode 100644 index 00000000..7db37be7 --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/jenis-pengaduan/page.tsx @@ -0,0 +1,125 @@ +'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 { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../../_com/header'; +import JudulList from '../../../_com/judulList'; +import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; +import layananonlineDesa from '../../../_state/inovasi/layanan-online-desa'; + + + +function JenisPengaduan() { + const [search, setSearch] = useState("") + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListJenisPengaduan({ search }: { search: string }) { + const state = useProxy(layananonlineDesa.jenisPengaduan) + const router = useRouter() + const [modalHapus, setModalHapus] = useState(false) + const [selectedId, setSelectedId] = useState(null) + + useShallowEffect(() => { + state.findMany.load() + }, []) + + const handleHapus = async () => { + if (selectedId) { + await state.delete.byId(selectedId); + setModalHapus(false) + setSelectedId(null) + } + } + + + const filteredData = (state.findMany.data || []).filter(item => { + const keyword = search.toLowerCase(); + return ( + item.nama.toLowerCase().includes(keyword) + ); + }); + + if (!state.findMany.data) { + return ( + + + + ) + } + + return ( + + + + + + + Nama Jenis Pengaduan + Edit + Hapus + + + + {filteredData.map((item) => ( + + {item.nama} + + { + if (item) { + router.push(`/admin/inovasi/layanan-online-desa/jenis-pengaduan/${item.id}`); + } + }} + > + + + + + { + if (item) { + setSelectedId(item.id); + setModalHapus(true); + } + }} + disabled={!item} + > + + + + + ))} + + + + {/* Modal Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text="Apakah anda yakin ingin menghapus jenis pengaduan ini?" + /> + + ); +} + +export default JenisPengaduan; 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 new file mode 100644 index 00000000..7cbd9d0c --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/pengaduan-masyarakat/[id]/page.tsx @@ -0,0 +1,124 @@ +'use client' +import { useProxy } from 'valtio/utils'; + +import { Box, Button, Flex, Image, 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 DetailPengaduanMasyarakat() { + const pengaduanState = useProxy(layananonlineDesa) + const [modalHapus, setModalHapus] = useState(false) + const [selectedId, setSelectedId] = useState(null) + const params = useParams() + const router = useRouter() + + useShallowEffect(() => { + pengaduanState.pengaduanMasyarakat.findUnique.load(params?.id as string) + }, []) + + + const handleHapus = () => { + if (selectedId) { + pengaduanState.pengaduanMasyarakat.delete.byId(selectedId) + setModalHapus(false) + setSelectedId(null) + router.push("/admin/inovasi/layanan-online-desa/pengaduan-masyarakat") + } + } + + if (!pengaduanState.pengaduanMasyarakat.findUnique.data) { + return ( + + + + ) + } + + return ( + + + router.back()}> + + + + + + + Detail Pengaduan Masyarakat + { + if (pengaduanState.pengaduanMasyarakat.findUnique.data) { + setSelectedId(pengaduanState.pengaduanMasyarakat.findUnique.data.id); + setModalHapus(true); + } + }} + disabled={pengaduanState.pengaduanMasyarakat.delete.loading || !pengaduanState.pengaduanMasyarakat.findUnique.data} + color={"red"} + > + + + + {pengaduanState.pengaduanMasyarakat.findUnique.data ? ( + + + + Nama + {pengaduanState.pengaduanMasyarakat.findUnique.data?.name} + + + Email + {pengaduanState.pengaduanMasyarakat.findUnique.data?.email} + + + Nomor Telepon + {pengaduanState.pengaduanMasyarakat.findUnique.data?.nomorTelepon} + + + NIK + {pengaduanState.pengaduanMasyarakat.findUnique.data?.nik} + + + Judul Pengaduan + {pengaduanState.pengaduanMasyarakat.findUnique.data?.judulPengaduan} + + + Lokasi Kejadian + {pengaduanState.pengaduanMasyarakat.findUnique.data?.lokasiKejadian} + + + Deskripsi Pengaduan + {pengaduanState.pengaduanMasyarakat.findUnique.data?.deskripsiPengaduan} + + + Jenis Pengaduan + {pengaduanState.pengaduanMasyarakat.findUnique.data?.jenisPengaduan?.nama} + + + Gambar + + + + + ) : null} + + + + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text='Apakah anda yakin ingin menghapus administrasi online ini?' + /> + + ); +} + +export default DetailPengaduanMasyarakat; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx new file mode 100644 index 00000000..571217ad --- /dev/null +++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/pengaduan-masyarakat/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 PengaduanMasyarakat() { + const [search, setSearch] = useState(""); + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListPengaduanMasyarakat({ search }: { search: string }) { + const listState = useProxy(layananonlineDesa.pengaduanMasyarakat) + 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.email.toLowerCase().includes(keyword) || + item.nomorTelepon.toLowerCase().includes(keyword) + ); + }); + + if (loading || !data) { + return ; + } + + return ( + + + List Pengaduan Masyarakat + + + + Nama + Email + Nomor Telepon + Detail + + + + {filteredData.map((item) => ( + + {item.name} + {item.email} + {item.nomorTelepon} + + router.push(`/admin//inovasi/layanan-online-desa/administrasi-online/${item.id}`)}> + + + + + ))} + + + + + load(newPage)} // ini penting! + total={totalPages} + mt="md" + mb="md" + /> + + + ); +} + +export default PengaduanMasyarakat; 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 index 5765dca8..b24d1c38 100644 --- a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/index.ts +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/index.ts @@ -1,13 +1,13 @@ import Elysia from "elysia"; import AdministrasiOnline from "./administrasi-online"; - +import PengaduanMasyarakat from "./pengaduan-masyarakat"; const LayananOnlineDesa = new Elysia({ prefix: "/layananonlinedesa", tags: ["Inovasi/Layanan Online Desa"], +}) +.use(AdministrasiOnline) +.use(PengaduanMasyarakat) - -}).use(AdministrasiOnline); - export default LayananOnlineDesa; diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/create.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/create.ts new file mode 100644 index 00000000..f861d5c6 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/create.ts @@ -0,0 +1,39 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + name: string; + email: string; + nomorTelepon: string; + nik: string; + judulPengaduan: string; + lokasiKejadian: string; + deskripsiPengaduan: string; + jenisPengaduanId: string; + imageId: string; +} +export default async function pengaduanMasyarakatCreate(context: Context){ + const body = context.body as FormCreate; + + await prisma.pengaduanMasyarakat.create({ + data: { + name: body.name, + email: body.email, + nomorTelepon: body.nomorTelepon, + nik: body.nik, + judulPengaduan: body.judulPengaduan, + lokasiKejadian: body.lokasiKejadian, + deskripsiPengaduan: body.deskripsiPengaduan, + jenisPengaduanId: body.jenisPengaduanId, + imageId: body.imageId, + } + }) + + return { + success: true, + message: "Success create pengaduan masyarakat", + data: { + ...body, + } + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/del.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/del.ts new file mode 100644 index 00000000..e47fd8d4 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/del.ts @@ -0,0 +1,47 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; +import fs from "fs/promises"; +import path from "path"; + +export default async function pengaduanMasyarakatDelete(context: Context) { + const id = context.params?.id as string; + + const pengaduan = await prisma.pengaduanMasyarakat.findUnique({ + where: { + id, + }, + include: { + image: true, + jenisPengaduan: true, + }, + }); + + if (!pengaduan) { + return { + status: 404, + body: "Pengaduan masyarakat tidak ditemukan", + }; + } + + if (pengaduan.image) { + try { + const filePath = path.join(pengaduan.image.path, pengaduan.image.name); + await fs.unlink(filePath); + await prisma.fileStorage.delete({ + where: { id: pengaduan.image.id }, + }); + } catch (error) { + console.error("Gagal hapus file image:", error); + } + } + + await prisma.pengaduanMasyarakat.delete({ + where: { id }, + }); + + return { + success: true, + message: "Pengaduan masyarakat berhasil dihapus", + status: 200, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/findMany.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/findMany.ts new file mode 100644 index 00000000..be9721cb --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/findMany.ts @@ -0,0 +1,44 @@ +// /api/berita/findManyPaginated.ts +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function pengaduanMasyarakatFindMany(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.pengaduanMasyarakat.findMany({ + where: { isActive: true }, + include: { + jenisPengaduan: true, + image: true, + }, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, // opsional, kalau mau urut berdasarkan waktu + }), + prisma.pengaduanMasyarakat.count({ + where: { isActive: true } + }) + ]); + + return { + success: true, + message: "Success fetch pengaduan masyarakat 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 pengaduan masyarakat with pagination", + }; + } +} + +export default pengaduanMasyarakatFindMany; 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 new file mode 100644 index 00000000..bb0009a7 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/findUnique.ts @@ -0,0 +1,50 @@ +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]; + + 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.pengaduanMasyarakat.findUnique({ + where: { id }, + include: { + image: true, + jenisPengaduan: true, + }, + }); + + if (!data) { + return Response.json({ + success: false, + message: "Pengaduan masyarakat tidak ditemukan", + }, {status: 404}); + } + + return Response.json({ + success: true, + message: "Success fetch pengaduan masyarakat by ID", + data, + }, {status: 200}); + } catch (error) { + console.error("Find by ID error:", error); + return Response.json({ + success: false, + message: "Gagal mengambil pengaduan masyarakat: " + (error instanceof Error ? error.message : 'Unknown error'), + }, {status: 500}); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/index.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/index.ts new file mode 100644 index 00000000..c3b940ab --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/index.ts @@ -0,0 +1,30 @@ +import Elysia from "elysia"; +import pengaduanMasyarakatCreate from "./create"; +import { t } from "elysia"; +import pengaduanMasyarakatDelete from "./del"; +import pengaduanMasyarakatFindMany from "./findMany"; +import pengaduanMasyarakatFindUnique from "./findUnique"; +import JenisPengaduan from "./jenis-pengaduan"; + +const PengaduanMasyarakat = new Elysia({ + prefix: "/pengaduanmasyarakat", + tags: ["Inovasi/Layanan Online Desa/Pengaduan Masyarakat"], +}) +.post("/create", pengaduanMasyarakatCreate, { + body: t.Object({ + name: t.String(), + email: t.String(), + nomorTelepon: t.String(), + nik: t.String(), + judulPengaduan: t.String(), + lokasiKejadian: t.String(), + deskripsiPengaduan: t.String(), + jenisPengaduanId: t.String(), + imageId: t.String(), + }), +}) +.get("/find-many", pengaduanMasyarakatFindMany) +.get("/:id", pengaduanMasyarakatFindUnique) +.delete("/del/:id", pengaduanMasyarakatDelete) +.use(JenisPengaduan) +export default PengaduanMasyarakat; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/create.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/create.ts new file mode 100644 index 00000000..27011f30 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/create.ts @@ -0,0 +1,24 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + nama: string; +} + +export default async function jenisPengaduanCreate(context: Context){ + const body = context.body as FormCreate; + + await prisma.jenisPengaduan.create({ + data: { + nama: body.nama, + } + }) + + return { + success: true, + message: "Success create jenis pengaduan", + data: { + ...body, + } + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/del.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/del.ts new file mode 100644 index 00000000..8721bac8 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/del.ts @@ -0,0 +1,33 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +const jenisPengaduanDelete = async (context: Context) => { + const id = context.params.id; + if (!id) { + return { + success: false, + message: "ID is required", + } + } + + const jenisPengaduan = await prisma.jenisPengaduan.delete({ + where: { + id: id, + }, + }) + + if(!jenisPengaduan) { + return { + success: false, + message: "Jenis pengaduan tidak ditemukan", + } + } + + return { + success: true, + message: "Success delete jenis pengaduan", + data: jenisPengaduan, + } +} + +export default jenisPengaduanDelete diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/findMany.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/findMany.ts new file mode 100644 index 00000000..ab248208 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/findMany.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import prisma from "@/lib/prisma"; + +export default async function jenisPengaduanFindMany() { + const data = await prisma.jenisPengaduan.findMany(); + return { + success: true, + data: data.map((item: any) => { + return { + id: item.id, + nama: item.nama, + } + }), + }; +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/findUnique.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/findUnique.ts new file mode 100644 index 00000000..cc8a73f5 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/findUnique.ts @@ -0,0 +1,47 @@ +import { Context } from "elysia"; +import prisma from "@/lib/prisma"; + +export default async function jenisPengaduanFindUnique(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.jenisPengaduan.findUnique({ + where: { id }, + }); + + if (!data) { + return { + success: false, + message: "Jenis pengaduan tidak ditemukan", + } + } + + return { + success: true, + message: "Success find jenis pengaduan", + data, + } + } catch (error) { + console.error("Find by ID error:", error); + return { + success: false, + message: "Gagal mengambil jenis pengaduan: " + (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/pengaduan-masyarakat/jenis-pengaduan/index.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/index.ts new file mode 100644 index 00000000..c18f1cef --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/index.ts @@ -0,0 +1,29 @@ +import Elysia from "elysia"; +import jenisPengaduanFindMany from "./findMany"; +import jenisPengaduanFindUnique from "./findUnique"; +import jenisPengaduanCreate from "./create"; +import jenisPengaduanUpdate from "./updt"; +import { t } from "elysia"; +import jenisPengaduanDelete from "./del"; + +const JenisPengaduan = new Elysia({ + prefix: "/jenispengaduan", + tags: ["Inovasi/Layanan Online Desa/Jenis Pengaduan"], +}) + .get("/find-many", jenisPengaduanFindMany) + .get("/:id", async (context) => { + const response = await jenisPengaduanFindUnique(context); + return response; + }) + .delete("/del/:id", jenisPengaduanDelete) + .post("/create", jenisPengaduanCreate, { + body: t.Object({ + nama: t.String(), + }), + }) + .put("/:id", jenisPengaduanUpdate, { + body: t.Object({ + nama: t.String(), + }), + }); +export default JenisPengaduan; diff --git a/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/updt.ts b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/updt.ts new file mode 100644 index 00000000..2964691e --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/inovasi/layanan-online-desa/pengaduan-masyarakat/jenis-pengaduan/updt.ts @@ -0,0 +1,44 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function jenisPengaduanUpdate(context: Context) { + const body = context.body as { nama: 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 jenisPengaduan = await prisma.jenisPengaduan.update({ + where: { id }, + data: { + nama: body.nama, + }, + }); + + return { + success: true, + message: "Success update jenis pengaduan", + data: jenisPengaduan, + }; + } catch (error) { + console.error("Update error:", error); + return { + success: false, + message: "Gagal update jenis pengaduan", + error: error instanceof Error ? error.message : String(error), + }; + } +} diff --git a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/page.tsx b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/page.tsx index 065bb66f..bd8ddc34 100644 --- a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/page.tsx +++ b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/page.tsx @@ -1,3 +1,4 @@ + 'use client' import colors from '@/con/colors'; import { Box, SimpleGrid, Stack, Text } from '@mantine/core'; diff --git a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx index de66a845..ce78eb08 100644 --- a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx +++ b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/pengaduan-masyarakat/page.tsx @@ -1,26 +1,211 @@ +/* eslint-disable react-hooks/exhaustive-deps */ 'use client' +import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor'; +import layananonlineDesa from '@/app/admin/(dashboard)/_state/inovasi/layanan-online-desa'; import colors from '@/con/colors'; -import { Box, Paper, Stack, Text } from '@mantine/core'; -import { IconMessageCircleQuestion } from '@tabler/icons-react'; +import ApiFetch from '@/lib/api-fetch'; +import { Box, Button, Center, Group, Image, Modal, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core'; +import { Dropzone } from '@mantine/dropzone'; +import { useDisclosure } from '@mantine/hooks'; +import { IconImageInPicture, IconMessageCircleQuestion, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { motion } from 'framer-motion'; +import { useState, useEffect } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; function PengaduanMasyarakat() { + const [opened, { open, close }] = useDisclosure(false); + const state = useProxy(layananonlineDesa); + const [previewImage, setPreviewImage] = useState(null); + const [file, setFile] = useState(null); + + useEffect(() => { + // ✅ Panggil load data jenis layanan dari backend + if (!state.jenisPengaduan.findMany.data) { + state.jenisPengaduan.findMany.load(); + } + }, []); + + const resetForm = () => { + state.pengaduanMasyarakat.create.form = { + name: '', + email: '', + nomorTelepon: '', + nik: '', + judulPengaduan: '', + lokasiKejadian: '', + deskripsiPengaduan: '', + jenisPengaduanId: '', + imageId: '', + }; + }; + + const handleSubmit = async () => { + if (!file) { + return toast.error("Silahkan pilih file gambar terlebih dahulu") + } + + try { + // Upload the image first + const uploadRes = await ApiFetch.api.fileStorage.create.post({ + file: file, + name: file.name + }) + + const uploaded = uploadRes.data?.data + if (!uploaded?.id) { + return toast.error("Gagal upload gambar") + } + + // Set the image ID in the form + state.pengaduanMasyarakat.create.form.imageId = uploaded.id + + // Submit the form + const success = await state.pengaduanMasyarakat.create.create() + + if (success) { + resetForm() + close() + } + } catch (error) { + console.error("Error in handleSubmit:", error) + toast.error("Terjadi kesalahan saat menyimpan data") + } + }; return ( - - - , - - Pengaduan Masyarakat - Sampaikan keluhan dan aspirasi Anda melalui platform digital kami - + onClick={open} + > + + + , + + Pengaduan Masyarakat + Sampaikan keluhan dan aspirasi Anda melalui platform digital kami + + + + + Ajukan Administrasi Online + Nama} + placeholder="masukkan nama" + onChange={(val) => (state.pengaduanMasyarakat.create.form.name = val.target.value)} + /> + Email} + placeholder="masukkan email" + onChange={(val) => (state.pengaduanMasyarakat.create.form.email = val.target.value)} + /> + Nomor Telepon} + placeholder="masukkan nomor telepon" + onChange={(val) => (state.pengaduanMasyarakat.create.form.nomorTelepon = val.target.value)} + /> + NIK} + placeholder="masukkan nik" + onChange={(val) => (state.pengaduanMasyarakat.create.form.nik = val.target.value)} + /> + Judul Pengaduan} + placeholder="masukkan judul pengaduan" + onChange={(val) => (state.pengaduanMasyarakat.create.form.judulPengaduan = val.target.value)} + /> + Lokasi Kejadian} + placeholder="masukkan lokasi kejadian" + onChange={(val) => (state.pengaduanMasyarakat.create.form.lokasiKejadian = val.target.value)} + /> + + Deskripsi + { + state.pengaduanMasyarakat.create.form.deskripsiPengaduan = htmlContent; + }} + /> + + { + state.pengaduanMasyarakat.create.form.jenisPengaduanId = val ?? ""; + }} + label={Jenis Pengaduan} + placeholder="Pilih kategori produk" + data={ + state.jenisPengaduan.findMany.data?.map((v) => ({ + value: v.id, + label: v.nama, + })) || [] + } + /> + + 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 images here or click to select files + + + Attach as many files as you like, each file should not exceed 5mb + + + + + {previewImage ? ( + + ) : ( + + + + )} + + + + + + Simpan + + + + ); }