diff --git a/.beans/nuzlocke-tracker-kghn--child-route-management-from-route-detail.md b/.beans/nuzlocke-tracker-kghn--child-route-management-from-route-detail.md index dd0cbc5..20e8db4 100644 --- a/.beans/nuzlocke-tracker-kghn--child-route-management-from-route-detail.md +++ b/.beans/nuzlocke-tracker-kghn--child-route-management-from-route-detail.md @@ -1,11 +1,11 @@ --- # nuzlocke-tracker-kghn title: Child route management from route detail -status: todo +status: completed type: feature priority: low created_at: 2026-02-08T12:33:53Z -updated_at: 2026-02-08T12:33:53Z +updated_at: 2026-02-08T19:31:57Z parent: nuzlocke-tracker-iu5b --- diff --git a/frontend/src/pages/admin/AdminRouteDetail.tsx b/frontend/src/pages/admin/AdminRouteDetail.tsx index a15a8d9..f166fd2 100644 --- a/frontend/src/pages/admin/AdminRouteDetail.tsx +++ b/frontend/src/pages/admin/AdminRouteDetail.tsx @@ -2,16 +2,22 @@ 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 { RouteFormModal } from '../../components/admin/RouteFormModal' +import { DeleteConfirmModal } from '../../components/admin/DeleteConfirmModal' import { useGame, useRoutePokemon } from '../../hooks/useGames' import { useAddRouteEncounter, useUpdateRouteEncounter, useRemoveRouteEncounter, + useCreateRoute, + useDeleteRoute, } from '../../hooks/useAdmin' import type { + Route, RouteEncounterDetail, CreateRouteEncounterInput, UpdateRouteEncounterInput, + CreateRouteInput, } from '../../types' export function AdminRouteDetail() { @@ -26,9 +32,13 @@ export function AdminRouteDetail() { const addEncounter = useAddRouteEncounter(rId) const updateEncounter = useUpdateRouteEncounter(rId) const removeEncounter = useRemoveRouteEncounter(rId) + const createRoute = useCreateRoute(gId) + const deleteRoute = useDeleteRoute(gId) const [showCreate, setShowCreate] = useState(false) const [editing, setEditing] = useState(null) + const [showCreateChild, setShowCreateChild] = useState(false) + const [deletingChild, setDeletingChild] = useState(null) const sortedRoutes = useMemo( () => [...(game?.routes ?? [])].sort((a, b) => a.order - b.order), @@ -42,6 +52,15 @@ export function AdminRouteDetail() { ? sortedRoutes[currentIndex + 1] : undefined + const childRoutes = useMemo( + () => (game?.routes ?? []).filter((r) => r.parentRouteId === rId).sort((a, b) => a.order - b.order), + [game?.routes, rId], + ) + + const nextChildOrder = childRoutes.length > 0 + ? Math.max(...childRoutes.map((r) => r.order)) + 1 + : (route?.order ?? 0) * 10 + 1 + const columns: Column[] = [ { header: 'Pokemon', @@ -171,6 +190,69 @@ export function AdminRouteDetail() { isDeleting={removeEncounter.isPending} /> )} + + {/* Sub-areas */} +
+
+

Sub-areas ({childRoutes.length})

+ +
+ {childRoutes.length === 0 ? ( +

No sub-areas for this route.

+ ) : ( +
+ {childRoutes.map((child) => ( +
+ + {child.name} + + +
+ ))} +
+ )} +
+ + {showCreateChild && ( + + createRoute.mutate( + { ...data, parentRouteId: rId } as CreateRouteInput, + { onSuccess: () => setShowCreateChild(false) }, + ) + } + onClose={() => setShowCreateChild(false)} + isSubmitting={createRoute.isPending} + /> + )} + + {deletingChild && ( + + deleteRoute.mutate(deletingChild.id, { + onSuccess: () => setDeletingChild(null), + }) + } + onCancel={() => setDeletingChild(null)} + isDeleting={deleteRoute.isPending} + /> + )} ) } diff --git a/frontend/src/types/admin.ts b/frontend/src/types/admin.ts index a633ff8..f657dda 100644 --- a/frontend/src/types/admin.ts +++ b/frontend/src/types/admin.ts @@ -19,6 +19,7 @@ export interface UpdateGameInput { export interface CreateRouteInput { name: string order: number + parentRouteId?: number | null pinwheelZone?: number | null }