102 lines
2.5 KiB
TypeScript
102 lines
2.5 KiB
TypeScript
import apiFetch from "@/lib/apiFetch";
|
|
import { Card, Flex, Grid, Group, Stack, Text } from "@mantine/core";
|
|
import { useShallowEffect } from "@mantine/hooks";
|
|
import {
|
|
IconFileCertificate,
|
|
IconMessageReport,
|
|
IconUsers,
|
|
} from "@tabler/icons-react";
|
|
import useSWR from "swr";
|
|
|
|
export default function DashboardCountData() {
|
|
const { data, mutate, isLoading } = useSWR("/", () =>
|
|
apiFetch.api.dashboard.count.get(),
|
|
);
|
|
|
|
useShallowEffect(() => {
|
|
mutate();
|
|
}, []);
|
|
|
|
return (
|
|
<Grid>
|
|
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
|
<MetricCard
|
|
icon={<IconMessageReport size={28} />}
|
|
label="Pengaduan Hari Ini"
|
|
value={String(data?.data?.pengaduan?.today)}
|
|
change={String(data?.data?.pengaduan?.kenaikan) + "%"}
|
|
color={"gray"}
|
|
/>
|
|
</Grid.Col>
|
|
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
|
<MetricCard
|
|
icon={<IconFileCertificate size={28} />}
|
|
label="Pengajuan Surat Hari Ini"
|
|
value={String(data?.data?.pelayanan?.today)}
|
|
change={String(data?.data?.pelayanan?.kenaikan) + "%"}
|
|
color="gray"
|
|
/>
|
|
</Grid.Col>
|
|
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
|
<MetricCard
|
|
icon={<IconUsers size={28} />}
|
|
label="Warga"
|
|
value={String(data?.data?.warga)}
|
|
color="blue"
|
|
/>
|
|
</Grid.Col>
|
|
</Grid>
|
|
);
|
|
}
|
|
|
|
function MetricCard({
|
|
icon,
|
|
label,
|
|
value,
|
|
change,
|
|
color,
|
|
}: {
|
|
icon: React.ReactNode;
|
|
label: string;
|
|
value: string;
|
|
change?: string;
|
|
color: string;
|
|
}) {
|
|
return (
|
|
<Card
|
|
radius="lg"
|
|
p="md"
|
|
withBorder
|
|
style={{
|
|
background:
|
|
"linear-gradient(145deg, rgba(30,30,30,0.95), rgba(55,55,55,0.9))",
|
|
borderColor: "rgba(100,100,100,0.2)",
|
|
transition: "transform 0.15s ease, box-shadow 0.15s ease",
|
|
}}
|
|
onMouseEnter={(e) =>
|
|
(e.currentTarget.style.boxShadow = "0 0 10px rgba(0,255,200,0.2)")
|
|
}
|
|
onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "none")}
|
|
>
|
|
<Stack gap={6}>
|
|
<Group gap={6}>
|
|
{icon}
|
|
<Text size="sm" c="dimmed">
|
|
{label}
|
|
</Text>
|
|
</Group>
|
|
<Flex align="center" justify="space-between">
|
|
<Text fw={600} size="xl" c="gray.0">
|
|
{value}
|
|
</Text>
|
|
{change && (
|
|
<Text size="sm" c={color}>
|
|
{change}
|
|
</Text>
|
|
)}
|
|
</Flex>
|
|
</Stack>
|
|
</Card>
|
|
);
|
|
}
|