Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | import React from 'react'; import { Card, CardContent, CardHeader } from '@/Components/ui/Card'; import { Badge } from '@/Components/ui/Badge'; import { cn } from '@/lib/utils'; import { TrendingUp, TrendingDown, Minus } from 'lucide-react'; interface MetricCardProps { title: string; value: string | number; description?: string; trend?: { value: number; isPositive?: boolean; label?: string; }; icon?: React.ReactNode; className?: string; valuePrefix?: string; valueSuffix?: string; } export function MetricCard({ title, value, description, trend, icon, className, valuePrefix = '', valueSuffix = '', }: MetricCardProps) { const formatValue = (val: string | number) => { if (typeof val === 'number') { return val.toLocaleString(); } return val; }; const getTrendIcon = () => { if (!trend) return null; if (trend.value === 0) { return <Minus className="h-3 w-3" />; } return trend.isPositive ? ( <TrendingUp className="h-3 w-3" /> ) : ( <TrendingDown className="h-3 w-3" /> ); }; const getTrendColor = () => { if (!trend || trend.value === 0) return 'secondary'; return trend.isPositive ? 'default' : 'destructive'; }; return ( <Card className={cn('transition-all hover:shadow-md', className)}> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <h3 className="text-sm font-medium text-muted-foreground">{title}</h3> {icon && ( <div className="h-4 w-4 text-muted-foreground"> {icon} </div> )} </CardHeader> <CardContent> <div className="flex items-center justify-between"> <div className="space-y-1"> <div className="text-2xl font-bold"> {valuePrefix}{formatValue(value)}{valueSuffix} </div> {description && ( <p className="text-xs text-muted-foreground"> {description} </p> )} </div> {trend && ( <div className="flex flex-col items-end space-y-1"> <Badge variant={getTrendColor()} className="flex items-center gap-1 text-xs" > {getTrendIcon()} {Math.abs(trend.value)}% </Badge> {trend.label && ( <span className="text-xs text-muted-foreground"> {trend.label} </span> )} </div> )} </div> </CardContent> </Card> ); } |