'use client' import * as React from 'react' import { cn } from '@/lib/utils' import { cva, type VariantProps } from 'class-variance-authority' // Trend icons const TrendUpIcon = () => ( ) const TrendDownIcon = () => ( ) const statCardVariants = cva( 'relative flex flex-col gap-2 p-5 rounded-xl border transition-all', { variants: { variant: { default: 'bg-card border-border', muted: 'bg-muted/50 border-transparent', glass: 'bg-card/80 backdrop-blur-xl border-border/50', gradient: 'bg-gradient-to-br from-card to-muted border-border', glow: 'bg-card border-border hover:shadow-[0_0_30px_oklch(0.75_0.18_195_/_0.1)] hover:border-primary/30', }, }, defaultVariants: { variant: 'default', }, }, ) interface StatCardProps extends React.ComponentProps<'div'>, VariantProps { label: string value: string | number change?: number changeLabel?: string icon?: React.ReactNode trend?: 'up' | 'down' | 'neutral' sparkline?: number[] } function StatCard({ label, value, change, changeLabel, icon, trend, sparkline, variant, className, ...props }: StatCardProps) { const trendColor = trend === 'up' ? 'text-success' : trend === 'down' ? 'text-destructive' : 'text-muted-foreground' return ( {label} {value} {icon && ( {icon} )} {(change !== undefined || sparkline) && ( {change !== undefined && ( {trend === 'up' && } {trend === 'down' && } {change > 0 && '+'} {change}% {changeLabel && ( {changeLabel} )} )} {sparkline && ( )} )} ) } // Mini sparkline chart interface SparklineProps { data: number[] trend?: 'up' | 'down' | 'neutral' className?: string } function Sparkline({ data, trend, className }: SparklineProps) { const max = Math.max(...data) const min = Math.min(...data) const range = max - min || 1 const points = data.map((value, i) => { const x = (i / (data.length - 1)) * 100 const y = 100 - ((value - min) / range) * 100 return `${x},${y}` }).join(' ') const strokeColor = trend === 'up' ? 'stroke-success' : trend === 'down' ? 'stroke-destructive' : 'stroke-primary' return ( ) } // Stats Grid interface StatsGridProps extends React.ComponentProps<'div'> { columns?: 2 | 3 | 4 } function StatsGrid({ columns = 4, className, children, ...props }: StatsGridProps) { return ( {children} ) } // XP/Gaming style progress interface XPBarProps { level: number currentXP: number maxXP: number className?: string } function XPBar({ level, currentXP, maxXP, className }: XPBarProps) { const progress = (currentXP / maxXP) * 100 return ( LVL {level} {currentXP.toLocaleString()} / {maxXP.toLocaleString()} XP ) } // Leaderboard entry interface LeaderboardEntryProps { rank: number name: string avatar?: string initials: string score: number | string highlight?: boolean className?: string } function LeaderboardEntry({ rank, name, avatar, initials, score, highlight = false, className, }: LeaderboardEntryProps) { const rankColor = rank === 1 ? 'text-[oklch(0.88_0.16_85)]' : rank === 2 ? 'text-muted-foreground' : rank === 3 ? 'text-[oklch(0.65_0.12_45)]' : 'text-muted-foreground' return ( {rank} {avatar ? ( ) : ( initials )} {name} {score} ) } export { StatCard, Sparkline, StatsGrid, XPBar, LeaderboardEntry, statCardVariants }
{label}
{value}