fix inputan edit menu: desa, ekonomi, inovasi, keamanan, kesehatan, landing-page, & lingkungan

This commit is contained in:
2025-09-30 21:41:26 +08:00
parent c2f1ab8179
commit 63054cedf0
67 changed files with 3056 additions and 2989 deletions

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
'use client';
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import dataLingkunganDesaState from '@/app/admin/(dashboard)/_state/lingkungan/data-lingkungan-desa';
import colors from '@/con/colors';
@@ -46,7 +47,7 @@ type IconKey =
| 'pohon'
| 'air';
function EditDataLingkunganDesa() {
export default function EditDataLingkunganDesa() {
const stateDataLingkunganDesa = useProxy(dataLingkunganDesaState);
const params = useParams();
const router = useRouter();
@@ -58,8 +59,9 @@ function EditDataLingkunganDesa() {
icon: '',
});
// Load data saat komponen mount
useEffect(() => {
const loadProgramKreatif = async () => {
const loadData = async () => {
const id = params?.id as string;
if (!id) return;
@@ -67,14 +69,6 @@ function EditDataLingkunganDesa() {
const data = await stateDataLingkunganDesa.update.load(id);
if (data) {
stateDataLingkunganDesa.update.id = id;
stateDataLingkunganDesa.update.form = {
name: data.name,
deskripsi: data.deskripsi,
jumlah: data.jumlah,
icon: data.icon,
};
setFormData({
name: data.name,
deskripsi: data.deskripsi,
@@ -88,13 +82,14 @@ function EditDataLingkunganDesa() {
}
};
loadProgramKreatif();
loadData();
}, [params?.id]);
const handleSubmit = async () => {
try {
// Update global state hanya saat submit
stateDataLingkunganDesa.update.form = {
...stateDataLingkunganDesa.update.form,
...formData,
name: formData.name.trim(),
deskripsi: formData.deskripsi.trim(),
jumlah: formData.jumlah.trim(),
@@ -132,27 +127,21 @@ function EditDataLingkunganDesa() {
>
<Stack gap="md">
<TextInput
defaultValue={formData.name}
value={formData.name}
label={<Text fz="sm" fw="bold">Nama Data Lingkungan Desa</Text>}
placeholder="Masukkan nama data lingkungan desa"
onChange={(val) =>
setFormData({
...formData,
name: val.target.value,
})
onChange={(e) =>
setFormData((prev) => ({ ...prev, name: e.target.value }))
}
required
/>
<TextInput
defaultValue={formData.jumlah}
value={formData.jumlah}
label={<Text fz="sm" fw="bold">Jumlah Data Lingkungan Desa</Text>}
placeholder="Masukkan jumlah data lingkungan desa"
onChange={(val) =>
setFormData({
...formData,
jumlah: val.target.value,
})
onChange={(e) =>
setFormData((prev) => ({ ...prev, jumlah: e.target.value }))
}
required
/>
@@ -163,10 +152,9 @@ function EditDataLingkunganDesa() {
</Text>
<EditEditor
value={formData.deskripsi}
onChange={(htmlContent) => {
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
stateDataLingkunganDesa.update.form.deskripsi = htmlContent;
}}
onChange={(htmlContent) =>
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
}
/>
</Box>
@@ -176,10 +164,9 @@ function EditDataLingkunganDesa() {
</Text>
<SelectIconProgramEdit
value={formData.icon as IconKey}
onChange={(value) => {
setFormData((prev) => ({ ...prev, icon: value }));
stateDataLingkunganDesa.update.form.icon = value;
}}
onChange={(value) =>
setFormData((prev) => ({ ...prev, icon: value }))
}
/>
</Box>
@@ -202,5 +189,3 @@ function EditDataLingkunganDesa() {
</Box>
);
}
export default EditDataLingkunganDesa;

View File

@@ -1,4 +1,5 @@
'use client'
import stateEdukasiLingkungan from '@/app/admin/(dashboard)/_state/lingkungan/edukasi-lingkungan';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, Title } from '@mantine/core';
@@ -10,48 +11,67 @@ import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
const EdukasiLingkunganTextEditor = dynamic(
() => import('../../_lib/edukasiLingkunganTextEditor').then(mod => mod.EdukasiLingkunganTextEditor),
() =>
import('../../_lib/edukasiLingkunganTextEditor').then(
(mod) => mod.EdukasiLingkunganTextEditor
),
{ ssr: false }
);
function EditContohKegiatanDesaDarmasaba() {
export default function EditContohKegiatanDesaDarmasaba() {
const router = useRouter();
const contohEdukasiState = useProxy(stateEdukasiLingkungan.stateContohEdukasiLingkungan);
const [judul, setJudul] = useState('');
const [content, setContent] = useState('');
const contohEdukasiState = useProxy(
stateEdukasiLingkungan.stateContohEdukasiLingkungan
);
// state lokal untuk form
const [formData, setFormData] = useState({
judul: '',
deskripsi: '',
});
// load data awal
useShallowEffect(() => {
if (!contohEdukasiState.findById.data) {
contohEdukasiState.findById.initialize();
}
}, []);
// update state lokal saat data global sudah ada
useEffect(() => {
if (contohEdukasiState.findById.data) {
setJudul(contohEdukasiState.findById.data.judul ?? '');
setContent(contohEdukasiState.findById.data.deskripsi ?? '');
setFormData({
judul: contohEdukasiState.findById.data.judul ?? '',
deskripsi: contohEdukasiState.findById.data.deskripsi ?? '',
});
}
}, [contohEdukasiState.findById.data]);
const submit = () => {
// handler perubahan input
const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
setFormData((prev) => ({ ...prev, [field]: value }));
};
// submit update
const handleSubmit = () => {
if (contohEdukasiState.findById.data) {
contohEdukasiState.findById.data.judul = judul;
contohEdukasiState.findById.data.deskripsi = content;
contohEdukasiState.update.save(contohEdukasiState.findById.data);
const updatedData = {
...contohEdukasiState.findById.data,
judul: formData.judul,
deskripsi: formData.deskripsi,
};
contohEdukasiState.update.save(updatedData);
}
router.push('/admin/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba');
router.push(
'/admin/lingkungan/edukasi-lingkungan/contoh-kegiatan-desa-darmasaba'
);
};
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
{/* Header */}
<Group mb="md">
<Button
variant="subtle"
onClick={() => router.back()}
p="xs"
radius="md"
>
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
<IconArrowBack color={colors['blue-button']} size={24} />
</Button>
<Title order={4} ml="sm" c="dark">
@@ -75,8 +95,8 @@ function EditContohKegiatanDesaDarmasaba() {
</Text>
<EdukasiLingkunganTextEditor
showSubmit={false}
onChange={setJudul}
initialContent={judul}
onChange={(value) => handleChange('judul', value)}
initialContent={formData.judul}
/>
</Box>
@@ -86,14 +106,14 @@ function EditContohKegiatanDesaDarmasaba() {
</Text>
<EdukasiLingkunganTextEditor
showSubmit={false}
onChange={setContent}
initialContent={content}
onChange={(value) => handleChange('deskripsi', value)}
initialContent={formData.deskripsi}
/>
</Box>
<Group justify="right" mt="md">
<Button
onClick={submit}
onClick={handleSubmit}
radius="md"
size="md"
style={{
@@ -111,5 +131,3 @@ function EditContohKegiatanDesaDarmasaba() {
</Box>
);
}
export default EditContohKegiatanDesaDarmasaba;

View File

@@ -14,30 +14,43 @@ const EdukasiLingkunganTextEditor = dynamic(
{ ssr: false }
);
function EditMateriEdukasiYangDiberikan() {
export default function EditMateriEdukasiYangDiberikan() {
const router = useRouter();
const materiEdukasiState = useProxy(stateEdukasiLingkungan.stateMateriEdukasiLingkungan);
const [judul, setJudul] = useState('');
const [content, setContent] = useState('');
// State lokal gabungan untuk form
const [formData, setFormData] = useState({ judul: '', content: '' });
// Initialize data kalau belum ada
useShallowEffect(() => {
if (!materiEdukasiState.findById.data) {
materiEdukasiState.findById.initialize();
}
}, []);
// Set formData saat data tersedia
useEffect(() => {
if (materiEdukasiState.findById.data) {
setJudul(materiEdukasiState.findById.data.judul ?? '');
setContent(materiEdukasiState.findById.data.deskripsi ?? '');
setFormData({
judul: materiEdukasiState.findById.data.judul ?? '',
content: materiEdukasiState.findById.data.deskripsi ?? '',
});
}
}, [materiEdukasiState.findById.data]);
// Handler perubahan form
const handleChange = (field: 'judul' | 'content', value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const submit = () => {
if (materiEdukasiState.findById.data) {
materiEdukasiState.findById.data.judul = judul;
materiEdukasiState.findById.data.deskripsi = content;
materiEdukasiState.update.save(materiEdukasiState.findById.data);
const updatedData = {
...materiEdukasiState.findById.data,
judul: formData.judul,
deskripsi: formData.content,
};
materiEdukasiState.update.save(updatedData);
}
router.push('/admin/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan');
};
@@ -66,14 +79,22 @@ function EditMateriEdukasiYangDiberikan() {
<Text fw="bold" mb={6}>
Judul
</Text>
<EdukasiLingkunganTextEditor showSubmit={false} onChange={setJudul} initialContent={judul} />
<EdukasiLingkunganTextEditor
showSubmit={false}
onChange={(val) => handleChange('judul', val)}
initialContent={formData.judul}
/>
</Box>
<Box>
<Text fw="bold" mb={6}>
Konten
</Text>
<EdukasiLingkunganTextEditor showSubmit={false} onChange={setContent} initialContent={content} />
<EdukasiLingkunganTextEditor
showSubmit={false}
onChange={(val) => handleChange('content', val)}
initialContent={formData.content}
/>
</Box>
<Group justify="right" mt="md">
@@ -96,5 +117,3 @@ function EditMateriEdukasiYangDiberikan() {
</Box>
);
}
export default EditMateriEdukasiYangDiberikan;

View File

@@ -1,4 +1,5 @@
'use client'
import stateEdukasiLingkungan from '@/app/admin/(dashboard)/_state/lingkungan/edukasi-lingkungan';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, Title } from '@mantine/core';
@@ -14,30 +15,44 @@ const EdukasiLingkunganTextEditor = dynamic(
{ ssr: false }
);
function EditTujuanEdukasiLingkungan() {
export default function EditTujuanEdukasiLingkungan() {
const router = useRouter();
const tujuanEdukasiState = useProxy(stateEdukasiLingkungan.stateTujuanEdukasi);
const [judul, setJudul] = useState('');
const [content, setContent] = useState('');
// State lokal untuk form, gak tergantung global state
const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
// Initialize global state
useShallowEffect(() => {
if (!tujuanEdukasiState.findById.data) {
tujuanEdukasiState.findById.initialize();
}
}, []);
// Sync initial values dari global state ke form lokal
useEffect(() => {
if (tujuanEdukasiState.findById.data) {
setJudul(tujuanEdukasiState.findById.data.judul ?? '');
setContent(tujuanEdukasiState.findById.data.deskripsi ?? '');
setFormData({
judul: tujuanEdukasiState.findById.data.judul ?? '',
deskripsi: tujuanEdukasiState.findById.data.deskripsi ?? '',
});
}
}, [tujuanEdukasiState.findById.data]);
// Handler input
const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const submit = () => {
if (tujuanEdukasiState.findById.data) {
tujuanEdukasiState.findById.data.judul = judul;
tujuanEdukasiState.findById.data.deskripsi = content;
tujuanEdukasiState.update.save(tujuanEdukasiState.findById.data);
// Update global state hanya saat submit
const updatedData = {
...tujuanEdukasiState.findById.data,
judul: formData.judul,
deskripsi: formData.deskripsi,
};
tujuanEdukasiState.update.save(updatedData);
}
router.push('/admin/lingkungan/edukasi-lingkungan/tujuan-edukasi-lingkungan');
};
@@ -73,8 +88,8 @@ function EditTujuanEdukasiLingkungan() {
</Text>
<EdukasiLingkunganTextEditor
showSubmit={false}
onChange={setJudul}
initialContent={judul}
onChange={(value) => handleChange('judul', value)}
initialContent={formData.judul}
/>
</Box>
@@ -84,8 +99,8 @@ function EditTujuanEdukasiLingkungan() {
</Text>
<EdukasiLingkunganTextEditor
showSubmit={false}
onChange={setContent}
initialContent={content}
onChange={(value) => handleChange('deskripsi', value)}
initialContent={formData.deskripsi}
/>
</Box>
@@ -109,5 +124,3 @@ function EditTujuanEdukasiLingkungan() {
</Box>
);
}
export default EditTujuanEdukasiLingkungan;

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
'use client';
import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
@@ -15,53 +16,59 @@ function EditKategoriKegiatan() {
const id = params?.id as string;
const stateKategori = useProxy(gotongRoyongState.kategoriKegiatan);
const [formData, setFormData] = useState({
nama: '',
});
const [formData, setFormData] = useState({ nama: '' });
const [loading, setLoading] = useState(true);
// Load data once
useEffect(() => {
const loadKategorikegiatan = async () => {
if (!id) return;
if (!id) return;
const loadKategori = async () => {
try {
const data = await stateKategori.edit.load(id);
if (data) {
stateKategori.edit.id = id;
setFormData({
nama: data.nama || '',
});
setFormData({ nama: data.nama || '' });
}
} catch (error) {
console.error('Error loading kategori kegiatan:', error);
} catch (err) {
console.error('Error loading kategori kegiatan:', err);
toast.error('Gagal memuat data kategori kegiatan');
} finally {
setLoading(false);
}
};
loadKategorikegiatan();
loadKategori();
}, [id]);
const handleSubmit = async () => {
try {
if (!formData.nama.trim()) {
toast.error('Nama kategori kegiatan tidak boleh kosong');
return;
}
const handleChange = (field: string, value: string) => {
setFormData((prev) => ({ ...prev, [field]: value }));
};
stateKategori.edit.form = { nama: formData.nama.trim() };
const handleSubmit = async () => {
const trimmedNama = formData.nama.trim();
if (!trimmedNama) {
toast.error('Nama kategori kegiatan tidak boleh kosong');
return;
}
try {
stateKategori.edit.form = { nama: trimmedNama };
if (!stateKategori.edit.id) stateKategori.edit.id = id;
const success = await stateKategori.edit.update();
if (success) {
toast.success('Kategori kegiatan berhasil diperbarui!');
router.push('/admin/lingkungan/gotong-royong/kategori-kegiatan');
}
} catch (error) {
console.error('Error updating kategori kegiatan:', error);
} catch (err) {
console.error('Error updating kategori kegiatan:', err);
toast.error('Gagal memperbarui kategori kegiatan');
}
};
if (loading) return <Text>Loading...</Text>;
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
<Group mb="md" align="center">
@@ -85,8 +92,8 @@ function EditKategoriKegiatan() {
>
<Stack gap="md">
<TextInput
defaultValue={formData.nama}
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
value={formData.nama}
onChange={(e) => handleChange('nama', e.target.value)}
label={<Text fw="bold" fz="sm">Nama Kategori Kegiatan</Text>}
placeholder="Masukkan nama kategori kegiatan"
required

View File

@@ -4,7 +4,19 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
import {
Box,
Button,
Group,
Image,
Paper,
Select,
Stack,
Text,
TextInput,
Title,
Tooltip,
} from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -23,12 +35,10 @@ interface FormKegiatanDesa {
kategoriKegiatanId: string;
}
function EditGotongRoyong() {
const kegiatanDesaState = useProxy(gotongRoyongState.kegiatanDesa)
const params = useParams()
const router = useRouter()
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
export default function EditKegiatanDesa() {
const kegiatanDesaState = useProxy(gotongRoyongState.kegiatanDesa);
const params = useParams();
const router = useRouter();
const [formData, setFormData] = useState<FormKegiatanDesa>({
judul: '',
@@ -39,16 +49,19 @@ function EditGotongRoyong() {
partisipan: 0,
imageId: '',
kategoriKegiatanId: '',
})
});
const [file, setFile] = useState<File | null>(null);
const [previewImage, setPreviewImage] = useState<string | null>(null);
const formatDateForInput = (dateString: string) => {
if (!dateString) return '';
const date = new Date(dateString);
return date.toISOString().split('T')[0];
return new Date(dateString).toISOString().split('T')[0];
};
// Load kategori & data kegiatan
useEffect(() => {
const loadKegiatanDesa = async () => {
const loadData = async () => {
gotongRoyongState.kategoriKegiatan.findMany.load();
const id = params?.id as string;
if (!id) return;
@@ -65,43 +78,48 @@ function EditGotongRoyong() {
imageId: data.imageId || '',
kategoriKegiatanId: data.kategoriKegiatanId || '',
});
if (data.imageId) {
// Optional: bisa fetch URL image dari backend
setPreviewImage(`/api/file/${data.imageId}`);
}
}
} catch (error) {
console.error("Error loading kegiatan desa:", error);
toast.error("Gagal memuat data kegiatan desa");
console.error(error);
toast.error('Gagal memuat data kegiatan desa');
}
}
gotongRoyongState.kategoriKegiatan.findMany.load()
loadKegiatanDesa();
};
loadData();
}, [params?.id]);
const handleSubmit = async () => {
try {
kegiatanDesaState.edit.form = {
...kegiatanDesaState.edit.form,
judul: formData.judul.trim(),
deskripsiSingkat: formData.deskripsiSingkat.trim(),
deskripsiLengkap: formData.deskripsiLengkap.trim(),
tanggal: new Date(formData.tanggal.trim()),
lokasi: formData.lokasi.trim(),
partisipan: formData.partisipan,
imageId: formData.imageId,
kategoriKegiatanId: formData.kategoriKegiatanId,
}
let imageId = formData.imageId;
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
if (!uploaded?.id) return toast.error("Gagal upload gambar");
kegiatanDesaState.edit.form.imageId = uploaded.id;
if (!uploaded?.id) return toast.error('Gagal upload gambar');
imageId = uploaded.id;
}
await kegiatanDesaState.edit.update()
toast.success("Kegiatan desa berhasil diperbarui!")
router.push("/admin/lingkungan/gotong-royong/kegiatan-desa");
kegiatanDesaState.edit.form = {
judul: formData.judul.trim(),
deskripsiSingkat: formData.deskripsiSingkat.trim(),
deskripsiLengkap: formData.deskripsiLengkap.trim(),
tanggal: new Date(formData.tanggal),
lokasi: formData.lokasi.trim(),
partisipan: formData.partisipan,
imageId,
kategoriKegiatanId: formData.kategoriKegiatanId,
};
await kegiatanDesaState.edit.update();
toast.success('Kegiatan desa berhasil diperbarui!');
router.push('/admin/lingkungan/gotong-royong/kegiatan-desa');
} catch (error) {
console.error("Error updating kegiatan desa:", error);
toast.error("Terjadi kesalahan saat memperbarui kegiatan desa");
console.error(error);
toast.error('Terjadi kesalahan saat memperbarui kegiatan desa');
}
}
};
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
@@ -126,14 +144,14 @@ function EditGotongRoyong() {
>
<Stack gap="md">
<TextInput
defaultValue={formData.judul}
value={formData.judul}
label={<Text fz="sm" fw="bold">Judul Kegiatan Desa</Text>}
placeholder="masukkan judul kegiatan desa"
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
required
/>
<TextInput
defaultValue={formData.deskripsiSingkat}
value={formData.deskripsiSingkat}
label={<Text fz="sm" fw="bold">Deskripsi Singkat Kegiatan Desa</Text>}
placeholder="masukkan deskripsi singkat kegiatan desa"
onChange={(e) => setFormData({ ...formData, deskripsiSingkat: e.target.value })}
@@ -148,33 +166,27 @@ function EditGotongRoyong() {
value={formData.kategoriKegiatanId}
onChange={(val) => setFormData({ ...formData, kategoriKegiatanId: val ?? '' })}
/>
<Box>
<Text fw="bold" fz="sm">Deskripsi Lengkap Kegiatan Desa</Text>
<EditEditor
value={formData.deskripsiLengkap}
onChange={(htmlContent) => {
setFormData((prev) => ({ ...prev, deskripsiLengkap: htmlContent }));
kegiatanDesaState.edit.form.deskripsiLengkap = htmlContent;
}}
onChange={(htmlContent) => setFormData(prev => ({ ...prev, deskripsiLengkap: htmlContent }))}
/>
</Box>
<TextInput
label={<Text fz="sm" fw="bold">Tanggal Kegiatan Desa</Text>}
placeholder="masukkan tanggal kegiatan desa"
type="date"
defaultValue={formatDateForInput(formData.tanggal)}
label={<Text fz="sm" fw="bold">Tanggal Kegiatan Desa</Text>}
value={formatDateForInput(formData.tanggal)}
onChange={(e) => setFormData({ ...formData, tanggal: e.target.value })}
/>
<TextInput
defaultValue={formData.lokasi}
value={formData.lokasi}
label={<Text fz="sm" fw="bold">Lokasi Kegiatan Desa</Text>}
placeholder="masukkan lokasi kegiatan desa"
onChange={(e) => setFormData({ ...formData, lokasi: e.target.value })}
/>
<TextInput
defaultValue={formData.partisipan}
value={formData.partisipan}
label={<Text fz="sm" fw="bold">Partisipan Kegiatan Desa</Text>}
placeholder="masukkan partisipan kegiatan desa"
onChange={(e) => {
@@ -187,11 +199,10 @@ function EditGotongRoyong() {
<Text fw="bold" fz="sm" mb={6}>Gambar Kegiatan Desa</Text>
<Dropzone
onDrop={(files) => {
const selectedFile = files[0];
if (selectedFile) {
setFile(selectedFile);
setPreviewImage(URL.createObjectURL(selectedFile));
}
const selected = files[0];
if (!selected) return;
setFile(selected);
setPreviewImage(URL.createObjectURL(selected));
}}
onReject={() => toast.error('File tidak valid.')}
maxSize={5 * 1024 ** 2}
@@ -248,5 +259,3 @@ function EditGotongRoyong() {
</Box>
);
}
export default EditGotongRoyong;

View File

@@ -17,9 +17,11 @@ const KonservasiAdatBaliTextEditor = dynamic(
function EditBentukKonservasiBerdasarkanAdat() {
const router = useRouter();
const bentukKonservasiState = useProxy(stateKonservasiAdatBali.stateBentukKonservasiBerdasarkanAdat);
const [judul, setJudul] = useState('');
const [content, setContent] = useState('');
// Gabung semua field form jadi satu object
const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
// Initialize data dari global state
useShallowEffect(() => {
if (!bentukKonservasiState.findById.data) {
bentukKonservasiState.findById.initialize();
@@ -28,16 +30,22 @@ function EditBentukKonservasiBerdasarkanAdat() {
useEffect(() => {
if (bentukKonservasiState.findById.data) {
setJudul(bentukKonservasiState.findById.data.judul ?? '');
setContent(bentukKonservasiState.findById.data.deskripsi ?? '');
setFormData({
judul: bentukKonservasiState.findById.data.judul ?? '',
deskripsi: bentukKonservasiState.findById.data.deskripsi ?? '',
});
}
}, [bentukKonservasiState.findById.data]);
const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const submit = () => {
if (bentukKonservasiState.findById.data) {
bentukKonservasiState.findById.data.judul = judul;
bentukKonservasiState.findById.data.deskripsi = content;
bentukKonservasiState.update.save(bentukKonservasiState.findById.data);
// Update global state cuma pas submit
const updatedData = { ...bentukKonservasiState.findById.data, ...formData };
bentukKonservasiState.update.save(updatedData);
}
router.push('/admin/lingkungan/konservasi-adat-bali/bentuk-konservasi-berdasarkan-adat');
};
@@ -46,12 +54,7 @@ function EditBentukKonservasiBerdasarkanAdat() {
<Box px={{ base: 'sm', md: 'lg' }} py="md">
{/* Header */}
<Group mb="md">
<Button
variant="subtle"
onClick={() => router.back()}
p="xs"
radius="md"
>
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
<IconArrowBack color={colors['blue-button']} size={24} />
</Button>
<Title order={4} ml="sm" c="dark">
@@ -75,8 +78,8 @@ function EditBentukKonservasiBerdasarkanAdat() {
</Text>
<KonservasiAdatBaliTextEditor
showSubmit={false}
onChange={setJudul}
initialContent={judul}
onChange={(value: string) => handleChange('judul', value)}
initialContent={formData.judul}
/>
</Box>
@@ -86,8 +89,8 @@ function EditBentukKonservasiBerdasarkanAdat() {
</Text>
<KonservasiAdatBaliTextEditor
showSubmit={false}
onChange={setContent}
initialContent={content}
onChange={(value: string) => handleChange('deskripsi', value)}
initialContent={formData.deskripsi}
/>
</Box>

View File

@@ -1,4 +1,5 @@
'use client'
import stateKonservasiAdatBali from '@/app/admin/(dashboard)/_state/lingkungan/konservasi-adat-bali';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, Title } from '@mantine/core';
@@ -20,26 +21,35 @@ const KonservasiAdatBaliTextEditor = dynamic(
function EditFilosofiTriHitaKarana() {
const router = useRouter();
const filosofiTriHitaState = useProxy(stateKonservasiAdatBali.stateFilosofiTriHita);
const [judul, setJudul] = useState('');
const [content, setContent] = useState('');
// Local state form
const [formData, setFormData] = useState({ judul: '', content: '' });
// Load data dari global state kalau belum ada
useShallowEffect(() => {
if (!filosofiTriHitaState.findById.data) {
filosofiTriHitaState.findById.initialize();
}
}, []);
// Set formData dari global state saat data tersedia
useEffect(() => {
if (filosofiTriHitaState.findById.data) {
setJudul(filosofiTriHitaState.findById.data.judul ?? '');
setContent(filosofiTriHitaState.findById.data.deskripsi ?? '');
setFormData({
judul: filosofiTriHitaState.findById.data.judul ?? '',
content: filosofiTriHitaState.findById.data.deskripsi ?? '',
});
}
}, [filosofiTriHitaState.findById.data]);
const submit = () => {
const handleChange = (field: 'judul' | 'content', value: string) => {
setFormData((prev) => ({ ...prev, [field]: value }));
};
const handleSubmit = () => {
if (filosofiTriHitaState.findById.data) {
filosofiTriHitaState.findById.data.judul = judul;
filosofiTriHitaState.findById.data.deskripsi = content;
filosofiTriHitaState.findById.data.judul = formData.judul;
filosofiTriHitaState.findById.data.deskripsi = formData.content;
filosofiTriHitaState.update.save(filosofiTriHitaState.findById.data);
}
router.push('/admin/lingkungan/konservasi-adat-bali/filosofi-tri-hita-karana');
@@ -49,12 +59,7 @@ function EditFilosofiTriHitaKarana() {
<Box px={{ base: 'sm', md: 'lg' }} py="md">
{/* Header */}
<Group mb="md">
<Button
variant="subtle"
onClick={() => router.back()}
p="xs"
radius="md"
>
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
<IconArrowBack color={colors['blue-button']} size={24} />
</Button>
<Title order={4} ml="sm" c="dark">
@@ -78,8 +83,8 @@ function EditFilosofiTriHitaKarana() {
</Text>
<KonservasiAdatBaliTextEditor
showSubmit={false}
onChange={setJudul}
initialContent={judul}
onChange={(val) => handleChange('judul', val)}
initialContent={formData.judul}
/>
</Box>
@@ -89,14 +94,14 @@ function EditFilosofiTriHitaKarana() {
</Text>
<KonservasiAdatBaliTextEditor
showSubmit={false}
onChange={setContent}
initialContent={content}
onChange={(val) => handleChange('content', val)}
initialContent={formData.content}
/>
</Box>
<Group justify="right" mt="md">
<Button
onClick={submit}
onClick={handleSubmit}
radius="md"
size="md"
style={{

View File

@@ -17,26 +17,36 @@ const KonservasiAdatBaliTextEditor = dynamic(
function EditNilaiKonservasiAdat() {
const router = useRouter();
const nilaiKonservasiState = useProxy(stateKonservasiAdatBali.stateNilaiKonservasiAdat);
const [judul, setJudul] = useState('');
const [content, setContent] = useState('');
// state lokal untuk form
const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
// load data awal
useShallowEffect(() => {
if (!nilaiKonservasiState.findById.data) {
nilaiKonservasiState.findById.initialize();
}
}, []);
// sync state lokal saat data backend siap
useEffect(() => {
if (nilaiKonservasiState.findById.data) {
setJudul(nilaiKonservasiState.findById.data.judul ?? '');
setContent(nilaiKonservasiState.findById.data.deskripsi ?? '');
setFormData({
judul: nilaiKonservasiState.findById.data.judul ?? '',
deskripsi: nilaiKonservasiState.findById.data.deskripsi ?? '',
});
}
}, [nilaiKonservasiState.findById.data]);
const handleChange = (field: 'judul' | 'deskripsi', value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const submit = () => {
if (nilaiKonservasiState.findById.data) {
nilaiKonservasiState.findById.data.judul = judul;
nilaiKonservasiState.findById.data.deskripsi = content;
// update global state saat submit
nilaiKonservasiState.findById.data.judul = formData.judul;
nilaiKonservasiState.findById.data.deskripsi = formData.deskripsi;
nilaiKonservasiState.update.save(nilaiKonservasiState.findById.data);
}
router.push('/admin/lingkungan/konservasi-adat-bali/nilai-konservasi-adat');
@@ -46,12 +56,7 @@ function EditNilaiKonservasiAdat() {
<Box px={{ base: 'sm', md: 'lg' }} py="md">
{/* Header */}
<Group mb="md">
<Button
variant="subtle"
onClick={() => router.back()}
p="xs"
radius="md"
>
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
<IconArrowBack color={colors['blue-button']} size={24} />
</Button>
<Title order={4} ml="sm" c="dark">
@@ -75,8 +80,8 @@ function EditNilaiKonservasiAdat() {
</Text>
<KonservasiAdatBaliTextEditor
showSubmit={false}
onChange={setJudul}
initialContent={judul}
onChange={val => handleChange('judul', val)}
initialContent={formData.judul}
/>
</Box>
@@ -86,8 +91,8 @@ function EditNilaiKonservasiAdat() {
</Text>
<KonservasiAdatBaliTextEditor
showSubmit={false}
onChange={setContent}
initialContent={content}
onChange={val => handleChange('deskripsi', val)}
initialContent={formData.deskripsi}
/>
</Box>

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
import pengelolaanSampahState from '@/app/admin/(dashboard)/_state/lingkungan/pengelolaan-sampah';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
@@ -13,9 +14,9 @@ import { useProxy } from 'valtio/utils';
const LeafletMapEdit = dynamic(() => import('@/app/admin/(dashboard)/_com/leafletMapEdit'), { ssr: false });
function EditKeteranganBankSampahTerdekat() {
const keteranganState = useProxy(pengelolaanSampahState.keteranganSampah)
const keteranganState = useProxy(pengelolaanSampahState.keteranganSampah);
const router = useRouter();
const params = useParams()
const params = useParams();
const [markerPosition, setMarkerPosition] = useState<{ lat: number; lng: number } | null>(null);
const [formData, setFormData] = useState({
@@ -24,10 +25,11 @@ function EditKeteranganBankSampahTerdekat() {
namaTempatMaps: '',
lat: 0,
lng: 0,
})
});
// Load data ketika component mount
useEffect(() => {
const loadKeteranganBankSampahTerdekat = async () => {
const loadKeterangan = async () => {
const id = params?.id as string;
if (!id) return;
@@ -35,14 +37,8 @@ function EditKeteranganBankSampahTerdekat() {
const data = await keteranganState.edit.load(id);
if (data) {
keteranganState.edit.id = id;
keteranganState.edit.form = {
name: data.name,
alamat: data.alamat,
namaTempatMaps: data.namaTempatMaps,
lat: data.lat,
lng: data.lng,
};
// Update local formData dan markerPosition
setFormData({
name: data.name,
alamat: data.alamat,
@@ -50,51 +46,47 @@ function EditKeteranganBankSampahTerdekat() {
lat: data.lat,
lng: data.lng,
});
setMarkerPosition({ lat: data.lat, lng: data.lng });
}
} catch (error) {
console.error("Error loading pengelolaan sampah:", error);
toast.error("Gagal memuat data pengelolaan sampah");
console.error(error);
toast.error('Gagal memuat data pengelolaan sampah');
}
}
};
loadKeteranganBankSampahTerdekat();
loadKeterangan();
}, [params?.id]);
const handleInputChange = (field: keyof typeof formData, value: string | number) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const handleSubmit = async () => {
try {
if (!formData.name.trim()) {
return toast.error('Nama bank sampah harus diisi');
}
if (!formData.alamat.trim()) {
return toast.error('Alamat harus diisi');
}
if (!formData.namaTempatMaps.trim()) {
return toast.error('Nama tempat di Maps harus diisi');
}
if (!markerPosition) {
return toast.error('Silakan pilih lokasi di peta');
}
if (!formData.name.trim()) return toast.error('Nama bank sampah harus diisi');
if (!formData.alamat.trim()) return toast.error('Alamat harus diisi');
if (!formData.namaTempatMaps.trim()) return toast.error('Nama tempat di Maps harus diisi');
if (!markerPosition) return toast.error('Silakan pilih lokasi di peta');
// Update global state hanya saat submit
keteranganState.edit.form = {
...keteranganState.edit.form,
name: formData.name.trim(),
alamat: formData.alamat.trim(),
namaTempatMaps: formData.namaTempatMaps.trim(),
lat: markerPosition.lat,
lng: markerPosition.lng,
};
await keteranganState.edit.update();
toast.success('Data bank sampah berhasil diperbarui');
keteranganState.findUnique.data = null;
router.push("/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat");
} catch (error) {
console.error("Error updating pengelolaan sampah:", error);
console.error(error);
toast.error(error instanceof Error ? error.message : 'Gagal memperbarui data bank sampah');
}
}
};
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
<Group mb="md">
@@ -120,45 +112,35 @@ function EditKeteranganBankSampahTerdekat() {
<TextInput
label="Nama Bank Sampah"
placeholder="Masukkan nama bank sampah"
defaultValue={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
value={formData.name}
onChange={(e) => handleInputChange('name', e.target.value)}
required
/>
<TextInput
label="Alamat"
placeholder="Masukkan alamat lengkap"
defaultValue={formData.alamat}
onChange={(e) => setFormData({ ...formData, alamat: e.target.value })}
value={formData.alamat}
onChange={(e) => handleInputChange('alamat', e.target.value)}
required
/>
<TextInput
label="Nama Tempat di Maps"
placeholder="Masukkan nama tempat yang terdaftar di Google Maps"
defaultValue={formData.namaTempatMaps}
onChange={(e) => setFormData({ ...formData, namaTempatMaps: e.target.value })}
value={formData.namaTempatMaps}
onChange={(e) => handleInputChange('namaTempatMaps', e.target.value)}
required
/>
<Box>
<Text fw="bold" fz="sm" mb={6}>
Pilih Lokasi di Peta
</Text>
<Text fz="xs" c="dimmed" mb={4}>
Klik pada peta untuk menandai lokasi
</Text>
<Text fw="bold" fz="sm" mb={6}>Pilih Lokasi di Peta</Text>
<Text fz="xs" c="dimmed" mb={4}>Klik pada peta untuk menandai lokasi</Text>
<Box style={{ height: 300, width: '100%', borderRadius: '8px', overflow: 'hidden' }}>
<LeafletMapEdit
key={markerPosition?.lat ?? 'default'}
initialPosition={markerPosition || { lat: -8.65, lng: 115.2 }}
onChange={(pos) => {
setMarkerPosition(pos);
setFormData(prev => ({
...prev,
lat: pos.lat,
lng: pos.lng,
}));
handleInputChange('lat', pos.lat);
handleInputChange('lng', pos.lng);
}}
/>
</Box>

View File

@@ -17,15 +17,16 @@ interface FormProgramKreatif {
type IconKey = 'ekowisata' | 'kompetisi' | 'wisata' | 'ekonomi' | 'sampah' | 'truck' | 'scale' | 'clipboard' | 'trash';
function EditProgramKreatifDesa() {
const stateSampah = useProxy(pengelolaanSampahState.pengelolaanSampah)
const params = useParams()
const router = useRouter();
// State lokal untuk form controlled
const [formData, setFormData] = useState<FormProgramKreatif>({
name: '',
icon: '',
})
});
useEffect(() => {
const loadProgramKreatif = async () => {
@@ -35,14 +36,7 @@ function EditProgramKreatifDesa() {
try {
const data = await stateSampah.update.load(id);
if (data) {
// ⬇️ FIX PENTING: tambahkan ini
stateSampah.update.id = id;
stateSampah.update.form = {
name: data.name,
icon: data.icon,
};
stateSampah.update.id = id; // simpan id di global state
setFormData({
name: data.name,
icon: data.icon,
@@ -52,21 +46,19 @@ function EditProgramKreatifDesa() {
console.error("Error loading pengelolaan sampah:", error);
toast.error("Gagal memuat data pengelolaan sampah");
}
}
};
loadProgramKreatif();
}, [params?.id]);
const handleSubmit = async () => {
try {
// Update global state HANYA saat submit
stateSampah.update.form = {
...stateSampah.update.form,
name: formData.name.trim(),
icon: formData.icon.trim(),
};
await stateSampah.update.submit();
toast.success('Data pengelolaan sampah berhasil diperbarui!');
router.push("/admin/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah");
@@ -74,17 +66,13 @@ function EditProgramKreatifDesa() {
console.error("Error updating pengelolaan sampah:", error);
toast.error(error instanceof Error ? error.message : "Gagal memperbarui data pengelolaan sampah");
}
}
};
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
<Group mb="md">
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
<Button
variant="subtle"
onClick={() => router.back()}
p="xs"
radius="md"
>
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
<IconArrowBack color={colors['blue-button']} size={24} />
</Button>
</Tooltip>
@@ -105,15 +93,8 @@ function EditProgramKreatifDesa() {
<TextInput
label="Nama Pengelolaan Sampah"
placeholder="Masukkan nama pengelolaan sampah"
defaultValue={formData.name}
onChange={(e) => {
const value = e.target.value;
setFormData(prev => ({
...prev,
name: value
}));
stateSampah.update.form.name = value;
}}
value={formData.name}
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
required
/>
@@ -123,10 +104,7 @@ function EditProgramKreatifDesa() {
</Text>
<SelectIconProgramEdit
value={formData.icon as IconKey}
onChange={(value) => {
setFormData(prev => ({ ...prev, icon: value }));
stateSampah.update.form.icon = value;
}}
onChange={(value) => setFormData(prev => ({ ...prev, icon: value }))}
/>
</Box>

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
'use client';
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import SelectIconProgramEdit from '@/app/admin/(dashboard)/_com/selectIconEdit';
import programPenghijauanState from '@/app/admin/(dashboard)/_state/lingkungan/program-penghijauan';
@@ -50,13 +51,14 @@ function EditProgramPenghijauan() {
const [formData, setFormData] = useState<FormProgramPenghijauan>({
name: '',
deskripsi: '',
judul: '',
deskripsi: '',
icon: '',
});
// Load data program penghijauan
useEffect(() => {
const loadProgramPenghijauan = async () => {
const loadProgram = async () => {
const id = params?.id as string;
if (!id) return;
@@ -64,14 +66,6 @@ function EditProgramPenghijauan() {
const data = await stateProgramPenghijauan.update.load(id);
if (data) {
stateProgramPenghijauan.update.id = id;
stateProgramPenghijauan.update.form = {
name: data.name,
judul: data.judul,
deskripsi: data.deskripsi,
icon: data.icon,
};
setFormData({
name: data.name,
judul: data.judul,
@@ -85,19 +79,21 @@ function EditProgramPenghijauan() {
}
};
loadProgramPenghijauan();
loadProgram();
}, [params?.id]);
// Hanya update global state saat submit
const handleSubmit = async () => {
try {
stateProgramPenghijauan.update.form = {
...stateProgramPenghijauan.update.form,
name: formData.name.trim(),
deskripsi: formData.deskripsi.trim(),
judul: formData.judul.trim(),
deskripsi: formData.deskripsi.trim(),
icon: formData.icon.trim(),
};
await stateProgramPenghijauan.update.submit();
toast.success('Program penghijauan berhasil diperbarui');
router.push('/admin/lingkungan/program-penghijauan');
} catch (error) {
console.error('Error updating program penghijauan:', error);
@@ -107,7 +103,7 @@ function EditProgramPenghijauan() {
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
{/* Header dengan back button */}
{/* Header */}
<Group mb="md">
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
<Button
@@ -135,27 +131,21 @@ function EditProgramPenghijauan() {
>
<Stack gap="md">
<TextInput
defaultValue={formData.name}
value={formData.name}
label="Nama Program Penghijauan"
placeholder="Masukkan nama program penghijauan"
onChange={(val) =>
setFormData({
...formData,
name: val.target.value,
})
onChange={(e) =>
setFormData((prev) => ({ ...prev, name: e.target.value }))
}
required
/>
<TextInput
defaultValue={formData.judul}
value={formData.judul}
label="Judul Deskripsi Program Penghijauan"
placeholder="Masukkan judul deskripsi program penghijauan"
onChange={(val) =>
setFormData({
...formData,
judul: val.target.value,
})
onChange={(e) =>
setFormData((prev) => ({ ...prev, judul: e.target.value }))
}
required
/>
@@ -166,10 +156,9 @@ function EditProgramPenghijauan() {
</Text>
<EditEditor
value={formData.deskripsi}
onChange={(htmlContent) => {
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
stateProgramPenghijauan.update.form.deskripsi = htmlContent;
}}
onChange={(htmlContent) =>
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
}
/>
</Box>
@@ -179,10 +168,9 @@ function EditProgramPenghijauan() {
</Text>
<SelectIconProgramEdit
value={formData.icon as IconKey}
onChange={(value) => {
setFormData((prev) => ({ ...prev, icon: value }));
stateProgramPenghijauan.update.form.icon = value;
}}
onChange={(value) =>
setFormData((prev) => ({ ...prev, icon: value }))
}
/>
</Box>