Align repo config with global development standards
Some checks failed
CI / backend-lint (push) Failing after 1m4s
CI / actions-lint (push) Failing after 6s
CI / frontend-lint (push) Successful in 59s

- Add missing tsconfig strictness flags (noUncheckedIndexedAccess,
  exactOptionalPropertyTypes, noImplicitOverride,
  noPropertyAccessFromIndexSignature) and fix all resulting type errors
- Replace ESLint/Prettier with oxlint 1.48.0 and oxfmt 0.33.0
- Pin all frontend and backend dependencies to exact versions
- Pin GitHub Actions to SHA hashes with persist-credentials: false
- Fix CI Python version mismatch (3.12 -> 3.14) and ruff target-version
- Add vitest 4.0.18 with jsdom environment for frontend testing
- Add ty 0.0.17 for Python type checking (non-blocking in CI)
- Add actionlint and zizmor CI job for workflow linting and security audit
- Add Dependabot config for npm, pip, and github-actions
- Update CLAUDE.md and pre-commit hooks to reflect new tooling
- Ignore Claude Code sandbox artifacts in gitignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 20:39:41 +01:00
parent e4814250db
commit 3a64661760
91 changed files with 2073 additions and 3215 deletions

View File

@@ -23,12 +23,7 @@ import type {
// --- Queries ---
export function usePokemonList(
search?: string,
limit = 50,
offset = 0,
type?: string
) {
export function usePokemonList(search?: string, limit = 50, offset = 0, type?: string) {
return useQuery({
queryKey: ['pokemon', { search, limit, offset, type }],
queryFn: () => adminApi.listPokemon(search, limit, offset, type),
@@ -92,13 +87,8 @@ export function useCreateRoute(gameId: number) {
export function useUpdateRoute(gameId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: ({
routeId,
data,
}: {
routeId: number
data: UpdateRouteInput
}) => adminApi.updateRoute(gameId, routeId, data),
mutationFn: ({ routeId, data }: { routeId: number; data: UpdateRouteInput }) =>
adminApi.updateRoute(gameId, routeId, data),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['games', gameId] })
qc.invalidateQueries({ queryKey: ['games', gameId, 'routes'] })
@@ -124,8 +114,7 @@ export function useDeleteRoute(gameId: number) {
export function useReorderRoutes(gameId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: (routes: RouteReorderItem[]) =>
adminApi.reorderRoutes(gameId, routes),
mutationFn: (routes: RouteReorderItem[]) => adminApi.reorderRoutes(gameId, routes),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['games', gameId] })
qc.invalidateQueries({ queryKey: ['games', gameId, 'routes'] })
@@ -188,9 +177,7 @@ export function useBulkImportPokemon() {
) => adminApi.bulkImportPokemon(items),
onSuccess: (result) => {
qc.invalidateQueries({ queryKey: ['pokemon'] })
toast.success(
`Import complete: ${result.created} created, ${result.updated} updated`
)
toast.success(`Import complete: ${result.created} created, ${result.updated} updated`)
},
onError: (err) => toast.error(`Import failed: ${err.message}`),
})
@@ -202,9 +189,7 @@ export function useBulkImportEvolutions() {
mutationFn: (items: unknown[]) => adminApi.bulkImportEvolutions(items),
onSuccess: (result) => {
qc.invalidateQueries({ queryKey: ['evolutions'] })
toast.success(
`Import complete: ${result.created} created, ${result.updated} updated`
)
toast.success(`Import complete: ${result.created} created, ${result.updated} updated`)
},
onError: (err) => toast.error(`Import failed: ${err.message}`),
})
@@ -217,9 +202,7 @@ export function useBulkImportRoutes(gameId: number) {
onSuccess: (result) => {
qc.invalidateQueries({ queryKey: ['games', gameId] })
qc.invalidateQueries({ queryKey: ['games', gameId, 'routes'] })
toast.success(
`Import complete: ${result.created} routes, ${result.updated} encounters`
)
toast.success(`Import complete: ${result.created} routes, ${result.updated} encounters`)
},
onError: (err) => toast.error(`Import failed: ${err.message}`),
})
@@ -239,12 +222,7 @@ export function useBulkImportBosses(gameId: number) {
// --- Evolution Queries & Mutations ---
export function useEvolutionList(
search?: string,
limit = 50,
offset = 0,
trigger?: string
) {
export function useEvolutionList(search?: string, limit = 50, offset = 0, trigger?: string) {
return useQuery({
queryKey: ['evolutions', { search, limit, offset, trigger }],
queryFn: () => adminApi.listEvolutions(search, limit, offset, trigger),
@@ -293,8 +271,7 @@ export function useDeleteEvolution() {
export function useAddRouteEncounter(routeId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: (data: CreateRouteEncounterInput) =>
adminApi.addRouteEncounter(routeId, data),
mutationFn: (data: CreateRouteEncounterInput) => adminApi.addRouteEncounter(routeId, data),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['routes', routeId, 'pokemon'] })
toast.success('Encounter added')
@@ -306,13 +283,8 @@ export function useAddRouteEncounter(routeId: number) {
export function useUpdateRouteEncounter(routeId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: ({
encounterId,
data,
}: {
encounterId: number
data: UpdateRouteEncounterInput
}) => adminApi.updateRouteEncounter(routeId, encounterId, data),
mutationFn: ({ encounterId, data }: { encounterId: number; data: UpdateRouteEncounterInput }) =>
adminApi.updateRouteEncounter(routeId, encounterId, data),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['routes', routeId, 'pokemon'] })
toast.success('Encounter updated')
@@ -324,8 +296,7 @@ export function useUpdateRouteEncounter(routeId: number) {
export function useRemoveRouteEncounter(routeId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: (encounterId: number) =>
adminApi.removeRouteEncounter(routeId, encounterId),
mutationFn: (encounterId: number) => adminApi.removeRouteEncounter(routeId, encounterId),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['routes', routeId, 'pokemon'] })
toast.success('Encounter removed')
@@ -339,41 +310,32 @@ export function useRemoveRouteEncounter(routeId: number) {
export function useCreateBossBattle(gameId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: (data: CreateBossBattleInput) =>
adminApi.createBossBattle(gameId, data),
mutationFn: (data: CreateBossBattleInput) => adminApi.createBossBattle(gameId, data),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['games', gameId, 'bosses'] })
toast.success('Boss battle created')
},
onError: (err) =>
toast.error(`Failed to create boss battle: ${err.message}`),
onError: (err) => toast.error(`Failed to create boss battle: ${err.message}`),
})
}
export function useUpdateBossBattle(gameId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: ({
bossId,
data,
}: {
bossId: number
data: UpdateBossBattleInput
}) => adminApi.updateBossBattle(gameId, bossId, data),
mutationFn: ({ bossId, data }: { bossId: number; data: UpdateBossBattleInput }) =>
adminApi.updateBossBattle(gameId, bossId, data),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['games', gameId, 'bosses'] })
toast.success('Boss battle updated')
},
onError: (err) =>
toast.error(`Failed to update boss battle: ${err.message}`),
onError: (err) => toast.error(`Failed to update boss battle: ${err.message}`),
})
}
export function useReorderBosses(gameId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: (bosses: BossReorderItem[]) =>
adminApi.reorderBosses(gameId, bosses),
mutationFn: (bosses: BossReorderItem[]) => adminApi.reorderBosses(gameId, bosses),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['games', gameId, 'bosses'] })
toast.success('Bosses reordered')
@@ -390,16 +352,14 @@ export function useDeleteBossBattle(gameId: number) {
qc.invalidateQueries({ queryKey: ['games', gameId, 'bosses'] })
toast.success('Boss battle deleted')
},
onError: (err) =>
toast.error(`Failed to delete boss battle: ${err.message}`),
onError: (err) => toast.error(`Failed to delete boss battle: ${err.message}`),
})
}
export function useSetBossTeam(gameId: number, bossId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: (team: BossPokemonInput[]) =>
adminApi.setBossTeam(gameId, bossId, team),
mutationFn: (team: BossPokemonInput[]) => adminApi.setBossTeam(gameId, bossId, team),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['games', gameId, 'bosses'] })
toast.success('Boss team updated')
@@ -438,8 +398,7 @@ export function useDeleteGenlocke() {
export function useAddGenlockeLeg(genlockeId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: (data: AddGenlockeLegInput) =>
adminApi.addGenlockeLeg(genlockeId, data),
mutationFn: (data: AddGenlockeLegInput) => adminApi.addGenlockeLeg(genlockeId, data),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['genlockes'] })
qc.invalidateQueries({ queryKey: ['genlockes', genlockeId] })
@@ -452,8 +411,7 @@ export function useAddGenlockeLeg(genlockeId: number) {
export function useDeleteGenlockeLeg(genlockeId: number) {
const qc = useQueryClient()
return useMutation({
mutationFn: (legId: number) =>
adminApi.deleteGenlockeLeg(genlockeId, legId),
mutationFn: (legId: number) => adminApi.deleteGenlockeLeg(genlockeId, legId),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['genlockes'] })
qc.invalidateQueries({ queryKey: ['genlockes', genlockeId] })

View File

@@ -1,11 +1,6 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import {
getGameBosses,
getBossResults,
createBossResult,
deleteBossResult,
} from '../api/bosses'
import { getGameBosses, getBossResults, createBossResult, deleteBossResult } from '../api/bosses'
import type { CreateBossResultInput } from '../types/game'
export function useGameBosses(gameId: number | null, all?: boolean) {

View File

@@ -57,11 +57,7 @@ export function useCreateGenlocke() {
})
}
export function useLegSurvivors(
genlockeId: number,
legOrder: number,
enabled: boolean
) {
export function useLegSurvivors(genlockeId: number, legOrder: number, enabled: boolean) {
return useQuery({
queryKey: ['genlockes', genlockeId, 'legs', legOrder, 'survivors'],
queryFn: () => getLegSurvivors(genlockeId, legOrder),
@@ -81,11 +77,7 @@ export function useAdvanceLeg() {
legOrder: number
transferEncounterIds?: number[]
}) =>
advanceLeg(
genlockeId,
legOrder,
transferEncounterIds ? { transferEncounterIds } : undefined
),
advanceLeg(genlockeId, legOrder, transferEncounterIds ? { transferEncounterIds } : undefined),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['runs'] })
queryClient.invalidateQueries({ queryKey: ['genlockes'] })

View File

@@ -68,10 +68,7 @@ export function useNamingCategories() {
})
}
export function useNameSuggestions(
runId: number | null,
pokemonId?: number | null
) {
export function useNameSuggestions(runId: number | null, pokemonId?: number | null) {
return useQuery({
queryKey: ['name-suggestions', runId, pokemonId ?? null],
queryFn: () => getNameSuggestions(runId!, 10, pokemonId ?? undefined),