import { ListOrdered } from "lucide-react"; import { Link } from "react-router"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardHeader } from "~/components/ui/card"; import { GradientIcon } from "~/components/ui/GradientIcon"; // ─── TeamAvatar ─────────────────────────────────────────────────────────────── const TEAM_AVATAR_COLORS = [ "#adf661", "#2ce1c1", "#8b5cf6", "#f59e0b", "#ef4444", "#3b82f6", ]; function hashTeamId(id: string): number { let hash = 0; for (let i = 0; i < id.length; i++) { hash = (hash * 31 + id.charCodeAt(i)) & 0xffff; } return hash; } function TeamAvatar({ teamId, teamName }: { teamId: string; teamName: string }) { const color = TEAM_AVATAR_COLORS[hashTeamId(teamId) % TEAM_AVATAR_COLORS.length]; const initials = teamName .split(/\s+/) .filter(Boolean) .slice(0, 2) .map((w) => w[0].toUpperCase()) .join("") || "?"; return (
{initials}
); } // ─── Stat helpers (mirrors LeagueRow) ──────────────────────────────────────── function StatColumn({ label, children, }: { label: string; children: React.ReactNode; }) { return (

{label}

{children}
); } function StatDivider() { return
; } function RankChangeIndicator({ change }: { change: number }) { if (change === 0) return null; if (change > 0) { return ( ▲{change} ); } return ( ▼{Math.abs(change)} ); } function PointChangeIndicator({ change }: { change: number }) { const sign = change >= 0 ? "+" : ""; if (change >= 0) { return {sign}{change.toFixed(1)}; } return ( {sign}{change.toFixed(1)} ); } // ─── Row styles ─────────────────────────────────────────────────────────────── const ROW_RANK_CLASSES: Record = { 1: "bg-yellow-500/10 hover:bg-yellow-500/15", 2: "bg-white/[0.14] hover:bg-white/[0.18]", 3: "bg-orange-600/[0.08] hover:bg-orange-600/[0.12]", }; function rowClasses(currentRank: number | undefined, hasHref: boolean): string { const podium = currentRank !== undefined ? ROW_RANK_CLASSES[currentRank] : undefined; const base = podium ?? `bg-white/[0.04] ${hasHref ? "hover:bg-white/[0.07]" : ""}`; return `flex flex-col sm:flex-row sm:items-center gap-0 rounded-lg px-3 py-3 sm:px-5 sm:py-4 transition-colors ${base}`; } // ─── Row content ────────────────────────────────────────────────────────────── function RowContent({ entry }: { entry: StandingsPreviewEntry }) { return ( <> {/* Left: avatar + name */}

{entry.teamName}

{entry.ownerName && (

{entry.ownerName}

)}
{/* Right: stats — second row on mobile */}
{entry.displayRank} {entry.rankChange !== undefined && entry.rankChange !== 0 && ( )} {Math.round(entry.points).toLocaleString("en-US")} {entry.pointChange !== undefined && entry.pointChange !== 0 && ( )}
); } // ─── Public API ─────────────────────────────────────────────────────────────── export interface StandingsPreviewEntry { teamId: string; teamName: string; ownerName?: string | null; /** Pre-computed display rank, e.g. 1, "T2", "T5". Use getDisplayRank(). */ displayRank: string | number; /** Numeric rank used to select the gold/silver/bronze row tint (1–3 only). */ currentRank?: number; points: number; href?: string; /** Positive = moved up, negative = moved down. From TeamStanding.rankChange. */ rankChange?: number; /** 7-day point delta. From TeamStanding.sevenDayPointChange. */ pointChange?: number; } export interface StandingsPreviewProps { entries: StandingsPreviewEntry[]; description?: string; fullStandingsHref?: string; } export function StandingsPreview({ entries, description, fullStandingsHref }: StandingsPreviewProps) { return (

Standings

{description && (

{description}

)}
{fullStandingsHref && ( )}
{entries.map((entry) => entry.href ? ( ) : (
) )}
); }