Add admin panel with CRUD endpoints and management UI

Add admin API endpoints for games, routes, pokemon, and route encounters
with full CRUD operations including bulk import. Build admin frontend
with game/route/pokemon management pages, navigation, and data tables.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-05 18:36:19 +01:00
parent a911259ef5
commit 55e6650e0e
28 changed files with 2140 additions and 10 deletions

71
frontend/src/api/admin.ts Normal file
View File

@@ -0,0 +1,71 @@
import { api } from './client'
import type {
Game,
Route,
Pokemon,
RouteEncounterDetail,
CreateGameInput,
UpdateGameInput,
CreateRouteInput,
UpdateRouteInput,
RouteReorderItem,
CreatePokemonInput,
UpdatePokemonInput,
BulkImportResult,
CreateRouteEncounterInput,
UpdateRouteEncounterInput,
} from '../types'
// Games
export const createGame = (data: CreateGameInput) =>
api.post<Game>('/games', data)
export const updateGame = (id: number, data: UpdateGameInput) =>
api.put<Game>(`/games/${id}`, data)
export const deleteGame = (id: number) =>
api.del(`/games/${id}`)
// Routes
export const createRoute = (gameId: number, data: CreateRouteInput) =>
api.post<Route>(`/games/${gameId}/routes`, data)
export const updateRoute = (gameId: number, routeId: number, data: UpdateRouteInput) =>
api.put<Route>(`/games/${gameId}/routes/${routeId}`, data)
export const deleteRoute = (gameId: number, routeId: number) =>
api.del(`/games/${gameId}/routes/${routeId}`)
export const reorderRoutes = (gameId: number, routes: RouteReorderItem[]) =>
api.put<Route[]>(`/games/${gameId}/routes/reorder`, { routes })
// Pokemon
export const listPokemon = (search?: string, limit = 50, offset = 0) => {
const params = new URLSearchParams()
if (search) params.set('search', search)
params.set('limit', String(limit))
params.set('offset', String(offset))
return api.get<Pokemon[]>(`/pokemon?${params}`)
}
export const createPokemon = (data: CreatePokemonInput) =>
api.post<Pokemon>('/pokemon', data)
export const updatePokemon = (id: number, data: UpdatePokemonInput) =>
api.put<Pokemon>(`/pokemon/${id}`, data)
export const deletePokemon = (id: number) =>
api.del(`/pokemon/${id}`)
export const bulkImportPokemon = (items: Array<{ nationalDex: number; name: string; types: string[]; spriteUrl?: string | null }>) =>
api.post<BulkImportResult>('/pokemon/bulk-import', items)
// Route Encounters
export const addRouteEncounter = (routeId: number, data: CreateRouteEncounterInput) =>
api.post<RouteEncounterDetail>(`/routes/${routeId}/pokemon`, data)
export const updateRouteEncounter = (routeId: number, encounterId: number, data: UpdateRouteEncounterInput) =>
api.put<RouteEncounterDetail>(`/routes/${routeId}/pokemon/${encounterId}`, data)
export const removeRouteEncounter = (routeId: number, encounterId: number) =>
api.del(`/routes/${routeId}/pokemon/${encounterId}`)