fix: make dashboard public and remove admin-only restriction from main pages
- Make homepage (/) accessible without authentication - Allow all authenticated users (user & admin) to access main pages: - /kinerja-divisi, /pengaduan, /jenna, /demografi - /keuangan, /bumdes, /sosial, /keamanan - /bantuan, /pengaturan - Reserve admin-only access for /admin/* routes - Update auth middleware to handle public routes properly Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -1,544 +1,411 @@
|
||||
import { BarChart, PieChart } from "@mantine/charts";
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
Grid,
|
||||
Group,
|
||||
Stack,
|
||||
Table,
|
||||
Text,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconArrowDown,
|
||||
IconArrowUp,
|
||||
IconBabyCarriage,
|
||||
IconSkull,
|
||||
IconUsers,
|
||||
IconHome,
|
||||
IconExclamationCircle,
|
||||
} from "@tabler/icons-react";
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
CartesianGrid,
|
||||
Cell,
|
||||
Pie,
|
||||
PieChart,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
import React from "react";
|
||||
|
||||
// Sample Data
|
||||
const kpiData = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Total Penduduk",
|
||||
value: "5.634",
|
||||
sub: "Aktif terdaftar",
|
||||
icon: (
|
||||
<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"
|
||||
role="img"
|
||||
aria-label="Icon penduduk"
|
||||
>
|
||||
<title>Total Penduduk</title>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Kepala Keluarga",
|
||||
value: "1.354",
|
||||
sub: "Total KK",
|
||||
icon: (
|
||||
<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"
|
||||
role="img"
|
||||
aria-label="Icon kepala keluarga"
|
||||
>
|
||||
<title>Kepala Keluarga</title>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Kelahiran",
|
||||
value: "23",
|
||||
sub: "Tahun ini",
|
||||
icon: (
|
||||
<IconBabyCarriage
|
||||
className="h-6 w-6 text-muted-foreground"
|
||||
role="img"
|
||||
aria-label="Icon kelahiran"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Kemiskinan",
|
||||
value: "324",
|
||||
delta: "-10% dari tahun lalu",
|
||||
deltaType: "positive",
|
||||
icon: (
|
||||
<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"
|
||||
role="img"
|
||||
aria-label="Icon kemiskinan"
|
||||
>
|
||||
<title>Kemiskinan</title>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const ageDistributionData = [
|
||||
{ ageRange: "17-25", total: 850 },
|
||||
{ ageRange: "26-35", total: 1200 },
|
||||
{ ageRange: "36-45", total: 1100 },
|
||||
{ ageRange: "46-55", total: 950 },
|
||||
{ ageRange: "56-65", total: 750 },
|
||||
{ ageRange: "65+", total: 484 },
|
||||
];
|
||||
|
||||
const jobDistributionData = [
|
||||
{ job: "Sipil", total: 1200 },
|
||||
{ job: "Guru", total: 850 },
|
||||
{ job: "Petani", total: 950 },
|
||||
{ job: "Pedagang", total: 750 },
|
||||
{ job: "Wiraswasta", total: 984 },
|
||||
];
|
||||
|
||||
const religionData = [
|
||||
{ religion: "Hindu", total: 4234, color: "red" },
|
||||
{ religion: "Islam", total: 856, color: "blue" },
|
||||
{ religion: "Kristen", total: 412, color: "green" },
|
||||
{ religion: "Buddha", total: 202, color: "yellow" },
|
||||
];
|
||||
|
||||
const banjarData = [
|
||||
{ banjar: "Banjar Darmasaba", population: 1200, kk: 300, poor: 45 },
|
||||
{ banjar: "Banjar Manesa", population: 950, kk: 240, poor: 32 },
|
||||
{ banjar: "Banjar Cabe", population: 800, kk: 200, poor: 28 },
|
||||
{ banjar: "Banjar Penenjoan", population: 1100, kk: 280, poor: 38 },
|
||||
{ banjar: "Banjar Baler Pasar", population: 984, kk: 250, poor: 42 },
|
||||
{ banjar: "Banjar Bucu", population: 600, kk: 184, poor: 25 },
|
||||
];
|
||||
|
||||
const dynamicStats = [
|
||||
{
|
||||
title: "Kelahiran",
|
||||
value: "23",
|
||||
icon: <IconBabyCarriage size={16} />,
|
||||
color: "green",
|
||||
},
|
||||
{
|
||||
title: "Kematian",
|
||||
value: "12",
|
||||
icon: <IconSkull size={16} />,
|
||||
color: "red",
|
||||
},
|
||||
{
|
||||
title: "Pindah Masuk",
|
||||
value: "45",
|
||||
icon: <IconArrowDown size={16} />,
|
||||
color: "blue",
|
||||
},
|
||||
{
|
||||
title: "Pindah Keluar",
|
||||
value: "32",
|
||||
icon: <IconArrowUp size={16} />,
|
||||
color: "orange",
|
||||
},
|
||||
];
|
||||
|
||||
const DemografiPekerjaan = () => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
|
||||
// KPI Data
|
||||
const kpiData = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Total Penduduk",
|
||||
value: "5.634",
|
||||
subtitle: "Aktif terdaftar",
|
||||
icon: IconUsers,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Kepala Keluarga",
|
||||
value: "1.354",
|
||||
subtitle: "Total KK",
|
||||
icon: IconHome,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Kelahiran",
|
||||
value: "23",
|
||||
subtitle: "Tahun ini",
|
||||
icon: IconBabyCarriage,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Kemiskinan",
|
||||
value: "324",
|
||||
subtitle: "-10% dari tahun lalu",
|
||||
icon: IconExclamationCircle,
|
||||
},
|
||||
];
|
||||
|
||||
// Age distribution data
|
||||
const ageDistributionData = [
|
||||
{ ageRange: "17-25", total: 850 },
|
||||
{ ageRange: "26-35", total: 1200 },
|
||||
{ ageRange: "36-45", total: 1100 },
|
||||
{ ageRange: "46-55", total: 950 },
|
||||
{ ageRange: "56-65", total: 750 },
|
||||
{ ageRange: "65+", total: 484 },
|
||||
];
|
||||
|
||||
// Job distribution data
|
||||
const jobDistributionData = [
|
||||
{ job: "Sipil", total: 1200 },
|
||||
{ job: "Guru", total: 850 },
|
||||
{ job: "Petani", total: 950 },
|
||||
{ job: "Pedagang", total: 750 },
|
||||
{ job: "Wiraswasta", total: 984 },
|
||||
];
|
||||
|
||||
// Religion data
|
||||
const religionData = [
|
||||
{ religion: "Hindu", total: 4234, color: "#EF4444" },
|
||||
{ religion: "Islam", total: 856, color: "#3B82F6" },
|
||||
{ religion: "Kristen", total: 412, color: "#10B981" },
|
||||
{ religion: "Buddha", total: 202, color: "#F59E0B" },
|
||||
];
|
||||
|
||||
// Banjar data
|
||||
const banjarData = [
|
||||
{ banjar: "Banjar Darmasaba", population: 1200, kk: 300, poor: 45 },
|
||||
{ banjar: "Banjar Manesa", population: 950, kk: 240, poor: 32 },
|
||||
{ banjar: "Banjar Cabe", population: 800, kk: 200, poor: 28 },
|
||||
{ banjar: "Banjar Penenjoan", population: 1100, kk: 280, poor: 38 },
|
||||
{ banjar: "Banjar Baler Pasar", population: 984, kk: 250, poor: 42 },
|
||||
{ banjar: "Banjar Bucu", population: 600, kk: 184, poor: 25 },
|
||||
];
|
||||
|
||||
// Dynamic stats
|
||||
const dynamicStats = [
|
||||
{
|
||||
title: "Kelahiran",
|
||||
value: "23",
|
||||
icon: IconBabyCarriage,
|
||||
color: "#10B981",
|
||||
},
|
||||
{
|
||||
title: "Kematian",
|
||||
value: "12",
|
||||
icon: IconSkull,
|
||||
color: "#EF4444",
|
||||
},
|
||||
{
|
||||
title: "Pindah Masuk",
|
||||
value: "45",
|
||||
icon: IconArrowDown,
|
||||
color: "#3B82F6",
|
||||
},
|
||||
{
|
||||
title: "Pindah Keluar",
|
||||
value: "32",
|
||||
icon: IconArrowUp,
|
||||
color: "#F59E0B",
|
||||
},
|
||||
];
|
||||
|
||||
const COLORS = ["#1E3A5F", "#3B82F6", "#60A5FA", "#93C5FD", "#DBEAFE"];
|
||||
|
||||
const cardStyle = {
|
||||
backgroundColor: dark ? "#141D34" : "white",
|
||||
border: `1px solid ${dark ? "#141D34" : "white"}`,
|
||||
};
|
||||
|
||||
const textStyle = {
|
||||
color: dark ? "white" : "#1F2937",
|
||||
};
|
||||
|
||||
const subtitleStyle = {
|
||||
color: dark ? "#9CA3AF" : "#6B7280",
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="min-h-screen"
|
||||
style={{
|
||||
backgroundColor: dark ? "#10192D" : "#F3F4F6",
|
||||
minHeight: "100vh",
|
||||
padding: "1.5rem",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="max-w-7xl mx-auto"
|
||||
style={{
|
||||
maxWidth: "80rem",
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
}}
|
||||
>
|
||||
{/* Row 1: 4 Statistic Cards */}
|
||||
<div
|
||||
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(4, 1fr)",
|
||||
gap: "1.5rem",
|
||||
marginBottom: "1.5rem",
|
||||
}}
|
||||
>
|
||||
<Box className="space-y-6">
|
||||
<Stack gap="xl">
|
||||
{/* KPI Cards */}
|
||||
<Grid gutter="lg">
|
||||
{kpiData.map((kpi) => (
|
||||
<div
|
||||
key={kpi.id}
|
||||
className="rounded-xl shadow-sm p-6"
|
||||
style={{
|
||||
...cardStyle,
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
|
||||
padding: "1.5rem",
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex-1">
|
||||
<h3
|
||||
className="text-sm font-medium mb-1"
|
||||
style={subtitleStyle}
|
||||
>
|
||||
{kpi.title}
|
||||
</h3>
|
||||
<p
|
||||
className="text-3xl font-bold mb-1"
|
||||
style={textStyle}
|
||||
>
|
||||
{kpi.value}
|
||||
</p>
|
||||
<p
|
||||
className="text-xs"
|
||||
style={subtitleStyle}
|
||||
>
|
||||
{kpi.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-shrink-0 ml-4">
|
||||
<div
|
||||
className="w-12 h-12 rounded-full flex items-center justify-center text-white"
|
||||
style={{ backgroundColor: "#1E3A5F" }}
|
||||
>
|
||||
<kpi.icon size={24} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Row 2: 2 Chart Cards */}
|
||||
<div
|
||||
className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(2, 1fr)",
|
||||
gap: "1.5rem",
|
||||
marginBottom: "1.5rem",
|
||||
}}
|
||||
>
|
||||
{/* Age Distribution Bar Chart */}
|
||||
<div
|
||||
className="rounded-xl shadow-sm p-6"
|
||||
style={{
|
||||
...cardStyle,
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
|
||||
padding: "1.5rem",
|
||||
}}
|
||||
>
|
||||
<h3
|
||||
className="text-lg font-semibold mb-4"
|
||||
style={textStyle}
|
||||
>
|
||||
Grafik Pengelompokan Umur
|
||||
</h3>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart data={ageDistributionData}>
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
vertical={false}
|
||||
stroke={dark ? "#2d3748" : "#E5E7EB"}
|
||||
/>
|
||||
<XAxis
|
||||
dataKey="ageRange"
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: dark ? "#9CA3AF" : "#6B7280" }}
|
||||
/>
|
||||
<YAxis
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: dark ? "#9CA3AF" : "#6B7280" }}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: dark ? "#1F2937" : "white",
|
||||
border: `1px solid ${dark ? "#374151" : "#E5E7EB"}`,
|
||||
borderRadius: "8px",
|
||||
color: dark ? "white" : "#1F2937",
|
||||
}}
|
||||
/>
|
||||
<Bar dataKey="total" radius={[4, 4, 0, 0]}>
|
||||
{ageDistributionData.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={COLORS[index % COLORS.length]}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Job Distribution Bar Chart */}
|
||||
<div
|
||||
className="rounded-xl shadow-sm p-6"
|
||||
style={{
|
||||
...cardStyle,
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
|
||||
padding: "1.5rem",
|
||||
}}
|
||||
>
|
||||
<h3
|
||||
className="text-lg font-semibold mb-4"
|
||||
style={textStyle}
|
||||
>
|
||||
Demografi Pekerjaan
|
||||
</h3>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart data={jobDistributionData}>
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
vertical={false}
|
||||
stroke={dark ? "#2d3748" : "#E5E7EB"}
|
||||
/>
|
||||
<XAxis
|
||||
dataKey="job"
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: dark ? "#9CA3AF" : "#6B7280" }}
|
||||
/>
|
||||
<YAxis
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: dark ? "#9CA3AF" : "#6B7280" }}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: dark ? "#1F2937" : "white",
|
||||
border: `1px solid ${dark ? "#374151" : "#E5E7EB"}`,
|
||||
borderRadius: "8px",
|
||||
color: dark ? "white" : "#1F2937",
|
||||
}}
|
||||
/>
|
||||
<Bar dataKey="total" radius={[4, 4, 0, 0]}>
|
||||
{jobDistributionData.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={COLORS[index % COLORS.length]}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 3: 3 Insight Cards */}
|
||||
<div
|
||||
className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(3, 1fr)",
|
||||
gap: "1.5rem",
|
||||
marginBottom: "1.5rem",
|
||||
}}
|
||||
>
|
||||
{/* Religion Distribution Pie Chart */}
|
||||
<div
|
||||
className="rounded-xl shadow-sm p-6"
|
||||
style={{
|
||||
...cardStyle,
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
|
||||
padding: "1.5rem",
|
||||
}}
|
||||
>
|
||||
<h3
|
||||
className="text-lg font-semibold mb-4"
|
||||
style={textStyle}
|
||||
>
|
||||
Distribusi Agama
|
||||
</h3>
|
||||
<ResponsiveContainer width="100%" height={250}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={religionData}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
outerRadius={80}
|
||||
dataKey="total"
|
||||
nameKey="religion"
|
||||
label={({ name, percent }) =>
|
||||
`${name}: ${percent ? (percent * 100).toFixed(0) : 0}%`
|
||||
}
|
||||
>
|
||||
{religionData.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: dark ? "#1F2937" : "white",
|
||||
border: `1px solid ${dark ? "#374151" : "#E5E7EB"}`,
|
||||
borderRadius: "8px",
|
||||
color: dark ? "white" : "#1F2937",
|
||||
}}
|
||||
/>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Population per Banjar Table */}
|
||||
<div
|
||||
className="rounded-xl shadow-sm p-6 lg:col-span-2"
|
||||
style={{
|
||||
...cardStyle,
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
|
||||
padding: "1.5rem",
|
||||
}}
|
||||
>
|
||||
<h3
|
||||
className="text-lg font-semibold mb-4"
|
||||
style={textStyle}
|
||||
>
|
||||
Data per Banjar
|
||||
</h3>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr
|
||||
style={{
|
||||
borderBottom: `1px solid ${dark ? "#2d3748" : "#E5E7EB"}`,
|
||||
}}
|
||||
>
|
||||
<th
|
||||
className="text-left py-3 px-4 text-sm font-medium"
|
||||
style={subtitleStyle}
|
||||
>
|
||||
Banjar
|
||||
</th>
|
||||
<th
|
||||
className="text-left py-3 px-4 text-sm font-medium"
|
||||
style={subtitleStyle}
|
||||
>
|
||||
Penduduk
|
||||
</th>
|
||||
<th
|
||||
className="text-left py-3 px-4 text-sm font-medium"
|
||||
style={subtitleStyle}
|
||||
>
|
||||
KK
|
||||
</th>
|
||||
<th
|
||||
className="text-left py-3 px-4 text-sm font-medium"
|
||||
style={subtitleStyle}
|
||||
>
|
||||
Miskin
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{banjarData.map((item, index) => (
|
||||
<tr
|
||||
key={index}
|
||||
style={{
|
||||
borderBottom: `1px solid ${dark ? "#2d3748" : "#F3F4F6"}`,
|
||||
}}
|
||||
>
|
||||
<td
|
||||
className="py-3 px-4 text-sm"
|
||||
style={textStyle}
|
||||
>
|
||||
{item.banjar}
|
||||
</td>
|
||||
<td
|
||||
className="py-3 px-4 text-sm"
|
||||
style={textStyle}
|
||||
>
|
||||
{item.population.toLocaleString()}
|
||||
</td>
|
||||
<td
|
||||
className="py-3 px-4 text-sm"
|
||||
style={textStyle}
|
||||
>
|
||||
{item.kk.toLocaleString()}
|
||||
</td>
|
||||
<td
|
||||
className="py-3 px-4 text-sm"
|
||||
style={{ color: "#EF4444" }}
|
||||
>
|
||||
{item.poor.toLocaleString()}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Population Dynamics Stats */}
|
||||
<div
|
||||
className="rounded-xl shadow-sm p-6"
|
||||
style={{
|
||||
...cardStyle,
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
|
||||
padding: "1.5rem",
|
||||
}}
|
||||
>
|
||||
<h3
|
||||
className="text-lg font-semibold mb-4"
|
||||
style={textStyle}
|
||||
>
|
||||
Statistik Dinamika Penduduk
|
||||
</h3>
|
||||
<div
|
||||
className="grid grid-cols-1 md:grid-cols-4 gap-4"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(4, 1fr)",
|
||||
gap: "1rem",
|
||||
}}
|
||||
>
|
||||
{dynamicStats.map((stat, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-4 rounded-lg"
|
||||
style={{
|
||||
backgroundColor: dark ? "#1F2937" : "#F9FAFB",
|
||||
borderRadius: "8px",
|
||||
padding: "1rem",
|
||||
}}
|
||||
<Grid.Col key={kpi.id} span={{ base: 12, md: 6, lg: 3 }}>
|
||||
<Card
|
||||
p="md"
|
||||
radius="md"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p
|
||||
className="text-sm font-medium mb-1"
|
||||
style={subtitleStyle}
|
||||
>
|
||||
{stat.title}
|
||||
</p>
|
||||
<p
|
||||
className="text-2xl font-bold"
|
||||
style={{ color: stat.color }}
|
||||
>
|
||||
{stat.value}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="w-10 h-10 rounded-full flex items-center justify-center"
|
||||
style={{
|
||||
backgroundColor: `${stat.color}20`,
|
||||
color: stat.color,
|
||||
}}
|
||||
<Group justify="space-between" align="flex-start" mb="xs">
|
||||
<Text size="sm" fw={500} c={dark ? "dark.3" : "dimmed"}>
|
||||
{kpi.title}
|
||||
</Text>
|
||||
{React.cloneElement(kpi.icon, {
|
||||
className: "h-6 w-6",
|
||||
color: dark
|
||||
? "var(--mantine-color-dark-3)"
|
||||
: "var(--mantine-color-dimmed)",
|
||||
})}
|
||||
</Group>
|
||||
<Title order={3} fw={700} c={dark ? "dark.0" : "black"} mt="xs">
|
||||
{kpi.value}
|
||||
</Title>
|
||||
{kpi.delta && (
|
||||
<Text
|
||||
size="xs"
|
||||
c={
|
||||
kpi.deltaType === "positive"
|
||||
? "green"
|
||||
: kpi.deltaType === "negative"
|
||||
? "red"
|
||||
: dark
|
||||
? "dark.3"
|
||||
: "dimmed"
|
||||
}
|
||||
mt={4}
|
||||
>
|
||||
<stat.icon size={20} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{kpi.delta}
|
||||
</Text>
|
||||
)}
|
||||
{kpi.sub && (
|
||||
<Text size="xs" c={dark ? "dark.3" : "dimmed"} mt={2}>
|
||||
{kpi.sub}
|
||||
</Text>
|
||||
)}
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
{/* Charts Section */}
|
||||
<Grid gutter="lg">
|
||||
{/* Grafik Pengelompokan Umur */}
|
||||
<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">
|
||||
Grafik Pengelompokan Umur
|
||||
</Title>
|
||||
<BarChart
|
||||
h={300}
|
||||
data={ageDistributionData}
|
||||
dataKey="ageRange"
|
||||
series={[{ name: "total", color: "darmasaba-navy" }]}
|
||||
withLegend
|
||||
/>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
|
||||
{/* Demografi Pekerjaan */}
|
||||
<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">
|
||||
Demografi Pekerjaan
|
||||
</Title>
|
||||
<BarChart
|
||||
h={300}
|
||||
data={jobDistributionData}
|
||||
dataKey="job"
|
||||
series={[{ name: "total", color: "darmasaba-navy" }]}
|
||||
withLegend
|
||||
/>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
{/* Agama & Data per Banjar */}
|
||||
<Grid gutter="lg">
|
||||
{/* Distribusi Agama */}
|
||||
<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">
|
||||
Distribusi Agama
|
||||
</Title>
|
||||
<PieChart
|
||||
h={300}
|
||||
data={religionData.map((item) => ({
|
||||
name: item.religion,
|
||||
value: item.total,
|
||||
color: item.color,
|
||||
}))}
|
||||
withLabels
|
||||
withLabelsLine
|
||||
labelsPosition="outside"
|
||||
labelsType="percent"
|
||||
/>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
|
||||
{/* Data per Banjar */}
|
||||
<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} c={dark ? "dark.0" : "black"} mb="md">
|
||||
Data per Banjar
|
||||
</Title>
|
||||
<Table striped highlightOnHover>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>
|
||||
<Text c={dark ? "dark.0" : "black"}>Banjar</Text>
|
||||
</Table.Th>
|
||||
<Table.Th>
|
||||
<Text c={dark ? "dark.0" : "black"}>Penduduk</Text>
|
||||
</Table.Th>
|
||||
<Table.Th>
|
||||
<Text c={dark ? "dark.0" : "black"}>KK</Text>
|
||||
</Table.Th>
|
||||
<Table.Th>
|
||||
<Text c={dark ? "dark.0" : "black"}>Miskin</Text>
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{banjarData.map((item, index) => (
|
||||
<Table.Tr key={`${item.banjar}-${index}`}>
|
||||
<Table.Td>
|
||||
<Text c={dark ? "dark.0" : "black"}>{item.banjar}</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Text c={dark ? "dark.0" : "black"}>
|
||||
{item.population.toLocaleString()}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Text c={dark ? "dark.0" : "black"}>
|
||||
{item.kk.toLocaleString()}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Text c={dark ? "red.4" : "red"}>
|
||||
{item.poor.toLocaleString()}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
{/* Statistik Dinamika Penduduk */}
|
||||
<Card
|
||||
p="md"
|
||||
radius="md"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
>
|
||||
<Title order={3} fw={500} c={dark ? "dark.0" : "black"} mb="md">
|
||||
Statistik Dinamika Penduduk
|
||||
</Title>
|
||||
<Grid gutter="md">
|
||||
{dynamicStats.map((stat, index) => (
|
||||
<Grid.Col
|
||||
key={`${stat.title}-${index}`}
|
||||
span={{ base: 12, md: 3 }}
|
||||
>
|
||||
<Card
|
||||
p="md"
|
||||
radius="md"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
>
|
||||
<Group justify="space-between" align="center">
|
||||
<Box>
|
||||
<Text size="sm" fw={500} c={dark ? "dark.3" : "dimmed"}>
|
||||
{stat.title}
|
||||
</Text>
|
||||
<Title order={4} fw={700} c={stat.color}>
|
||||
{stat.value}
|
||||
</Title>
|
||||
</Box>
|
||||
<Box c={stat.color}>{stat.icon}</Box>
|
||||
</Group>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default DemografiPekerjaan;
|
||||
export default DemografiPekerjaan;
|
||||
Reference in New Issue
Block a user