diff --git a/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/[id]/page.tsx b/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/[id]/page.tsx index f8891222..c2eeb624 100644 --- a/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/[id]/page.tsx +++ b/src/app/admin/(dashboard)/desa/pengumuman/kategori-pengumuman/[id]/page.tsx @@ -1,5 +1,6 @@ /* eslint-disable react-hooks/exhaustive-deps */ 'use client'; + import stateDesaPengumuman from '@/app/admin/(dashboard)/_state/desa/pengumuman'; import colors from '@/con/colors'; import { @@ -10,7 +11,7 @@ import { Stack, TextInput, Title, - Tooltip + Tooltip, } from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; @@ -23,10 +24,9 @@ function EditKategoriPengumuman() { const router = useRouter(); const params = useParams(); - const [formData, setFormData] = useState({ - name: editState.update.form.name || '', - }); + const [formData, setFormData] = useState({ name: '' }); + // Load data awal sekali aja useEffect(() => { const loadKategori = async () => { const id = params?.id as string; @@ -35,9 +35,7 @@ function EditKategoriPengumuman() { try { const data = await editState.update.load(id); if (data) { - setFormData({ - name: data.name || '', - }); + setFormData({ name: data.name || '' }); } } catch (error) { console.error('Error loading kategori Pengumuman:', error); @@ -48,8 +46,16 @@ function EditKategoriPengumuman() { loadKategori(); }, [params?.id]); + const handleChange = (field: string, value: string) => { + setFormData((prev) => ({ + ...prev, + [field]: value, + })); + }; + const handleSubmit = async () => { try { + // Update global state hanya di sini editState.update.form = { ...editState.update.form, name: formData.name, @@ -97,9 +103,7 @@ function EditKategoriPengumuman() { label="Nama Kategori Pengumuman" placeholder="Masukkan nama kategori Pengumuman" value={formData.name} - onChange={(e) => - setFormData({ ...formData, name: e.target.value }) - } + onChange={(e) => handleChange('name', e.target.value)} required /> diff --git a/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/edit/page.tsx index 2ecca8ff..9e7a622f 100644 --- a/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/desa/pengumuman/list-pengumuman/[id]/edit/page.tsx @@ -28,16 +28,16 @@ function EditPengumuman() { const params = useParams(); const [formData, setFormData] = useState({ - judul: editState.pengumuman.edit.form.judul || "", - deskripsi: editState.pengumuman.edit.form.deskripsi || "", - categoryPengumumanId: - editState.pengumuman.edit.form.categoryPengumumanId || "", - content: editState.pengumuman.edit.form.content || "", + judul: "", + deskripsi: "", + categoryPengumumanId: "", + content: "", }); - // Load pengumuman by id saat pertama kali + // Load kategori & pengumuman by id saat pertama kali useEffect(() => { editState.category.findMany.load(); + const loadpengumuman = async () => { const id = params?.id as string; if (!id) return; @@ -61,9 +61,13 @@ function EditPengumuman() { loadpengumuman(); }, [params?.id]); + const handleChange = (field: keyof typeof formData, value: string) => { + setFormData((prev) => ({ ...prev, [field]: value })); + }; + const handleSubmit = async () => { try { - // update global state + // update global state hanya sekali pas submit editState.pengumuman.edit.form = { ...editState.pengumuman.edit.form, ...formData, @@ -108,26 +112,22 @@ function EditPengumuman() { setFormData({ ...formData, judul: e.target.value })} + value={formData.judul} + onChange={(e) => handleChange("judul", e.target.value)} required /> - setFormData({ ...formData, deskripsi: e.target.value }) - } + value={formData.deskripsi} + onChange={(e) => handleChange("deskripsi", e.target.value)} required /> setFormData({ ...formData, kategoriId: val || "" })} + onChange={(val) => handleChange("kategoriId", val || "")} label="Kategori" placeholder="Pilih kategori" data={ @@ -164,7 +180,9 @@ function EditPotensi() { setPreviewImage(URL.createObjectURL(selectedFile)); } }} - onReject={() => toast.error("File tidak valid, gunakan format gambar")} + onReject={() => + toast.error("File tidak valid, gunakan format gambar") + } maxSize={5 * 1024 ** 2} accept={{ "image/*": [] }} radius="md" @@ -172,7 +190,11 @@ function EditPotensi() { > - + @@ -214,7 +236,9 @@ function EditPotensi() { setFormData({ ...formData, content: htmlContent })} + onChange={(htmlContent) => + handleChange("content", htmlContent) + } /> diff --git a/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/[id]/edit/page.tsx index 5cabb73a..56f2c2e2 100644 --- a/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/desa/profile/profile-perbekel-dari-masa-ke-masa/[id]/edit/page.tsx @@ -26,15 +26,17 @@ function EditPerbekelDariMasaKeMasa() { const state = useProxy(stateProfileDesa.mantanPerbekel); const router = useRouter(); const params = useParams(); + const [previewImage, setPreviewImage] = useState(null); const [file, setFile] = useState(null); const [formData, setFormData] = useState({ - nama: state.update.form.nama || '', - daerah: state.update.form.daerah || '', - periode: state.update.form.periode || '', - imageId: state.update.form.imageId || '' + nama: '', + daerah: '', + periode: '', + imageId: '' }); + // load data pertama kali useEffect(() => { const loadFoto = async () => { const id = params?.id as string; @@ -48,7 +50,9 @@ function EditPerbekelDariMasaKeMasa() { periode: data.periode || '', imageId: data.imageId || '' }); - if (data?.imageGalleryFoto?.link) setPreviewImage(data.imageGalleryFoto.link); + if (data?.imageGalleryFoto?.link) { + setPreviewImage(data.imageGalleryFoto.link); + } } } catch (error) { console.error('Error loading foto:', error); @@ -58,8 +62,17 @@ function EditPerbekelDariMasaKeMasa() { loadFoto(); }, [params?.id]); + // helper ubah state formData + const handleChange = (field: keyof typeof formData, value: string) => { + setFormData((prev) => ({ + ...prev, + [field]: value, + })); + }; + const handleSubmit = async () => { try { + // update global state hanya sekali pas submit state.update.form = { ...state.update.form, ...formData }; if (file) { @@ -106,8 +119,8 @@ function EditPerbekelDariMasaKeMasa() { setFormData({ ...formData, nama: e.target.value })} + value={formData.nama} + onChange={(e) => handleChange('nama', e.target.value)} required /> @@ -161,7 +174,7 @@ function EditPerbekelDariMasaKeMasa() { objectFit: 'contain', border: `1px solid ${colors['blue-button']}`, }} - loading='lazy' + loading="lazy" /> )} @@ -170,16 +183,16 @@ function EditPerbekelDariMasaKeMasa() { setFormData({ ...formData, daerah: e.target.value })} + value={formData.daerah} + onChange={(e) => handleChange('daerah', e.target.value)} required /> setFormData({ ...formData, periode: e.target.value })} + value={formData.periode} + onChange={(e) => handleChange('periode', e.target.value)} required /> diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/[id]/edit/page.tsx index 3c6b290b..956f84e1 100644 --- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/[id]/edit/page.tsx @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -'use client' /* eslint-disable react-hooks/exhaustive-deps */ +'use client' import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa'; import colors from '@/con/colors'; import { @@ -29,13 +29,13 @@ function EditAPBDesa() { const params = useParams(); const [formData, setFormData] = useState({ - tahun: apbState.update.form.tahun || '', - pendapatanIds: apbState.update.form.pendapatanIds || [], - belanjaIds: apbState.update.form.belanjaIds || [], - pembiayaanIds: apbState.update.form.pembiayaanIds || [], + tahun: '', + pendapatanIds: [] as string[], + belanjaIds: [] as string[], + pembiayaanIds: [] as string[], }); - // Load APB desa by id + // Load APB desa by id → hanya update formData, bukan global state useEffect(() => { const loadAPBdesa = async () => { const id = params?.id as string; @@ -45,7 +45,7 @@ function EditAPBDesa() { const data = await apbState.update.load(id); if (data) { setFormData({ - tahun: data.tahun || 0, + tahun: String(data.tahun || ''), pendapatanIds: data.pendapatan?.map((p: any) => p.id) || [], belanjaIds: data.belanja?.map((b: any) => b.id) || [], pembiayaanIds: data.pembiayaan?.map((p: any) => p.id) || [], @@ -60,8 +60,13 @@ function EditAPBDesa() { loadAPBdesa(); }, [params?.id]); + const handleChange = (field: keyof typeof formData, value: any) => { + setFormData(prev => ({ ...prev, [field]: value })); + }; + const handleSubmit = async () => { try { + // update global state cuma pas submit apbState.update.form = { ...apbState.update.form, tahun: Number(formData.tahun), @@ -111,10 +116,8 @@ function EditAPBDesa() { {/* Tahun */} - setFormData({ ...formData, tahun: e.target.value }) - } + value={formData.tahun} + onChange={(e) => handleChange("tahun", e.target.value)} label={Tahun} placeholder="Masukkan tahun anggaran" required @@ -123,23 +126,17 @@ function EditAPBDesa() { {/* Selects */} - setFormData({ ...formData, pendapatanIds: ids }) - } + onSelectionChange={(ids) => handleChange("pendapatanIds", ids)} /> - setFormData({ ...formData, belanjaIds: ids }) - } + onSelectionChange={(ids) => handleChange("belanjaIds", ids)} /> - setFormData({ ...formData, pembiayaanIds: ids }) - } + onSelectionChange={(ids) => handleChange("pembiayaanIds", ids)} /> {/* Save Button */} @@ -164,7 +161,13 @@ function EditAPBDesa() { /* --- Sub Components --- */ - function SelectPendapatan({ selectedIds, onSelectionChange }: { selectedIds: string[]; onSelectionChange: (ids: string[]) => void }) { + function SelectPendapatan({ + selectedIds, + onSelectionChange, + }: { + selectedIds: string[]; + onSelectionChange: (ids: string[]) => void; + }) { const pendapatanState = useProxy(PendapatanAsliDesa.pendapatan); useShallowEffect(() => { @@ -192,7 +195,13 @@ function EditAPBDesa() { ); } - function SelectBelanja({ selectedIds, onSelectionChange }: { selectedIds: string[]; onSelectionChange: (ids: string[]) => void }) { + function SelectBelanja({ + selectedIds, + onSelectionChange, + }: { + selectedIds: string[]; + onSelectionChange: (ids: string[]) => void; + }) { const belanjaState = useProxy(PendapatanAsliDesa.belanja); useShallowEffect(() => { @@ -220,7 +229,13 @@ function EditAPBDesa() { ); } - function SelectPembiayaan({ selectedIds, onSelectionChange }: { selectedIds: string[]; onSelectionChange: (ids: string[]) => void }) { + function SelectPembiayaan({ + selectedIds, + onSelectionChange, + }: { + selectedIds: string[]; + onSelectionChange: (ids: string[]) => void; + }) { const pembiayaanState = useProxy(PendapatanAsliDesa.pembiayaan); useShallowEffect(() => { diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/[id]/page.tsx index 75ed35df..7c03d4a8 100644 --- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/[id]/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/belanja/[id]/page.tsx @@ -1,5 +1,6 @@ /* eslint-disable react-hooks/exhaustive-deps */ 'use client' + import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa'; import colors from '@/con/colors'; import { @@ -24,12 +25,16 @@ function EditBelanja() { const params = useParams(); const [formData, setFormData] = useState({ - name: belanjaState.update.form.name || '', - value: belanjaState.update.form.value || '', + name: '', + value: '', }); + // format angka ke rupiah const formatRupiah = (value: number | string) => { - const number = typeof value === 'number' ? value : Number(value.replace(/\D/g, '')); + const number = + typeof value === 'number' + ? value + : Number(value.replace(/\D/g, '')) || 0; return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', @@ -37,8 +42,9 @@ function EditBelanja() { }).format(number); }; + // buang semua simbol jadi angka murni const unformatRupiah = (value: string) => { - return Number(value.replace(/\D/g, '')); + return Number(value.replace(/\D/g, '')) || 0; }; useEffect(() => { @@ -51,7 +57,7 @@ function EditBelanja() { if (data) { setFormData({ name: data.name || '', - value: data.value || '', + value: String(data.value || ''), }); } } catch (error) { @@ -69,7 +75,7 @@ function EditBelanja() { ...belanjaState.update.form, name: formData.name, value: Number(formData.value), - } + }; await belanjaState.update.update(); toast.success("Jenis Belanja berhasil diperbarui!"); @@ -78,7 +84,7 @@ function EditBelanja() { console.error("Error updating jenis belanja:", error); toast.error("Terjadi kesalahan saat memperbarui jenis belanja"); } - } + }; return ( @@ -112,19 +118,21 @@ function EditBelanja() { setFormData({ ...formData, name: e.target.value })} + value={formData.name} + onChange={(e) => + setFormData({ ...formData, name: e.target.value }) + } required /> { const raw = e.currentTarget.value; const cleanValue = unformatRupiah(raw); - setFormData({ ...formData, value: cleanValue }); + setFormData({ ...formData, value: String(cleanValue) }); }} required /> diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/[id]/page.tsx index 988b9eda..25df9c5b 100644 --- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/[id]/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan/[id]/page.tsx @@ -24,12 +24,15 @@ function EditPembiayaan() { const params = useParams(); const [formData, setFormData] = useState({ - name: pembiayaanState.update.form.name || '', - value: pembiayaanState.update.form.value || '', + name: '', + value: '', }); const formatRupiah = (value: number | string) => { - const number = typeof value === 'number' ? value : Number(value.toString().replace(/\D/g, '')); + const number = + typeof value === 'number' + ? value + : Number(value.toString().replace(/\D/g, '')) || 0; return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', @@ -38,7 +41,7 @@ function EditPembiayaan() { }; const unformatRupiah = (value: string) => { - return Number(value.replace(/\D/g, '')); + return Number(value.replace(/\D/g, '')) || 0; }; useEffect(() => { @@ -51,7 +54,7 @@ function EditPembiayaan() { if (data) { setFormData({ name: data.name || '', - value: data.value || '', + value: String(data.value || ''), }); } } catch (error) { @@ -68,7 +71,7 @@ function EditPembiayaan() { pembiayaanState.update.form = { ...pembiayaanState.update.form, name: formData.name, - value: Number(formData.value), + value: unformatRupiah(formData.value), }; await pembiayaanState.update.update(); @@ -82,7 +85,7 @@ function EditPembiayaan() { return ( - {/* Header dengan Back Button */} + {/* Header */} @@ -78,10 +111,8 @@ function EditJumlahPendudukMiskin() { placeholder="Masukkan tahun" type="number" required - defaultValue={stateJPM.update.form.year} - onChange={(val) => { - stateJPM.update.form.year = Number(val.currentTarget.value); - }} + value={formData.year} + onChange={(e) => handleChange('year', e.currentTarget.value)} /> { - stateJPM.update.form.totalPoorPopulation = Number(val.currentTarget.value); - }} + value={formData.totalPoorPopulation} + onChange={(e) => + handleChange('totalPoorPopulation', e.currentTarget.value) + } /> diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/[id]/page.tsx index fb6cef40..7e069310 100644 --- a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/[id]/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/[id]/page.tsx @@ -1,11 +1,11 @@ +/* eslint-disable react-hooks/exhaustive-deps */ 'use client'; import grafikNganggur from '@/app/admin/(dashboard)/_state/ekonomi/usia-kerja-nganggur'; -/* eslint-disable react-hooks/exhaustive-deps */ import colors from '@/con/colors'; import { Box, Button, Group, Paper, Stack, TextInput, Title, Tooltip } from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useProxy } from 'valtio/utils'; function EditGrafikBerdasarkanPendidikan() { @@ -14,34 +14,59 @@ function EditGrafikBerdasarkanPendidikan() { const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanPendidikan); const id = params.id; + // state lokal untuk form + const [formData, setFormData] = useState({ + SD: '', + SMP: '', + SMA: '', + D3: '', + S1: '', + }); + useEffect(() => { if (id) { stategrafik.findUnique.load(id).then(() => { const data = stategrafik.findUnique.data; if (data) { - stategrafik.update.form = { + setFormData({ SD: data.SD || '', SMP: data.SMP || '', SMA: data.SMA || '', D3: data.D3 || '', S1: data.S1 || '', - }; + }); } }); } }, [id]); + const handleChange = (field: keyof typeof formData) => + (e: React.ChangeEvent) => { + setFormData((prev) => ({ + ...prev, + [field]: e.currentTarget.value, + })); + }; + const handleSubmit = async () => { stategrafik.update.id = id; + stategrafik.update.form = { ...formData }; // update global state pas submit aja await stategrafik.update.submit(); - router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan'); + router.push( + '/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan' + ); }; return ( - @@ -63,36 +88,36 @@ function EditGrafikBerdasarkanPendidikan() { label="SD" type="number" placeholder="Masukkan jumlah" - defaultValue={stategrafik.update.form.SD} - onChange={(val) => (stategrafik.update.form.SD = val.currentTarget.value)} + value={formData.SD} + onChange={handleChange('SD')} /> (stategrafik.update.form.SMP = val.currentTarget.value)} + value={formData.SMP} + onChange={handleChange('SMP')} /> (stategrafik.update.form.SMA = val.currentTarget.value)} + value={formData.SMA} + onChange={handleChange('SMA')} /> (stategrafik.update.form.D3 = val.currentTarget.value)} + value={formData.D3} + onChange={handleChange('D3')} /> (stategrafik.update.form.S1 = val.currentTarget.value)} + value={formData.S1} + onChange={handleChange('S1')} /> diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/[id]/page.tsx index f80dd83a..f5e77711 100644 --- a/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/[id]/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/[id]/page.tsx @@ -2,39 +2,70 @@ 'use client'; import grafikNganggur from '@/app/admin/(dashboard)/_state/ekonomi/usia-kerja-nganggur'; import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, TextInput, Title, Group, Tooltip } from '@mantine/core'; +import { + Box, + Button, + Paper, + Stack, + TextInput, + Title, + Group, + Tooltip, +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useProxy } from 'valtio/utils'; import { toast } from 'react-toastify'; function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() { const router = useRouter(); const params = useParams() as { id: string }; - const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur); + const stategrafik = useProxy( + grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur + ); const id = params.id; + // ✅ state lokal, controlled + const [formData, setFormData] = useState({ + usia18_25: '', + usia26_35: '', + usia36_45: '', + usia46_keatas: '', + }); + + // load data dari global state -> masukin ke local state useEffect(() => { if (id) { stategrafik.findUnique.load(id).then(() => { const data = stategrafik.findUnique.data; if (data) { - stategrafik.update.form = { + setFormData({ usia18_25: data.usia18_25 || '', usia26_35: data.usia26_35 || '', usia36_45: data.usia36_45 || '', usia46_keatas: data.usia46_keatas || '', - }; + }); } }); } }, [id]); + const handleChange = (field: string, value: string) => { + setFormData((prev) => ({ + ...prev, + [field]: value, + })); + }; + const handleSubmit = async () => { try { + // ✅ baru update global state pas submit stategrafik.update.id = id; + stategrafik.update.form = { ...formData }; + await stategrafik.update.submit(); + toast.success('Data grafik berhasil diperbarui!'); router.push( '/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia' @@ -49,7 +80,12 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() { - @@ -71,40 +107,32 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() { label="Usia 18 - 25" type="number" placeholder="Masukkan jumlah" - defaultValue={stategrafik.update.form.usia18_25} - onChange={(val) => { - stategrafik.update.form.usia18_25 = val.currentTarget.value; - }} + value={formData.usia18_25} + onChange={(e) => handleChange('usia18_25', e.currentTarget.value)} required /> { - stategrafik.update.form.usia26_35 = val.currentTarget.value; - }} + value={formData.usia26_35} + onChange={(e) => handleChange('usia26_35', e.currentTarget.value)} required /> { - stategrafik.update.form.usia36_45 = val.currentTarget.value; - }} + value={formData.usia36_45} + onChange={(e) => handleChange('usia36_45', e.currentTarget.value)} required /> { - stategrafik.update.form.usia46_keatas = val.currentTarget.value; - }} + value={formData.usia46_keatas} + onChange={(e) => handleChange('usia46_keatas', e.currentTarget.value)} required /> diff --git a/src/app/admin/(dashboard)/ekonomi/jumlah-pengangguran/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ekonomi/jumlah-pengangguran/[id]/edit/page.tsx index 29f9a929..426a1fc7 100644 --- a/src/app/admin/(dashboard)/ekonomi/jumlah-pengangguran/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/jumlah-pengangguran/[id]/edit/page.tsx @@ -2,10 +2,21 @@ 'use client'; import jumlahPengangguranState from '@/app/admin/(dashboard)/_state/ekonomi/jumlah-pengangguran'; import colors from '@/con/colors'; -import { Box, Button, Group, Paper, Stack, Text, TextInput, Title, Select, NumberInput } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + Text, + TextInput, + Title, + Select, + NumberInput, +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useCallback } from 'react'; import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; @@ -24,40 +35,57 @@ function EditDetailDataPengangguran() { }); // Hitung total & perubahan otomatis - const calculateTotalAndChange = async () => { - const total = formData.educatedUnemployment + formData.uneducatedUnemployment; + const calculateTotalAndChange = useCallback( + async (data: typeof formData) => { + const total = data.educatedUnemployment + data.uneducatedUnemployment; - let percentageChange = 0; - const monthOrder = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des']; - const currentMonthIndex = monthOrder.indexOf(formData.month); + let percentageChange = 0; + const monthOrder = [ + 'Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', + 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des', + ]; + const currentMonthIndex = monthOrder.indexOf(data.month); - if (currentMonthIndex !== -1) { - let prevMonthIndex = currentMonthIndex - 1; - let prevYear = formData.year; + if (currentMonthIndex !== -1) { + let prevMonthIndex = currentMonthIndex - 1; + let prevYear = data.year; - if (prevMonthIndex < 0) { - prevMonthIndex = 11; - prevYear--; + if (prevMonthIndex < 0) { + prevMonthIndex = 11; + prevYear--; + } + + const prevMonth = monthOrder[prevMonthIndex]; + const prevData = await stateDetail.findByMonthYear.load({ + month: prevMonth, + year: prevYear, + }); + + if (prevData && prevData.totalUnemployment > 0) { + const change = + ((total - prevData.totalUnemployment) / + prevData.totalUnemployment) * + 100; + percentageChange = parseFloat(change.toFixed(1)); + } } - const prevMonth = monthOrder[prevMonthIndex]; - const prevData = await stateDetail.findByMonthYear.load({ month: prevMonth, year: prevYear }); - - if (prevData && prevData.totalUnemployment > 0) { - const change = ((total - prevData.totalUnemployment) / prevData.totalUnemployment) * 100; - percentageChange = parseFloat(change.toFixed(1)); - } - } - - return { total, percentageChange }; - }; + return { total, percentageChange }; + }, + [stateDetail.findByMonthYear] + ); const updateFormData = async (updates: Partial) => { const newData = { ...formData, ...updates }; - const { total, percentageChange } = await calculateTotalAndChange(); - setFormData({ ...newData, totalUnemployment: total, percentageChange }); + const { total, percentageChange } = await calculateTotalAndChange(newData); + setFormData({ + ...newData, + totalUnemployment: total, + percentageChange, + }); }; + // Load detail hanya sekali useEffect(() => { const loadDetail = async () => { const id = params?.id as string; @@ -68,45 +96,39 @@ function EditDetailDataPengangguran() { const data = stateDetail.findUnique.data; if (data) { + const yearValue = + data.year && typeof data.year === 'object' && 'getFullYear' in data.year + ? (data.year as Date).getFullYear() + : Number(data.year); - // Convert year from Date to number if needed - const yearValue = data.year && typeof data.year === 'object' && 'getFullYear' in data.year - ? (data.year as Date).getFullYear() - : Number(data.year); + stateDetail.update.id = id; // set ID untuk update - // Set the ID for update - stateDetail.update.id = id; - - // Update Valtio state with converted year - stateDetail.update.form = { - ...data, - year: yearValue, - percentageChange: data.percentageChange || 0 // Ensure it's always a number - }; - - // Update local formData with converted year setFormData({ month: data.month, year: yearValue, totalUnemployment: data.totalUnemployment, educatedUnemployment: data.educatedUnemployment, uneducatedUnemployment: data.uneducatedUnemployment, - percentageChange: data.percentageChange || 0, // Ensure it's always a number + percentageChange: data.percentageChange || 0, }); } } catch (error) { - console.error("Error loading detail:", error); - toast.error("Gagal memuat data detail"); + console.error('Error loading detail:', error); + toast.error('Gagal memuat data detail'); } }; loadDetail(); - }, [params?.id]); + }, [params?.id, stateDetail.findUnique]); const handleSubmit = async () => { - const { total, percentageChange } = await calculateTotalAndChange(); + const { total, percentageChange } = await calculateTotalAndChange(formData); try { - stateDetail.update.form = { ...formData, totalUnemployment: total, percentageChange }; + stateDetail.update.form = { + ...formData, + totalUnemployment: total, + percentageChange, + }; const success = await stateDetail.update.submit(); if (success) { toast.success('Detail data pengangguran berhasil diperbarui!'); @@ -121,7 +143,12 @@ function EditDetailDataPengangguran() { return ( - @@ -129,36 +156,57 @@ function EditDetailDataPengangguran() { - + ({ - value: p.id, - label: p.namaLengkap, - })) || []} + data={ + pegawaiList?.map(p => ({ + value: p.id, + label: p.namaLengkap, + })) || [] + } value={form.atasanId} - onChange={(val) => setForm({ ...form, atasanId: val || '' })} + onChange={val => handleChange('atasanId', val || '')} /> + ({ - value: p.id, // harus string - label: p.nama, - })) || [] + strukturorganisasiState.posisiOrganisasi.findMany.data?.map( + (p) => ({ + value: p.id, + label: p.nama, + }) + ) || [] } value={formData.posisiId} onChange={(value) => { - if (value !== null) { - setFormData({ ...formData, posisiId: value }); // value harus string - } + if (value !== null) updateForm('posisiId', value); }} /> + setFormData({ ...formData, status: e as Status })} + onChange={(val) => handleChange('status', val as Status)} label={Status Laporan Publik} placeholder="Pilih status laporan publik" data={[ @@ -152,10 +161,7 @@ function EditLaporanPublik() { Kronologi Laporan Publik { - setFormData((prev) => ({ ...prev, kronologi: htmlContent })); - stateLaporan.edit.form.kronologi = htmlContent; - }} + onChange={(htmlContent) => handleChange('kronologi', htmlContent)} /> @@ -163,10 +169,7 @@ function EditLaporanPublik() { Penanganan Laporan Publik { - setFormData((prev) => ({ ...prev, penanganan: htmlContent })); - stateLaporan.edit.form.penanganan = htmlContent; - }} + onChange={(htmlContent) => handleChange('penanganan', htmlContent)} /> diff --git a/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/edit/page.tsx index b2292469..9918b93e 100644 --- a/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/keamanan/pencegahan-kriminalitas/[id]/edit/page.tsx @@ -12,7 +12,7 @@ import { Stack, TextInput, Title, - Tooltip + Tooltip, } from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; @@ -32,6 +32,7 @@ function EditPencegahanKriminalitas() { linkVideo: '', }); + // load data hanya sekali pas id berubah useEffect(() => { const loadKriminalitas = async () => { const id = params?.id as string; @@ -41,10 +42,10 @@ function EditPencegahanKriminalitas() { const data = await kriminalitasState.update.load(id); if (data) { setFormData({ - judul: data.judul || '', - deskripsi: data.deskripsi || '', - deskripsiSingkat: data.deskripsiSingkat || '', - linkVideo: data.linkVideo || '', + judul: data.judul ?? '', + deskripsi: data.deskripsi ?? '', + deskripsiSingkat: data.deskripsiSingkat ?? '', + linkVideo: data.linkVideo ?? '', }); } } catch (error) { @@ -58,6 +59,12 @@ function EditPencegahanKriminalitas() { const embedLink = convertYoutubeUrlToEmbed(formData.linkVideo); + const handleChange = + (field: keyof typeof formData) => + (e: React.ChangeEvent) => { + setFormData((prev) => ({ ...prev, [field]: e.target.value })); + }; + const handleSubmit = async () => { const converted = convertYoutubeUrlToEmbed(formData.linkVideo); if (!converted) { @@ -66,16 +73,13 @@ function EditPencegahanKriminalitas() { } try { - // Update the form data first + // update global state saat submit kriminalitasState.update.form = { - ...kriminalitasState.update.form, judul: formData.judul, deskripsi: formData.deskripsi, deskripsiSingkat: formData.deskripsiSingkat, linkVideo: formData.linkVideo, }; - - // Set the ID and then call update kriminalitasState.update.id = params?.id as string; await kriminalitasState.update.update(); @@ -119,18 +123,16 @@ function EditPencegahanKriminalitas() { setFormData({ ...formData, judul: e.target.value })} + value={formData.judul} + onChange={handleChange('judul')} required /> - setFormData({ ...formData, deskripsiSingkat: e.target.value }) - } + value={formData.deskripsiSingkat} + onChange={handleChange('deskripsiSingkat')} required /> @@ -141,7 +143,7 @@ function EditPencegahanKriminalitas() { - setFormData({ ...formData, deskripsi: val }) + setFormData((prev) => ({ ...prev, deskripsi: val })) } /> @@ -150,10 +152,8 @@ function EditPencegahanKriminalitas() { - setFormData({ ...formData, linkVideo: e.currentTarget.value }) - } + value={formData.linkVideo} + onChange={handleChange('linkVideo')} required /> {embedLink && ( diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx index c9bb69fb..bf826e3d 100644 --- a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx @@ -52,50 +52,36 @@ function EditPolsekTerdekat() { layananPolsekId: "", }); - useEffect(() => { - const loadPolsekTerdekat = async () => { - const id = params?.id as string; - if (!id) return; - - try { - const data = await polsekState.edit.load(id); - if (data) { - setFormData({ - nama: data.nama || "", - jarakKeDesa: data.jarakKeDesa || "", - alamat: data.alamat || "", - nomorTelepon: data.nomorTelepon || "", - jamOperasional: data.jamOperasional || "", - embedMapUrl: data.embedMapUrl || "", - namaTempatMaps: data.namaTempatMaps || "", - alamatMaps: data.alamatMaps || "", - linkPetunjukArah: data.linkPetunjukArah || "", - layananPolsekId: data.layananPolsekId || "", - }); + // load data untuk form edit + useEffect(() => { + const loadPolsekTerdekat = async () => { + const id = params?.id as string; + if (!id) return; + + try { + const data = await polsekState.edit.load(id); + if (data) { + setFormData({ + nama: data.nama || "", + jarakKeDesa: data.jarakKeDesa || "", + alamat: data.alamat || "", + nomorTelepon: data.nomorTelepon || "", + jamOperasional: data.jamOperasional || "", + embedMapUrl: data.embedMapUrl || "", + namaTempatMaps: data.namaTempatMaps || "", + alamatMaps: data.alamatMaps || "", + linkPetunjukArah: data.linkPetunjukArah || "", + layananPolsekId: data.layananPolsekId || "", + }); + } + } catch (error) { + console.error("Error loading polsek terdekat:", error); + toast.error("Gagal memuat data polsek terdekat"); } - } catch (error) { - console.error("Error loading polsek terdekat:", error); - toast.error("Gagal memuat data polsek terdekat"); - } - }; - - loadPolsekTerdekat(); - }, [params?.id]); - - const handleSubmit = async () => { - try { - polsekState.edit.form = { - ...polsekState.edit.form, - ...formData, }; - await polsekState.edit.update(); - toast.success("Polsek terdekat berhasil diperbarui!"); - router.push("/admin/keamanan/polsek-terdekat"); - } catch (error) { - console.error("Error updating polsek terdekat:", error); - toast.error("Gagal memperbarui data polsek terdekat"); - } - }; + + loadPolsekTerdekat(); + }, [params?.id]); const fetchLayanan = async () => { try { @@ -198,6 +184,22 @@ function EditPolsekTerdekat() { fetchLayanan(); }, []); + const handleChange = (field: string, value: string) => { + setFormData(prev => ({ ...prev, [field]: value })); + }; + + const handleSubmit = async () => { + try { + polsekState.edit.form = { ...formData }; // update global state hanya di sini + await polsekState.edit.update(); + toast.success("Polsek terdekat berhasil diperbarui!"); + router.push("/admin/keamanan/polsek-terdekat"); + } catch (error) { + console.error("Error updating polsek terdekat:", error); + toast.error("Gagal memperbarui data polsek terdekat"); + } + }; + return ( {/* Modal Tambah */} @@ -273,88 +275,59 @@ function EditPolsekTerdekat() { {/* Input fields */} - setFormData({ ...formData, nama: val.target.value }) - } + value={formData.nama} + onChange={(e) => handleChange("nama", e.currentTarget.value)} label="Nama Polsek Terdekat" placeholder="Masukkan nama Polsek Terdekat" required /> - setFormData({ ...formData, jarakKeDesa: val.target.value }) - } + value={formData.jarakKeDesa} + onChange={(e) => handleChange("jarakKeDesa", e.currentTarget.value)} label="Jarak Polsek Terdekat" - placeholder="Masukkan jarak Polsek Terdekat" /> - setFormData({ ...formData, alamat: val.target.value }) - } + value={formData.alamat} + onChange={(e) => handleChange("alamat", e.currentTarget.value)} label="Alamat Polsek Terdekat" - placeholder="Masukkan alamat Polsek Terdekat" /> - setFormData({ ...formData, nomorTelepon: val.target.value }) - } + value={formData.nomorTelepon} + onChange={(e) => handleChange("nomorTelepon", e.currentTarget.value)} label="Nomor Telepon" - placeholder="Masukkan nomor telepon Polsek Terdekat" /> - setFormData({ ...formData, jamOperasional: val.target.value }) - } + value={formData.jamOperasional} + onChange={(e) => handleChange("jamOperasional", e.currentTarget.value)} label="Jam Operasional" - placeholder="Masukkan jam operasional Polsek Terdekat" /> - setFormData({ ...formData, embedMapUrl: val.target.value }) - } + value={formData.embedMapUrl} + onChange={(e) => handleChange("embedMapUrl", e.currentTarget.value)} label="Embed Map URL" - placeholder="Masukkan embed map url" /> - setFormData({ ...formData, namaTempatMaps: val.target.value }) - } + value={formData.namaTempatMaps} + onChange={(e) => handleChange("namaTempatMaps", e.currentTarget.value)} label="Nama Tempat Maps" - placeholder="Masukkan nama tempat di maps" /> - setFormData({ ...formData, alamatMaps: val.target.value }) - } + value={formData.alamatMaps} + onChange={(e) => handleChange("alamatMaps", e.currentTarget.value)} label="Alamat Maps" - placeholder="Masukkan alamat di maps" /> - setFormData({ ...formData, linkPetunjukArah: val.target.value }) - } + value={formData.linkPetunjukArah} + onChange={(e) => handleChange("linkPetunjukArah", e.currentTarget.value)} label="Link Petunjuk Arah" - placeholder="Masukkan link petunjuk arah" /> - {/* Dropdown Layanan */} setFormData({ ...formData, kategoriId: val || '' })} - data={ - korupsiState.kategoriDesaAntiKorupsi.findMany.data?.map((v) => ({ - value: v.id, - label: v.name, - })) || [] - } + onChange={handleSelectChange('kategoriId')} + data={korupsiState.kategoriDesaAntiKorupsi.findMany.data?.map((v) => ({ + value: v.id, + label: v.name, + })) || []} required searchable clearable @@ -180,13 +176,7 @@ export default function EditDesaAntiKorupsi() { Dokumen { - const selectedFile = files[0]; - if (selectedFile) { - setFile(selectedFile); - setPreviewFile(URL.createObjectURL(selectedFile)); - } - }} + onDrop={handleDrop} onReject={() => toast.error('File tidak valid, gunakan format dokumen')} maxSize={5 * 1024 ** 2} accept={{ @@ -229,12 +219,7 @@ export default function EditDesaAntiKorupsi() { width: '100%', }} > -