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'
import prestasiState from '@/app/admin/(dashboard)/_state/landing-page/prestasi-desa';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, TextInput, Title, Tooltip } from '@mantine/core';
@@ -15,57 +16,51 @@ function EditKategoriPrestasi() {
const id = params?.id as string;
const stateKategori = useProxy(prestasiState.kategoriPrestasi);
const [formData, setFormData] = useState({
name: "",
});
const [formData, setFormData] = useState({ name: '' });
const [loading, setLoading] = useState(false);
// Load data kategori prestasi saat component mount
useEffect(() => {
const loadKategoriprestasi = async () => {
if (!id) return;
if (!id) return;
const loadKategori = async () => {
setLoading(true);
try {
const data = await stateKategori.edit.load(id);
if (data) {
// pastikan id-nya masuk ke state edit
stateKategori.edit.id = id;
setFormData({
name: data.name || '',
});
setFormData({ name: data.name || '' });
}
} catch (error) {
console.error("Error loading kategori prestasi desa:", error);
toast.error("Gagal memuat data kategori prestasi desa");
} catch (err) {
console.error(err);
toast.error('Gagal memuat data kategori prestasi desa');
} finally {
setLoading(false);
}
};
loadKategoriprestasi();
loadKategori();
}, [id]);
// Submit: update global state hanya saat submit
const handleSubmit = async () => {
if (!formData.name.trim()) {
toast.error('Nama kategori prestasi tidak boleh kosong');
return;
}
try {
if (!formData.name.trim()) {
toast.error('Nama kategori prestasi tidak boleh kosong');
return;
}
stateKategori.edit.form = {
name: formData.name.trim(),
};
// Safety check tambahan: pastikan ID tidak kosong
if (!stateKategori.edit.id) {
stateKategori.edit.id = id; // fallback
}
stateKategori.edit.form = { name: formData.name.trim() };
stateKategori.edit.id ||= id; // fallback jika id belum ada
const success = await stateKategori.edit.update();
if (success) {
router.push("/admin/landing-page/prestasi-desa/kategori-prestasi-desa");
toast.success('Kategori prestasi berhasil diperbarui');
router.push('/admin/landing-page/prestasi-desa/kategori-prestasi-desa');
}
} catch (error) {
console.error("Error updating kategori prestasi desa:", error);
// toast akan ditampilkan dari fungsi update
} catch (err) {
console.error(err);
toast.error('Gagal memperbarui kategori prestasi desa');
}
};
@@ -94,9 +89,10 @@ function EditKategoriPrestasi() {
<TextInput
label="Nama Kategori Prestasi"
placeholder="Masukkan nama kategori prestasi"
defaultValue={formData.name}
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required
disabled={loading}
/>
<Group justify="right">
@@ -104,6 +100,7 @@ function EditKategoriPrestasi() {
onClick={handleSubmit}
radius="md"
size="md"
loading={loading}
style={{
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
color: '#fff',

View File

@@ -1,19 +1,17 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
'use client';
import { useProxy } from 'valtio/utils';
import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { toast } from 'react-toastify';
import colors from '@/con/colors';
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import prestasiState from '@/app/admin/(dashboard)/_state/landing-page/prestasi-desa';
import ApiFetch from '@/lib/api-fetch';
import { Dropzone } from '@mantine/dropzone';
import { toast } from 'react-toastify';
interface FormPrestasiDesa {
name: string;
@@ -22,31 +20,31 @@ interface FormPrestasiDesa {
imageId: string;
}
function EditPrestasiDesa() {
const editState = useProxy(prestasiState.prestasiDesa)
const [previewFile, setPreviewFile] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const params = useParams()
const router = useRouter()
export default function EditPrestasiDesa() {
const editState = useProxy(prestasiState.prestasiDesa);
const [formData, setFormData] = useState<FormPrestasiDesa>({
name: '',
deskripsi: '',
kategoriId: '',
imageId: '',
})
});
const [previewFile, setPreviewFile] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const params = useParams();
const router = useRouter();
// Load kategori & prestasi desa
useEffect(() => {
prestasiState.kategoriPrestasi.findMany.load()
const loadDesaAntiKorupsi = async () => {
prestasiState.kategoriPrestasi.findMany.load();
const loadPrestasi = async () => {
const id = params?.id as string;
if (!id) return;
try {
const data = await editState.edit.load(id);
if (data) {
// ⬇️ FIX PENTING: tambahkan ini
editState.edit.id = id;
editState.edit.form = {
name: data.name,
deskripsi: data.deskripsi,
@@ -61,51 +59,37 @@ function EditPrestasiDesa() {
imageId: data.imageId,
});
if (data?.image?.link) {
setPreviewFile(data.image.link)
}
if (data.image?.link) setPreviewFile(data.image.link);
}
} catch (error) {
console.error("Error loading prestasi desa:", error);
toast.error("Gagal memuat data prestasi desa");
console.error('Error loading prestasi desa:', error);
toast.error('Gagal memuat data prestasi desa');
}
}
};
loadDesaAntiKorupsi();
loadPrestasi();
}, [params?.id]);
const handleSubmit = async () => {
try {
// Update global state with form data
editState.edit.form = {
...editState.edit.form,
name: formData.name,
deskripsi: formData.deskripsi,
kategoriId: formData.kategoriId || '',
imageId: formData.imageId // Keep existing imageId if not changed
};
// Jika ada file baru, upload
// Jika ada file baru, upload dulu
let imageId = formData.imageId;
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
if (!uploaded?.id) {
return toast.error("Gagal upload gambar");
}
// Update imageId in global state
editState.edit.form.imageId = uploaded.id;
if (!uploaded?.id) return toast.error('Gagal upload gambar');
imageId = uploaded.id;
}
// Update global state sekaligus
editState.edit.form = { ...formData, imageId };
await editState.edit.update();
toast.success("prestasi desa berhasil diperbarui!");
router.push("/admin/landing-page/prestasi-desa/list-prestasi-desa");
toast.success('Prestasi desa berhasil diperbarui!');
router.push('/admin/landing-page/prestasi-desa/list-prestasi-desa');
} catch (error) {
console.error("Error updating prestasi desa:", error);
toast.error("Terjadi kesalahan saat memperbarui prestasi desa");
console.error('Error updating prestasi desa:', error);
toast.error('Terjadi kesalahan saat memperbarui prestasi desa');
}
};
@@ -117,71 +101,35 @@ function EditPrestasiDesa() {
<IconArrowBack color={colors['blue-button']} size={24} />
</Button>
</Tooltip>
<Title order={4} ml="sm" c="dark">
Edit Prestasi Desa
</Title>
<Title order={4} ml="sm" c="dark">Edit Prestasi Desa</Title>
</Group>
<Paper
w={{ base: '100%', md: '50%' }}
bg={colors['white-1']}
p="lg"
radius="md"
shadow="sm"
style={{ border: '1px solid #e0e0e0' }}
>
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p="lg" radius="md" shadow="sm" style={{ border: '1px solid #e0e0e0' }}>
<Stack gap="md">
<TextInput
label="Judul Prestasi"
placeholder="Masukkan judul prestasi"
defaultValue={formData.name}
onChange={(val) => {
setFormData({
...formData,
name: val.target.value
});
}}
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required
/>
<Box>
<Text fw="bold" fz="sm" mb={6}>
Deskripsi
</Text>
<EditEditor
value={formData.deskripsi}
onChange={(val) => {
setFormData({
...formData,
deskripsi: val
});
}}
/>
<Text fw="bold" fz="sm" mb={6}>Deskripsi</Text>
<EditEditor value={formData.deskripsi} onChange={(val) => setFormData({ ...formData, deskripsi: val })} />
</Box>
<Select
label="Kategori"
placeholder="Pilih kategori"
value={formData.kategoriId}
onChange={(val) => {
setFormData({
...formData,
kategoriId: val ?? ""
});
}}
data={
prestasiState.kategoriPrestasi.findMany.data?.map((v) => ({
value: v.id,
label: v.name,
})) || []
}
onChange={(val) => setFormData({ ...formData, kategoriId: val ?? '' })}
data={prestasiState.kategoriPrestasi.findMany.data?.map((v) => ({ value: v.id, label: v.name })) || []}
required
/>
<Box>
<Text fw="bold" fz="sm" mb={6}>
Gambar Prestasi
</Text>
<Text fw="bold" fz="sm" mb={6}>Gambar Prestasi</Text>
<Dropzone
onDrop={(files) => {
const selectedFile = files[0];
@@ -197,22 +145,12 @@ function EditPrestasiDesa() {
p="xl"
>
<Group justify="center" gap="xl" mih={180}>
<Dropzone.Accept>
<IconUpload size={48} color={colors['blue-button']} stroke={1.5} />
</Dropzone.Accept>
<Dropzone.Reject>
<IconX size={48} color="red" stroke={1.5} />
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size={48} color="#868e96" stroke={1.5} />
</Dropzone.Idle>
<Dropzone.Accept><IconUpload size={48} color={colors['blue-button']} stroke={1.5} /></Dropzone.Accept>
<Dropzone.Reject><IconX size={48} color="red" stroke={1.5} /></Dropzone.Reject>
<Dropzone.Idle><IconPhoto size={48} color="#868e96" stroke={1.5} /></Dropzone.Idle>
<Stack gap="xs" align="center">
<Text size="md" fw={500}>
Seret gambar atau klik untuk memilih file
</Text>
<Text size="sm" c="dimmed">
Maksimal 5MB, format gambar wajib
</Text>
<Text size="md" fw={500}>Seret gambar atau klik untuk memilih file</Text>
<Text size="sm" c="dimmed">Maksimal 5MB, format gambar wajib</Text>
</Stack>
</Group>
</Dropzone>
@@ -224,7 +162,7 @@ function EditPrestasiDesa() {
alt="Preview Gambar"
radius="md"
style={{ maxHeight: 220, objectFit: 'contain', border: `1px solid ${colors['blue-button']}` }}
loading='lazy'
loading="lazy"
/>
</Box>
)}
@@ -249,6 +187,3 @@ function EditPrestasiDesa() {
</Box>
);
}
export default EditPrestasiDesa;