From 8159216a2c7f61fa0afbec22b7404260188c5af9 Mon Sep 17 00:00:00 2001 From: nico Date: Tue, 24 Mar 2026 23:17:23 +0800 Subject: [PATCH] Refactor ui keuangan --- src/components/keuangan-anggaran.tsx | 675 +++++++++++++++++---------- src/routes/bantuan.tsx | 6 +- src/routes/bumdes.tsx | 48 +- src/routes/keamanan.tsx | 48 +- src/routes/sosial.tsx | 48 +- 5 files changed, 561 insertions(+), 264 deletions(-) diff --git a/src/components/keuangan-anggaran.tsx b/src/components/keuangan-anggaran.tsx index 47ef4db..10498cd 100644 --- a/src/components/keuangan-anggaran.tsx +++ b/src/components/keuangan-anggaran.tsx @@ -1,73 +1,70 @@ -import { BarChart } from "@mantine/charts"; import { Badge, Box, - Button, Card, Grid, + GridCol, Group, - Progress, Stack, Text, + ThemeIcon, Title, useMantineColorScheme, } from "@mantine/core"; import { - IconCurrency, - IconTrendingDown, - IconTrendingUp, -} from "@tabler/icons-react"; -import React from "react"; + Coins, + CheckCircle, + TrendingUp, + TrendingDown, + PieChart as PieChartIcon, + Receipt, +} from "lucide-react"; +import { + Bar, + BarChart, + CartesianGrid, + Line, + LineChart, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from "recharts"; -// Sample Data +// KPI Data const kpiData = [ { id: 1, title: "Total APBDes", value: "Rp 5.2M", - sub: "Tahun 2025", - icon: , + subtitle: "Tahun 2025", + icon: Coins, }, { id: 2, title: "Realisasi", value: "68%", - sub: "Rp 3.5M dari 5.2M", - icon: ( - - - - ), + subtitle: "Rp 3.5M dari 5.2M", + icon: CheckCircle, }, { id: 3, title: "Pemasukan", value: "Rp 580jt", - sub: "Bulan ini", - delta: "+8%", - deltaType: "positive", - icon: , + subtitle: "Bulan ini", + trend: "+8%", + icon: TrendingUp, }, { id: 4, title: "Pengeluaran", value: "Rp 520jt", - sub: "Bulan ini", - icon: , + subtitle: "Bulan ini", + icon: TrendingDown, }, ]; +// Income & Expense Data const incomeExpenseData = [ { month: "Apr", income: 450, expense: 380 }, { month: "Mei", income: 520, expense: 420 }, @@ -78,6 +75,7 @@ const incomeExpenseData = [ { month: "Okt", income: 580, expense: 520 }, ]; +// Sector Allocation Data const allocationData = [ { sector: "Pembangunan", amount: 1200 }, { sector: "Kesehatan", amount: 800 }, @@ -87,13 +85,7 @@ const allocationData = [ { sector: "Teknologi", amount: 300 }, ]; -const assistanceFundData = [ - { source: "Dana Desa (DD)", amount: 1800, status: "cair" }, - { source: "Alokasi Dana Desa (ADD)", amount: 950, status: "cair" }, - { source: "Bagi Hasil Pajak", amount: 450, status: "cair" }, - { source: "Hibah Provinsi", amount: 300, status: "proses" }, -]; - +// APBDes Report Data const apbdReport = { income: [ { category: "Dana Desa", amount: 1800 }, @@ -113,244 +105,423 @@ const apbdReport = { totalExpenses: 2155, }; +// Aid & Grants Data +const assistanceFundData = [ + { source: "Dana Desa (DD)", amount: 1800, status: "cair" }, + { source: "Alokasi Dana Desa (ADD)", amount: 950, status: "cair" }, + { source: "Bagi Hasil Pajak", amount: 450, status: "cair" }, + { source: "Hibah Provinsi", amount: 300, status: "proses" }, +]; + const KeuanganAnggaran = () => { const { colorScheme } = useMantineColorScheme(); const dark = colorScheme === "dark"; - return ( - - - {/* KPI Cards */} - - {kpiData.map((kpi) => ( - - - - - {kpi.title} - - {React.cloneElement(kpi.icon, { - className: "h-6 w-6", - color: "var(--mantine-color-dimmed)", - })} - - - {kpi.value} - - {kpi.delta && ( - - {kpi.delta} - - )} - {kpi.sub && ( - - {kpi.sub} - - )} - - - ))} - - {/* Charts Section */} - - {/* Grafik Pemasukan vs Pengeluaran */} - + return ( + + {/* TOP SECTION - 4 STAT CARDS */} + + {kpiData.map((item) => ( + - - Pemasukan vs Pengeluaran - - + + + + {item.title} + + + {item.value} + + + {item.trend && ( + + )} + + {item.subtitle} + + + + + + + + ))} + - {/* Alokasi Anggaran Per Sektor */} - - - + {/* MAIN CHART SECTION */} + <Grid gutter="lg"> + {/* LEFT: PEMASUKAN DAN PENGELUARAN (70%) */} + <Grid.Col span={{ base: 12, lg: 8 }}> + <Card + p="md" + radius="xl" + withBorder + bg={dark ? "#1E293B" : "white"} + style={{ + borderColor: dark ? "#334155" : "white", + boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)", + }} + h="100%" + > + <Group gap="xs" mb="md"> + <ThemeIcon color="#1E3A5F" variant="filled" size="sm" radius="sm"> + <PieChartIcon size={14} /> + </ThemeIcon> + <Title order={4} c={dark ? "white" : "gray.9"}> + Pemasukan dan Pengeluaran + + + + + + + `Rp ${value}jt`} + /> + [ + `Rp ${value}jt`, + "", + ]} + /> + + + + + + + + {/* RIGHT: ALOKASI ANGGARAN PER SEKTOR (30%) */} + + + + + + + Alokasi Anggaran Per Sektor - - - - + + + + + `${value}`} + /> + + [`Rp ${value}jt`, "Jumlah"]} + /> + + + + + + - - {/* Dana Bantuan & Hibah */} - - - - Dana Bantuan & Hibah + {/* BOTTOM SECTION */} + <Grid gutter="lg"> + {/* LEFT: LAPORAN APBDES */} + <Grid.Col span={{ base: 12, lg: 6 }}> + <Card + p="md" + radius="xl" + withBorder + bg={dark ? "#1E293B" : "white"} + style={{ + borderColor: dark ? "#334155" : "white", + boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)", + }} + h="100%" + > + <Group gap="xs" mb="md"> + <ThemeIcon color="#1E3A5F" variant="filled" size="sm" radius="sm"> + <Receipt size={14} /> + </ThemeIcon> + <Title order={4} c={dark ? "white" : "gray.9"}> + Laporan APBDes - - {assistanceFundData.map((fund, index) => ( - + + + + {/* Pendapatan */} + + + + Pendapatan + + + {apbdReport.income.map((item, index) => ( + + + {item.category} + + + Rp {item.amount.toLocaleString()}jt + + + ))} + + + Total: + + + Rp {apbdReport.totalIncome.toLocaleString()}jt + + + + + + + {/* Belanja */} + + + + Belanja + + + {apbdReport.expenses.map((item, index) => ( + + + {item.category} + + + Rp {item.amount.toLocaleString()}jt + + + ))} + + + Total: + + + Rp {apbdReport.totalExpenses.toLocaleString()}jt + + + + + + + + {/* Saldo */} + + + Saldo: + + apbdReport.totalExpenses + ? "#22C55E" + : "#EF4444" + } + > + Rp{" "} + {( + apbdReport.totalIncome - apbdReport.totalExpenses + ).toLocaleString()} + jt + + + + + + {/* RIGHT: DANA BANTUAN DAN HIBAH */} + + + + + + + + Dana Bantuan dan Hibah + + + + {assistanceFundData.map((fund, index) => ( + + - + {fund.source} - + Rp {fund.amount.toLocaleString()}jt - {fund.status} + {fund.status === "cair" ? "Cair" : "Proses"} - ))} - - - - - {/* Laporan APBDes */} - - - - Laporan APBDes - - - - - Pendapatan - - - {apbdReport.income.map((item, index) => ( - - {item.category} - - Rp {item.amount.toLocaleString()}jt - - - ))} - - Total Pendapatan: - - Rp {apbdReport.totalIncome.toLocaleString()}jt - - - - - - - - Belanja - - - {apbdReport.expenses.map((item, index) => ( - - {item.category} - - Rp {item.amount.toLocaleString()}jt - - - ))} - - Total Belanja: - - Rp {apbdReport.totalExpenses.toLocaleString()}jt - - - - - - - - Saldo: - apbdReport.totalExpenses - ? "green" - : "red" - } - > - Rp{" "} - {( - apbdReport.totalIncome - apbdReport.totalExpenses - ).toLocaleString()} - jt - - - - - - - - + + ))} + + + + + ); }; diff --git a/src/routes/bantuan.tsx b/src/routes/bantuan.tsx index 2d45d0e..cd6170c 100644 --- a/src/routes/bantuan.tsx +++ b/src/routes/bantuan.tsx @@ -2,14 +2,14 @@ import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core"; import { useDisclosure } from "@mantine/hooks"; import { createFileRoute } from "@tanstack/react-router"; import { Header } from "@/components/header"; -import HelpPage from "@/components/help-page"; import { Sidebar } from "@/components/sidebar"; +import HelpPage from "@/components/help-page"; export const Route = createFileRoute("/bantuan")({ - component: BantuanPage, + component: BantuanRoute, }); -function BantuanPage() { +function BantuanRoute() { const [opened, { toggle }] = useDisclosure(); const { colorScheme } = useMantineColorScheme(); const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E"; diff --git a/src/routes/bumdes.tsx b/src/routes/bumdes.tsx index 2a85961..d1660fa 100644 --- a/src/routes/bumdes.tsx +++ b/src/routes/bumdes.tsx @@ -1,9 +1,51 @@ +import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core"; +import { useDisclosure } from "@mantine/hooks"; import { createFileRoute } from "@tanstack/react-router"; +import { Header } from "@/components/header"; +import { Sidebar } from "@/components/sidebar"; +import BumdesPage from "@/components/bumdes-page"; export const Route = createFileRoute("/bumdes")({ - component: RouteComponent, + component: BumdesRoute, }); -function RouteComponent() { - return
Hello "/bumdes"!
; +function BumdesRoute() { + const [opened, { toggle }] = useDisclosure(); + const { colorScheme } = useMantineColorScheme(); + const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E"; + const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white"; + const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff"; + + return ( + + + + +
+ + + + +
+ +
+
+ + + + + + ); } diff --git a/src/routes/keamanan.tsx b/src/routes/keamanan.tsx index 8ad1d6c..6223907 100644 --- a/src/routes/keamanan.tsx +++ b/src/routes/keamanan.tsx @@ -1,9 +1,51 @@ +import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core"; +import { useDisclosure } from "@mantine/hooks"; import { createFileRoute } from "@tanstack/react-router"; +import { Header } from "@/components/header"; +import { Sidebar } from "@/components/sidebar"; +import KeamananPage from "@/components/keamanan-page"; export const Route = createFileRoute("/keamanan")({ - component: RouteComponent, + component: KeamananRoute, }); -function RouteComponent() { - return
Hello "/keamanan"!
; +function KeamananRoute() { + const [opened, { toggle }] = useDisclosure(); + const { colorScheme } = useMantineColorScheme(); + const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E"; + const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white"; + const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff"; + + return ( + + + + +
+ + + + +
+ +
+
+ + + + + + ); } diff --git a/src/routes/sosial.tsx b/src/routes/sosial.tsx index 656e360..4a3d79f 100644 --- a/src/routes/sosial.tsx +++ b/src/routes/sosial.tsx @@ -1,9 +1,51 @@ +import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core"; +import { useDisclosure } from "@mantine/hooks"; import { createFileRoute } from "@tanstack/react-router"; +import { Header } from "@/components/header"; +import { Sidebar } from "@/components/sidebar"; +import SosialPage from "@/components/sosial-page"; export const Route = createFileRoute("/sosial")({ - component: RouteComponent, + component: SosialRoute, }); -function RouteComponent() { - return
Hello "/sosial"!
; +function SosialRoute() { + const [opened, { toggle }] = useDisclosure(); + const { colorScheme } = useMantineColorScheme(); + const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E"; + const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white"; + const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff"; + + return ( + + + + +
+ + + + +
+ +
+
+ + + + + + ); }