Files
EpicNext-Cms/src/components/admin/stats-card.tsx
T
Simo b1ddda66ff
CI / check (push) Successful in 27s
CI / release (push) Skipped
CI / deploy (push) Successful in 43s
Revert "Merge pull request 'Complete Housekeeping migration and /ase cutover' (#52) from codex/housekeeping-complete into main"
This reverts commit 488b6e57c4, reversing
changes made to b506b4499a.
2026-08-30 21:31:34 +02:00

81 lines
1.9 KiB
TypeScript

import { Minus, TrendingDown, TrendingUp } from "lucide-react";
import Link from "@/components/link";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
interface StatsCardProps {
title: string;
value: string | number;
description?: string;
icon?: React.ReactNode;
href?: string;
/** Percentage change vs previous period. Positive = up, negative = down, undefined = no trend */
trend?: number;
}
export function StatsCard({
title,
value,
description,
icon,
href,
trend,
}: StatsCardProps) {
const content = (
<Card
className={
href
? "transition-all duration-200 hover:border-primary/50 hover:shadow-lg hover:shadow-primary/5 hover:-translate-y-0.5 cursor-pointer"
: ""
}
>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">{title}</CardTitle>
{icon && <div className="text-muted-foreground">{icon}</div>}
</CardHeader>
<CardContent>
<div className="flex items-baseline gap-2">
<div className="text-2xl font-bold">{value}</div>
{trend !== undefined && <TrendBadge value={trend} />}
</div>
{description && (
<p className="text-xs text-muted-foreground">{description}</p>
)}
</CardContent>
</Card>
);
if (href) {
return <Link href={href}>{content}</Link>;
}
return content;
}
function TrendBadge({ value }: { value: number }) {
if (value === 0) {
return (
<span className="inline-flex items-center gap-0.5 text-xs text-muted-foreground">
<Minus className="h-3 w-3" />
0%
</span>
);
}
const isUp = value > 0;
return (
<span
className={`inline-flex items-center gap-0.5 text-xs font-medium ${
isUp ? "text-[var(--admin-success)]" : "text-[var(--admin-error)]"
}`}
>
{isUp ? (
<TrendingUp className="h-3 w-3" />
) : (
<TrendingDown className="h-3 w-3" />
)}
{isUp ? "+" : ""}
{value}%
</span>
);
}