Refactor New Ui Sosial, Keamanan, & Bantuan
This commit is contained in:
@@ -25,7 +25,7 @@ export function StatCard({
|
|||||||
trend,
|
trend,
|
||||||
trendValue,
|
trendValue,
|
||||||
icon,
|
icon,
|
||||||
iconColor = "darmasaba-blue",
|
iconColor = "#1E3A5F",
|
||||||
}: StatCardProps) {
|
}: StatCardProps) {
|
||||||
const { colorScheme } = useMantineColorScheme();
|
const { colorScheme } = useMantineColorScheme();
|
||||||
const dark = colorScheme === "dark";
|
const dark = colorScheme === "dark";
|
||||||
@@ -77,6 +77,7 @@ export function StatCard({
|
|||||||
size="xl"
|
size="xl"
|
||||||
radius="xl"
|
radius="xl"
|
||||||
color={dark ? "gray" : iconColor}
|
color={dark ? "gray" : iconColor}
|
||||||
|
bg={dark ? "gray" : iconColor}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ const HelpPage = () => {
|
|||||||
<HelpCard
|
<HelpCard
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||||
bg={dark ? "#141D34" : "white"}
|
bg={dark ? "#141D34" : "white"}
|
||||||
icon={<IconBook size={24} />}
|
icon={<IconBook size={24} color="white" />}
|
||||||
title="Panduan Memulai"
|
title="Panduan Memulai"
|
||||||
h="100%"
|
h="100%"
|
||||||
>
|
>
|
||||||
@@ -211,7 +211,7 @@ const HelpPage = () => {
|
|||||||
<HelpCard
|
<HelpCard
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||||
bg={dark ? "#141D34" : "white"}
|
bg={dark ? "#141D34" : "white"}
|
||||||
icon={<IconVideo size={24} />}
|
icon={<IconVideo size={24} color="white" />}
|
||||||
title="Video Tutorial"
|
title="Video Tutorial"
|
||||||
h="100%"
|
h="100%"
|
||||||
>
|
>
|
||||||
@@ -241,7 +241,7 @@ const HelpPage = () => {
|
|||||||
<HelpCard
|
<HelpCard
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||||
bg={dark ? "#141D34" : "white"}
|
bg={dark ? "#141D34" : "white"}
|
||||||
icon={<IconHelpCircle size={24} />}
|
icon={<IconHelpCircle size={24} color="white" />}
|
||||||
title="FAQ"
|
title="FAQ"
|
||||||
h="100%"
|
h="100%"
|
||||||
>
|
>
|
||||||
@@ -273,7 +273,7 @@ const HelpPage = () => {
|
|||||||
<HelpCard
|
<HelpCard
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||||
bg={dark ? "#141D34" : "white"}
|
bg={dark ? "#141D34" : "white"}
|
||||||
icon={<IconHeadphones size={24} />}
|
icon={<IconHeadphones size={24} color="white" />}
|
||||||
title="Hubungi Support"
|
title="Hubungi Support"
|
||||||
h="100%"
|
h="100%"
|
||||||
>
|
>
|
||||||
@@ -308,7 +308,7 @@ const HelpPage = () => {
|
|||||||
<HelpCard
|
<HelpCard
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||||
bg={dark ? "#141D34" : "white"}
|
bg={dark ? "#141D34" : "white"}
|
||||||
icon={<IconFileText size={24} />}
|
icon={<IconFileText size={24} color="white" />}
|
||||||
title="Dokumentasi"
|
title="Dokumentasi"
|
||||||
h="100%"
|
h="100%"
|
||||||
>
|
>
|
||||||
@@ -340,7 +340,7 @@ const HelpPage = () => {
|
|||||||
<HelpCard
|
<HelpCard
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||||
bg={dark ? "#141D34" : "white"}
|
bg={dark ? "#141D34" : "white"}
|
||||||
icon={<IconMessage size={24} />}
|
icon={<IconMessage size={24} color="white" />}
|
||||||
title="Jenna - Virtual Assistant"
|
title="Jenna - Virtual Assistant"
|
||||||
h="100%"
|
h="100%"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -125,10 +125,51 @@ const KeamananPage = () => {
|
|||||||
</Title>
|
</Title>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
{/* KPI Cards */}
|
|
||||||
|
|
||||||
<Grid gutter="md">
|
<Grid gutter="md">
|
||||||
{kpiData.map((kpi, index) => (
|
{/* Peta Keamanan CCTV */}
|
||||||
<GridCol key={index} span={{ base: 12, sm: 6, md: 6 }}>
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
{/* KPI Cards */}
|
||||||
|
<Grid gutter="md">
|
||||||
|
{kpiData.map((kpi, index) => (
|
||||||
|
<GridCol key={index} span={{ base: 12, sm: 6, md: 6 }}>
|
||||||
|
<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"}>
|
||||||
|
{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>
|
||||||
<Card
|
<Card
|
||||||
p="md"
|
p="md"
|
||||||
radius="md"
|
radius="md"
|
||||||
@@ -137,119 +178,81 @@ const KeamananPage = () => {
|
|||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||||
h="100%"
|
h="100%"
|
||||||
>
|
>
|
||||||
<Group justify="space-between" align="center">
|
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>
|
||||||
<Stack gap={0}>
|
Peta Keamanan CCTV
|
||||||
<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
|
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#141D34" : "white"}
|
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
h="100%"
|
|
||||||
>
|
|
||||||
<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>
|
</Title>
|
||||||
{cctvLocations.map((cctv, index) => (
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"} mb="md">
|
||||||
<Card
|
Titik Lokasi CCTV
|
||||||
key={index}
|
</Text>
|
||||||
p="md"
|
|
||||||
radius="md"
|
{/* Placeholder for map */}
|
||||||
withBorder
|
<Box
|
||||||
bg={dark ? "#263852ff" : "#F1F5F9"}
|
style={{
|
||||||
style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}
|
backgroundColor: dark ? "#2d3748" : "#e2e8f0",
|
||||||
>
|
borderRadius: "8px",
|
||||||
<Group justify="space-between">
|
height: "400px",
|
||||||
<Stack gap={0}>
|
display: "flex",
|
||||||
<Group gap="xs">
|
alignItems: "center",
|
||||||
<Text fw={500} c={dark ? "dark.0" : "black"}>
|
justifyContent: "center",
|
||||||
{cctv.id}
|
}}
|
||||||
|
>
|
||||||
|
<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}
|
||||||
|
p="md"
|
||||||
|
radius="md"
|
||||||
|
withBorder
|
||||||
|
bg={dark ? "#263852ff" : "#F1F5F9"}
|
||||||
|
style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}
|
||||||
|
>
|
||||||
|
<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>
|
</Text>
|
||||||
<Badge
|
|
||||||
variant="dot"
|
|
||||||
color={cctv.status === "active" ? "green" : "gray"}
|
|
||||||
>
|
|
||||||
{cctv.status === "active" ? "Online" : "Offline"}
|
|
||||||
</Badge>
|
|
||||||
</Group>
|
</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>
|
||||||
</Group>
|
</Card>
|
||||||
</Card>
|
))}
|
||||||
))}
|
</Stack>
|
||||||
</Stack>
|
</Card>
|
||||||
</Card>
|
</Stack>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
|
|
||||||
{/* Daftar Laporan Keamanan */}
|
{/* Daftar Laporan Keamanan */}
|
||||||
|
|||||||
@@ -1,463 +1,45 @@
|
|||||||
import {
|
import { Grid, GridCol, Stack } from "@mantine/core";
|
||||||
Badge,
|
import { SummaryCards } from "./sosial/summary-cards";
|
||||||
Card,
|
import { HealthStats } from "./sosial/health-stats";
|
||||||
Grid,
|
import { PosyanduSchedule } from "./sosial/posyandu-schedule";
|
||||||
GridCol,
|
import { Pendidikan } from "./sosial/pendidikan";
|
||||||
Group,
|
import { Beasiswa } from "./sosial/beasiswa";
|
||||||
List,
|
import { EventCalendar } from "./sosial/event-calendar";
|
||||||
Progress,
|
|
||||||
Stack,
|
|
||||||
Text,
|
|
||||||
ThemeIcon,
|
|
||||||
Title,
|
|
||||||
useMantineColorScheme,
|
|
||||||
} from "@mantine/core";
|
|
||||||
import {
|
|
||||||
IconAward,
|
|
||||||
IconBabyCarriage,
|
|
||||||
IconBook,
|
|
||||||
IconCalendarEvent,
|
|
||||||
IconHeartbeat,
|
|
||||||
IconMedicalCross,
|
|
||||||
IconSchool,
|
|
||||||
IconStethoscope,
|
|
||||||
} from "@tabler/icons-react";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const SosialPage = () => {
|
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 (
|
return (
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
{/* Health Statistics Cards */}
|
{/* Top Summary Cards - 4 Grid */}
|
||||||
|
<SummaryCards />
|
||||||
|
|
||||||
|
{/* Second Row - 2 Column Grid */}
|
||||||
<Grid gutter="md">
|
<Grid gutter="md">
|
||||||
<GridCol span={{ base: 12, sm: 6, md: 3 }}>
|
{/* Left - Statistik Kesehatan */}
|
||||||
<Card
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
p="md"
|
<HealthStats />
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#141D34" : "white"}
|
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
>
|
|
||||||
<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>
|
||||||
|
|
||||||
<GridCol span={{ base: 12, sm: 6, md: 3 }}>
|
{/* Right - Jadwal Posyandu */}
|
||||||
<Card
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
p="md"
|
<PosyanduSchedule />
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#141D34" : "white"}
|
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
>
|
|
||||||
<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
|
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#141D34" : "white"}
|
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
>
|
|
||||||
<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
|
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#141D34" : "white"}
|
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
>
|
|
||||||
<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>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* Health Progress Bars */}
|
{/* Third Row - 2 Column Grid */}
|
||||||
<Card
|
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#141D34" : "white"}
|
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
>
|
|
||||||
<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">
|
<Grid gutter="md">
|
||||||
{/* Jadwal Posyandu */}
|
{/* Left - Pendidikan */}
|
||||||
<GridCol span={{ base: 12, lg: 6 }}>
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
<Card
|
<Pendidikan />
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#141D34" : "white"}
|
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
>
|
|
||||||
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>
|
|
||||||
Jadwal Posyandu
|
|
||||||
</Title>
|
|
||||||
<Stack gap="sm">
|
|
||||||
{posyanduSchedule.map((item, index) => (
|
|
||||||
<Card
|
|
||||||
key={index}
|
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#263852ff" : "#F1F5F9"}
|
|
||||||
style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}
|
|
||||||
h="100%"
|
|
||||||
>
|
|
||||||
<Group justify="space-between">
|
|
||||||
<Stack gap={0}>
|
|
||||||
<Text fw={500} c={dark ? "dark.0" : "black"}>
|
|
||||||
{item.nama}
|
|
||||||
</Text>
|
|
||||||
<Text size="sm" c={dark ? "dark.0" : "black"}>
|
|
||||||
{item.tanggal}
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
<Badge variant="light" color="darmasaba-blue">
|
|
||||||
{item.jam}
|
|
||||||
</Badge>
|
|
||||||
</Group>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</Card>
|
|
||||||
</GridCol>
|
</GridCol>
|
||||||
|
|
||||||
{/* Pendidikan */}
|
{/* Right - Beasiswa Desa */}
|
||||||
<GridCol span={{ base: 12, lg: 6 }}>
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
<Card
|
<Beasiswa />
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#141D34" : "white"}
|
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
h="100%"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
bg={dark ? "#263852ff" : "#F1F5F9"}
|
|
||||||
style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}
|
|
||||||
>
|
|
||||||
<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>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid gutter="md">
|
{/* Bottom Section - Event Budaya */}
|
||||||
{/* Beasiswa Desa */}
|
<EventCalendar />
|
||||||
<GridCol span={{ base: 12, lg: 6 }}>
|
|
||||||
<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"}>
|
|
||||||
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
|
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
withBorder
|
|
||||||
bg={dark ? "#141D34" : "white"}
|
|
||||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
|
||||||
h="100%"
|
|
||||||
>
|
|
||||||
<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>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
69
src/components/sosial/beasiswa.tsx
Normal file
69
src/components/sosial/beasiswa.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { Card, Group, Stack, Text, ThemeIcon, Title } from "@mantine/core";
|
||||||
|
import { useMantineColorScheme } from "@mantine/core";
|
||||||
|
import { IconAward } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
interface ScholarshipData {
|
||||||
|
penerima: number;
|
||||||
|
dana: string;
|
||||||
|
tahunAjaran: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BeasiswaProps {
|
||||||
|
data?: ScholarshipData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Beasiswa = ({ data }: BeasiswaProps) => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === "dark";
|
||||||
|
|
||||||
|
const defaultData: ScholarshipData = {
|
||||||
|
penerima: 45,
|
||||||
|
dana: "Rp 1.200.000.000",
|
||||||
|
tahunAjaran: "2025/2026",
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayData = data || defaultData;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
p="md"
|
||||||
|
radius="xl"
|
||||||
|
withBorder
|
||||||
|
shadow="sm"
|
||||||
|
bg={dark ? "#141D34" : "white"}
|
||||||
|
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||||
|
h={"100%"}
|
||||||
|
>
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={2}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"} fw={500}>
|
||||||
|
Beasiswa Desa
|
||||||
|
</Text>
|
||||||
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
Penerima: {displayData.penerima}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<ThemeIcon
|
||||||
|
variant="light"
|
||||||
|
color="darmasaba-success"
|
||||||
|
size="xl"
|
||||||
|
radius="xl"
|
||||||
|
>
|
||||||
|
<IconAward size={24} />
|
||||||
|
</ThemeIcon>
|
||||||
|
</Group>
|
||||||
|
<Stack gap="xs" mt="md">
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text c={dark ? "dark.3" : "dimmed"}>Dana Tersalurkan:</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
{displayData.dana}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text c={dark ? "dark.3" : "dimmed"}>Tahun Ajaran:</Text>
|
||||||
|
<Text c={dark ? "dark.0" : "#1e3a5f"}>{displayData.tahunAjaran}</Text>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
94
src/components/sosial/event-calendar.tsx
Normal file
94
src/components/sosial/event-calendar.tsx
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import { Card, Group, Stack, Text, Title, ThemeIcon } from "@mantine/core";
|
||||||
|
import { useMantineColorScheme } from "@mantine/core";
|
||||||
|
import { IconCalendarEvent } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
interface EventItem {
|
||||||
|
id: string;
|
||||||
|
nama: string;
|
||||||
|
tanggal: string;
|
||||||
|
lokasi: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventCalendarProps {
|
||||||
|
data?: EventItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EventCalendar = ({ data }: EventCalendarProps) => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === "dark";
|
||||||
|
|
||||||
|
const defaultData: EventItem[] = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
nama: "Hari Kesaktian Pancasila",
|
||||||
|
tanggal: "1 Oktober 2025",
|
||||||
|
lokasi: "Balai Desa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
nama: "Festival Budaya Desa",
|
||||||
|
tanggal: "20 Mei 2026",
|
||||||
|
lokasi: "Lapangan Desa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
nama: "Perayaan HUT Desa",
|
||||||
|
tanggal: "17 Agustus 2026",
|
||||||
|
lokasi: "Balai Desa",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const displayData = data || defaultData;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
p="md"
|
||||||
|
radius="xl"
|
||||||
|
withBorder
|
||||||
|
shadow="sm"
|
||||||
|
bg={dark ? "#141D34" : "white"}
|
||||||
|
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||||
|
>
|
||||||
|
<Title order={3} mb="md" c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
Kalender Event Budaya
|
||||||
|
</Title>
|
||||||
|
<Stack gap="sm">
|
||||||
|
{displayData.map((event) => (
|
||||||
|
<Card
|
||||||
|
key={event.id}
|
||||||
|
p="md"
|
||||||
|
radius="md"
|
||||||
|
withBorder
|
||||||
|
bg={dark ? "#263852ff" : "#F1F5F9"}
|
||||||
|
style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}
|
||||||
|
hoverable
|
||||||
|
>
|
||||||
|
<Group justify="space-between" mb="xs">
|
||||||
|
<Group gap="sm" align="center">
|
||||||
|
<ThemeIcon
|
||||||
|
color="darmasaba-blue"
|
||||||
|
size="md"
|
||||||
|
radius="xl"
|
||||||
|
variant="light"
|
||||||
|
>
|
||||||
|
<IconCalendarEvent size={16} />
|
||||||
|
</ThemeIcon>
|
||||||
|
<Text fw={600} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
{event.nama}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"} fw={500}>
|
||||||
|
{event.lokasi}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Group pl={36}>
|
||||||
|
<Text size="sm" c={dark ? "dark.4" : "gray.6"}>
|
||||||
|
{event.tanggal}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
66
src/components/sosial/health-stats.tsx
Normal file
66
src/components/sosial/health-stats.tsx
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { Card, Group, Progress, Stack, Text, Title } from "@mantine/core";
|
||||||
|
import { useMantineColorScheme } from "@mantine/core";
|
||||||
|
|
||||||
|
interface HealthProgressItem {
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HealthStatsProps {
|
||||||
|
data?: HealthProgressItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HealthStats = ({ data }: HealthStatsProps) => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === "dark";
|
||||||
|
|
||||||
|
const defaultData: HealthProgressItem[] = [
|
||||||
|
{ 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" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const displayData = data || defaultData;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
p="md"
|
||||||
|
radius="xl"
|
||||||
|
withBorder
|
||||||
|
shadow="sm"
|
||||||
|
bg={dark ? "#141D34" : "white"}
|
||||||
|
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||||
|
h={'100%'}
|
||||||
|
>
|
||||||
|
<Title order={3} mb="md" c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
Statistik Kesehatan
|
||||||
|
</Title>
|
||||||
|
<Stack gap="md">
|
||||||
|
{displayData.map((item, index) => (
|
||||||
|
<div key={index}>
|
||||||
|
<Group justify="space-between" mb={5}>
|
||||||
|
<Text size="sm" fw={500} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
{item.label}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
size="sm"
|
||||||
|
fw={600}
|
||||||
|
c={item.color === "red" ? "red" : dark ? "dark.0" : "#1e3a5f"}
|
||||||
|
>
|
||||||
|
{item.value}%
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Progress
|
||||||
|
value={item.value}
|
||||||
|
size="lg"
|
||||||
|
radius="xl"
|
||||||
|
color={item.color}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
114
src/components/sosial/pendidikan.tsx
Normal file
114
src/components/sosial/pendidikan.tsx
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { Card, Group, Stack, Text, Title } from "@mantine/core";
|
||||||
|
import { useMantineColorScheme } from "@mantine/core";
|
||||||
|
|
||||||
|
interface EducationData {
|
||||||
|
siswa: {
|
||||||
|
tk: number;
|
||||||
|
sd: number;
|
||||||
|
smp: number;
|
||||||
|
sma: number;
|
||||||
|
};
|
||||||
|
sekolah: {
|
||||||
|
jumlah: number;
|
||||||
|
guru: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PendidikanProps {
|
||||||
|
data?: EducationData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Pendidikan = ({ data }: PendidikanProps) => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === "dark";
|
||||||
|
|
||||||
|
const defaultData: EducationData = {
|
||||||
|
siswa: {
|
||||||
|
tk: 125,
|
||||||
|
sd: 480,
|
||||||
|
smp: 210,
|
||||||
|
sma: 150,
|
||||||
|
},
|
||||||
|
sekolah: {
|
||||||
|
jumlah: 8,
|
||||||
|
guru: 42,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayData = data || defaultData;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
p="md"
|
||||||
|
radius="xl"
|
||||||
|
withBorder
|
||||||
|
shadow="sm"
|
||||||
|
bg={dark ? "#141D34" : "white"}
|
||||||
|
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||||
|
>
|
||||||
|
<Title order={3} mb="md" c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
Pendidikan
|
||||||
|
</Title>
|
||||||
|
<Stack gap="md">
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
TK / PAUD
|
||||||
|
</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
{displayData.siswa.tk}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
SD
|
||||||
|
</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
{displayData.siswa.sd}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
SMP
|
||||||
|
</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
{displayData.siswa.smp}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
SMA
|
||||||
|
</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
{displayData.siswa.sma}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
withBorder
|
||||||
|
radius="md"
|
||||||
|
p="md"
|
||||||
|
mt="md"
|
||||||
|
bg={dark ? "#263852ff" : "#F1F5F9"}
|
||||||
|
style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}
|
||||||
|
>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
Jumlah Lembaga Pendidikan
|
||||||
|
</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
{displayData.sekolah.jumlah}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between" mt="sm">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
Jumlah Tenaga Pengajar
|
||||||
|
</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
{displayData.sekolah.guru}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
89
src/components/sosial/posyandu-schedule.tsx
Normal file
89
src/components/sosial/posyandu-schedule.tsx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { Badge, Card, Group, Stack, Text, Title } from "@mantine/core";
|
||||||
|
import { useMantineColorScheme } from "@mantine/core";
|
||||||
|
|
||||||
|
interface PosyanduItem {
|
||||||
|
id: string;
|
||||||
|
nama: string;
|
||||||
|
tanggal: string;
|
||||||
|
jam: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PosyanduScheduleProps {
|
||||||
|
data?: PosyanduItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PosyanduSchedule = ({ data }: PosyanduScheduleProps) => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === "dark";
|
||||||
|
|
||||||
|
const defaultData: PosyanduItem[] = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
nama: "Posyandu Mawar",
|
||||||
|
tanggal: "Senin, 15 Feb 2026",
|
||||||
|
jam: "08:00 - 11:00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
nama: "Posyandu Melati",
|
||||||
|
tanggal: "Selasa, 16 Feb 2026",
|
||||||
|
jam: "08:00 - 11:00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
nama: "Posyandu Dahlia",
|
||||||
|
tanggal: "Rabu, 17 Feb 2026",
|
||||||
|
jam: "08:00 - 11:00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "4",
|
||||||
|
nama: "Posyandu Anggrek",
|
||||||
|
tanggal: "Kamis, 18 Feb 2026",
|
||||||
|
jam: "08:00 - 11:00",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const displayData = data || defaultData;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
p="md"
|
||||||
|
radius="xl"
|
||||||
|
withBorder
|
||||||
|
shadow="sm"
|
||||||
|
bg={dark ? "#141D34" : "white"}
|
||||||
|
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||||
|
>
|
||||||
|
<Title order={3} mb="md" c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
Jadwal Posyandu
|
||||||
|
</Title>
|
||||||
|
<Stack gap="sm">
|
||||||
|
{displayData.map((item) => (
|
||||||
|
<Card
|
||||||
|
key={item.id}
|
||||||
|
p="md"
|
||||||
|
radius="md"
|
||||||
|
withBorder
|
||||||
|
bg={dark ? "#263852ff" : "#F1F5F9"}
|
||||||
|
style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}
|
||||||
|
hoverable
|
||||||
|
>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text fw={600} c={dark ? "dark.0" : "#1e3a5f"}>
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
{item.tanggal}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<Badge variant="light" color="darmasaba-blue" size="md">
|
||||||
|
{item.jam}
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
132
src/components/sosial/summary-cards.tsx
Normal file
132
src/components/sosial/summary-cards.tsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import { Card, Grid, GridCol, Group, Stack, Text, ThemeIcon } from "@mantine/core";
|
||||||
|
import { useMantineColorScheme } from "@mantine/core";
|
||||||
|
import {
|
||||||
|
IconBabyCarriage,
|
||||||
|
IconHeartbeat,
|
||||||
|
IconMedicalCross,
|
||||||
|
IconStethoscope,
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
|
||||||
|
interface SummaryCardProps {
|
||||||
|
title: string;
|
||||||
|
value: number;
|
||||||
|
subtitle?: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
color: string;
|
||||||
|
highlight?: boolean;
|
||||||
|
backgroundColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SummaryCard = ({
|
||||||
|
title,
|
||||||
|
value,
|
||||||
|
subtitle,
|
||||||
|
icon,
|
||||||
|
color,
|
||||||
|
highlight = false,
|
||||||
|
backgroundColor,
|
||||||
|
}: SummaryCardProps) => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === "dark";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
p="md"
|
||||||
|
radius="xl"
|
||||||
|
withBorder
|
||||||
|
shadow="sm"
|
||||||
|
bg={dark ? "#141D34" : "white"}
|
||||||
|
style={{ borderColor: dark ? "#141D34" : "#e5e7eb" }}
|
||||||
|
>
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={2}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"} fw={500}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
size="xl"
|
||||||
|
fw={700}
|
||||||
|
c={highlight ? "red" : dark ? "dark.0" : "#1e3a5f"}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</Text>
|
||||||
|
{subtitle && (
|
||||||
|
<Text size="xs" c={dark ? "dark.4" : "gray.6"}>
|
||||||
|
{subtitle}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
<ThemeIcon bg={backgroundColor} color={color} size="xl" radius="xl">
|
||||||
|
{icon}
|
||||||
|
</ThemeIcon>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface HealthSummaryData {
|
||||||
|
ibuHamil: number;
|
||||||
|
balita: number;
|
||||||
|
alertStunting: number;
|
||||||
|
posyanduAktif: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SummaryCardsProps {
|
||||||
|
data?: HealthSummaryData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SummaryCards = ({ data }: SummaryCardsProps) => {
|
||||||
|
const defaultData: HealthSummaryData = {
|
||||||
|
ibuHamil: 87,
|
||||||
|
balita: 342,
|
||||||
|
alertStunting: 12,
|
||||||
|
posyanduAktif: 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayData = data || defaultData;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid gutter="md">
|
||||||
|
<GridCol span={{ base: 12, sm: 6, lg: 3 }}>
|
||||||
|
<SummaryCard
|
||||||
|
title="Ibu Hamil Aktif"
|
||||||
|
value={displayData.ibuHamil}
|
||||||
|
subtitle="Aktif"
|
||||||
|
icon={<IconHeartbeat size={20} />}
|
||||||
|
color= "white"
|
||||||
|
backgroundColor= "#1E3A5F"
|
||||||
|
/>
|
||||||
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 12, sm: 6, lg: 3 }}>
|
||||||
|
<SummaryCard
|
||||||
|
title="Balita Terdaftar"
|
||||||
|
value={displayData.balita}
|
||||||
|
subtitle="Terdaftar"
|
||||||
|
icon={<IconBabyCarriage size={20} />}
|
||||||
|
color= "white"
|
||||||
|
backgroundColor= "#1E3A5F"
|
||||||
|
/>
|
||||||
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 12, sm: 6, lg: 3 }}>
|
||||||
|
<SummaryCard
|
||||||
|
title="Alert Stunting"
|
||||||
|
value={displayData.alertStunting}
|
||||||
|
subtitle="Perhatian"
|
||||||
|
icon={<IconStethoscope size={20} />}
|
||||||
|
color= "white"
|
||||||
|
backgroundColor= "#1E3A5F"
|
||||||
|
/>
|
||||||
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 12, sm: 6, lg: 3 }}>
|
||||||
|
<SummaryCard
|
||||||
|
title="Posyandu Aktif"
|
||||||
|
value={displayData.posyanduAktif}
|
||||||
|
subtitle="Aktif"
|
||||||
|
icon={<IconMedicalCross size={20} />}
|
||||||
|
color= "white"
|
||||||
|
backgroundColor= "#1E3A5F"
|
||||||
|
/>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -52,8 +52,8 @@ export const HelpCard = ({
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: isDark
|
backgroundColor: isDark
|
||||||
? theme.colors.blue[8]
|
? "#263852ff"
|
||||||
: theme.colors.blue[0],
|
: "#1E3A5F",
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
padding: "8px",
|
padding: "8px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
|||||||
30
src/store/sosial.ts
Normal file
30
src/store/sosial.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { proxy } from "valtio";
|
||||||
|
|
||||||
|
type SelectedYear = string;
|
||||||
|
|
||||||
|
interface SosialState {
|
||||||
|
selectedYear: SelectedYear;
|
||||||
|
filters: {
|
||||||
|
dusun: string | null;
|
||||||
|
kategori: string | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sosialStore = proxy<SosialState>({
|
||||||
|
selectedYear: new Date().getFullYear().toString(),
|
||||||
|
filters: {
|
||||||
|
dusun: null,
|
||||||
|
kategori: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setYear = (year: SelectedYear) => {
|
||||||
|
sosialStore.selectedYear = year;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setFilter = (
|
||||||
|
key: keyof SosialState["filters"],
|
||||||
|
value: string | null,
|
||||||
|
) => {
|
||||||
|
sosialStore.filters[key] = value;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user