Add click-to-edit pattern across all admin tables

Replace Actions columns with clickable rows that open edit modals
directly. Delete is now an inline two-step confirm button in the
edit modal footer. Games modal links to routes/bosses detail,
route modal links to encounters, and boss modal has an Edit Team button.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 13:44:38 +01:00
parent 76d69dfaf1
commit f09b8213fd
14 changed files with 145 additions and 228 deletions

View File

@@ -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<Game | null>(null)
const [deleting, setDeleting] = useState<Game | null>(null)
const columns: Column<Game>[] = [
{ 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) => (
<div className="flex gap-2" onClick={(e) => e.stopPropagation()}>
<button
onClick={() => setEditing(g)}
className="text-blue-600 hover:text-blue-800 dark:text-blue-400 text-sm"
>
Edit
</button>
<button
onClick={() => setDeleting(g)}
className="text-red-600 hover:text-red-800 dark:text-red-400 text-sm"
>
Delete
</button>
</div>
),
},
]
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 && (
<DeleteConfirmModal
title={`Delete ${deleting.name}?`}
message="This will permanently delete the game and all its routes. Games with existing runs cannot be deleted."
onConfirm={() =>
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}`}
/>
)}
</div>