Refactor ui keuangan
This commit is contained in:
@@ -1,73 +1,70 @@
|
|||||||
import { BarChart } from "@mantine/charts";
|
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
|
||||||
Card,
|
Card,
|
||||||
Grid,
|
Grid,
|
||||||
|
GridCol,
|
||||||
Group,
|
Group,
|
||||||
Progress,
|
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
|
ThemeIcon,
|
||||||
Title,
|
Title,
|
||||||
useMantineColorScheme,
|
useMantineColorScheme,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import {
|
import {
|
||||||
IconCurrency,
|
Coins,
|
||||||
IconTrendingDown,
|
CheckCircle,
|
||||||
IconTrendingUp,
|
TrendingUp,
|
||||||
} from "@tabler/icons-react";
|
TrendingDown,
|
||||||
import React from "react";
|
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 = [
|
const kpiData = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: "Total APBDes",
|
title: "Total APBDes",
|
||||||
value: "Rp 5.2M",
|
value: "Rp 5.2M",
|
||||||
sub: "Tahun 2025",
|
subtitle: "Tahun 2025",
|
||||||
icon: <IconCurrency className="h-6 w-6 text-muted-foreground" />,
|
icon: Coins,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
title: "Realisasi",
|
title: "Realisasi",
|
||||||
value: "68%",
|
value: "68%",
|
||||||
sub: "Rp 3.5M dari 5.2M",
|
subtitle: "Rp 3.5M dari 5.2M",
|
||||||
icon: (
|
icon: CheckCircle,
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
strokeWidth={1.5}
|
|
||||||
stroke="currentColor"
|
|
||||||
className="h-6 w-6 text-muted-foreground"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
d="M9 12.75 11.25 15 15 9.75M21 12c0 1.268-.63 2.473-1.688 3.342-.48.485-.926.97-1.378 1.44c-1.472 1.58-2.306 2.787-2.91 3.514-.15.18-.207.33-.207.33A.75.75 0 0 1 15 21h-3c-1.104 0-2.08-.542-2.657-1.455-.139-.201-.264-.406-.38-.614l-.014-.025C8.85 18.067 8.156 17.2 7.5 16.325.728 12.56.728 7.44 7.5 3.675c3.04-.482 5.584.47 7.042 1.956.674.672 1.228 1.462 1.696 2.307.426.786.793 1.582 1.113 2.392h.001Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
title: "Pemasukan",
|
title: "Pemasukan",
|
||||||
value: "Rp 580jt",
|
value: "Rp 580jt",
|
||||||
sub: "Bulan ini",
|
subtitle: "Bulan ini",
|
||||||
delta: "+8%",
|
trend: "+8%",
|
||||||
deltaType: "positive",
|
icon: TrendingUp,
|
||||||
icon: <IconTrendingUp className="h-6 w-6 text-muted-foreground" />,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
title: "Pengeluaran",
|
title: "Pengeluaran",
|
||||||
value: "Rp 520jt",
|
value: "Rp 520jt",
|
||||||
sub: "Bulan ini",
|
subtitle: "Bulan ini",
|
||||||
icon: <IconTrendingDown className="h-6 w-6 text-muted-foreground" />,
|
icon: TrendingDown,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Income & Expense Data
|
||||||
const incomeExpenseData = [
|
const incomeExpenseData = [
|
||||||
{ month: "Apr", income: 450, expense: 380 },
|
{ month: "Apr", income: 450, expense: 380 },
|
||||||
{ month: "Mei", income: 520, expense: 420 },
|
{ month: "Mei", income: 520, expense: 420 },
|
||||||
@@ -78,6 +75,7 @@ const incomeExpenseData = [
|
|||||||
{ month: "Okt", income: 580, expense: 520 },
|
{ month: "Okt", income: 580, expense: 520 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Sector Allocation Data
|
||||||
const allocationData = [
|
const allocationData = [
|
||||||
{ sector: "Pembangunan", amount: 1200 },
|
{ sector: "Pembangunan", amount: 1200 },
|
||||||
{ sector: "Kesehatan", amount: 800 },
|
{ sector: "Kesehatan", amount: 800 },
|
||||||
@@ -87,13 +85,7 @@ const allocationData = [
|
|||||||
{ sector: "Teknologi", amount: 300 },
|
{ sector: "Teknologi", amount: 300 },
|
||||||
];
|
];
|
||||||
|
|
||||||
const assistanceFundData = [
|
// APBDes Report Data
|
||||||
{ 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 apbdReport = {
|
const apbdReport = {
|
||||||
income: [
|
income: [
|
||||||
{ category: "Dana Desa", amount: 1800 },
|
{ category: "Dana Desa", amount: 1800 },
|
||||||
@@ -113,229 +105,353 @@ const apbdReport = {
|
|||||||
totalExpenses: 2155,
|
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 KeuanganAnggaran = () => {
|
||||||
const { colorScheme } = useMantineColorScheme();
|
const { colorScheme } = useMantineColorScheme();
|
||||||
const dark = colorScheme === "dark";
|
const dark = colorScheme === "dark";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Stack gap="lg">
|
||||||
<Stack gap="xl">
|
{/* TOP SECTION - 4 STAT CARDS */}
|
||||||
{/* KPI Cards */}
|
<Grid gutter="md">
|
||||||
<Grid gutter="lg">
|
{kpiData.map((item) => (
|
||||||
{kpiData.map((kpi) => (
|
<Grid.Col key={item.id} span={{ base: 12, sm: 6, lg: 3 }}>
|
||||||
<Grid.Col key={kpi.id} span={{ base: 12, md: 6, lg: 3 }}>
|
|
||||||
<Card
|
<Card
|
||||||
p="md"
|
p="md"
|
||||||
radius="md"
|
radius="xl"
|
||||||
withBorder
|
withBorder
|
||||||
bg={dark ? "#141D34" : "white"}
|
bg={dark ? "#1E293B" : "white"}
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
style={{
|
||||||
|
borderColor: dark ? "#334155" : "white",
|
||||||
|
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
|
||||||
|
transition: "transform 0.15s ease, box-shadow 0.15s ease",
|
||||||
|
}}
|
||||||
h="100%"
|
h="100%"
|
||||||
>
|
>
|
||||||
<Group justify="space-between" align="flex-start" mb="xs">
|
<Group justify="space-between" align="flex-start" w="100%">
|
||||||
<Text size="sm" fw={500} c="dimmed">
|
<Stack gap={2}>
|
||||||
{kpi.title}
|
<Text size="sm" c="dimmed">
|
||||||
|
{item.title}
|
||||||
</Text>
|
</Text>
|
||||||
{React.cloneElement(kpi.icon, {
|
<Text size="xl" fw={700} c={dark ? "white" : "gray.9"}>
|
||||||
className: "h-6 w-6",
|
{item.value}
|
||||||
color: "var(--mantine-color-dimmed)",
|
</Text>
|
||||||
})}
|
<Group gap={4} align="flex-start">
|
||||||
</Group>
|
{item.trend && (
|
||||||
<Title order={3} fw={700} mt="xs">
|
<TrendingUp size={14} color="#22C55E" />
|
||||||
{kpi.value}
|
)}
|
||||||
</Title>
|
|
||||||
{kpi.delta && (
|
|
||||||
<Text
|
<Text
|
||||||
size="xs"
|
size="xs"
|
||||||
c={
|
c={
|
||||||
kpi.deltaType === "positive"
|
item.trend
|
||||||
? "green"
|
? "green"
|
||||||
: kpi.deltaType === "negative"
|
: dark
|
||||||
? "red"
|
? "gray.4"
|
||||||
: "dimmed"
|
: "gray.5"
|
||||||
}
|
}
|
||||||
mt={4}
|
|
||||||
>
|
>
|
||||||
{kpi.delta}
|
{item.subtitle}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
</Group>
|
||||||
{kpi.sub && (
|
</Stack>
|
||||||
<Text size="xs" c="dimmed" mt="auto">
|
<ThemeIcon
|
||||||
{kpi.sub}
|
color="#1E3A5F"
|
||||||
</Text>
|
variant="filled"
|
||||||
)}
|
size="lg"
|
||||||
|
radius="xl"
|
||||||
|
>
|
||||||
|
<item.icon style={{ width: "60%", height: "60%" }} />
|
||||||
|
</ThemeIcon>
|
||||||
|
</Group>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* Charts Section */}
|
{/* MAIN CHART SECTION */}
|
||||||
<Grid gutter="lg">
|
<Grid gutter="lg">
|
||||||
{/* Grafik Pemasukan vs Pengeluaran */}
|
{/* LEFT: PEMASUKAN DAN PENGELUARAN (70%) */}
|
||||||
<Grid.Col span={{ base: 12, lg: 6 }}>
|
<Grid.Col span={{ base: 12, lg: 8 }}>
|
||||||
<Card
|
<Card
|
||||||
p="md"
|
p="md"
|
||||||
radius="md"
|
radius="xl"
|
||||||
withBorder
|
withBorder
|
||||||
bg={dark ? "#141D34" : "white"}
|
bg={dark ? "#1E293B" : "white"}
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
style={{
|
||||||
|
borderColor: dark ? "#334155" : "white",
|
||||||
|
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
|
||||||
|
}}
|
||||||
|
h="100%"
|
||||||
>
|
>
|
||||||
<Title order={3} fw={500} mb="md">
|
<Group gap="xs" mb="md">
|
||||||
Pemasukan vs Pengeluaran
|
<ThemeIcon color="#1E3A5F" variant="filled" size="sm" radius="sm">
|
||||||
|
<PieChartIcon size={14} />
|
||||||
|
</ThemeIcon>
|
||||||
|
<Title order={4} c={dark ? "white" : "gray.9"}>
|
||||||
|
Pemasukan dan Pengeluaran
|
||||||
</Title>
|
</Title>
|
||||||
<BarChart
|
</Group>
|
||||||
h={300}
|
<ResponsiveContainer width="100%" height={300}>
|
||||||
data={incomeExpenseData}
|
<LineChart data={incomeExpenseData}>
|
||||||
dataKey="month"
|
<CartesianGrid
|
||||||
series={[
|
strokeDasharray="3 3"
|
||||||
{ name: "income", color: "green", label: "Pemasukan" },
|
vertical={false}
|
||||||
{ name: "expense", color: "red", label: "Pengeluaran" },
|
stroke={dark ? "#334155" : "#e5e7eb"}
|
||||||
]}
|
|
||||||
withLegend
|
|
||||||
/>
|
/>
|
||||||
|
<XAxis
|
||||||
|
dataKey="month"
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{
|
||||||
|
fill: dark ? "#E2E8F0" : "#374151",
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<YAxis
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{
|
||||||
|
fill: dark ? "#E2E8F0" : "#374151",
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
tickFormatter={(value) => `Rp ${value}jt`}
|
||||||
|
/>
|
||||||
|
<Tooltip
|
||||||
|
contentStyle={{
|
||||||
|
backgroundColor: dark ? "#1E293B" : "white",
|
||||||
|
borderColor: dark ? "#334155" : "#e5e7eb",
|
||||||
|
borderRadius: "8px",
|
||||||
|
}}
|
||||||
|
labelStyle={{ color: dark ? "#E2E8F0" : "#374151" }}
|
||||||
|
formatter={(value: number | undefined) => [
|
||||||
|
`Rp ${value}jt`,
|
||||||
|
"",
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Line
|
||||||
|
type="monotone"
|
||||||
|
dataKey="income"
|
||||||
|
stroke="#22C55E"
|
||||||
|
strokeWidth={2}
|
||||||
|
dot={{ fill: "#22C55E", strokeWidth: 2, r: 4 }}
|
||||||
|
activeDot={{ r: 6 }}
|
||||||
|
name="Pemasukan"
|
||||||
|
/>
|
||||||
|
<Line
|
||||||
|
type="monotone"
|
||||||
|
dataKey="expense"
|
||||||
|
stroke="#EF4444"
|
||||||
|
strokeWidth={2}
|
||||||
|
dot={{ fill: "#EF4444", strokeWidth: 2, r: 4 }}
|
||||||
|
activeDot={{ r: 6 }}
|
||||||
|
name="Pengeluaran"
|
||||||
|
/>
|
||||||
|
</LineChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
|
|
||||||
{/* Alokasi Anggaran Per Sektor */}
|
{/* RIGHT: ALOKASI ANGGARAN PER SEKTOR (30%) */}
|
||||||
<Grid.Col span={{ base: 12, lg: 6 }}>
|
<Grid.Col span={{ base: 12, lg: 4 }}>
|
||||||
<Card
|
<Card
|
||||||
p="md"
|
p="md"
|
||||||
radius="md"
|
radius="xl"
|
||||||
withBorder
|
withBorder
|
||||||
bg={dark ? "#141D34" : "white"}
|
bg={dark ? "#1E293B" : "white"}
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
style={{
|
||||||
|
borderColor: dark ? "#334155" : "white",
|
||||||
|
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
|
||||||
|
}}
|
||||||
|
h="100%"
|
||||||
>
|
>
|
||||||
<Title order={3} fw={500} mb="md">
|
<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"}>
|
||||||
Alokasi Anggaran Per Sektor
|
Alokasi Anggaran Per Sektor
|
||||||
</Title>
|
</Title>
|
||||||
<BarChart
|
</Group>
|
||||||
h={300}
|
<ResponsiveContainer width="100%" height={300}>
|
||||||
data={allocationData}
|
<BarChart data={allocationData} layout="vertical">
|
||||||
dataKey="sector"
|
<CartesianGrid
|
||||||
series={[
|
strokeDasharray="3 3"
|
||||||
{ name: "amount", color: "darmasaba-navy", label: "Jumlah" },
|
horizontal={false}
|
||||||
]}
|
stroke={dark ? "#334155" : "#e5e7eb"}
|
||||||
withLegend
|
|
||||||
orientation="horizontal"
|
|
||||||
/>
|
/>
|
||||||
|
<XAxis
|
||||||
|
type="number"
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{
|
||||||
|
fill: dark ? "#E2E8F0" : "#374151",
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
tickFormatter={(value) => `${value}`}
|
||||||
|
/>
|
||||||
|
<YAxis
|
||||||
|
type="category"
|
||||||
|
dataKey="sector"
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{
|
||||||
|
fill: dark ? "#E2E8F0" : "#374151",
|
||||||
|
fontSize: 11,
|
||||||
|
}}
|
||||||
|
width={100}
|
||||||
|
/>
|
||||||
|
<Tooltip
|
||||||
|
contentStyle={{
|
||||||
|
backgroundColor: dark ? "#1E293B" : "white",
|
||||||
|
borderColor: dark ? "#334155" : "#e5e7eb",
|
||||||
|
borderRadius: "8px",
|
||||||
|
}}
|
||||||
|
formatter={(value: number | undefined) => [`Rp ${value}jt`, "Jumlah"]}
|
||||||
|
/>
|
||||||
|
<Bar
|
||||||
|
dataKey="amount"
|
||||||
|
fill="#1E3A5F"
|
||||||
|
radius={[0, 8, 8, 0]}
|
||||||
|
maxBarSize={30}
|
||||||
|
/>
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
{/* BOTTOM SECTION */}
|
||||||
<Grid gutter="lg">
|
<Grid gutter="lg">
|
||||||
{/* Dana Bantuan & Hibah */}
|
{/* LEFT: LAPORAN APBDES */}
|
||||||
<Grid.Col span={{ base: 12, lg: 6 }}>
|
<Grid.Col span={{ base: 12, lg: 6 }}>
|
||||||
<Card
|
<Card
|
||||||
p="md"
|
p="md"
|
||||||
radius="md"
|
radius="xl"
|
||||||
withBorder
|
withBorder
|
||||||
bg={dark ? "#141D34" : "white"}
|
bg={dark ? "#1E293B" : "white"}
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
>
|
|
||||||
<Title order={3} fw={500} mb="md">
|
|
||||||
Dana Bantuan & Hibah
|
|
||||||
</Title>
|
|
||||||
<Stack gap="sm">
|
|
||||||
{assistanceFundData.map((fund, index) => (
|
|
||||||
<Group
|
|
||||||
key={index}
|
|
||||||
justify="space-between"
|
|
||||||
align="center"
|
|
||||||
p="sm"
|
|
||||||
style={{
|
style={{
|
||||||
border: "1px solid var(--mantine-color-gray-3)",
|
borderColor: dark ? "#334155" : "white",
|
||||||
borderRadius: "var(--mantine-radius-sm)",
|
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
|
||||||
}}
|
}}
|
||||||
|
h="100%"
|
||||||
>
|
>
|
||||||
<Box>
|
<Group gap="xs" mb="md">
|
||||||
<Text size="sm" fw={500}>
|
<ThemeIcon color="#1E3A5F" variant="filled" size="sm" radius="sm">
|
||||||
{fund.source}
|
<Receipt size={14} />
|
||||||
</Text>
|
</ThemeIcon>
|
||||||
<Text size="sm" c="dimmed">
|
<Title order={4} c={dark ? "white" : "gray.9"}>
|
||||||
Rp {fund.amount.toLocaleString()}jt
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
<Badge
|
|
||||||
variant="light"
|
|
||||||
color={fund.status === "cair" ? "green" : "yellow"}
|
|
||||||
>
|
|
||||||
{fund.status}
|
|
||||||
</Badge>
|
|
||||||
</Group>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</Card>
|
|
||||||
</Grid.Col>
|
|
||||||
|
|
||||||
{/* Laporan APBDes */}
|
|
||||||
<Grid.Col span={{ base: 12, lg: 6 }}>
|
|
||||||
<Card
|
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#141D34" : "white"}
|
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
>
|
|
||||||
<Title order={3} fw={500} mb="md">
|
|
||||||
Laporan APBDes
|
Laporan APBDes
|
||||||
</Title>
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
<Box mb="md">
|
<Grid gutter="md">
|
||||||
<Title order={4} mb="sm">
|
{/* Pendapatan */}
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<Card
|
||||||
|
p="sm"
|
||||||
|
radius="lg"
|
||||||
|
bg={dark ? "#064E3B" : "#DCFCE7"}
|
||||||
|
>
|
||||||
|
<Title order={5} c="#22C55E" mb="sm">
|
||||||
Pendapatan
|
Pendapatan
|
||||||
</Title>
|
</Title>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
{apbdReport.income.map((item, index) => (
|
{apbdReport.income.map((item, index) => (
|
||||||
<Group key={index} justify="space-between">
|
<Group key={index} justify="space-between">
|
||||||
<Text size="sm">{item.category}</Text>
|
<Text size="sm" c={dark ? "gray.3" : "gray.7"}>
|
||||||
<Text size="sm" c="green">
|
{item.category}
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" fw={600} c="#22C55E">
|
||||||
Rp {item.amount.toLocaleString()}jt
|
Rp {item.amount.toLocaleString()}jt
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
))}
|
))}
|
||||||
<Group justify="space-between" mt="sm">
|
<Group
|
||||||
<Text fw={700}>Total Pendapatan:</Text>
|
justify="space-between"
|
||||||
<Text fw={700} c="green">
|
mt="sm"
|
||||||
|
pt="sm"
|
||||||
|
style={{
|
||||||
|
borderTop: `1px solid ${dark ? "#065F46" : "#86EFAC"}`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text fw={700} c="#22C55E">
|
||||||
|
Total:
|
||||||
|
</Text>
|
||||||
|
<Text fw={700} c="#22C55E">
|
||||||
Rp {apbdReport.totalIncome.toLocaleString()}jt
|
Rp {apbdReport.totalIncome.toLocaleString()}jt
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Card>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
<Box>
|
{/* Belanja */}
|
||||||
<Title order={4} mb="sm">
|
<Grid.Col span={6}>
|
||||||
|
<Card
|
||||||
|
p="sm"
|
||||||
|
radius="lg"
|
||||||
|
bg={dark ? "#7F1D1D" : "#FEE2E2"}
|
||||||
|
>
|
||||||
|
<Title order={5} c="#EF4444" mb="sm">
|
||||||
Belanja
|
Belanja
|
||||||
</Title>
|
</Title>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
{apbdReport.expenses.map((item, index) => (
|
{apbdReport.expenses.map((item, index) => (
|
||||||
<Group key={index} justify="space-between">
|
<Group key={index} justify="space-between">
|
||||||
<Text size="sm">{item.category}</Text>
|
<Text size="sm" c={dark ? "gray.3" : "gray.7"}>
|
||||||
<Text size="sm" c="red">
|
{item.category}
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" fw={600} c="#EF4444">
|
||||||
Rp {item.amount.toLocaleString()}jt
|
Rp {item.amount.toLocaleString()}jt
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
))}
|
))}
|
||||||
<Group justify="space-between" mt="sm">
|
<Group
|
||||||
<Text fw={700}>Total Belanja:</Text>
|
justify="space-between"
|
||||||
<Text fw={700} c="red">
|
mt="sm"
|
||||||
|
pt="sm"
|
||||||
|
style={{
|
||||||
|
borderTop: `1px solid ${dark ? "#991B1B" : "#FCA5A5"}`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text fw={700} c="#EF4444">
|
||||||
|
Total:
|
||||||
|
</Text>
|
||||||
|
<Text fw={700} c="#EF4444">
|
||||||
Rp {apbdReport.totalExpenses.toLocaleString()}jt
|
Rp {apbdReport.totalExpenses.toLocaleString()}jt
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Card>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Box
|
{/* Saldo */}
|
||||||
|
<Group
|
||||||
|
justify="space-between"
|
||||||
mt="md"
|
mt="md"
|
||||||
pt="md"
|
pt="md"
|
||||||
style={{ borderTop: "1px solid var(--mantine-color-gray-3)" }}
|
style={{
|
||||||
|
borderTop: `1px solid ${dark ? "#334155" : "#e5e7eb"}`,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Group justify="space-between">
|
<Text fw={700} c={dark ? "white" : "gray.9"}>
|
||||||
<Text fw={700}>Saldo:</Text>
|
Saldo:
|
||||||
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
fw={700}
|
fw={700}
|
||||||
|
size="lg"
|
||||||
c={
|
c={
|
||||||
apbdReport.totalIncome > apbdReport.totalExpenses
|
apbdReport.totalIncome > apbdReport.totalExpenses
|
||||||
? "green"
|
? "#22C55E"
|
||||||
: "red"
|
: "#EF4444"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Rp{" "}
|
Rp{" "}
|
||||||
@@ -345,12 +461,67 @@ const KeuanganAnggaran = () => {
|
|||||||
jt
|
jt
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
{/* RIGHT: DANA BANTUAN DAN HIBAH */}
|
||||||
|
<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">
|
||||||
|
<Coins size={14} />
|
||||||
|
</ThemeIcon>
|
||||||
|
<Title order={4} c={dark ? "white" : "gray.9"}>
|
||||||
|
Dana Bantuan dan Hibah
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
<Stack gap="sm">
|
||||||
|
{assistanceFundData.map((fund, index) => (
|
||||||
|
<Card
|
||||||
|
key={index}
|
||||||
|
p="sm"
|
||||||
|
radius="lg"
|
||||||
|
bg={dark ? "#334155" : "#F1F5F9"}
|
||||||
|
style={{
|
||||||
|
borderColor: "transparent",
|
||||||
|
transition: "background-color 0.15s ease",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Box>
|
||||||
|
<Text size="sm" fw={600} c={dark ? "white" : "gray.9"}>
|
||||||
|
{fund.source}
|
||||||
|
</Text>
|
||||||
|
<Text size="xs" c="dimmed">
|
||||||
|
Rp {fund.amount.toLocaleString()}jt
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Badge
|
||||||
|
variant="light"
|
||||||
|
color={fund.status === "cair" ? "green" : "yellow"}
|
||||||
|
radius="sm"
|
||||||
|
fw={600}
|
||||||
|
>
|
||||||
|
{fund.status === "cair" ? "Cair" : "Proses"}
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
|
|||||||
import { useDisclosure } from "@mantine/hooks";
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { Header } from "@/components/header";
|
import { Header } from "@/components/header";
|
||||||
import HelpPage from "@/components/help-page";
|
|
||||||
import { Sidebar } from "@/components/sidebar";
|
import { Sidebar } from "@/components/sidebar";
|
||||||
|
import HelpPage from "@/components/help-page";
|
||||||
|
|
||||||
export const Route = createFileRoute("/bantuan")({
|
export const Route = createFileRoute("/bantuan")({
|
||||||
component: BantuanPage,
|
component: BantuanRoute,
|
||||||
});
|
});
|
||||||
|
|
||||||
function BantuanPage() {
|
function BantuanRoute() {
|
||||||
const [opened, { toggle }] = useDisclosure();
|
const [opened, { toggle }] = useDisclosure();
|
||||||
const { colorScheme } = useMantineColorScheme();
|
const { colorScheme } = useMantineColorScheme();
|
||||||
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
|
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
|
||||||
|
|||||||
@@ -1,9 +1,51 @@
|
|||||||
|
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
|
||||||
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
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")({
|
export const Route = createFileRoute("/bumdes")({
|
||||||
component: RouteComponent,
|
component: BumdesRoute,
|
||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function BumdesRoute() {
|
||||||
return <div>Hello "/bumdes"!</div>;
|
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 (
|
||||||
|
<AppShell
|
||||||
|
header={{ height: 60 }}
|
||||||
|
navbar={{
|
||||||
|
width: 300,
|
||||||
|
breakpoint: "sm",
|
||||||
|
collapsed: { mobile: !opened },
|
||||||
|
}}
|
||||||
|
padding="md"
|
||||||
|
>
|
||||||
|
<AppShell.Header bg={headerBgColor}>
|
||||||
|
<Group h="100%" px="md">
|
||||||
|
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
|
||||||
|
<Header />
|
||||||
|
</Group>
|
||||||
|
</AppShell.Header>
|
||||||
|
|
||||||
|
<AppShell.Navbar
|
||||||
|
p="md"
|
||||||
|
bg={navbarBgColor}
|
||||||
|
style={{ display: "flex", flexDirection: "column" }}
|
||||||
|
>
|
||||||
|
<div style={{ flex: 1, overflowY: "auto" }}>
|
||||||
|
<Sidebar />
|
||||||
|
</div>
|
||||||
|
</AppShell.Navbar>
|
||||||
|
|
||||||
|
<AppShell.Main bg={mainBgColor}>
|
||||||
|
<BumdesPage />
|
||||||
|
</AppShell.Main>
|
||||||
|
</AppShell>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,51 @@
|
|||||||
|
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
|
||||||
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
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")({
|
export const Route = createFileRoute("/keamanan")({
|
||||||
component: RouteComponent,
|
component: KeamananRoute,
|
||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function KeamananRoute() {
|
||||||
return <div>Hello "/keamanan"!</div>;
|
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 (
|
||||||
|
<AppShell
|
||||||
|
header={{ height: 60 }}
|
||||||
|
navbar={{
|
||||||
|
width: 300,
|
||||||
|
breakpoint: "sm",
|
||||||
|
collapsed: { mobile: !opened },
|
||||||
|
}}
|
||||||
|
padding="md"
|
||||||
|
>
|
||||||
|
<AppShell.Header bg={headerBgColor}>
|
||||||
|
<Group h="100%" px="md">
|
||||||
|
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
|
||||||
|
<Header />
|
||||||
|
</Group>
|
||||||
|
</AppShell.Header>
|
||||||
|
|
||||||
|
<AppShell.Navbar
|
||||||
|
p="md"
|
||||||
|
bg={navbarBgColor}
|
||||||
|
style={{ display: "flex", flexDirection: "column" }}
|
||||||
|
>
|
||||||
|
<div style={{ flex: 1, overflowY: "auto" }}>
|
||||||
|
<Sidebar />
|
||||||
|
</div>
|
||||||
|
</AppShell.Navbar>
|
||||||
|
|
||||||
|
<AppShell.Main bg={mainBgColor}>
|
||||||
|
<KeamananPage />
|
||||||
|
</AppShell.Main>
|
||||||
|
</AppShell>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,51 @@
|
|||||||
|
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
|
||||||
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
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")({
|
export const Route = createFileRoute("/sosial")({
|
||||||
component: RouteComponent,
|
component: SosialRoute,
|
||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function SosialRoute() {
|
||||||
return <div>Hello "/sosial"!</div>;
|
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 (
|
||||||
|
<AppShell
|
||||||
|
header={{ height: 60 }}
|
||||||
|
navbar={{
|
||||||
|
width: 300,
|
||||||
|
breakpoint: "sm",
|
||||||
|
collapsed: { mobile: !opened },
|
||||||
|
}}
|
||||||
|
padding="md"
|
||||||
|
>
|
||||||
|
<AppShell.Header bg={headerBgColor}>
|
||||||
|
<Group h="100%" px="md">
|
||||||
|
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
|
||||||
|
<Header />
|
||||||
|
</Group>
|
||||||
|
</AppShell.Header>
|
||||||
|
|
||||||
|
<AppShell.Navbar
|
||||||
|
p="md"
|
||||||
|
bg={navbarBgColor}
|
||||||
|
style={{ display: "flex", flexDirection: "column" }}
|
||||||
|
>
|
||||||
|
<div style={{ flex: 1, overflowY: "auto" }}>
|
||||||
|
<Sidebar />
|
||||||
|
</div>
|
||||||
|
</AppShell.Navbar>
|
||||||
|
|
||||||
|
<AppShell.Main bg={mainBgColor}>
|
||||||
|
<SosialPage />
|
||||||
|
</AppShell.Main>
|
||||||
|
</AppShell>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user