From 952f7ecb1689ce3c149d2104ee60a0506f5a024b Mon Sep 17 00:00:00 2001 From: nico Date: Fri, 13 Mar 2026 15:36:31 +0800 Subject: [PATCH] refactor: modularize dashboard components per PromptDashboard.md - Create reusable StatCard component for header metrics - Create ChartSurat component for bar chart (surat statistics) - Create DivisionProgress component for divisi teraktif - Create ChartAPBDes component for APBDes horizontal bar chart - Create ActivityList component for calendar events - Create SatisfactionChart component for donut chart - Create SDGSCard component for SDGs metrics - Refactor DashboardContent to use new modular components - Add proper dark mode support with specified colors - Implement responsive grid layout (12/6/1 columns) - Add custom SDGs icons (Energy, Peace, Health, Poverty, Ocean) New components structure: src/components/dashboard/ - stat-card.tsx - chart-surat.tsx - chart-apbdes.tsx - division-progress.tsx - activity-list.tsx - satisfaction-chart.tsx - sdgs-card.tsx - index.ts (exports) Co-authored-by: Qwen-Coder --- src/components/dashboard-content.tsx | 689 ++++++------------ src/components/dashboard/activity-list.tsx | 69 ++ src/components/dashboard/chart-apbdes.tsx | 74 ++ src/components/dashboard/chart-surat.tsx | 110 +++ .../dashboard/division-progress.tsx | 69 ++ src/components/dashboard/index.ts | 7 + .../dashboard/satisfaction-chart.tsx | 82 +++ src/components/dashboard/sdgs-card.tsx | 53 ++ src/components/dashboard/stat-card.tsx | 86 +++ 9 files changed, 754 insertions(+), 485 deletions(-) create mode 100644 src/components/dashboard/activity-list.tsx create mode 100644 src/components/dashboard/chart-apbdes.tsx create mode 100644 src/components/dashboard/chart-surat.tsx create mode 100644 src/components/dashboard/division-progress.tsx create mode 100644 src/components/dashboard/index.ts create mode 100644 src/components/dashboard/satisfaction-chart.tsx create mode 100644 src/components/dashboard/sdgs-card.tsx create mode 100644 src/components/dashboard/stat-card.tsx diff --git a/src/components/dashboard-content.tsx b/src/components/dashboard-content.tsx index 30349ee..319137e 100644 --- a/src/components/dashboard-content.tsx +++ b/src/components/dashboard-content.tsx @@ -1,523 +1,242 @@ -import { - Calendar, - CheckCircle, - FileText, - MessageCircle, - Users, -} from "lucide-react"; -import { - Bar, - BarChart, - CartesianGrid, - Cell, - Pie, - PieChart, - ResponsiveContainer, - Tooltip, // Added Tooltip import - XAxis, - YAxis, -} from "recharts"; +import { Grid, Stack, useMantineColorScheme } from "@mantine/core"; +import { CheckCircle, FileText, MessageCircle, Users } from "lucide-react"; +import { ActivityList } from "./dashboard/activity-list"; +import { ChartAPBDes } from "./dashboard/chart-apbdes"; +import { ChartSurat } from "./dashboard/chart-surat"; +import { DivisionProgress } from "./dashboard/division-progress"; +import { SatisfactionChart } from "./dashboard/satisfaction-chart"; +import { SDGSCard } from "./dashboard/sdgs-card"; +import { StatCard } from "./dashboard/stat-card"; -// Import Mantine components +// SDGs Icons +function EnergyIcon() { + return ( + + + + ); +} -import { - ActionIcon, - Badge, - Box, - Card, // Added for icon containers - Grid, - Group, - Progress, - Stack, - Text, - ThemeIcon, - Title, - useMantineColorScheme, // Add this import -} from "@mantine/core"; +function PeaceIcon() { + return ( + + + + + ); +} -const barChartData = [ - { month: "Jan", value: 145 }, - { month: "Feb", value: 165 }, - { month: "Mar", value: 195 }, - { month: "Apr", value: 155 }, - { month: "Mei", value: 205 }, - { month: "Jun", value: 185 }, -]; +function HealthIcon() { + return ( + + + + ); +} -const pieChartData = [ - { name: "Puas", value: 25 }, - { name: "Cukup", value: 25 }, - { name: "Kurang", value: 25 }, - { name: "Sangat puas", value: 25 }, -]; +function PovertyIcon() { + return ( + + + + + ); +} -const COLORS = ["#4E5BA6", "#F4C542", "#8CC63F", "#E57373"]; +function OceanIcon() { + return ( + + + + + + ); +} -const divisiData = [ - { name: "Kesejahteraan", value: 37 }, - { name: "Pemerintahan", value: 26 }, - { name: "Keuangan", value: 17 }, - { name: "Sekretaris Desa", value: 15 }, -]; - -const eventData = [ - { date: "1 Oktober 2025", title: "Hari Kesaktian Pancasila" }, - { date: "15 Oktober 2025", title: "Davest" }, - { date: "19 Oktober 2025", title: "Rapat Koordinasi" }, +const sdgsData = [ + { + title: "Desa Berenergi Bersih dan Terbarukan", + score: 99.64, + icon: , + color: "#FACC15", + bgColor: "#FEF9C3", + }, + { + title: "Desa Damai Berkeadilan", + score: 78.65, + icon: , + color: "#3B82F6", + bgColor: "#DBEAFE", + }, + { + title: "Desa Sehat dan Sejahtera", + score: 77.37, + icon: , + color: "#22C55E", + bgColor: "#DCFCE7", + }, + { + title: "Desa Tanpa Kemiskinan", + score: 52.62, + icon: , + color: "#EF4444", + bgColor: "#FEE2E2", + }, + { + title: "Desa Peduli Lingkungan Laut", + score: 50.0, + icon: , + color: "#06B6D4", + bgColor: "#CFFAFE", + }, ]; export function DashboardContent() { const { colorScheme } = useMantineColorScheme(); const dark = colorScheme === "dark"; + return ( - {/* Stats Cards */} + {/* Header Metrics - 4 Stat Cards */} - - - - - Surat Minggu Ini - - - - 99 - - - - 14 baru, 14 diproses - - - 12% dari minggu lalu ↗ +12% - - - - - - - + } + /> - - - - - Pengaduan Aktif - - - - 28 - - - - 14 baru, 14 diproses - - - - - - - + } + /> - - - - - Layanan Selesai - - - - 156 - - - - bulan ini - - - +8% - - - - - - - + } + /> - - - - - Kepuasan Warga - - - - 87.2% - - - - dari 482 responden - - - - - - - + } + /> + + {/* Section 2: Chart & Division Progress */} - {/* Bar Chart */} - - - - - - Statistik Pengajuan Surat - - - Trend pengajuan surat 6 bulan terakhir - - - - {/* Original SVG converted to a generic Icon placeholder */} - - - - - - - - - - - - - - - + + - - {/* Pie Chart */} - - - - Tingkat Kepuasan - - - Tingkat kepuasan layanan - - - - - {pieChartData.map((_entry, index) => ( - - ))} - - - - - - - - Sangat puas (0%) - - - - Puas (0%) - - - - Cukup (0%) - - - - Kurang (0%) - - - + + - {/* Bottom Section */} + {/* Section 3: APBDes Chart */} + + + {/* Section 4 & 5: Activity List & Satisfaction Chart */} - {/* Divisi Teraktif */} - - - - {/* Original SVG icon */} - - - - - - - - Divisi Teraktif - - - {divisiData.map((divisi, index) => ( - - - - {divisi.name} - - - {divisi.value} Kegiatan - - - - - ))} - - + - - {/* Kalender */} - - - - Kalender & Kegiatan Mendatang - - - {eventData.map((event, index) => ( - - - {event.date} - - {event.title} - - ))} - - + - {/* APBDes Chart */} - - - Grafik APBDes - - - - - Belanja - - + {sdgsData.map((sdg, index) => ( + + - - - - Pendapatan - - - - - - Pembangunan - - - - - + + ))} + ); -} \ No newline at end of file +} diff --git a/src/components/dashboard/activity-list.tsx b/src/components/dashboard/activity-list.tsx new file mode 100644 index 0000000..d42067c --- /dev/null +++ b/src/components/dashboard/activity-list.tsx @@ -0,0 +1,69 @@ +import { + Box, + Card, + Group, + Stack, + Text, + Title, + useMantineColorScheme, +} from "@mantine/core"; +import { Calendar } from "lucide-react"; + +interface EventData { + date: string; + title: string; +} + +const events: EventData[] = [ + { date: "1 Oktober 2025", title: "Hari Kesaktian Pancasila" }, + { date: "15 Oktober 2025", title: "Davest" }, + { date: "19 Oktober 2025", title: "Rapat Koordinasi" }, +]; + +export function ActivityList() { + const { colorScheme } = useMantineColorScheme(); + const dark = colorScheme === "dark"; + + return ( + + + + + Kalender & Kegiatan Mendatang + + + + {events.map((event, index) => ( + + + {event.date} + + + {event.title} + + + ))} + + + ); +} diff --git a/src/components/dashboard/chart-apbdes.tsx b/src/components/dashboard/chart-apbdes.tsx new file mode 100644 index 0000000..6b1f62b --- /dev/null +++ b/src/components/dashboard/chart-apbdes.tsx @@ -0,0 +1,74 @@ +import { + Card, + Group, + Stack, + Text, + Title, + useMantineColorScheme, +} from "@mantine/core"; +import { + Bar, + BarChart, + Cell, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from "recharts"; + +const apbdesData = [ + { name: "Belanja", value: 70, color: "#3B82F6" }, + { name: "Pangan", value: 45, color: "#22C55E" }, + { name: "Pembiayaan", value: 55, color: "#FACC15" }, + { name: "Pendapatan", value: 90, color: "#3B82F6" }, +]; + +export function ChartAPBDes() { + const { colorScheme } = useMantineColorScheme(); + const dark = colorScheme === "dark"; + + return ( + + + Grafik APBDes + + + {apbdesData.map((item, index) => ( + + + {item.name} + + + + + + [`${value}%`, ""]} + contentStyle={{ + backgroundColor: dark ? "#1E293B" : "white", + borderColor: dark ? "#334155" : "#e5e7eb", + borderRadius: "8px", + }} + /> + + + + + + + ))} + + + ); +} diff --git a/src/components/dashboard/chart-surat.tsx b/src/components/dashboard/chart-surat.tsx new file mode 100644 index 0000000..3e6a195 --- /dev/null +++ b/src/components/dashboard/chart-surat.tsx @@ -0,0 +1,110 @@ +import { + ActionIcon, + Box, + Card, + Group, + Text, + Title, + useMantineColorScheme, +} from "@mantine/core"; +import { + Bar, + BarChart, + CartesianGrid, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from "recharts"; + +const chartData = [ + { month: "Jan", value: 150 }, + { month: "Feb", value: 165 }, + { month: "Mar", value: 195 }, + { month: "Apr", value: 160 }, + { month: "Mei", value: 205 }, + { month: "Jun", value: 185 }, +]; + +export function ChartSurat() { + const { colorScheme } = useMantineColorScheme(); + const dark = colorScheme === "dark"; + + return ( + + + + + Statistik Pengajuan Surat + + + Trend pengajuan surat 6 bulan terakhir + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/dashboard/division-progress.tsx b/src/components/dashboard/division-progress.tsx new file mode 100644 index 0000000..36d40cc --- /dev/null +++ b/src/components/dashboard/division-progress.tsx @@ -0,0 +1,69 @@ +import { + Box, + Card, + Group, + Progress, + Stack, + Text, + Title, + useMantineColorScheme, +} from "@mantine/core"; + +interface DivisionData { + name: string; + value: number; +} + +const divisionData: DivisionData[] = [ + { name: "Kesejahteraan", value: 37 }, + { name: "Pemberdayaan", value: 26 }, + { name: "Keuangan", value: 17 }, + { name: "Sekretaris Desa", value: 15 }, +]; + +const max_value = 37; + +export function DivisionProgress() { + const { colorScheme } = useMantineColorScheme(); + const dark = colorScheme === "dark"; + + return ( + + + Divisi Teraktif + + + {divisionData.map((divisi, index) => ( + + + + {divisi.name} + + + {divisi.value} Kegiatan + + + + + ))} + + + ); +} diff --git a/src/components/dashboard/index.ts b/src/components/dashboard/index.ts new file mode 100644 index 0000000..d3998cc --- /dev/null +++ b/src/components/dashboard/index.ts @@ -0,0 +1,7 @@ +export { ActivityList } from "./activity-list"; +export { ChartAPBDes } from "./chart-apbdes"; +export { ChartSurat } from "./chart-surat"; +export { DivisionProgress } from "./division-progress"; +export { SatisfactionChart } from "./satisfaction-chart"; +export { SDGSCard } from "./sdgs-card"; +export { StatCard } from "./stat-card"; diff --git a/src/components/dashboard/satisfaction-chart.tsx b/src/components/dashboard/satisfaction-chart.tsx new file mode 100644 index 0000000..7db291d --- /dev/null +++ b/src/components/dashboard/satisfaction-chart.tsx @@ -0,0 +1,82 @@ +import { + Box, + Card, + Group, + Stack, + Text, + Title, + useMantineColorScheme, +} from "@mantine/core"; +import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from "recharts"; + +const satisfactionData = [ + { name: "Sangat Puas", value: 25, color: "#4E5BA6" }, + { name: "Puas", value: 25, color: "#F4C542" }, + { name: "Cukup", value: 25, color: "#8CC63F" }, + { name: "Kurang", value: 25, color: "#E57373" }, +]; + +export function SatisfactionChart() { + const { colorScheme } = useMantineColorScheme(); + const dark = colorScheme === "dark"; + + return ( + + + Tingkat Kepuasan + + + Tingkat kepuasan layanan + + + + + {satisfactionData.map((entry, index) => ( + + ))} + + + + + + {satisfactionData.map((item, index) => ( + + + + {item.name} + + + ))} + + + ); +} diff --git a/src/components/dashboard/sdgs-card.tsx b/src/components/dashboard/sdgs-card.tsx new file mode 100644 index 0000000..f9c8431 --- /dev/null +++ b/src/components/dashboard/sdgs-card.tsx @@ -0,0 +1,53 @@ +import { Box, Card, Group, Text, useMantineColorScheme } from "@mantine/core"; +import type { ReactNode } from "react"; + +interface SDGSCardProps { + title: string; + score: number; + icon: ReactNode; + color: string; + bgColor: string; +} + +export function SDGSCard({ + title, + score, + icon, + color, + bgColor, +}: SDGSCardProps) { + const { colorScheme } = useMantineColorScheme(); + const dark = colorScheme === "dark"; + + return ( + + + + + {title} + + + {score.toFixed(2)} + + + + {icon} + + + + ); +} diff --git a/src/components/dashboard/stat-card.tsx b/src/components/dashboard/stat-card.tsx new file mode 100644 index 0000000..25de1d5 --- /dev/null +++ b/src/components/dashboard/stat-card.tsx @@ -0,0 +1,86 @@ +import { + Box, + Card, + Group, + Text, + ThemeIcon, + useMantineColorScheme, +} from "@mantine/core"; +import type { ReactNode } from "react"; + +interface StatCardProps { + title: string; + value: string | number; + detail?: string; + trend?: string; + trendValue?: number; + icon: ReactNode; + iconColor?: string; +} + +export function StatCard({ + title, + value, + detail, + trend, + trendValue, + icon, + iconColor = "darmasaba-blue", +}: StatCardProps) { + const { colorScheme } = useMantineColorScheme(); + const dark = colorScheme === "dark"; + + const isPositiveTrend = trendValue ? trendValue >= 0 : true; + + return ( + + + + + {title} + + + + {value} + + + {detail && ( + + {detail} + + )} + {trend && ( + + {trend} + + )} + + + {icon} + + + + ); +}