import { useState } from 'react' import { useStats } from '../hooks/useStats' import { StatCard } from '../components' import type { PokemonRanking, StatsResponse } from '../types/stats' const typeBarColors: Record = { normal: 'bg-gray-400', fire: 'bg-red-500', water: 'bg-blue-500', electric: 'bg-yellow-400', grass: 'bg-green-500', ice: 'bg-cyan-300', fighting: 'bg-red-700', poison: 'bg-purple-500', ground: 'bg-amber-600', flying: 'bg-indigo-300', psychic: 'bg-pink-500', bug: 'bg-lime-500', rock: 'bg-amber-700', ghost: 'bg-purple-700', dragon: 'bg-indigo-600', dark: 'bg-gray-700', steel: 'bg-gray-400', fairy: 'bg-pink-300', } function fmt(value: number | null, suffix = ''): string { if (value === null) return '—' return `${value}${suffix}` } function pct(value: number | null): string { if (value === null) return '—' return `${(value * 100).toFixed(1)}%` } function PokemonList({ title, pokemon, }: { title: string pokemon: PokemonRanking[] }) { const [expanded, setExpanded] = useState(false) const visible = expanded ? pokemon : pokemon.slice(0, 5) return (

{title}

{pokemon.length === 0 ? (

No data

) : ( <>
{visible.map((p, i) => (
{i + 1}. {p.spriteUrl ? ( {p.name} ) : (
)} {p.name} {p.count}
))}
{pokemon.length > 5 && ( )} )}
) } function hexLuminance(hex: string): number { const r = parseInt(hex.slice(1, 3), 16) const g = parseInt(hex.slice(3, 5), 16) const b = parseInt(hex.slice(5, 7), 16) return (0.299 * r + 0.587 * g + 0.114 * b) / 255 } function HorizontalBar({ label, value, max, color, colorHex, }: { label: string value: number max: number color?: string colorHex?: string }) { const width = max > 0 ? (value / max) * 100 : 0 const isLight = colorHex ? hexLuminance(colorHex) > 0.55 : false return (
{label}
{value}
) } function Section({ title, children, }: { title: string children: React.ReactNode }) { return (

{title}

{children}
) } function StatsContent({ stats }: { stats: StatsResponse }) { const gameMax = Math.max(...stats.runsByGame.map((g) => g.count), 0) const typeMax = Math.max(...stats.typeDistribution.map((t) => t.count), 0) return (
{/* Run Overview */}
Win Rate: {pct(stats.winRate)} Avg Duration: {fmt(stats.avgDurationDays, ' days')}
{/* Runs by Game */} {stats.runsByGame.length > 0 && (
{stats.runsByGame.map((g) => ( ))}
)} {/* Encounter Stats */}
Catch Rate: {pct(stats.catchRate)} Avg per Run: {fmt(stats.avgEncountersPerRun)}
{/* Pokemon Rankings */}
{/* Team & Deaths */}
{pct(stats.mortalityRate)}
Mortality Rate
{fmt(stats.avgCatchLevel)}
Avg Catch Lv.
{fmt(stats.avgFaintLevel)}
Avg Faint Lv.
{stats.topDeathCauses.length > 0 && (

Top Death Causes

{stats.topDeathCauses.map((d, i) => (
{i + 1}. {d.cause} {d.count}
))}
)}
{/* Type Distribution */} {stats.typeDistribution.length > 0 && (
{stats.typeDistribution.map((t) => ( ))}
)}
) } export function Stats() { const { data: stats, isLoading, error } = useStats() return (

Stats

{isLoading && (
)} {error && (
Failed to load stats: {error.message}
)} {stats && stats.totalRuns === 0 && (

No data yet

Start a Nuzlocke run to see your stats here.

)} {stats && stats.totalRuns > 0 && }
) }