Pipeline/frontend/src/components/dashboard/DashboardMetricCard.tsx

63 lines
1.8 KiB
TypeScript

import type { ReactNode } from "react";
import { Info } from "lucide-react";
import { toneCard, toneGlow, toneIcon, 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={`relative overflow-hidden rounded-xl p-4 shadow-lush transition hover:-translate-y-0.5 hover:shadow-md md:p-6 ${toneCard[tone]}`}
>
<span
className={`pointer-events-none absolute inset-x-4 top-0 h-px ${toneGlow[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>
);
}