Add pokemon status management with death tracking

Implement status change workflow (alive → dead) with confirmation modal,
death cause recording, and visual status indicators on pokemon cards.
Includes backend migration for death_cause field and graveyard view
on the run dashboard.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-05 18:36:08 +01:00
parent 628c621fa9
commit a911259ef5
12 changed files with 462 additions and 53 deletions

View File

@@ -19,7 +19,12 @@ interface EncounterModalProps {
}) => void
onUpdate?: (data: {
id: number
data: { nickname?: string; status?: EncounterStatus; faintLevel?: number }
data: {
nickname?: string
status?: EncounterStatus
faintLevel?: number
deathCause?: string
}
}) => void
onClose: () => void
isPending: boolean
@@ -69,6 +74,7 @@ export function EncounterModal({
existing?.catchLevel?.toString() ?? '',
)
const [faintLevel, setFaintLevel] = useState<string>('')
const [deathCause, setDeathCause] = useState('')
const [search, setSearch] = useState('')
const isEditing = !!existing
@@ -95,6 +101,7 @@ export function EncounterModal({
nickname: nickname || undefined,
status,
faintLevel: faintLevel ? Number(faintLevel) : undefined,
deathCause: deathCause || undefined,
},
})
} else if (selectedPokemon) {
@@ -301,31 +308,53 @@ export function EncounterModal({
</div>
)}
{/* Faint Level (only when editing a caught pokemon to mark dead) */}
{/* Faint Level + Death Cause (only when editing a caught pokemon to mark dead) */}
{isEditing &&
existing?.status === 'caught' &&
existing?.faintLevel === null && (
<div>
<label
htmlFor="faint-level"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Faint Level{' '}
<span className="font-normal text-gray-400">
(mark as dead)
</span>
</label>
<input
id="faint-level"
type="number"
min={1}
max={100}
value={faintLevel}
onChange={(e) => setFaintLevel(e.target.value)}
placeholder="Leave empty if still alive"
className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<>
<div>
<label
htmlFor="faint-level"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Faint Level{' '}
<span className="font-normal text-gray-400">
(mark as dead)
</span>
</label>
<input
id="faint-level"
type="number"
min={1}
max={100}
value={faintLevel}
onChange={(e) => setFaintLevel(e.target.value)}
placeholder="Leave empty if still alive"
className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div>
<label
htmlFor="death-cause"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Cause of Death{' '}
<span className="font-normal text-gray-400">
(optional)
</span>
</label>
<input
id="death-cause"
type="text"
maxLength={100}
value={deathCause}
onChange={(e) => setDeathCause(e.target.value)}
placeholder="e.g. Crit from rival's Charizard"
className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</>
)}
</div>