Add pre-commit hooks for linting and formatting
Set up pre-commit framework with ruff (backend) and ESLint/Prettier/tsc (frontend) hooks to catch issues locally before CI. Auto-format all frontend files with Prettier to comply with the new check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
import { useState, useMemo } from 'react'
|
||||
import type { EncounterDetail, UpdateEncounterInput, CreateEncounterInput } from '../types'
|
||||
import type {
|
||||
EncounterDetail,
|
||||
UpdateEncounterInput,
|
||||
CreateEncounterInput,
|
||||
} from '../types'
|
||||
import { useEvolutions, useForms } from '../hooks/useEncounters'
|
||||
import { TypeBadge } from './TypeBadge'
|
||||
import { formatEvolutionMethod } from '../utils/formatEvolution'
|
||||
|
||||
interface StatusChangeModalProps {
|
||||
encounter: EncounterDetail
|
||||
onUpdate: (data: {
|
||||
id: number
|
||||
data: UpdateEncounterInput
|
||||
}) => void
|
||||
onUpdate: (data: { id: number; data: UpdateEncounterInput }) => void
|
||||
onClose: () => void
|
||||
isPending: boolean
|
||||
region?: string
|
||||
@@ -24,15 +25,24 @@ export function StatusChangeModal({
|
||||
region,
|
||||
onCreateEncounter,
|
||||
}: StatusChangeModalProps) {
|
||||
const { pokemon, currentPokemon, route, nickname, catchLevel, faintLevel, deathCause } =
|
||||
encounter
|
||||
const {
|
||||
pokemon,
|
||||
currentPokemon,
|
||||
route,
|
||||
nickname,
|
||||
catchLevel,
|
||||
faintLevel,
|
||||
deathCause,
|
||||
} = encounter
|
||||
const isDead = faintLevel !== null
|
||||
const displayPokemon = currentPokemon ?? pokemon
|
||||
const [showConfirm, setShowConfirm] = useState(false)
|
||||
const [showEvolve, setShowEvolve] = useState(false)
|
||||
const [showFormChange, setShowFormChange] = useState(false)
|
||||
const [showShedConfirm, setShowShedConfirm] = useState(false)
|
||||
const [pendingEvolutionId, setPendingEvolutionId] = useState<number | null>(null)
|
||||
const [pendingEvolutionId, setPendingEvolutionId] = useState<number | null>(
|
||||
null
|
||||
)
|
||||
const [shedNickname, setShedNickname] = useState('')
|
||||
const [deathLevel, setDeathLevel] = useState('')
|
||||
const [cause, setCause] = useState('')
|
||||
@@ -40,15 +50,15 @@ export function StatusChangeModal({
|
||||
const activePokemonId = currentPokemon?.id ?? pokemon.id
|
||||
const { data: evolutions, isLoading: evolutionsLoading } = useEvolutions(
|
||||
showEvolve || showShedConfirm ? activePokemonId : null,
|
||||
region,
|
||||
region
|
||||
)
|
||||
const { data: forms } = useForms(isDead ? null : activePokemonId)
|
||||
|
||||
const { normalEvolutions, shedCompanion } = useMemo(() => {
|
||||
if (!evolutions) return { normalEvolutions: [], shedCompanion: null }
|
||||
return {
|
||||
normalEvolutions: evolutions.filter(e => e.trigger !== 'shed'),
|
||||
shedCompanion: evolutions.find(e => e.trigger === 'shed') ?? null,
|
||||
normalEvolutions: evolutions.filter((e) => e.trigger !== 'shed'),
|
||||
shedCompanion: evolutions.find((e) => e.trigger === 'shed') ?? null,
|
||||
}
|
||||
}, [evolutions])
|
||||
|
||||
@@ -187,33 +197,37 @@ export function StatusChangeModal({
|
||||
)}
|
||||
|
||||
{/* Alive pokemon: actions */}
|
||||
{!isDead && !showConfirm && !showEvolve && !showFormChange && !showShedConfirm && (
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowEvolve(true)}
|
||||
className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Evolve
|
||||
</button>
|
||||
{forms && forms.length > 0 && (
|
||||
{!isDead &&
|
||||
!showConfirm &&
|
||||
!showEvolve &&
|
||||
!showFormChange &&
|
||||
!showShedConfirm && (
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowFormChange(true)}
|
||||
className="flex-1 px-4 py-2 bg-purple-600 text-white rounded-lg font-medium hover:bg-purple-700 transition-colors"
|
||||
onClick={() => setShowEvolve(true)}
|
||||
className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Change Form
|
||||
Evolve
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowConfirm(true)}
|
||||
className="flex-1 px-4 py-2 bg-red-600 text-white rounded-lg font-medium hover:bg-red-700 transition-colors"
|
||||
>
|
||||
Mark as Dead
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{forms && forms.length > 0 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowFormChange(true)}
|
||||
className="flex-1 px-4 py-2 bg-purple-600 text-white rounded-lg font-medium hover:bg-purple-700 transition-colors"
|
||||
>
|
||||
Change Form
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowConfirm(true)}
|
||||
className="flex-1 px-4 py-2 bg-red-600 text-white rounded-lg font-medium hover:bg-red-700 transition-colors"
|
||||
>
|
||||
Mark as Dead
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Evolution selection */}
|
||||
{!isDead && showEvolve && (
|
||||
@@ -231,10 +245,14 @@ export function StatusChangeModal({
|
||||
</button>
|
||||
</div>
|
||||
{evolutionsLoading && (
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Loading evolutions...</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Loading evolutions...
|
||||
</p>
|
||||
)}
|
||||
{!evolutionsLoading && normalEvolutions.length === 0 && (
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">No evolutions available</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
No evolutions available
|
||||
</p>
|
||||
)}
|
||||
{!evolutionsLoading && normalEvolutions.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
@@ -247,7 +265,11 @@ export function StatusChangeModal({
|
||||
className="w-full flex items-center gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-600 hover:bg-blue-50 dark:hover:bg-blue-900/20 hover:border-blue-300 dark:hover:border-blue-600 transition-colors disabled:opacity-50"
|
||||
>
|
||||
{evo.toPokemon.spriteUrl ? (
|
||||
<img src={evo.toPokemon.spriteUrl} alt={evo.toPokemon.name} className="w-10 h-10" />
|
||||
<img
|
||||
src={evo.toPokemon.spriteUrl}
|
||||
alt={evo.toPokemon.name}
|
||||
className="w-10 h-10"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-10 h-10 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center text-sm font-bold text-gray-600 dark:text-gray-300">
|
||||
{evo.toPokemon.name[0].toUpperCase()}
|
||||
@@ -302,8 +324,12 @@ export function StatusChangeModal({
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm text-amber-800 dark:text-amber-300">
|
||||
{displayPokemon.name} shed its shell! Would you also like to add{' '}
|
||||
<span className="font-semibold">{shedCompanion.toPokemon.name}</span>?
|
||||
{displayPokemon.name} shed its shell! Would you also like to
|
||||
add{' '}
|
||||
<span className="font-semibold">
|
||||
{shedCompanion.toPokemon.name}
|
||||
</span>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -340,7 +366,9 @@ export function StatusChangeModal({
|
||||
onClick={() => applyEvolution(true)}
|
||||
className="flex-1 px-4 py-2 bg-amber-600 text-white rounded-lg font-medium hover:bg-amber-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{isPending ? 'Saving...' : `Add ${shedCompanion.toPokemon.name}`}
|
||||
{isPending
|
||||
? 'Saving...'
|
||||
: `Add ${shedCompanion.toPokemon.name}`}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -372,7 +400,11 @@ export function StatusChangeModal({
|
||||
className="w-full flex items-center gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-600 hover:bg-purple-50 dark:hover:bg-purple-900/20 hover:border-purple-300 dark:hover:border-purple-600 transition-colors disabled:opacity-50"
|
||||
>
|
||||
{form.spriteUrl ? (
|
||||
<img src={form.spriteUrl} alt={form.name} className="w-10 h-10" />
|
||||
<img
|
||||
src={form.spriteUrl}
|
||||
alt={form.name}
|
||||
className="w-10 h-10"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-10 h-10 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center text-sm font-bold text-gray-600 dark:text-gray-300">
|
||||
{form.name[0].toUpperCase()}
|
||||
@@ -465,7 +497,12 @@ export function StatusChangeModal({
|
||||
</div>
|
||||
|
||||
{/* Footer for dead/no-confirm/no-evolve views */}
|
||||
{(isDead || (!isDead && !showConfirm && !showEvolve && !showFormChange && !showShedConfirm)) && (
|
||||
{(isDead ||
|
||||
(!isDead &&
|
||||
!showConfirm &&
|
||||
!showEvolve &&
|
||||
!showFormChange &&
|
||||
!showShedConfirm)) && (
|
||||
<div className="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user