import { Column, Text } from '@umami/react-zen'; import { useSpring, useTransform } from 'motion/react'; import { useEffect, useRef } from 'react'; import { AnimatedDiv } from '@/components/common/AnimatedDiv'; import { Badge } from '@/components/common/Badge'; import { useMessages } from '@/components/hooks'; import { WEB_VITALS_THRESHOLDS } from '@/lib/constants'; import { formatNumber } from '@/lib/format'; import styles from './PerformanceCard.module.css'; export interface PerformanceCardProps { metric: 'lcp' | 'inp' | 'cls' | 'fcp' | 'ttfb'; value: number; label: string; formatValue?: (n: any) => string; onClick?: () => void; selected?: boolean; } const RATING_VARIANTS = { good: 'good', 'needs-improvement': 'warning', poor: 'danger', } as const; function getRating(metric: string, value: number): 'good' | 'needs-improvement' | 'poor' { const threshold = WEB_VITALS_THRESHOLDS[metric as keyof typeof WEB_VITALS_THRESHOLDS]; if (!threshold || value <= 0) return 'good'; if (value <= threshold.good) return 'good'; if (value <= threshold.poor) return 'needs-improvement'; return 'poor'; } export const PerformanceCard = ({ metric, value = 0, label, formatValue = formatNumber, onClick, selected = false, }: PerformanceCardProps) => { const { t, labels } = useMessages(); const rating = getRating(metric, value); const prevMetricRef = useRef(metric); const metricChanged = prevMetricRef.current !== metric; if (metricChanged) prevMetricRef.current = metric; const target = !Number.isNaN(value) ? value : 0; const spring = useSpring(target, { stiffness: 170, damping: 26 }); const display = useTransform(spring, n => formatValue(n)); useEffect(() => { if (metricChanged) spring.jump(target); else spring.set(target); }, [target, metricChanged, spring]); return ( {label} {display} {t(labels[rating === 'needs-improvement' ? 'needsImprovement' : rating])} ); };