fix(apbdes): integrate new APBDes API with admin UI
- Update API schema to support name, deskripsi, and jumlah fields - Enhance state management with additional form fields - Add input fields for name, description, and total amount in create/edit pages - Display description and total amount in detail page - Fix APBDes component order in landing page - Update TypeScript types and Prisma schema integration API Changes: - POST /api/landingpage/apbdes/create: Added optional fields (name, deskripsi, jumlah) - PUT /api/landingpage/apbdes/🆔 Added optional fields (name, deskripsi, jumlah) Admin UI Changes: - create/page.tsx: Add TextInput for name, deskripsi, and jumlah - edit/page.tsx: Add TextInput for name, deskripsi, and jumlah; improve reset functionality - [id]/page.tsx: Display deskripsi and jumlah if available - page.tsx: Minor formatting fix - _state/apbdes.ts: Update Zod schema and default form with new fields Landing Page: - Move Apbdes component to top of stack for better visibility Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -19,6 +19,9 @@ const ApbdesItemSchema = z.object({
|
|||||||
|
|
||||||
const ApbdesFormSchema = z.object({
|
const ApbdesFormSchema = z.object({
|
||||||
tahun: z.number().int().min(2000, "Tahun tidak valid"),
|
tahun: z.number().int().min(2000, "Tahun tidak valid"),
|
||||||
|
name: z.string().optional(),
|
||||||
|
deskripsi: z.string().optional(),
|
||||||
|
jumlah: z.string().optional(),
|
||||||
imageId: z.string().min(1, "Gambar wajib diunggah"),
|
imageId: z.string().min(1, "Gambar wajib diunggah"),
|
||||||
fileId: z.string().min(1, "File wajib diunggah"),
|
fileId: z.string().min(1, "File wajib diunggah"),
|
||||||
items: z.array(ApbdesItemSchema).min(1, "Minimal ada 1 item"),
|
items: z.array(ApbdesItemSchema).min(1, "Minimal ada 1 item"),
|
||||||
@@ -27,6 +30,9 @@ const ApbdesFormSchema = z.object({
|
|||||||
// --- Default Form ---
|
// --- Default Form ---
|
||||||
const defaultApbdesForm = {
|
const defaultApbdesForm = {
|
||||||
tahun: new Date().getFullYear(),
|
tahun: new Date().getFullYear(),
|
||||||
|
name: "",
|
||||||
|
deskripsi: "",
|
||||||
|
jumlah: "",
|
||||||
imageId: "",
|
imageId: "",
|
||||||
fileId: "",
|
fileId: "",
|
||||||
items: [] as z.infer<typeof ApbdesItemSchema>[],
|
items: [] as z.infer<typeof ApbdesItemSchema>[],
|
||||||
@@ -244,6 +250,9 @@ const apbdes = proxy({
|
|||||||
this.id = data.id;
|
this.id = data.id;
|
||||||
this.form = {
|
this.form = {
|
||||||
tahun: data.tahun || new Date().getFullYear(),
|
tahun: data.tahun || new Date().getFullYear(),
|
||||||
|
name: data.name || "",
|
||||||
|
deskripsi: data.deskripsi || "",
|
||||||
|
jumlah: data.jumlah || "",
|
||||||
imageId: data.imageId || "",
|
imageId: data.imageId || "",
|
||||||
fileId: data.fileId || "",
|
fileId: data.fileId || "",
|
||||||
items: (data.items || []).map((item: any) => ({
|
items: (data.items || []).map((item: any) => ({
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ function EditAPBDes() {
|
|||||||
// Simpan data original untuk reset form
|
// Simpan data original untuk reset form
|
||||||
const [originalData, setOriginalData] = useState({
|
const [originalData, setOriginalData] = useState({
|
||||||
tahun: 0,
|
tahun: 0,
|
||||||
|
name: '',
|
||||||
|
deskripsi: '',
|
||||||
|
jumlah: '',
|
||||||
imageId: '',
|
imageId: '',
|
||||||
fileId: '',
|
fileId: '',
|
||||||
imageUrl: '',
|
imageUrl: '',
|
||||||
@@ -103,6 +106,9 @@ function EditAPBDes() {
|
|||||||
// Simpan data original untuk reset
|
// Simpan data original untuk reset
|
||||||
setOriginalData({
|
setOriginalData({
|
||||||
tahun: data.tahun || new Date().getFullYear(),
|
tahun: data.tahun || new Date().getFullYear(),
|
||||||
|
name: data.name || '',
|
||||||
|
deskripsi: data.deskripsi || '',
|
||||||
|
jumlah: data.jumlah || '',
|
||||||
imageId: data.imageId || '',
|
imageId: data.imageId || '',
|
||||||
fileId: data.fileId || '',
|
fileId: data.fileId || '',
|
||||||
imageUrl: data.image?.link || '',
|
imageUrl: data.image?.link || '',
|
||||||
@@ -112,6 +118,9 @@ function EditAPBDes() {
|
|||||||
// Set form dengan data lama (termasuk imageId dan fileId)
|
// Set form dengan data lama (termasuk imageId dan fileId)
|
||||||
apbdesState.edit.form = {
|
apbdesState.edit.form = {
|
||||||
tahun: data.tahun || new Date().getFullYear(),
|
tahun: data.tahun || new Date().getFullYear(),
|
||||||
|
name: data.name || '',
|
||||||
|
deskripsi: data.deskripsi || '',
|
||||||
|
jumlah: data.jumlah || '',
|
||||||
imageId: data.imageId || '',
|
imageId: data.imageId || '',
|
||||||
fileId: data.fileId || '',
|
fileId: data.fileId || '',
|
||||||
items: (data.items || []).map((item: any) => ({
|
items: (data.items || []).map((item: any) => ({
|
||||||
@@ -238,9 +247,12 @@ function EditAPBDes() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
// Reset ke data original (tahun, imageId, fileId)
|
// Reset ke data original (tahun, name, deskripsi, jumlah, imageId, fileId)
|
||||||
apbdesState.edit.form = {
|
apbdesState.edit.form = {
|
||||||
tahun: originalData.tahun,
|
tahun: originalData.tahun,
|
||||||
|
name: originalData.name,
|
||||||
|
deskripsi: originalData.deskripsi,
|
||||||
|
jumlah: originalData.jumlah,
|
||||||
imageId: originalData.imageId,
|
imageId: originalData.imageId,
|
||||||
fileId: originalData.fileId,
|
fileId: originalData.fileId,
|
||||||
items: [...apbdesState.edit.form.items], // keep existing items
|
items: [...apbdesState.edit.form.items], // keep existing items
|
||||||
@@ -288,6 +300,33 @@ function EditAPBDes() {
|
|||||||
>
|
>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{/* Header Form */}
|
{/* Header Form */}
|
||||||
|
<TextInput
|
||||||
|
label="Nama APBDes"
|
||||||
|
placeholder="Contoh: APBDes Tahun 2025"
|
||||||
|
value={apbdesState.edit.form.name}
|
||||||
|
onChange={(e) =>
|
||||||
|
(apbdesState.edit.form.name = e.target.value)
|
||||||
|
}
|
||||||
|
description="Opsional - akan diisi otomatis jika kosong"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label="Deskripsi"
|
||||||
|
placeholder="Deskripsi APBDes (opsional)"
|
||||||
|
value={apbdesState.edit.form.deskripsi}
|
||||||
|
onChange={(e) =>
|
||||||
|
(apbdesState.edit.form.deskripsi = e.target.value)
|
||||||
|
}
|
||||||
|
description="Opsional"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label="Jumlah Total"
|
||||||
|
placeholder="Contoh: Rp 1.000.000.000"
|
||||||
|
value={apbdesState.edit.form.jumlah}
|
||||||
|
onChange={(e) =>
|
||||||
|
(apbdesState.edit.form.jumlah = e.target.value)
|
||||||
|
}
|
||||||
|
description="Opsional - total keseluruhan anggaran"
|
||||||
|
/>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
label="Tahun"
|
label="Tahun"
|
||||||
value={apbdesState.edit.form.tahun || new Date().getFullYear()}
|
value={apbdesState.edit.form.tahun || new Date().getFullYear()}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ function DetailAPBDes() {
|
|||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">Nama APBDes</Text>
|
<Text fz="lg" fw="bold">Nama APBDes</Text>
|
||||||
<Text fz="md" c="dimmed">
|
<Text fz="md" c="dimmed">
|
||||||
{data.name || '-'}
|
{data.name || `APBDes Tahun ${data.tahun}`}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -105,6 +105,24 @@ function DetailAPBDes() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{data.deskripsi && (
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Deskripsi</Text>
|
||||||
|
<Text fz="md" c="dimmed">
|
||||||
|
{data.deskripsi}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{data.jumlah && (
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Jumlah Total</Text>
|
||||||
|
<Text fz="md" c="dimmed">
|
||||||
|
{data.jumlah}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">Gambar</Text>
|
<Text fz="lg" fw="bold">Gambar</Text>
|
||||||
{data.image?.link ? (
|
{data.image?.link ? (
|
||||||
|
|||||||
@@ -117,9 +117,9 @@ function CreateAPBDes() {
|
|||||||
toast.success("Berhasil menambahkan APBDes");
|
toast.success("Berhasil menambahkan APBDes");
|
||||||
resetForm();
|
resetForm();
|
||||||
router.push("/admin/landing-page/apbdes");
|
router.push("/admin/landing-page/apbdes");
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error("Gagal submit:", error);
|
console.error("Gagal submit:", error);
|
||||||
toast.error("Gagal menyimpan data");
|
toast.error(error?.message || "Gagal menyimpan data");
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
@@ -334,6 +334,27 @@ function CreateAPBDes() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{/* Form Header */}
|
{/* Form Header */}
|
||||||
|
<TextInput
|
||||||
|
label="Nama APBDes"
|
||||||
|
placeholder="Contoh: APBDes Tahun 2025"
|
||||||
|
value={stateAPBDes.create.form.name}
|
||||||
|
onChange={(e) => (stateAPBDes.create.form.name = e.target.value)}
|
||||||
|
description="Opsional - akan diisi otomatis jika kosong"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label="Deskripsi"
|
||||||
|
placeholder="Deskripsi APBDes (opsional)"
|
||||||
|
value={stateAPBDes.create.form.deskripsi}
|
||||||
|
onChange={(e) => (stateAPBDes.create.form.deskripsi = e.target.value)}
|
||||||
|
description="Opsional"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label="Jumlah Total"
|
||||||
|
placeholder="Contoh: Rp 1.000.000.000"
|
||||||
|
value={stateAPBDes.create.form.jumlah}
|
||||||
|
onChange={(e) => (stateAPBDes.create.form.jumlah = e.target.value)}
|
||||||
|
description="Opsional - total keseluruhan anggaran"
|
||||||
|
/>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
label="Tahun"
|
label="Tahun"
|
||||||
value={stateAPBDes.create.form.tahun || new Date().getFullYear()}
|
value={stateAPBDes.create.form.tahun || new Date().getFullYear()}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ type APBDesItemInput = {
|
|||||||
|
|
||||||
type FormCreate = {
|
type FormCreate = {
|
||||||
tahun: number;
|
tahun: number;
|
||||||
|
name?: string;
|
||||||
|
deskripsi?: string;
|
||||||
|
jumlah?: string;
|
||||||
imageId: string;
|
imageId: string;
|
||||||
fileId: string;
|
fileId: string;
|
||||||
items: APBDesItemInput[];
|
items: APBDesItemInput[];
|
||||||
@@ -48,7 +51,9 @@ export default async function apbdesCreate(context: Context) {
|
|||||||
const apbdes = await prisma.aPBDes.create({
|
const apbdes = await prisma.aPBDes.create({
|
||||||
data: {
|
data: {
|
||||||
tahun: body.tahun,
|
tahun: body.tahun,
|
||||||
name: `APBDes Tahun ${body.tahun}`,
|
name: body.name || `APBDes Tahun ${body.tahun}`,
|
||||||
|
deskripsi: body.deskripsi,
|
||||||
|
jumlah: body.jumlah,
|
||||||
imageId: body.imageId,
|
imageId: body.imageId,
|
||||||
fileId: body.fileId,
|
fileId: body.fileId,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -47,7 +47,13 @@ export default async function apbdesFindMany(context: Context) {
|
|||||||
include: {
|
include: {
|
||||||
image: true,
|
image: true,
|
||||||
file: true,
|
file: true,
|
||||||
items: true,
|
items: {
|
||||||
|
where: { isActive: true },
|
||||||
|
orderBy: { kode: "asc" },
|
||||||
|
include: {
|
||||||
|
parent: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
prisma.aPBDes.count({ where }),
|
prisma.aPBDes.count({ where }),
|
||||||
|
|||||||
@@ -48,11 +48,14 @@ export default async function apbdesFindUnique(context: Context) {
|
|||||||
include: {
|
include: {
|
||||||
items: {
|
items: {
|
||||||
where: { isActive: true },
|
where: { isActive: true },
|
||||||
orderBy: { kode: 'asc' }
|
orderBy: { kode: 'asc' },
|
||||||
|
include: {
|
||||||
|
parent: true, // Include parent item for hierarchy
|
||||||
|
},
|
||||||
},
|
},
|
||||||
image: true,
|
image: true,
|
||||||
file: true
|
file: true,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result || !result.isActive) {
|
if (!result || !result.isActive) {
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ const APBDes = new Elysia({
|
|||||||
.post("/create", apbdesCreate, {
|
.post("/create", apbdesCreate, {
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
tahun: t.Number(),
|
tahun: t.Number(),
|
||||||
|
name: t.Optional(t.String()),
|
||||||
|
deskripsi: t.Optional(t.String()),
|
||||||
|
jumlah: t.Optional(t.String()),
|
||||||
imageId: t.String(),
|
imageId: t.String(),
|
||||||
fileId: t.String(),
|
fileId: t.String(),
|
||||||
items: t.Array(ApbdesItemSchema),
|
items: t.Array(ApbdesItemSchema),
|
||||||
@@ -44,6 +47,9 @@ const APBDes = new Elysia({
|
|||||||
params: t.Object({ id: t.String() }),
|
params: t.Object({ id: t.String() }),
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
tahun: t.Number(),
|
tahun: t.Number(),
|
||||||
|
name: t.Optional(t.String()),
|
||||||
|
deskripsi: t.Optional(t.String()),
|
||||||
|
jumlah: t.Optional(t.String()),
|
||||||
imageId: t.String(),
|
imageId: t.String(),
|
||||||
fileId: t.String(),
|
fileId: t.String(),
|
||||||
items: t.Array(ApbdesItemSchema),
|
items: t.Array(ApbdesItemSchema),
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ type APBDesItemInput = {
|
|||||||
|
|
||||||
type FormUpdateBody = {
|
type FormUpdateBody = {
|
||||||
tahun: number;
|
tahun: number;
|
||||||
|
name?: string;
|
||||||
|
deskripsi?: string;
|
||||||
|
jumlah?: string;
|
||||||
imageId: string;
|
imageId: string;
|
||||||
fileId: string;
|
fileId: string;
|
||||||
items: APBDesItemInput[];
|
items: APBDesItemInput[];
|
||||||
@@ -79,6 +82,9 @@ export default async function apbdesUpdate(context: Context) {
|
|||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
tahun: body.tahun,
|
tahun: body.tahun,
|
||||||
|
name: body.name || `APBDes Tahun ${body.tahun}`,
|
||||||
|
deskripsi: body.deskripsi,
|
||||||
|
jumlah: body.jumlah,
|
||||||
imageId: body.imageId,
|
imageId: body.imageId,
|
||||||
fileId: body.fileId,
|
fileId: body.fileId,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -150,13 +150,13 @@ export default function Page() {
|
|||||||
<Box id="page-root">
|
<Box id="page-root">
|
||||||
<Stack bg={colors.grey[1]} gap={0}>
|
<Stack bg={colors.grey[1]} gap={0}>
|
||||||
<LandingPage />
|
<LandingPage />
|
||||||
|
<Apbdes />
|
||||||
<Penghargaan />
|
<Penghargaan />
|
||||||
<Layanan />
|
<Layanan />
|
||||||
<Potensi />
|
<Potensi />
|
||||||
<DesaAntiKorupsi />
|
<DesaAntiKorupsi />
|
||||||
<Kepuasan />
|
<Kepuasan />
|
||||||
<SDGS />
|
<SDGS />
|
||||||
<Apbdes />
|
|
||||||
<Prestasi />
|
<Prestasi />
|
||||||
<ScrollToTopButton />
|
<ScrollToTopButton />
|
||||||
<NewsReaderLanding />
|
<NewsReaderLanding />
|
||||||
|
|||||||
Reference in New Issue
Block a user