diff --git a/frontend/src/components/admin/RouteEncounterFormModal.tsx b/frontend/src/components/admin/RouteEncounterFormModal.tsx
index 4b0fa0e..d7fb3fe 100644
--- a/frontend/src/components/admin/RouteEncounterFormModal.tsx
+++ b/frontend/src/components/admin/RouteEncounterFormModal.tsx
@@ -8,6 +8,8 @@ interface RouteEncounterFormModalProps {
onSubmit: (data: CreateRouteEncounterInput | UpdateRouteEncounterInput) => void
onClose: () => void
isSubmitting?: boolean
+ onDelete?: () => void
+ isDeleting?: boolean
}
export function RouteEncounterFormModal({
@@ -15,6 +17,8 @@ export function RouteEncounterFormModal({
onSubmit,
onClose,
isSubmitting,
+ onDelete,
+ isDeleting,
}: RouteEncounterFormModalProps) {
const [search, setSearch] = useState('')
const [pokemonId, setPokemonId] = useState(encounter?.pokemonId ?? 0)
@@ -52,6 +56,8 @@ export function RouteEncounterFormModal({
onClose={onClose}
onSubmit={handleSubmit}
isSubmitting={isSubmitting}
+ onDelete={onDelete}
+ isDeleting={isDeleting}
>
{!encounter && (
diff --git a/frontend/src/components/admin/RouteFormModal.tsx b/frontend/src/components/admin/RouteFormModal.tsx
index 14035af..1f6fb1b 100644
--- a/frontend/src/components/admin/RouteFormModal.tsx
+++ b/frontend/src/components/admin/RouteFormModal.tsx
@@ -1,4 +1,5 @@
import { type FormEvent, useState } from 'react'
+import { Link } from 'react-router-dom'
import { FormModal } from './FormModal'
import type { Route, CreateRouteInput, UpdateRouteInput } from '../../types'
@@ -8,9 +9,12 @@ interface RouteFormModalProps {
onSubmit: (data: CreateRouteInput | UpdateRouteInput) => void
onClose: () => void
isSubmitting?: boolean
+ onDelete?: () => void
+ isDeleting?: boolean
+ detailUrl?: string
}
-export function RouteFormModal({ route, nextOrder, onSubmit, onClose, isSubmitting }: RouteFormModalProps) {
+export function RouteFormModal({ route, nextOrder, onSubmit, onClose, isSubmitting, onDelete, isDeleting, detailUrl }: RouteFormModalProps) {
const [name, setName] = useState(route?.name ?? '')
const [order, setOrder] = useState(String(route?.order ?? nextOrder ?? 0))
const [pinwheelZone, setPinwheelZone] = useState(
@@ -32,6 +36,16 @@ export function RouteFormModal({ route, nextOrder, onSubmit, onClose, isSubmitti
onClose={onClose}
onSubmit={handleSubmit}
isSubmitting={isSubmitting}
+ onDelete={onDelete}
+ isDeleting={isDeleting}
+ headerExtra={detailUrl ? (
+
+ View Encounters
+
+ ) : undefined}
>
diff --git a/frontend/src/pages/admin/AdminEvolutions.tsx b/frontend/src/pages/admin/AdminEvolutions.tsx
index 416a7e3..d1c7e89 100644
--- a/frontend/src/pages/admin/AdminEvolutions.tsx
+++ b/frontend/src/pages/admin/AdminEvolutions.tsx
@@ -1,7 +1,6 @@
import { useState } from 'react'
import { AdminTable, type Column } from '../../components/admin/AdminTable'
import { EvolutionFormModal } from '../../components/admin/EvolutionFormModal'
-import { DeleteConfirmModal } from '../../components/admin/DeleteConfirmModal'
import {
useEvolutionList,
useCreateEvolution,
@@ -29,7 +28,6 @@ export function AdminEvolutions() {
const [showCreate, setShowCreate] = useState(false)
const [editing, setEditing] = useState
(null)
- const [deleting, setDeleting] = useState(null)
const columns: Column[] = [
{
@@ -57,25 +55,6 @@ export function AdminEvolutions() {
{ header: 'Trigger', accessor: (e) => e.trigger },
{ header: 'Level', accessor: (e) => e.minLevel ?? '-' },
{ header: 'Item', accessor: (e) => e.item ?? '-' },
- {
- header: 'Actions',
- accessor: (e) => (
-
-
-
-
- ),
- },
]
return (
@@ -123,6 +102,7 @@ export function AdminEvolutions() {
isLoading={isLoading}
emptyMessage="No evolutions found."
keyFn={(e) => e.id}
+ onRowClick={(e) => setEditing(e)}
/>
{totalPages > 1 && (
@@ -189,19 +169,11 @@ export function AdminEvolutions() {
}
onClose={() => setEditing(null)}
isSubmitting={updateEvolution.isPending}
- />
- )}
-
- {deleting && (
-
- deleteEvolution.mutate(deleting.id, {
- onSuccess: () => setDeleting(null),
+ onDelete={() =>
+ deleteEvolution.mutate(editing.id, {
+ onSuccess: () => setEditing(null),
})
}
- onCancel={() => setDeleting(null)}
isDeleting={deleteEvolution.isPending}
/>
)}
diff --git a/frontend/src/pages/admin/AdminGameDetail.tsx b/frontend/src/pages/admin/AdminGameDetail.tsx
index 6cd115f..11fa2e0 100644
--- a/frontend/src/pages/admin/AdminGameDetail.tsx
+++ b/frontend/src/pages/admin/AdminGameDetail.tsx
@@ -1,5 +1,5 @@
import { useState } from 'react'
-import { useParams, useNavigate, Link } from 'react-router-dom'
+import { useParams, Link } from 'react-router-dom'
import {
DndContext,
closestCenter,
@@ -17,7 +17,6 @@ import {
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { RouteFormModal } from '../../components/admin/RouteFormModal'
-import { DeleteConfirmModal } from '../../components/admin/DeleteConfirmModal'
import { BossBattleFormModal } from '../../components/admin/BossBattleFormModal'
import { BossTeamEditor } from '../../components/admin/BossTeamEditor'
import { useGame } from '../../hooks/useGames'
@@ -39,13 +38,9 @@ import type { CreateBossBattleInput, UpdateBossBattleInput } from '../../types/a
function SortableRouteRow({
route,
- onEdit,
- onDelete,
onClick,
}: {
route: GameRoute
- onEdit: (r: GameRoute) => void
- onDelete: (r: GameRoute) => void
onClick: (r: GameRoute) => void
}) {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } =
@@ -83,29 +78,12 @@ function SortableRouteRow({
{route.order} |
{route.name} |
-
- e.stopPropagation()}>
-
-
-
- |
)
}
export function AdminGameDetail() {
const { gameId } = useParams<{ gameId: string }>()
- const navigate = useNavigate()
const id = Number(gameId)
const { data: game, isLoading } = useGame(id)
@@ -121,10 +99,8 @@ export function AdminGameDetail() {
const [tab, setTab] = useState<'routes' | 'bosses'>('routes')
const [showCreate, setShowCreate] = useState(false)
const [editing, setEditing] = useState(null)
- const [deleting, setDeleting] = useState(null)
const [showCreateBoss, setShowCreateBoss] = useState(false)
const [editingBoss, setEditingBoss] = useState(null)
- const [deletingBoss, setDeletingBoss] = useState(null)
const [editingTeam, setEditingTeam] = useState(null)
const sensors = useSensors(
@@ -235,9 +211,6 @@ export function AdminGameDetail() {
Name
|
-
- Actions
- |
navigate(`/admin/games/${id}/routes/${r.id}`)}
+ onClick={(r) => setEditing(r)}
/>
))}
@@ -291,20 +262,13 @@ export function AdminGameDetail() {
}
onClose={() => setEditing(null)}
isSubmitting={updateRoute.isPending}
- />
- )}
-
- {deleting && (
-
- deleteRoute.mutate(deleting.id, {
- onSuccess: () => setDeleting(null),
+ onDelete={() =>
+ deleteRoute.mutate(editing.id, {
+ onSuccess: () => setEditing(null),
})
}
- onCancel={() => setDeleting(null)}
isDeleting={deleteRoute.isPending}
+ detailUrl={`/admin/games/${id}/routes/${editing.id}`}
/>
)}
>
@@ -358,14 +322,15 @@ export function AdminGameDetail() {
Team
|
-
- Actions
- |
{bosses.map((boss) => (
-
+
setEditingBoss(boss)}
+ >
| {boss.order} |
{boss.name} |
@@ -374,28 +339,6 @@ export function AdminGameDetail() {
| {boss.location} |
{boss.levelCap} |
{boss.pokemon.length} |
-
-
-
-
-
-
- |
))}
@@ -434,20 +377,16 @@ export function AdminGameDetail() {
}
onClose={() => setEditingBoss(null)}
isSubmitting={updateBoss.isPending}
- />
- )}
-
- {deletingBoss && (
-
- deleteBoss.mutate(deletingBoss.id, {
- onSuccess: () => setDeletingBoss(null),
+ onDelete={() =>
+ deleteBoss.mutate(editingBoss.id, {
+ onSuccess: () => setEditingBoss(null),
})
}
- onCancel={() => setDeletingBoss(null)}
isDeleting={deleteBoss.isPending}
+ onEditTeam={() => {
+ setEditingTeam(editingBoss)
+ setEditingBoss(null)
+ }}
/>
)}
diff --git a/frontend/src/pages/admin/AdminGames.tsx b/frontend/src/pages/admin/AdminGames.tsx
index d15de9d..c1e6298 100644
--- a/frontend/src/pages/admin/AdminGames.tsx
+++ b/frontend/src/pages/admin/AdminGames.tsx
@@ -1,8 +1,6 @@
import { useState } from 'react'
-import { useNavigate } from 'react-router-dom'
import { AdminTable, type Column } from '../../components/admin/AdminTable'
import { GameFormModal } from '../../components/admin/GameFormModal'
-import { DeleteConfirmModal } from '../../components/admin/DeleteConfirmModal'
import { useGames } from '../../hooks/useGames'
import { useCreateGame, useUpdateGame, useDeleteGame } from '../../hooks/useAdmin'
import { exportGames } from '../../api/admin'
@@ -10,7 +8,6 @@ import { downloadJson } from '../../utils/download'
import type { Game, CreateGameInput, UpdateGameInput } from '../../types'
export function AdminGames() {
- const navigate = useNavigate()
const { data: games = [], isLoading } = useGames()
const createGame = useCreateGame()
const updateGame = useUpdateGame()
@@ -18,7 +15,6 @@ export function AdminGames() {
const [showCreate, setShowCreate] = useState(false)
const [editing, setEditing] = useState(null)
- const [deleting, setDeleting] = useState(null)
const columns: Column[] = [
{ header: 'Name', accessor: (g) => g.name },
@@ -26,25 +22,6 @@ export function AdminGames() {
{ header: 'Region', accessor: (g) => g.region, sortKey: (g) => g.region },
{ header: 'Gen', accessor: (g) => g.generation, sortKey: (g) => g.generation },
{ header: 'Year', accessor: (g) => g.releaseYear ?? '-', sortKey: (g) => g.releaseYear ?? 0 },
- {
- header: 'Actions',
- accessor: (g) => (
- e.stopPropagation()}>
-
-
-
- ),
- },
]
return (
@@ -75,7 +52,7 @@ export function AdminGames() {
data={games}
isLoading={isLoading}
emptyMessage="No games yet. Add one to get started."
- onRowClick={(g) => navigate(`/admin/games/${g.id}`)}
+ onRowClick={(g) => setEditing(g)}
keyFn={(g) => g.id}
/>
@@ -102,20 +79,13 @@ export function AdminGames() {
}
onClose={() => setEditing(null)}
isSubmitting={updateGame.isPending}
- />
- )}
-
- {deleting && (
-
- deleteGame.mutate(deleting.id, {
- onSuccess: () => setDeleting(null),
+ onDelete={() =>
+ deleteGame.mutate(editing.id, {
+ onSuccess: () => setEditing(null),
})
}
- onCancel={() => setDeleting(null)}
isDeleting={deleteGame.isPending}
+ detailUrl={`/admin/games/${editing.id}`}
/>
)}
diff --git a/frontend/src/pages/admin/AdminPokemon.tsx b/frontend/src/pages/admin/AdminPokemon.tsx
index 050acc8..da466f5 100644
--- a/frontend/src/pages/admin/AdminPokemon.tsx
+++ b/frontend/src/pages/admin/AdminPokemon.tsx
@@ -2,7 +2,6 @@ import { useState } from 'react'
import { AdminTable, type Column } from '../../components/admin/AdminTable'
import { PokemonFormModal } from '../../components/admin/PokemonFormModal'
import { BulkImportModal } from '../../components/admin/BulkImportModal'
-import { DeleteConfirmModal } from '../../components/admin/DeleteConfirmModal'
import {
usePokemonList,
useCreatePokemon,
@@ -32,7 +31,6 @@ export function AdminPokemon() {
const [showCreate, setShowCreate] = useState(false)
const [showBulkImport, setShowBulkImport] = useState(false)
const [editing, setEditing] = useState
(null)
- const [deleting, setDeleting] = useState(null)
const columns: Column[] = [
{ header: 'Dex #', accessor: (p) => p.nationalDex, className: 'w-16' },
@@ -48,25 +46,6 @@ export function AdminPokemon() {
},
{ header: 'Name', accessor: (p) => p.name },
{ header: 'Types', accessor: (p) => p.types.join(', ') },
- {
- header: 'Actions',
- accessor: (p) => (
-
-
-
-
- ),
- },
]
return (
@@ -120,6 +99,7 @@ export function AdminPokemon() {
isLoading={isLoading}
emptyMessage="No pokemon found."
keyFn={(p) => p.id}
+ onRowClick={(p) => setEditing(p)}
/>
{/* Pagination */}
@@ -194,19 +174,11 @@ export function AdminPokemon() {
}
onClose={() => setEditing(null)}
isSubmitting={updatePokemon.isPending}
- />
- )}
-
- {deleting && (
-
- deletePokemon.mutate(deleting.id, {
- onSuccess: () => setDeleting(null),
+ onDelete={() =>
+ deletePokemon.mutate(editing.id, {
+ onSuccess: () => setEditing(null),
})
}
- onCancel={() => setDeleting(null)}
isDeleting={deletePokemon.isPending}
/>
)}
diff --git a/frontend/src/pages/admin/AdminRouteDetail.tsx b/frontend/src/pages/admin/AdminRouteDetail.tsx
index b11d7b2..a3a2263 100644
--- a/frontend/src/pages/admin/AdminRouteDetail.tsx
+++ b/frontend/src/pages/admin/AdminRouteDetail.tsx
@@ -2,7 +2,6 @@ import { useState } from 'react'
import { useParams, Link } from 'react-router-dom'
import { AdminTable, type Column } from '../../components/admin/AdminTable'
import { RouteEncounterFormModal } from '../../components/admin/RouteEncounterFormModal'
-import { DeleteConfirmModal } from '../../components/admin/DeleteConfirmModal'
import { useGame, useRoutePokemon } from '../../hooks/useGames'
import {
useAddRouteEncounter,
@@ -29,7 +28,6 @@ export function AdminRouteDetail() {
const [showCreate, setShowCreate] = useState(false)
const [editing, setEditing] = useState(null)
- const [deleting, setDeleting] = useState(null)
const route = game?.routes?.find((r) => r.id === rId)
@@ -54,25 +52,6 @@ export function AdminRouteDetail() {
accessor: (e) =>
e.minLevel === e.maxLevel ? `Lv ${e.minLevel}` : `Lv ${e.minLevel}-${e.maxLevel}`,
},
- {
- header: 'Actions',
- accessor: (e) => (
-
-
-
-
- ),
- },
]
return (
@@ -109,6 +88,7 @@ export function AdminRouteDetail() {
isLoading={isLoading}
emptyMessage="No pokemon assigned to this route yet."
keyFn={(e) => e.id}
+ onRowClick={(e) => setEditing(e)}
/>
{showCreate && (
@@ -134,19 +114,11 @@ export function AdminRouteDetail() {
}
onClose={() => setEditing(null)}
isSubmitting={updateEncounter.isPending}
- />
- )}
-
- {deleting && (
-
- removeEncounter.mutate(deleting.id, {
- onSuccess: () => setDeleting(null),
+ onDelete={() =>
+ removeEncounter.mutate(editing.id, {
+ onSuccess: () => setEditing(null),
})
}
- onCancel={() => setDeleting(null)}
isDeleting={removeEncounter.isPending}
/>
)}
diff --git a/frontend/src/pages/admin/AdminRuns.tsx b/frontend/src/pages/admin/AdminRuns.tsx
index c9fc4ab..50512f3 100644
--- a/frontend/src/pages/admin/AdminRuns.tsx
+++ b/frontend/src/pages/admin/AdminRuns.tsx
@@ -46,19 +46,6 @@ export function AdminRuns() {
accessor: (r) => new Date(r.startedAt).toLocaleDateString(),
sortKey: (r) => r.startedAt,
},
- {
- header: 'Actions',
- accessor: (r) => (
- e.stopPropagation()}>
-
-
- ),
- },
]
return (
@@ -73,6 +60,7 @@ export function AdminRuns() {
isLoading={runsLoading || gamesLoading}
emptyMessage="No runs yet."
keyFn={(r) => r.id}
+ onRowClick={(r) => setDeleting(r)}
/>
{deleting && (