feat: update components with Mantine UI and improve dark mode support

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-02-12 15:47:31 +08:00
parent cffb9f4aa4
commit 4ed1c664d1
22 changed files with 3074 additions and 588 deletions

View File

@@ -0,0 +1,303 @@
import { useState } from "react";
import {
Card,
Grid,
GridCol,
Group,
Text,
Title,
Button,
Badge,
Table,
Stack,
Select,
useMantineColorScheme
} from "@mantine/core";
import { IconBuildingStore, IconCategory, IconCurrency, IconUsers } from "@tabler/icons-react";
const BumdesPage = () => {
const { colorScheme } = useMantineColorScheme();
const dark = colorScheme === 'dark';
const [timeFilter, setTimeFilter] = useState<string>("bulan");
// Sample data for KPI cards
const kpiData = [
{
title: "UMKM Aktif",
value: 45,
icon: <IconUsers size={24} />,
color: "darmasaba-blue",
},
{
title: "UMKM Terdaftar",
value: 68,
icon: <IconBuildingStore size={24} />,
color: "darmasaba-success",
},
{
title: "Omzet",
value: "Rp 48.000.000",
icon: <IconCurrency size={24} />,
color: "darmasaba-warning",
},
{
title: "Kategori UMKM",
value: 34,
icon: <IconCategory size={24} />,
color: "darmasaba-danger",
},
];
// Sample data for top products
const topProducts = [
{
rank: 1,
name: "Beras Premium Organik",
umkmOwner: "Warung Pak Joko",
growth: "+12%",
},
{
rank: 2,
name: "Keripik Singkong",
umkmOwner: "Ibu Sari Snack",
growth: "+8%",
},
{
rank: 3,
name: "Madu Alami",
umkmOwner: "Peternakan Lebah",
growth: "+5%",
},
];
// Sample data for product sales
const productSales = [
{
produk: "Beras Premium Organik",
penjualanBulanIni: "Rp 8.500.000",
bulanLalu: "Rp 8.500.000",
trend: 10,
volume: "650 Kg",
stok: "850 Kg",
},
{
produk: "Keripik Singkong",
penjualanBulanIni: "Rp 4.200.000",
bulanLalu: "Rp 3.800.000",
trend: 10,
volume: "320 Kg",
stok: "120 Kg",
},
{
produk: "Madu Alami",
penjualanBulanIni: "Rp 3.750.000",
bulanLalu: "Rp 4.100.000",
trend: -8,
volume: "150 Liter",
stok: "45 Liter",
},
{
produk: "Kecap Tradisional",
penjualanBulanIni: "Rp 2.800.000",
bulanLalu: "Rp 2.500.000",
trend: 12,
volume: "280 Botol",
stok: "95 Botol",
},
];
return (
<Stack gap="lg">
{/* Page Header */}
<Group justify="space-between" align="center">
<Title order={2} c={dark ? "dark.0" : "black"}>
BUMDes & UMKM Desa
</Title>
</Group>
{/* KPI Cards */}
<Grid gutter="md">
{kpiData.map((kpi, index) => (
<GridCol key={index} span={{ base: 12, sm: 6, md: 3 }}>
<Card withBorder radius="md" padding="lg">
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
{kpi.title}
</Text>
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
{typeof kpi.value === 'number' ? kpi.value.toLocaleString() : kpi.value}
</Text>
</Stack>
<Badge
variant="light"
color={kpi.color}
p={8}
radius="md"
>
{kpi.icon}
</Badge>
</Group>
</Card>
</GridCol>
))}
</Grid>
{/* Update Penjualan Produk Header */}
<Card withBorder radius="md" bg={dark ? "dark.8" : "darmasaba-blue.0"}>
<Group justify="space-between" align="center" px="md" py="xs">
<Title order={3} c={dark ? "dark.0" : "black"}>
Update Penjualan Produk
</Title>
<Group>
<Button
variant={timeFilter === "minggu" ? "filled" : "light"}
onClick={() => setTimeFilter("minggu")}
color="darmasaba-blue"
>
Minggu ini
</Button>
<Button
variant={timeFilter === "bulan" ? "filled" : "light"}
onClick={() => setTimeFilter("bulan")}
color="darmasaba-blue"
>
Bulan ini
</Button>
</Group>
</Group>
</Card>
<Grid gutter="md">
{/* Produk Unggulan (Left Column) */}
<GridCol span={{ base: 12, lg: 4 }}>
<Stack gap="md">
{/* Total Penjualan, Produk Aktif, Total Transaksi */}
<Card withBorder radius="md" p="lg">
<Stack gap="md">
<Group justify="space-between">
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>Total Penjualan</Text>
<Text size="lg" fw={700} c={dark ? "dark.0" : "black"}>Rp 28.500.000</Text>
</Group>
<Group justify="space-between">
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>Produk Aktif</Text>
<Text size="lg" fw={700} c={dark ? "dark.0" : "black"}>124 Produk</Text>
</Group>
<Group justify="space-between">
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>Total Transaksi</Text>
<Text size="lg" fw={700} c={dark ? "dark.0" : "black"}>1.240 Transaksi</Text>
</Group>
</Stack>
</Card>
{/* Top 3 Produk Terlaris */}
<Card withBorder radius="md" p="lg">
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>Top 3 Produk Terlaris</Title>
<Stack gap="sm">
{topProducts.map((product) => (
<Group key={product.rank} justify="space-between" align="center">
<Group gap="sm">
<Badge
variant="filled"
color={product.rank === 1 ? "gold" : product.rank === 2 ? "gray" : "bronze"}
radius="xl"
size="lg"
>
{product.rank}
</Badge>
<Stack gap={0}>
<Text fw={500} c={dark ? "dark.0" : "black"}>{product.name}</Text>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{product.umkmOwner}</Text>
</Stack>
</Group>
<Badge
variant="light"
color={product.growth.startsWith('+') ? "green" : "red"}
>
{product.growth}
</Badge>
</Group>
))}
</Stack>
</Card>
</Stack>
</GridCol>
{/* Detail Penjualan Produk (Right Column) */}
<GridCol span={{ base: 12, lg: 8 }}>
<Card withBorder radius="md" p="lg">
<Group justify="space-between" mb="md">
<Title order={4} c={dark ? "dark.0" : "black"}>Detail Penjualan Produk</Title>
<Select
placeholder="Filter kategori"
data={[
{ value: 'semua', label: 'Semua Kategori' },
{ value: 'makanan', label: 'Makanan' },
{ value: 'minuman', label: 'Minuman' },
{ value: 'kerajinan', label: 'Kerajinan' },
]}
defaultValue="semua"
w={200}
/>
</Group>
<Table striped highlightOnHover withColumnBorders>
<Table.Thead>
<Table.Tr>
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Produk</Text></Table.Th>
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Penjualan Bulan Ini</Text></Table.Th>
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Bulan Lalu</Text></Table.Th>
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Trend</Text></Table.Th>
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Volume</Text></Table.Th>
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Stok</Text></Table.Th>
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Aksi</Text></Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{productSales.map((product, index) => (
<Table.Tr key={index}>
<Table.Td>
<Text fw={500} c={dark ? "dark.0" : "black"}>{product.produk}</Text>
</Table.Td>
<Table.Td>
<Text fz={"sm"} c={dark ? "dark.0" : "black"}>{product.penjualanBulanIni}</Text>
</Table.Td>
<Table.Td>
<Text fz={"sm"} c={dark ? "dark.3" : "dimmed"}>{product.bulanLalu}</Text>
</Table.Td>
<Table.Td>
<Group gap="xs">
<Text c={product.trend >= 0 ? "green" : "red"}>
{product.trend >= 0 ? '↑' : '↓'} {Math.abs(product.trend)}%
</Text>
</Group>
</Table.Td>
<Table.Td>
<Text fz={"sm"} c={dark ? "dark.0" : "black"}>{product.volume}</Text>
</Table.Td>
<Table.Td>
<Badge
variant="light"
color={parseInt(product.stok) > 200 ? "green" : "yellow"}
>
{product.stok}
</Badge>
</Table.Td>
<Table.Td>
<Button variant="subtle" size="compact-sm" color="darmasaba-blue">
Detail
</Button>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Card>
</GridCol>
</Grid>
</Stack>
);
};
export default BumdesPage;

View File

@@ -74,7 +74,7 @@ export function DashboardContent() {
{/* Stats Cards */}
<Grid gutter="md">
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
<Card p="md" radius="md" h="100%" withBorder>
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" h="100%" withBorder bg={dark ? "#141D34" : "white"}>
<Group justify="space-between" align="flex-start" w="100%">
<Box style={{ flex: 1 }}>
<Text size="sm" c="dimmed" mb="xs">
@@ -99,7 +99,7 @@ export function DashboardContent() {
</Card>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
<Card p="md" radius="md" h="100%" withBorder>
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" h="100%" withBorder bg={dark ? "#141D34" : "white"}>
<Group justify="space-between" align="flex-start" w="100%">
<Box style={{ flex: 1 }}>
<Text size="sm" c="dimmed" mb="xs">
@@ -121,7 +121,7 @@ export function DashboardContent() {
</Card>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
<Card p="md" radius="md" h="100%" withBorder>
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" h="100%" withBorder bg={dark ? "#141D34" : "white"}>
<Group justify="space-between" align="flex-start" w="100%">
<Box style={{ flex: 1 }}>
<Text size="sm" c="dimmed" mb="xs">
@@ -146,7 +146,7 @@ export function DashboardContent() {
</Card>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
<Card p="md" radius="md" h="100%" withBorder>
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" h="100%" withBorder bg={dark ? "#141D34" : "white"}>
<Group justify="space-between" align="flex-start" w="100%">
<Box style={{ flex: 1 }}>
<Text size="sm" c="dimmed" mb="xs">
@@ -171,7 +171,7 @@ export function DashboardContent() {
<Grid gutter="lg">
{/* Bar Chart */}
<Grid.Col span={{ base: 12, lg: 6 }}>
<Card p="md" radius="md" withBorder>
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" withBorder bg={dark ? "#141D34" : "white"}>
<Group justify="space-between" mb="md">
<Box>
<Title order={4} mb={5}>
@@ -232,7 +232,7 @@ export function DashboardContent() {
{/* Pie Chart */}
<Grid.Col span={{ base: 12, lg: 6 }}>
<Card p="md" radius="md" withBorder>
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" withBorder bg={dark ? "#141D34" : "white"}>
<Title order={4} mb={5}>
Tingkat Kepuasan
</Title>
@@ -283,7 +283,7 @@ export function DashboardContent() {
<Grid gutter="lg">
{/* Divisi Teraktif */}
<Grid.Col span={{ base: 12, lg: 6 }}>
<Card p="md" radius="md" withBorder>
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" withBorder bg={dark ? "#141D34" : "white"}>
<Group gap="xs" mb="lg">
<Box>
{/* Original SVG icon */}
@@ -355,7 +355,7 @@ export function DashboardContent() {
{/* Kalender */}
<Grid.Col span={{ base: 12, lg: 6 }}>
<Card p="md" radius="md" withBorder>
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" withBorder bg={dark ? "#141D34" : "white"}>
<Group gap="xs" mb="lg">
<Calendar style={{ width: 20, height: 20 }} />
<Title order={4}>Kalender & Kegiatan Mendatang</Title>
@@ -378,7 +378,7 @@ export function DashboardContent() {
</Grid>
{/* APBDes Chart */}
<Card p="md" radius="md" withBorder>
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" withBorder bg={dark ? "#141D34" : "white"}>
<Title order={4} mb="lg">
Grafik APBDes
</Title>

View File

@@ -140,7 +140,7 @@ const DemografiPekerjaan = () => {
<Box className="space-y-6">
<Stack gap="xl">
<Group justify="space-between" align="center">
<Title order={1} fw={700}>
<Title order={2} fw={700}>
Demografi & Kependudukan
</Title>
<Button variant="filled">Export Data</Button>

View File

@@ -22,7 +22,7 @@ export function Header() {
const getPageTitle = () => {
switch (location.pathname) {
case "/":
return "Dashboard";
return "Desa Darmasaba";
case "/kinerja-divisi":
return "Kinerja Divisi";
case "/pengaduan":
@@ -43,14 +43,14 @@ export function Header() {
case "/pengaturan":
return "Pengaturan";
default:
return "Dashboard";
return "Desa Darmasaba";
}
};
return (
<Group justify="space-between" w="100%">
{/* Title */}
<Title order={2}>{getPageTitle()}</Title>
<Title order={3} c={"white"}>{getPageTitle()}</Title>
{/* Right Section */}
<Group gap="md">
@@ -59,15 +59,15 @@ export function Header() {
{/* User Info */}
<Group gap="sm">
<Box ta="right">
<Text size="sm" fw={500}>
<Text c={"white"} size="sm" fw={500}>
I. B. Surya Prabhawa M...
</Text>
<Text size="xs" c="dimmed">
<Text c={"white"} size="xs">
Kepala Desa
</Text>
</Box>
<Avatar color="blue" radius="xl">
<UserIcon style={{ width: "70%", height: "70%" }} />
<UserIcon color="white" style={{ width: "70%", height: "70%" }} />
</Avatar>
</Group>
@@ -84,13 +84,13 @@ export function Header() {
aria-label="Toggle color scheme"
>
{dark ? (
<Sun style={{ width: "70%", height: "70%" }} />
<Sun color="white" style={{ width: "70%", height: "70%" }} />
) : (
<Moon style={{ width: "70%", height: "70%" }} />
<Moon color="white" style={{ width: "70%", height: "70%" }} />
)}
</ActionIcon>
<ActionIcon variant="subtle" size="lg" radius="xl" pos="relative">
<Bell style={{ width: "70%", height: "70%" }} />
<Bell color="white" style={{ width: "70%", height: "70%" }} />
<Badge
size="xs"
color="red"

View File

@@ -10,6 +10,7 @@ import {
Stack,
Grid,
Box,
useMantineColorScheme,
} from "@mantine/core";
import { BarChart } from "@mantine/charts";
@@ -133,21 +134,17 @@ const busyHours = [
];
const JennaAnalytic = () => {
const { colorScheme } = useMantineColorScheme();
const dark = colorScheme === "dark";
return (
<Box className="space-y-6">
<Stack gap="xl">
<Group justify="space-between" align="center">
<Title order={1} fw={700}>
Jenna Analytic
</Title>
<Button variant="filled">Export Data</Button>
</Group>
{/* KPI Cards */}
<Grid gutter="lg">
{kpiData.map((kpi) => (
<Grid.Col key={kpi.id} span={{ base: 12, md: 6, lg: 3 }}>
<Card shadow="sm" padding="lg" radius="md" withBorder>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
<Group justify="space-between" align="flex-start" mb="xs">
<Text size="sm" fw={500} c="dimmed">
{kpi.title}
@@ -185,21 +182,42 @@ const JennaAnalytic = () => {
))}
</Grid>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
<Title order={3} fw={500} mb="md">
Interaksi Chatbot
</Title>
<BarChart
h={300}
data={chartData}
dataKey="day"
series={[{ name: 'total', color: 'blue' }]}
withLegend
/>
</Card>
{/* Charts and Lists Section */}
<Grid gutter="lg">
{/* Grafik Interaksi Chatbot (now Bar Chart) */}
<Grid.Col span={{ base: 12, lg: 6 }}>
<Card shadow="sm" padding="lg" radius="md" withBorder>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
<Title order={3} fw={500} mb="md">
Interaksi Chatbot
Jam Tersibuk
</Title>
<BarChart
h={300}
data={chartData}
dataKey="day"
series={[{ name: 'total', color: 'blue' }]}
withLegend
/>
<Stack gap="sm">
{busyHours.map((item, index) => (
<Box key={index}>
<Text size="sm">
{item.period}
</Text>
<Group align="center">
<Progress value={item.percentage} flex={1} />
<Text size="sm" fw={500}>
{item.percentage}%
</Text>
</Group>
</Box>
))}
</Stack>
</Card>
</Grid.Col>
@@ -207,7 +225,7 @@ const JennaAnalytic = () => {
<Grid.Col span={{ base: 12, lg: 6 }}>
<Stack gap="lg">
{/* Topik Pertanyaan Terbanyak */}
<Card shadow="sm" padding="lg" radius="md" withBorder>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
<Title order={3} fw={500} mb="md">
Topik Pertanyaan Terbanyak
</Title>
@@ -231,29 +249,12 @@ const JennaAnalytic = () => {
</Card>
{/* Jam Tersibuk */}
<Card shadow="sm" padding="lg" radius="md" withBorder>
<Title order={3} fw={500} mb="md">
Jam Tersibuk
</Title>
<Stack gap="sm">
{busyHours.map((item, index) => (
<Group key={index} align="center">
<Text w={80} size="sm">
{item.period}
</Text>
<Progress value={item.percentage} flex={1} />
<Text size="sm" fw={500}>
{item.percentage}%
</Text>
</Group>
))}
</Stack>
</Card>
</Stack>
</Grid.Col>
</Grid>
</Stack>
</Box>
</Grid >
</Stack >
</Box >
);
}

View File

@@ -0,0 +1,225 @@
import { useState } from "react";
import {
Card,
Grid,
GridCol,
Group,
Text,
Title,
Stack,
useMantineColorScheme,
Badge,
List,
ThemeIcon,
Box
} from "@mantine/core";
import {
IconCamera,
IconAlertTriangle,
IconMapPin,
IconClock,
IconEye,
IconShieldLock
} from "@tabler/icons-react";
const KeamananPage = () => {
const { colorScheme } = useMantineColorScheme();
const dark = colorScheme === 'dark';
// Sample data for KPI cards
const kpiData = [
{
title: "CCTV Aktif",
value: 20,
subtitle: "Kamera Online",
icon: <IconCamera size={24} />,
color: "darmasaba-success",
},
{
title: "Laporan Keamanan",
value: 15,
subtitle: "Minggu ini",
icon: <IconAlertTriangle size={24} />,
color: "darmasaba-danger",
},
];
// Sample data for CCTV locations
const cctvLocations = [
{ id: "CCTV-01", lat: -8.5, lng: 115.2, status: "active", lastSeen: "2 jam yang lalu", location: "Balai Desa" },
{ id: "CCTV-02", lat: -8.6, lng: 115.3, status: "active", lastSeen: "1 jam yang lalu", location: "Pintu Masuk Desa" },
{ id: "CCTV-03", lat: -8.4, lng: 115.1, status: "offline", lastSeen: "1 hari yang lalu", location: "Taman Desa" },
{ id: "CCTV-04", lat: -8.7, lng: 115.4, status: "active", lastSeen: "30 menit yang lalu", location: "Pasar Desa" },
];
// Sample data for security reports
const securityReports = [
{
id: "REP-001",
title: "Pencurian Motor",
reportedAt: "2 jam yang lalu",
date: "12 Feb 2026, 14:30",
location: "Jl. Kecubung 20",
status: "baru",
},
{
id: "REP-002",
title: "Kerusuhan Antar Warga",
reportedAt: "4 jam yang lalu",
date: "12 Feb 2026, 12:15",
location: "RT 05 RW 02",
status: "baru",
},
{
id: "REP-003",
title: "Kebakaran Rumah",
reportedAt: "1 hari yang lalu",
date: "11 Feb 2026, 08:45",
location: "Jl. Flamboyan 15",
status: "diproses",
},
{
id: "REP-004",
title: "Kehilangan Barang",
reportedAt: "2 hari yang lalu",
date: "10 Feb 2026, 16:20",
location: "Taman Desa",
status: "selesai",
},
];
return (
<Stack gap="lg">
{/* Page Header */}
<Group justify="space-between" align="center">
<Title order={2} c={dark ? "dark.0" : "black"}>
Keamanan Lingkungan Desa
</Title>
</Group>
{/* KPI Cards */}
<Grid gutter="md">
{kpiData.map((kpi, index) => (
<GridCol key={index} span={{ base: 12, sm: 6, md: 6 }}>
<Card withBorder radius="md" padding="lg">
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
{kpi.subtitle}
</Text>
<Group gap="xs" align="center">
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
{kpi.value}
</Text>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
{kpi.title}
</Text>
</Group>
</Stack>
<ThemeIcon variant="light" color={kpi.color} size="xl" radius="xl">
{kpi.icon}
</ThemeIcon>
</Group>
</Card>
</GridCol>
))}
</Grid>
<Grid gutter="md">
{/* Peta Keamanan CCTV */}
<GridCol span={{ base: 12, lg: 6 }}>
<Card withBorder radius="md" p="lg">
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Peta Keamanan CCTV</Title>
<Text size="sm" c={dark ? "dark.3" : "dimmed"} mb="md">Titik Lokasi CCTV</Text>
{/* Placeholder for map */}
<Box
style={{
backgroundColor: dark ? '#2d3748' : '#e2e8f0',
borderRadius: '8px',
height: '400px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Stack align="center">
<IconMapPin size={48} stroke={1.5} color={dark ? '#94a3b8' : '#64748b'} />
<Text c={dark ? "dark.3" : "dimmed"}>Peta Lokasi CCTV</Text>
<Text size="sm" c={dark ? "dark.3" : "dimmed"} ta="center">Integrasi dengan Google Maps atau Mapbox akan ditampilkan di sini</Text>
</Stack>
</Box>
{/* CCTV Locations List */}
<Stack mt="md" gap="sm">
<Title order={4} c={dark ? "dark.0" : "black"}>Daftar CCTV</Title>
{cctvLocations.map((cctv, index) => (
<Card key={index} withBorder radius="md" p="md">
<Group justify="space-between">
<Stack gap={0}>
<Group gap="xs">
<Text fw={500} c={dark ? "dark.0" : "black"}>{cctv.id}</Text>
<Badge
variant="dot"
color={cctv.status === "active" ? "green" : "gray"}
>
{cctv.status === "active" ? "Online" : "Offline"}
</Badge>
</Group>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{cctv.location}</Text>
</Stack>
<Group gap="xs">
<IconClock size={16} stroke={1.5} />
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{cctv.lastSeen}</Text>
</Group>
</Group>
</Card>
))}
</Stack>
</Card>
</GridCol>
{/* Daftar Laporan Keamanan */}
<GridCol span={{ base: 12, lg: 6 }}>
<Card withBorder radius="md" p="lg">
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Laporan Keamanan Lingkungan</Title>
<Stack gap="sm">
{securityReports.map((report, index) => (
<Card key={index} withBorder radius="md" p="md">
<Group justify="space-between" mb="sm">
<Text fw={500} c={dark ? "dark.0" : "black"}>{report.title}</Text>
<Badge
variant="light"
color={
report.status === "baru" ? "red" :
report.status === "diproses" ? "yellow" : "green"
}
>
{report.status}
</Badge>
</Group>
<Group justify="space-between">
<Group gap="xs">
<IconMapPin size={16} stroke={1.5} />
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{report.location}</Text>
</Group>
<Group gap="xs">
<IconClock size={16} stroke={1.5} />
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{report.reportedAt}</Text>
</Group>
</Group>
<Text size="sm" c={dark ? "dark.3" : "dimmed"} mt="sm">{report.date}</Text>
</Card>
))}
</Stack>
</Card>
</GridCol>
</Grid>
</Stack>
);
};
export default KeamananPage;

View File

@@ -116,10 +116,10 @@ const apbdReport = {
const KeuanganAnggaran = () => {
return (
<Box p="md">
<Box>
<Stack gap="xl">
<Group justify="space-between" align="center">
<Title order={1} fw={700}>
<Title order={2} fw={700}>
Keuangan & Anggaran
</Title>
<Button variant="filled">Export Laporan</Button>

View File

@@ -1,265 +1,343 @@
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; // Correct import for Button
import {
Stack,
Grid,
GridCol,
Group,
Text,
Title,
ActionIcon,
Progress as MantineProgress,
Box,
Badge as MantineBadge,
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
useMantineColorScheme,
ThemeIcon,
List,
Divider,
Skeleton
} from "@mantine/core";
import { Button } from "@/components/ui/button";
import { Bar, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, ResponsiveContainer, PieChart, Pie, Cell } from "recharts";
const KinerjaDivisi = () => {
// Sample data for division performance
const divisions = [
const { colorScheme } = useMantineColorScheme();
const dark = colorScheme === 'dark';
// Data for division progress chart
const divisionProgressData = [
{ name: "Sekretariat", selesai: 12, berjalan: 5, tertunda: 2 },
{ name: "Keuangan", selesai: 8, berjalan: 7, tertunda: 1 },
{ name: "Sosial", selesai: 10, berjalan: 3, tertunda: 4 },
{ name: "Humas", selesai: 6, berjalan: 9, tertunda: 3 },
];
// Division task summaries
const divisionTasks = [
{
id: 1,
name: "Divisi Teknologi",
target: 95,
achievement: 87,
status: "On Track",
projects: 12,
budget: "Rp 2.5M",
lastUpdate: "2 days ago",
name: "Sekretariat",
tasks: [
{ title: "Laporan Bulanan", status: "selesai" },
{ title: "Arsip Dokumen", status: "berjalan" },
{ title: "Undangan Rapat", status: "tertunda" },
]
},
{
id: 2,
name: "Divisi Keuangan",
target: 90,
achievement: 92,
status: "Above Target",
projects: 8,
budget: "Rp 1.8M",
lastUpdate: "1 day ago",
name: "Keuangan",
tasks: [
{ title: "Laporan APBDes", status: "selesai" },
{ title: "Verifikasi Dana", status: "tertunda" },
{ title: "Pengeluaran Harian", status: "berjalan" },
]
},
{
id: 3,
name: "Divisi SDM",
target: 85,
achievement: 78,
status: "Needs Attention",
projects: 6,
budget: "Rp 1.2M",
lastUpdate: "3 days ago",
name: "Sosial",
tasks: [
{ title: "Program Bantuan", status: "selesai" },
{ title: "Kegiatan Posyandu", status: "berjalan" },
{ title: "Monitoring Stunting", status: "tertunda" },
]
},
{
id: 4,
name: "Divisi Operasional",
target: 92,
achievement: 89,
status: "On Track",
projects: 15,
budget: "Rp 3.2M",
lastUpdate: "5 hours ago",
},
{
id: 5,
name: "Divisi Pemasaran",
target: 88,
achievement: 91,
status: "Above Target",
projects: 10,
budget: "Rp 2.1M",
lastUpdate: "1 day ago",
name: "Humas",
tasks: [
{ title: "Publikasi Kegiatan", status: "selesai" },
{ title: "Koordinasi Media", status: "berjalan" },
{ title: "Laporan Kegiatan", status: "tertunda" },
]
},
];
// Archive items
const archiveItems = [
{ name: "Surat Keputusan", count: 12 },
{ name: "Laporan Keuangan", count: 8 },
{ name: "Dokumentasi", count: 24 },
{ name: "Notulensi Rapat", count: 15 },
];
// Activity progress
const activityProgress = [
{ name: "Pembangunan Jalan", progress: 75, date: "15 Feb 2026", status: "berjalan" },
{ name: "Posyandu Bulanan", progress: 100, date: "10 Feb 2026", status: "selesai" },
{ name: "Vaksinasi Massal", progress: 45, date: "20 Feb 2026", status: "berjalan" },
{ name: "Festival Budaya", progress: 20, date: "5 Mar 2026", status: "berjalan" },
];
// Document statistics
const documentStats = [
{ name: "Gambar", value: 42 },
{ name: "Dokumen", value: 87 },
];
// Activity progress statistics
const activityProgressStats = [
{ name: "Selesai", value: 12 },
{ name: "Dikerjakan", value: 8 },
{ name: "Segera Dikerjakan", value: 5 },
{ name: "Dibatalkan", value: 2 },
];
const COLORS = ['#10B981', '#F59E0B', '#EF4444', '#6B7280'];
const STATUS_COLORS: Record<string, string> = {
selesai: 'green',
berjalan: 'blue',
tertunda: 'red',
proses: 'yellow'
};
// Discussion data
const discussions = [
{ title: "Pembahasan APBDes 2026", sender: "Kepala Desa", timestamp: "2 jam yang lalu" },
{ title: "Kegiatan Posyandu", sender: "Divisi Sosial", timestamp: "5 jam yang lalu" },
{ title: "Festival Budaya", sender: "Divisi Humas", timestamp: "1 hari yang lalu" },
];
// Today's agenda
const todayAgenda = [
{ time: "09:00", event: "Rapat Evaluasi Bulanan" },
{ time: "14:00", event: "Koordinasi Program Bantuan" },
];
return (
<div className="space-y-6">
<div className="flex justify-between items-center">
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
Kinerja Divisi
</h1>
<div className="flex space-x-4">
<Button variant="default">Export Data</Button>
<Button variant="outline">Filter</Button>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium dark:text-gray-100">Total Divisi</CardTitle>
<div className="h-6 w-6 text-muted-foreground">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
className="h-6 w-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"
/>
</svg>
</div>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">5</div>
<p className="text-xs text-muted-foreground">Jumlah divisi aktif</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium dark:text-gray-100">
Rata-rata Pencapaian
</CardTitle>
<div className="h-6 w-6 text-muted-foreground">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
className="h-6 w-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
/>
</svg>
</div>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">87.4%</div>
<p className="text-xs text-muted-foreground">Target tercapai</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium dark:text-gray-100">
Divisi Melebihi Target
</CardTitle>
<div className="h-6 w-6 text-muted-foreground">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
className="h-6 w-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"
/>
</svg>
</div>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">2</div>
<p className="text-xs text-muted-foreground">Dari total 5 divisi</p>
</CardContent>
</Card>
</div>
<Card>
<CardHeader>
<CardTitle className="dark:text-gray-100">Detail Kinerja Divisi</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead className="dark:text-white">Nama Divisi</TableHead>
<TableHead className="dark:text-white">Target (%)</TableHead>
<TableHead className="dark:text-white">Pencapaian (%)</TableHead>
<TableHead className="dark:text-white">Status</TableHead>
<TableHead className="dark:text-white">Proyek Aktif</TableHead>
<TableHead className="dark:text-white">Anggaran</TableHead>
<TableHead className="dark:text-white">Terakhir Diperbarui</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{divisions.map((division) => (
<TableRow key={division.id}>
<TableCell className="font-medium dark:text-white">
{division.name}
</TableCell>
<TableCell className="dark:text-white">
{division.target}%
</TableCell>
<TableCell className="dark:text-white">
{division.achievement}%
</TableCell>
<TableCell>
<div className="flex items-center">
<Progress
value={division.achievement}
max={100}
className="w-24 mr-2"
/>
<Badge
variant={
division.status === "Above Target"
? "success"
: division.status === "On Track"
? "secondary"
: "destructive"
}
>
{division.status}
</Badge>
</div>
</TableCell>
<TableCell className="dark:text-white">
{division.projects}
</TableCell>
<TableCell className="dark:text-white">
{division.budget}
</TableCell>
<TableCell className="dark:text-white">
{division.lastUpdate}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
<Stack gap="lg">
{/* Grafik Progres Tugas per Divisi */}
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} >
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
Grafik Progres Tugas per Divisi
</Title>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={divisionProgressData}>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke={dark ? "#141D34" : "white"} />
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
/>
<Tooltip
contentStyle={dark
? { backgroundColor: 'var(--mantine-color-dark-7)', borderColor: 'var(--mantine-color-dark-6)' }
: {}}
/>
<Bar dataKey="selesai" stackId="a" fill="#10B981" name="Selesai" radius={[4, 4, 0, 0]} />
<Bar dataKey="berjalan" stackId="a" fill="#3B82F6" name="Berjalan" radius={[4, 4, 0, 0]} />
<Bar dataKey="tertunda" stackId="a" fill="#EF4444" name="Tertunda" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</Card>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle className="dark:text-gray-100">Grafik Pencapaian Divisi</CardTitle>
</CardHeader> <CardContent>
<div className="h-80 flex items-center justify-center bg-gray-50 dark:bg-gray-700 rounded-lg">
<p className="text-gray-500 dark:text-gray-300">
Grafik pencapaian akan ditampilkan di sini
</p>
</div>
</CardContent>
</Card>
{/* Ringkasan Tugas per Divisi */}
<Grid gutter="md">
{divisionTasks.map((division, index) => (
<GridCol key={index} span={{ base: 12, md: 6, lg: 3 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
<Title order={4} mb="sm" c={dark ? 'white' : 'darmasaba-navy'}>
{division.name}
</Title>
<Stack gap="sm">
{division.tasks.map((task, taskIndex) => (
<Box key={taskIndex}>
<Group justify="space-between">
<Text size="sm" c={dark ? 'white' : 'darmasaba-navy'}>{task.title}</Text>
<MantineBadge
color={STATUS_COLORS[task.status] || 'gray'}
variant="light"
>
{task.status}
</MantineBadge>
</Group>
</Box>
))}
</Stack>
</Card>
</GridCol>
))}
</Grid>
<Card>
<CardHeader>
<CardTitle className="dark:text-gray-100">Distribusi Anggaran Divisi</CardTitle>
</CardHeader> <CardContent>
<div className="h-80 flex items-center justify-center bg-gray-50 dark:bg-gray-700 rounded-lg">
<p className="text-gray-500 dark:text-gray-300">
Diagram distribusi anggaran akan ditampilkan di sini
</p>
</div>
</CardContent>
</Card>
</div>
</div>
{/* Arsip Digital Perangkat Desa */}
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
Arsip Digital Perangkat Desa
</Title>
<Grid gutter="md">
{archiveItems.map((item, index) => (
<GridCol key={index} span={{ base: 12, md: 6, lg: 3 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#263852ff" : "#F1F5F9"} style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}>
<Group justify="space-between">
<Text c={dark ? 'white' : 'darmasaba-navy'} fw={500}>{item.name}</Text>
<Text c={dark ? 'white' : 'darmasaba-navy'} fw={700}>{item.count}</Text>
</Group>
</Card>
</GridCol>
))}
</Grid>
</Card>
{/* Kartu Progres Kegiatan */}
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
Progres Kegiatan / Program
</Title>
<Stack gap="md">
{activityProgress.map((activity, index) => (
<Card key={index} p="md" radius="md" withBorder bg={dark ? "#263852ff" : "#F1F5F9"} style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}>
<Group justify="space-between" mb="sm">
<Text c={dark ? 'white' : 'darmasaba-navy'} fw={500}>{activity.name}</Text>
<MantineBadge
color={STATUS_COLORS[activity.status] || 'gray'}
variant="light"
>
{activity.status}
</MantineBadge>
</Group>
<Group justify="space-between">
<MantineProgress
value={activity.progress}
size="sm"
radius="xl"
color={activity.progress === 100 ? "green" : "blue"}
w="calc(100% - 80px)"
/>
<Text size="sm" c={dark ? 'white' : 'darmasaba-navy'}>{activity.progress}%</Text>
</Group>
<Text size="sm" c="dimmed" mt="sm">{activity.date}</Text>
</Card>
))}
</Stack>
</Card>
{/* Statistik Dokumen & Progres Kegiatan */}
<Grid gutter="md">
<GridCol span={{ base: 12, lg: 6 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
Jumlah Dokumen
</Title>
<ResponsiveContainer width="100%" height={200}>
<BarChart data={documentStats}>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke={dark ? "#141D34" : "white"} />
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
/>
<Tooltip
contentStyle={dark
? { backgroundColor: 'var(--mantine-color-dark-7)', borderColor: 'var(--mantine-color-dark-6)' }
: {}}
/>
<Bar dataKey="value" fill={dark ? "var(--mantine-color-blue-6)" : "var(--mantine-color-blue-filled)"} radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</Card>
</GridCol>
<GridCol span={{ base: 12, lg: 6 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
Progres Kegiatan
</Title>
<ResponsiveContainer width="100%" height={200}>
<PieChart>
<Pie
data={activityProgressStats}
cx="50%"
cy="50%"
labelLine={false}
outerRadius={80}
fill="#8884d8"
dataKey="value"
label={({ name, percent }) => `${name}: ${percent ? (percent * 100).toFixed(0) : '0'}%`}
>
{activityProgressStats.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
<Tooltip
contentStyle={dark
? { backgroundColor: 'var(--mantine-color-dark-7)', borderColor: 'var(--mantine-color-dark-6)' }
: {}}
/>
</PieChart>
</ResponsiveContainer>
</Card>
</GridCol>
</Grid>
{/* Diskusi Internal */}
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
Diskusi Internal
</Title>
<Stack gap="sm">
{discussions.map((discussion, index) => (
<Card key={index} p="md" radius="md" withBorder bg={dark ? "#263852ff" : "#F1F5F9"} style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}>
<Group justify="space-between">
<Text c={dark ? 'white' : 'darmasaba-navy'} fw={500}>{discussion.title}</Text>
<Text size="sm" c="dimmed">{discussion.timestamp}</Text>
</Group>
<Text size="sm" c="dimmed">{discussion.sender}</Text>
</Card>
))}
</Stack>
</Card>
{/* Agenda / Acara Hari Ini */}
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
Agenda / Acara Hari Ini
</Title>
{todayAgenda.length > 0 ? (
<Stack gap="sm">
{todayAgenda.map((agenda, index) => (
<Group key={index} align="flex-start">
<Box w={60}>
<Text c="dimmed">{agenda.time}</Text>
</Box>
<Divider orientation="vertical" mx="sm" />
<Text c={dark ? 'white' : 'darmasaba-navy'}>{agenda.event}</Text>
</Group>
))}
</Stack>
) : (
<Text c="dimmed" ta="center" py="md">
Tidak ada acara hari ini
</Text>
)}
</Card>
</Stack>
);
};
export default KinerjaDivisi;
export default KinerjaDivisi;

View File

@@ -1,26 +1,76 @@
import type React from "react";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Button,
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Select } from "@/components/ui/select";
import {
Grid,
GridCol,
Group,
Text,
Title,
TextInput,
Textarea,
Select,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Textarea } from "@/components/ui/textarea";
Badge,
Stack,
useMantineColorScheme,
List,
Divider,
ActionIcon,
Box
} from "@mantine/core";
import { IconMessage, IconAlertTriangle, IconClock, IconCheck, IconChevronRight } from "@tabler/icons-react";
import { Line, LineChart, Bar, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts";
const PengaduanLayananPublik = () => {
const { colorScheme } = useMantineColorScheme();
const dark = colorScheme === 'dark';
// Summary data
const summaryData = {
total: 42,
baru: 14,
diproses: 14,
selesai: 14
};
// Tren pengaduan data
const trenData = [
{ bulan: "Jan", jumlah: 30 },
{ bulan: "Feb", jumlah: 50 },
{ bulan: "Mar", jumlah: 42 },
{ bulan: "Apr", jumlah: 38 },
{ bulan: "Mei", jumlah: 45 },
{ bulan: "Jun", jumlah: 42 }
];
// Surat terbanyak data
const suratData = [
{ jenis: "KTP", jumlah: 24 },
{ jenis: "KK", jumlah: 18 },
{ jenis: "Domisili", jumlah: 15 },
{ jenis: "Usaha", jumlah: 12 },
{ jenis: "Lainnya", jumlah: 8 }
];
// Pengajuan terbaru data
const pengajuanTerbaru = [
{ nama: "Budi Santoso", jenis: "Ketertiban Umum", waktu: "2 jam yang lalu", status: "baru" },
{ nama: "Siti Rahayu", jenis: "Pelayanan Kesehatan", waktu: "5 jam yang lalu", status: "diproses" },
{ nama: "Ahmad Fauzi", jenis: "Infrastruktur", waktu: "1 hari yang lalu", status: "selesai" },
{ nama: "Dewi Lestari", jenis: "Administrasi", waktu: "1 hari yang lalu", status: "baru" },
{ nama: "Joko Widodo", jenis: "Keamanan", waktu: "2 hari yang lalu", status: "diproses" }
];
// Ide inovatif data
const ideInovatif = [
{ nama: "Andi Prasetyo", judul: "Penerapan Smart Village", kategori: "Teknologi" },
{ nama: "Rina Kusuma", judul: "Program Ekowisata Desa", kategori: "Ekonomi" },
{ nama: "Bambang Suryono", judul: "Peningkatan Sanitasi", kategori: "Kesehatan" },
{ nama: "Lina Marlina", judul: "Pusat Kreatif Anak Muda", kategori: "Pendidikan" }
];
const [activeTab, setActiveTab] = useState<"complaints" | "services">(
"complaints",
);
@@ -133,280 +183,463 @@ const PengaduanLayananPublik = () => {
setNewComplaint({ title: "", category: "", description: "" });
};
// Render complaint table rows
const complaintRows = complaints.map((complaint) => (
<Table.Tr key={complaint.id}>
<Table.Td className="font-medium">
<Text c={dark ? "white" : "dark.3"}>{complaint.title}</Text>
</Table.Td>
<Table.Td>
<Text c={dark ? "white" : "dark.3"}>{complaint.category}</Text>
</Table.Td>
<Table.Td>
<Badge
variant="filled"
color={
complaint.status === "Resolved"
? "green"
: complaint.status === "In Progress"
? "yellow"
: "red"
}
>
{complaint.status}
</Badge>
</Table.Td>
<Table.Td>
<Badge
variant="filled"
color={
complaint.priority === "High"
? "red"
: complaint.priority === "Medium"
? "yellow"
: "blue"
}
>
{complaint.priority}
</Badge>
</Table.Td>
<Table.Td>
<Text c={dark ? "white" : "dark.3"}>{complaint.date}</Text>
</Table.Td>
</Table.Tr>
));
// Status badge color mapping
const getStatusColor = (status: string) => {
switch (status) {
case 'baru': return 'red';
case 'diproses': return 'yellow';
case 'selesai': return 'green';
default: return 'gray';
}
};
return (
<div className="space-y-6">
<div className="flex justify-between items-center">
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
Pengaduan & Layanan Publik
</h1>
<div className="flex space-x-4">
<Button
variant={activeTab === "complaints" ? "default" : "outline"}
onClick={() => setActiveTab("complaints")}
className="dark:bg-slate-700 dark:hover:bg-slate-600 dark:text-white"
>
Pengaduan
</Button>
<Button
variant={activeTab === "services" ? "default" : "outline"}
onClick={() => setActiveTab("services")}
className="dark:bg-slate-700 dark:hover:bg-slate-600 dark:text-white"
>
Layanan Publik
</Button>
</div>
</div>
<Stack gap="lg">
{activeTab === "complaints" ? (
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Complaint Submission Form */}
<div className="lg:col-span-1">
<Card className="dark:bg-gray-800 dark:border-gray-700">
<CardHeader>
<CardTitle className="dark:text-white">
Ajukan Pengaduan
</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmitComplaint} className="space-y-4">
<div>
<label
htmlFor="title"
className="block text-sm font-medium mb-1 dark:text-gray-300"
>
Judul Pengaduan
</label>
<Input
id="title"
name="title"
value={newComplaint.title}
onChange={handleInputChange}
placeholder="Masukkan judul pengaduan"
className="dark:bg-gray-700 dark:border-gray-600 dark:text-white"
required
/>
</div>
<div>
<label
htmlFor="category"
className="block text-sm font-medium mb-1 dark:text-gray-300"
>
Kategori
</label>
<Select
id="category"
name="category"
value={newComplaint.category}
onChange={handleSelectChange}
placeholder="Pilih kategori"
data={[
{ value: "infrastruktur", label: "Infrastruktur" },
{ value: "administrasi", label: "Administrasi" },
{ value: "utilitas", label: "Utilitas" },
{ value: "sanitasi", label: "Sanitasi" },
{ value: "kesehatan", label: "Kesehatan" },
{ value: "pendidikan", label: "Pendidikan" },
]}
className="dark:bg-gray-700 dark:border-gray-600 dark:text-white"
clearable
/>
</div>
<div>
<label
htmlFor="description"
className="block text-sm font-medium mb-1 dark:text-gray-300"
>
Deskripsi
</label>
<Textarea
id="description"
name="description"
value={newComplaint.description}
onChange={handleInputChange}
placeholder="Jelaskan pengaduan Anda secara detail..."
rows={4}
className="dark:bg-gray-700 dark:border-gray-600 dark:text-white"
required
/>
</div>
<Button
type="submit"
className="w-full dark:bg-blue-600 dark:hover:bg-blue-700"
<>
{/* Summary Cards */}
<Grid gutter="md">
<GridCol span={{ base: 12, md: 6, lg: 3 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
Total Pengaduan
</Text>
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
{summaryData.total}
</Text>
</Stack>
<Badge
variant="light"
color="darmasaba-blue"
p={8}
radius="md"
>
Kirim Pengaduan
</Button>
</form>
</CardContent>
</Card>
</div>
<IconMessage size={20} />
</Badge>
</Group>
</Card>
</GridCol>
{/* Complaints List */}
<div className="lg:col-span-2">
<Card className="dark:bg-gray-800 dark:border-gray-700">
<CardHeader>
<CardTitle className="dark:text-white">
Daftar Pengaduan
</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead className="dark:text-gray-300">
Judul
</TableHead>
<TableHead className="dark:text-gray-300">
Kategori
</TableHead>
<TableHead className="dark:text-gray-300">
Status
</TableHead>
<TableHead className="dark:text-gray-300">
Prioritas
</TableHead>
<TableHead className="dark:text-gray-300">
Tanggal
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{complaints.map((complaint) => (
<TableRow key={complaint.id}>
<TableCell className="font-medium dark:text-white">
{complaint.title}
</TableCell>
<TableCell className="dark:text-gray-300">
{complaint.category}
</TableCell>
<TableCell>
<Badge
variant={
complaint.status === "Resolved"
? "success"
: complaint.status === "In Progress"
? "secondary"
: "destructive"
}
>
{complaint.status}
</Badge>
</TableCell>
<TableCell>
<Badge
variant={
complaint.priority === "High"
? "destructive"
: complaint.priority === "Medium"
? "secondary"
: "default"
}
>
{complaint.priority}
</Badge>
</TableCell>
<TableCell className="dark:text-gray-300">
{complaint.date}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
</div>
</div>
<GridCol span={{ base: 12, md: 6, lg: 3 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
Baru
</Text>
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
{summaryData.baru}
</Text>
</Stack>
<Badge
variant="light"
color="red"
p={8}
radius="md"
>
<IconAlertTriangle size={20} />
</Badge>
</Group>
</Card>
</GridCol>
<GridCol span={{ base: 12, md: 6, lg: 3 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
Diproses
</Text>
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
{summaryData.diproses}
</Text>
</Stack>
<Badge
variant="light"
color="yellow"
p={8}
radius="md"
>
<IconClock size={20} />
</Badge>
</Group>
</Card>
</GridCol>
<GridCol span={{ base: 12, md: 6, lg: 3 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
Selesai
</Text>
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
{summaryData.selesai}
</Text>
</Stack>
<Badge
variant="light"
color="green"
p={8}
radius="md"
>
<IconCheck size={20} />
</Badge>
</Group>
</Card>
</GridCol>
</Grid>
{/* Grafik Tren Pengaduan */}
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} >
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
Grafik Tren Pengaduan
</Title>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={trenData}>
<CartesianGrid
strokeDasharray="3 3"
vertical={false}
stroke={dark ? "var(--mantine-color-gray-7)" : "var(--mantine-color-gray-3)"}
/>
<XAxis
dataKey="bulan"
axisLine={false}
tickLine={false}
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
/>
<Tooltip
contentStyle={dark
? { backgroundColor: 'var(--mantine-color-dark-7)', borderColor: 'var(--mantine-color-dark-6)' }
: {}}
/>
<Line
type="monotone"
dataKey="jumlah"
stroke={dark ? "var(--mantine-color-blue-6)" : "var(--mantine-color-blue-filled)"}
strokeWidth={2}
dot={{ stroke: dark ? "var(--mantine-color-blue-6)" : "var(--mantine-color-blue-filled)", strokeWidth: 2, r: 4 }}
activeDot={{ r: 6, stroke: '#fff', strokeWidth: 2 }}
/>
</LineChart>
</ResponsiveContainer>
</Card>
{/* Surat Terbanyak & Pengajuan Terbaru & Ide Inovatif */}
<Grid gutter="md">
{/* Surat Terbanyak */}
<GridCol span={{ base: 12, lg: 4 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
Surat Terbanyak
</Title>
<ResponsiveContainer width="100%" height={250}>
<BarChart data={suratData} layout="horizontal">
<CartesianGrid
strokeDasharray="3 3"
horizontal={false}
stroke={dark ? "var(--mantine-color-gray-7)" : "var(--mantine-color-gray-3)"}
/>
<XAxis
dataKey="jumlah"
axisLine={false}
tickLine={false}
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
/>
<YAxis
dataKey="jenis"
type="category"
axisLine={false}
tickLine={false}
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
width={80}
/>
<Tooltip
contentStyle={dark
? { backgroundColor: 'var(--mantine-color-dark-7)', borderColor: 'var(--mantine-color-dark-6)' }
: {}}
/>
<Bar
dataKey="jumlah"
fill={dark ? "var(--mantine-color-blue-6)" : "var(--mantine-color-blue-filled)"}
radius={[0, 4, 4, 0]}
/>
</BarChart>
</ResponsiveContainer>
</Card>
</GridCol>
{/* Pengajuan Terbaru */}
<GridCol span={{ base: 12, lg: 4 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
Pengajuan Terbaru
</Title>
{pengajuanTerbaru.map((item, index) => (
<Box key={index}>
<Group justify="space-between">
<Stack gap={0}>
<Text fw={500} c={dark ? "dark.0" : "black"}>{item.nama}</Text>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{item.jenis}</Text>
</Stack>
<Stack gap={0} align="flex-end">
<Badge color={getStatusColor(item.status)} variant="light">
{item.status}
</Badge>
<Text size="xs" c={dark ? "dark.4" : "dimmed"}>{item.waktu}</Text>
</Stack>
</Group>
<Divider my="sm" />
</Box>
))}
</Card>
</GridCol>
{/* Ajuan Ide Inovatif */}
<GridCol span={{ base: 12, lg: 4 }}>
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
Ajuan Ide Inovatif
</Title>
{ideInovatif.map((item, index) => (
<Box key={index}>
<Group justify="space-between">
<Stack gap={0}>
<Text fw={500} c={dark ? "dark.0" : "black"}>{item.judul}</Text>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{item.nama}</Text>
</Stack>
<Group>
<Badge color="blue" variant="light">
{item.kategori}
</Badge>
<ActionIcon variant="subtle" color="darmasaba-blue">
<IconChevronRight size={16} />
</ActionIcon>
</Group>
</Group>
<Divider my="sm" />
</Box>
))}
</Card>
</GridCol>
</Grid>
{/* Complaint Submission Form and List */}
<Grid gutter="md">
{/* Complaint Submission Form */}
<GridCol span={{ base: 12, lg: 4 }}>
<Card p="md" withBorder radius="md" h="100%" bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
<Card.Section withBorder inheritPadding py="xs">
<Title order={3} py="xs">Ajukan Pengaduan</Title>
</Card.Section>
<Card.Section>
<form onSubmit={handleSubmitComplaint}>
<Stack gap="md" p={"sm"}>
<TextInput
label="Judul Pengaduan"
id="title"
name="title"
value={newComplaint.title}
onChange={handleInputChange}
placeholder="Masukkan judul pengaduan"
required
withAsterisk
/>
<Select
label="Kategori"
id="category"
name="category"
value={newComplaint.category}
onChange={handleSelectChange}
placeholder="Pilih kategori"
data={[
{ value: "infrastruktur", label: "Infrastruktur" },
{ value: "administrasi", label: "Administrasi" },
{ value: "utilitas", label: "Utilitas" },
{ value: "sanitasi", label: "Sanitasi" },
{ value: "kesehatan", label: "Kesehatan" },
{ value: "pendidikan", label: "Pendidikan" },
]}
clearable
/>
<Textarea
label="Deskripsi"
id="description"
name="description"
value={newComplaint.description}
onChange={handleInputChange}
placeholder="Jelaskan pengaduan Anda secara detail..."
minRows={4}
required
withAsterisk
/>
<Button type="submit" mt="md" color="darmasaba-blue">
Kirim Pengaduan
</Button>
</Stack>
</form>
</Card.Section>
</Card>
</GridCol>
{/* Complaints List */}
<GridCol span={{ base: 12, lg: 8 }}>
<Card withBorder radius="md" bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
<Card.Section withBorder inheritPadding py="xs">
<Title order={3} py="xs">Daftar Pengaduan</Title>
</Card.Section>
<Card.Section py="md" px="xs">
<Table withColumnBorders>
<Table.Thead>
<Table.Tr>
<Table.Th><Text c={dark ? "white" : "dark.3" }>Judul</Text></Table.Th>
<Table.Th><Text c={dark ? "white" : "dark.3" }>Kategori</Text></Table.Th>
<Table.Th><Text c={dark ? "white" : "dark.3" }>Status</Text></Table.Th>
<Table.Th><Text c={dark ? "white" : "dark.3" }>Prioritas</Text></Table.Th>
<Table.Th><Text c={dark ? "white" : "dark.3" }>Tanggal</Text></Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{complaintRows}
</Table.Tbody>
</Table>
</Card.Section>
</Card>
</GridCol>
</Grid>
</>
) : (
<div>
<Card className="dark:bg-gray-800 dark:border-gray-700">
<CardHeader>
<CardTitle className="dark:text-white">
Layanan Publik Tersedia
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Stack gap="lg">
<Card withBorder radius="md">
<Card.Section withBorder inheritPadding py="xs">
<Title order={3} py="xs">Layanan Publik Tersedia</Title>
</Card.Section>
<Card.Section pt="md">
<Grid gutter="md">
{services.map((service) => (
<Card
key={service.id}
className="dark:bg-gray-700 dark:border-gray-600"
>
<CardHeader>
<CardTitle className="text-lg dark:text-white">
{service.name}
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-gray-600 dark:text-gray-300 mb-3">
<GridCol key={service.id} span={{ base: 12, md: 6, lg: 4 }}>
<Card withBorder radius="md" h="100%">
<Title order={4} mb="sm">{service.name}</Title>
<Text size="sm" c={dark ? "white" : "dark.3" } mb="md">
{service.description}
</p>
<div className="flex justify-between items-center">
</Text>
<Group justify="space-between">
<Badge
variant={
variant="filled"
color={
service.status === "Available"
? "success"
? "green"
: service.status === "Limited"
? "secondary"
: "destructive"
? "yellow"
: "red"
}
>
{service.status}
</Badge>
<span className="text-xs text-gray-500 dark:text-gray-400">
<Text size="sm" c={dark ? "white" : "dark.3" }>
{service.category}
</span>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-2">
</Text>
</Group>
<Text size="xs" c={dark ? "white" : "dark.3" } mt="sm">
Terakhir diperbarui: {service.lastUpdated}
</p>
</CardContent>
</Card>
</Text>
</Card>
</GridCol>
))}
</div>
</CardContent>
</Grid>
</Card.Section>
</Card>
<Card className="mt-6 dark:bg-gray-800 dark:border-gray-700">
<CardHeader>
<CardTitle className="dark:text-white">
Statistik Layanan
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg">
<h3 className="text-lg font-semibold dark:text-white">
Jumlah Layanan Tersedia
</h3>
<p className="text-3xl font-bold text-blue-600 dark:text-blue-400">
12
</p>
</div>
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg">
<h3 className="text-lg font-semibold dark:text-white">
Layanan Terpopuler
</h3>
<p className="text-3xl font-bold text-green-600 dark:text-green-400">
4
</p>
</div>
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg">
<h3 className="text-lg font-semibold dark:text-white">
Permintaan Baru
</h3>
<p className="text-3xl font-bold text-purple-600 dark:text-purple-400">
23
</p>
</div>
</div>
</CardContent>
<Card withBorder radius="md">
<Card.Section withBorder inheritPadding py="xs">
<Title order={3} py="xs">Statistik Layanan</Title>
</Card.Section>
<Card.Section pt="md">
<Grid gutter="md">
<GridCol span={{ base: 12, md: 4 }}>
<Card p="md" bg={dark ? "dark.7" : "gray.0"} radius="md">
<Title order={4} mb="xs">Jumlah Layanan Tersedia</Title>
<Text size="xl" fw={700} c="darmasaba-blue">
12
</Text>
</Card>
</GridCol>
<GridCol span={{ base: 12, md: 4 }}>
<Card p="md" bg={dark ? "dark.7" : "gray.0"} radius="md">
<Title order={4} mb="xs">Layanan Terpopuler</Title>
<Text size="xl" fw={700} c="darmasaba-success">
4
</Text>
</Card>
</GridCol>
<GridCol span={{ base: 12, md: 4 }}>
<Card p="md" bg={dark ? "dark.7" : "gray.0"} radius="md">
<Title order={4} mb="xs">Permintaan Baru</Title>
<Text size="xl" fw={700} c="darmasaba-warning">
23
</Text>
</Card>
</GridCol>
</Grid>
</Card.Section>
</Card>
</div>
</Stack>
)}
</div>
</Stack>
);
};
export default PengaduanLayananPublik;
export default PengaduanLayananPublik;

View File

@@ -8,6 +8,7 @@ import {
Input,
NavLink as MantineNavLink,
Box,
useMantineColorScheme,
} from "@mantine/core";
interface SidebarProps {
@@ -17,6 +18,9 @@ interface SidebarProps {
export function Sidebar({ className }: SidebarProps) {
const location = useLocation();
const navigate = useNavigate();
const { colorScheme } = useMantineColorScheme();
const isActiveBg = colorScheme === 'dark' ? "#182949" : "#E6F0FF";
const isActiveBorder = colorScheme === 'dark' ? "#00398D" : "#1F41AE";
// Define menu items with their paths
const menuItems = [
@@ -75,16 +79,34 @@ export function Sidebar({ className }: SidebarProps) {
{/* Menu Items */}
<Stack gap={0} px="xs" flex={1} style={{ overflowY: "auto" }}>
{menuItems.map((item, index) => (
<MantineNavLink
key={index}
onClick={() => navigate({ to: item.path })}
label={item.name}
active={location.pathname === item.path}
variant="subtle"
color="blue"
/>
))}
{menuItems.map((item, index) => {
const isActive = location.pathname === item.path;
return (
<MantineNavLink
key={index}
onClick={() => navigate({ to: item.path })}
label={item.name}
active={isActive}
variant="subtle"
color="blue"
style={{
background: isActive ? isActiveBg : "transparent",
fontWeight: isActive ? "bold" : "normal",
borderLeft: isActive ? `4px solid ${isActiveBorder}` : "4px solid transparent",
borderRadius: "8px",
transition: "all 200ms ease",
margin: "2px 0",
}}
styles={{
body: {
"&:hover": {
background: "#F1F5F9",
}
}
}}
/>
);
})}
</Stack>
</Box>
);

View File

@@ -0,0 +1,296 @@
import { useState } from "react";
import {
Card,
Grid,
GridCol,
Group,
Text,
Title,
Progress,
Stack,
useMantineColorScheme,
Badge,
List,
ThemeIcon
} from "@mantine/core";
import {
IconHeartbeat,
IconBabyCarriage,
IconStethoscope,
IconMedicalCross,
IconSchool,
IconBook,
IconCalendarEvent,
IconAward
} from "@tabler/icons-react";
const SosialPage = () => {
const { colorScheme } = useMantineColorScheme();
const dark = colorScheme === 'dark';
// Sample data for health statistics
const healthStats = {
ibuHamil: 87,
balita: 342,
alertStunting: 12,
posyanduAktif: 8,
};
// Sample data for health progress
const healthProgress = [
{ label: "Imunisasi Lengkap", value: 92, color: "green" },
{ label: "Pemeriksaan Rutin", value: 88, color: "blue" },
{ label: "Gizi Baik", value: 86, color: "teal" },
{ label: "Target Stunting", value: 14, color: "red" },
];
// Sample data for posyandu schedule
const posyanduSchedule = [
{ nama: "Posyandu Mawar", tanggal: "Senin, 15 Feb 2026", jam: "08:00 - 11:00" },
{ nama: "Posyandu Melati", tanggal: "Selasa, 16 Feb 2026", jam: "08:00 - 11:00" },
{ nama: "Posyandu Dahlia", tanggal: "Rabu, 17 Feb 2026", jam: "08:00 - 11:00" },
{ nama: "Posyandu Anggrek", tanggal: "Kamis, 18 Feb 2026", jam: "08:00 - 11:00" },
];
// Sample data for education stats
const educationStats = {
siswa: {
tk: 125,
sd: 480,
smp: 210,
sma: 150,
},
sekolah: {
jumlah: 8,
guru: 42,
}
};
// Sample data for scholarships
const scholarshipData = {
penerima: 45,
dana: "Rp 1.200.000.000",
tahunAjaran: "2025/2026",
};
// Sample data for cultural events
const culturalEvents = [
{ nama: "Hari Kesaktian Pancasila", tanggal: "1 Oktober 2025", lokasi: "Balai Desa" },
{ nama: "Festival Budaya Desa", tanggal: "20 Mei 2026", lokasi: "Lapangan Desa" },
{ nama: "Perayaan HUT Desa", tanggal: "17 Agustus 2026", lokasi: "Balai Desa" },
];
return (
<Stack gap="lg">
{/* Page Header */}
<Group justify="space-between" align="center">
<Title order={2} c={dark ? "dark.0" : "black"}>
Sosial Desa
</Title>
</Group>
{/* Health Statistics Cards */}
<Grid gutter="md">
<GridCol span={{ base: 12, sm: 6, md: 3 }}>
<Card withBorder radius="md" padding="lg">
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
Ibu Hamil Aktif
</Text>
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
{healthStats.ibuHamil}
</Text>
</Stack>
<ThemeIcon variant="light" color="darmasaba-blue" size="xl" radius="xl">
<IconHeartbeat size={24} />
</ThemeIcon>
</Group>
</Card>
</GridCol>
<GridCol span={{ base: 12, sm: 6, md: 3 }}>
<Card withBorder radius="md" padding="lg">
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
Balita Terdaftar
</Text>
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
{healthStats.balita}
</Text>
</Stack>
<ThemeIcon variant="light" color="darmasaba-success" size="xl" radius="xl">
<IconBabyCarriage size={24} />
</ThemeIcon>
</Group>
</Card>
</GridCol>
<GridCol span={{ base: 12, sm: 6, md: 3 }}>
<Card withBorder radius="md" padding="lg">
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
Alert Stunting
</Text>
<Text size="xl" fw={700} c="red">
{healthStats.alertStunting}
</Text>
</Stack>
<ThemeIcon variant="light" color="red" size="xl" radius="xl">
<IconStethoscope size={24} />
</ThemeIcon>
</Group>
</Card>
</GridCol>
<GridCol span={{ base: 12, sm: 6, md: 3 }}>
<Card withBorder radius="md" padding="lg">
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
Posyandu Aktif
</Text>
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
{healthStats.posyanduAktif}
</Text>
</Stack>
<ThemeIcon variant="light" color="darmasaba-warning" size="xl" radius="xl">
<IconMedicalCross size={24} />
</ThemeIcon>
</Group>
</Card>
</GridCol>
</Grid>
{/* Health Progress Bars */}
<Card withBorder radius="md" p="lg">
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Statistik Kesehatan</Title>
<Stack gap="md">
{healthProgress.map((item, index) => (
<div key={index}>
<Group justify="space-between" mb={5}>
<Text size="sm" fw={500} c={dark ? "dark.0" : "black"}>
{item.label}
</Text>
<Text size="sm" fw={600} c={dark ? "dark.0" : "black"}>
{item.value}%
</Text>
</Group>
<Progress
value={item.value}
size="lg"
radius="xl"
color={item.color}
/>
</div>
))}
</Stack>
</Card>
<Grid gutter="md">
{/* Jadwal Posyandu */}
<GridCol span={{ base: 12, lg: 6 }}>
<Card withBorder radius="md" p="lg">
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Jadwal Posyandu</Title>
<Stack gap="sm">
{posyanduSchedule.map((item, index) => (
<Card key={index} withBorder radius="md" p="md">
<Group justify="space-between">
<Stack gap={0}>
<Text fw={500} c={dark ? "dark.0" : "black"}>{item.nama}</Text>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{item.tanggal}</Text>
</Stack>
<Badge variant="light" color="darmasaba-blue">
{item.jam}
</Badge>
</Group>
</Card>
))}
</Stack>
</Card>
</GridCol>
{/* Pendidikan */}
<GridCol span={{ base: 12, lg: 6 }}>
<Card withBorder radius="md" p="lg">
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Pendidikan</Title>
<Stack gap="md">
<Group justify="space-between">
<Text fw={500} c={dark ? "dark.0" : "black"}>TK / PAUD</Text>
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.siswa.tk}</Text>
</Group>
<Group justify="space-between">
<Text fw={500} c={dark ? "dark.0" : "black"}>SD</Text>
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.siswa.sd}</Text>
</Group>
<Group justify="space-between">
<Text fw={500} c={dark ? "dark.0" : "black"}>SMP</Text>
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.siswa.smp}</Text>
</Group>
<Group justify="space-between">
<Text fw={500} c={dark ? "dark.0" : "black"}>SMA</Text>
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.siswa.sma}</Text>
</Group>
<Card withBorder radius="md" p="md" mt="md">
<Group justify="space-between">
<Text fw={500} c={dark ? "dark.0" : "black"}>Jumlah Lembaga Pendidikan</Text>
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.sekolah.jumlah}</Text>
</Group>
<Group justify="space-between" mt="sm">
<Text fw={500} c={dark ? "dark.0" : "black"}>Jumlah Tenaga Pengajar</Text>
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.sekolah.guru}</Text>
</Group>
</Card>
</Stack>
</Card>
</GridCol>
</Grid>
<Grid gutter="md">
{/* Beasiswa Desa */}
<GridCol span={{ base: 12, lg: 6 }}>
<Card withBorder radius="md" p="lg" bg={dark ? "dark.8" : "darmasaba-blue.0"}>
<Group justify="space-between" align="center">
<Stack gap={0}>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>Beasiswa Desa</Text>
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>Penerima: {scholarshipData.penerima}</Text>
</Stack>
<ThemeIcon variant="light" color="darmasaba-success" size="xl" radius="xl">
<IconAward size={24} />
</ThemeIcon>
</Group>
<Text mt="md" c={dark ? "dark.0" : "black"}>Dana Tersalurkan: <Text span fw={700}>{scholarshipData.dana}</Text></Text>
<Text mt="sm" c={dark ? "dark.3" : "dimmed"}>Tahun Ajaran: {scholarshipData.tahunAjaran}</Text>
</Card>
</GridCol>
{/* Kalender Event Budaya */}
<GridCol span={{ base: 12, lg: 6 }}>
<Card withBorder radius="md" p="lg">
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Kalender Event Budaya</Title>
<List spacing="sm">
{culturalEvents.map((event, index) => (
<List.Item key={index} icon={
<ThemeIcon color="darmasaba-blue" size={24} radius="xl">
<IconCalendarEvent size={12} />
</ThemeIcon>
}>
<Group justify="space-between">
<Text fw={500} c={dark ? "dark.0" : "black"}>{event.nama}</Text>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{event.lokasi}</Text>
</Group>
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{event.tanggal}</Text>
</List.Item>
))}
</List>
</Card>
</GridCol>
</Grid>
</Stack>
);
};
export default SosialPage;