'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 ? ( {name} ) : ( initials )}
{name} {score}
) } export { StatCard, Sparkline, StatsGrid, XPBar, LeaderboardEntry, statCardVariants }