Files
desa-darmasaba/src/app/admin/(dashboard)/landing-page/apbdes/[id]/page.tsx
nico c8484357cb Fix QC Kak Ayu 15 Des
Fix QC Kak Inno 15 Des
Fix UI User Font Size, Font Weight, Line Height
Fix UI Admin Font Size, Font Weight, Line Height & UI Mobile
2025-12-16 16:37:17 +08:00

234 lines
7.1 KiB
TypeScript

/* eslint-disable react-hooks/exhaustive-deps */
'use client';
import { useProxy } from 'valtio/utils';
import {
Box,
Button,
Group,
Image,
Paper,
Skeleton,
Stack,
Table,
TableTbody,
TableTd,
TableTh,
TableThead,
TableTr,
Text
} from '@mantine/core';
import { IconArrowBack, IconEdit, IconFile, IconTrash } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import colors from '@/con/colors';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import apbdes from '../../../_state/landing-page/apbdes';
function DetailAPBDes() {
const apbdesState = useProxy(apbdes);
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState<string | null>(null);
const params = useParams();
const router = useRouter();
useEffect(() => {
if (!params?.id) return;
apbdesState.findUnique.load(params.id as string);
}, [params?.id]);
const handleHapus = () => {
if (selectedId) {
apbdesState.delete.byId(selectedId);
setModalHapus(false);
setSelectedId(null);
router.push('/admin/landing-page/apbdes');
}
};
if (!apbdesState.findUnique.data) {
return (
<Stack py={10}>
<Skeleton height={600} radius="md" />
</Stack>
);
}
const data = apbdesState.findUnique.data;
// Helper: indentasi berdasarkan level
const getIndent = (level: number) => ({
paddingLeft: `${(level - 1) * 20}px`,
});
return (
<Box px={{ base: 0, md: 'xs' }} py="xs">
<Button
variant="subtle"
onClick={() => router.back()}
leftSection={<IconArrowBack size={24} color={colors['blue-button']} />}
mb={15}
>
Kembali
</Button>
<Paper
withBorder
w={{ base: '100%', md: '70%' }}
bg={colors['white-1']}
p="lg"
radius="md"
shadow="sm"
>
<Stack gap="md">
<Text fz="2xl" fw="bold" c={colors['blue-button']}>
Detail APBDes
</Text>
{/* Info Header */}
<Paper bg="#ECEEF8" p="md" radius="md" shadow="xs">
<Stack gap="md">
<Box>
<Text fz="lg" fw="bold">Nama APBDes</Text>
<Text fz="md" c="dimmed">
{data.name || '-'}
</Text>
</Box>
<Box>
<Text fz="lg" fw="bold">Tahun</Text>
<Text fz="md" c="dimmed">
{data.tahun || '-'}
</Text>
</Box>
<Box>
<Text fz="lg" fw="bold">Gambar</Text>
{data.image?.link ? (
<Image
src={data.image.link}
alt={data.name || 'Gambar APBDes'}
w={200}
height={150}
radius="md"
fit="contain"
style={{ border: '1px solid #ddd' }}
loading="lazy"
/>
) : (
<Text fz="sm" c="dimmed">Tidak ada gambar</Text>
)}
</Box>
<Box>
<Text fz="lg" fw="bold">Dokumen</Text>
{data.file?.link ? (
<Button
component="a"
href={data.file.link}
target="_blank"
rel="noopener noreferrer"
variant="light"
leftSection={<IconFile size={18} />}
size="sm"
mt="xs"
>
Lihat Dokumen
</Button>
) : (
<Text fz="sm" c="dimmed">Tidak ada dokumen</Text>
)}
</Box>
<Group gap="sm" mt="md">
<Button
color="red"
onClick={() => {
setSelectedId(data.id);
setModalHapus(true);
}}
disabled={apbdesState.delete.loading}
variant="light"
radius="md"
size="md"
>
<IconTrash size={20} />
</Button>
<Button
color="green"
onClick={() => router.push(`/admin/landing-page/apbdes/${data.id}/edit`)}
variant="light"
radius="md"
size="md"
>
<IconEdit size={20} />
</Button>
</Group>
</Stack>
</Paper>
{/* Tabel Items */}
{data.items && data.items.length > 0 ? (
<Paper withBorder p="md" radius="md">
<Text fz="lg" fw="bold" mb="sm">
Rincian Pendapatan & Belanja ({data.items.length} item)
</Text>
<Box style={{ overflowX: 'auto' }}>
<Table striped highlightOnHover>
<TableThead>
<TableTr>
<TableTh>Uraian</TableTh>
<TableTh>Anggaran (Rp)</TableTh>
<TableTh>Realisasi (Rp)</TableTh>
<TableTh>Selisih (Rp)</TableTh>
<TableTh>Persentase (%)</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{[...data.items] // Create a new array before sorting
.sort((a, b) => a.kode.localeCompare(b.kode))
.map((item) => (
<TableTr key={item.id}>
<TableTd style={getIndent(item.level)}>
<Group>
<Text fw={item.level === 1 ? 'bold' : 'normal'}>{item.kode}</Text>
<Text fz="sm" c="dimmed">{item.uraian}</Text>
</Group>
</TableTd>
<TableTd>{item.anggaran.toLocaleString('id-ID')}</TableTd>
<TableTd>{item.realisasi.toLocaleString('id-ID')}</TableTd>
<TableTd>
<Text c={item.selisih >= 0 ? 'green' : 'red'}>
{item.selisih.toLocaleString('id-ID')}
</Text>
</TableTd>
<TableTd>
<Text fw={500}>{item.persentase.toFixed(2)}%</Text>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Paper>
) : (
<Text>Belum ada data item</Text>
)}
</Stack>
</Paper>
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text="Apakah Anda yakin ingin menghapus APBDes ini?"
/>
</Box>
);
}
export default DetailAPBDes;