dress/resources/js/components/cards/stat-card.tsx

87 lines
3.2 KiB
TypeScript

import { Badge } from '@/components/ui/badge';
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
CardAction,
} from '@/components/ui/card';
import { formatCurrency, formatNumber } from '@/lib/formatters';
import { cn } from '@/lib/utils';
import type { StatItem } from '@/types';
import { TrendingDown, TrendingUp } from 'lucide-react';
export interface StatCardProps {
title: string;
stat: StatItem;
isCurrency?: boolean;
isPercentage?: boolean;
className?: string;
}
export function StatCard({
title,
stat,
isCurrency = true,
isPercentage = false,
className,
}: StatCardProps) {
const hasComparison = stat.yesterday !== null;
const isPositive = stat.change !== null && stat.change >= 0;
return (
<Card className={cn('@container/card relative', className)}>
<CardHeader className="gap-3">
<div className="flex w-full items-start justify-between gap-3">
<CardDescription className="min-w-0 flex-1 break-words leading-tight">
{title}
</CardDescription>
{hasComparison && (
<Badge
variant="default"
className={`shrink-0 flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-bold sm:text-xs ${
isPositive
? 'bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-300'
: 'bg-red-50 text-red-700 dark:bg-red-950 dark:text-red-300'
}`}
>
{isPositive ? (
<TrendingUp className="size-3" />
) : (
<TrendingDown className="size-3" />
)}
{stat.change === null
? '∞'
: `${Math.abs(stat.change)}%`}
</Badge>
)}
</div>
<CardTitle className="break-words text-xl sm:text-2xl font-bold tabular-nums tracking-tight">
{isPercentage
? `${stat.value}%`
: isCurrency
? formatCurrency(stat.value)
: formatNumber(stat.value)}
</CardTitle>
</CardHeader>
{hasComparison && (
<CardFooter className="flex-col items-start gap-1.5 text-xs">
<div className="w-full break-words text-muted-foreground leading-relaxed">
Kemarin:{' '}
<span className="font-semibold break-words inline-block">
{isPercentage
? `${stat.yesterday}%`
: isCurrency
? formatCurrency(stat.yesterday)
: formatNumber(stat.yesterday)}
</span>
</div>
</CardFooter>
)}
</Card>
);
}