Fix SDGs Desa Barchart sudah responsive, tabel dan bar progress di menu apbdes sudah sesuai dengan data
This commit is contained in:
@@ -4,12 +4,14 @@
|
||||
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, Paper, Progress, SimpleGrid, Stack, Table, Text, Title } from '@mantine/core'
|
||||
import { ActionIcon, BackgroundImage, Box, Center, Container, Group, Loader, 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'
|
||||
|
||||
function Page() {
|
||||
const state = useProxy(apbdes)
|
||||
@@ -92,200 +94,10 @@ function Page() {
|
||||
))}
|
||||
</SimpleGrid>
|
||||
)}
|
||||
<DetailAPBDesaTable />
|
||||
<APBDesaProgress />
|
||||
<APBDesTable />
|
||||
<APBDesProgress />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
function DetailAPBDesaTable() {
|
||||
// 🔹 Dummy data
|
||||
const data = {
|
||||
tahun: 2024,
|
||||
pendapatan: [
|
||||
{ id: 1, nama: 'Pendapatan Asli Desa', anggaran: 32000000, realisasi: 6500000 },
|
||||
{ id: 2, nama: 'Dana Desa', anggaran: 125000000, realisasi: 120000000 },
|
||||
{ id: 3, nama: 'Bagi Hasil Pajak dan Retribusi', anggaran: 10000000, realisasi: 9000000 },
|
||||
],
|
||||
belanja: [
|
||||
{ id: 1, nama: 'Belanja Pegawai', anggaran: 80000000, realisasi: 75000000 },
|
||||
{ id: 2, nama: 'Belanja Barang & Jasa', anggaran: 50000000, realisasi: 42000000 },
|
||||
],
|
||||
pembiayaan: [
|
||||
{ id: 1, nama: 'Penerimaan Pembiayaan', anggaran: 15000000, realisasi: 15000000 },
|
||||
{ id: 2, nama: 'Pengeluaran Pembiayaan', anggaran: 10000000, realisasi: 8000000 },
|
||||
],
|
||||
};
|
||||
|
||||
const formatRupiah = (value: number) =>
|
||||
new Intl.NumberFormat('id-ID', {
|
||||
minimumFractionDigits: 2,
|
||||
style: 'decimal',
|
||||
}).format(value);
|
||||
|
||||
// 🔹 Helper buat render satu kategori (Pendapatan, Belanja, Pembiayaan)
|
||||
const renderSection = (title: string, items: any[]) => {
|
||||
const totalAnggaran = items.reduce((sum, i) => sum + Number(i.anggaran || 0), 0);
|
||||
const totalRealisasi = items.reduce((sum, i) => sum + Number(i.realisasi || 0), 0);
|
||||
const totalSelisih = totalAnggaran - totalRealisasi;
|
||||
const totalPersen = totalAnggaran
|
||||
? (totalRealisasi / totalAnggaran) * 100
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<Paper withBorder radius="md" shadow="xs" p="md">
|
||||
<Title order={5} mb="sm" c={colors['blue-button']}>
|
||||
{title.toUpperCase()}
|
||||
</Title>
|
||||
<Table withColumnBorders highlightOnHover>
|
||||
<Table.Thead bg={colors['blue-button']}>
|
||||
<Table.Tr>
|
||||
<Table.Th c="white">Uraian</Table.Th>
|
||||
<Table.Th c="white" ta="right">
|
||||
Anggaran (Rp)
|
||||
</Table.Th>
|
||||
<Table.Th c="white" ta="right">
|
||||
Realisasi (Rp)
|
||||
</Table.Th>
|
||||
<Table.Th c="white" ta="right">
|
||||
Lebih/(Kurang) (Rp)
|
||||
</Table.Th>
|
||||
<Table.Th c="white" ta="center">
|
||||
Persentase (%)
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{items.map((item, index) => {
|
||||
const selisih = Number(item.anggaran) - Number(item.realisasi);
|
||||
const persen = item.anggaran
|
||||
? (item.realisasi / item.anggaran) * 100
|
||||
: 0;
|
||||
return (
|
||||
<Table.Tr key={item.id || index}>
|
||||
<Table.Td>
|
||||
<strong>{`${index + 1}. ${item.nama}`}</strong>
|
||||
</Table.Td>
|
||||
<Table.Td ta="right">{formatRupiah(item.anggaran)}</Table.Td>
|
||||
<Table.Td ta="right">{formatRupiah(item.realisasi)}</Table.Td>
|
||||
<Table.Td ta="right">{formatRupiah(selisih)}</Table.Td>
|
||||
<Table.Td ta="center">{persen.toFixed(2)}</Table.Td>
|
||||
</Table.Tr>
|
||||
);
|
||||
})}
|
||||
</Table.Tbody>
|
||||
<Table.Tfoot bg="#f1f5fb">
|
||||
<Table.Tr>
|
||||
<Table.Th>Total {title}</Table.Th>
|
||||
<Table.Th ta="right">{formatRupiah(totalAnggaran)}</Table.Th>
|
||||
<Table.Th ta="right">{formatRupiah(totalRealisasi)}</Table.Th>
|
||||
<Table.Th ta="right">{formatRupiah(totalSelisih)}</Table.Th>
|
||||
<Table.Th ta="center">{totalPersen.toFixed(2)}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Tfoot>
|
||||
</Table>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box py="md" px={{ base: 'md', md: 100 }}>
|
||||
<Stack gap="xl">
|
||||
<Title order={4} c={colors['blue-button']}>
|
||||
APB Desa Tahun {data.tahun}
|
||||
</Title>
|
||||
|
||||
{renderSection('Pendapatan', data.pendapatan)}
|
||||
{renderSection('Belanja', data.belanja)}
|
||||
{renderSection('Pembiayaan', data.pembiayaan)}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function APBDesaProgress() {
|
||||
const data = {
|
||||
tahun: 2024,
|
||||
pendapatan: [
|
||||
{ id: 1, nama: 'Pendapatan Asli Desa', anggaran: 32000000, realisasi: 6500000 },
|
||||
{ id: 2, nama: 'Dana Desa', anggaran: 125000000, realisasi: 120000000 },
|
||||
{ id: 3, nama: 'Bagi Hasil Pajak dan Retribusi', anggaran: 10000000, realisasi: 9000000 },
|
||||
],
|
||||
belanja: [
|
||||
{ id: 1, nama: 'Belanja Pegawai', anggaran: 80000000, realisasi: 75000000 },
|
||||
{ id: 2, nama: 'Belanja Barang & Jasa', anggaran: 50000000, realisasi: 42000000 },
|
||||
],
|
||||
pembiayaan: [
|
||||
{ id: 1, nama: 'Penerimaan Pembiayaan', anggaran: 15000000, realisasi: 15000000 },
|
||||
{ id: 2, nama: 'Pengeluaran Pembiayaan', anggaran: 10000000, realisasi: 8000000 },
|
||||
],
|
||||
};
|
||||
|
||||
const formatRupiah = (value: number) =>
|
||||
new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 2,
|
||||
}).format(value);
|
||||
|
||||
const calcProgress = (items: any[]) => {
|
||||
const anggaran = items.reduce((sum, i) => sum + i.anggaran, 0);
|
||||
const realisasi = items.reduce((sum, i) => sum + i.realisasi, 0);
|
||||
const persen = anggaran ? (realisasi / anggaran) * 100 : 0;
|
||||
return { anggaran, realisasi, persen };
|
||||
};
|
||||
|
||||
const pendapatan = calcProgress(data.pendapatan);
|
||||
const belanja = calcProgress(data.belanja);
|
||||
const pembiayaan = calcProgress(data.pembiayaan);
|
||||
|
||||
const renderProgress = (label: string, dataset: any) => (
|
||||
<Box>
|
||||
<Text fw={600}>{label}</Text>
|
||||
<Text fw={700} mb="xs">
|
||||
{formatRupiah(dataset.realisasi)} | {formatRupiah(dataset.anggaran)}
|
||||
</Text>
|
||||
<Progress
|
||||
value={dataset.persen}
|
||||
size="xl"
|
||||
radius="xl"
|
||||
striped={false}
|
||||
styles={{
|
||||
root: { backgroundColor: '#d7e3f1' },
|
||||
section: {
|
||||
backgroundColor: colors['blue-button'],
|
||||
position: 'relative',
|
||||
'&::after': {
|
||||
content: `'${dataset.persen.toFixed(2)}%'`,
|
||||
position: 'absolute',
|
||||
right: 10,
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
color: 'white',
|
||||
fontWeight: 700,
|
||||
fontSize: '0.8rem',
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<Paper mx={{ base: 'md', md: 100 }} p="xl" radius="md" shadow="sm" withBorder bg={colors['white-1']}>
|
||||
<Stack gap="lg">
|
||||
<Title order={4} c={colors['blue-button']}>
|
||||
Grafik APB Desa Tahun {data.tahun}
|
||||
</Title>
|
||||
|
||||
{renderProgress('Pendapatan Desa', pendapatan)}
|
||||
{renderProgress('Belanja Desa', belanja)}
|
||||
{renderProgress('Pembiayaan Desa', pembiayaan)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default Page
|
||||
export default Page
|
||||
Reference in New Issue
Block a user