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:
164
frontend/src/hooks/useAdmin.ts
Normal file
164
frontend/src/hooks/useAdmin.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import * as adminApi from '../api/admin'
|
||||
import type {
|
||||
CreateGameInput,
|
||||
UpdateGameInput,
|
||||
CreateRouteInput,
|
||||
UpdateRouteInput,
|
||||
RouteReorderItem,
|
||||
CreatePokemonInput,
|
||||
UpdatePokemonInput,
|
||||
CreateRouteEncounterInput,
|
||||
UpdateRouteEncounterInput,
|
||||
} from '../types'
|
||||
|
||||
// --- Queries ---
|
||||
|
||||
export function usePokemonList(search?: string) {
|
||||
return useQuery({
|
||||
queryKey: ['pokemon', { search }],
|
||||
queryFn: () => adminApi.listPokemon(search),
|
||||
})
|
||||
}
|
||||
|
||||
// --- Game Mutations ---
|
||||
|
||||
export function useCreateGame() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateGameInput) => adminApi.createGame(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['games'] }),
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateGame() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: UpdateGameInput }) =>
|
||||
adminApi.updateGame(id, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['games'] }),
|
||||
})
|
||||
}
|
||||
|
||||
export function useDeleteGame() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => adminApi.deleteGame(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['games'] }),
|
||||
})
|
||||
}
|
||||
|
||||
// --- Route Mutations ---
|
||||
|
||||
export function useCreateRoute(gameId: number) {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateRouteInput) => adminApi.createRoute(gameId, data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['games', gameId] })
|
||||
qc.invalidateQueries({ queryKey: ['games', gameId, 'routes'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateRoute(gameId: number) {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ routeId, data }: { routeId: number; data: UpdateRouteInput }) =>
|
||||
adminApi.updateRoute(gameId, routeId, data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['games', gameId] })
|
||||
qc.invalidateQueries({ queryKey: ['games', gameId, 'routes'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useDeleteRoute(gameId: number) {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (routeId: number) => adminApi.deleteRoute(gameId, routeId),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['games', gameId] })
|
||||
qc.invalidateQueries({ queryKey: ['games', gameId, 'routes'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useReorderRoutes(gameId: number) {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (routes: RouteReorderItem[]) => adminApi.reorderRoutes(gameId, routes),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['games', gameId] })
|
||||
qc.invalidateQueries({ queryKey: ['games', gameId, 'routes'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// --- Pokemon Mutations ---
|
||||
|
||||
export function useCreatePokemon() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (data: CreatePokemonInput) => adminApi.createPokemon(data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['pokemon'] }),
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdatePokemon() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: UpdatePokemonInput }) =>
|
||||
adminApi.updatePokemon(id, data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['pokemon'] }),
|
||||
})
|
||||
}
|
||||
|
||||
export function useDeletePokemon() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => adminApi.deletePokemon(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['pokemon'] }),
|
||||
})
|
||||
}
|
||||
|
||||
export function useBulkImportPokemon() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (items: Array<{ nationalDex: number; name: string; types: string[]; spriteUrl?: string | null }>) =>
|
||||
adminApi.bulkImportPokemon(items),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['pokemon'] }),
|
||||
})
|
||||
}
|
||||
|
||||
// --- Route Encounter Mutations ---
|
||||
|
||||
export function useAddRouteEncounter(routeId: number) {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateRouteEncounterInput) =>
|
||||
adminApi.addRouteEncounter(routeId, data),
|
||||
onSuccess: () =>
|
||||
qc.invalidateQueries({ queryKey: ['routes', routeId, 'pokemon'] }),
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateRouteEncounter(routeId: number) {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ encounterId, data }: { encounterId: number; data: UpdateRouteEncounterInput }) =>
|
||||
adminApi.updateRouteEncounter(routeId, encounterId, data),
|
||||
onSuccess: () =>
|
||||
qc.invalidateQueries({ queryKey: ['routes', routeId, 'pokemon'] }),
|
||||
})
|
||||
}
|
||||
|
||||
export function useRemoveRouteEncounter(routeId: number) {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (encounterId: number) =>
|
||||
adminApi.removeRouteEncounter(routeId, encounterId),
|
||||
onSuccess: () =>
|
||||
qc.invalidateQueries({ queryKey: ['routes', routeId, 'pokemon'] }),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user