import { type FormEvent, useState, useEffect } from 'react' import { Link } from 'react-router-dom' import { useQueryClient } from '@tanstack/react-query' import { EvolutionFormModal } from './EvolutionFormModal' import type { Pokemon, CreatePokemonInput, UpdatePokemonInput, EvolutionAdmin, UpdateEvolutionInput, } from '../../types' import { usePokemonEncounterLocations, usePokemonEvolutionChain, } from '../../hooks/usePokemon' import { useUpdateEvolution, useDeleteEvolution } from '../../hooks/useAdmin' import { formatEvolutionMethod } from '../../utils/formatEvolution' interface PokemonFormModalProps { pokemon?: Pokemon onSubmit: (data: CreatePokemonInput | UpdatePokemonInput) => void onClose: () => void isSubmitting?: boolean onDelete?: () => void isDeleting?: boolean } type Tab = 'details' | 'evolutions' | 'encounters' export function PokemonFormModal({ pokemon, onSubmit, onClose, isSubmitting, onDelete, isDeleting, }: PokemonFormModalProps) { const [pokeapiId, setPokeapiId] = useState(String(pokemon?.pokeapiId ?? '')) const [nationalDex, setNationalDex] = useState( String(pokemon?.nationalDex ?? '') ) const [name, setName] = useState(pokemon?.name ?? '') const [types, setTypes] = useState(pokemon?.types.join(', ') ?? '') const [spriteUrl, setSpriteUrl] = useState(pokemon?.spriteUrl ?? '') const [activeTab, setActiveTab] = useState('details') const [editingEvolution, setEditingEvolution] = useState(null) const [confirmingDelete, setConfirmingDelete] = useState(false) const isEdit = !!pokemon const pokemonId = pokemon?.id ?? null const { data: encounterLocations, isLoading: encountersLoading } = usePokemonEncounterLocations(pokemonId) const { data: evolutionChain, isLoading: evolutionsLoading } = usePokemonEvolutionChain(pokemonId) const queryClient = useQueryClient() const updateEvolution = useUpdateEvolution() const deleteEvolution = useDeleteEvolution() useEffect(() => { setConfirmingDelete(false) }, [onDelete]) const invalidateChain = () => { queryClient.invalidateQueries({ queryKey: ['pokemon', pokemonId, 'evolution-chain'], }) } const handleSubmit = (e: FormEvent) => { e.preventDefault() const typesList = types .split(',') .map((t) => t.trim()) .filter(Boolean) onSubmit({ pokeapiId: Number(pokeapiId), nationalDex: Number(nationalDex), name, types: typesList, spriteUrl: spriteUrl || null, }) } const tabs: { key: Tab; label: string }[] = [ { key: 'details', label: 'Details' }, { key: 'evolutions', label: 'Evolutions' }, { key: 'encounters', label: 'Encounters' }, ] const tabClass = (tab: Tab) => `px-3 py-1.5 text-sm font-medium rounded-t-md border-b-2 transition-colors ${ activeTab === tab ? 'border-blue-600 text-blue-600 dark:border-blue-400 dark:text-blue-400' : 'border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300' }` return ( <>
{/* Header */}

{pokemon ? 'Edit Pokemon' : 'Add Pokemon'}

{isEdit && (
{tabs.map((tab) => ( ))}
)}
{/* Details tab (form) */} {activeTab === 'details' && (
setPokeapiId(e.target.value)} className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600" />
setNationalDex(e.target.value)} className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600" />
setName(e.target.value)} className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600" />
setTypes(e.target.value)} placeholder="Fire, Flying" className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600" />
setSpriteUrl(e.target.value)} placeholder="Optional" className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600" />
{onDelete && ( )}
)} {/* Evolutions tab */} {activeTab === 'evolutions' && (
{evolutionsLoading && (

Loading...

)} {!evolutionsLoading && (!evolutionChain || evolutionChain.length === 0) && (

No evolutions

)} {!evolutionsLoading && evolutionChain && evolutionChain.length > 0 && (
{evolutionChain.map((evo) => ( ))}
)}
)} {/* Encounters tab */} {activeTab === 'encounters' && (
{encountersLoading && (

Loading...

)} {!encountersLoading && (!encounterLocations || encounterLocations.length === 0) && (

No encounters

)} {!encountersLoading && encounterLocations && encounterLocations.length > 0 && (
{encounterLocations.map((game) => (
{game.gameName}
{game.encounters.map((enc, i) => (
{enc.routeName} — {enc.encounterMethod}, Lv. {enc.minLevel}– {enc.maxLevel}
))}
))}
)}
)}
{editingEvolution && ( updateEvolution.mutate( { id: editingEvolution.id, data: data as UpdateEvolutionInput }, { onSuccess: () => { setEditingEvolution(null) invalidateChain() }, } ) } onClose={() => setEditingEvolution(null)} isSubmitting={updateEvolution.isPending} onDelete={() => deleteEvolution.mutate(editingEvolution.id, { onSuccess: () => { setEditingEvolution(null) invalidateChain() }, }) } isDeleting={deleteEvolution.isPending} /> )} ) }