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

@@ -14,6 +14,9 @@ class Route(Base):
name: Mapped[str] = mapped_column(String(100))
game_id: Mapped[int] = mapped_column(ForeignKey("games.id"), index=True)
order: Mapped[int] = mapped_column(SmallInteger)
parent_route_id: Mapped[int | None] = mapped_column(
ForeignKey("routes.id", ondelete="CASCADE"), index=True, default=None
)
game: Mapped["Game"] = relationship(back_populates="routes")
route_encounters: Mapped[list["RouteEncounter"]] = relationship(
@@ -21,5 +24,13 @@ class Route(Base):
)
encounters: Mapped[list["Encounter"]] = relationship(back_populates="route")
# Self-referential relationships for route grouping
parent: Mapped["Route | None"] = relationship(
back_populates="children", remote_side=[id]
)
children: Mapped[list["Route"]] = relationship(
back_populates="parent", cascade="all, delete-orphan"
)
def __repr__(self) -> str:
return f"<Route(id={self.id}, name='{self.name}')>"