QC Admin - User Menu Ekonomi : Jumlah Pengangguran

This commit is contained in:
2025-09-16 10:11:54 +08:00
parent a5d841bb6b
commit 4ceea5203f
97 changed files with 6023 additions and 3481 deletions

View File

@@ -3,7 +3,19 @@
/* eslint-disable react-hooks/exhaustive-deps */
import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
import colors from '@/con/colors';
import { Box, Button, MultiSelect, Paper, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
import {
Box,
Button,
MultiSelect,
Paper,
Skeleton,
Stack,
Text,
TextInput,
Title,
Tooltip,
Group,
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
@@ -23,7 +35,7 @@ function EditAPBDesa() {
pembiayaanIds: apbState.update.form.pembiayaanIds || [],
});
// Load APB desa by id saat pertama kali
// Load APB desa by id
useEffect(() => {
const loadAPBdesa = async () => {
const id = params?.id as string;
@@ -46,12 +58,10 @@ function EditAPBDesa() {
};
loadAPBdesa();
}, [params?.id]); // ✅ hapus beritaState dari dependency
}, [params?.id]);
const handleSubmit = async () => {
try {
// Update global state with form data
apbState.update.form = {
...apbState.update.form,
tahun: Number(formData.tahun),
@@ -70,65 +80,95 @@ function EditAPBDesa() {
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors["blue-button"]} size={30} />
</Button>
</Box>
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
<Stack gap={"xs"}>
<Title order={3}>Edit APB Desa</Title>
<Box px={{ base: 'sm', md: 'lg' }} py="md">
{/* Header */}
<Group mb="md">
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
<Button
variant="subtle"
onClick={() => router.back()}
p="xs"
radius="md"
>
<IconArrowBack color={colors['blue-button']} size={24} />
</Button>
</Tooltip>
<Title order={4} ml="sm" c="dark">
Edit APB Desa
</Title>
</Group>
{/* Form Card */}
<Paper
w={{ base: '100%', md: '50%' }}
bg={colors['white-1']}
p="lg"
radius="md"
shadow="sm"
style={{ border: '1px solid #e0e0e0' }}
>
<Stack gap="md">
{/* Tahun */}
<TextInput
type='number'
type="number"
value={formData.tahun}
onChange={(val) => {
setFormData({ ...formData, tahun: val.target.value });
}}
label={<Text fz={"sm"} fw={"bold"}>Tahun</Text>}
placeholder="masukkan tahun"
onChange={(e) =>
setFormData({ ...formData, tahun: e.target.value })
}
label={<Text fz="sm" fw="bold">Tahun</Text>}
placeholder="Masukkan tahun anggaran"
required
/>
{/* Selects */}
<SelectPendapatan
selectedIds={formData.pendapatanIds}
onSelectionChange={(ids) => {
setFormData({ ...formData, pendapatanIds: ids });
}}
onSelectionChange={(ids) =>
setFormData({ ...formData, pendapatanIds: ids })
}
/>
<SelectBelanja
selectedIds={formData.belanjaIds}
onSelectionChange={(ids) => {
setFormData({ ...formData, belanjaIds: ids });
}}
onSelectionChange={(ids) =>
setFormData({ ...formData, belanjaIds: ids })
}
/>
<SelectPembiayaan
selectedIds={formData.pembiayaanIds}
onSelectionChange={(ids) => {
setFormData({ ...formData, pembiayaanIds: ids });
}}
onSelectionChange={(ids) =>
setFormData({ ...formData, pembiayaanIds: ids })
}
/>
<Button onClick={handleSubmit}>Simpan</Button>
{/* Save Button */}
<Group justify="right">
<Button
onClick={handleSubmit}
radius="md"
size="md"
style={{
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
color: '#fff',
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
}}
>
Simpan
</Button>
</Group>
</Stack>
</Paper>
</Box>
);
/* --- Sub Components --- */
/* Select Pendapatan */
interface SelectPendapatanProps {
selectedIds: string[];
onSelectionChange: (ids: string[]) => void;
}
function SelectPendapatan({
selectedIds = [],
onSelectionChange,
}: SelectPendapatanProps) {
function SelectPendapatan({ selectedIds, onSelectionChange }: { selectedIds: string[]; onSelectionChange: (ids: string[]) => void }) {
const pendapatanState = useProxy(PendapatanAsliDesa.pendapatan);
useShallowEffect(() => {
pendapatanState.findMany.load().then(() => {
console.log("Pendapatan berhasil dimuat:", pendapatanState.findMany.data);
});
pendapatanState.findMany.load();
}, []);
if (!pendapatanState.findMany.data) {
@@ -137,10 +177,10 @@ function EditAPBDesa() {
return (
<MultiSelect
label={<Text fz={"sm"} fw={"bold"}>Pendapatan</Text>}
data={pendapatanState.findMany.data.map(p => ({
label={<Text fz="sm" fw="bold">Pendapatan</Text>}
data={pendapatanState.findMany.data.map((p: any) => ({
value: p.id,
label: p.name
label: p.name,
}))}
value={selectedIds}
onChange={onSelectionChange}
@@ -152,22 +192,11 @@ function EditAPBDesa() {
);
}
/* Select Belanja */
interface SelectBelanjaProps {
selectedIds: string[];
onSelectionChange: (ids: string[]) => void;
}
function SelectBelanja({
selectedIds = [],
onSelectionChange,
}: SelectBelanjaProps) {
function SelectBelanja({ selectedIds, onSelectionChange }: { selectedIds: string[]; onSelectionChange: (ids: string[]) => void }) {
const belanjaState = useProxy(PendapatanAsliDesa.belanja);
useShallowEffect(() => {
belanjaState.findMany.load().then(() => {
console.log("Belanja berhasil dimuat:", belanjaState.findMany.data);
});
belanjaState.findMany.load();
}, []);
if (!belanjaState.findMany.data) {
@@ -176,10 +205,10 @@ function EditAPBDesa() {
return (
<MultiSelect
label={<Text fz={"sm"} fw={"bold"}>Belanja</Text>}
data={belanjaState.findMany.data.map(b => ({
label={<Text fz="sm" fw="bold">Belanja</Text>}
data={belanjaState.findMany.data.map((b: any) => ({
value: b.id,
label: b.name
label: b.name,
}))}
value={selectedIds}
onChange={onSelectionChange}
@@ -191,22 +220,11 @@ function EditAPBDesa() {
);
}
/* Select Pembiayaan */
interface SelectPembiayaanProps {
selectedIds: string[];
onSelectionChange: (ids: string[]) => void;
}
function SelectPembiayaan({
selectedIds = [],
onSelectionChange,
}: SelectPembiayaanProps) {
function SelectPembiayaan({ selectedIds, onSelectionChange }: { selectedIds: string[]; onSelectionChange: (ids: string[]) => void }) {
const pembiayaanState = useProxy(PendapatanAsliDesa.pembiayaan);
useShallowEffect(() => {
pembiayaanState.findMany.load().then(() => {
console.log("Pembiayaan berhasil dimuat:", pembiayaanState.findMany.data);
});
pembiayaanState.findMany.load();
}, []);
if (!pembiayaanState.findMany.data) {
@@ -215,10 +233,10 @@ function EditAPBDesa() {
return (
<MultiSelect
label={<Text fz={"sm"} fw={"bold"}>Pembiayaan</Text>}
data={pembiayaanState.findMany.data.map(b => ({
value: b.id,
label: b.name
label={<Text fz="sm" fw="bold">Pembiayaan</Text>}
data={pembiayaanState.findMany.data.map((p: any) => ({
value: p.id,
label: p.name,
}))}
value={selectedIds}
onChange={onSelectionChange}

View File

@@ -2,148 +2,204 @@
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
import colors from '@/con/colors';
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
import {
Box,
Button,
Group,
Paper,
Skeleton,
Stack,
Text,
Tooltip
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
function DetailAPBDesa() {
const apbState = useProxy(PendapatanAsliDesa.ApbDesa)
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const params = useParams()
const router = useRouter()
const apbState = useProxy(PendapatanAsliDesa.ApbDesa);
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState<string | null>(null);
const params = useParams();
const router = useRouter();
useShallowEffect(() => {
console.log("PARAM ID:", params?.id)
apbState.findUnique.load(params?.id as string)
}, [])
apbState.findUnique.load(params?.id as string);
}, []);
const formatRupiah = (value: number) => {
return new Intl.NumberFormat('id-ID', {
const formatRupiah = (value: number) =>
new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
minimumFractionDigits: 0,
}).format(value);
};
const handleHapus = () => {
if (selectedId) {
apbState.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
router.push("/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa")
apbState.delete.byId(selectedId);
setModalHapus(false);
setSelectedId(null);
router.push(
'/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa'
);
}
}
};
if (!apbState.findUnique.data) {
return (
<Stack py={10}>
<Skeleton h={40} />
<Skeleton height={500} radius="md" />
</Stack>
)
);
}
const data = apbState.findUnique.data;
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
<Stack>
<Text fz={"xl"} fw={"bold"}>Detail APB Desa</Text>
{apbState.findUnique.data ? (
<Paper key={apbState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fw={"bold"} fz={"lg"}>Tahun</Text>
<Text fz={"lg"}>{apbState.findUnique.data?.tahun}</Text>
</Box>
<Box>
<Stack gap={"xs"}>
<Text fw={"bold"} fz={"lg"}>Detail Pembiayaan</Text>
{(apbState.findUnique.data?.pembiayaan || []).map((item) => (
<Text fz={"lg"} key={item.id}>
{item.name}: {formatRupiah(Number(item.value))}
</Text>
))}
<Text fz={"lg"} fw={"bold"}>
Total: {formatRupiah((apbState.findUnique.data?.pembiayaan || [])
.reduce((sum, item) => sum + Number(item.value), 0))}
<Box py={10}>
<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%', lg: '50%' }}
bg={colors['white-1']}
p="lg"
radius="md"
shadow="sm"
>
<Stack gap="md">
<Text fz="2xl" fw="bold" c={colors['blue-button']}>
Detail APB Desa
</Text>
<Paper bg={colors['BG-trans']} p="md" radius="md" shadow="xs">
<Stack gap="sm">
<Box>
<Text fz="lg" fw="bold">
Tahun
</Text>
<Text fz="md" c="dimmed">
{data.tahun}
</Text>
</Box>
<Box>
<Stack gap="xs">
<Text fw="bold" fz="lg">
Detail Pembiayaan
</Text>
{(data?.pembiayaan || []).map((item) => (
<Text fz="md" c="dimmed" key={item.id}>
{item.name}: {formatRupiah(Number(item.value))}
</Text>
</Stack>
</Box>
<Box>
<Stack gap={"xs"}>
<Text fw={"bold"} fz={"lg"}>Detail Belanja</Text>
{(apbState.findUnique.data?.belanja || []).map((item) => (
<Text fz={"lg"} key={item.id}>
{item.name}: {formatRupiah(Number(item.value))}
</Text>
))}
<Text fz={"lg"} fw={"bold"}>
Total: {formatRupiah((apbState.findUnique.data?.belanja || [])
.reduce((sum, item) => sum + Number(item.value), 0))}
))}
<Text fz="md" fw="bold">
Total:{' '}
{formatRupiah(
(data?.pembiayaan || []).reduce(
(sum, item) => sum + Number(item.value),
0
)
)}
</Text>
</Stack>
</Box>
<Box>
<Stack gap="xs">
<Text fw="bold" fz="lg">
Detail Belanja
</Text>
{(data?.belanja || []).map((item) => (
<Text fz="md" c="dimmed" key={item.id}>
{item.name}: {formatRupiah(Number(item.value))}
</Text>
</Stack>
</Box>
<Box>
<Stack gap={"xs"}>
<Text fw={"bold"} fz={"lg"}>Detail Pendapatan</Text>
{(apbState.findUnique.data?.pendapatan || []).map((item) => (
<Text fz={"lg"} key={item.id}>
{item.name}: {formatRupiah(Number(item.value))}
</Text>
))}
<Text fz={"lg"} fw={"bold"}>
Total: {formatRupiah((apbState.findUnique.data?.pendapatan || [])
.reduce((sum, item) => sum + Number(item.value), 0))}
))}
<Text fz="md" fw="bold">
Total:{' '}
{formatRupiah(
(data?.belanja || []).reduce(
(sum, item) => sum + Number(item.value),
0
)
)}
</Text>
</Stack>
</Box>
<Box>
<Stack gap="xs">
<Text fw="bold" fz="lg">
Detail Pendapatan
</Text>
{(data?.pendapatan || []).map((item) => (
<Text fz="md" c="dimmed" key={item.id}>
{item.name}: {formatRupiah(Number(item.value))}
</Text>
</Stack>
</Box>
<Flex gap={"xs"} mt={10}>
))}
<Text fz="md" fw="bold">
Total:{' '}
{formatRupiah(
(data?.pendapatan || []).reduce(
(sum, item) => sum + Number(item.value),
0
)
)}
</Text>
</Stack>
</Box>
<Group gap="sm" mt={10}>
<Tooltip label="Hapus APB Desa" withArrow position="top">
<Button
color="red"
onClick={() => {
if (apbState.findUnique.data) {
setSelectedId(apbState.findUnique.data.id);
setModalHapus(true);
}
setSelectedId(data.id);
setModalHapus(true);
}}
disabled={apbState.delete.loading || !apbState.findUnique.data}
color={"red"}
variant="light"
radius="md"
size="md"
>
<IconX size={20} />
<IconTrash size={20} />
</Button>
</Tooltip>
<Tooltip label="Edit APB Desa" withArrow position="top">
<Button
onClick={() => {
if (apbState.findUnique.data) {
router.push(`/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/${apbState.findUnique.data.id}/edit`);
}
}}
disabled={!apbState.findUnique.data}
color={"green"}
color="green"
onClick={() =>
router.push(
`/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/${data.id}/edit`
)
}
variant="light"
radius="md"
size="md"
>
<IconEdit size={20} />
</Button>
</Flex>
</Stack>
</Paper>
) : null}
</Tooltip>
</Group>
</Stack>
</Paper>
</Stack>
</Paper>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text='Apakah anda yakin ingin menghapus APB Desa ini?'
text="Apakah Anda yakin ingin menghapus APB Desa ini?"
/>
</Box>
);

View File

@@ -1,15 +1,28 @@
'use client'
'use client';
import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
import colors from '@/con/colors';
import { Box, Button, MultiSelect, Paper, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
import {
Box,
Button,
Group,
MultiSelect,
Paper,
Skeleton,
Stack,
Text,
TextInput,
Title,
Tooltip,
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
function CreateAPBDesa() {
const apbDesaState = useProxy(PendapatanAsliDesa.ApbDesa)
const router = useRouter()
const apbDesaState = useProxy(PendapatanAsliDesa.ApbDesa);
const router = useRouter();
const resetForm = () => {
apbDesaState.create.form = {
@@ -17,74 +30,101 @@ function CreateAPBDesa() {
pendapatanIds: [],
belanjaIds: [],
pembiayaanIds: [],
}
}
};
};
const handleSubmit = async () => {
await apbDesaState.create.submit()
resetForm()
router.push("/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa")
}
await apbDesaState.create.submit();
resetForm();
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa');
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
<Stack gap={"xs"}>
<Title order={3}>Create APB Desa</Title>
<Box px={{ base: 'sm', md: 'lg' }} py="md">
{/* Header */}
<Group mb="md">
<Tooltip label="Kembali ke halaman sebelumnya" withArrow>
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
<IconArrowBack color={colors['blue-button']} size={24} />
</Button>
</Tooltip>
<Title order={4} ml="sm" c="dark">
Tambah APB Desa
</Title>
</Group>
{/* Form */}
<Paper
w={{ base: '100%', md: '50%' }}
bg={colors['white-1']}
p="lg"
radius="md"
shadow="sm"
style={{ border: '1px solid #e0e0e0' }}
>
<Stack gap="md">
<TextInput
type='number'
type="number"
value={apbDesaState.create.form.tahun}
onChange={(val) => {
apbDesaState.create.form.tahun = Number(val.target.value);
}}
label={<Text fz={"sm"} fw={"bold"}>Tahun</Text>}
placeholder="masukkan tahun"
label={<Text fz="sm" fw="bold">Tahun</Text>}
placeholder="Masukkan tahun anggaran"
required
/>
<SelectPendapatan
selectedIds={apbDesaState.create.form.pendapatanIds}
onSelectionChange={(ids) => {
apbDesaState.create.form.pendapatanIds = ids;
}}
/>
<SelectBelanja
selectedIds={apbDesaState.create.form.belanjaIds}
onSelectionChange={(ids) => {
apbDesaState.create.form.belanjaIds = ids;
}}
/>
<SelectPembiayaan
selectedIds={apbDesaState.create.form.pembiayaanIds}
onSelectionChange={(ids) => {
apbDesaState.create.form.pembiayaanIds = ids;
}}
/>
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan</Button>
{/* Action */}
<Group justify="right">
<Button
onClick={handleSubmit}
radius="md"
size="md"
style={{
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
color: '#fff',
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
}}
>
Simpan
</Button>
</Group>
</Stack>
</Paper>
</Box>
);
/* Select Pendapatan */
/* ---------- Select Pendapatan ---------- */
interface SelectPendapatanProps {
selectedIds: string[];
onSelectionChange: (ids: string[]) => void;
}
function SelectPendapatan({
selectedIds = [],
onSelectionChange,
}: SelectPendapatanProps) {
function SelectPendapatan({ selectedIds = [], onSelectionChange }: SelectPendapatanProps) {
const pendapatanState = useProxy(PendapatanAsliDesa.pendapatan);
useShallowEffect(() => {
pendapatanState.findMany.load().then(() => {
console.log("Pendapatan berhasil dimuat:", pendapatanState.findMany.data);
});
pendapatanState.findMany.load();
}, []);
if (!pendapatanState.findMany.data) {
@@ -93,10 +133,10 @@ function CreateAPBDesa() {
return (
<MultiSelect
label={<Text fz={"sm"} fw={"bold"}>Pendapatan</Text>}
data={pendapatanState.findMany.data.map(p => ({
label={<Text fz="sm" fw="bold">Pendapatan</Text>}
data={pendapatanState.findMany.data.map((p) => ({
value: p.id,
label: p.name
label: p.name,
}))}
value={selectedIds}
onChange={onSelectionChange}
@@ -108,22 +148,16 @@ function CreateAPBDesa() {
);
}
/* Select Belanja */
/* ---------- Select Belanja ---------- */
interface SelectBelanjaProps {
selectedIds: string[];
onSelectionChange: (ids: string[]) => void;
}
function SelectBelanja({
selectedIds = [],
onSelectionChange,
}: SelectBelanjaProps) {
function SelectBelanja({ selectedIds = [], onSelectionChange }: SelectBelanjaProps) {
const belanjaState = useProxy(PendapatanAsliDesa.belanja);
useShallowEffect(() => {
belanjaState.findMany.load().then(() => {
console.log("Belanja berhasil dimuat:", belanjaState.findMany.data);
});
belanjaState.findMany.load();
}, []);
if (!belanjaState.findMany.data) {
@@ -132,10 +166,10 @@ function CreateAPBDesa() {
return (
<MultiSelect
label={<Text fz={"sm"} fw={"bold"}>Belanja</Text>}
data={belanjaState.findMany.data.map(b => ({
label={<Text fz="sm" fw="bold">Belanja</Text>}
data={belanjaState.findMany.data.map((b) => ({
value: b.id,
label: b.name
label: b.name,
}))}
value={selectedIds}
onChange={onSelectionChange}
@@ -147,22 +181,16 @@ function CreateAPBDesa() {
);
}
/* Select Pembiayaan */
/* ---------- Select Pembiayaan ---------- */
interface SelectPembiayaanProps {
selectedIds: string[];
onSelectionChange: (ids: string[]) => void;
}
function SelectPembiayaan({
selectedIds = [],
onSelectionChange,
}: SelectPembiayaanProps) {
function SelectPembiayaan({ selectedIds = [], onSelectionChange }: SelectPembiayaanProps) {
const pembiayaanState = useProxy(PendapatanAsliDesa.pembiayaan);
useShallowEffect(() => {
pembiayaanState.findMany.load().then(() => {
console.log("Pembiayaan berhasil dimuat:", pembiayaanState.findMany.data);
});
pembiayaanState.findMany.load();
}, []);
if (!pembiayaanState.findMany.data) {
@@ -171,10 +199,10 @@ function CreateAPBDesa() {
return (
<MultiSelect
label={<Text fz={"sm"} fw={"bold"}>Pembiayaan</Text>}
data={pembiayaanState.findMany.data.map(b => ({
label={<Text fz="sm" fw="bold">Pembiayaan</Text>}
data={pembiayaanState.findMany.data.map((b) => ({
value: b.id,
label: b.name
label: b.name,
}))}
value={selectedIds}
onChange={onSelectionChange}

View File

@@ -1,23 +1,38 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import {
Box,
Button,
Center,
Group,
Paper,
Pagination,
Skeleton,
Stack,
Table,
TableTbody,
TableTd,
TableTh,
TableThead,
TableTr,
Text,
Tooltip,
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import PendapatanAsliDesa from '../../../_state/ekonomi/PADesa';
function APBDesa() {
const [search, setSearch] = useState("");
return (
<Box>
<HeaderSearch
title='APB Desa'
placeholder='pencarian'
title="APB Desa"
placeholder="Cari tahun atau nominal..."
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
@@ -28,77 +43,147 @@ function APBDesa() {
}
function ListAPBDesa({ search }: { search: string }) {
const apbDesaState = useProxy(PendapatanAsliDesa.ApbDesa)
const apbDesaState = useProxy(PendapatanAsliDesa.ApbDesa);
const router = useRouter();
const formatRupiah = (value: number) => {
return new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
const {
data,
page,
totalPages,
loading,
load,
} = apbDesaState.findMany;
const formatRupiah = (value: number) =>
new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
minimumFractionDigits: 0,
}).format(value);
};
useShallowEffect(() => {
apbDesaState.findMany.load();
}, [])
load(page, 10, search);
}, [page, search]);
const filteredData = (apbDesaState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.tahun.toString().toLowerCase().includes(keyword) ||
item.pembiayaan.map((item) => item.value.toString()).includes(keyword) ||
item.belanja.map((item) => item.value.toString()).includes(keyword) ||
item.pendapatan.map((item) => item.value.toString()).includes(keyword)
);
});
const filteredData = data || [];
if (!apbDesaState.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
<Skeleton height={500} radius="md" />
</Stack>
)
);
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulList
title='List APB Desa'
href='/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Tahun</TableTh>
<TableTh>Pembiayaan</TableTh>
<TableTh>Belanja</TableTh>
<TableTh>Pendapatan</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.tahun}</TableTd>
<TableTd>{formatRupiah(item.pembiayaan.reduce((sum, item) => sum + Number(item.value), 0))}</TableTd>
<TableTd>{formatRupiah(item.belanja.reduce((sum, item) => sum + Number(item.value), 0))}</TableTd>
<TableTd>{formatRupiah(item.pendapatan.reduce((sum, item) => sum + Number(item.value), 0))}</TableTd>
<TableTd>
<Button
bg={"green"}
onClick={() =>
router.push(`/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/${item.id}`)
}
>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
<Paper withBorder bg={colors["white-1"]} p="lg" shadow="md" radius="md">
<Group justify="space-between" mb="md">
<Text fw={600} fz="lg">
List APB Desa
</Text>
<Tooltip label="Tambah APB Desa" withArrow>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() =>
router.push(
"/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/create"
)
}
>
Tambah Baru
</Button>
</Tooltip>
</Group>
<Box style={{ overflowX: "auto" }}>
<Table highlightOnHover>
<TableThead>
<TableTr>
<TableTh style={{ width: "15%" }}>Tahun</TableTh>
<TableTh style={{ width: "25%" }}>Pembiayaan</TableTh>
<TableTh style={{ width: "25%" }}>Belanja</TableTh>
<TableTh style={{ width: "25%" }}>Pendapatan</TableTh>
<TableTh style={{ width: "10%" }}>Aksi</TableTh>
</TableTr>
))}
</TableTbody>
</Table>
</TableThead>
<TableTbody>
{filteredData.length > 0 ? (
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.tahun}</TableTd>
<TableTd>
{formatRupiah(
item.pembiayaan.reduce(
(sum, val) => sum + Number(val.value),
0
)
)}
</TableTd>
<TableTd>
{formatRupiah(
item.belanja.reduce(
(sum, val) => sum + Number(val.value),
0
)
)}
</TableTd>
<TableTd>
{formatRupiah(
item.pendapatan.reduce(
(sum, val) => sum + Number(val.value),
0
)
)}
</TableTd>
<TableTd>
<Tooltip label="Lihat Detail" withArrow>
<Button
variant="light"
color="green"
onClick={() =>
router.push(
`/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/${item.id}`
)
}
>
<IconDeviceImacCog size={20} />
<Text ml={5}>Detail</Text>
</Button>
</Tooltip>
</TableTd>
</TableTr>
))
) : (
<TableTr>
<TableTd colSpan={5}>
<Center py={20}>
<Text color="dimmed">
Tidak ada data APB Desa yang cocok
</Text>
</Center>
</TableTd>
</TableTr>
)}
</TableTbody>
</Table>
</Box>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10);
window.scrollTo({ top: 0, behavior: "smooth" });
}}
total={totalPages}
mt="md"
mb="md"
color="blue"
radius="md"
/>
</Center>
</Box>
);
}