fix(realisasi): add kode field to RealisasiItem and simplify table display
- Add kode field to RealisasiItem model in Prisma schema - Update API endpoints (create, update) to accept kode parameter - Update state management with proper type definitions - Add kode input field in RealisasiManager component - Simplify realisasiTable to show flat list (Kode, Uraian, Realisasi, %) - Remove section grouping and expandable details - Fix race condition in findUnique.load() with loading guard - Fix linting errors across multiple files Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -14,14 +14,6 @@ const ApbdesItemSchema = z.object({
|
||||
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(),
|
||||
@@ -157,33 +149,37 @@ const apbdes = proxy({
|
||||
findUnique: {
|
||||
data: null as
|
||||
| Prisma.APBDesGetPayload<{
|
||||
include: { image: true; file: true; items: true };
|
||||
include: { image: true; file: true; items: { include: { realisasiItems: true } } };
|
||||
}>
|
||||
| null,
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
|
||||
|
||||
async load(id: string) {
|
||||
if (!id || id.trim() === '') {
|
||||
this.data = null;
|
||||
this.error = "ID tidak valid";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Prevent multiple simultaneous loads
|
||||
if (this.loading) {
|
||||
console.log("⚠️ Already loading, skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
|
||||
try {
|
||||
// Pastikan URL-nya benar
|
||||
const url = `/api/landingpage/apbdes/${id}`;
|
||||
console.log("🌐 Fetching:", url);
|
||||
|
||||
// Gunakan fetch biasa atau ApiFetch dengan cara yang benar
|
||||
|
||||
const response = await fetch(url);
|
||||
const res = await response.json();
|
||||
|
||||
|
||||
console.log("📦 Response:", res);
|
||||
|
||||
|
||||
if (res.success && res.data) {
|
||||
this.data = res.data;
|
||||
} else {
|
||||
@@ -322,15 +318,16 @@ const apbdes = proxy({
|
||||
// =========================================
|
||||
realisasi: {
|
||||
// Create realisasi
|
||||
async create(itemId: string, data: { jumlah: number; tanggal: string; keterangan?: string; buktiFileId?: string }) {
|
||||
async create(itemId: string, data: { kode: string; 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);
|
||||
const currentId = apbdes.findUnique.data?.id;
|
||||
if (currentId) {
|
||||
await apbdes.findUnique.load(currentId);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@@ -345,15 +342,16 @@ const apbdes = proxy({
|
||||
},
|
||||
|
||||
// Update realisasi
|
||||
async update(realisasiId: string, data: { jumlah?: number; tanggal?: string; keterangan?: string; buktiFileId?: string }) {
|
||||
async update(realisasiId: string, data: { kode?: string; 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);
|
||||
const currentId = apbdes.findUnique.data?.id;
|
||||
if (currentId) {
|
||||
await apbdes.findUnique.load(currentId);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import apbdes from '@/app/admin/(dashboard)/_state/landing-page/apbdes';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
@@ -23,7 +25,6 @@ import {
|
||||
Badge,
|
||||
Modal,
|
||||
Divider,
|
||||
Loader,
|
||||
Center,
|
||||
} from '@mantine/core';
|
||||
import {
|
||||
@@ -33,9 +34,6 @@ import {
|
||||
IconCalendar,
|
||||
IconCoin,
|
||||
} from '@tabler/icons-react';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import colors from '@/con/colors';
|
||||
|
||||
interface RealisasiManagerProps {
|
||||
itemId: string;
|
||||
@@ -63,6 +61,7 @@ export default function RealisasiManager({
|
||||
|
||||
// Form state
|
||||
const [formData, setFormData] = useState({
|
||||
kode: '',
|
||||
jumlah: 0,
|
||||
tanggal: new Date().toISOString().split('T')[0], // YYYY-MM-DD format for input
|
||||
keterangan: '',
|
||||
@@ -70,6 +69,7 @@ export default function RealisasiManager({
|
||||
|
||||
const resetForm = () => {
|
||||
setFormData({
|
||||
kode: '',
|
||||
jumlah: 0,
|
||||
tanggal: new Date().toISOString().split('T')[0],
|
||||
keterangan: '',
|
||||
@@ -85,8 +85,9 @@ export default function RealisasiManager({
|
||||
const handleOpenEdit = (realisasi: any) => {
|
||||
const tanggal = new Date(realisasi.tanggal);
|
||||
const tanggalStr = tanggal.toISOString().split('T')[0]; // YYYY-MM-DD
|
||||
|
||||
|
||||
setFormData({
|
||||
kode: realisasi.kode || '',
|
||||
jumlah: realisasi.jumlah,
|
||||
tanggal: tanggalStr,
|
||||
keterangan: realisasi.keterangan || '',
|
||||
@@ -100,12 +101,17 @@ export default function RealisasiManager({
|
||||
return toast.warn('Jumlah realisasi harus lebih dari 0');
|
||||
}
|
||||
|
||||
if (!formData.kode || formData.kode.trim() === '') {
|
||||
return toast.warn('Kode realisasi wajib diisi');
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
if (editingId) {
|
||||
// Update existing realisasi
|
||||
const success = await state.realisasi.update(editingId, {
|
||||
kode: formData.kode,
|
||||
jumlah: formData.jumlah,
|
||||
tanggal: new Date(formData.tanggal).toISOString(),
|
||||
keterangan: formData.keterangan,
|
||||
@@ -117,6 +123,7 @@ export default function RealisasiManager({
|
||||
} else {
|
||||
// Create new realisasi
|
||||
const success = await state.realisasi.create(itemId, {
|
||||
kode: formData.kode,
|
||||
jumlah: formData.jumlah,
|
||||
tanggal: new Date(formData.tanggal).toISOString(),
|
||||
keterangan: formData.keterangan,
|
||||
@@ -257,6 +264,7 @@ export default function RealisasiManager({
|
||||
<Table striped highlightOnHover fz="sm">
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Kode</TableTh>
|
||||
<TableTh>Tanggal</TableTh>
|
||||
<TableTh>Uraian</TableTh>
|
||||
<TableTh ta="right">Jumlah</TableTh>
|
||||
@@ -266,6 +274,11 @@ export default function RealisasiManager({
|
||||
<TableTbody>
|
||||
{realisasiItems.map((realisasi) => (
|
||||
<TableTr key={realisasi.id}>
|
||||
<TableTd>
|
||||
<Badge variant="light" color="blue" size="sm">
|
||||
{realisasi.kode || '-'}
|
||||
</Badge>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Group gap="xs">
|
||||
<IconCalendar size={16} />
|
||||
@@ -314,7 +327,7 @@ export default function RealisasiManager({
|
||||
Belum ada realisasi untuk item ini
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed">
|
||||
Klik tombol "Tambah Realisasi" untuk menambahkan
|
||||
Klik tombol "Tambah Realisasi" untuk menambahkan
|
||||
</Text>
|
||||
</Stack>
|
||||
</Center>
|
||||
@@ -349,6 +362,15 @@ export default function RealisasiManager({
|
||||
</Text>
|
||||
</Paper>
|
||||
|
||||
<TextInput
|
||||
label="Kode Realisasi"
|
||||
placeholder="Contoh: 4.1.1-R1"
|
||||
value={formData.kode}
|
||||
onChange={(e) => setFormData({ ...formData, kode: e.target.value })}
|
||||
description="Kode unik untuk realisasi ini"
|
||||
required
|
||||
/>
|
||||
|
||||
<NumberInput
|
||||
label="Jumlah Realisasi (Rp)"
|
||||
value={formData.jumlah}
|
||||
|
||||
@@ -233,7 +233,7 @@ function DetailAPBDes() {
|
||||
</Table>
|
||||
|
||||
{/* Realisasi Manager untuk setiap item */}
|
||||
{data.items.map((item: any) => (
|
||||
{data.items.map((item) => (
|
||||
<RealisasiManager
|
||||
key={item.id}
|
||||
itemId={item.id}
|
||||
|
||||
Reference in New Issue
Block a user