Files
nuzlocke-tracker/frontend/src/components/PokemonCard.tsx
Julian Tabel 9728773a94 Add pokemon evolution support across the full stack
- Evolution model with trigger, level, item, and condition fields
- Encounter.current_pokemon_id tracks evolved species separately
- Alembic migration for evolutions table and current_pokemon_id column
- Seed pipeline loads evolution data with manual overrides
- GET /pokemon/{id}/evolutions and PATCH /encounters/{id} endpoints
- Evolve button in StatusChangeModal with evolution method details
- PokemonCard shows evolved species with "Originally" label

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 19:26:49 +01:00

104 lines
3.1 KiB
TypeScript

import type { EncounterDetail } from '../types'
interface PokemonCardProps {
encounter: EncounterDetail
showFaintLevel?: boolean
onClick?: () => void
}
const typeColors: Record<string, string> = {
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',
}
export function PokemonCard({ encounter, showFaintLevel, onClick }: PokemonCardProps) {
const { pokemon, currentPokemon, route, nickname, catchLevel, faintLevel, deathCause } = encounter
const isDead = faintLevel !== null
const displayPokemon = currentPokemon ?? pokemon
const isEvolved = currentPokemon !== null
return (
<div
onClick={onClick}
className={`bg-white dark:bg-gray-800 rounded-lg shadow p-4 flex flex-col items-center text-center ${
isDead ? 'opacity-60 grayscale' : ''
} ${onClick ? 'cursor-pointer hover:ring-2 hover:ring-blue-400 transition-shadow' : ''}`}
>
{displayPokemon.spriteUrl ? (
<img
src={displayPokemon.spriteUrl}
alt={displayPokemon.name}
className="w-16 h-16"
/>
) : (
<div className="w-16 h-16 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center text-xl font-bold text-gray-600 dark:text-gray-300">
{displayPokemon.name[0].toUpperCase()}
</div>
)}
<div className="mt-2 flex items-center gap-1.5">
<span
className={`w-2 h-2 rounded-full shrink-0 ${isDead ? 'bg-red-500' : 'bg-green-500'}`}
/>
<span className="font-semibold text-gray-900 dark:text-gray-100 text-sm">
{nickname || displayPokemon.name}
</span>
</div>
{nickname && (
<div className="text-xs text-gray-500 dark:text-gray-400">
{displayPokemon.name}
</div>
)}
<div className="flex gap-1 mt-1">
{displayPokemon.types.map((type) => (
<span
key={type}
className={`px-1.5 py-0.5 rounded text-[10px] font-medium text-white ${typeColors[type] ?? 'bg-gray-500'}`}
>
{type}
</span>
))}
</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">
{showFaintLevel && isDead
? `Lv. ${catchLevel}${faintLevel}`
: `Lv. ${catchLevel ?? '?'}`}
</div>
<div className="text-xs text-gray-400 dark:text-gray-500 mt-0.5">
{route.name}
</div>
{isEvolved && (
<div className="text-[10px] text-gray-400 dark:text-gray-500 mt-0.5">
Originally: {pokemon.name}
</div>
)}
{isDead && deathCause && (
<div className="text-[10px] italic text-gray-400 dark:text-gray-500 mt-0.5 line-clamp-2">
{deathCause}
</div>
)}
</div>
)
}