'use client' import type { FrustrationSummary } from '@/lib/api/stats' interface FrustrationSummaryCardsProps { data: FrustrationSummary | null loading: boolean } function pctChange(current: number, previous: number): { type: 'pct'; value: number } | { type: 'new' } | null { if (previous === 0 && current === 0) return null if (previous === 0) return { type: 'new' } return { type: 'pct', value: Math.round(((current - previous) / previous) * 100) } } function ChangeIndicator({ change }: { change: ReturnType }) { if (change === null) return null if (change.type === 'new') { return ( New ) } const isUp = change.value > 0 const isDown = change.value < 0 return ( {isUp ? '+' : ''}{change.value}% ) } function SkeletonCard() { return (
) } export default function FrustrationSummaryCards({ data, loading }: FrustrationSummaryCardsProps) { if (loading || !data) { return (
) } const rageChange = pctChange(data.rage_clicks, data.prev_rage_clicks) const deadChange = pctChange(data.dead_clicks, data.prev_dead_clicks) const topPage = data.rage_top_page || data.dead_top_page const totalSignals = data.rage_clicks + data.dead_clicks return (
{/* Rage Clicks */}

Rage Clicks

{data.rage_clicks.toLocaleString()}

{data.rage_unique_elements} unique elements

{/* Dead Clicks */}

Dead Clicks

{data.dead_clicks.toLocaleString()}

{data.dead_unique_elements} unique elements

{/* Total Frustration Signals */}

Total Signals

{totalSignals.toLocaleString()} {topPage ? (

Top page: {topPage}

) : (

No data in this period

)}
) }