diff --git a/src/app/admin/(dashboard)/_state/landing-page/apbdes.ts b/src/app/admin/(dashboard)/_state/landing-page/apbdes.ts index 6575d16d..c858fba5 100644 --- a/src/app/admin/(dashboard)/_state/landing-page/apbdes.ts +++ b/src/app/admin/(dashboard)/_state/landing-page/apbdes.ts @@ -5,18 +5,23 @@ import { toast } from "react-toastify"; import { proxy } from "valtio"; import { z } from "zod"; -// --- Zod Schema --- +// --- Zod Schema untuk APBDes Item (tanpa field kalkulasi) --- const ApbdesItemSchema = z.object({ kode: z.string().min(1, "Kode wajib diisi"), uraian: z.string().min(1, "Uraian wajib diisi"), - anggaran: z.number().min(0), - realisasi: z.number().min(0), - selisih: z.number(), - persentase: z.number(), + anggaran: z.number().min(0, "Anggaran tidak boleh negatif"), level: z.number().int().min(1).max(3), tipe: z.enum(['pendapatan', 'belanja', 'pembiayaan']).nullable().optional(), }); +// --- Zod Schema untuk Realisasi Item --- +const RealisasiItemSchema = z.object({ + jumlah: z.number().min(0, "Jumlah tidak boleh negatif"), + tanggal: z.string(), + keterangan: z.string().optional(), + buktiFileId: z.string().optional(), +}); + const ApbdesFormSchema = z.object({ tahun: z.number().int().min(2000, "Tahun tidak valid"), name: z.string().optional(), @@ -38,26 +43,14 @@ const defaultApbdesForm = { items: [] as z.infer[], }; -// --- Helper: hitung selisih & persentase otomatis (opsional di frontend) --- -// --- Helper: hitung selisih & persentase otomatis (opsional di frontend) --- +// --- Helper: Normalize item (tanpa kalkulasi, backend yang hitung) --- function normalizeItem(item: Partial>): z.infer { - const anggaran = item.anggaran ?? 0; - const realisasi = item.realisasi ?? 0; - - - // ✅ Formula yang benar - const selisih = realisasi - anggaran; // positif = sisa anggaran, negatif = over budget - const persentase = anggaran > 0 ? (realisasi / anggaran) * 100 : 0; // persentase realisasi terhadap anggaran - return { kode: item.kode || "", uraian: item.uraian || "", - anggaran, - realisasi, - selisih, - persentase, + anggaran: item.anggaran ?? 0, level: item.level || 1, - tipe: item.tipe, // biarkan null jika memang null + tipe: item.tipe ?? null, }; } @@ -259,9 +252,6 @@ const apbdes = proxy({ 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', })), @@ -326,6 +316,80 @@ const apbdes = proxy({ this.form = { ...defaultApbdesForm }; }, }, + + // ========================================= + // REALISASI STATE MANAGEMENT + // ========================================= + realisasi: { + // Create realisasi + async create(itemId: string, data: { jumlah: number; tanggal: string; keterangan?: string; buktiFileId?: string }) { + try { + const res = await (ApiFetch.api.landingpage.apbdes as any)[itemId].realisasi.post(data); + + if (res.data?.success) { + toast.success("Realisasi berhasil ditambahkan"); + // Reload findUnique untuk update data + if (apbdes.findUnique.data) { + await apbdes.findUnique.load(apbdes.findUnique.data.id); + } + return true; + } else { + toast.error(res.data?.message || "Gagal menambahkan realisasi"); + return false; + } + } catch (error: any) { + console.error("Create realisasi error:", error); + toast.error(error?.message || "Terjadi kesalahan saat menambahkan realisasi"); + return false; + } + }, + + // Update realisasi + async update(realisasiId: string, data: { jumlah?: number; tanggal?: string; keterangan?: string; buktiFileId?: string }) { + try { + const res = await (ApiFetch.api.landingpage.apbdes as any).realisasi[realisasiId].put(data); + + if (res.data?.success) { + toast.success("Realisasi berhasil diperbarui"); + // Reload findUnique untuk update data + if (apbdes.findUnique.data) { + await apbdes.findUnique.load(apbdes.findUnique.data.id); + } + return true; + } else { + toast.error(res.data?.message || "Gagal memperbarui realisasi"); + return false; + } + } catch (error: any) { + console.error("Update realisasi error:", error); + toast.error(error?.message || "Terjadi kesalahan saat memperbarui realisasi"); + return false; + } + }, + + // Delete realisasi + async delete(realisasiId: string) { + try { + const res = await (ApiFetch.api.landingpage.apbdes as any).realisasi[realisasiId].delete(); + + if (res.data?.success) { + toast.success("Realisasi berhasil dihapus"); + // Reload findUnique untuk update data + if (apbdes.findUnique.data) { + await apbdes.findUnique.load(apbdes.findUnique.data.id); + } + return true; + } else { + toast.error(res.data?.message || "Gagal menghapus realisasi"); + return false; + } + } catch (error: any) { + console.error("Delete realisasi error:", error); + toast.error(error?.message || "Terjadi kesalahan saat menghapus realisasi"); + return false; + } + }, + }, }); export default apbdes; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/landing-page/apbdes/[id]/edit/page.tsx b/src/app/admin/(dashboard)/landing-page/apbdes/[id]/edit/page.tsx index 96bbf4b2..31fb2faf 100644 --- a/src/app/admin/(dashboard)/landing-page/apbdes/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/landing-page/apbdes/[id]/edit/page.tsx @@ -42,7 +42,6 @@ type ItemForm = { kode: string; uraian: string; anggaran: number; - realisasi: number; level: number; tipe: 'pendapatan' | 'belanja' | 'pembiayaan'; }; @@ -71,7 +70,6 @@ function EditAPBDes() { kode: '', uraian: '', anggaran: 0, - realisasi: 0, level: 1, tipe: 'pendapatan', }); @@ -157,32 +155,25 @@ function EditAPBDes() { }; const handleAddItem = () => { - const { kode, uraian, anggaran, realisasi, level, tipe } = newItem; + const { kode, uraian, anggaran, 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 + tipe: finalTipe, }); - setNewItem({ kode: '', uraian: '', anggaran: 0, - realisasi: 0, level: 1, tipe: 'pendapatan', }); @@ -271,7 +262,6 @@ function EditAPBDes() { kode: '', uraian: '', anggaran: 0, - realisasi: 0, level: 1, tipe: 'pendapatan', }); @@ -514,13 +504,6 @@ function EditAPBDes() { thousandSeparator min={0} /> - setNewItem({ ...newItem, realisasi: Number(val) || 0 })} - thousandSeparator - min={0} - />