| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 |
- "use client"
- import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card"
- import CountUp from "react-countup"
- import {cn} from "@/lib/utils"
- import type {ReactNode} from "react"
- type MetricCardProps = {
- title: string
- value: number
- suffix?: string
- icon?: ReactNode
- className?: string
- trend?: { delta: number; label?: string } // delta in percent
- }
- export default function MetricCard({
- title,
- value,
- suffix,
- icon,
- className,
- trend,
- }: MetricCardProps) {
- const trendColor =
- trend && trend.delta !== 0
- ? trend.delta > 0
- ? "text-emerald-600"
- : "text-rose-600"
- : "text-muted-foreground"
- return (
- <Card className={cn("h-full", className)}>
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
- <CardTitle className="text-sm font-medium">{title}</CardTitle>
- {icon}
- </CardHeader>
- <CardContent>
- <div className="text-2xl font-bold">
- <CountUp end={value} duration={1.2} separator="," />
- {suffix ? <span className="ml-1 text-base font-medium text-muted-foreground">{suffix}</span> : null}
- </div>
- {trend ? (
- <p className={cn("text-xs mt-1", trendColor)}>
- {trend.delta > 0 ? "↑" : trend.delta < 0 ? "↓" : "—"} {Math.abs(trend.delta)}%{" "}
- {trend.label ? <span className="text-muted-foreground">· {trend.label}</span> : null}
- </p>
- ) : null}
- </CardContent>
- </Card>
- )
- }
|