Add hierarchical route grouping for multi-area locations

Locations like Mt. Moon (with 1F, B1F, B2F floors) are now grouped so
only one encounter can be logged per location group, enforcing Nuzlocke
first-encounter rules correctly.

- Add parent_route_id column with self-referential FK to routes table
- Add parent/children relationships on Route model
- Update games API to return hierarchical route structure
- Add validation in encounters API to prevent parent route encounters
  and duplicate encounters within sibling routes (409 conflict)
- Update frontend with collapsible RouteGroup component
- Auto-derive route groups from PokeAPI location/location-area structure
- Regenerate seed data with 70 parent routes and 315 child routes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Julian Tabel
2026-02-06 11:07:45 +01:00
parent b434ab52ae
commit 2aa60f0ace
17 changed files with 24876 additions and 23896 deletions

View File

@@ -1,19 +1,64 @@
---
# nuzlocke-tracker-u7i9
title: Combine sub-areas into single locations
status: todo
status: completed
type: feature
priority: normal
created_at: 2026-02-05T14:27:13Z
updated_at: 2026-02-05T14:27:13Z
updated_at: 2026-02-06T09:56:11Z
parent: nuzlocke-tracker-f5ob
---
Some game locations have multiple encounter tables (e.g. Mount Moon 1F, Mount Moon B1F, Mount Moon B2F) but are treated as a single location for Nuzlocke first-encounter rules.
Some game locations have multiple encounter tables (e.g. Mount Moon 1F, Mount Moon B1F, Mount Moon B2F) but are treated as a single location for Nuzlocke first-encounter rules.
Needs a concept of 'location groups' — a parent location that contains multiple sub-areas, each with their own encounter table. For Nuzlocke purposes, the first encounter in *any* sub-area of the group counts as that location's encounter.
## Implementation Summary
Added hierarchical route grouping so locations like Mt. Moon (with floors 1F, B1F, B2F) are treated as a single location for Nuzlocke first-encounter rules. The first encounter in ANY sub-area counts as that location's encounter.
### Changes Made
1. **Database Migration** (`c3d4e5f6a7b8_add_route_grouping.py`)
- Added nullable `parent_route_id` column with self-referential FK (CASCADE delete)
2. **Backend Model** (`backend/src/app/models/route.py`)
- Added `parent_route_id` field
- Added self-referential `parent` and `children` relationships
3. **Backend Schemas** (`backend/src/app/schemas/game.py`)
- Added `parent_route_id` to `RouteResponse`, `RouteCreate`, `RouteUpdate`
- Added `RouteWithChildrenResponse` with nested children
4. **Backend API - Games** (`backend/src/app/api/games.py`)
- Updated `list_game_routes` to support hierarchical response (default) or flat list (`?flat=true`)
5. **Backend API - Encounters** (`backend/src/app/api/encounters.py`)
- Added validation: cannot create encounter on parent route (400 error)
- Added validation: cannot create encounter if sibling already has one (409 error)
6. **Frontend Types** (`frontend/src/types/game.ts`)
- Added `parentRouteId` to `Route` interface
- Added `RouteWithChildren` interface
7. **Frontend Page** (`frontend/src/pages/RunEncounters.tsx`)
- Added `organizeRoutes()` helper to build hierarchy from flat list
- Added `getGroupEncounter()` to check if any child has an encounter
- Updated progress counter to count groups (not individual sub-routes)
- Added collapsible `RouteGroup` component
- Sibling routes are disabled after one has an encounter
8. **Seed Data** (`backend/src/app/seeds/fetch_pokeapi.py`)
- Updated to automatically detect grouped locations from PokeAPI's location/location-area structure
- Parent routes have empty encounters; children have actual encounters
- 70 parent routes with 315 child routes across all games
9. **Seed Loader** (`backend/src/app/seeds/loader.py`, `run.py`)
- Updated `upsert_routes` to handle hierarchical structure with parent_route_id
- Updated seed runner to process child route encounters
## Considerations
- Data model: add a parent_route_id or location_group concept to the Route model
- Seed data: identify which routes should be grouped (may need manual curation per game)
- Encounter tracking: when logging an encounter in a sub-area, mark the whole group as visited
- Route list UI: show grouped locations as collapsible sections
- [x] Data model: add a parent_route_id or location_group concept to the Route model
- [x] Seed data: identify which routes should be grouped (automatically derived from PokeAPI location/area structure)
- [x] Encounter tracking: when logging an encounter in a sub-area, mark the whole group as visited
- [x] Route list UI: show grouped locations as collapsible sections