'use client'; import { useEffect, useState } from 'react'; import { getHeadshotUrl, PLAYER_SILHOUETTE } from '@/lib/playerHeadshot'; import { getVisibleCount, getHiddenCount, type Tier } from '@/lib/tierGate'; /** * HotListPanel (Session 23). * * Rolling recent-window leaders โ€” ranked by who's TRENDING, not who has * the biggest raw number. A 20-PPG player erupting for 28/31/25 is hot; * a 30-PPG star who dropped 28 is not. Free users see the top 3. * * Self-hides when empty so the landing page never renders a dead box. */ interface HotPlayer { rank: number; name: string; playerId: string | number | null; team: string | null; stat: string; recentAvg: number; statLine: string; trendDescription: string; } export interface HotListPanelProps { sport: string; tier?: Tier; stat?: string; limit?: number; } export default function HotListPanel({ sport, tier = 'free', stat = 'all', limit }: HotListPanelProps) { const [players, setPlayers] = useState(null); useEffect(() => { let cancelled = false; async function load() { try { const res = await fetch(`/api/hotlist/${sport}?stat=${encodeURIComponent(stat)}`); if (!res.ok) { if (!cancelled) setPlayers([]); return; } const data = await res.json(); if (!cancelled) setPlayers(Array.isArray(data?.players) ? data.players : []); } catch { if (!cancelled) setPlayers([]); } } load(); return () => { cancelled = true; }; }, [sport, stat]); if (!players || players.length === 0) return null; const tierCount = getVisibleCount(tier, players.length); const cap = limit && limit > 0 ? Math.min(limit, tierCount) : tierCount; const visible = players.slice(0, cap); const hidden = limit ? players.length - visible.length : getHiddenCount(tier, players.length); return (

๐Ÿ“ˆ HOT RIGHT NOW

{visible.map((p) => (
#{p.rank} {p.name} { (e.target as HTMLImageElement).src = PLAYER_SILHOUETTE; }} />
{p.name}{p.team ? ` ยท ${p.team}` : ''}
{p.statLine}
{p.trendDescription}
))}
{hidden > 0 && ( {hidden} more โ€” upgrade to see the full board โ†’ )}
); } const panelHeading: React.CSSProperties = { fontSize: 12, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'var(--text-tertiary, #6A6A78)', margin: '0 0 10px', }; const rowStyle: React.CSSProperties = { display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px', borderRadius: 10, background: 'var(--surface, #12121A)', border: '1px solid var(--border, #2A2A36)', }; const rankStyle: React.CSSProperties = { flex: '0 0 auto', fontSize: 13, fontWeight: 800, width: 28, color: 'var(--text-tertiary, #6A6A78)', }; const avatarStyle: React.CSSProperties = { borderRadius: '50%', objectFit: 'cover', background: '#1A1A24', flex: '0 0 auto', }; const playerName: React.CSSProperties = { fontSize: 14, fontWeight: 700, color: 'var(--text-primary, #F0F0F4)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', }; const statLineStyle: React.CSSProperties = { fontSize: 12, color: 'var(--text-secondary, #9A9AA8)', }; const trendStyle: React.CSSProperties = { flex: '0 0 auto', fontSize: 11, fontWeight: 700, padding: '3px 8px', borderRadius: 6, background: 'rgba(46,160,67,0.15)', color: '#3FB950', }; const upsellStyle: React.CSSProperties = { display: 'inline-block', marginTop: 10, fontSize: 12, fontWeight: 600, color: 'var(--accent, #E94B3C)', textDecoration: 'none', };