/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable @typescript-eslint/no-explicit-any */ 'use client'; import apbdes from '@/app/admin/(dashboard)/_state/landing-page/apbdes'; import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; import { ActionIcon, Badge, Box, Button, Group, Image, Loader, NumberInput, Paper, Select, Stack, Table, Text, TextInput, Title, } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconFile, IconPhoto, IconPlus, IconTrash, IconX } 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'; // Tipe untuk form item type ItemForm = { kode: string; uraian: string; anggaran: number; realisasi: number; level: number; tipe: 'pendapatan' | 'belanja' | 'pembiayaan'; }; function EditAPBDes() { const apbdesState = useProxy(apbdes); const router = useRouter(); const params = useParams(); const [isSubmitting, setIsSubmitting] = useState(false); // Check if form is valid const isFormValid = () => { return ( apbdesState.edit.form.items.length > 0 ); }; const [previewImage, setPreviewImage] = useState(null); const [previewDoc, setPreviewDoc] = useState(null); const [imageFile, setImageFile] = useState(null); const [docFile, setDocFile] = useState(null); // Form input untuk item baru const [newItem, setNewItem] = useState({ kode: '', uraian: '', anggaran: 0, realisasi: 0, level: 1, tipe: 'pendapatan', }); // Simpan data original untuk reset form const [originalData, setOriginalData] = useState({ tahun: 0, imageId: '', fileId: '', imageUrl: '', fileUrl: '', }); // Load data saat pertama kali useEffect(() => { const id = params?.id as string; if (!id) return; const loadData = async () => { try { const data = await apbdesState.edit.load(id); if (!data) return; // Set preview dari data lama setPreviewImage(data.image?.link || null); setPreviewDoc(data.file?.link || null); // Simpan data original untuk reset setOriginalData({ tahun: data.tahun || new Date().getFullYear(), imageId: data.imageId || '', fileId: data.fileId || '', imageUrl: data.image?.link || '', fileUrl: data.file?.link || '', }); // Set form dengan data lama (termasuk imageId dan fileId) apbdesState.edit.form = { tahun: data.tahun || new Date().getFullYear(), imageId: data.imageId || '', fileId: data.fileId || '', items: (data.items || []).map((item: any) => ({ kode: item.kode, uraian: item.uraian, anggaran: item.anggaran, realisasi: item.realisasi, selisih: item.selisih, persentase: item.persentase, level: item.level, tipe: item.tipe || 'pendapatan', })), }; } catch (error) { console.error('Error loading APBDes:', error); toast.error('Gagal memuat data APBDes'); } }; loadData(); }, [params?.id]); 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 handleAddItem = () => { const { kode, uraian, anggaran, realisasi, level, tipe } = newItem; if (!kode || !uraian) { return toast.warn('Kode dan uraian wajib diisi'); } const finalTipe = level === 1 ? null : tipe; const selisih = realisasi - anggaran; const persentase = anggaran > 0 ? (realisasi / anggaran) * 100 : 0; apbdesState.edit.addItem({ kode, uraian, anggaran, realisasi, selisih, persentase, level, tipe: finalTipe, // ✅ Tidak akan undefined }); setNewItem({ kode: '', uraian: '', anggaran: 0, realisasi: 0, level: 1, tipe: 'pendapatan', }); }; const handleRemoveItem = (index: number) => { apbdesState.edit.removeItem(index); }; const handleSubmit = async () => { if (apbdesState.edit.form.items.length === 0) { return toast.warn('Minimal harus ada 1 item APBDes'); } try { setIsSubmitting(true); // Upload file baru jika ada perubahan if (imageFile) { // Hapus file lama dari form jika ada file baru const res = await ApiFetch.api.fileStorage.create.post({ file: imageFile, name: imageFile.name, }); const imageId = res.data?.data?.id; if (imageId) { apbdesState.edit.form.imageId = imageId; } } if (docFile) { // Hapus file lama dari form jika ada file baru const res = await ApiFetch.api.fileStorage.create.post({ file: docFile, name: docFile.name, }); const fileId = res.data?.data?.id; if (fileId) { apbdesState.edit.form.fileId = fileId; } } // Jika tidak ada file baru, gunakan ID lama (sudah ada di form) // Pastikan imageId dan fileId tetap ada if (!apbdesState.edit.form.imageId) { return toast.warn('Gambar wajib diunggah'); } if (!apbdesState.edit.form.fileId) { return toast.warn('Dokumen wajib diunggah'); } const success = await apbdesState.edit.update(); if (success) { router.push('/admin/landing-page/apbdes'); } } catch (err) { console.error('Update error:', err); toast.error('Gagal memperbarui APBDes'); } finally { setIsSubmitting(false); } }; const handleReset = () => { // Reset ke data original (tahun, imageId, fileId) apbdesState.edit.form = { tahun: originalData.tahun, imageId: originalData.imageId, fileId: originalData.fileId, items: [...apbdesState.edit.form.items], // keep existing items }; // Reset preview ke data original setPreviewImage(originalData.imageUrl || null); setPreviewDoc(originalData.fileUrl || null); // Reset file uploads setImageFile(null); setDocFile(null); // Reset new item form setNewItem({ kode: '', uraian: '', anggaran: 0, realisasi: 0, level: 1, tipe: 'pendapatan', }); toast.info('Form dikembalikan ke data awal'); }; return ( Edit APBDes {/* Header Form */} (apbdesState.edit.form.tahun = Number(val) || new Date().getFullYear()) } min={2000} max={2100} required /> {/* Gambar & Dokumen */} Gambar APBDes toast.error('File gambar tidak valid')} maxSize={5 * 1024 ** 2} accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }} radius="md" p="xl" > {previewImage ? 'Ganti gambar' : 'Unggah gambar'} {previewImage && ( Preview { setPreviewImage(null); setImageFile(null); }} > )} Dokumen APBDes toast.error('File dokumen tidak valid')} maxSize={10 * 1024 ** 2} accept={{ 'application/pdf': ['.pdf'], 'application/msword': ['.doc'], 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'], 'application/vnd.ms-excel': ['.xls'], 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'], }} radius="md" p="xl" > {previewDoc ? 'Ganti dokumen' : 'Unggah dokumen'} {previewDoc && ( { setPreviewDoc(null); setDocFile(null); }} > )} {/* Input Item Baru */} Tambah Item Pendapatan/Belanja setNewItem({ ...newItem, kode: e.target.value })} required /> setNewItem({ ...newItem, tipe: (val as any) || 'pendapatan' })} /> setNewItem({ ...newItem, uraian: e.target.value })} required /> setNewItem({ ...newItem, anggaran: Number(val) || 0 })} thousandSeparator min={0} /> setNewItem({ ...newItem, realisasi: Number(val) || 0 })} thousandSeparator min={0} /> {/* Tabel Items */} {apbdesState.edit.form.items.length > 0 && ( Daftar Item ({apbdesState.edit.form.items.length}) {apbdesState.edit.form.items.map((item, idx) => ( ))}
Kode Uraian Anggaran Realisasi Level Tipe Aksi
{item.kode} {item.uraian} {item.anggaran.toLocaleString('id-ID')} {item.realisasi.toLocaleString('id-ID')} L{item.level} {item.tipe ? ( {item.tipe} ) : ( '-' )} handleRemoveItem(idx)}>
)} {/* Tombol Aksi */}
); } export default EditAPBDes;