Refactor New Ui Sosial, Keamanan, & Bantuan

This commit is contained in:
2026-03-25 11:10:50 +08:00
parent 71a305cd4b
commit 113dd7ba6f
12 changed files with 743 additions and 563 deletions

View 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>
);
};

View 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>
);
};

View 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>
);
};

View 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>
);
};

View 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>
);
};

View 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>
);
};