Files
krow_workforce_owliver/src/components/shared/MetricCard.tsx
2026-09-16 20:11:27 +05:30

113 lines
3.0 KiB
TypeScript

import type { LucideIcon } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Progress } from "@/components/ui/progress"
import type { Tone } from "@/data/types"
import { cn } from "@/lib/utils"
/* ===========================================================================
The KPI card from IMAGE 1: uppercase label, status chip, icon, headline
figure, supporting line and an optional progress bar.
=========================================================================== */
const SURFACE: Record<Tone, string> = {
neutral: "border-line bg-surface",
primary: "border-line bg-surface",
good: "border-line bg-surface",
warn: "border-warn-edge bg-warn-soft",
bad: "border-bad-edge bg-bad-soft",
}
const VALUE: Record<Tone, string> = {
neutral: "text-ink",
primary: "text-ink",
good: "text-ink",
warn: "text-ink",
bad: "text-bad",
}
const ICON_WRAP: Record<Tone, string> = {
neutral: "bg-surface-sunk text-ink-3",
primary: "bg-primary-soft text-primary",
good: "bg-good-soft text-good",
warn: "bg-warn-soft text-warn",
bad: "bg-bad-soft text-bad",
}
export interface MetricCardProps {
label: string
value: string
sub: string
icon: LucideIcon
badge?: string
badgeTone?: Tone
tone?: Tone
progress?: number
/** Compact form used on Home (section 10): no progress bar, tighter type. */
compact?: boolean
}
export function MetricCard({
label,
value,
sub,
icon: Icon,
badge,
badgeTone = "neutral",
tone = "neutral",
progress,
compact = false,
}: MetricCardProps) {
return (
<div
className={cn(
"flex min-w-0 flex-col rounded-[12px] border shadow-xs transition-shadow hover:shadow-sm",
compact ? "p-2 sm:p-2.5" : "p-2.5 sm:p-3",
SURFACE[tone]
)}
>
<div className="flex items-start justify-between gap-1.5">
<span className="min-w-0 truncate text-[10px] font-bold uppercase leading-tight tracking-[0.08em] text-ink-3">
{label}
</span>
<div className="flex shrink-0 items-center gap-1">
{badge && !compact && (
<Badge tone={badgeTone} size="sm" className="px-1.5 py-0 text-[9px] font-bold h-4">
{badge}
</Badge>
)}
<span
className={cn(
"grid size-5 shrink-0 place-items-center rounded-[6px]",
ICON_WRAP[tone === "neutral" ? "primary" : tone]
)}
>
<Icon className="size-3" />
</span>
</div>
</div>
<div
className={cn(
"mt-1.5 truncate font-bold leading-none",
compact ? "text-[18px]" : "text-[21px]",
VALUE[tone]
)}
>
{value}
</div>
<p
className={cn(
"truncate leading-tight text-ink-2",
compact ? "mt-0.5 text-[11px]" : "mt-1 text-[11.5px]"
)}
>
{sub}
</p>
{progress !== undefined && !compact && <Progress value={progress} className="mt-2 h-1" />}
</div>
)
}