import { useState } from 'react'
import { useParams, useNavigate, Link } from 'react-router-dom'
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
type DragEndEvent,
} from '@dnd-kit/core'
import {
SortableContext,
sortableKeyboardCoordinates,
useSortable,
verticalListSortingStrategy,
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { RouteFormModal } from '../../components/admin/RouteFormModal'
import { DeleteConfirmModal } from '../../components/admin/DeleteConfirmModal'
import { useGame } from '../../hooks/useGames'
import {
useCreateRoute,
useUpdateRoute,
useDeleteRoute,
useReorderRoutes,
} from '../../hooks/useAdmin'
import type { Route as GameRoute, CreateRouteInput, UpdateRouteInput } from '../../types'
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 } =
useSortable({ id: route.id })
const style = {
transform: CSS.Transform.toString(transform),
transition,
}
return (
onClick(route)}
>
|
|
{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)
const createRoute = useCreateRoute(id)
const updateRoute = useUpdateRoute(id)
const deleteRoute = useDeleteRoute(id)
const reorderRoutes = useReorderRoutes(id)
const [showCreate, setShowCreate] = useState(false)
const [editing, setEditing] = useState(null)
const [deleting, setDeleting] = useState(null)
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
)
if (isLoading) return Loading...
if (!game) return Game not found
const routes = game.routes ?? []
const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event
if (!over || active.id === over.id) return
const oldIndex = routes.findIndex((r) => r.id === active.id)
const newIndex = routes.findIndex((r) => r.id === over.id)
if (oldIndex === -1 || newIndex === -1) return
// Build new order assignments based on rearranged positions
const reordered = [...routes]
const [moved] = reordered.splice(oldIndex, 1)
reordered.splice(newIndex, 0, moved)
const newOrders = reordered.map((r, i) => ({
id: r.id,
order: i + 1,
}))
reorderRoutes.mutate(newOrders)
}
return (
{game.name}
{game.region} · Gen {game.generation}
{game.releaseYear ? ` \u00b7 ${game.releaseYear}` : ''}
Routes ({routes.length})
{routes.length === 0 ? (
No routes yet. Add one to get started.
) : (
|
Order
|
Name
|
Actions
|
r.id)}
strategy={verticalListSortingStrategy}
>
{routes.map((route) => (
navigate(`/admin/games/${id}/routes/${r.id}`)}
/>
))}
)}
{showCreate && (
0 ? Math.max(...routes.map((r) => r.order)) + 1 : 1}
onSubmit={(data) =>
createRoute.mutate(data as CreateRouteInput, {
onSuccess: () => setShowCreate(false),
})
}
onClose={() => setShowCreate(false)}
isSubmitting={createRoute.isPending}
/>
)}
{editing && (
updateRoute.mutate(
{ routeId: editing.id, data: data as UpdateRouteInput },
{ onSuccess: () => setEditing(null) },
)
}
onClose={() => setEditing(null)}
isSubmitting={updateRoute.isPending}
/>
)}
{deleting && (
deleteRoute.mutate(deleting.id, {
onSuccess: () => setDeleting(null),
})
}
onCancel={() => setDeleting(null)}
isDeleting={deleteRoute.isPending}
/>
)}
)
}