From dcf195f54f7036752d8cbd11050b46d5633fdbe3 Mon Sep 17 00:00:00 2001 From: nico Date: Mon, 1 Dec 2025 17:11:24 +0800 Subject: [PATCH] Tambahan filter data sesuai tahun, di landing page apbdes --- .../landing-page/apbdes/create/page.tsx | 1 + .../ppid/daftar-informasi-publik/page.tsx | 2 +- .../(tambahan)/apbdes/lib/apbDesaProgress.tsx | 141 ++++-------------- .../(tambahan)/apbdes/lib/apbDesaTable.tsx | 54 ++----- .../darmasaba/(tambahan)/apbdes/lib/types.ts | 42 ++++++ src/app/darmasaba/(tambahan)/apbdes/page.tsx | 50 ++++++- .../darmasaba/_com/main-page/apbdes/index.tsx | 51 ++++++- 7 files changed, 172 insertions(+), 169 deletions(-) create mode 100644 src/app/darmasaba/(tambahan)/apbdes/lib/types.ts diff --git a/src/app/admin/(dashboard)/landing-page/apbdes/create/page.tsx b/src/app/admin/(dashboard)/landing-page/apbdes/create/page.tsx index f91d6a37..a419308b 100644 --- a/src/app/admin/(dashboard)/landing-page/apbdes/create/page.tsx +++ b/src/app/admin/(dashboard)/landing-page/apbdes/create/page.tsx @@ -361,6 +361,7 @@ function CreateAPBDes() { data={[ { value: 'pendapatan', label: 'Pendapatan' }, { value: 'belanja', label: 'Belanja' }, + { value: 'pembiayaan', label: 'Pembiayaan' }, ]} value={newItem.level === 1 ? null : newItem.tipe} onChange={(val) => setNewItem({ ...newItem, tipe: val as any })} diff --git a/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik/page.tsx b/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik/page.tsx index e5c33633..559ebe9f 100644 --- a/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik/page.tsx +++ b/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik/page.tsx @@ -151,7 +151,7 @@ function Page() { variant="light" color="blue" leftSection={} - onClick={() => router.push(`/darmasaba/ppid/daftar-informasi-publik-desa-darmasaba/${item.id}`)} + onClick={() => router.push(`/darmasaba/ppid/daftar-informasi-publik/${item.id}`)} > Detail diff --git a/src/app/darmasaba/(tambahan)/apbdes/lib/apbDesaProgress.tsx b/src/app/darmasaba/(tambahan)/apbdes/lib/apbDesaProgress.tsx index 918d64c9..71825fff 100644 --- a/src/app/darmasaba/(tambahan)/apbdes/lib/apbDesaProgress.tsx +++ b/src/app/darmasaba/(tambahan)/apbdes/lib/apbDesaProgress.tsx @@ -1,13 +1,8 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -// src/app/admin/(dashboard)/landing-page/APBDes/APBDesProgress.tsx 'use client'; -import { Box, Paper, Progress, Stack, Text, Title } from '@mantine/core'; -import { useProxy } from 'valtio/utils'; -import apbdes from '@/app/admin/(dashboard)/_state/landing-page/apbdes'; import colors from '@/con/colors'; - - +import { Box, Paper, Progress, Stack, Text, Title } from '@mantine/core'; +import { APBDesData } from './types'; function formatRupiah(value: number) { return new Intl.NumberFormat('id-ID', { @@ -17,31 +12,33 @@ function formatRupiah(value: number) { }).format(value); } -function APBDesProgress() { - const state = useProxy(apbdes); - const data = state.findMany.data || []; +interface APBDesProgressProps { + apbdesData: APBDesData; +} - // Ambil APBDes pertama (misalnya, jika hanya satu tahun ditampilkan) - const apbdesItem = data[0]; // 👈 sesuaikan logika jika ada banyak APBDes - - if (!apbdesItem) { - return ( - - Belum ada data APBDes untuk ditampilkan. - - ); +function APBDesProgress({ apbdesData }: APBDesProgressProps) { + // Return null if apbdesData is not available yet + if (!apbdesData) { + return null; } - const items = apbdesItem.items || []; + const items = apbdesData.items || []; const sortedItems = [...items].sort((a, b) => a.kode.localeCompare(b.kode)); // Kelompokkan berdasarkan tipe const pendapatanItems = sortedItems.filter(item => item.tipe === 'pendapatan'); const belanjaItems = sortedItems.filter(item => item.tipe === 'belanja'); - const pembiayaanItems = sortedItems.filter(item => item.tipe === 'pembiayaan'); // jika ada + const pembiayaanItems = sortedItems.filter(item => item.tipe === 'pembiayaan'); + + // Items without a type (should be filtered out from calculations) + const untypedItems = sortedItems.filter(item => !item.tipe); + + if (untypedItems.length > 0) { + console.warn(`Found ${untypedItems.length} items without a type. These will be excluded from calculations.`); + } // Hitung total per kategori - const calcTotal = (items: any[]) => { + const calcTotal = (items: { anggaran: number; realisasi: number }[]) => { const anggaran = items.reduce((sum, item) => sum + item.anggaran, 0); const realisasi = items.reduce((sum, item) => sum + item.realisasi, 0); const persen = anggaran > 0 ? (realisasi / anggaran) * 100 : 0; @@ -50,10 +47,10 @@ function APBDesProgress() { const pendapatan = calcTotal(pendapatanItems); const belanja = calcTotal(belanjaItems); - const pembiayaan = calcTotal(pembiayaanItems); // bisa kosong + const pembiayaan = calcTotal(pembiayaanItems); // Render satu progress bar - const renderProgress = (label: string, dataset: any) => { + const renderProgress = (label: string, dataset: { realisasi: number; anggaran: number; persen: number }) => { const isPembiayaan = label.includes('Pembiayaan'); return ( @@ -71,8 +68,8 @@ function APBDesProgress() { root: { backgroundColor: '#d7e3f1' }, section: { backgroundColor: isPembiayaan - ? 'green' // warna hijau untuk pembiayaan - : colors['blue-button'], // biru untuk pendapatan/belanja + ? 'green' + : colors['blue-button'], position: 'relative', '&::after': { content: `'${dataset.persen.toFixed(2)}%'`, @@ -102,7 +99,7 @@ function APBDesProgress() { > - Grafik Pelaksanaan APBDes Tahun {apbdesItem.tahun} + Grafik Pelaksanaan APBDes Tahun {apbdesData.tahun} @@ -112,97 +109,9 @@ function APBDesProgress() { {renderProgress('Pendapatan Desa', pendapatan)} {renderProgress('Belanja Desa', belanja)} {renderProgress('Pembiayaan Desa', pembiayaan)} - {pembiayaanItems.length > 0 && renderProgress('Pembiayaan Desa', pembiayaan)} ); } -export default APBDesProgress; - -// /* eslint-disable @typescript-eslint/no-explicit-any */ -// 'use client'; - -// import { Box, Paper, Stack, Text, Title } from '@mantine/core'; -// import { BarChart } from '@mantine/charts'; -// import { useProxy } from 'valtio/utils'; -// import apbdes from '@/app/admin/(dashboard)/_state/landing-page/apbdes'; -// import colors from '@/con/colors'; - -// function APBDesProgress() { -// const state = useProxy(apbdes); -// const data = state.findMany.data || []; - -// const apbdesItem = data[0]; -// if (!apbdesItem) { -// return ( -// -// Belum ada data APBDes untuk ditampilkan. -// -// ); -// } - -// const items = apbdesItem.items || []; -// const sortedItems = [...items].sort((a, b) => a.kode.localeCompare(b.kode)); - -// const pendapatanItems = sortedItems.filter(i => i.tipe === 'pendapatan'); -// const belanjaItems = sortedItems.filter(i => i.tipe === 'belanja'); -// const pembiayaanItems = sortedItems.filter(i => i.tipe === 'pembiayaan'); - -// const total = (rows: any[]) => { -// const anggaran = rows.reduce((s, i) => s + i.anggaran, 0); -// const realisasi = rows.reduce((s, i) => s + i.realisasi, 0); -// return anggaran === 0 ? 0 : (realisasi / anggaran) * 100; -// }; - -// const chartData = [ -// { name: 'Pendapatan', persen: total(pendapatanItems) }, -// { name: 'Belanja', persen: total(belanjaItems) }, -// ]; - -// if (pembiayaanItems.length > 0) { -// chartData.push({ name: 'Pembiayaan', persen: total(pembiayaanItems) }); -// } - -// return ( -// -// -// -// Grafik Pelaksanaan APBDes Tahun {apbdesItem.tahun} -// - -// -// Persentase Realisasi (%) dari Anggaran -// - -// `${v.toFixed(1)}%`} -// /> -// -// -// ); -// } - -// export default APBDesProgress; +export default APBDesProgress; \ No newline at end of file diff --git a/src/app/darmasaba/(tambahan)/apbdes/lib/apbDesaTable.tsx b/src/app/darmasaba/(tambahan)/apbdes/lib/apbDesaTable.tsx index ec1aa0eb..82a6a499 100644 --- a/src/app/darmasaba/(tambahan)/apbdes/lib/apbDesaTable.tsx +++ b/src/app/darmasaba/(tambahan)/apbdes/lib/apbDesaTable.tsx @@ -1,30 +1,8 @@ -// src/app/admin/(dashboard)/landing-page/APBDes/APBDesTable.tsx 'use client'; import { Box, Paper, Table, Text, Title, Badge, Group } from '@mantine/core'; -import { useProxy } from 'valtio/utils'; -import apbdes from '@/app/admin/(dashboard)/_state/landing-page/apbdes'; import colors from '@/con/colors'; - -interface APBDesItem { - id: string; - kode: string; - uraian: string; - anggaran: number; - realisasi: number; - selisih: number; - persentase: number; - level: number; - tipe: 'pendapatan' | 'belanja' | 'pembiayaan'; -} - -interface APBDesData { - id: string; - tahun: number; - items: APBDesItem[]; - image?: { id: string; url: string } | null; - file?: { id: string; url: string } | null; -} +import { APBDesData } from './types'; // Helper: Format Rupiah, tapi jika 0 → tampilkan '-' function formatRupiahOrEmpty(value: number): string { @@ -51,22 +29,12 @@ function getIndent(level: number) { }; } -function APBDesTable() { - const state = useProxy(apbdes); - const data = state.findMany.data || []; +interface APBDesTableProps { + apbdesData: APBDesData; +} - // Get the first APBDes item - const apbdesItem = data[0] as unknown as APBDesData | undefined; - - if (!apbdesItem) { - return ( - - Belum ada data APBDes untuk ditampilkan. - - ); - } - - const items = Array.isArray(apbdesItem.items) ? apbdesItem.items : []; +function APBDesTable({ apbdesData }: APBDesTableProps) { + const items = Array.isArray(apbdesData.items) ? apbdesData.items : []; const sortedItems = [...items].sort((a, b) => a.kode.localeCompare(b.kode)); // Calculate totals @@ -76,13 +44,13 @@ function APBDesTable() { const totalPersentase = totalAnggaran > 0 ? (totalRealisasi / totalAnggaran) * 100 : 0; return ( - + - Rincian APBDes Tahun {apbdesItem.tahun} + Rincian APBDes Tahun {apbdesData.tahun} - + @@ -109,9 +77,7 @@ function APBDesTable() { {item.kode} - - {item.uraian} - + {item.uraian} {formatRupiahOrEmpty(item.anggaran)} diff --git a/src/app/darmasaba/(tambahan)/apbdes/lib/types.ts b/src/app/darmasaba/(tambahan)/apbdes/lib/types.ts new file mode 100644 index 00000000..a52445aa --- /dev/null +++ b/src/app/darmasaba/(tambahan)/apbdes/lib/types.ts @@ -0,0 +1,42 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +export type APBDesTipe = 'pendapatan' | 'belanja' | 'pembiayaan'; + +export function isAPBDesTipe(tipe: string | null | undefined): tipe is APBDesTipe { + return tipe === 'pendapatan' || tipe === 'belanja' || tipe === 'pembiayaan'; +} + +export interface APBDesItem { + id: string; + kode: string; + uraian: string; + anggaran: number; + realisasi: number; + selisih: number; + persentase: number; + level: number; + tipe?: APBDesTipe | null; + // Additional fields from API + createdAt?: Date; + updatedAt?: Date; + deletedAt?: Date | null; + isActive?: boolean; + apbdesId?: string; +} + +export interface APBDesData { + id: string; + tahun: number | null; + items: APBDesItem[]; + image?: { id: string; url: string } | null; + file?: { id: string; url: string } | null; +} + +export function transformAPBDesData(data: any): APBDesData { + return { + ...data, + items: data.items.map((item: any) => ({ + ...item, + tipe: isAPBDesTipe(item.tipe) ? item.tipe : null + })) + }; +} diff --git a/src/app/darmasaba/(tambahan)/apbdes/page.tsx b/src/app/darmasaba/(tambahan)/apbdes/page.tsx index 7592c967..9e4847e8 100644 --- a/src/app/darmasaba/(tambahan)/apbdes/page.tsx +++ b/src/app/darmasaba/(tambahan)/apbdes/page.tsx @@ -4,19 +4,21 @@ import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa' import apbdes from '@/app/admin/(dashboard)/_state/landing-page/apbdes' import colors from '@/con/colors' -import { ActionIcon, BackgroundImage, Box, Center, Container, Group, Loader, SimpleGrid, Stack, Text, Title } from '@mantine/core' +import { ActionIcon, BackgroundImage, Box, Center, Container, Group, Loader, Select, SimpleGrid, Stack, Text, Title } from '@mantine/core' import { IconDownload } from '@tabler/icons-react' import { Link } from 'next-view-transitions' import { useEffect, useState } from 'react' import { useProxy } from 'valtio/utils' import BackButton from '../../(pages)/desa/layanan/_com/BackButto' -import APBDesProgress from './lib/apbDesaProgress' import APBDesTable from './lib/apbDesaTable' +import APBDesProgress from './lib/apbDesaProgress' +import { transformAPBDesData } from './lib/types' function Page() { const state = useProxy(apbdes) const paDesaState = useProxy(PendapatanAsliDesa.ApbDesa) const [loading, setLoading] = useState(false) + const [selectedYear, setSelectedYear] = useState(null) useEffect(() => { const loadData = async () => { try { @@ -34,6 +36,23 @@ function Page() { const dataAPBDes = state.findMany.data || [] + // Buat daftar tahun unik dari data + const years = Array.from(new Set(dataAPBDes.map((item: any) => item.tahun))) + .sort((a, b) => b - a) // urutkan descending + .map(year => ({ value: year.toString(), label: `Tahun ${year}` })) + + // Pilih tahun pertama sebagai default jika belum ada yang dipilih + useEffect(() => { + if (years.length > 0 && !selectedYear) { + setSelectedYear(years[0].value) + } + }, [years, selectedYear]) + + // Transform and filter data based on selected year + const currentApbdes = dataAPBDes.length > 0 + ? transformAPBDesData(dataAPBDes.find(item => item?.tahun?.toString() === selectedYear) || dataAPBDes[0]) + : null + return ( @@ -94,8 +113,31 @@ function Page() { ))} )} - - + {/* 🔥 COMBOBOX UNTUK PILIH TAHUN */} + + + + + {currentApbdes ? ( + <> + + + ) : ( + + Tidak ada data APBDes untuk tahun yang dipilih. + + )} {loading ? ( @@ -129,7 +172,7 @@ function Apbdes() { )} - + ) }