63 lines
1.7 KiB
TypeScript
63 lines
1.7 KiB
TypeScript
|
|
import type { ReactNode } from "react";
|
|||
|
|
|
|||
|
|
const TONES = {
|
|||
|
|
teal: "bg-primary/5",
|
|||
|
|
rose: "bg-destructive/5",
|
|||
|
|
amber: "bg-amber-500/5",
|
|||
|
|
green: "bg-emerald-500/5",
|
|||
|
|
violet: "bg-violet-500/5",
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const ICON_TONES = {
|
|||
|
|
teal: "text-primary",
|
|||
|
|
rose: "text-destructive",
|
|||
|
|
amber: "text-amber-700 dark:text-amber-400",
|
|||
|
|
green: "text-emerald-700 dark:text-emerald-400",
|
|||
|
|
violet: "text-violet-700 dark:text-violet-400",
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export function StatisticCard({
|
|||
|
|
icon,
|
|||
|
|
label,
|
|||
|
|
value,
|
|||
|
|
tone = "teal",
|
|||
|
|
onClick,
|
|||
|
|
status,
|
|||
|
|
}: {
|
|||
|
|
icon: ReactNode;
|
|||
|
|
label: string;
|
|||
|
|
value?: number | undefined;
|
|||
|
|
tone?: "teal" | "rose" | "amber" | "green" | "violet";
|
|||
|
|
onClick: () => void;
|
|||
|
|
status?: string | undefined;
|
|||
|
|
}) {
|
|||
|
|
return (
|
|||
|
|
<button
|
|||
|
|
className={`flex min-h-28 items-start gap-3 rounded-xl border border-border p-4 text-left shadow-sm transition-colors hover:border-primary focus-visible:outline-2 focus-visible:outline-ring ${TONES[tone]}`}
|
|||
|
|
onClick={onClick}
|
|||
|
|
type="button"
|
|||
|
|
>
|
|||
|
|
<span
|
|||
|
|
aria-hidden="true"
|
|||
|
|
className={`flex size-10 shrink-0 items-center justify-center rounded-full bg-background/60 [&_svg]:size-5 ${ICON_TONES[tone]}`}
|
|||
|
|
>
|
|||
|
|
{icon}
|
|||
|
|
</span>
|
|||
|
|
<span className="min-w-0">
|
|||
|
|
<span className="block text-xs font-medium text-foreground">{label}</span>
|
|||
|
|
<strong className="mt-2 block text-3xl font-semibold text-foreground">
|
|||
|
|
{value ?? "—"}
|
|||
|
|
</strong>
|
|||
|
|
{status && (
|
|||
|
|
<small className="block mt-1 text-muted-foreground" role="status">
|
|||
|
|
{status}
|
|||
|
|
</small>
|
|||
|
|
)}
|
|||
|
|
</span>
|
|||
|
|
<span aria-hidden="true" className="ml-auto self-center text-lg text-primary">
|
|||
|
|
›
|
|||
|
|
</span>
|
|||
|
|
</button>
|
|||
|
|
);
|
|||
|
|
}
|