Add prev/next and dropdown route navigation to route detail page

Reduces navigation depth when working with route encounters by adding
prev/next buttons and a route dropdown selector to AdminRouteDetail.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 20:02:59 +01:00
parent b29b716fe5
commit 3d515f0e4e
2 changed files with 61 additions and 11 deletions

View File

@@ -1,5 +1,5 @@
import { useState } from 'react'
import { useParams, Link } from 'react-router-dom'
import { useMemo, useState } from 'react'
import { useParams, Link, useNavigate } from 'react-router-dom'
import { AdminTable, type Column } from '../../components/admin/AdminTable'
import { RouteEncounterFormModal } from '../../components/admin/RouteEncounterFormModal'
import { useGame, useRoutePokemon } from '../../hooks/useGames'
@@ -18,6 +18,7 @@ export function AdminRouteDetail() {
const { gameId, routeId } = useParams<{ gameId: string; routeId: string }>()
const gId = Number(gameId)
const rId = Number(routeId)
const navigate = useNavigate()
const { data: game } = useGame(gId)
const { data: encounters = [], isLoading } = useRoutePokemon(rId, gId)
@@ -29,7 +30,17 @@ export function AdminRouteDetail() {
const [showCreate, setShowCreate] = useState(false)
const [editing, setEditing] = useState<RouteEncounterDetail | null>(null)
const route = game?.routes?.find((r) => r.id === rId)
const sortedRoutes = useMemo(
() => [...(game?.routes ?? [])].sort((a, b) => a.order - b.order),
[game?.routes],
)
const currentIndex = sortedRoutes.findIndex((r) => r.id === rId)
const route = currentIndex >= 0 ? sortedRoutes[currentIndex] : undefined
const prevRoute = currentIndex > 0 ? sortedRoutes[currentIndex - 1] : undefined
const nextRoute =
currentIndex >= 0 && currentIndex < sortedRoutes.length - 1
? sortedRoutes[currentIndex + 1]
: undefined
const columns: Column<RouteEncounterDetail>[] = [
{
@@ -65,15 +76,53 @@ export function AdminRouteDetail() {
{game?.name ?? '...'}
</Link>
{' / '}
<span className="text-gray-900 dark:text-gray-100">
{route?.name ?? '...'}
</span>
<select
className="text-gray-900 dark:text-gray-100 bg-transparent font-medium cursor-pointer hover:underline border-none p-0 text-sm"
value={rId}
onChange={(e) => navigate(`/admin/games/${gId}/routes/${e.target.value}`)}
>
{sortedRoutes.map((r) => (
<option key={r.id} value={r.id}>
{r.name}
</option>
))}
</select>
</nav>
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold">
{route?.name ?? 'Route'} - Pokemon ({encounters.length})
</h2>
<div className="flex items-center gap-2">
<h2 className="text-xl font-semibold">
{route?.name ?? 'Route'} - Pokemon ({encounters.length})
</h2>
<div className="flex items-center gap-1 ml-2">
{prevRoute ? (
<Link
to={`/admin/games/${gId}/routes/${prevRoute.id}`}
className="px-2 py-1 text-sm rounded border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700"
title={prevRoute.name}
>
&larr; Prev
</Link>
) : (
<span className="px-2 py-1 text-sm rounded border border-gray-200 dark:border-gray-700 text-gray-300 dark:text-gray-600 cursor-not-allowed">
&larr; Prev
</span>
)}
{nextRoute ? (
<Link
to={`/admin/games/${gId}/routes/${nextRoute.id}`}
className="px-2 py-1 text-sm rounded border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700"
title={nextRoute.name}
>
Next &rarr;
</Link>
) : (
<span className="px-2 py-1 text-sm rounded border border-gray-200 dark:border-gray-700 text-gray-300 dark:text-gray-600 cursor-not-allowed">
Next &rarr;
</span>
)}
</div>
</div>
<button
onClick={() => setShowCreate(true)}
className="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700"