import type { ReactNode } from 'react'; import { Badge } from '@/components/ui/badge'; import { cn } from '@/lib/utils'; export type StatTone = 'default' | 'positive' | 'warning' | 'danger' | 'info' | 'brand'; export interface Stat { /** Short. Two or three words; it sits above the number. */ label: string; value: ReactNode; /** One clarifying line, shown under the number at a smaller size. */ hint?: string; tone?: StatTone; /** Set when this number was derived under an edited reward, not recorded. */ edited?: boolean; } export interface StatStripProps { stats: Stat[]; className?: string; /** Announce changes as they happen. Off by default — the shell owns the * page's single live region and two competing ones talk over each other. */ live?: boolean; } const TONE: Record = { default: 'text-fg', positive: 'text-positive', warning: 'text-warning', danger: 'text-danger', info: 'text-info', brand: 'text-accent-fg', }; /** * A row of headline numbers. Scrolls horizontally on a phone rather than * wrapping into a ragged grid: four stats reflowing to 2x2 at 390px puts the * least important number in the most prominent corner. */ export function StatStrip({ stats, className, live = false }: StatStripProps) { if (stats.length === 0) return null; return (
{stats.map((stat) => (
{stat.label} {stat.edited ? : null}
{stat.value}
{stat.hint ?
{stat.hint}
: null}
))}
); } /** * Marks a number the visitor caused rather than one we recorded. It appears on * every derived value in the reward editor; without it, an edited ranking * screenshots identically to a measured one. */ export function EditedChip({ className }: { className?: string }) { return ( edited ); }