import { ArrowDown, ArrowUp } from 'lucide-react'; import * as React from 'react'; import { Card } from '../dashboard/components/Card'; import { cn, type Delta } from '../lib/utils'; import { SlotNumber } from './ui/slot-number'; export type DeltaTone = 'positive' | 'negative' | 'neutral'; interface Props { label: string; value: string; hint?: string; delta?: Delta; deltaLabel?: string; deltaTone?: DeltaTone; className?: string; } // Shared metric tile used by the Analytics page Summary strip and the // Content Explorer hero strip. Visual contract: !p-4 Card, 11px // uppercase label, 4xl semibold value, optional delta arrow + hint // underneath. `delta` and `deltaTone` are optional — when omitted the // tile renders exactly as the original `Summary.StatTile` so existing // callers stay pixel-identical. export const MetricTile = React.memo( function MetricTile( { label, value, hint, delta, deltaLabel, deltaTone = 'neutral', className, }: Props ) { const renderDelta = !! delta; const arrow = delta?.direction === 1 ? : delta?.direction === -1 ? : null; const deltaColor = ! delta?.hasData || delta.direction === 0 ? 'text-gray-400' : deltaTone === 'positive' ? 'text-emerald-600' : deltaTone === 'negative' ? 'text-rose-600' : 'text-gray-500'; return ( { label } { renderDelta && ( { delta?.hasData && delta.direction !== 0 ? ( <> { arrow } { delta.capped ? `>${ delta.pct }%` : `${ delta.pct }%` } > ) : ( — ) } { deltaLabel && ( { deltaLabel } ) } ) } { hint && ( { hint } ) } ); }, ( prev, next ) => prev.value === next.value && prev.label === next.label && prev.hint === next.hint && prev.deltaTone === next.deltaTone && prev.deltaLabel === next.deltaLabel && prev.delta?.direction === next.delta?.direction && prev.delta?.pct === next.delta?.pct && prev.delta?.hasData === next.delta?.hasData && prev.delta?.capped === next.delta?.capped );
{ label }
{ delta?.hasData && delta.direction !== 0 ? ( <> { arrow } { delta.capped ? `>${ delta.pct }%` : `${ delta.pct }%` } > ) : ( — ) } { deltaLabel && ( { deltaLabel } ) }
{ hint }