fix inputan edit menu: desa, ekonomi, inovasi, keamanan, kesehatan, landing-page, & lingkungan
This commit is contained in:
@@ -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
|
||||
/>
|
||||
|
||||
|
||||
@@ -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() {
|
||||
<TextInput
|
||||
label="Judul Pengumuman"
|
||||
placeholder="Masukkan judul"
|
||||
defaultValue={formData.judul}
|
||||
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
|
||||
value={formData.judul}
|
||||
onChange={(e) => handleChange("judul", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Deskripsi Singkat"
|
||||
placeholder="Masukkan deskripsi"
|
||||
defaultValue={formData.deskripsi}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, deskripsi: e.target.value })
|
||||
}
|
||||
value={formData.deskripsi}
|
||||
onChange={(e) => handleChange("deskripsi", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<Select
|
||||
value={formData.categoryPengumumanId}
|
||||
onChange={(val) =>
|
||||
setFormData({ ...formData, categoryPengumumanId: val || "" })
|
||||
}
|
||||
onChange={(val) => handleChange("categoryPengumumanId", val || "")}
|
||||
label="Kategori"
|
||||
placeholder="Pilih kategori"
|
||||
data={
|
||||
@@ -150,9 +150,7 @@ function EditPengumuman() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.content}
|
||||
onChange={(htmlContent) =>
|
||||
setFormData({ ...formData, content: htmlContent })
|
||||
}
|
||||
onChange={(htmlContent) => handleChange("content", htmlContent)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -24,9 +24,10 @@ function EditKategoriPotensi() {
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: editState.update.form.nama || '',
|
||||
nama: '',
|
||||
});
|
||||
|
||||
// Load data dari backend -> isi ke formData lokal
|
||||
useEffect(() => {
|
||||
const loadKategori = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -48,8 +49,16 @@ function EditKategoriPotensi() {
|
||||
loadKategori();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange = (field: string, value: string) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// Update global state hanya pas submit
|
||||
editState.update.form = {
|
||||
...editState.update.form,
|
||||
nama: formData.nama,
|
||||
@@ -68,7 +77,12 @@ function EditKategoriPotensi() {
|
||||
<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>
|
||||
@@ -89,12 +103,12 @@ function EditKategoriPotensi() {
|
||||
<TextInput
|
||||
label="Nama Kategori Potensi"
|
||||
placeholder="Masukkan nama kategori potensi"
|
||||
defaultValue={formData.nama}
|
||||
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
|
||||
value={formData.nama}
|
||||
onChange={(e) => handleChange('nama', e.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
|
||||
@@ -40,8 +40,14 @@ function EditPotensi() {
|
||||
imageId: "",
|
||||
});
|
||||
|
||||
// handle input changes
|
||||
const handleChange = (field: string, value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
potensiDesaState.kategoriPotensi.findMany.load();
|
||||
|
||||
const loadPotensi = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
@@ -72,22 +78,27 @@ function EditPotensi() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
potensiState.edit.form = {
|
||||
...potensiState.edit.form,
|
||||
...formData,
|
||||
};
|
||||
let imageId = formData.imageId;
|
||||
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||
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");
|
||||
}
|
||||
|
||||
potensiState.edit.form.imageId = uploaded.id;
|
||||
imageId = uploaded.id;
|
||||
}
|
||||
|
||||
potensiState.edit.form = {
|
||||
...formData,
|
||||
imageId,
|
||||
};
|
||||
|
||||
await potensiState.edit.update();
|
||||
toast.success("Potensi berhasil diperbarui!");
|
||||
router.push("/admin/desa/potensi/list-potensi");
|
||||
@@ -101,7 +112,12 @@ function EditPotensi() {
|
||||
<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>
|
||||
@@ -122,22 +138,22 @@ function EditPotensi() {
|
||||
<TextInput
|
||||
label="Judul Potensi"
|
||||
placeholder="Masukkan judul"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) => handleChange("name", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Deskripsi Singkat"
|
||||
placeholder="Masukkan deskripsi"
|
||||
defaultValue={formData.deskripsi}
|
||||
onChange={(e) => setFormData({ ...formData, deskripsi: e.target.value })}
|
||||
value={formData.deskripsi}
|
||||
onChange={(e) => handleChange("deskripsi", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<Select
|
||||
value={formData.kategoriId}
|
||||
onChange={(val) => 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() {
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={180}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={48} color={colors["blue-button"]} stroke={1.5} />
|
||||
<IconUpload
|
||||
size={48}
|
||||
color={colors["blue-button"]}
|
||||
stroke={1.5}
|
||||
/>
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={48} color="red" stroke={1.5} />
|
||||
@@ -214,7 +236,9 @@ function EditPotensi() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.content}
|
||||
onChange={(htmlContent) => setFormData({ ...formData, content: htmlContent })}
|
||||
onChange={(htmlContent) =>
|
||||
handleChange("content", htmlContent)
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -26,15 +26,17 @@ function EditPerbekelDariMasaKeMasa() {
|
||||
const state = useProxy(stateProfileDesa.mantanPerbekel);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(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() {
|
||||
<TextInput
|
||||
label="Nama"
|
||||
placeholder="Masukkan nama"
|
||||
defaultValue={formData.nama}
|
||||
onChange={(e) => 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"
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
@@ -170,16 +183,16 @@ function EditPerbekelDariMasaKeMasa() {
|
||||
<TextInput
|
||||
label="Daerah"
|
||||
placeholder="Masukkan daerah"
|
||||
defaultValue={formData.daerah}
|
||||
onChange={(e) => setFormData({ ...formData, daerah: e.target.value })}
|
||||
value={formData.daerah}
|
||||
onChange={(e) => handleChange('daerah', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Periode"
|
||||
placeholder="Masukkan periode"
|
||||
defaultValue={formData.periode}
|
||||
onChange={(e) => setFormData({ ...formData, periode: e.target.value })}
|
||||
value={formData.periode}
|
||||
onChange={(e) => handleChange('periode', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
@@ -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 */}
|
||||
<TextInput
|
||||
type="number"
|
||||
defaultValue={formData.tahun}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, tahun: e.target.value })
|
||||
}
|
||||
value={formData.tahun}
|
||||
onChange={(e) => handleChange("tahun", e.target.value)}
|
||||
label={<Text fz="sm" fw="bold">Tahun</Text>}
|
||||
placeholder="Masukkan tahun anggaran"
|
||||
required
|
||||
@@ -123,23 +126,17 @@ function EditAPBDesa() {
|
||||
{/* Selects */}
|
||||
<SelectPendapatan
|
||||
selectedIds={formData.pendapatanIds}
|
||||
onSelectionChange={(ids) =>
|
||||
setFormData({ ...formData, pendapatanIds: ids })
|
||||
}
|
||||
onSelectionChange={(ids) => handleChange("pendapatanIds", ids)}
|
||||
/>
|
||||
|
||||
<SelectBelanja
|
||||
selectedIds={formData.belanjaIds}
|
||||
onSelectionChange={(ids) =>
|
||||
setFormData({ ...formData, belanjaIds: ids })
|
||||
}
|
||||
onSelectionChange={(ids) => handleChange("belanjaIds", ids)}
|
||||
/>
|
||||
|
||||
<SelectPembiayaan
|
||||
selectedIds={formData.pembiayaanIds}
|
||||
onSelectionChange={(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(() => {
|
||||
|
||||
@@ -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 (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
@@ -112,19 +118,21 @@ function EditBelanja() {
|
||||
<TextInput
|
||||
label="Nama Jenis Belanja"
|
||||
placeholder="Masukkan nama jenis belanja"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, name: e.target.value })
|
||||
}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Nilai"
|
||||
placeholder="Masukkan nilai"
|
||||
defaultValue={formatRupiah(formData.value)}
|
||||
value={formData.value ? formatRupiah(formData.value) : ''}
|
||||
onChange={(e) => {
|
||||
const raw = e.currentTarget.value;
|
||||
const cleanValue = unformatRupiah(raw);
|
||||
setFormData({ ...formData, value: cleanValue });
|
||||
setFormData({ ...formData, value: String(cleanValue) });
|
||||
}}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -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 (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header dengan Back Button */}
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button
|
||||
@@ -112,19 +115,21 @@ function EditPembiayaan() {
|
||||
<TextInput
|
||||
label="Nama Jenis Pembiayaan"
|
||||
placeholder="Masukkan nama jenis pembiayaan"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) =>
|
||||
setFormData((prev) => ({ ...prev, name: e.target.value }))
|
||||
}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Nilai"
|
||||
placeholder="Masukkan nilai"
|
||||
defaultValue={formatRupiah(formData.value)}
|
||||
value={formData.value ? formatRupiah(formData.value) : ''}
|
||||
onChange={(e) => {
|
||||
const raw = e.currentTarget.value;
|
||||
const cleanValue = unformatRupiah(raw);
|
||||
setFormData({ ...formData, value: cleanValue });
|
||||
setFormData((prev) => ({ ...prev, value: String(cleanValue) }));
|
||||
}}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -24,12 +24,16 @@ function EditPendapatan() {
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: pendapatanState.update.form.name || '',
|
||||
value: pendapatanState.update.form.value || '',
|
||||
name: '',
|
||||
value: '',
|
||||
});
|
||||
|
||||
// helper format
|
||||
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, ''));
|
||||
|
||||
return new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
@@ -39,6 +43,7 @@ function EditPendapatan() {
|
||||
|
||||
const unformatRupiah = (value: string) => Number(value.replace(/\D/g, ''));
|
||||
|
||||
// load data once
|
||||
useEffect(() => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
@@ -48,8 +53,8 @@ function EditPendapatan() {
|
||||
const data = await pendapatanState.update.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
value: data.value || '',
|
||||
name: data.name ?? '',
|
||||
value: data.value?.toString() ?? '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -61,6 +66,13 @@ function EditPendapatan() {
|
||||
loadPendapatan();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange = (field: string, value: string) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
pendapatanState.update.form = {
|
||||
@@ -110,19 +122,19 @@ function EditPendapatan() {
|
||||
<TextInput
|
||||
label="Nama Jenis Pendapatan"
|
||||
placeholder="Masukkan nama jenis pendapatan"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) => handleChange('name', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Nilai"
|
||||
placeholder="Masukkan nilai"
|
||||
defaultValue={formatRupiah(formData.value)}
|
||||
value={formData.value ? formatRupiah(formData.value) : ''}
|
||||
onChange={(e) => {
|
||||
const raw = e.currentTarget.value;
|
||||
const cleanValue = unformatRupiah(raw);
|
||||
setFormData({ ...formData, value: cleanValue });
|
||||
const cleanValue = unformatRupiah(raw).toString();
|
||||
handleChange('value', cleanValue);
|
||||
}}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -10,15 +10,21 @@ import {
|
||||
Stack,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip
|
||||
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 { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import demografiPekerjaan from '../../../_state/ekonomi/demografi-pekerjaan';
|
||||
|
||||
interface FormData {
|
||||
pekerjaan: string;
|
||||
lakiLaki: number;
|
||||
perempuan: number;
|
||||
}
|
||||
|
||||
function EditDemografiPekerjaan() {
|
||||
const router = useRouter();
|
||||
const params = useParams() as { id: string };
|
||||
@@ -26,19 +32,27 @@ function EditDemografiPekerjaan() {
|
||||
|
||||
const id = params.id;
|
||||
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
pekerjaan: '',
|
||||
lakiLaki: 0,
|
||||
perempuan: 0,
|
||||
});
|
||||
|
||||
// Load data sekali waktu
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
stateDemografi.update.id = id;
|
||||
|
||||
stateDemografi.findUnique
|
||||
.load(id)
|
||||
.then(() => {
|
||||
const data = stateDemografi.findUnique.data;
|
||||
if (data) {
|
||||
stateDemografi.update.form = {
|
||||
setFormData({
|
||||
pekerjaan: String(data.pekerjaan || ''),
|
||||
lakiLaki: Number(data.lakiLaki || 0),
|
||||
perempuan: Number(data.perempuan || 0),
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -47,9 +61,22 @@ function EditDemografiPekerjaan() {
|
||||
});
|
||||
}, [id]);
|
||||
|
||||
const handleChange =
|
||||
(field: keyof FormData) =>
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]:
|
||||
field === 'lakiLaki' || field === 'perempuan'
|
||||
? Number(e.currentTarget.value)
|
||||
: e.currentTarget.value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
stateDemografi.update.id = id;
|
||||
stateDemografi.update.form = { ...formData };
|
||||
await stateDemografi.update.submit();
|
||||
toast.success('Data berhasil diperbarui');
|
||||
router.push('/admin/ekonomi/demografi-pekerjaan');
|
||||
@@ -91,10 +118,8 @@ function EditDemografiPekerjaan() {
|
||||
<TextInput
|
||||
label="Pekerjaan"
|
||||
placeholder="Masukkan jenis pekerjaan"
|
||||
defaultValue={stateDemografi.update.form.pekerjaan}
|
||||
onChange={(e) =>
|
||||
(stateDemografi.update.form.pekerjaan = e.currentTarget.value)
|
||||
}
|
||||
value={formData.pekerjaan}
|
||||
onChange={handleChange('pekerjaan')}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -102,12 +127,8 @@ function EditDemografiPekerjaan() {
|
||||
label="Jumlah Pekerja Laki-laki"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah pekerja laki-laki"
|
||||
defaultValue={stateDemografi.update.form.lakiLaki}
|
||||
onChange={(e) =>
|
||||
(stateDemografi.update.form.lakiLaki = Number(
|
||||
e.currentTarget.value
|
||||
))
|
||||
}
|
||||
value={formData.lakiLaki}
|
||||
onChange={handleChange('lakiLaki')}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -115,12 +136,8 @@ function EditDemografiPekerjaan() {
|
||||
label="Jumlah Pekerja Perempuan"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah pekerja perempuan"
|
||||
defaultValue={stateDemografi.update.form.perempuan}
|
||||
onChange={(e) =>
|
||||
(stateDemografi.update.form.perempuan = Number(
|
||||
e.currentTarget.value
|
||||
))
|
||||
}
|
||||
value={formData.perempuan}
|
||||
onChange={handleChange('perempuan')}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
@@ -3,10 +3,19 @@
|
||||
|
||||
import jumlahPendudukMiskin from '@/app/admin/(dashboard)/_state/ekonomi/jumlah-penduduk-miskin';
|
||||
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';
|
||||
|
||||
@@ -17,6 +26,13 @@ function EditJumlahPendudukMiskin() {
|
||||
|
||||
const id = params.id;
|
||||
|
||||
// 🔹 State lokal untuk form
|
||||
const [formData, setFormData] = useState({
|
||||
year: 0,
|
||||
totalPoorPopulation: 0,
|
||||
});
|
||||
|
||||
// 🔹 Load data awal dari backend
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
@@ -25,10 +41,10 @@ function EditJumlahPendudukMiskin() {
|
||||
await stateJPM.findUnique.load(id);
|
||||
const data = stateJPM.findUnique.data;
|
||||
if (data) {
|
||||
stateJPM.update.form = {
|
||||
setFormData({
|
||||
year: data.year || 0,
|
||||
totalPoorPopulation: data.totalPoorPopulation || 0,
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Gagal memuat data:', error);
|
||||
@@ -39,9 +55,21 @@ function EditJumlahPendudukMiskin() {
|
||||
loadData();
|
||||
}, [id]);
|
||||
|
||||
// 🔹 Handler input controlled
|
||||
const handleChange = (field: keyof typeof formData, value: string) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]: Number(value),
|
||||
}));
|
||||
};
|
||||
|
||||
// 🔹 Submit form
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
stateJPM.update.id = id;
|
||||
// update global state cuma saat submit
|
||||
stateJPM.update.form = { ...formData };
|
||||
|
||||
await stateJPM.update.submit();
|
||||
toast.success('Data jumlah penduduk miskin berhasil diperbarui!');
|
||||
router.push('/admin/ekonomi/jumlah-penduduk-miskin');
|
||||
@@ -55,7 +83,12 @@ function EditJumlahPendudukMiskin() {
|
||||
<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>
|
||||
@@ -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)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
@@ -89,10 +120,10 @@ function EditJumlahPendudukMiskin() {
|
||||
placeholder="Masukkan jumlah penduduk miskin"
|
||||
type="number"
|
||||
required
|
||||
defaultValue={stateJPM.update.form.totalPoorPopulation}
|
||||
onChange={(val) => {
|
||||
stateJPM.update.form.totalPoorPopulation = Number(val.currentTarget.value);
|
||||
}}
|
||||
value={formData.totalPoorPopulation}
|
||||
onChange={(e) =>
|
||||
handleChange('totalPoorPopulation', e.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
|
||||
@@ -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<HTMLInputElement>) => {
|
||||
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 (
|
||||
<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>
|
||||
@@ -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')}
|
||||
/>
|
||||
<TextInput
|
||||
label="SMP"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.update.form.SMP}
|
||||
onChange={(val) => (stategrafik.update.form.SMP = val.currentTarget.value)}
|
||||
value={formData.SMP}
|
||||
onChange={handleChange('SMP')}
|
||||
/>
|
||||
<TextInput
|
||||
label="SMA"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.update.form.SMA}
|
||||
onChange={(val) => (stategrafik.update.form.SMA = val.currentTarget.value)}
|
||||
value={formData.SMA}
|
||||
onChange={handleChange('SMA')}
|
||||
/>
|
||||
<TextInput
|
||||
label="D3"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.update.form.D3}
|
||||
onChange={(val) => (stategrafik.update.form.D3 = val.currentTarget.value)}
|
||||
value={formData.D3}
|
||||
onChange={handleChange('D3')}
|
||||
/>
|
||||
<TextInput
|
||||
label="S1"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.update.form.S1}
|
||||
onChange={(val) => (stategrafik.update.form.S1 = val.currentTarget.value)}
|
||||
value={formData.S1}
|
||||
onChange={handleChange('S1')}
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
|
||||
@@ -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() {
|
||||
<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>
|
||||
@@ -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
|
||||
/>
|
||||
<TextInput
|
||||
label="Usia 26 - 35"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.update.form.usia26_35}
|
||||
onChange={(val) => {
|
||||
stategrafik.update.form.usia26_35 = val.currentTarget.value;
|
||||
}}
|
||||
value={formData.usia26_35}
|
||||
onChange={(e) => handleChange('usia26_35', e.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Usia 36 - 45"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.update.form.usia36_45}
|
||||
onChange={(val) => {
|
||||
stategrafik.update.form.usia36_45 = val.currentTarget.value;
|
||||
}}
|
||||
value={formData.usia36_45}
|
||||
onChange={(e) => handleChange('usia36_45', e.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Usia 46 +"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.update.form.usia46_keatas}
|
||||
onChange={(val) => {
|
||||
stategrafik.update.form.usia46_keatas = val.currentTarget.value;
|
||||
}}
|
||||
value={formData.usia46_keatas}
|
||||
onChange={(e) => handleChange('usia46_keatas', e.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
@@ -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<typeof formData>) => {
|
||||
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 (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<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">
|
||||
@@ -129,36 +156,57 @@ function EditDetailDataPengangguran() {
|
||||
</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">
|
||||
<Select
|
||||
label="Bulan"
|
||||
data={['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des']}
|
||||
data={[
|
||||
'Jan','Feb','Mar','Apr','Mei','Jun',
|
||||
'Jul','Agu','Sep','Okt','Nov','Des',
|
||||
]}
|
||||
value={formData.month}
|
||||
onChange={(val) => updateFormData({ month: val || '' })}
|
||||
/>
|
||||
<NumberInput
|
||||
label="Tahun"
|
||||
defaultValue={formData.year}
|
||||
value={formData.year}
|
||||
onChange={(val) => updateFormData({ year: Number(val) })}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Pengangguran Terdidik"
|
||||
type="number"
|
||||
defaultValue={formData.educatedUnemployment}
|
||||
onChange={(val) => updateFormData({ educatedUnemployment: Number(val.currentTarget.value) || 0 })}
|
||||
value={formData.educatedUnemployment}
|
||||
onChange={(val) =>
|
||||
updateFormData({ educatedUnemployment: Number(val.currentTarget.value) || 0 })
|
||||
}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Pengangguran Tidak Terdidik"
|
||||
type="number"
|
||||
defaultValue={formData.uneducatedUnemployment}
|
||||
onChange={(val) => updateFormData({ uneducatedUnemployment: Number(val.currentTarget.value) || 0 })}
|
||||
value={formData.uneducatedUnemployment}
|
||||
onChange={(val) =>
|
||||
updateFormData({ uneducatedUnemployment: Number(val.currentTarget.value) || 0 })
|
||||
}
|
||||
required
|
||||
/>
|
||||
<Text fz="sm" fw={500}>Total Otomatis: {formData.totalUnemployment}</Text>
|
||||
<Text fz="sm" fw={500}>Perubahan Otomatis: {formData.percentageChange !== null ? `${formData.percentageChange}%` : '-'}</Text>
|
||||
<Text fz="sm" fw={500}>
|
||||
Total Otomatis: {formData.totalUnemployment}
|
||||
</Text>
|
||||
<Text fz="sm" fw={500}>
|
||||
Perubahan Otomatis:{' '}
|
||||
{formData.percentageChange !== null
|
||||
? `${formData.percentageChange}%`
|
||||
: '-'}
|
||||
</Text>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
@@ -181,4 +229,3 @@ function EditDetailDataPengangguran() {
|
||||
}
|
||||
|
||||
export default EditDetailDataPengangguran;
|
||||
|
||||
|
||||
@@ -26,15 +26,16 @@ function EditLowonganKerja() {
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
posisi: lowonganKerjaState.update.form.posisi,
|
||||
namaPerusahaan: lowonganKerjaState.update.form.namaPerusahaan,
|
||||
lokasi: lowonganKerjaState.update.form.lokasi,
|
||||
tipePekerjaan: lowonganKerjaState.update.form.tipePekerjaan,
|
||||
gaji: lowonganKerjaState.update.form.gaji,
|
||||
deskripsi: lowonganKerjaState.update.form.deskripsi,
|
||||
kualifikasi: lowonganKerjaState.update.form.kualifikasi,
|
||||
posisi: '',
|
||||
namaPerusahaan: '',
|
||||
lokasi: '',
|
||||
tipePekerjaan: '',
|
||||
gaji: '',
|
||||
deskripsi: '',
|
||||
kualifikasi: '',
|
||||
});
|
||||
|
||||
// load data sekali aja ketika mount / id berubah
|
||||
useEffect(() => {
|
||||
const loadLowongan = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -62,14 +63,17 @@ function EditLowonganKerja() {
|
||||
loadLowongan();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange = (field: string, value: string) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
lowonganState.update.id = params?.id as string;
|
||||
|
||||
lowonganState.update.form = {
|
||||
...lowonganState.update.form,
|
||||
...formData,
|
||||
};
|
||||
lowonganState.update.form = { ...formData };
|
||||
|
||||
await lowonganState.update.update();
|
||||
toast.success("Lowongan kerja berhasil diperbarui!");
|
||||
@@ -107,40 +111,40 @@ function EditLowonganKerja() {
|
||||
<TextInput
|
||||
label="Posisi"
|
||||
placeholder="Masukkan posisi"
|
||||
defaultValue={formData.posisi}
|
||||
onChange={(e) => setFormData({ ...formData, posisi: e.target.value })}
|
||||
value={formData.posisi}
|
||||
onChange={(e) => handleChange("posisi", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Nama Perusahaan"
|
||||
placeholder="Masukkan nama perusahaan"
|
||||
defaultValue={formData.namaPerusahaan}
|
||||
onChange={(e) => setFormData({ ...formData, namaPerusahaan: e.target.value })}
|
||||
value={formData.namaPerusahaan}
|
||||
onChange={(e) => handleChange("namaPerusahaan", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Lokasi"
|
||||
placeholder="Masukkan lokasi"
|
||||
defaultValue={formData.lokasi}
|
||||
onChange={(e) => setFormData({ ...formData, lokasi: e.target.value })}
|
||||
value={formData.lokasi}
|
||||
onChange={(e) => handleChange("lokasi", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Tipe Pekerjaan"
|
||||
placeholder="Masukkan tipe pekerjaan"
|
||||
defaultValue={formData.tipePekerjaan}
|
||||
onChange={(e) => setFormData({ ...formData, tipePekerjaan: e.target.value })}
|
||||
value={formData.tipePekerjaan}
|
||||
onChange={(e) => handleChange("tipePekerjaan", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Gaji (per bulan)"
|
||||
placeholder="Masukkan gaji"
|
||||
defaultValue={formData.gaji}
|
||||
onChange={(e) => setFormData({ ...formData, gaji: e.target.value })}
|
||||
value={formData.gaji}
|
||||
onChange={(e) => handleChange("gaji", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -150,7 +154,7 @@ function EditLowonganKerja() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => setFormData({ ...formData, deskripsi: val })}
|
||||
onChange={(val) => handleChange("deskripsi", val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -160,7 +164,7 @@ function EditLowonganKerja() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.kualifikasi}
|
||||
onChange={(val) => setFormData({ ...formData, kualifikasi: val })}
|
||||
onChange={(val) => handleChange("kualifikasi", val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -25,9 +25,8 @@ function EditKategoriProduk() {
|
||||
const id = params?.id as string;
|
||||
const statePasar = useProxy(pasarDesaState.kategoriProduk);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: '',
|
||||
});
|
||||
const [formData, setFormData] = useState({ nama: '' });
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const loadKategoriProduk = async () => {
|
||||
@@ -37,21 +36,30 @@ function EditKategoriProduk() {
|
||||
const data = await statePasar.edit.load(id);
|
||||
|
||||
if (data) {
|
||||
// pastikan id-nya masuk ke state edit
|
||||
// simpan id ke state global hanya untuk referensi
|
||||
statePasar.edit.id = id;
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
});
|
||||
|
||||
// simpan data ke state lokal
|
||||
setFormData({ nama: data.nama || '' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading kategori produk:', error);
|
||||
toast.error('Gagal memuat data kategori produk');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadKategoriProduk();
|
||||
}, [id]);
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[e.target.name]: e.target.value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
if (!formData.nama.trim()) {
|
||||
@@ -59,10 +67,8 @@ function EditKategoriProduk() {
|
||||
return;
|
||||
}
|
||||
|
||||
statePasar.edit.form = {
|
||||
nama: formData.nama.trim(),
|
||||
};
|
||||
|
||||
// update global state hanya saat submit
|
||||
statePasar.edit.form = { nama: formData.nama.trim() };
|
||||
if (!statePasar.edit.id) {
|
||||
statePasar.edit.id = id; // fallback
|
||||
}
|
||||
@@ -79,12 +85,21 @@ function EditKategoriProduk() {
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <Text>Loading...</Text>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header dengan tombol back */}
|
||||
<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>
|
||||
@@ -104,10 +119,11 @@ function EditKategoriProduk() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
name="nama"
|
||||
label={<Text fw="bold" fz="sm">Nama Kategori Produk</Text>}
|
||||
placeholder="Masukkan nama kategori produk"
|
||||
defaultValue={formData.nama}
|
||||
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
|
||||
value={formData.nama}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client';
|
||||
import pasarDesaState from '@/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa';
|
||||
import colors from '@/con/colors';
|
||||
@@ -24,6 +24,15 @@ import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
type FormData = {
|
||||
nama: string;
|
||||
harga: number;
|
||||
alamatUsaha: string;
|
||||
imageId: string;
|
||||
rating: number;
|
||||
kategoriId: string[];
|
||||
};
|
||||
|
||||
function EditPasarDesa() {
|
||||
const pasarState = useProxy(pasarDesaState);
|
||||
const router = useRouter();
|
||||
@@ -31,17 +40,19 @@ function EditPasarDesa() {
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
nama: pasarState.pasarDesa.edit.form.nama || '',
|
||||
harga: pasarState.pasarDesa.edit.form.harga || 0,
|
||||
alamatUsaha: pasarState.pasarDesa.edit.form.alamatUsaha || '',
|
||||
imageId: pasarState.pasarDesa.edit.form.imageId || '',
|
||||
rating: pasarState.pasarDesa.edit.form.rating || 0,
|
||||
kategoriId: pasarState.pasarDesa.edit.form.kategoriId || [],
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
nama: '',
|
||||
harga: 0,
|
||||
alamatUsaha: '',
|
||||
imageId: '',
|
||||
rating: 0,
|
||||
kategoriId: [],
|
||||
});
|
||||
|
||||
// load data awal
|
||||
useEffect(() => {
|
||||
pasarState.kategoriProduk.findMany.load();
|
||||
|
||||
const loadPasarDesa = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
@@ -70,19 +81,27 @@ function EditPasarDesa() {
|
||||
loadPasarDesa();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange = (key: keyof FormData, value: any) => {
|
||||
setFormData((prev) => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
pasarState.pasarDesa.edit.form = { ...pasarState.pasarDesa.edit.form, ...formData };
|
||||
|
||||
// upload image kalau ada file baru
|
||||
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');
|
||||
|
||||
pasarState.pasarDesa.edit.form.imageId = uploaded.id;
|
||||
imageId = uploaded.id;
|
||||
}
|
||||
|
||||
// update global state hanya saat submit
|
||||
pasarState.pasarDesa.edit.form = {
|
||||
...formData,
|
||||
imageId,
|
||||
};
|
||||
|
||||
await pasarState.pasarDesa.edit.update();
|
||||
toast.success('Pasar desa berhasil diperbarui!');
|
||||
router.push('/admin/ekonomi/pasar-desa/produk-pasar-desa');
|
||||
@@ -114,6 +133,7 @@ function EditPasarDesa() {
|
||||
style={{ border: '1px solid #e0e0e0' }}
|
||||
>
|
||||
<Stack gap="md">
|
||||
{/* Dropzone upload */}
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Gambar Produk
|
||||
@@ -170,11 +190,12 @@ function EditPasarDesa() {
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Controlled Inputs */}
|
||||
<TextInput
|
||||
label="Nama Produk"
|
||||
placeholder="Masukkan nama produk"
|
||||
defaultValue={formData.nama}
|
||||
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
|
||||
value={formData.nama}
|
||||
onChange={(e) => handleChange('nama', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -182,8 +203,8 @@ function EditPasarDesa() {
|
||||
type="number"
|
||||
label="Harga Produk"
|
||||
placeholder="Masukkan harga produk"
|
||||
defaultValue={formData.harga}
|
||||
onChange={(e) => setFormData({ ...formData, harga: Number(e.target.value) })}
|
||||
value={formData.harga}
|
||||
onChange={(e) => handleChange('harga', Number(e.target.value))}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -194,16 +215,16 @@ function EditPasarDesa() {
|
||||
step={0.1}
|
||||
label="Rating Produk"
|
||||
placeholder="Masukkan rating produk (0-5)"
|
||||
defaultValue={formData.rating}
|
||||
onChange={(e) => setFormData({ ...formData, rating: Number(e.target.value) })}
|
||||
value={formData.rating}
|
||||
onChange={(e) => handleChange('rating', Number(e.target.value))}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Alamat Usaha"
|
||||
placeholder="Masukkan alamat usaha"
|
||||
defaultValue={formData.alamatUsaha}
|
||||
onChange={(e) => setFormData({ ...formData, alamatUsaha: e.target.value })}
|
||||
value={formData.alamatUsaha}
|
||||
onChange={(e) => handleChange('alamatUsaha', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -211,7 +232,7 @@ function EditPasarDesa() {
|
||||
label="Kategori Produk"
|
||||
placeholder="Pilih kategori produk"
|
||||
value={formData.kategoriId}
|
||||
onChange={(val) => setFormData({ ...formData, kategoriId: val })}
|
||||
onChange={(val) => handleChange('kategoriId', val)}
|
||||
data={
|
||||
pasarState.kategoriProduk.findMany.data?.map((v) => ({
|
||||
value: v.id,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
@@ -19,53 +18,95 @@ import {
|
||||
} 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';
|
||||
|
||||
type FormData = {
|
||||
nama: string;
|
||||
deskripsi: string;
|
||||
icon: string;
|
||||
statistik: {
|
||||
tahun: string;
|
||||
jumlah: string;
|
||||
};
|
||||
};
|
||||
|
||||
function EditProgramKemiskinan() {
|
||||
const router = useRouter();
|
||||
const params = useParams() as { id: string };
|
||||
const stateProgram = useProxy(programKemiskinanState);
|
||||
const id = params.id;
|
||||
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
nama: '',
|
||||
deskripsi: '',
|
||||
icon: '',
|
||||
statistik: {
|
||||
tahun: '',
|
||||
jumlah: '',
|
||||
},
|
||||
});
|
||||
|
||||
// load data ke local state sekali aja
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
stateProgram.findUnique.load(id).then(() => {
|
||||
const data = stateProgram.findUnique.data;
|
||||
if (data) {
|
||||
stateProgram.update.form = {
|
||||
nama: data.nama || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
icon: data.icon || '',
|
||||
statistik: {
|
||||
tahun: data.statistik?.tahun?.toString() || '',
|
||||
jumlah: data.statistik?.jumlah?.toString() || '',
|
||||
},
|
||||
};
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error("Error load data:", err);
|
||||
toast.error("Gagal mengambil data program");
|
||||
});
|
||||
stateProgram.findUnique
|
||||
.load(id)
|
||||
.then(() => {
|
||||
const data = stateProgram.findUnique.data;
|
||||
if (data) {
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
icon: data.icon || '',
|
||||
statistik: {
|
||||
tahun: data.statistik?.tahun?.toString() || '',
|
||||
jumlah: data.statistik?.jumlah?.toString() || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Error load data:', err);
|
||||
toast.error('Gagal mengambil data program');
|
||||
});
|
||||
}
|
||||
}, [id]);
|
||||
}, [id, stateProgram.findUnique]);
|
||||
|
||||
const handleChange = (field: keyof FormData, value: string) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleStatistikChange = (field: keyof FormData['statistik'], value: string) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
statistik: {
|
||||
...prev.statistik,
|
||||
[field]: value,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
stateProgram.update.id = id;
|
||||
stateProgram.update.form = formData;
|
||||
await stateProgram.update.update();
|
||||
toast.success("Program berhasil diperbarui!");
|
||||
toast.success('Program berhasil diperbarui!');
|
||||
router.push('/admin/ekonomi/program-kemiskinan');
|
||||
} catch (error) {
|
||||
console.error("Error update program:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui program");
|
||||
console.error('Error update program:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui program');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header dengan tombol kembali */}
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button
|
||||
@@ -92,10 +133,8 @@ function EditProgramKemiskinan() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={stateProgram.update.form.nama}
|
||||
onChange={(e) => {
|
||||
stateProgram.update.form.nama = e.target.value;
|
||||
}}
|
||||
value={formData.nama}
|
||||
onChange={(e) => handleChange('nama', e.target.value)}
|
||||
label={<Text fw="bold" fz="sm">Judul Program</Text>}
|
||||
placeholder="Masukkan judul program"
|
||||
required
|
||||
@@ -106,10 +145,8 @@ function EditProgramKemiskinan() {
|
||||
Deskripsi
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={stateProgram.update.form.deskripsi}
|
||||
onChange={(val) => {
|
||||
stateProgram.update.form.deskripsi = val;
|
||||
}}
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => handleChange('deskripsi', val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -118,10 +155,8 @@ function EditProgramKemiskinan() {
|
||||
Ikon Program Kreatif Desa
|
||||
</Text>
|
||||
<SelectIconProgramEdit
|
||||
value={stateProgram.update.form.icon as IconKey}
|
||||
onChange={(value) => {
|
||||
stateProgram.update.form.icon = value;
|
||||
}}
|
||||
value={formData.icon as IconKey}
|
||||
onChange={(val) => handleChange('icon', val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -131,10 +166,8 @@ function EditProgramKemiskinan() {
|
||||
</Text>
|
||||
<TextInput
|
||||
type="number"
|
||||
defaultValue={stateProgram.update.form.statistik.jumlah}
|
||||
onChange={(e) => {
|
||||
stateProgram.update.form.statistik.jumlah = e.target.value;
|
||||
}}
|
||||
value={formData.statistik.jumlah}
|
||||
onChange={(e) => handleStatistikChange('jumlah', e.target.value)}
|
||||
label="Jumlah Masyarakat Miskin"
|
||||
placeholder="Masukkan jumlah masyarakat miskin"
|
||||
required
|
||||
@@ -142,10 +175,8 @@ function EditProgramKemiskinan() {
|
||||
|
||||
<TextInput
|
||||
type="number"
|
||||
defaultValue={stateProgram.update.form.statistik.tahun}
|
||||
onChange={(e) => {
|
||||
stateProgram.update.form.statistik.tahun = e.target.value;
|
||||
}}
|
||||
value={formData.statistik.tahun}
|
||||
onChange={(e) => handleStatistikChange('tahun', e.target.value)}
|
||||
label="Tahun"
|
||||
placeholder="Masukkan tahun"
|
||||
required
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} 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';
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
@@ -28,28 +28,46 @@ function EditSektorUnggulanDesa() {
|
||||
|
||||
const id = params.id;
|
||||
|
||||
// state lokal buat form
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
description: '',
|
||||
value: 0,
|
||||
});
|
||||
|
||||
// Load data saat komponen mount
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
stateGrafik.findUnique.load(id).then(() => {
|
||||
const data = stateGrafik.findUnique.data;
|
||||
if (data) {
|
||||
stateGrafik.update.form = {
|
||||
name: data.name || '',
|
||||
description: data.description || '',
|
||||
value: data.value || 0,
|
||||
};
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error('Error load sektor unggulan:', err);
|
||||
toast.error('Gagal mengambil data sektor unggulan');
|
||||
});
|
||||
stateGrafik.findUnique
|
||||
.load(id)
|
||||
.then(() => {
|
||||
const data = stateGrafik.findUnique.data;
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
description: data.description || '',
|
||||
value: data.value || 0,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Error load sektor unggulan:', err);
|
||||
toast.error('Gagal mengambil data sektor unggulan');
|
||||
});
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const handleChange =
|
||||
(field: keyof typeof formData) =>
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = field === 'value' ? Number(e.currentTarget.value) : e.currentTarget.value;
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
stateGrafik.update.id = id;
|
||||
stateGrafik.update.form = { ...formData }; // update global pas submit
|
||||
await stateGrafik.update.submit();
|
||||
toast.success('Sektor unggulan berhasil diperbarui!');
|
||||
router.push('/admin/ekonomi/sektor-unggulan-desa');
|
||||
@@ -89,10 +107,8 @@ function EditSektorUnggulanDesa() {
|
||||
<TextInput
|
||||
label="Nama Sektor Unggulan"
|
||||
placeholder="Masukkan nama sektor unggulan"
|
||||
defaultValue={stateGrafik.update.form.name}
|
||||
onChange={(val) => {
|
||||
stateGrafik.update.form.name = val.currentTarget.value;
|
||||
}}
|
||||
value={formData.name}
|
||||
onChange={handleChange('name')}
|
||||
required
|
||||
/>
|
||||
<Box>
|
||||
@@ -100,20 +116,18 @@ function EditSektorUnggulanDesa() {
|
||||
Konten
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={stateGrafik.update.form.description}
|
||||
onChange={(htmlContent) => {
|
||||
stateGrafik.update.form.description = htmlContent;
|
||||
}}
|
||||
value={formData.description}
|
||||
onChange={(htmlContent) =>
|
||||
setFormData((prev) => ({ ...prev, description: htmlContent }))
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<TextInput
|
||||
label="Jumlah"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stateGrafik.update.form.value}
|
||||
onChange={(val) => {
|
||||
stateGrafik.update.form.value = Number(val.currentTarget.value);
|
||||
}}
|
||||
value={formData.value}
|
||||
onChange={handleChange('value')}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -20,36 +19,49 @@ export default function EditHubunganOrganisasi() {
|
||||
tipe: '',
|
||||
});
|
||||
|
||||
// load data awal sekali aja
|
||||
useEffect(() => {
|
||||
strukturorganisasiState.pegawai.findMany.load();
|
||||
|
||||
if (id) {
|
||||
state.edit.load(id).then(data => {
|
||||
(async () => {
|
||||
const data = await state.edit.load(id);
|
||||
if (data) {
|
||||
setForm({
|
||||
atasanId: data.atasanId,
|
||||
bawahanId: data.bawahanId,
|
||||
tipe: data.tipe || '',
|
||||
atasanId: data.atasanId ?? '',
|
||||
bawahanId: data.bawahanId ?? '',
|
||||
tipe: data.tipe ?? '',
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [id]);
|
||||
|
||||
const handleChange = (field: keyof typeof form, value: string) => {
|
||||
setForm(prev => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!form.atasanId || !form.bawahanId) {
|
||||
toast.warn("Atasan dan bawahan harus diisi");
|
||||
toast.warn('Atasan dan bawahan harus diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
// update global state cuma pas submit
|
||||
state.edit.id = id;
|
||||
state.edit.form = form;
|
||||
|
||||
const result = await state.edit.update();
|
||||
|
||||
if (result) {
|
||||
toast.success("Data berhasil diperbarui");
|
||||
router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi');
|
||||
toast.success('Data berhasil diperbarui');
|
||||
router.push(
|
||||
'/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -58,35 +70,45 @@ export default function EditHubunganOrganisasi() {
|
||||
<Paper p="md" w={{ base: '100%', md: '50%' }}>
|
||||
<Stack>
|
||||
<Title order={3}>Edit Hubungan Organisasi</Title>
|
||||
|
||||
<Select
|
||||
label="Atasan"
|
||||
placeholder="Pilih atasan"
|
||||
searchable
|
||||
data={pegawaiList?.map(p => ({
|
||||
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 || '')}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label="Bawahan"
|
||||
placeholder="Pilih bawahan"
|
||||
searchable
|
||||
data={pegawaiList?.map(p => ({
|
||||
value: p.id,
|
||||
label: p.namaLengkap,
|
||||
})) || []}
|
||||
data={
|
||||
pegawaiList?.map(p => ({
|
||||
value: p.id,
|
||||
label: p.namaLengkap,
|
||||
})) || []
|
||||
}
|
||||
value={form.bawahanId}
|
||||
onChange={(val) => setForm({ ...form, bawahanId: val || '' })}
|
||||
onChange={val => handleChange('bawahanId', val || '')}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Tipe"
|
||||
placeholder="Contoh: langsung_melapor"
|
||||
defaultValue={form.tipe}
|
||||
onChange={(e) => setForm({ ...form, tipe: e.currentTarget.value })}
|
||||
value={form.tipe}
|
||||
onChange={e => handleChange('tipe', e.currentTarget.value)}
|
||||
/>
|
||||
<Button onClick={handleSubmit} color="blue">Simpan</Button>
|
||||
|
||||
<Button onClick={handleSubmit} color="blue">
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
@@ -41,20 +42,28 @@ interface PegawaiFormData {
|
||||
export default function EditPegawai() {
|
||||
const router = useRouter();
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const [previewImage, setPreviewImage] = useState<PreviewImage | string | null>(null);
|
||||
const stateOrganisasi = useProxy(strukturorganisasiState.pegawai);
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<PreviewImage | string | null>(null);
|
||||
const [formData, setFormData] = useState<PegawaiFormData>({
|
||||
namaLengkap: "",
|
||||
gelarAkademik: "",
|
||||
imageId: "",
|
||||
tanggalMasuk: "",
|
||||
email: "",
|
||||
telepon: "",
|
||||
alamat: "",
|
||||
posisiId: "",
|
||||
namaLengkap: '',
|
||||
gelarAkademik: '',
|
||||
imageId: '',
|
||||
tanggalMasuk: '',
|
||||
email: '',
|
||||
telepon: '',
|
||||
alamat: '',
|
||||
posisiId: '',
|
||||
isActive: true,
|
||||
});
|
||||
|
||||
// helper buat update state formData
|
||||
const updateForm = (field: keyof PegawaiFormData, value: any) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
// Format date to YYYY-MM-DD for date input
|
||||
const formatDateForInput = (dateString: string) => {
|
||||
@@ -65,32 +74,29 @@ export default function EditPegawai() {
|
||||
|
||||
useEffect(() => {
|
||||
strukturorganisasiState.posisiOrganisasi.findMany.load();
|
||||
|
||||
const loadPegawai = async () => {
|
||||
try {
|
||||
const data = await stateOrganisasi.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
namaLengkap: data.namaLengkap || "",
|
||||
gelarAkademik: data.gelarAkademik || "",
|
||||
imageId: data.imageId || "",
|
||||
tanggalMasuk: data.tanggalMasuk || "",
|
||||
email: data.email || "",
|
||||
telepon: data.telepon || "",
|
||||
alamat: data.alamat || "",
|
||||
posisiId: data.posisiId || "",
|
||||
isActive: data.isActive ?? true, // pakai nullish coalescing
|
||||
namaLengkap: data.namaLengkap || '',
|
||||
gelarAkademik: data.gelarAkademik || '',
|
||||
imageId: data.imageId || '',
|
||||
tanggalMasuk: data.tanggalMasuk || '',
|
||||
email: data.email || '',
|
||||
telepon: data.telepon || '',
|
||||
alamat: data.alamat || '',
|
||||
posisiId: data.posisiId || '',
|
||||
isActive: data.isActive ?? true,
|
||||
});
|
||||
|
||||
if (data.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
} else {
|
||||
setPreviewImage(null);
|
||||
}
|
||||
setPreviewImage(data.image?.link || null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pegawai:", error);
|
||||
console.error('Error loading pegawai:', error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal mengambil data pegawai"
|
||||
error instanceof Error ? error.message : 'Gagal mengambil data pegawai'
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -106,18 +112,16 @@ export default function EditPegawai() {
|
||||
}
|
||||
|
||||
stateOrganisasi.edit.form = {
|
||||
...formData,
|
||||
namaLengkap: formData.namaLengkap.trim(),
|
||||
gelarAkademik: formData.gelarAkademik.trim(),
|
||||
imageId: formData.imageId ? formData.imageId.trim() : "",
|
||||
imageId: formData.imageId ? formData.imageId.trim() : '',
|
||||
tanggalMasuk: formData.tanggalMasuk.trim(),
|
||||
email: formData.email.trim(),
|
||||
telepon: formData.telepon.trim(),
|
||||
alamat: formData.alamat.trim(),
|
||||
posisiId: formData.posisiId.trim(),
|
||||
isActive: formData.isActive,
|
||||
};
|
||||
|
||||
|
||||
|
||||
if (id && !stateOrganisasi.edit.id) {
|
||||
stateOrganisasi.edit.id = id;
|
||||
@@ -126,67 +130,90 @@ export default function EditPegawai() {
|
||||
const success = await stateOrganisasi.edit.submit();
|
||||
|
||||
if (success) {
|
||||
router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai");
|
||||
router.push(
|
||||
'/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating pegawai:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memperbarui data pegawai");
|
||||
console.error('Error updating pegawai:', error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : 'Gagal memperbarui data pegawai'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||
<Button onClick={() => router.back()} variant="subtle" color={'blue'}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Stack gap={'xs'}>
|
||||
<Title order={4}>Edit Data Pegawai</Title>
|
||||
|
||||
<TextInput
|
||||
label="Nama Lengkap"
|
||||
placeholder="Masukkan nama lengkap"
|
||||
defaultValue={formData.namaLengkap}
|
||||
onChange={(e) => setFormData({ ...formData, namaLengkap: e.target.value })}
|
||||
value={formData.namaLengkap}
|
||||
onChange={(e) => updateForm('namaLengkap', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Gelar Akademik"
|
||||
placeholder="Contoh: S.Kom"
|
||||
defaultValue={formData.gelarAkademik}
|
||||
onChange={(e) => setFormData({ ...formData, gelarAkademik: e.target.value })}
|
||||
value={formData.gelarAkademik}
|
||||
onChange={(e) => updateForm('gelarAkademik', e.target.value)}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Gambar</Text>
|
||||
<Box >
|
||||
<Text fz={'md'} fw={'bold'}>
|
||||
Gambar
|
||||
</Text>
|
||||
<Box>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const file = files[0]; // Hanya ambil file pertama
|
||||
const file = files[0];
|
||||
if (file) {
|
||||
setPreviewImage({
|
||||
file,
|
||||
preview: URL.createObjectURL(file)
|
||||
preview: URL.createObjectURL(file),
|
||||
});
|
||||
}
|
||||
}}
|
||||
maxSize={5 * 1024 ** 2} // 5MB
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{
|
||||
'image/*': ['.jpeg', '.jpg', '.png', '.webp']
|
||||
'image/*': ['.jpeg', '.jpg', '.png', '.webp'],
|
||||
}}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Group
|
||||
justify="center"
|
||||
gap="xl"
|
||||
mih={220}
|
||||
style={{ pointerEvents: 'none' }}
|
||||
>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
||||
<IconUpload
|
||||
size={52}
|
||||
color="var(--mantine-color-blue-6)"
|
||||
stroke={1.5}
|
||||
/>
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
||||
<IconX
|
||||
size={52}
|
||||
color="var(--mantine-color-red-6)"
|
||||
stroke={1.5}
|
||||
/>
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
||||
<IconPhoto
|
||||
size={52}
|
||||
color="var(--mantine-color-dimmed)"
|
||||
stroke={1.5}
|
||||
/>
|
||||
</Dropzone.Idle>
|
||||
|
||||
<div>
|
||||
@@ -194,14 +221,20 @@ export default function EditPegawai() {
|
||||
Drag images here or click to select files
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" inline mt={7}>
|
||||
Attach as many files as you like, each file should not exceed 5mb
|
||||
Attach as many files as you like, each file should not
|
||||
exceed 5mb
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Image
|
||||
src={typeof previewImage === 'string' ? previewImage : previewImage?.preview}
|
||||
src={
|
||||
typeof previewImage === 'string'
|
||||
? previewImage
|
||||
: previewImage?.preview
|
||||
}
|
||||
alt="Preview"
|
||||
width={280}
|
||||
height={180}
|
||||
@@ -213,70 +246,70 @@ export default function EditPegawai() {
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<TextInput
|
||||
label="Tanggal Masuk"
|
||||
type="date"
|
||||
placeholder="Contoh: 2022-01-01"
|
||||
defaultValue={formatDateForInput(formData.tanggalMasuk)}
|
||||
onChange={(e) => setFormData({ ...formData, tanggalMasuk: e.target.value })}
|
||||
value={formatDateForInput(formData.tanggalMasuk)}
|
||||
onChange={(e) => updateForm('tanggalMasuk', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Email"
|
||||
placeholder="Contoh: email@example.com"
|
||||
defaultValue={formData.email}
|
||||
onChange={(e) => (formData.email = e.currentTarget.value)}
|
||||
value={formData.email}
|
||||
onChange={(e) => updateForm('email', e.currentTarget.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Telepon"
|
||||
placeholder="Contoh: 08123456789"
|
||||
defaultValue={formData.telepon}
|
||||
onChange={(e) => (formData.telepon = e.currentTarget.value)}
|
||||
value={formData.telepon}
|
||||
onChange={(e) => updateForm('telepon', e.currentTarget.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
placeholder="Contoh: Jl. Contoh No. 1"
|
||||
defaultValue={formData.alamat}
|
||||
onChange={(e) => (formData.alamat = e.currentTarget.value)}
|
||||
value={formData.alamat}
|
||||
onChange={(e) => updateForm('alamat', e.currentTarget.value)}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label="Posisi"
|
||||
placeholder="Pilih posisi"
|
||||
data={
|
||||
strukturorganisasiState.posisiOrganisasi.findMany.data?.map((p) => ({
|
||||
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);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label="Status Pegawai"
|
||||
data={[
|
||||
{ value: 'true', label: 'Aktif' },
|
||||
{ value: 'false', label: 'Tidak Aktif' },
|
||||
]}
|
||||
value={String(formData.isActive)} // 'true' atau 'false'
|
||||
onChange={(val) => {
|
||||
setFormData({ ...formData, isActive: val === 'true' });
|
||||
}}
|
||||
value={String(formData.isActive)}
|
||||
onChange={(val) => updateForm('isActive', val === 'true')}
|
||||
/>
|
||||
|
||||
|
||||
<Group>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
color="blue"
|
||||
>
|
||||
<Button onClick={handleSubmit} color="blue">
|
||||
Simpan
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box >
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ function EditPosisiOrganisasi() {
|
||||
hierarki: 0,
|
||||
});
|
||||
|
||||
// Load data awal sekali saja
|
||||
useEffect(() => {
|
||||
const loadPosisiOrganisasi = async () => {
|
||||
if (!id) return;
|
||||
@@ -42,9 +43,9 @@ function EditPosisiOrganisasi() {
|
||||
if (data) {
|
||||
stateOrganisasi.edit.id = id;
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
hierarki: data.hierarki || 0,
|
||||
nama: data.nama ?? '',
|
||||
deskripsi: data.deskripsi ?? '',
|
||||
hierarki: data.hierarki ?? 0,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -56,6 +57,10 @@ function EditPosisiOrganisasi() {
|
||||
loadPosisiOrganisasi();
|
||||
}, [id]);
|
||||
|
||||
const handleChange = (field: string, value: string | number) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
if (!formData.nama.trim()) {
|
||||
@@ -63,6 +68,7 @@ function EditPosisiOrganisasi() {
|
||||
return;
|
||||
}
|
||||
|
||||
// update global state HANYA saat submit
|
||||
stateOrganisasi.edit.form = {
|
||||
nama: formData.nama.trim(),
|
||||
deskripsi: formData.deskripsi.trim(),
|
||||
@@ -114,10 +120,8 @@ function EditPosisiOrganisasi() {
|
||||
>
|
||||
<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="Nama Posisi Organisasi"
|
||||
placeholder="Masukkan nama posisi organisasi"
|
||||
required
|
||||
@@ -130,19 +134,16 @@ function EditPosisiOrganisasi() {
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) =>
|
||||
setFormData({ ...formData, deskripsi: htmlContent })
|
||||
handleChange('deskripsi', htmlContent)
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<TextInput
|
||||
type="number"
|
||||
defaultValue={formData.hierarki}
|
||||
value={formData.hierarki}
|
||||
onChange={(e) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
hierarki: parseInt(e.target.value) || 0,
|
||||
})
|
||||
handleChange('hierarki', parseInt(e.target.value) || 0)
|
||||
}
|
||||
label="Hierarki"
|
||||
placeholder="Masukkan hierarki"
|
||||
|
||||
@@ -12,18 +12,22 @@ import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function EditPenghargaan() {
|
||||
const stateDesaDigital = useProxy(desaDigitalState)
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null)
|
||||
const [file, setFile] = useState<File | null>(null)
|
||||
const [formData, setFormData] = useState({
|
||||
name: stateDesaDigital.findUnique.data?.name || '',
|
||||
deskripsi: stateDesaDigital.findUnique.data?.deskripsi || '',
|
||||
imageId: stateDesaDigital.findUnique.data?.imageId || '',
|
||||
})
|
||||
function EditDigitalSmartVillage() {
|
||||
const stateDesaDigital = useProxy(desaDigitalState);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
// ✅ hanya lokal state untuk form
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
});
|
||||
|
||||
// load data sekali saat mount
|
||||
useEffect(() => {
|
||||
const loadPenghargaan = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -53,12 +57,11 @@ function EditPenghargaan() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// ✅ update global state hanya saat submit
|
||||
stateDesaDigital.edit.form = {
|
||||
...stateDesaDigital.edit.form,
|
||||
name: formData.name,
|
||||
deskripsi: formData.deskripsi,
|
||||
imageId: formData.imageId,
|
||||
}
|
||||
...formData,
|
||||
};
|
||||
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||
@@ -78,7 +81,7 @@ function EditPenghargaan() {
|
||||
console.error("Error updating desa digital smart village:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui desa digital smart village");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
@@ -87,78 +90,80 @@ function EditPenghargaan() {
|
||||
<IconArrowBack color={colors["blue-button"]} size={30} />
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Edit Desa Digital Smart Village</Title>
|
||||
|
||||
{/* ✅ controlled input */}
|
||||
<TextInput
|
||||
defaultValue={formData.name}
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Gambar</Text>
|
||||
<Box>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
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/*': [] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
||||
</Dropzone.Idle>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
}
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
||||
</Dropzone.Idle>
|
||||
|
||||
<div>
|
||||
<Text size="xl" inline>
|
||||
Drag gambar ke sini atau klik untuk pilih file
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" inline mt={7}>
|
||||
Maksimal 5MB dan harus format gambar
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
<div>
|
||||
<Text size="xl" inline>
|
||||
Drag gambar ke sini atau klik untuk pilih file
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" inline mt={7}>
|
||||
Maksimal 5MB dan harus format gambar
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{/* Tampilkan preview kalau ada */}
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '200px',
|
||||
objectFit: 'contain',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '200px',
|
||||
objectFit: 'contain',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>
|
||||
{/* ✅ controlled editor */}
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
|
||||
stateDesaDigital.edit.form.deskripsi = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -170,4 +175,4 @@ function EditPenghargaan() {
|
||||
);
|
||||
}
|
||||
|
||||
export default EditPenghargaan;
|
||||
export default EditDigitalSmartVillage;
|
||||
|
||||
@@ -30,17 +30,19 @@ function EditInfoTeknologiTepatGuna() {
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: stateInfoTekno.findUnique.data?.name || '',
|
||||
deskripsi: stateInfoTekno.findUnique.data?.deskripsi || '',
|
||||
imageId: stateInfoTekno.findUnique.data?.imageId || '',
|
||||
name: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
});
|
||||
|
||||
// Load data pertama kali
|
||||
useEffect(() => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
const loadPenghargaan = async () => {
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const data = await stateInfoTekno.edit.load(id);
|
||||
if (data) {
|
||||
@@ -58,16 +60,19 @@ function EditInfoTeknologiTepatGuna() {
|
||||
}
|
||||
};
|
||||
|
||||
loadPenghargaan();
|
||||
loadData();
|
||||
}, [params?.id]);
|
||||
|
||||
// Submit form
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// sync local → global pas submit
|
||||
stateInfoTekno.edit.form = {
|
||||
...stateInfoTekno.edit.form,
|
||||
...formData,
|
||||
};
|
||||
|
||||
// upload file kalau ada
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file,
|
||||
@@ -119,8 +124,8 @@ function EditInfoTeknologiTepatGuna() {
|
||||
<TextInput
|
||||
label="Judul"
|
||||
placeholder="Masukkan judul info teknologi tepat guna"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -188,10 +193,9 @@ function EditInfoTeknologiTepatGuna() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
|
||||
stateInfoTekno.edit.form.deskripsi = htmlContent;
|
||||
}}
|
||||
onChange={(htmlContent) =>
|
||||
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -27,14 +27,14 @@ function EditKolaborasiInovasi() {
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: kolaborasiState.update.form.name || "",
|
||||
deskripsi: kolaborasiState.update.form.deskripsi || "",
|
||||
tahun: kolaborasiState.update.form.tahun || "",
|
||||
slug: kolaborasiState.update.form.slug || "",
|
||||
kolaborator: kolaborasiState.update.form.kolaborator || "",
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
tahun: "",
|
||||
slug: "",
|
||||
kolaborator: "",
|
||||
});
|
||||
|
||||
// Load data
|
||||
// Load data awal dari server
|
||||
useEffect(() => {
|
||||
const loadKolaborasi = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -44,11 +44,11 @@ function EditKolaborasiInovasi() {
|
||||
const data = await kolaborasiState.update.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || "",
|
||||
deskripsi: data.deskripsi || "",
|
||||
tahun: data.tahun || "",
|
||||
slug: data.slug || "",
|
||||
kolaborator: data.kolaborator || "",
|
||||
name: data.name ?? "",
|
||||
deskripsi: data.deskripsi ?? "",
|
||||
tahun: data.tahun?.toString() ?? "",
|
||||
slug: data.slug ?? "",
|
||||
kolaborator: data.kolaborator ?? "",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -60,6 +60,7 @@ function EditKolaborasiInovasi() {
|
||||
loadKolaborasi();
|
||||
}, [params?.id]);
|
||||
|
||||
// Handler submit → baru update global state
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
kolaborasiState.update.form = {
|
||||
@@ -70,6 +71,7 @@ function EditKolaborasiInovasi() {
|
||||
slug: formData.slug,
|
||||
kolaborator: formData.kolaborator,
|
||||
};
|
||||
|
||||
await kolaborasiState.update.submit();
|
||||
toast.success("Kolaborasi inovasi berhasil diperbarui!");
|
||||
router.push("/admin/inovasi/kolaborasi-inovasi");
|
||||
@@ -79,6 +81,11 @@ function EditKolaborasiInovasi() {
|
||||
}
|
||||
};
|
||||
|
||||
// Handler input (biar lebih DRY)
|
||||
const handleChange = (field: keyof typeof formData, value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: "sm", md: "lg" }} py="md">
|
||||
<Group mb="md">
|
||||
@@ -104,32 +111,32 @@ function EditKolaborasiInovasi() {
|
||||
<TextInput
|
||||
label="Nama Kolaborasi"
|
||||
placeholder="Masukkan nama kolaborasi"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) => handleChange("name", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Deskripsi Singkat"
|
||||
placeholder="Masukkan deskripsi singkat"
|
||||
defaultValue={formData.slug}
|
||||
onChange={(e) => setFormData({ ...formData, slug: e.target.value })}
|
||||
value={formData.slug}
|
||||
onChange={(e) => handleChange("slug", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Tahun"
|
||||
placeholder="Masukkan tahun"
|
||||
defaultValue={formData.tahun}
|
||||
onChange={(e) => setFormData({ ...formData, tahun: e.target.value })}
|
||||
value={formData.tahun}
|
||||
onChange={(e) => handleChange("tahun", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Kolaborator"
|
||||
placeholder="Masukkan nama kolaborator"
|
||||
defaultValue={formData.kolaborator}
|
||||
onChange={(e) => setFormData({ ...formData, kolaborator: e.target.value })}
|
||||
value={formData.kolaborator}
|
||||
onChange={(e) => handleChange("kolaborator", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -139,10 +146,7 @@ function EditKolaborasiInovasi() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
|
||||
kolaborasiState.update.form.deskripsi = htmlContent;
|
||||
}}
|
||||
onChange={(htmlContent) => handleChange("deskripsi", htmlContent)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -33,17 +33,22 @@ function EditMitraKolaborasi() {
|
||||
const state = useProxy(mitraKolaborasi);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
// Local form state (controlled)
|
||||
const [formData, setFormData] = useState({
|
||||
name: state.update.form.name || '',
|
||||
imageId: state.update.form.imageId || '',
|
||||
name: '',
|
||||
imageId: '',
|
||||
});
|
||||
|
||||
// Load data ke state lokal sekali saja
|
||||
useEffect(() => {
|
||||
const loadFoto = async () => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await state.update.load(id);
|
||||
if (data) {
|
||||
@@ -51,25 +56,31 @@ function EditMitraKolaborasi() {
|
||||
name: data.name || '',
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading foto:', error);
|
||||
toast.error('Gagal memuat data foto');
|
||||
console.error('Error loading data:', error);
|
||||
toast.error('Gagal memuat data mitra');
|
||||
}
|
||||
};
|
||||
loadFoto();
|
||||
|
||||
loadData();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange = (key: string, value: string) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[key]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
state.update.form = {
|
||||
...state.update.form,
|
||||
name: formData.name,
|
||||
imageId: formData.imageId,
|
||||
};
|
||||
// upload file jika ada
|
||||
let imageId = formData.imageId;
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file,
|
||||
@@ -79,8 +90,16 @@ function EditMitraKolaborasi() {
|
||||
if (!uploaded?.id) {
|
||||
return toast.error('Gagal upload gambar');
|
||||
}
|
||||
state.update.form.imageId = uploaded.id;
|
||||
imageId = uploaded.id;
|
||||
}
|
||||
|
||||
// update global state hanya saat submit
|
||||
state.update.form = {
|
||||
...state.update.form,
|
||||
name: formData.name,
|
||||
imageId,
|
||||
};
|
||||
|
||||
await state.update.update();
|
||||
toast.success('Mitra berhasil diperbarui!');
|
||||
router.push('/admin/inovasi/kolaborasi-inovasi/mitra-kolaborasi');
|
||||
@@ -118,8 +137,8 @@ function EditMitraKolaborasi() {
|
||||
<TextInput
|
||||
label="Nama Mitra"
|
||||
placeholder="Masukkan nama mitra"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) => handleChange('name', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
@@ -11,24 +11,28 @@ import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function EditJenisLayanan() {
|
||||
const state = useProxy(layananonlineDesa.jenisLayanan)
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const [formData, setFormData] = useState({
|
||||
nama: state.edit.form.nama,
|
||||
deskripsi: state.edit.form.deskripsi,
|
||||
})
|
||||
const state = useProxy(layananonlineDesa.jenisLayanan);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
// state lokal untuk form
|
||||
const [formData, setFormData] = useState({
|
||||
nama: "",
|
||||
deskripsi: "",
|
||||
});
|
||||
|
||||
// load data dari backend ke local state
|
||||
useEffect(() => {
|
||||
const loadJenisLayanan = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await state.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
nama: data.nama,
|
||||
deskripsi: data.deskripsi,
|
||||
nama: data.nama ?? "",
|
||||
deskripsi: data.deskripsi ?? "",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -36,24 +40,26 @@ function EditJenisLayanan() {
|
||||
toast.error("Gagal memuat data jenis layanan");
|
||||
}
|
||||
};
|
||||
|
||||
loadJenisLayanan();
|
||||
}, [params?.id]);
|
||||
|
||||
// submit update → baru sync ke global state
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
state.edit.form = {
|
||||
...state.edit.form,
|
||||
nama: formData.nama,
|
||||
deskripsi: formData.deskripsi,
|
||||
}
|
||||
await state.edit.update()
|
||||
toast.success("Jenis layanan berhasil diperbarui!")
|
||||
router.push("/admin/inovasi/layanan-online-desa/jenis-layanan")
|
||||
...formData,
|
||||
};
|
||||
|
||||
await state.edit.update();
|
||||
toast.success("Jenis layanan berhasil diperbarui!");
|
||||
router.push("/admin/inovasi/layanan-online-desa/jenis-layanan");
|
||||
} catch (error) {
|
||||
console.error("Error updating jenis layanan:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui jenis layanan");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
@@ -62,27 +68,33 @@ function EditJenisLayanan() {
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||
<Stack gap={"xs"}>
|
||||
|
||||
<Paper bg={colors["white-1"]} p="md" w={{ base: "100%", md: "50%" }}>
|
||||
<Stack gap="xs">
|
||||
<Title order={3}>Edit Jenis Layanan</Title>
|
||||
|
||||
<TextInput
|
||||
defaultValue={formData.nama}
|
||||
onChange={(val) => {
|
||||
setFormData({ ...formData, nama: val.target.value });
|
||||
}}
|
||||
label={<Text fz={"sm"} fw={"bold"}>Nama Jenis Layanan</Text>}
|
||||
value={formData.nama}
|
||||
onChange={(e) =>
|
||||
setFormData((prev) => ({ ...prev, nama: e.target.value }))
|
||||
}
|
||||
label={<Text fz="sm" fw="bold">Nama Jenis Layanan</Text>}
|
||||
placeholder="masukkan nama jenis layanan"
|
||||
/>
|
||||
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>
|
||||
<Text fz="sm" fw="bold">Deskripsi</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData({ ...formData, deskripsi: htmlContent });
|
||||
}}
|
||||
onChange={(htmlContent) =>
|
||||
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
|
||||
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
Stack,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
@@ -25,9 +25,10 @@ function EditJenisPengaduan() {
|
||||
const state = useProxy(layananonlineDesa.jenisPengaduan);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: "",
|
||||
nama: '',
|
||||
});
|
||||
|
||||
// Load data sekali aja
|
||||
useEffect(() => {
|
||||
const loadJenisPengaduan = async () => {
|
||||
if (!id) return;
|
||||
@@ -36,51 +37,58 @@ function EditJenisPengaduan() {
|
||||
const data = await state.edit.load(id);
|
||||
|
||||
if (data) {
|
||||
// pastikan id-nya masuk ke state edit
|
||||
state.edit.id = id;
|
||||
state.edit.id = id; // inject id ke state global (hanya sekali)
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading jenis pengaduan:", error);
|
||||
toast.error("Gagal memuat data jenis pengaduan");
|
||||
console.error('Error loading jenis pengaduan:', error);
|
||||
toast.error('Gagal memuat data jenis pengaduan');
|
||||
}
|
||||
};
|
||||
|
||||
loadJenisPengaduan();
|
||||
}, [id]);
|
||||
|
||||
const handleChange = (field: keyof typeof formData, value: string) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.nama.trim()) {
|
||||
toast.error('Nama jenis pengaduan tidak boleh kosong');
|
||||
return;
|
||||
}
|
||||
|
||||
// Update ke global state HANYA pas submit
|
||||
state.edit.form = {
|
||||
nama: formData.nama.trim(),
|
||||
};
|
||||
|
||||
// Safety fallback kalau ID belum ada
|
||||
if (!state.edit.id) {
|
||||
state.edit.id = id;
|
||||
}
|
||||
|
||||
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");
|
||||
router.push('/admin/inovasi/layanan-online-desa/jenis-pengaduan');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating jenis pengaduan:", error);
|
||||
// toast akan ditampilkan dari fungsi update
|
||||
console.error('Error updating jenis pengaduan:', error);
|
||||
// toast ditangani di dalam state.update
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header + tombol back */}
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button
|
||||
@@ -97,7 +105,7 @@ function EditJenisPengaduan() {
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
{/* Card Form */}
|
||||
{/* Form */}
|
||||
<Paper
|
||||
w={{ base: '100%', md: '50%' }}
|
||||
bg={colors['white-1']}
|
||||
@@ -108,8 +116,8 @@ function EditJenisPengaduan() {
|
||||
>
|
||||
<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="Nama Jenis Pengaduan"
|
||||
placeholder="Masukkan nama jenis pengaduan"
|
||||
required
|
||||
|
||||
@@ -41,6 +41,7 @@ function EditProgramKreatifDesa() {
|
||||
icon: '',
|
||||
});
|
||||
|
||||
// Load data hanya sekali berdasarkan params.id
|
||||
useEffect(() => {
|
||||
const loadProgramKreatif = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -50,17 +51,11 @@ function EditProgramKreatifDesa() {
|
||||
const data = await stateProgramKreatif.update.load(id);
|
||||
if (data) {
|
||||
stateProgramKreatif.update.id = id;
|
||||
stateProgramKreatif.update.form = {
|
||||
name: data.name,
|
||||
slug: data.slug,
|
||||
deskripsi: data.deskripsi,
|
||||
icon: data.icon,
|
||||
};
|
||||
setFormData({
|
||||
name: data.name,
|
||||
slug: data.slug,
|
||||
deskripsi: data.deskripsi,
|
||||
icon: data.icon,
|
||||
name: data.name || '',
|
||||
slug: data.slug || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
icon: data.icon || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -72,10 +67,15 @@ function EditProgramKreatifDesa() {
|
||||
loadProgramKreatif();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange =
|
||||
(field: keyof FormProgramKreatif) =>
|
||||
(value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
stateProgramKreatif.update.form = {
|
||||
...stateProgramKreatif.update.form,
|
||||
name: formData.name.trim(),
|
||||
deskripsi: formData.deskripsi.trim(),
|
||||
slug: formData.slug.trim(),
|
||||
@@ -85,7 +85,7 @@ function EditProgramKreatifDesa() {
|
||||
router.push('/admin/inovasi/program-kreatif-desa');
|
||||
} catch (error) {
|
||||
console.error('Error updating program kreatif:', error);
|
||||
toast.error('Gagal memuat data program kreatif');
|
||||
toast.error('Gagal menyimpan program kreatif');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -93,7 +93,12 @@ function EditProgramKreatifDesa() {
|
||||
<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>
|
||||
@@ -114,16 +119,16 @@ function EditProgramKreatifDesa() {
|
||||
<TextInput
|
||||
label="Nama Program Kreatif Desa"
|
||||
placeholder="Masukkan nama program kreatif desa"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) => handleChange('name')(e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Deskripsi Singkat Program Kreatif Desa"
|
||||
placeholder="Masukkan deskripsi singkat program kreatif desa"
|
||||
defaultValue={formData.slug}
|
||||
onChange={(e) => setFormData({ ...formData, slug: e.target.value })}
|
||||
value={formData.slug}
|
||||
onChange={(e) => handleChange('slug')(e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -133,10 +138,7 @@ function EditProgramKreatifDesa() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
|
||||
stateProgramKreatif.update.form.deskripsi = htmlContent;
|
||||
}}
|
||||
onChange={handleChange('deskripsi')}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -146,10 +148,7 @@ function EditProgramKreatifDesa() {
|
||||
</Text>
|
||||
<SelectIconProgramEdit
|
||||
value={formData.icon as IconKey}
|
||||
onChange={(value) => {
|
||||
setFormData((prev) => ({ ...prev, icon: value }));
|
||||
stateProgramKreatif.update.form.icon = value;
|
||||
}}
|
||||
onChange={handleChange('icon')}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
"use client";
|
||||
|
||||
import {
|
||||
@@ -40,12 +39,12 @@ function EditKeamananLingkungan() {
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
name: keamananState.edit.form.name || "",
|
||||
deskripsi: keamananState.edit.form.deskripsi || "",
|
||||
imageId: keamananState.edit.form.imageId || "",
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
});
|
||||
|
||||
// Load data by id
|
||||
// Load data sekali pas mount
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -71,16 +70,16 @@ function EditKeamananLingkungan() {
|
||||
};
|
||||
|
||||
loadData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange = (field: string, value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
keamananState.edit.form = {
|
||||
...keamananState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsi: formData.deskripsi,
|
||||
imageId: formData.imageId,
|
||||
};
|
||||
let imageId = formData.imageId;
|
||||
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
@@ -93,9 +92,17 @@ function EditKeamananLingkungan() {
|
||||
return toast.error("Gagal upload gambar");
|
||||
}
|
||||
|
||||
keamananState.edit.form.imageId = uploaded.id;
|
||||
imageId = uploaded.id;
|
||||
}
|
||||
|
||||
// update global state hanya sekali pas submit
|
||||
keamananState.edit.form = {
|
||||
...keamananState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsi: formData.deskripsi,
|
||||
imageId,
|
||||
};
|
||||
|
||||
await keamananState.edit.update();
|
||||
toast.success("Keamanan Lingkungan berhasil diperbarui!");
|
||||
router.push("/admin/keamanan/keamanan-lingkungan-pecalang-patwal");
|
||||
@@ -186,7 +193,7 @@ function EditKeamananLingkungan() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage ? (
|
||||
<Image alt="" src={previewImage} w={200} h={200} loading="lazy"/>
|
||||
<Image alt="" src={previewImage} w={200} h={200} loading="lazy" />
|
||||
) : (
|
||||
<Center w={200} h={200} bg={"gray"}>
|
||||
<IconImageInPicture />
|
||||
@@ -194,10 +201,8 @@ function EditKeamananLingkungan() {
|
||||
)}
|
||||
|
||||
<TextInput
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, name: e.target.value })
|
||||
}
|
||||
value={formData.name}
|
||||
onChange={(e) => handleChange("name", e.target.value)}
|
||||
label="Judul Keamanan Lingkungan"
|
||||
placeholder="Masukkan judul"
|
||||
required
|
||||
@@ -209,10 +214,7 @@ function EditKeamananLingkungan() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
|
||||
keamananState.edit.form.deskripsi = htmlContent;
|
||||
}}
|
||||
onChange={(htmlContent) => handleChange("deskripsi", htmlContent)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -27,11 +27,12 @@ function EditKontakItem() {
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: kontakState.update.form.nama || '',
|
||||
nomorTelepon: kontakState.update.form.nomorTelepon || '',
|
||||
icon: kontakState.update.form.icon || '',
|
||||
name: '',
|
||||
nomorTelepon: '',
|
||||
icon: '',
|
||||
});
|
||||
|
||||
// Load data sekali dari global state
|
||||
useEffect(() => {
|
||||
const loadKontakDarurat = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -55,14 +56,23 @@ function EditKontakItem() {
|
||||
loadKontakDarurat();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange = (field: keyof typeof formData, value: string) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// Update global state sekali pas submit
|
||||
kontakState.update.form = {
|
||||
...kontakState.update.form,
|
||||
nama: formData.name,
|
||||
nomorTelepon: formData.nomorTelepon,
|
||||
icon: formData.icon,
|
||||
};
|
||||
|
||||
await kontakState.update.update();
|
||||
toast.success('Kontak Darurat berhasil diperbarui!');
|
||||
router.push('/admin/keamanan/kontak-darurat/kontak-darurat-item');
|
||||
@@ -99,16 +109,16 @@ function EditKontakItem() {
|
||||
<TextInput
|
||||
label="Nama Kontak"
|
||||
placeholder="Masukkan nama kontak"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) => handleChange('name', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Nomor Telepon"
|
||||
placeholder="Masukkan nomor telepon"
|
||||
defaultValue={formData.nomorTelepon}
|
||||
onChange={(e) => setFormData({ ...formData, nomorTelepon: e.target.value })}
|
||||
value={formData.nomorTelepon}
|
||||
onChange={(e) => handleChange('nomorTelepon', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -118,10 +128,7 @@ function EditKontakItem() {
|
||||
</Text>
|
||||
<SelectIconProgramEdit
|
||||
value={formData.icon as IconKey}
|
||||
onChange={(value) => {
|
||||
setFormData((prev) => ({ ...prev, icon: value }));
|
||||
kontakState.update.form.icon = value;
|
||||
}}
|
||||
onChange={(value) => handleChange('icon', value)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -15,40 +15,40 @@ import {
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconArrowBack
|
||||
} from "@tabler/icons-react";
|
||||
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 EditKontakDaruratKeamanan() {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const router = useRouter();
|
||||
const kontakState = useProxy(kontakDarurat.kontakDaruratKeamananState);
|
||||
const params = useParams();
|
||||
const kontakState = useProxy(kontakDarurat.kontakDaruratKeamananState);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [formData, setFormData] = useState({
|
||||
name: kontakState.update.form.nama || "",
|
||||
icon: kontakState.update.form.icon || "",
|
||||
kategoriId: kontakState.update.form.kategoriId || [],
|
||||
name: "",
|
||||
icon: "" as IconKey | "",
|
||||
kategoriId: [] as string[],
|
||||
});
|
||||
|
||||
// Load data
|
||||
// Load data dari backend
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await kontakDarurat.kontakDaruratItem.findMany.load();
|
||||
|
||||
const id = params?.id as string;
|
||||
if (id) {
|
||||
const data = await kontakState.update.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.nama || "",
|
||||
icon: data.icon || "",
|
||||
icon: (data.icon as IconKey) || "",
|
||||
kategoriId: data.kategoriId || [],
|
||||
});
|
||||
}
|
||||
@@ -63,6 +63,7 @@ function EditKontakDaruratKeamanan() {
|
||||
|
||||
loadData();
|
||||
}, [params?.id]);
|
||||
|
||||
// Handle submit
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
@@ -72,6 +73,7 @@ function EditKontakDaruratKeamanan() {
|
||||
icon: formData.icon,
|
||||
kategoriId: formData.kategoriId,
|
||||
};
|
||||
|
||||
await kontakState.update.update();
|
||||
toast.success("Kontak Darurat berhasil diperbarui!");
|
||||
router.push("/admin/keamanan/kontak-darurat");
|
||||
@@ -110,43 +112,54 @@ function EditKontakDaruratKeamanan() {
|
||||
style={{ border: "1px solid #e0e0e0" }}
|
||||
>
|
||||
<Stack gap="md">
|
||||
{/* Nama kategori */}
|
||||
{/* Nama Kontak */}
|
||||
<TextInput
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) =>
|
||||
setFormData((prev) => ({ ...prev, name: e.target.value }))
|
||||
}
|
||||
label="Nama Kontak Darurat"
|
||||
placeholder="Masukkan nama kontak darurat"
|
||||
required
|
||||
/>
|
||||
|
||||
{/* MultiSelect */}
|
||||
<MultiSelect
|
||||
value={formData.kategoriId}
|
||||
onChange={(val) => setFormData({ ...formData, kategoriId: val })}
|
||||
onChange={(val) =>
|
||||
setFormData((prev) => ({ ...prev, kategoriId: val }))
|
||||
}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Kontak Item</Text>}
|
||||
placeholder={isLoading ? "Memuat data..." : "Pilih kontak item"}
|
||||
data={
|
||||
Array.isArray(kontakDarurat.kontakDaruratItem.findMany.data)
|
||||
? kontakDarurat.kontakDaruratItem.findMany.data.map((v) => ({
|
||||
value: v.id,
|
||||
label: v.nama
|
||||
}))
|
||||
value: v.id,
|
||||
label: v.nama,
|
||||
}))
|
||||
: []
|
||||
}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
error={!formData.kategoriId.length ? "Pilih minimal satu kategori" : undefined}
|
||||
error={
|
||||
!formData.kategoriId.length
|
||||
? "Pilih minimal satu kategori"
|
||||
: undefined
|
||||
}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
|
||||
{/* Icon Select */}
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Ikon Program Kreatif Desa</Text>
|
||||
<Text fz={"sm"} fw={"bold"}>
|
||||
Ikon Program Kreatif Desa
|
||||
</Text>
|
||||
<SelectIconProgramEdit
|
||||
value={formData.icon as IconKey}
|
||||
onChange={(value) => {
|
||||
setFormData((prev) => ({ ...prev, icon: value }));
|
||||
kontakState.update.form.icon = value;
|
||||
}}
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, icon: value }))
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import laporanPublikState from '@/app/admin/(dashboard)/_state/keamanan/laporan-publik';
|
||||
@@ -30,13 +28,20 @@ function EditLaporanPublik() {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
judul: stateLaporan.edit.form.judul || '',
|
||||
lokasi: stateLaporan.edit.form.lokasi || '',
|
||||
tanggalWaktu: stateLaporan.edit.form.tanggalWaktu || '',
|
||||
status: stateLaporan.edit.form.status || '',
|
||||
penanganan: stateLaporan.edit.form.penanganan || '',
|
||||
kronologi: stateLaporan.edit.form.kronologi || '',
|
||||
const [formData, setFormData] = useState<{
|
||||
judul: string;
|
||||
lokasi: string;
|
||||
tanggalWaktu: string;
|
||||
status: Status;
|
||||
penanganan: string;
|
||||
kronologi: string;
|
||||
}>({
|
||||
judul: '',
|
||||
lokasi: '',
|
||||
tanggalWaktu: '',
|
||||
status: 'Proses', // Default status
|
||||
penanganan: '',
|
||||
kronologi: '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -52,7 +57,7 @@ function EditLaporanPublik() {
|
||||
lokasi: data.lokasi || '',
|
||||
tanggalWaktu: data.tanggalWaktu || '',
|
||||
status: data.status || '',
|
||||
penanganan: data.penanganan?.map((p: any) => p.deskripsi)[0] || '',
|
||||
penanganan: data.penanganan?.[0]?.deskripsi || '',
|
||||
kronologi: data.kronologi || '',
|
||||
});
|
||||
}
|
||||
@@ -63,18 +68,17 @@ function EditLaporanPublik() {
|
||||
};
|
||||
|
||||
loadLaporanPublik();
|
||||
}, [params?.id]);
|
||||
}, [params?.id, stateLaporan.edit]);
|
||||
|
||||
const handleChange = (field: string, value: string | Status) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
stateLaporan.edit.form = {
|
||||
...stateLaporan.edit.form,
|
||||
judul: formData.judul,
|
||||
lokasi: formData.lokasi,
|
||||
tanggalWaktu: formData.tanggalWaktu,
|
||||
status: formData.status,
|
||||
penanganan: formData.penanganan,
|
||||
kronologi: formData.kronologi,
|
||||
...formData,
|
||||
};
|
||||
|
||||
await stateLaporan.edit.update();
|
||||
@@ -111,16 +115,16 @@ function EditLaporanPublik() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={formData.judul}
|
||||
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
|
||||
value={formData.judul}
|
||||
onChange={(e) => handleChange('judul', e.target.value)}
|
||||
label={<Text fw="bold" fz="sm">Judul Laporan Publik</Text>}
|
||||
placeholder="Masukkan judul laporan publik"
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
defaultValue={formData.lokasi}
|
||||
onChange={(e) => setFormData({ ...formData, lokasi: e.target.value })}
|
||||
value={formData.lokasi}
|
||||
onChange={(e) => handleChange('lokasi', e.target.value)}
|
||||
label={<Text fw="bold" fz="sm">Lokasi Laporan Publik</Text>}
|
||||
placeholder="Masukkan lokasi laporan publik"
|
||||
required
|
||||
@@ -129,15 +133,20 @@ function EditLaporanPublik() {
|
||||
<DateTimePicker
|
||||
label="Tanggal & Waktu Laporan Publik"
|
||||
value={formData.tanggalWaktu ? new Date(formData.tanggalWaktu) : null}
|
||||
onChange={(val) =>
|
||||
setFormData({ ...formData, tanggalWaktu: val ? val.toString() : '' })
|
||||
}
|
||||
onChange={(value: string | null) => {
|
||||
if (value) {
|
||||
const date = new Date(value);
|
||||
handleChange('tanggalWaktu', date.toISOString());
|
||||
} else {
|
||||
handleChange('tanggalWaktu', '');
|
||||
}
|
||||
}}
|
||||
required
|
||||
/>
|
||||
|
||||
<Select
|
||||
value={formData.status}
|
||||
onChange={(e) => setFormData({ ...formData, status: e as Status })}
|
||||
onChange={(val) => handleChange('status', val as Status)}
|
||||
label={<Text fw="bold" fz="sm">Status Laporan Publik</Text>}
|
||||
placeholder="Pilih status laporan publik"
|
||||
data={[
|
||||
@@ -152,10 +161,7 @@ function EditLaporanPublik() {
|
||||
<Text fw="bold" fz="sm" mb={6}>Kronologi Laporan Publik</Text>
|
||||
<EditEditor
|
||||
value={formData.kronologi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, kronologi: htmlContent }));
|
||||
stateLaporan.edit.form.kronologi = htmlContent;
|
||||
}}
|
||||
onChange={(htmlContent) => handleChange('kronologi', htmlContent)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -163,10 +169,7 @@ function EditLaporanPublik() {
|
||||
<Text fw="bold" fz="sm" mb={6}>Penanganan Laporan Publik</Text>
|
||||
<EditEditor
|
||||
value={formData.penanganan}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, penanganan: htmlContent }));
|
||||
stateLaporan.edit.form.penanganan = htmlContent;
|
||||
}}
|
||||
onChange={(htmlContent) => handleChange('penanganan', htmlContent)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -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<HTMLInputElement>) => {
|
||||
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() {
|
||||
<TextInput
|
||||
label="Judul"
|
||||
placeholder="Masukkan judul"
|
||||
defaultValue={formData.judul}
|
||||
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
|
||||
value={formData.judul}
|
||||
onChange={handleChange('judul')}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Deskripsi Singkat"
|
||||
placeholder="Masukkan deskripsi singkat"
|
||||
defaultValue={formData.deskripsiSingkat}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, deskripsiSingkat: e.target.value })
|
||||
}
|
||||
value={formData.deskripsiSingkat}
|
||||
onChange={handleChange('deskripsiSingkat')}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -141,7 +143,7 @@ function EditPencegahanKriminalitas() {
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) =>
|
||||
setFormData({ ...formData, deskripsi: val })
|
||||
setFormData((prev) => ({ ...prev, deskripsi: val }))
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
@@ -150,10 +152,8 @@ function EditPencegahanKriminalitas() {
|
||||
<TextInput
|
||||
label="Link Video YouTube"
|
||||
placeholder="https://www.youtube.com/watch?v=abc123"
|
||||
defaultValue={formData.linkVideo}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, linkVideo: e.currentTarget.value })
|
||||
}
|
||||
value={formData.linkVideo}
|
||||
onChange={handleChange('linkVideo')}
|
||||
required
|
||||
/>
|
||||
{embedLink && (
|
||||
|
||||
@@ -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 (
|
||||
<Box px={{ base: "sm", md: "lg" }} py="md">
|
||||
{/* Modal Tambah */}
|
||||
@@ -273,88 +275,59 @@ function EditPolsekTerdekat() {
|
||||
<Stack gap="md">
|
||||
{/* Input fields */}
|
||||
<TextInput
|
||||
defaultValue={formData.nama}
|
||||
onChange={(val) =>
|
||||
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
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.jarakKeDesa}
|
||||
onChange={(val) =>
|
||||
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"
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.alamat}
|
||||
onChange={(val) =>
|
||||
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"
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.nomorTelepon}
|
||||
onChange={(val) =>
|
||||
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"
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.jamOperasional}
|
||||
onChange={(val) =>
|
||||
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"
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.embedMapUrl}
|
||||
onChange={(val) =>
|
||||
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"
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.namaTempatMaps}
|
||||
onChange={(val) =>
|
||||
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"
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.alamatMaps}
|
||||
onChange={(val) =>
|
||||
setFormData({ ...formData, alamatMaps: val.target.value })
|
||||
}
|
||||
value={formData.alamatMaps}
|
||||
onChange={(e) => handleChange("alamatMaps", e.currentTarget.value)}
|
||||
label="Alamat Maps"
|
||||
placeholder="Masukkan alamat di maps"
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.linkPetunjukArah}
|
||||
onChange={(val) =>
|
||||
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 */}
|
||||
<Select
|
||||
label="Layanan Polsek"
|
||||
placeholder="Pilih layanan polsek"
|
||||
data={layananOptions}
|
||||
value={polsekState.create.form.layananPolsekId}
|
||||
onChange={(val) => {
|
||||
polsekState.create.form.layananPolsekId = val || "";
|
||||
}}
|
||||
value={formData.layananPolsekId}
|
||||
onChange={(val) => handleChange("layananPolsekId", val || "")}
|
||||
/>
|
||||
<Button
|
||||
variant="light"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
"use client";
|
||||
|
||||
import {
|
||||
@@ -39,9 +38,9 @@ function EditTipsKeamanan() {
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
judul: keamananState.update.form.judul || "",
|
||||
deskripsi: keamananState.update.form.deskripsi || "",
|
||||
imageId: keamananState.update.form.imageId || "",
|
||||
judul: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
});
|
||||
|
||||
// Load data saat pertama kali
|
||||
@@ -70,14 +69,12 @@ function EditTipsKeamanan() {
|
||||
};
|
||||
|
||||
loadData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
keamananState.update.form = {
|
||||
...keamananState.update.form,
|
||||
...formData,
|
||||
};
|
||||
let imageId = formData.imageId;
|
||||
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
@@ -87,10 +84,14 @@ function EditTipsKeamanan() {
|
||||
const uploaded = res.data?.data;
|
||||
|
||||
if (!uploaded?.id) return toast.error("Gagal upload gambar");
|
||||
|
||||
keamananState.update.form.imageId = uploaded.id;
|
||||
imageId = uploaded.id;
|
||||
}
|
||||
|
||||
keamananState.update.form = {
|
||||
...formData,
|
||||
imageId,
|
||||
};
|
||||
|
||||
await keamananState.update.update();
|
||||
toast.success("Tips Keamanan berhasil diperbarui!");
|
||||
router.push("/admin/keamanan/tips-keamanan");
|
||||
@@ -105,7 +106,12 @@ function EditTipsKeamanan() {
|
||||
{/* Header */}
|
||||
<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>
|
||||
@@ -137,7 +143,9 @@ function EditTipsKeamanan() {
|
||||
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"
|
||||
@@ -145,7 +153,11 @@ function EditTipsKeamanan() {
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={180}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={48} color={colors["blue-button"]} stroke={1.5} />
|
||||
<IconUpload
|
||||
size={48}
|
||||
color={colors["blue-button"]}
|
||||
stroke={1.5}
|
||||
/>
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={48} color="red" stroke={1.5} />
|
||||
@@ -199,8 +211,10 @@ function EditTipsKeamanan() {
|
||||
<TextInput
|
||||
label="Nama Tips Keamanan"
|
||||
placeholder="Masukkan nama tips keamanan"
|
||||
defaultValue={formData.judul}
|
||||
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
|
||||
value={formData.judul}
|
||||
onChange={(e) =>
|
||||
setFormData((prev) => ({ ...prev, judul: e.target.value }))
|
||||
}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -211,10 +225,9 @@ function EditTipsKeamanan() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }));
|
||||
keamananState.update.form.deskripsi = htmlContent;
|
||||
}}
|
||||
onChange={(htmlContent) =>
|
||||
setFormData((prev) => ({ ...prev, deskripsi: htmlContent }))
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
'use client';
|
||||
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
|
||||
@@ -44,32 +44,18 @@ function EditArtikelKesehatan() {
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState<ArtikelKesehatanFormBase>({
|
||||
title: stateArtikelKesehatan.edit.form.title,
|
||||
content: stateArtikelKesehatan.edit.form.content,
|
||||
imageId: stateArtikelKesehatan.edit.form.imageId,
|
||||
introduction: { content: stateArtikelKesehatan.edit.form.introduction?.content },
|
||||
symptom: {
|
||||
title: stateArtikelKesehatan.edit.form.symptom?.title,
|
||||
content: stateArtikelKesehatan.edit.form.symptom?.content
|
||||
},
|
||||
prevention: {
|
||||
title: stateArtikelKesehatan.edit.form.prevention?.title,
|
||||
content: stateArtikelKesehatan.edit.form.prevention?.content
|
||||
},
|
||||
firstAid: {
|
||||
title: stateArtikelKesehatan.edit.form.firstAid?.title,
|
||||
content: stateArtikelKesehatan.edit.form.firstAid?.content
|
||||
},
|
||||
mythVsFact: {
|
||||
title: stateArtikelKesehatan.edit.form.mythVsFact?.title,
|
||||
mitos: stateArtikelKesehatan.edit.form.mythVsFact?.mitos,
|
||||
fakta: stateArtikelKesehatan.edit.form.mythVsFact?.fakta
|
||||
},
|
||||
doctorSign: {
|
||||
content: stateArtikelKesehatan.edit.form.doctorSign?.content
|
||||
}
|
||||
title: '',
|
||||
content: '',
|
||||
imageId: '',
|
||||
introduction: { content: '' },
|
||||
symptom: { title: '', content: '' },
|
||||
prevention: { title: '', content: '' },
|
||||
firstAid: { title: '', content: '' },
|
||||
mythVsFact: { title: '', mitos: '', fakta: '' },
|
||||
doctorSign: { content: '' },
|
||||
});
|
||||
|
||||
// Load data artikel
|
||||
useEffect(() => {
|
||||
const loadArtikelKesehatan = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -78,74 +64,89 @@ function EditArtikelKesehatan() {
|
||||
try {
|
||||
await stateArtikelKesehatan.edit.load(id);
|
||||
const { form } = stateArtikelKesehatan.edit;
|
||||
if (form) {
|
||||
setFormData({
|
||||
title: form.title,
|
||||
content: form.content,
|
||||
introduction: { content: form.introduction?.content || '' },
|
||||
imageId: form.imageId,
|
||||
symptom: {
|
||||
title: form.symptom?.title || '',
|
||||
content: form.symptom?.content || ''
|
||||
},
|
||||
prevention: {
|
||||
title: form.prevention?.title || '',
|
||||
content: form.prevention?.content || ''
|
||||
},
|
||||
firstAid: {
|
||||
title: form.firstAid?.title || '',
|
||||
content: form.firstAid?.content || ''
|
||||
},
|
||||
mythVsFact: {
|
||||
title: form.mythVsFact?.title || '',
|
||||
mitos: form.mythVsFact?.mitos || '',
|
||||
fakta: form.mythVsFact?.fakta || ''
|
||||
},
|
||||
doctorSign: {
|
||||
content: form.doctorSign?.content || ''
|
||||
}
|
||||
});
|
||||
if (!form) return;
|
||||
|
||||
if (form?.imageId) {
|
||||
setPreviewImage(`${process.env.NEXT_PUBLIC_API_URL}/file/${form.imageId}`);
|
||||
}
|
||||
setFormData({
|
||||
title: form.title || '',
|
||||
content: form.content || '',
|
||||
imageId: form.imageId || '',
|
||||
introduction: { content: form.introduction?.content || '' },
|
||||
symptom: { title: form.symptom?.title || '', content: form.symptom?.content || '' },
|
||||
prevention: { title: form.prevention?.title || '', content: form.prevention?.content || '' },
|
||||
firstAid: { title: form.firstAid?.title || '', content: form.firstAid?.content || '' },
|
||||
mythVsFact: {
|
||||
title: form.mythVsFact?.title || '',
|
||||
mitos: form.mythVsFact?.mitos || '',
|
||||
fakta: form.mythVsFact?.fakta || '',
|
||||
},
|
||||
doctorSign: { content: form.doctorSign?.content || '' },
|
||||
});
|
||||
|
||||
if (form.imageId) {
|
||||
setPreviewImage(`${process.env.NEXT_PUBLIC_API_URL}/file/${form.imageId}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading artikel kesehatan:", error);
|
||||
toast.error("Gagal memuat data artikel kesehatan");
|
||||
console.error('Error loading artikel kesehatan:', error);
|
||||
toast.error('Gagal memuat data artikel kesehatan');
|
||||
}
|
||||
};
|
||||
|
||||
loadArtikelKesehatan();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleFileChange = (files: File[]) => {
|
||||
const selectedFile = files[0];
|
||||
if (!selectedFile) return;
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// Copy formData ke global state
|
||||
stateArtikelKesehatan.edit.form = { ...formData };
|
||||
|
||||
// Upload gambar kalau ada
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file,
|
||||
name: file.name,
|
||||
});
|
||||
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");
|
||||
}
|
||||
|
||||
if (!uploaded?.id) return toast.error('Gagal upload gambar');
|
||||
stateArtikelKesehatan.edit.form.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
const success = await stateArtikelKesehatan.edit.submit();
|
||||
if (success) {
|
||||
toast.success("Artikel kesehatan berhasil diperbarui!");
|
||||
router.push("/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan");
|
||||
toast.success('Artikel kesehatan berhasil diperbarui!');
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating artikel kesehatan:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memperbarui data artikel kesehatan");
|
||||
console.error('Error updating artikel kesehatan:', error);
|
||||
toast.error(error instanceof Error ? error.message : 'Gagal memperbarui data artikel kesehatan');
|
||||
}
|
||||
};
|
||||
|
||||
const InputText = ({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
required,
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
onChange: (v: string) => void;
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
}) => (
|
||||
<TextInput
|
||||
label={label}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
required={required}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header */}
|
||||
@@ -170,34 +171,31 @@ function EditArtikelKesehatan() {
|
||||
style={{ border: '1px solid #e0e0e0' }}
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
{/* Judul */}
|
||||
<InputText
|
||||
label="Judul"
|
||||
value={formData.title}
|
||||
onChange={(value) => setFormData((prev) => ({ ...prev, title: value }))}
|
||||
placeholder="Masukkan judul artikel"
|
||||
defaultValue={formData.title}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
|
||||
required
|
||||
/>
|
||||
|
||||
{/* Gambar */}
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Gambar Berita
|
||||
</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
}
|
||||
}}
|
||||
onReject={() => toast.error("File tidak valid, gunakan format gambar")}
|
||||
onDrop={handleFileChange}
|
||||
onReject={() => toast.error('File tidak valid, gunakan format gambar')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ "image/*": [] }}
|
||||
accept={{ 'image/*': [] }}
|
||||
radius="md"
|
||||
p="xl"
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={180}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={48} color={colors["blue-button"]} stroke={1.5} />
|
||||
<IconUpload size={48} color={colors['blue-button']} stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={48} color="red" stroke={1.5} />
|
||||
@@ -215,64 +213,57 @@ function EditArtikelKesehatan() {
|
||||
</Stack>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm" style={{ display: "flex", justifyContent: "center" }}>
|
||||
<Box mt="sm" style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview Gambar"
|
||||
radius="md"
|
||||
style={{
|
||||
maxHeight: 220,
|
||||
objectFit: "contain",
|
||||
border: `1px solid ${colors["blue-button"]}`,
|
||||
objectFit: 'contain',
|
||||
border: `1px solid ${colors['blue-button']}`,
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<TextInput
|
||||
|
||||
{/* Konten */}
|
||||
<InputText
|
||||
label="Deskripsi"
|
||||
value={formData.content}
|
||||
onChange={(value) => setFormData((prev) => ({ ...prev, content: value }))}
|
||||
placeholder="Masukkan deskripsi artikel"
|
||||
defaultValue={formData.content}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, content: e.target.value }))}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
|
||||
{/* Pendahuluan */}
|
||||
<InputText
|
||||
label="Pendahuluan"
|
||||
placeholder="Masukkan pendahuluan"
|
||||
defaultValue={formData.introduction.content}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
introduction: { ...prev.introduction, content: e.target.value }
|
||||
}))
|
||||
value={formData.introduction.content}
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, introduction: { content: value } }))
|
||||
}
|
||||
placeholder="Masukkan pendahuluan"
|
||||
/>
|
||||
|
||||
{/* Gejala */}
|
||||
<Box>
|
||||
<Text fw="bold">Gejala</Text>
|
||||
<Stack gap="xs">
|
||||
<TextInput
|
||||
<InputText
|
||||
label="Judul Gejala"
|
||||
placeholder="Masukkan judul gejala"
|
||||
defaultValue={formData.symptom.title}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
symptom: { ...prev.symptom, title: e.target.value }
|
||||
}))
|
||||
value={formData.symptom.title}
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, symptom: { ...prev.symptom, title: value } }))
|
||||
}
|
||||
/>
|
||||
<EditEditor
|
||||
value={formData.symptom.content}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
symptom: { ...prev.symptom, content: e }
|
||||
}))
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, symptom: { ...prev.symptom, content: value } }))
|
||||
}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -281,23 +272,17 @@ function EditArtikelKesehatan() {
|
||||
{/* Pencegahan */}
|
||||
<Box>
|
||||
<Text fw="bold">Pencegahan</Text>
|
||||
<TextInput
|
||||
<InputText
|
||||
label="Judul"
|
||||
defaultValue={formData.prevention.title}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
prevention: { ...prev.prevention, title: e.target.value }
|
||||
}))
|
||||
value={formData.prevention.title}
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, prevention: { ...prev.prevention, title: value } }))
|
||||
}
|
||||
/>
|
||||
<EditEditor
|
||||
value={formData.prevention.content}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
prevention: { ...prev.prevention, content: e }
|
||||
}))
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, prevention: { ...prev.prevention, content: value } }))
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
@@ -305,23 +290,17 @@ function EditArtikelKesehatan() {
|
||||
{/* Pertolongan Pertama */}
|
||||
<Box>
|
||||
<Text fw="bold">Pertolongan Pertama</Text>
|
||||
<TextInput
|
||||
<InputText
|
||||
label="Judul"
|
||||
defaultValue={formData.firstAid.title}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
firstAid: { ...prev.firstAid, title: e.target.value }
|
||||
}))
|
||||
value={formData.firstAid.title}
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, firstAid: { ...prev.firstAid, title: value } }))
|
||||
}
|
||||
/>
|
||||
<EditEditor
|
||||
value={formData.firstAid.content}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
firstAid: { ...prev.firstAid, content: e }
|
||||
}))
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, firstAid: { ...prev.firstAid, content: value } }))
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
@@ -329,48 +308,36 @@ function EditArtikelKesehatan() {
|
||||
{/* Mitos vs Fakta */}
|
||||
<Box>
|
||||
<Text fw="bold">Mitos vs Fakta</Text>
|
||||
<TextInput
|
||||
<InputText
|
||||
label="Judul"
|
||||
defaultValue={formData.mythVsFact.title}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
mythVsFact: { ...prev.mythVsFact, title: e.target.value }
|
||||
}))
|
||||
value={formData.mythVsFact.title}
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, mythVsFact: { ...prev.mythVsFact, title: value } }))
|
||||
}
|
||||
/>
|
||||
<Text fw="500">Mitos</Text>
|
||||
<EditEditor
|
||||
value={formData.mythVsFact.mitos}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
mythVsFact: { ...prev.mythVsFact, mitos: e }
|
||||
}))
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, mythVsFact: { ...prev.mythVsFact, mitos: value } }))
|
||||
}
|
||||
/>
|
||||
<Text fw="500">Fakta</Text>
|
||||
<EditEditor
|
||||
value={formData.mythVsFact.fakta}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
mythVsFact: { ...prev.mythVsFact, fakta: e }
|
||||
}))
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, mythVsFact: { ...prev.mythVsFact, fakta: value } }))
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Kapan harus ke dokter */}
|
||||
{/* Dokter */}
|
||||
<Box>
|
||||
<Text fw="bold">Kapan Harus Ke Dokter</Text>
|
||||
<EditEditor
|
||||
value={formData.doctorSign.content}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
doctorSign: { ...prev.doctorSign, content: e }
|
||||
}))
|
||||
onChange={(value) =>
|
||||
setFormData((prev) => ({ ...prev, doctorSign: { content: value } }))
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
@@ -384,7 +351,7 @@ function EditArtikelKesehatan() {
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)'
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
'use client';
|
||||
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||
@@ -43,89 +43,64 @@ interface FasilitasKesehatanFormBase {
|
||||
}
|
||||
|
||||
function EditFasilitasKesehatan() {
|
||||
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
|
||||
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState<FasilitasKesehatanFormBase>({
|
||||
name: stateFasilitasKesehatan.edit.form.name || '',
|
||||
informasiUmum: {
|
||||
fasilitas: stateFasilitasKesehatan.edit.form.informasiUmum?.fasilitas || '',
|
||||
alamat: stateFasilitasKesehatan.edit.form.informasiUmum?.alamat || '',
|
||||
jamOperasional: stateFasilitasKesehatan.edit.form.informasiUmum?.jamOperasional || '',
|
||||
},
|
||||
layananUnggulan: {
|
||||
content: stateFasilitasKesehatan.edit.form.layananUnggulan?.content || '',
|
||||
},
|
||||
dokterdanTenagaMedis: {
|
||||
name: stateFasilitasKesehatan.edit.form.dokterdanTenagaMedis?.name || '',
|
||||
specialist: stateFasilitasKesehatan.edit.form.dokterdanTenagaMedis?.specialist || '',
|
||||
jadwal: stateFasilitasKesehatan.edit.form.dokterdanTenagaMedis?.jadwal || '',
|
||||
},
|
||||
fasilitasPendukung: {
|
||||
content: stateFasilitasKesehatan.edit.form.fasilitasPendukung?.content || '',
|
||||
},
|
||||
prosedurPendaftaran: {
|
||||
content: stateFasilitasKesehatan.edit.form.prosedurPendaftaran?.content || '',
|
||||
},
|
||||
tarifDanLayanan: {
|
||||
layanan: stateFasilitasKesehatan.edit.form.tarifDanLayanan?.layanan || '',
|
||||
tarif: stateFasilitasKesehatan.edit.form.tarifDanLayanan?.tarif || '',
|
||||
},
|
||||
name: '',
|
||||
informasiUmum: { fasilitas: '', alamat: '', jamOperasional: '' },
|
||||
layananUnggulan: { content: '' },
|
||||
dokterdanTenagaMedis: { name: '', specialist: '', jadwal: '' },
|
||||
fasilitasPendukung: { content: '' },
|
||||
prosedurPendaftaran: { content: '' },
|
||||
tarifDanLayanan: { layanan: '', tarif: '' },
|
||||
});
|
||||
|
||||
// Helper untuk update nested state
|
||||
const updateForm = <K extends keyof FasilitasKesehatanFormBase>(
|
||||
key: K,
|
||||
value: FasilitasKesehatanFormBase[K]
|
||||
) => setFormData(prev => ({ ...prev, [key]: value }));
|
||||
|
||||
const updateNested = <
|
||||
K extends keyof FasilitasKesehatanFormBase,
|
||||
N extends keyof FasilitasKesehatanFormBase[K]
|
||||
>(key: K, nestedKey: N, value: FasilitasKesehatanFormBase[K][N]) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[key]: { ...prev[key] as object, [nestedKey]: value },
|
||||
}));
|
||||
|
||||
// Load data
|
||||
useEffect(() => {
|
||||
const loadFasilitasKesehatan = async () => {
|
||||
const load = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
await stateFasilitasKesehatan.edit.load(id);
|
||||
const { form } = stateFasilitasKesehatan.edit;
|
||||
if (form) {
|
||||
setFormData({
|
||||
name: form.name,
|
||||
informasiUmum: {
|
||||
fasilitas: form.informasiUmum?.fasilitas || '',
|
||||
alamat: form.informasiUmum?.alamat || '',
|
||||
jamOperasional: form.informasiUmum?.jamOperasional || '',
|
||||
},
|
||||
layananUnggulan: { content: form.layananUnggulan?.content || '' },
|
||||
dokterdanTenagaMedis: {
|
||||
name: form.dokterdanTenagaMedis?.name || '',
|
||||
specialist: form.dokterdanTenagaMedis?.specialist || '',
|
||||
jadwal: form.dokterdanTenagaMedis?.jadwal || '',
|
||||
},
|
||||
fasilitasPendukung: { content: form.fasilitasPendukung?.content || '' },
|
||||
prosedurPendaftaran: { content: form.prosedurPendaftaran?.content || '' },
|
||||
tarifDanLayanan: {
|
||||
layanan: form.tarifDanLayanan?.layanan || '',
|
||||
tarif: form.tarifDanLayanan?.tarif || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading fasilitas kesehatan:", error);
|
||||
toast.error("Gagal memuat data fasilitas kesehatan");
|
||||
await state.edit.load(id);
|
||||
const form = state.edit.form;
|
||||
if (form) setFormData(form as FasilitasKesehatanFormBase);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Gagal memuat data fasilitas kesehatan');
|
||||
}
|
||||
};
|
||||
loadFasilitasKesehatan();
|
||||
load();
|
||||
}, [params?.id]);
|
||||
|
||||
// Submit
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
stateFasilitasKesehatan.edit.form = {
|
||||
...stateFasilitasKesehatan.edit.form,
|
||||
...formData,
|
||||
};
|
||||
const success = await stateFasilitasKesehatan.edit.submit();
|
||||
state.edit.form = { ...state.edit.form, ...formData };
|
||||
const success = await state.edit.submit();
|
||||
if (success) {
|
||||
toast.success("Fasilitas kesehatan berhasil diperbarui!");
|
||||
router.push("/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan");
|
||||
toast.success('Fasilitas kesehatan berhasil diperbarui!');
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating fasilitas kesehatan:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui data fasilitas kesehatan");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data fasilitas kesehatan');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -156,145 +131,104 @@ function EditFasilitasKesehatan() {
|
||||
<TextInput
|
||||
label="Nama Fasilitas Kesehatan"
|
||||
placeholder="Masukkan nama fasilitas kesehatan"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
||||
value={formData.name}
|
||||
onChange={(e) => updateForm('name', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
{/* Informasi Umum */}
|
||||
<Box>
|
||||
<Text fw="bold" mb={5}>Informasi Umum</Text>
|
||||
<Text fw="bold" mb={5}>
|
||||
Informasi Umum
|
||||
</Text>
|
||||
<TextInput
|
||||
label="Fasilitas"
|
||||
defaultValue={formData.informasiUmum.fasilitas}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
informasiUmum: { ...prev.informasiUmum, fasilitas: e.target.value },
|
||||
}))
|
||||
}
|
||||
value={formData.informasiUmum.fasilitas}
|
||||
onChange={(e) => updateNested('informasiUmum', 'fasilitas', e.target.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
defaultValue={formData.informasiUmum.alamat}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
informasiUmum: { ...prev.informasiUmum, alamat: e.target.value },
|
||||
}))
|
||||
}
|
||||
value={formData.informasiUmum.alamat}
|
||||
onChange={(e) => updateNested('informasiUmum', 'alamat', e.target.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Jam Operasional"
|
||||
defaultValue={formData.informasiUmum.jamOperasional}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
informasiUmum: { ...prev.informasiUmum, jamOperasional: e.target.value },
|
||||
}))
|
||||
}
|
||||
value={formData.informasiUmum.jamOperasional}
|
||||
onChange={(e) => updateNested('informasiUmum', 'jamOperasional', e.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Layanan Unggulan */}
|
||||
<Box>
|
||||
<Text fw="bold" mb={5}>Layanan Unggulan</Text>
|
||||
<Text fw="bold" mb={5}>
|
||||
Layanan Unggulan
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.layananUnggulan.content}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
layananUnggulan: { content: e },
|
||||
}))
|
||||
}
|
||||
onChange={(v) => updateNested('layananUnggulan', 'content', v)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Dokter dan Tenaga Medis */}
|
||||
<Box>
|
||||
<Text fw="bold" mb={5}>Dokter dan Tenaga Medis</Text>
|
||||
<Text fw="bold" mb={5}>
|
||||
Dokter dan Tenaga Medis
|
||||
</Text>
|
||||
<TextInput
|
||||
label="Nama Dokter"
|
||||
defaultValue={formData.dokterdanTenagaMedis.name}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
dokterdanTenagaMedis: { ...prev.dokterdanTenagaMedis, name: e.target.value },
|
||||
}))
|
||||
}
|
||||
value={formData.dokterdanTenagaMedis.name}
|
||||
onChange={(e) => updateNested('dokterdanTenagaMedis', 'name', e.target.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Specialist"
|
||||
defaultValue={formData.dokterdanTenagaMedis.specialist}
|
||||
value={formData.dokterdanTenagaMedis.specialist}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
dokterdanTenagaMedis: { ...prev.dokterdanTenagaMedis, specialist: e.target.value },
|
||||
}))
|
||||
updateNested('dokterdanTenagaMedis', 'specialist', e.target.value)
|
||||
}
|
||||
/>
|
||||
<TextInput
|
||||
label="Jadwal"
|
||||
defaultValue={formData.dokterdanTenagaMedis.jadwal}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
dokterdanTenagaMedis: { ...prev.dokterdanTenagaMedis, jadwal: e.target.value },
|
||||
}))
|
||||
}
|
||||
value={formData.dokterdanTenagaMedis.jadwal}
|
||||
onChange={(e) => updateNested('dokterdanTenagaMedis', 'jadwal', e.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Fasilitas Pendukung */}
|
||||
<Box>
|
||||
<Text fw="bold" mb={5}>Fasilitas Pendukung</Text>
|
||||
<Text fw="bold" mb={5}>
|
||||
Fasilitas Pendukung
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.fasilitasPendukung.content}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
fasilitasPendukung: { content: e },
|
||||
}))
|
||||
}
|
||||
onChange={(v) => updateNested('fasilitasPendukung', 'content', v)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Prosedur Pendaftaran */}
|
||||
<Box>
|
||||
<Text fw="bold" mb={5}>Prosedur Pendaftaran</Text>
|
||||
<Text fw="bold" mb={5}>
|
||||
Prosedur Pendaftaran
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.prosedurPendaftaran.content}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
prosedurPendaftaran: { content: e },
|
||||
}))
|
||||
}
|
||||
onChange={(v) => updateNested('prosedurPendaftaran', 'content', v)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Tarif dan Layanan */}
|
||||
<Box>
|
||||
<Text fw="bold" mb={5}>Tarif dan Layanan</Text>
|
||||
<Text fw="bold" mb={5}>
|
||||
Tarif dan Layanan
|
||||
</Text>
|
||||
<TextInput
|
||||
label="Tarif"
|
||||
defaultValue={formData.tarifDanLayanan.tarif}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
tarifDanLayanan: { ...prev.tarifDanLayanan, tarif: e.target.value },
|
||||
}))
|
||||
}
|
||||
value={formData.tarifDanLayanan.tarif}
|
||||
onChange={(e) => updateNested('tarifDanLayanan', 'tarif', e.target.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Layanan"
|
||||
defaultValue={formData.tarifDanLayanan.layanan}
|
||||
onChange={(e) =>
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
tarifDanLayanan: { ...prev.tarifDanLayanan, layanan: e.target.value },
|
||||
}))
|
||||
}
|
||||
value={formData.tarifDanLayanan.layanan}
|
||||
onChange={(e) => updateNested('tarifDanLayanan', 'layanan', e.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -304,7 +238,7 @@ function EditFasilitasKesehatan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
loading={stateFasilitasKesehatan.edit.loading}
|
||||
loading={state.edit.loading}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
|
||||
import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
@@ -24,13 +25,14 @@ function EditGrafikHasilKepuasan() {
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: editState.update.form.nama || '',
|
||||
tanggal: editState.update.form.tanggal || '',
|
||||
jenisKelamin: editState.update.form.jenisKelamin || '',
|
||||
alamat: editState.update.form.alamat || '',
|
||||
penyakit: editState.update.form.penyakit || '',
|
||||
nama: '',
|
||||
tanggal: '',
|
||||
jenisKelamin: '',
|
||||
alamat: '',
|
||||
penyakit: '',
|
||||
});
|
||||
|
||||
// Load data once
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -38,17 +40,15 @@ function EditGrafikHasilKepuasan() {
|
||||
|
||||
try {
|
||||
const data = await editState.update.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: data.tanggal || '',
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || '',
|
||||
penyakit: data.penyakit || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading grafik hasil kepuasan:", error);
|
||||
if (data) setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: data.tanggal || '',
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || '',
|
||||
penyakit: data.penyakit || '',
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error loading grafik hasil kepuasan:", err);
|
||||
toast.error("Gagal memuat data grafik hasil kepuasan");
|
||||
}
|
||||
};
|
||||
@@ -56,17 +56,19 @@ function EditGrafikHasilKepuasan() {
|
||||
loadData();
|
||||
}, [params?.id]);
|
||||
|
||||
// Generic handler for controlled inputs
|
||||
const handleChange = (field: keyof typeof formData, value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
editState.update.form = {
|
||||
...editState.update.form,
|
||||
...formData,
|
||||
};
|
||||
editState.update.form = { ...editState.update.form, ...formData };
|
||||
await editState.update.submit();
|
||||
toast.success('Grafik hasil kepuasan berhasil diperbarui!');
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan');
|
||||
} catch (error) {
|
||||
console.error('Error updating grafik hasil kepuasan:', error);
|
||||
} catch (err) {
|
||||
console.error('Error updating grafik hasil kepuasan:', err);
|
||||
toast.error('Terjadi kesalahan saat memperbarui grafik hasil kepuasan');
|
||||
}
|
||||
};
|
||||
@@ -100,44 +102,17 @@ function EditGrafikHasilKepuasan() {
|
||||
style={{ border: '1px solid #e0e0e0' }}
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={formData.nama}
|
||||
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
|
||||
label="Nama"
|
||||
placeholder="Masukkan nama"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
type="date"
|
||||
defaultValue={formData.tanggal}
|
||||
onChange={(e) => setFormData({ ...formData, tanggal: e.target.value })}
|
||||
label="Tanggal"
|
||||
placeholder="Masukkan tanggal"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.jenisKelamin}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, jenisKelamin: e.target.value })
|
||||
}
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Masukkan jenis kelamin"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.alamat}
|
||||
onChange={(e) => setFormData({ ...formData, alamat: e.target.value })}
|
||||
label="Alamat"
|
||||
placeholder="Masukkan alamat"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.penyakit}
|
||||
onChange={(e) => setFormData({ ...formData, penyakit: e.target.value })}
|
||||
label="Penyakit"
|
||||
placeholder="Masukkan penyakit"
|
||||
required
|
||||
/>
|
||||
{(['nama','tanggal','jenisKelamin','alamat','penyakit'] as const).map((field) => (
|
||||
<TextInput
|
||||
key={field}
|
||||
value={formData[field]}
|
||||
onChange={(e) => handleChange(field, e.target.value)}
|
||||
type={field === 'tanggal' ? 'date' : 'text'}
|
||||
label={field === 'jenisKelamin' ? 'Jenis Kelamin' : field.charAt(0).toUpperCase() + field.slice(1)}
|
||||
placeholder={`Masukkan ${field}`}
|
||||
required
|
||||
/>
|
||||
))}
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
|
||||
@@ -4,17 +4,7 @@
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import jadwalKegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -29,46 +19,49 @@ interface JadwalKegiatanFormBase {
|
||||
waktu: string;
|
||||
lokasi: string;
|
||||
};
|
||||
deskripsiJadwalKegiatan: {
|
||||
deskripsi: string;
|
||||
};
|
||||
layananJadwalKegiatan: {
|
||||
content: string;
|
||||
};
|
||||
syaratKetentuanJadwalKegiatan: {
|
||||
content: string;
|
||||
};
|
||||
dokumenJadwalKegiatan: {
|
||||
content: string;
|
||||
};
|
||||
deskripsiJadwalKegiatan: { deskripsi: string };
|
||||
layananJadwalKegiatan: { content: string };
|
||||
syaratKetentuanJadwalKegiatan: { content: string };
|
||||
dokumenJadwalKegiatan: { content: string };
|
||||
}
|
||||
|
||||
const emptyForm = (): JadwalKegiatanFormBase => ({
|
||||
content: '',
|
||||
informasiJadwalKegiatan: { name: '', tanggal: '', waktu: '', lokasi: '' },
|
||||
deskripsiJadwalKegiatan: { deskripsi: '' },
|
||||
layananJadwalKegiatan: { content: '' },
|
||||
syaratKetentuanJadwalKegiatan: { content: '' },
|
||||
dokumenJadwalKegiatan: { content: '' },
|
||||
});
|
||||
|
||||
function EditJadwalKegiatan() {
|
||||
const stateJadwalKegiatan = useProxy(jadwalKegiatanState);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState<JadwalKegiatanFormBase>({
|
||||
content: stateJadwalKegiatan.edit.form.content || '',
|
||||
informasiJadwalKegiatan: {
|
||||
name: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.name || '',
|
||||
tanggal: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.tanggal || '',
|
||||
waktu: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.waktu || '',
|
||||
lokasi: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.lokasi || '',
|
||||
},
|
||||
deskripsiJadwalKegiatan: {
|
||||
deskripsi: stateJadwalKegiatan.edit.form.deskripsiJadwalKegiatan?.deskripsi || '',
|
||||
},
|
||||
layananJadwalKegiatan: {
|
||||
content: stateJadwalKegiatan.edit.form.layananJadwalKegiatan?.content || '',
|
||||
},
|
||||
syaratKetentuanJadwalKegiatan: {
|
||||
content: stateJadwalKegiatan.edit.form.syaratKetentuanJadwalKegiatan?.content || '',
|
||||
},
|
||||
dokumenJadwalKegiatan: {
|
||||
content: stateJadwalKegiatan.edit.form.dokumenJadwalKegiatan?.content || '',
|
||||
},
|
||||
});
|
||||
const [formData, setFormData] = useState<JadwalKegiatanFormBase>(emptyForm());
|
||||
|
||||
// Helper untuk update nested state
|
||||
const updateNested = <
|
||||
K extends keyof JadwalKegiatanFormBase,
|
||||
N extends keyof JadwalKegiatanFormBase[K]
|
||||
>(
|
||||
key: K,
|
||||
subKey: N,
|
||||
value: string
|
||||
) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[key]: {
|
||||
...(prev[key] as Record<string, unknown>),
|
||||
[subKey]: value
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const updateSimple = (key: keyof JadwalKegiatanFormBase, value: string) => {
|
||||
setFormData(prev => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadJadwalKegiatan = async () => {
|
||||
@@ -80,25 +73,17 @@ function EditJadwalKegiatan() {
|
||||
const { form } = stateJadwalKegiatan.edit;
|
||||
if (form) {
|
||||
setFormData({
|
||||
content: form.content,
|
||||
content: form.content || '',
|
||||
informasiJadwalKegiatan: {
|
||||
name: form.informasiJadwalKegiatan?.name || '',
|
||||
tanggal: form.informasiJadwalKegiatan?.tanggal || '',
|
||||
waktu: form.informasiJadwalKegiatan?.waktu || '',
|
||||
lokasi: form.informasiJadwalKegiatan?.lokasi || '',
|
||||
},
|
||||
deskripsiJadwalKegiatan: {
|
||||
deskripsi: form.deskripsiJadwalKegiatan?.deskripsi || '',
|
||||
},
|
||||
layananJadwalKegiatan: {
|
||||
content: form.layananJadwalKegiatan?.content || '',
|
||||
},
|
||||
syaratKetentuanJadwalKegiatan: {
|
||||
content: form.syaratKetentuanJadwalKegiatan?.content || '',
|
||||
},
|
||||
dokumenJadwalKegiatan: {
|
||||
content: form.dokumenJadwalKegiatan?.content || '',
|
||||
},
|
||||
deskripsiJadwalKegiatan: { deskripsi: form.deskripsiJadwalKegiatan?.deskripsi || '' },
|
||||
layananJadwalKegiatan: { content: form.layananJadwalKegiatan?.content || '' },
|
||||
syaratKetentuanJadwalKegiatan: { content: form.syaratKetentuanJadwalKegiatan?.content || '' },
|
||||
dokumenJadwalKegiatan: { content: form.dokumenJadwalKegiatan?.content || '' },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -111,16 +96,7 @@ function EditJadwalKegiatan() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
stateJadwalKegiatan.edit.form = {
|
||||
...stateJadwalKegiatan.edit.form,
|
||||
content: formData.content,
|
||||
informasiJadwalKegiatan: { ...formData.informasiJadwalKegiatan },
|
||||
deskripsiJadwalKegiatan: { ...formData.deskripsiJadwalKegiatan },
|
||||
layananJadwalKegiatan: { ...formData.layananJadwalKegiatan },
|
||||
syaratKetentuanJadwalKegiatan: { ...formData.syaratKetentuanJadwalKegiatan },
|
||||
dokumenJadwalKegiatan: { ...formData.dokumenJadwalKegiatan }
|
||||
};
|
||||
|
||||
stateJadwalKegiatan.edit.form = { ...stateJadwalKegiatan.edit.form, ...formData };
|
||||
const success = await stateJadwalKegiatan.edit.submit();
|
||||
if (success) {
|
||||
toast.success("Jadwal kegiatan berhasil diperbarui!");
|
||||
@@ -160,8 +136,8 @@ function EditJadwalKegiatan() {
|
||||
<TextInput
|
||||
label="Nama Jadwal Kegiatan"
|
||||
placeholder="Masukkan nama jadwal kegiatan"
|
||||
defaultValue={formData.content}
|
||||
onChange={(e) => setFormData((prev) => ({ ...prev, content: e.target.value }))}
|
||||
value={formData.content}
|
||||
onChange={(e) => updateSimple('content', e.target.value)}
|
||||
/>
|
||||
|
||||
{/* Deskripsi */}
|
||||
@@ -169,45 +145,22 @@ function EditJadwalKegiatan() {
|
||||
<Text fz="sm" fw="bold">Deskripsi Jadwal Kegiatan</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsiJadwalKegiatan.deskripsi}
|
||||
onChange={(val) => setFormData((prev) => ({
|
||||
...prev,
|
||||
deskripsiJadwalKegiatan: { deskripsi: val }
|
||||
}))}
|
||||
onChange={(val) => updateNested('deskripsiJadwalKegiatan', 'deskripsi', val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Informasi Jadwal */}
|
||||
<Box>
|
||||
<Text fz="md" fw="bold">Informasi Jadwal Kegiatan</Text>
|
||||
<TextInput
|
||||
label="Nama"
|
||||
defaultValue={formData.informasiJadwalKegiatan.name}
|
||||
onChange={(e) => setFormData((prev) => ({
|
||||
...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, name: e.target.value }
|
||||
}))}
|
||||
/>
|
||||
<TextInput
|
||||
type="date"
|
||||
label="Tanggal"
|
||||
defaultValue={formData.informasiJadwalKegiatan.tanggal}
|
||||
onChange={(e) => setFormData((prev) => ({
|
||||
...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, tanggal: e.target.value }
|
||||
}))}
|
||||
/>
|
||||
<TextInput
|
||||
label="Waktu"
|
||||
defaultValue={formData.informasiJadwalKegiatan.waktu}
|
||||
onChange={(e) => setFormData((prev) => ({
|
||||
...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, waktu: e.target.value }
|
||||
}))}
|
||||
/>
|
||||
<TextInput
|
||||
label="Lokasi"
|
||||
defaultValue={formData.informasiJadwalKegiatan.lokasi}
|
||||
onChange={(e) => setFormData((prev) => ({
|
||||
...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, lokasi: e.target.value }
|
||||
}))}
|
||||
/>
|
||||
{(['name', 'tanggal', 'waktu', 'lokasi'] as const).map((field) => (
|
||||
<TextInput
|
||||
key={field}
|
||||
label={field === 'name' ? 'Nama' : field.charAt(0).toUpperCase() + field.slice(1)}
|
||||
type={field === 'tanggal' ? 'date' : 'text'}
|
||||
value={formData.informasiJadwalKegiatan[field]}
|
||||
onChange={(e) => updateNested('informasiJadwalKegiatan', field, e.target.value)}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{/* Layanan */}
|
||||
@@ -215,10 +168,7 @@ function EditJadwalKegiatan() {
|
||||
<Text fz="md" fw="bold">Layanan Jadwal Kegiatan</Text>
|
||||
<EditEditor
|
||||
value={formData.layananJadwalKegiatan.content}
|
||||
onChange={(val) => setFormData((prev) => ({
|
||||
...prev,
|
||||
layananJadwalKegiatan: { content: val }
|
||||
}))}
|
||||
onChange={(val) => updateNested('layananJadwalKegiatan', 'content', val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -227,10 +177,7 @@ function EditJadwalKegiatan() {
|
||||
<Text fz="md" fw="bold">Syarat dan Ketentuan</Text>
|
||||
<EditEditor
|
||||
value={formData.syaratKetentuanJadwalKegiatan.content}
|
||||
onChange={(val) => setFormData((prev) => ({
|
||||
...prev,
|
||||
syaratKetentuanJadwalKegiatan: { content: val }
|
||||
}))}
|
||||
onChange={(val) => updateNested('syaratKetentuanJadwalKegiatan', 'content', val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -239,10 +186,7 @@ function EditJadwalKegiatan() {
|
||||
<Text fz="md" fw="bold">Dokumen Yang Perlu Dibawa</Text>
|
||||
<EditEditor
|
||||
value={formData.dokumenJadwalKegiatan.content}
|
||||
onChange={(val) => setFormData((prev) => ({
|
||||
...prev,
|
||||
dokumenJadwalKegiatan: { content: val }
|
||||
}))}
|
||||
onChange={(val) => updateNested('dokumenJadwalKegiatan', 'content', val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
'use client';
|
||||
|
||||
import persentaseKelahiranKematian from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Paper,
|
||||
Stack,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Paper,
|
||||
Stack,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
@@ -18,146 +19,130 @@ import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function EditKelahiran() {
|
||||
const editState = useProxy(persentaseKelahiranKematian.kelahiran);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const editState = useProxy(persentaseKelahiranKematian.kelahiran);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: '',
|
||||
tanggal: '',
|
||||
jenisKelamin: '',
|
||||
alamat: '',
|
||||
});
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: editState.edit.form.nama || '',
|
||||
tanggal: editState.edit.form.tanggal || '',
|
||||
jenisKelamin: editState.edit.form.jenisKelamin || '',
|
||||
alamat: editState.edit.form.alamat || '',
|
||||
});
|
||||
// Load data saat mount atau params.id berubah
|
||||
useEffect(() => {
|
||||
const loadKelahiran = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await editState.edit.load(id);
|
||||
if (data) setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: data.tanggal || '',
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || ''
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error loading data kelahiran:', error);
|
||||
toast.error('Gagal memuat data kelahiran');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadKelahiran = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
loadKelahiran();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange = (key: keyof typeof formData, value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
try {
|
||||
const data = await editState.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: data.tanggal || '',
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading data kelahiran:', error);
|
||||
toast.error('Gagal memuat data kelahiran');
|
||||
}
|
||||
};
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// Update global state hanya saat submit
|
||||
editState.edit.form = { ...editState.edit.form, ...formData };
|
||||
await editState.edit.update();
|
||||
toast.success('Data kelahiran berhasil diperbarui!');
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran');
|
||||
} catch (error) {
|
||||
console.error('Error updating data kelahiran:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data kelahiran');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
Edit Data Kelahiran
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
loadKelahiran();
|
||||
}, [params?.id]);
|
||||
{/* Form */}
|
||||
<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
|
||||
value={formData.nama}
|
||||
onChange={(e) => handleChange('nama', e.target.value)}
|
||||
label="Nama"
|
||||
placeholder="Masukkan nama"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
type="date"
|
||||
value={formData.tanggal}
|
||||
onChange={(e) => handleChange('tanggal', e.target.value)}
|
||||
label="Tanggal"
|
||||
placeholder="Masukkan tanggal"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
value={formData.jenisKelamin}
|
||||
onChange={(e) => handleChange('jenisKelamin', e.target.value)}
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Masukkan jenis kelamin"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
value={formData.alamat}
|
||||
onChange={(e) => handleChange('alamat', e.target.value)}
|
||||
label="Alamat"
|
||||
placeholder="Masukkan alamat"
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
editState.edit.form = {
|
||||
...editState.edit.form,
|
||||
nama: formData.nama,
|
||||
tanggal: formData.tanggal,
|
||||
jenisKelamin: formData.jenisKelamin,
|
||||
alamat: formData.alamat,
|
||||
};
|
||||
|
||||
|
||||
await editState.edit.update();
|
||||
toast.success('Data kelahiran berhasil diperbarui!');
|
||||
router.push(
|
||||
'/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran'
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error updating data kelahiran:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data kelahiran');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
Edit Data Kelahiran
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
|
||||
{/* Form */}
|
||||
<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
|
||||
defaultValue={formData.nama}
|
||||
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
|
||||
label="Nama"
|
||||
placeholder="Masukkan nama"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
type="date"
|
||||
defaultValue={formData.tanggal}
|
||||
onChange={(e) => setFormData({ ...formData, tanggal: e.target.value })}
|
||||
label="Tanggal"
|
||||
placeholder="Masukkan tanggal"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.jenisKelamin}
|
||||
onChange={(e) => setFormData({ ...formData, jenisKelamin: e.target.value })}
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Masukkan jenis kelamin"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={formData.alamat}
|
||||
onChange={(e) => setFormData({ ...formData, alamat: e.target.value })}
|
||||
label="Alamat"
|
||||
placeholder="Masukkan alamat"
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
<Group justify="right">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default EditKelahiran;
|
||||
export default EditKelahiran;
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import persentaseKelahiranKematian from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
@@ -20,160 +21,149 @@ import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function EditKematian() {
|
||||
const editState = useProxy(persentaseKelahiranKematian.kematian);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const editState = useProxy(persentaseKelahiranKematian.kematian);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: '',
|
||||
tanggal: '',
|
||||
jenisKelamin: '',
|
||||
alamat: '',
|
||||
penyebab: '',
|
||||
});
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: editState.edit.form.nama || '',
|
||||
tanggal: editState.edit.form.tanggal || '',
|
||||
jenisKelamin: editState.edit.form.jenisKelamin || '',
|
||||
alamat: editState.edit.form.alamat || '',
|
||||
penyebab: editState.edit.form.penyebab || '',
|
||||
});
|
||||
// Load data saat mount
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await editState.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: data.tanggal || '',
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || '',
|
||||
penyebab: data.penyebab || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading data kematian:', error);
|
||||
toast.error('Gagal memuat data kematian');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
loadData();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange = (key: keyof typeof formData, value: string) => {
|
||||
setFormData(prev => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
try {
|
||||
const data = await editState.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: data.tanggal || '',
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || '',
|
||||
penyebab: data.penyebab || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading data kematian:', error);
|
||||
toast.error('Gagal memuat data kematian');
|
||||
}
|
||||
};
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// Update global state saat submit
|
||||
editState.edit.form = { ...editState.edit.form, ...formData };
|
||||
await editState.edit.update();
|
||||
toast.success('Data kematian berhasil diperbarui!');
|
||||
router.push(
|
||||
'/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian'
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error updating data kematian:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data kematian');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
Edit Data Kematian
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
loadData();
|
||||
}, [params?.id]);
|
||||
{/* Form Card */}
|
||||
<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="Nama"
|
||||
placeholder="Masukkan nama"
|
||||
value={formData.nama}
|
||||
onChange={(e) => handleChange('nama', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
type="date"
|
||||
label="Tanggal"
|
||||
placeholder="Masukkan tanggal"
|
||||
value={formData.tanggal}
|
||||
onChange={(e) => handleChange('tanggal', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
editState.edit.form = { ...editState.edit.form, ...formData };
|
||||
await editState.edit.update();
|
||||
toast.success('Data kematian berhasil diperbarui!');
|
||||
router.push(
|
||||
'/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian'
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error updating data kematian:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data kematian');
|
||||
}
|
||||
};
|
||||
<TextInput
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Masukkan jenis kelamin"
|
||||
value={formData.jenisKelamin}
|
||||
onChange={(e) => handleChange('jenisKelamin', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
placeholder="Masukkan alamat"
|
||||
value={formData.alamat}
|
||||
onChange={(e) => handleChange('alamat', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header dengan tombol back */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
Edit Data Kematian
|
||||
</Title>
|
||||
</Group>
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold" mb={6}>
|
||||
Penyebab
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.penyebab}
|
||||
onChange={(htmlContent) => handleChange('penyebab', htmlContent)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
{/* Card Form */}
|
||||
<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="Nama"
|
||||
placeholder="Masukkan nama"
|
||||
defaultValue={formData.nama}
|
||||
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
<TextInput
|
||||
type="date"
|
||||
label="Tanggal"
|
||||
placeholder="Masukkan tanggal"
|
||||
defaultValue={formData.tanggal}
|
||||
onChange={(e) => setFormData({ ...formData, tanggal: e.target.value })}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
<TextInput
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Masukkan jenis kelamin"
|
||||
defaultValue={formData.jenisKelamin}
|
||||
onChange={(e) => setFormData({ ...formData, jenisKelamin: e.target.value })}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
placeholder="Masukkan alamat"
|
||||
defaultValue={formData.alamat}
|
||||
onChange={(e) => setFormData({ ...formData, alamat: e.target.value })}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold" mb={6}>
|
||||
Penyebab
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.penyebab}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData((prev) => ({ ...prev, penyebab: htmlContent }));
|
||||
editState.edit.form.penyebab = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
<Group justify="right">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default EditKematian;
|
||||
export default EditKematian;
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState, ChangeEvent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
@@ -28,15 +28,21 @@ function EditInfoWabahPenyakit() {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
deskripsiSingkat: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
});
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
name: infoWabahPenyakitState.edit.form.name || '',
|
||||
deskripsiSingkat: infoWabahPenyakitState.edit.form.deskripsiSingkat || '',
|
||||
deskripsi: infoWabahPenyakitState.edit.form.deskripsiLengkap || '',
|
||||
imageId: infoWabahPenyakitState.edit.form.imageId || '',
|
||||
});
|
||||
|
||||
// Helper untuk update field formData
|
||||
const updateField = (field: keyof typeof formData, value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
// Load data edit
|
||||
useEffect(() => {
|
||||
const loadInfoWabahPenyakit = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -52,12 +58,10 @@ function EditInfoWabahPenyakit() {
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
if (data.image?.link) setPreviewImage(data.image.link);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading info wabah penyakit:', error);
|
||||
console.error(error);
|
||||
toast.error('Gagal memuat data info wabah penyakit');
|
||||
}
|
||||
};
|
||||
@@ -67,34 +71,43 @@ function EditInfoWabahPenyakit() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
let uploadedImageId = formData.imageId;
|
||||
|
||||
// Upload file kalau ada
|
||||
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');
|
||||
uploadedImageId = uploaded.id;
|
||||
}
|
||||
|
||||
// Update global state
|
||||
infoWabahPenyakitState.edit.form = {
|
||||
...infoWabahPenyakitState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsiSingkat: formData.deskripsiSingkat,
|
||||
deskripsiLengkap: formData.deskripsi,
|
||||
imageId: formData.imageId,
|
||||
imageId: uploadedImageId,
|
||||
};
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
infoWabahPenyakitState.edit.form.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
await infoWabahPenyakitState.edit.update();
|
||||
toast.success('Info wabah penyakit berhasil diperbarui!');
|
||||
router.push('/admin/kesehatan/info-wabah-penyakit');
|
||||
} catch (error) {
|
||||
console.error('Error updating info wabah penyakit:', error);
|
||||
console.error(error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui info wabah penyakit');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDrop = (files: File[]) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header */}
|
||||
@@ -120,16 +133,18 @@ function EditInfoWabahPenyakit() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => updateField('name', e.target.value)}
|
||||
label="Judul"
|
||||
placeholder="Masukkan judul"
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
defaultValue={formData.deskripsiSingkat}
|
||||
onChange={(e) => setFormData({ ...formData, deskripsiSingkat: e.target.value })}
|
||||
value={formData.deskripsiSingkat}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
updateField('deskripsiSingkat', e.target.value)
|
||||
}
|
||||
label="Deskripsi Singkat"
|
||||
placeholder="Masukkan deskripsi singkat"
|
||||
required
|
||||
@@ -141,7 +156,7 @@ function EditInfoWabahPenyakit() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => setFormData({ ...formData, deskripsi: val })}
|
||||
onChange={(val) => updateField('deskripsi', val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -150,13 +165,7 @@ function EditInfoWabahPenyakit() {
|
||||
Gambar
|
||||
</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
}
|
||||
}}
|
||||
onDrop={handleDrop}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
'use client';
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import kontakDarurat from '@/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat';
|
||||
import colors from '@/con/colors';
|
||||
@@ -23,7 +22,6 @@ import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function EditKontakDarurat() {
|
||||
const kontakDaruratState = useProxy(kontakDarurat);
|
||||
const router = useRouter();
|
||||
@@ -32,11 +30,13 @@ function EditKontakDarurat() {
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
name: kontakDaruratState.edit.form.name || '',
|
||||
deskripsi: kontakDaruratState.edit.form.deskripsi || '',
|
||||
imageId: kontakDaruratState.edit.form.imageId || '',
|
||||
name: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Load data sekali saat mount
|
||||
useEffect(() => {
|
||||
const loadKontakDarurat = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -50,40 +50,38 @@ function EditKontakDarurat() {
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
if (data?.image?.link) setPreviewImage(data.image.link);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kontak darurat:", error);
|
||||
toast.error("Gagal memuat data kontak darurat");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadKontakDarurat();
|
||||
}, [params?.id]);
|
||||
}, [params?.id, kontakDaruratState.edit]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
kontakDaruratState.edit.form = {
|
||||
...kontakDaruratState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsi: formData.deskripsi,
|
||||
imageId: formData.imageId,
|
||||
};
|
||||
let imageId = formData.imageId;
|
||||
|
||||
// Upload file baru jika ada
|
||||
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");
|
||||
}
|
||||
|
||||
kontakDaruratState.edit.form.imageId = uploaded.id;
|
||||
if (!uploaded?.id) return toast.error("Gagal upload gambar");
|
||||
imageId = uploaded.id;
|
||||
}
|
||||
|
||||
// Update global state sekaligus submit
|
||||
kontakDaruratState.edit.form = {
|
||||
...kontakDaruratState.edit.form,
|
||||
...formData,
|
||||
imageId,
|
||||
};
|
||||
|
||||
await kontakDaruratState.edit.update();
|
||||
toast.success("Kontak darurat berhasil diperbarui!");
|
||||
router.push("/admin/kesehatan/kontak-darurat");
|
||||
@@ -93,9 +91,10 @@ function EditKontakDarurat() {
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <Text>Loading...</Text>;
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
@@ -107,7 +106,6 @@ function EditKontakDarurat() {
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
{/* Form */}
|
||||
<Paper
|
||||
w={{ base: '100%', md: '50%' }}
|
||||
bg={colors['white-1']}
|
||||
@@ -117,9 +115,10 @@ function EditKontakDarurat() {
|
||||
style={{ border: '1px solid #e0e0e0' }}
|
||||
>
|
||||
<Stack gap="md">
|
||||
{/* Controlled Input */}
|
||||
<TextInput
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
||||
label="Judul"
|
||||
placeholder="Masukkan judul"
|
||||
required
|
||||
@@ -129,7 +128,7 @@ function EditKontakDarurat() {
|
||||
<Text fz="sm" fw="bold">Deskripsi</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => setFormData({ ...formData, deskripsi: val })}
|
||||
onChange={(val) => setFormData(prev => ({ ...prev, deskripsi: val }))}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penanganan-darurat/penangananDarurat';
|
||||
import colors from '@/con/colors';
|
||||
@@ -28,16 +29,19 @@ function EditPenangananDarurat() {
|
||||
const router = useRouter();
|
||||
const params = useParams()
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
});
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
name: penangananDaruratState.edit.form.name || '',
|
||||
deskripsi: penangananDaruratState.edit.form.deskripsi || '',
|
||||
imageId: penangananDaruratState.edit.form.imageId || '',
|
||||
})
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Load data satu kali saat component mount
|
||||
useEffect(() => {
|
||||
const loadPenangananDarurat = async () => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
@@ -48,49 +52,66 @@ function EditPenangananDarurat() {
|
||||
name: data.name || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
})
|
||||
});
|
||||
|
||||
if (data?.image?.link) {
|
||||
if (data.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading penanganan darurat:', error);
|
||||
} catch (err) {
|
||||
console.error('Error loading penanganan darurat:', err);
|
||||
toast.error('Gagal memuat data penanganan darurat');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
loadPenangananDarurat();
|
||||
}, [params?.id])
|
||||
loadData();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleChange = (key: keyof typeof formData, value: string) => {
|
||||
setFormData(prev => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
const handleDrop = (files: File[]) => {
|
||||
const selected = files[0];
|
||||
if (!selected) return;
|
||||
|
||||
setFile(selected);
|
||||
setPreviewImage(URL.createObjectURL(selected));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
penangananDaruratState.edit.form = {
|
||||
...penangananDaruratState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsi: formData.deskripsi,
|
||||
imageId: formData.imageId,
|
||||
}
|
||||
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");
|
||||
}
|
||||
if (!uploaded?.id) return toast.error("Gagal upload gambar");
|
||||
|
||||
penangananDaruratState.edit.form.imageId = uploaded.id;
|
||||
imageId = uploaded.id;
|
||||
}
|
||||
|
||||
// update global state sekali saat submit
|
||||
penangananDaruratState.edit.form = {
|
||||
...penangananDaruratState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsi: formData.deskripsi,
|
||||
imageId,
|
||||
};
|
||||
|
||||
await penangananDaruratState.edit.update();
|
||||
toast.success("Penanganan darurat berhasil diperbarui!");
|
||||
router.push("/admin/kesehatan/penanganan-darurat");
|
||||
} catch (error) {
|
||||
console.error("Error updating penanganan darurat:", error);
|
||||
} catch (err) {
|
||||
console.error("Error updating penanganan darurat:", err);
|
||||
toast.error("Gagal memperbarui data penanganan darurat");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <Text>Loading...</Text>;
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
@@ -117,8 +138,8 @@ function EditPenangananDarurat() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) => handleChange('name', e.target.value)}
|
||||
label="Judul"
|
||||
placeholder="Masukkan judul"
|
||||
required
|
||||
@@ -128,20 +149,14 @@ function EditPenangananDarurat() {
|
||||
<Text fz="sm" fw="bold">Deskripsi</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => setFormData({ ...formData, deskripsi: val })}
|
||||
onChange={(val) => handleChange('deskripsi', val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold">Gambar</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
}
|
||||
}}
|
||||
onDrop={handleDrop}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
'use client';
|
||||
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import posyandustate from '@/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
|
||||
@@ -23,234 +24,211 @@ import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function EditPosyandu() {
|
||||
const statePosyandu = useProxy(posyandustate);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const statePosyandu = useProxy(posyandustate);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
nomor: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
jadwalPelayanan: '',
|
||||
});
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
name: statePosyandu.edit.form.name || '',
|
||||
nomor: statePosyandu.edit.form.nomor || '',
|
||||
deskripsi: statePosyandu.edit.form.deskripsi || '',
|
||||
imageId: statePosyandu.edit.form.imageId || '',
|
||||
jadwalPelayanan: statePosyandu.edit.form.jadwalPelayanan || '',
|
||||
});
|
||||
// Load data posyandu
|
||||
useEffect(() => {
|
||||
const loadPosyandu = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await statePosyandu.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
nomor: data.nomor || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
jadwalPelayanan: data.jadwalPelayanan || '',
|
||||
});
|
||||
if (data?.image?.link) setPreviewImage(data.image.link);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading posyandu:', error);
|
||||
toast.error('Gagal memuat data posyandu');
|
||||
}
|
||||
};
|
||||
loadPosyandu();
|
||||
}, [params?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadPosyandu = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// Update global state hanya saat submit
|
||||
const updatedForm = { ...statePosyandu.edit.form, ...formData };
|
||||
|
||||
// Upload file jika ada
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||
const uploaded = res.data?.data;
|
||||
|
||||
try {
|
||||
const data = await statePosyandu.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
nomor: data.nomor || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
jadwalPelayanan: data.jadwalPelayanan || '',
|
||||
});
|
||||
if (!uploaded?.id) return toast.error('Gagal upload gambar');
|
||||
updatedForm.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
statePosyandu.edit.form = updatedForm;
|
||||
await statePosyandu.edit.update();
|
||||
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading posyandu:", error);
|
||||
toast.error("Gagal memuat data posyandu");
|
||||
}
|
||||
};
|
||||
loadPosyandu();
|
||||
}, [params?.id]);
|
||||
toast.success('Posyandu berhasil diperbarui!');
|
||||
router.push('/admin/kesehatan/posyandu');
|
||||
} catch (error) {
|
||||
console.error('Error updating posyandu:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui posyandu');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Tombol Back */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
Edit Posyandu
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
statePosyandu.edit.form = {
|
||||
...statePosyandu.edit.form,
|
||||
...formData,
|
||||
};
|
||||
{/* Card utama */}
|
||||
<Paper
|
||||
w={{ base: '100%', md: '50%' }}
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
shadow="sm"
|
||||
style={{ border: '1px solid #e0e0e0' }}
|
||||
>
|
||||
<Stack gap="md">
|
||||
{/* Upload Gambar */}
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Gambar Posyandu
|
||||
</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
}
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid, gunakan format gambar')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
radius="md"
|
||||
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>
|
||||
<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>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm" style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview Gambar"
|
||||
radius="md"
|
||||
style={{
|
||||
maxHeight: 220,
|
||||
objectFit: 'contain',
|
||||
border: `1px solid ${colors['blue-button']}`,
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||
const uploaded = res.data?.data;
|
||||
{/* Input Form */}
|
||||
<TextInput
|
||||
label="Nama Posyandu"
|
||||
placeholder="Masukkan nama posyandu"
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Nomor Posyandu"
|
||||
placeholder="Masukkan nomor posyandu"
|
||||
value={formData.nomor}
|
||||
onChange={(e) => setFormData({ ...formData, nomor: e.target.value })}
|
||||
required
|
||||
/>
|
||||
|
||||
if (!uploaded?.id) {
|
||||
return toast.error("Gagal upload gambar");
|
||||
}
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Deskripsi Posyandu
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => setFormData({ ...formData, deskripsi: htmlContent })}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Jadwal Pelayanan
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.jadwalPelayanan}
|
||||
onChange={(htmlContent) =>
|
||||
setFormData({ ...formData, jadwalPelayanan: htmlContent })
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
statePosyandu.edit.form.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
|
||||
await statePosyandu.edit.update();
|
||||
toast.success("Posyandu berhasil diperbarui!");
|
||||
router.push("/admin/kesehatan/posyandu");
|
||||
} catch (error) {
|
||||
console.error("Error updating posyandu:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui posyandu");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Tombol Back */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
Edit Posyandu
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
|
||||
{/* Card utama */}
|
||||
<Paper
|
||||
w={{ base: '100%', md: '50%' }}
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
shadow="sm"
|
||||
style={{ border: '1px solid #e0e0e0' }}
|
||||
>
|
||||
<Stack gap="md">
|
||||
{/* Upload Gambar */}
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Gambar Posyandu
|
||||
</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
}
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid, gunakan format gambar')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
radius="md"
|
||||
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>
|
||||
<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>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm" style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview Gambar"
|
||||
radius="md"
|
||||
style={{
|
||||
maxHeight: 220,
|
||||
objectFit: 'contain',
|
||||
border: `1px solid ${colors['blue-button']}`,
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
|
||||
{/* Input Form */}
|
||||
<TextInput
|
||||
label="Nama Posyandu"
|
||||
placeholder="Masukkan nama posyandu"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
<TextInput
|
||||
label="Nomor Posyandu"
|
||||
placeholder="Masukkan nomor posyandu"
|
||||
defaultValue={formData.nomor}
|
||||
onChange={(e) => setFormData({ ...formData, nomor: e.target.value })}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>Deskripsi Posyandu</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData({ ...formData, deskripsi: htmlContent });
|
||||
statePosyandu.edit.form.deskripsi = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>Jadwal Pelayanan</Text>
|
||||
<EditEditor
|
||||
value={formData.jadwalPelayanan}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData({ ...formData, jadwalPelayanan: htmlContent });
|
||||
statePosyandu.edit.form.jadwalPelayanan = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
{/* Tombol Submit */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
{/* Tombol Submit */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default EditPosyandu;
|
||||
export default EditPosyandu;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
'use client';
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import programKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/program-kesehatan/programKesehatan';
|
||||
import colors from '@/con/colors';
|
||||
@@ -31,73 +31,70 @@ function EditProgramKesehatan() {
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
name: programKesehatanState.edit.form.name || '',
|
||||
deskripsiSingkat: programKesehatanState.edit.form.deskripsiSingkat || '',
|
||||
deskripsi: programKesehatanState.edit.form.deskripsi || '',
|
||||
imageId: programKesehatanState.edit.form.imageId || '',
|
||||
name: '',
|
||||
deskripsiSingkat: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
});
|
||||
|
||||
// Load data awal
|
||||
useEffect(() => {
|
||||
const loadProgramKesehatan = async () => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await programKesehatanState.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
deskripsiSingkat: data.deskripsiSingkat || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
if (!data) return;
|
||||
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading program kesehatan:', error);
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
deskripsiSingkat: data.deskripsiSingkat || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
|
||||
if (data?.image?.link) setPreviewImage(data.image.link);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Gagal memuat data program kesehatan');
|
||||
}
|
||||
};
|
||||
|
||||
loadProgramKesehatan();
|
||||
loadData();
|
||||
}, [params?.id]);
|
||||
|
||||
// Handler input controlled
|
||||
const handleChange = (key: keyof typeof formData, value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
// Submit form
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
programKesehatanState.edit.form = {
|
||||
...programKesehatanState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsiSingkat: formData.deskripsiSingkat,
|
||||
deskripsi: formData.deskripsi,
|
||||
imageId: formData.imageId,
|
||||
};
|
||||
const updatedForm = { ...programKesehatanState.edit.form, ...formData };
|
||||
|
||||
// Upload file kalau ada
|
||||
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');
|
||||
|
||||
if (!uploaded?.id) {
|
||||
return toast.error('Gagal upload gambar');
|
||||
}
|
||||
|
||||
programKesehatanState.edit.form.imageId = uploaded.id;
|
||||
updatedForm.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
programKesehatanState.edit.form = updatedForm;
|
||||
await programKesehatanState.edit.update();
|
||||
toast.success('Program kesehatan berhasil diperbarui!');
|
||||
router.push('/admin/kesehatan/program-kesehatan');
|
||||
} catch (error) {
|
||||
console.error('Error updating program kesehatan:', error);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Terjadi kesalahan saat memperbarui program kesehatan');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header dengan tombol back */}
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
@@ -109,7 +106,6 @@ function EditProgramKesehatan() {
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
{/* Card Form */}
|
||||
<Paper
|
||||
w={{ base: '100%', md: '50%' }}
|
||||
bg={colors['white-1']}
|
||||
@@ -119,21 +115,19 @@ function EditProgramKesehatan() {
|
||||
style={{ border: '1px solid #e0e0e0' }}
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
label="Judul"
|
||||
placeholder="Masukkan judul"
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
defaultValue={formData.deskripsiSingkat}
|
||||
onChange={(e) => setFormData({ ...formData, deskripsiSingkat: e.target.value })}
|
||||
label="Deskripsi Singkat"
|
||||
placeholder="Masukkan deskripsi singkat"
|
||||
required
|
||||
/>
|
||||
{[
|
||||
{ label: 'Judul', key: 'name', placeholder: 'Masukkan judul' },
|
||||
{ label: 'Deskripsi Singkat', key: 'deskripsiSingkat', placeholder: 'Masukkan deskripsi singkat' },
|
||||
].map((field) => (
|
||||
<TextInput
|
||||
key={field.key}
|
||||
label={field.label}
|
||||
placeholder={field.placeholder}
|
||||
value={formData[field.key as keyof typeof formData]}
|
||||
onChange={(e) => handleChange(field.key as keyof typeof formData, e.target.value)}
|
||||
required
|
||||
/>
|
||||
))}
|
||||
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold" mb={6}>
|
||||
@@ -141,7 +135,7 @@ function EditProgramKesehatan() {
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => setFormData({ ...formData, deskripsi: val })}
|
||||
onChange={(val) => handleChange('deskripsi', val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -152,10 +146,9 @@ function EditProgramKesehatan() {
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
}
|
||||
if (!selectedFile) return;
|
||||
setFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
|
||||
@@ -54,20 +54,11 @@ function EditPuskesmas() {
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState<PuskesmasFormData>({
|
||||
name: statePuskesmas.edit.form.name || '',
|
||||
alamat: statePuskesmas.edit.form.alamat || '',
|
||||
jam: {
|
||||
workDays: statePuskesmas.edit.form.jam?.workDays || '',
|
||||
weekDays: statePuskesmas.edit.form.jam?.weekDays || '',
|
||||
holiday: statePuskesmas.edit.form.jam?.holiday || '',
|
||||
},
|
||||
kontak: {
|
||||
kontakPuskesmas: statePuskesmas.edit.form.kontak?.kontakPuskesmas || '',
|
||||
email: statePuskesmas.edit.form.kontak?.email || '',
|
||||
facebook: statePuskesmas.edit.form.kontak?.facebook || '',
|
||||
kontakUGD: statePuskesmas.edit.form.kontak?.kontakUGD || '',
|
||||
},
|
||||
imageId: statePuskesmas.edit.form.imageId || '',
|
||||
name: '',
|
||||
alamat: '',
|
||||
jam: { workDays: '', weekDays: '', holiday: '' },
|
||||
kontak: { kontakPuskesmas: '', email: '', facebook: '', kontakUGD: '' },
|
||||
imageId: '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -183,7 +174,7 @@ function EditPuskesmas() {
|
||||
label="Nama Puskesmas"
|
||||
placeholder="Masukkan nama puskesmas"
|
||||
name="name"
|
||||
defaultValue={formData.name}
|
||||
value={formData.name}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
/>
|
||||
@@ -192,7 +183,7 @@ function EditPuskesmas() {
|
||||
label="Alamat"
|
||||
placeholder="Masukkan alamat"
|
||||
name="alamat"
|
||||
defaultValue={formData.alamat}
|
||||
value={formData.alamat}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
/>
|
||||
@@ -200,7 +191,7 @@ function EditPuskesmas() {
|
||||
<TextInput
|
||||
label="Jam Buka"
|
||||
placeholder="Masukkan jam buka"
|
||||
defaultValue={formData.jam.workDays}
|
||||
value={formData.jam.workDays}
|
||||
onChange={(e) => handleNestedChange('jam', 'workDays', e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -208,7 +199,7 @@ function EditPuskesmas() {
|
||||
<TextInput
|
||||
label="Jam Tutup"
|
||||
placeholder="Masukkan jam tutup"
|
||||
defaultValue={formData.jam.weekDays}
|
||||
value={formData.jam.weekDays}
|
||||
onChange={(e) => handleNestedChange('jam', 'weekDays', e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -216,7 +207,7 @@ function EditPuskesmas() {
|
||||
<TextInput
|
||||
label="Jam Libur"
|
||||
placeholder="Masukkan jam libur"
|
||||
defaultValue={formData.jam.holiday}
|
||||
value={formData.jam.holiday}
|
||||
onChange={(e) => handleNestedChange('jam', 'holiday', e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -224,28 +215,28 @@ function EditPuskesmas() {
|
||||
<TextInput
|
||||
label="Kontak Puskesmas"
|
||||
placeholder="Masukkan kontak puskesmas"
|
||||
defaultValue={formData.kontak.kontakPuskesmas}
|
||||
value={formData.kontak.kontakPuskesmas}
|
||||
onChange={(e) => handleNestedChange('kontak', 'kontakPuskesmas', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Email"
|
||||
placeholder="Masukkan email"
|
||||
defaultValue={formData.kontak.email}
|
||||
value={formData.kontak.email}
|
||||
onChange={(e) => handleNestedChange('kontak', 'email', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Facebook"
|
||||
placeholder="Masukkan facebook"
|
||||
defaultValue={formData.kontak.facebook}
|
||||
value={formData.kontak.facebook}
|
||||
onChange={(e) => handleNestedChange('kontak', 'facebook', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Kontak UGD"
|
||||
placeholder="Masukkan kontak UGD"
|
||||
defaultValue={formData.kontak.kontakUGD}
|
||||
value={formData.kontak.kontakUGD}
|
||||
onChange={(e) => handleNestedChange('kontak', 'kontakUGD', e.target.value)}
|
||||
/>
|
||||
|
||||
|
||||
@@ -28,20 +28,21 @@ function EditAPBDes() {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
jumlah: '',
|
||||
imageId: '',
|
||||
fileId: ''
|
||||
});
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [previewDoc, setPreviewDoc] = useState<string | null>(null);
|
||||
const [imageFile, setImageFile] = useState<File | null>(null);
|
||||
const [docFile, setDocFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
name: apbdesState.edit.form.name || '',
|
||||
jumlah: apbdesState.edit.form.jumlah || '',
|
||||
imageId: apbdesState.edit.form.imageId || '',
|
||||
fileId: apbdesState.edit.form.fileId || ''
|
||||
});
|
||||
|
||||
// Load APBDes data by id
|
||||
// Load data on mount
|
||||
useEffect(() => {
|
||||
const loadAPBDes = async () => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
@@ -54,62 +55,58 @@ function EditAPBDes() {
|
||||
imageId: data.imageId || '',
|
||||
fileId: data.fileId || ''
|
||||
});
|
||||
|
||||
if (data.image?.link) setPreviewImage(data.image.link);
|
||||
if (data.file?.link) setPreviewDoc(data.file.link);
|
||||
setPreviewImage(data.image?.link || null);
|
||||
setPreviewDoc(data.file?.link || null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading APBDes:', error);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Gagal memuat data APBDes');
|
||||
}
|
||||
};
|
||||
|
||||
loadAPBDes();
|
||||
loadData();
|
||||
}, [params?.id]);
|
||||
|
||||
// Generic Dropzone handler
|
||||
const handleDrop = (fileType: 'image' | 'doc') => (files: File[]) => {
|
||||
const file = files[0];
|
||||
if (!file) return;
|
||||
|
||||
if (fileType === 'image') {
|
||||
setImageFile(file);
|
||||
setPreviewImage(URL.createObjectURL(file));
|
||||
} else {
|
||||
setDocFile(file);
|
||||
setPreviewDoc(URL.createObjectURL(file));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// Update global state with form data
|
||||
apbdesState.edit.form = {
|
||||
...apbdesState.edit.form,
|
||||
...formData,
|
||||
// Update global state with local form data first
|
||||
apbdesState.edit.form = { ...apbdesState.edit.form, ...formData };
|
||||
|
||||
// Helper function for uploading file
|
||||
const uploadFile = async (file: File | null) => {
|
||||
if (!file) return null;
|
||||
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||
const uploaded = res.data?.data;
|
||||
if (!uploaded?.id) throw new Error('Upload gagal');
|
||||
return uploaded.id;
|
||||
};
|
||||
|
||||
// Upload new image if exists
|
||||
if (imageFile) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file: imageFile,
|
||||
name: imageFile.name
|
||||
});
|
||||
const uploaded = res.data?.data;
|
||||
// Upload files if selected
|
||||
const uploadedImageId = await uploadFile(imageFile);
|
||||
const uploadedDocId = await uploadFile(docFile);
|
||||
|
||||
if (!uploaded?.id) {
|
||||
return toast.error('Gagal upload gambar');
|
||||
}
|
||||
|
||||
apbdesState.edit.form.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
// Upload new document if exists
|
||||
if (docFile) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file: docFile,
|
||||
name: docFile.name
|
||||
});
|
||||
const uploaded = res.data?.data;
|
||||
|
||||
if (!uploaded?.id) {
|
||||
return toast.error('Gagal upload dokumen');
|
||||
}
|
||||
|
||||
apbdesState.edit.form.fileId = uploaded.id;
|
||||
}
|
||||
if (uploadedImageId) apbdesState.edit.form.imageId = uploadedImageId;
|
||||
if (uploadedDocId) apbdesState.edit.form.fileId = uploadedDocId;
|
||||
|
||||
await apbdesState.edit.update();
|
||||
toast.success('APBDes berhasil diperbarui!');
|
||||
router.push('/admin/landing-page/apbdes');
|
||||
} catch (error) {
|
||||
console.error('Error updating APBDes:', error);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Terjadi kesalahan saat memperbarui APBDes');
|
||||
}
|
||||
};
|
||||
@@ -127,19 +124,13 @@ function EditAPBDes() {
|
||||
</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">
|
||||
{/* Controlled Inputs */}
|
||||
<TextInput
|
||||
label="Nama APBDes"
|
||||
placeholder="Masukkan nama APBDes"
|
||||
defaultValue={formData.name}
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
required
|
||||
/>
|
||||
@@ -147,23 +138,16 @@ function EditAPBDes() {
|
||||
<TextInput
|
||||
label="Jumlah Anggaran"
|
||||
placeholder="Masukkan jumlah anggaran"
|
||||
defaultValue={formData.jumlah}
|
||||
value={formData.jumlah}
|
||||
onChange={(e) => setFormData({ ...formData, jumlah: e.target.value })}
|
||||
required
|
||||
/>
|
||||
|
||||
{/* Image Dropzone */}
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Gambar APBDes
|
||||
</Text>
|
||||
<Text fw="bold" fz="sm" mb={6}>Gambar APBDes</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
setImageFile(selectedFile);
|
||||
setPreviewImage(URL.createObjectURL(selectedFile));
|
||||
}
|
||||
}}
|
||||
onDrop={handleDrop('image')}
|
||||
onReject={() => toast.error('File tidak valid, gunakan format gambar')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
@@ -171,57 +155,29 @@ function EditAPBDes() {
|
||||
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>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm" style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview Gambar"
|
||||
radius="md"
|
||||
style={{
|
||||
maxHeight: 300,
|
||||
objectFit: 'contain',
|
||||
border: `1px solid ${colors['blue-button']}`
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<Image src={previewImage} alt="Preview Gambar" radius="md" style={{ maxHeight: 300, objectFit: 'contain', border: `1px solid ${colors['blue-button']}` }} loading="lazy" />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Document Dropzone */}
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Dokumen APBDes
|
||||
</Text>
|
||||
<Text fw="bold" fz="sm" mb={6}>Dokumen APBDes</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
setDocFile(selectedFile);
|
||||
setPreviewDoc(URL.createObjectURL(selectedFile));
|
||||
}
|
||||
}}
|
||||
onDrop={handleDrop('doc')}
|
||||
onReject={() => toast.error('File tidak valid, gunakan format dokumen')}
|
||||
maxSize={10 * 1024 ** 2} // 10MB
|
||||
maxSize={10 * 1024 ** 2}
|
||||
accept={{
|
||||
'application/pdf': ['.pdf'],
|
||||
'application/msword': ['.doc'],
|
||||
@@ -233,40 +189,19 @@ function EditAPBDes() {
|
||||
p="xl"
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={150}>
|
||||
<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>
|
||||
<IconFile 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><IconFile size={48} color="#868e96" stroke={1.5} /></Dropzone.Idle>
|
||||
<Stack gap="xs" align="center">
|
||||
<Text size="md" fw={500}>
|
||||
Seret dokumen atau klik untuk memilih file
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
Maksimal 10MB, format PDF/DOC/DOCX/XLS/XLSX
|
||||
</Text>
|
||||
<Text size="md" fw={500}>Seret dokumen atau klik untuk memilih file</Text>
|
||||
<Text size="sm" c="dimmed">Maksimal 10MB, format PDF/DOC/DOCX/XLS/XLSX</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{previewDoc && (
|
||||
<Box mt="sm">
|
||||
<Text size="sm" c="dimmed" mb="xs">
|
||||
Dokumen terpilih: {docFile?.name || 'Dokumen'}
|
||||
</Text>
|
||||
<Button
|
||||
component="a"
|
||||
href={previewDoc}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
variant="light"
|
||||
leftSection={<IconFile size={16} />}
|
||||
size="sm"
|
||||
>
|
||||
<Text size="sm" c="dimmed" mb="xs">Dokumen terpilih: {docFile?.name || 'Dokumen'}</Text>
|
||||
<Button component="a" href={previewDoc} target="_blank" rel="noopener noreferrer" variant="light" leftSection={<IconFile size={16} />} size="sm">
|
||||
Lihat Dokumen
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
|
||||
import korupsiState from '@/app/admin/(dashboard)/_state/landing-page/desa-anti-korupsi';
|
||||
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, useState } from 'react';
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
@@ -15,28 +16,25 @@ export default function EditKategoriDesaAntiKorupsi() {
|
||||
const id = params?.id as string;
|
||||
const stateKategori = useProxy(korupsiState.kategoriDesaAntiKorupsi);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
});
|
||||
// state lokal untuk form
|
||||
const [formData, setFormData] = useState({ name: '' });
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// load data kategori saat mount atau id berubah
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
const loadKategori = async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const data = await stateKategori.edit.load(id);
|
||||
|
||||
if (data) {
|
||||
stateKategori.edit.id = id;
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
});
|
||||
setFormData({ name: data.name || '' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading kategori desa anti korupsi:", error);
|
||||
toast.error("Gagal memuat data kategori desa anti korupsi");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Gagal memuat data kategori desa anti korupsi');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -45,36 +43,40 @@ export default function EditKategoriDesaAntiKorupsi() {
|
||||
loadKategori();
|
||||
}, [id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.name.trim()) {
|
||||
return toast.error('Nama kategori tidak boleh kosong');
|
||||
}
|
||||
// handler controlled input
|
||||
const handleChange = useCallback(
|
||||
(field: keyof typeof formData, value: string) => {
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// submit form
|
||||
const handleSubmit = useCallback(async () => {
|
||||
if (!formData.name.trim()) return toast.error('Nama kategori tidak boleh kosong');
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
stateKategori.edit.form = {
|
||||
name: formData.name.trim(),
|
||||
};
|
||||
|
||||
if (!stateKategori.edit.id) {
|
||||
stateKategori.edit.id = id;
|
||||
}
|
||||
// update global state hanya saat submit
|
||||
stateKategori.edit.form = { name: formData.name.trim() };
|
||||
if (!stateKategori.edit.id) stateKategori.edit.id = id;
|
||||
|
||||
await stateKategori.edit.update();
|
||||
toast.success('Kategori berhasil diperbarui');
|
||||
router.push("/admin/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi");
|
||||
} catch (error) {
|
||||
console.error("Error updating kategori desa anti korupsi:", error);
|
||||
toast.error(error instanceof Error ? error.message : 'Gagal memperbarui kategori');
|
||||
router.push('/admin/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error(err instanceof Error ? err.message : 'Gagal memperbarui kategori');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
}, [formData.name, id, router, stateKategori.edit]);
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Group mb="md">
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
</Button>
|
||||
@@ -96,8 +98,8 @@ export default function EditKategoriDesaAntiKorupsi() {
|
||||
<TextInput
|
||||
label="Nama Kategori"
|
||||
placeholder="Masukkan nama kategori"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name} // controlled
|
||||
onChange={(e) => handleChange('name', e.currentTarget.value)}
|
||||
required
|
||||
disabled={isLoading}
|
||||
/>
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client';
|
||||
|
||||
import { Box, Button, Group, Paper, Select, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core';
|
||||
import { IconArrowBack, IconFile, IconUpload, IconX } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState, ChangeEvent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
|
||||
import colors from '@/con/colors';
|
||||
import korupsiState from '@/app/admin/(dashboard)/_state/landing-page/desa-anti-korupsi';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
|
||||
interface FormDesaAntiKorupsi {
|
||||
name: string;
|
||||
@@ -23,12 +24,9 @@ interface FormDesaAntiKorupsi {
|
||||
|
||||
export default function EditDesaAntiKorupsi() {
|
||||
const desaAntiKorupsiState = useProxy(korupsiState.desaAntikorupsi);
|
||||
const [previewFile, setPreviewFile] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const params = useParams();
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
const [formData, setFormData] = useState<FormDesaAntiKorupsi>({
|
||||
name: '',
|
||||
deskripsi: '',
|
||||
@@ -36,10 +34,16 @@ export default function EditDesaAntiKorupsi() {
|
||||
fileId: '',
|
||||
});
|
||||
|
||||
const [previewFile, setPreviewFile] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// Load kategori
|
||||
useShallowEffect(() => {
|
||||
korupsiState.kategoriDesaAntiKorupsi.findMany.load();
|
||||
}, []);
|
||||
|
||||
// Load data existing
|
||||
useEffect(() => {
|
||||
const loadDesaAntiKorupsi = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -47,29 +51,21 @@ export default function EditDesaAntiKorupsi() {
|
||||
|
||||
try {
|
||||
const data = await desaAntiKorupsiState.edit.load(id);
|
||||
if (data) {
|
||||
desaAntiKorupsiState.edit.id = id;
|
||||
if (!data) return;
|
||||
|
||||
desaAntiKorupsiState.edit.form = {
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
kategoriId: data.kategoriId,
|
||||
fileId: data.fileId,
|
||||
};
|
||||
desaAntiKorupsiState.edit.id = id;
|
||||
desaAntiKorupsiState.edit.form = { ...data };
|
||||
|
||||
setFormData({
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
kategoriId: data.kategoriId,
|
||||
fileId: data.fileId,
|
||||
});
|
||||
setFormData({
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
kategoriId: data.kategoriId,
|
||||
fileId: data.fileId,
|
||||
});
|
||||
|
||||
if (data?.file?.link) {
|
||||
setPreviewFile(data.file.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading data:', error);
|
||||
if (data.file?.link) setPreviewFile(data.file.link);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Gagal memuat data Desa Anti Korupsi');
|
||||
}
|
||||
};
|
||||
@@ -77,42 +73,47 @@ export default function EditDesaAntiKorupsi() {
|
||||
loadDesaAntiKorupsi();
|
||||
}, [params?.id]);
|
||||
|
||||
// Generic handler input
|
||||
const handleInputChange = (key: keyof FormDesaAntiKorupsi) => (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | string | null) => {
|
||||
const value = typeof e === 'string' ? e : e?.target?.value ?? '';
|
||||
setFormData((prev) => ({ ...prev, [key]: value || '' }));
|
||||
};
|
||||
|
||||
// Special handler for Select component
|
||||
const handleSelectChange = (key: keyof FormDesaAntiKorupsi) => (value: string | null) => {
|
||||
setFormData((prev) => ({ ...prev, [key]: value || '' }));
|
||||
};
|
||||
|
||||
const handleDrop = (files: File[]) => {
|
||||
if (!files.length) return;
|
||||
const selectedFile = files[0];
|
||||
setFile(selectedFile);
|
||||
setPreviewFile(URL.createObjectURL(selectedFile));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.name) {
|
||||
return toast.warn('Masukkan judul dokumen');
|
||||
}
|
||||
if (!formData.kategoriId) {
|
||||
return toast.warn('Pilih kategori dokumen');
|
||||
}
|
||||
if (!formData.name) return toast.warn('Masukkan judul dokumen');
|
||||
if (!formData.kategoriId) return toast.warn('Pilih kategori dokumen');
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// Update global state with form data
|
||||
desaAntiKorupsiState.edit.form = {
|
||||
...desaAntiKorupsiState.edit.form,
|
||||
...formData,
|
||||
kategoriId: formData.kategoriId || '',
|
||||
};
|
||||
// Update global state
|
||||
desaAntiKorupsiState.edit.form = { ...desaAntiKorupsiState.edit.form, ...formData };
|
||||
|
||||
// Upload new file if exists
|
||||
// Upload file jika ada
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file,
|
||||
name: file.name
|
||||
});
|
||||
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||
const uploaded = res.data?.data;
|
||||
if (!uploaded?.id) throw new Error('Gagal mengunggah dokumen');
|
||||
|
||||
if (!uploaded?.id) {
|
||||
throw new Error('Gagal mengunggah dokumen');
|
||||
}
|
||||
desaAntiKorupsiState.edit.form.fileId = uploaded.id;
|
||||
}
|
||||
|
||||
await desaAntiKorupsiState.edit.update();
|
||||
toast.success('Data berhasil diperbarui');
|
||||
router.push('/admin/landing-page/desa-anti-korupsi/list-desa-anti-korupsi');
|
||||
} catch (error) {
|
||||
console.error('Error updating data:', error);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -144,8 +145,8 @@ export default function EditDesaAntiKorupsi() {
|
||||
<TextInput
|
||||
label="Judul Dokumen"
|
||||
placeholder="Masukkan judul dokumen"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={handleInputChange('name')}
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -153,23 +154,18 @@ export default function EditDesaAntiKorupsi() {
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Deskripsi
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => setFormData({ ...formData, deskripsi: val })}
|
||||
/>
|
||||
<EditEditor value={formData.deskripsi} onChange={handleInputChange('deskripsi')} />
|
||||
</Box>
|
||||
|
||||
<Select
|
||||
label="Kategori"
|
||||
placeholder="Pilih kategori"
|
||||
value={formData.kategoriId}
|
||||
onChange={(val) => 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
|
||||
</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
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%',
|
||||
}}
|
||||
>
|
||||
<iframe
|
||||
src={previewFile}
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{ border: 'none' }}
|
||||
/>
|
||||
<iframe src={previewFile} width="100%" height="100%" style={{ border: 'none' }} />
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
@@ -259,4 +244,4 @@ export default function EditDesaAntiKorupsi() {
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
'use client'
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { useRouter, useParams } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, Title, TextInput, Text, Select, Tooltip } from '@mantine/core';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Paper,
|
||||
Stack,
|
||||
Title,
|
||||
TextInput,
|
||||
Text,
|
||||
Select,
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { IconArrowBack, IconDeviceFloppy } from '@tabler/icons-react';
|
||||
import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan';
|
||||
import { toast } from 'react-toastify';
|
||||
@@ -18,64 +29,93 @@ interface FormResponden {
|
||||
}
|
||||
|
||||
function EditResponden() {
|
||||
const router = useRouter()
|
||||
const params = useParams() as { id: string }
|
||||
const state = useProxy(indeksKepuasanState.responden)
|
||||
const id = params.id
|
||||
const router = useRouter();
|
||||
const params = useParams() as { id: string };
|
||||
const state = useProxy(indeksKepuasanState.responden);
|
||||
const id = params.id;
|
||||
|
||||
const [formData, setFormData] = useState<FormResponden>({
|
||||
name: '',
|
||||
tanggal: '',
|
||||
jenisKelaminId: '',
|
||||
ratingId: '',
|
||||
kelompokUmurId: '',
|
||||
})
|
||||
useEffect(() => {
|
||||
});
|
||||
|
||||
// Helper untuk load pilihan select
|
||||
const loadSelectOptions = useCallback(() => {
|
||||
indeksKepuasanState.jenisKelaminResponden.findMany.load();
|
||||
indeksKepuasanState.pilihanRatingResponden.findMany.load();
|
||||
indeksKepuasanState.kelompokUmurResponden.findMany.load();
|
||||
}, []);
|
||||
|
||||
const loadResponden = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
// Load data responden
|
||||
const loadResponden = useCallback(async () => {
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await state.update.load(id);
|
||||
if (data) {
|
||||
state.update.id = id;
|
||||
try {
|
||||
const data = await state.update.load(id);
|
||||
if (!data) return;
|
||||
|
||||
state.update.form = {
|
||||
name: data.name,
|
||||
tanggal: data.tanggal,
|
||||
jenisKelaminId: data.jenisKelaminId,
|
||||
ratingId: data.ratingId,
|
||||
kelompokUmurId: data.kelompokUmurId,
|
||||
};
|
||||
|
||||
setFormData({
|
||||
name: data.name,
|
||||
tanggal: data.tanggal,
|
||||
jenisKelaminId: data.jenisKelaminId,
|
||||
ratingId: data.ratingId,
|
||||
kelompokUmurId: data.kelompokUmurId,
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading program penghijauan:", error);
|
||||
toast.error("Gagal memuat data program penghijauan");
|
||||
}
|
||||
setFormData({
|
||||
name: data.name,
|
||||
tanggal: data.tanggal,
|
||||
jenisKelaminId: data.jenisKelaminId,
|
||||
ratingId: data.ratingId,
|
||||
kelompokUmurId: data.kelompokUmurId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error loading responden:", error);
|
||||
toast.error("Gagal memuat data responden");
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
loadSelectOptions();
|
||||
loadResponden();
|
||||
}, [params?.id]);
|
||||
}, [loadSelectOptions, loadResponden]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
state.update.id = id;
|
||||
state.update.form = { ...formData }; // <-- sinkronisasi manual
|
||||
state.update.form = { ...formData }; // sinkronisasi manual
|
||||
await state.update.submit();
|
||||
router.push('/admin/landing-page/indeks-kepuasan-masyarakat/responden')
|
||||
}
|
||||
router.push('/admin/landing-page/indeks-kepuasan-masyarakat/responden');
|
||||
};
|
||||
|
||||
// Reusable Select component
|
||||
const ControlledSelect = ({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
options,
|
||||
error,
|
||||
placeholder = 'Pilih',
|
||||
loading = false,
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
onChange: (val: string) => void;
|
||||
options: { value: string; label: string }[];
|
||||
error?: string;
|
||||
placeholder?: string;
|
||||
loading?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<Select
|
||||
label={<Text fw="bold" fz="sm" mb={4}>{label}</Text>}
|
||||
value={value}
|
||||
onChange={(val) => onChange(val || '')}
|
||||
data={options}
|
||||
placeholder={placeholder}
|
||||
disabled={loading}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
radius="md"
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
@@ -101,116 +141,61 @@ function EditResponden() {
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
label="Nama Responden"
|
||||
type='text'
|
||||
placeholder="Masukkan nama responden"
|
||||
defaultValue={formData.name}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
name: val.currentTarget.value
|
||||
})
|
||||
}}
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.currentTarget.value })}
|
||||
radius="md"
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Tanggal"
|
||||
type="date"
|
||||
placeholder='Pilih tanggal'
|
||||
defaultValue={formData.tanggal ? new Date(formData.tanggal).toISOString().split('T')[0] : ''}
|
||||
onChange={(e) => {
|
||||
const selectedDate = e.currentTarget.value;
|
||||
setFormData({
|
||||
...formData,
|
||||
tanggal: selectedDate,
|
||||
});
|
||||
}}
|
||||
value={formData.tanggal ? new Date(formData.tanggal).toISOString().split('T')[0] : ''}
|
||||
onChange={(e) => setFormData({ ...formData, tanggal: e.currentTarget.value })}
|
||||
radius="md"
|
||||
required
|
||||
/>
|
||||
<Select
|
||||
key="jenisKelamin"
|
||||
label={
|
||||
<Text fw="bold" fz="sm" mb={4}>
|
||||
Jenis Kelamin
|
||||
</Text>
|
||||
}
|
||||
placeholder="Pilih jenis kelamin"
|
||||
value={formData.jenisKelaminId}
|
||||
onChange={(val) => setFormData({ ...formData, jenisKelaminId: val || "" })}
|
||||
data={
|
||||
(indeksKepuasanState.jenisKelaminResponden.findMany.data || [])
|
||||
.filter(Boolean)
|
||||
.map((v) => ({
|
||||
value: v.id || '',
|
||||
label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
|
||||
}))
|
||||
}
|
||||
disabled={indeksKepuasanState.jenisKelaminResponden.findMany.loading}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
radius="md"
|
||||
error={!formData.jenisKelaminId ? "Pilih jenis kelamin" : undefined}
|
||||
/>
|
||||
<Select
|
||||
key="rating"
|
||||
value={formData.ratingId}
|
||||
onChange={(val) => setFormData({ ...formData, ratingId: val || "" })}
|
||||
label={
|
||||
<Text fw="bold" fz="sm" mb={4}>
|
||||
Rating
|
||||
</Text>
|
||||
}
|
||||
placeholder='Pilih rating'
|
||||
data={
|
||||
(indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
|
||||
.filter(Boolean)
|
||||
.map((v) => ({
|
||||
value: v.id || '',
|
||||
label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
|
||||
}))
|
||||
}
|
||||
disabled={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
radius="md"
|
||||
error={!formData.ratingId ? "Pilih rating" : undefined}
|
||||
/>
|
||||
|
||||
<Select
|
||||
key={"kelompokUmur"}
|
||||
value={formData.kelompokUmurId}
|
||||
onChange={(val) => setFormData({ ...formData, kelompokUmurId: val || "" })}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Kelompok Umur</Text>}
|
||||
placeholder='Pilih kelompok umur'
|
||||
data={
|
||||
(indeksKepuasanState.kelompokUmurResponden.findMany.data || [])
|
||||
.filter(Boolean)
|
||||
.map((v) => ({
|
||||
value: v.id || '',
|
||||
label: typeof v.name === 'string' ? v.name : 'Tanpa Nama'
|
||||
}))
|
||||
}
|
||||
disabled={indeksKepuasanState.kelompokUmurResponden.findMany.loading}
|
||||
clearable
|
||||
searchable
|
||||
required
|
||||
error={!formData.kelompokUmurId ? "Pilih kelompok umur" : undefined}
|
||||
<ControlledSelect
|
||||
label="Jenis Kelamin"
|
||||
value={formData.jenisKelaminId}
|
||||
onChange={(val) => setFormData({ ...formData, jenisKelaminId: val })}
|
||||
options={(indeksKepuasanState.jenisKelaminResponden.findMany.data || [])
|
||||
.filter(Boolean)
|
||||
.map((v) => ({ value: v.id || '', label: v.name || 'Tanpa Nama' }))}
|
||||
loading={indeksKepuasanState.jenisKelaminResponden.findMany.loading}
|
||||
error={!formData.jenisKelaminId ? 'Pilih jenis kelamin' : undefined}
|
||||
/>
|
||||
|
||||
|
||||
<ControlledSelect
|
||||
label="Rating"
|
||||
value={formData.ratingId}
|
||||
onChange={(val) => setFormData({ ...formData, ratingId: val })}
|
||||
options={(indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
|
||||
.filter(Boolean)
|
||||
.map((v) => ({ value: v.id || '', label: v.name || 'Tanpa Nama' }))}
|
||||
loading={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
|
||||
error={!formData.ratingId ? 'Pilih rating' : undefined}
|
||||
/>
|
||||
|
||||
<ControlledSelect
|
||||
label="Kelompok Umur"
|
||||
value={formData.kelompokUmurId}
|
||||
onChange={(val) => setFormData({ ...formData, kelompokUmurId: val })}
|
||||
options={(indeksKepuasanState.kelompokUmurResponden.findMany.data || [])
|
||||
.filter(Boolean)
|
||||
.map((v) => ({ value: v.id || '', label: v.name || 'Tanpa Nama' }))}
|
||||
loading={indeksKepuasanState.kelompokUmurResponden.findMany.loading}
|
||||
error={!formData.kelompokUmurId ? 'Pilih kelompok umur' : undefined}
|
||||
/>
|
||||
|
||||
<Group justify="flex-end" mt="md">
|
||||
<Button
|
||||
variant="light"
|
||||
color="red"
|
||||
onClick={() => router.back()}
|
||||
>
|
||||
<Button variant="light" color="red" onClick={() => router.back()}>
|
||||
Batal
|
||||
</Button>
|
||||
<Button
|
||||
<Button
|
||||
leftSection={<IconDeviceFloppy size={20} />}
|
||||
onClick={handleSubmit}
|
||||
onClick={handleSubmit}
|
||||
loading={state.update.loading}
|
||||
color={colors['blue-button']}
|
||||
>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip,
|
||||
Image
|
||||
Image,
|
||||
} from "@mantine/core";
|
||||
import { Dropzone } from "@mantine/dropzone";
|
||||
import { IconArrowBack, IconDeviceFloppy, IconPhoto, IconUpload, IconX } from "@tabler/icons-react";
|
||||
@@ -23,37 +23,35 @@ import { useEffect, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { useProxy } from "valtio/utils";
|
||||
|
||||
function EditKolaborasiInovasi() {
|
||||
export default function EditKolaborasiInovasi() {
|
||||
const sdgsState = useProxy(sdgsDesa);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
jumlah: "",
|
||||
imageId: "",
|
||||
});
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState({
|
||||
name: sdgsState.edit.form.name || '',
|
||||
jumlah: sdgsState.edit.form.jumlah || '',
|
||||
imageId: sdgsState.edit.form.imageId || ''
|
||||
});
|
||||
|
||||
// Load sdgs desa by id saat pertama kali
|
||||
// Load data sdgs desa by id
|
||||
useEffect(() => {
|
||||
const loadKolaborasi = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await sdgsState.edit.load(id); // akses langsung, bukan dari proxy
|
||||
const data = await sdgsState.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
jumlah: data.jumlah || '',
|
||||
imageId: data.imageId || '',
|
||||
name: data.name || "",
|
||||
jumlah: data.jumlah || "",
|
||||
imageId: data.imageId || "",
|
||||
});
|
||||
if (data.image) {
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
if (data.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -65,31 +63,26 @@ function EditKolaborasiInovasi() {
|
||||
loadKolaborasi();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleInputChange = (field: keyof typeof formData, value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
|
||||
try {
|
||||
// edit global state with form data
|
||||
sdgsState.edit.form = {
|
||||
...sdgsState.edit.form,
|
||||
name: formData.name,
|
||||
jumlah: formData.jumlah,
|
||||
imageId: formData.imageId // Keep existing imageId if not changed
|
||||
};
|
||||
let imageId = formData.imageId;
|
||||
|
||||
// Jika ada file baru, upload
|
||||
// Upload file baru jika ada
|
||||
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");
|
||||
}
|
||||
|
||||
// edit imageId in global state
|
||||
sdgsState.edit.form.imageId = uploaded.id;
|
||||
if (!uploaded?.id) return toast.error("Gagal upload gambar");
|
||||
imageId = uploaded.id;
|
||||
}
|
||||
|
||||
// Update global state hanya saat submit
|
||||
sdgsState.edit.form = { ...sdgsState.edit.form, ...formData, imageId };
|
||||
await sdgsState.edit.update();
|
||||
|
||||
toast.success("sdgs desa berhasil diperbarui!");
|
||||
router.push("/admin/landing-page/sdgs-desa");
|
||||
} catch (error) {
|
||||
@@ -99,11 +92,11 @@ function EditKolaborasiInovasi() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<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">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
<IconArrowBack color={colors["blue-button"]} size={24} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
@@ -112,12 +105,12 @@ function EditKolaborasiInovasi() {
|
||||
</Group>
|
||||
|
||||
<Paper
|
||||
w={{ base: '100%', md: '50%' }}
|
||||
bg={colors['white-1']}
|
||||
w={{ base: "100%", md: "50%" }}
|
||||
bg={colors["white-1"]}
|
||||
p="lg"
|
||||
radius="md"
|
||||
shadow="sm"
|
||||
style={{ border: '1px solid #e0e0e0' }}
|
||||
style={{ border: "1px solid #e0e0e0" }}
|
||||
>
|
||||
<Stack gap="md">
|
||||
<Box>
|
||||
@@ -132,15 +125,15 @@ function EditKolaborasiInovasi() {
|
||||
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/*': [] }}
|
||||
accept={{ "image/*": [] }}
|
||||
radius="md"
|
||||
p="xl"
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={180}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={48} color={colors['blue-button']} stroke={1.5} />
|
||||
<IconUpload size={48} color={colors["blue-button"]} stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={48} color="red" stroke={1.5} />
|
||||
@@ -160,12 +153,12 @@ function EditKolaborasiInovasi() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm" style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Box mt="sm" style={{ display: "flex", justifyContent: "center" }}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview Gambar"
|
||||
radius="md"
|
||||
style={{ maxHeight: 220, objectFit: 'contain', border: `1px solid ${colors['blue-button']}` }}
|
||||
style={{ maxHeight: 220, objectFit: "contain", border: `1px solid ${colors["blue-button"]}` }}
|
||||
loading="lazy"
|
||||
/>
|
||||
</Box>
|
||||
@@ -175,16 +168,16 @@ function EditKolaborasiInovasi() {
|
||||
<TextInput
|
||||
label="Nama Sdgs Desa"
|
||||
placeholder="Masukkan nama Sdgs Desa"
|
||||
defaultValue={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
value={formData.name}
|
||||
onChange={(e) => handleInputChange("name", e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Jumlah"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={formData.jumlah}
|
||||
onChange={(e) => setFormData({ ...formData, jumlah: e.target.value })}
|
||||
value={formData.jumlah}
|
||||
onChange={(e) => handleInputChange("jumlah", e.target.value)}
|
||||
required
|
||||
type="number"
|
||||
/>
|
||||
@@ -197,9 +190,9 @@ function EditKolaborasiInovasi() {
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
background: `linear-gradient(135deg, ${colors["blue-button"]}, #4facfe)`,
|
||||
color: "#fff",
|
||||
boxShadow: "0 4px 15px rgba(79, 172, 254, 0.4)",
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
@@ -210,5 +203,3 @@ function EditKolaborasiInovasi() {
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditKolaborasiInovasi;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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={{
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user