feat: initial commit with seeded database and premium ui changes
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
"use client";
|
||||
|
||||
import type * as React from "react";
|
||||
import { Bar, BarChart, XAxis } from "recharts";
|
||||
import {
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
type ChartConfig,
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from "@/components/ui/chart";
|
||||
import { Delta, DeltaIcon, DeltaValue } from "@/components/delta";
|
||||
import { DashboardCard } from "@/components/dashboard-card";
|
||||
import { formatDate } from "@/components/formater";
|
||||
import type { PovertyOverview } from "@/lib/poverty-types";
|
||||
|
||||
interface HouseholdsTrendChartProps {
|
||||
monthly: PovertyOverview["monthly"];
|
||||
}
|
||||
|
||||
const chartConfig = {
|
||||
count: {
|
||||
label: "KK Terdata",
|
||||
color: "var(--chart-2)",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
function CustomGradientBar(
|
||||
props: React.SVGProps<SVGRectElement> & {
|
||||
index?: number;
|
||||
dataKey?: string | number;
|
||||
}
|
||||
) {
|
||||
const {
|
||||
fill,
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 0,
|
||||
height = 0,
|
||||
dataKey = "count",
|
||||
index = 0,
|
||||
} = props;
|
||||
const gid = `gradient-bar-${String(dataKey)}-${index}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<rect
|
||||
fill={`url(#${gid})`}
|
||||
height={height}
|
||||
stroke="none"
|
||||
width={width}
|
||||
x={x}
|
||||
y={y}
|
||||
/>
|
||||
<rect fill={fill} height={2} stroke="none" width={width} x={x} y={y} />
|
||||
<defs>
|
||||
<linearGradient id={gid} x1="0" x2="0" y1="0" y2="1">
|
||||
<stop offset="0%" stopColor={fill} stopOpacity={0.5} />
|
||||
<stop offset="100%" stopColor={fill} stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function HouseholdsTrendChart({ monthly }: HouseholdsTrendChartProps) {
|
||||
const chartRows = monthly.map((m) => ({ month: m.month, count: m.count }));
|
||||
|
||||
const first = chartRows[0]?.count ?? 0;
|
||||
const last = chartRows.at(-1)?.count ?? 0;
|
||||
const growthPct = first > 0 ? ((last - first) / first) * 100 : 0;
|
||||
|
||||
return (
|
||||
<DashboardCard className="gap-0 md:col-span-2">
|
||||
<CardHeader className="gap-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<CardTitle>Pendataan Warga</CardTitle>
|
||||
{chartRows.length > 1 && (
|
||||
<Delta value={Number(growthPct.toFixed(1))} variant="badge">
|
||||
<DeltaIcon variant="trend" />
|
||||
<DeltaValue />
|
||||
</Delta>
|
||||
)}
|
||||
</div>
|
||||
<CardDescription>
|
||||
KK baru terdata per bulan, 12 bulan terakhir.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{chartRows.length === 0 ? (
|
||||
<div className="flex h-60 w-full items-center justify-center text-xs text-muted-foreground md:h-80">
|
||||
Belum ada data pendataan warga.
|
||||
</div>
|
||||
) : (
|
||||
<ChartContainer
|
||||
className="aspect-auto h-60 w-full md:h-80"
|
||||
config={chartConfig}
|
||||
>
|
||||
<BarChart accessibilityLayer data={chartRows}>
|
||||
<XAxis
|
||||
axisLine={false}
|
||||
dataKey="month"
|
||||
interval={0}
|
||||
tickFormatter={(value) => formatDate(`${value}-01`, "month")}
|
||||
tickLine={false}
|
||||
tickMargin={10}
|
||||
/>
|
||||
<ChartTooltip
|
||||
content={<ChartTooltipContent hideLabel />}
|
||||
cursor={false}
|
||||
/>
|
||||
<Bar
|
||||
dataKey="count"
|
||||
fill="var(--color-count)"
|
||||
shape={<CustomGradientBar />}
|
||||
/>
|
||||
</BarChart>
|
||||
</ChartContainer>
|
||||
)}
|
||||
</CardContent>
|
||||
</DashboardCard>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user