52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
import type { ReactNode } from "react";
|
|
import { Info } from "lucide-react";
|
|
import { toneIcon, toneCard, type MetricToneKey } from "./tokens";
|
|
|
|
interface DashboardMetricCardProps {
|
|
title: string;
|
|
value: string;
|
|
secondary?: string;
|
|
infoText?: string;
|
|
icon: ReactNode;
|
|
tone: MetricToneKey;
|
|
}
|
|
|
|
/**
|
|
* Top-row metric card. Tone drives the icon container color only —
|
|
* card background and text always come from design tokens.
|
|
*/
|
|
export function DashboardMetricCard({
|
|
title,
|
|
value,
|
|
secondary,
|
|
infoText,
|
|
icon,
|
|
tone,
|
|
}: DashboardMetricCardProps) {
|
|
return (
|
|
<section className={`rounded-xl p-4 md:p-6 transition hover:-translate-y-0.5 hover:shadow-md ${toneCard[tone]}`}>
|
|
<div className="flex items-start justify-between gap-3">
|
|
<div>
|
|
<div className="flex items-center gap-1.5">
|
|
<p className="text-xs font-semibold uppercase tracking-wider text-muted">
|
|
{title}
|
|
</p>
|
|
{infoText && (
|
|
<span className="inline-flex text-muted" title={infoText} aria-label={infoText}>
|
|
<Info className="h-3.5 w-3.5" />
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className="mt-2 flex items-end gap-2">
|
|
<p className="font-heading text-4xl font-bold text-strong">{value}</p>
|
|
{secondary && (
|
|
<p className="pb-1 text-xs text-muted">{secondary}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className={`rounded-lg p-2 ${toneIcon[tone]}`}>{icon}</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|