diff --git a/.beans/nuzlocke-tracker-704x--run-progression-dividers-main-story-endgame.md b/.beans/nuzlocke-tracker-704x--run-progression-dividers-main-story-endgame.md
new file mode 100644
index 0000000..3e8be75
--- /dev/null
+++ b/.beans/nuzlocke-tracker-704x--run-progression-dividers-main-story-endgame.md
@@ -0,0 +1,11 @@
+---
+# nuzlocke-tracker-704x
+title: Run progression dividers (main story / endgame)
+status: completed
+type: feature
+priority: normal
+created_at: 2026-02-08T13:46:12Z
+updated_at: 2026-02-08T13:48:28Z
+---
+
+Add section field to boss battles to enable visual dividers between game progression phases (Main Story, Endgame, etc.) in both admin and run views.
\ No newline at end of file
diff --git a/backend/src/app/alembic/versions/f5a6b7c8d9e0_add_section_to_boss_battles.py b/backend/src/app/alembic/versions/f5a6b7c8d9e0_add_section_to_boss_battles.py
new file mode 100644
index 0000000..c21a956
--- /dev/null
+++ b/backend/src/app/alembic/versions/f5a6b7c8d9e0_add_section_to_boss_battles.py
@@ -0,0 +1,26 @@
+"""add section to boss battles
+
+Revision ID: f5a6b7c8d9e0
+Revises: e4f5a6b7c8d9
+Create Date: 2026-02-08 20:00:00.000000
+
+"""
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+
+# revision identifiers, used by Alembic.
+revision: str = 'f5a6b7c8d9e0'
+down_revision: Union[str, Sequence[str], None] = 'e4f5a6b7c8d9'
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+ op.add_column('boss_battles', sa.Column('section', sa.String(100), nullable=True))
+
+
+def downgrade() -> None:
+ op.drop_column('boss_battles', 'section')
diff --git a/backend/src/app/models/boss_battle.py b/backend/src/app/models/boss_battle.py
index b4012f2..03b5013 100644
--- a/backend/src/app/models/boss_battle.py
+++ b/backend/src/app/models/boss_battle.py
@@ -24,6 +24,7 @@ class BossBattle(Base):
ForeignKey("routes.id"), index=True, default=None
)
location: Mapped[str] = mapped_column(String(200))
+ section: Mapped[str | None] = mapped_column(String(100), default=None)
sprite_url: Mapped[str | None] = mapped_column(String(500))
version_group: Mapped["VersionGroup"] = relationship(
diff --git a/backend/src/app/schemas/boss.py b/backend/src/app/schemas/boss.py
index 3ad605b..61476f3 100644
--- a/backend/src/app/schemas/boss.py
+++ b/backend/src/app/schemas/boss.py
@@ -23,6 +23,7 @@ class BossBattleResponse(CamelModel):
order: int
after_route_id: int | None
location: str
+ section: str | None
sprite_url: str | None
pokemon: list[BossPokemonResponse] = []
@@ -48,6 +49,7 @@ class BossBattleCreate(CamelModel):
order: int
after_route_id: int | None = None
location: str
+ section: str | None = None
sprite_url: str | None = None
@@ -60,6 +62,7 @@ class BossBattleUpdate(CamelModel):
order: int | None = None
after_route_id: int | None = None
location: str | None = None
+ section: str | None = None
sprite_url: str | None = None
@@ -75,6 +78,15 @@ class BossResultCreate(CamelModel):
attempts: int = 1
+class BossReorderItem(CamelModel):
+ id: int
+ order: int
+
+
+class BossReorderRequest(CamelModel):
+ bosses: list[BossReorderItem]
+
+
class BossResultUpdate(CamelModel):
result: str | None = None
attempts: int | None = None
diff --git a/frontend/src/components/admin/BossBattleFormModal.tsx b/frontend/src/components/admin/BossBattleFormModal.tsx
index 88ec4fc..a89f105 100644
--- a/frontend/src/components/admin/BossBattleFormModal.tsx
+++ b/frontend/src/components/admin/BossBattleFormModal.tsx
@@ -43,6 +43,7 @@ export function BossBattleFormModal({
const [order, setOrder] = useState(String(boss?.order ?? nextOrder))
const [afterRouteId, setAfterRouteId] = useState(String(boss?.afterRouteId ?? ''))
const [location, setLocation] = useState(boss?.location ?? '')
+ const [section, setSection] = useState(boss?.section ?? '')
const [spriteUrl, setSpriteUrl] = useState(boss?.spriteUrl ?? '')
const handleSubmit = (e: FormEvent) => {
@@ -56,6 +57,7 @@ export function BossBattleFormModal({
order: Number(order),
afterRouteId: afterRouteId ? Number(afterRouteId) : null,
location,
+ section: section || null,
spriteUrl: spriteUrl || null,
})
}
@@ -146,6 +148,17 @@ export function BossBattleFormModal({
+
+
+ setSection(e.target.value)}
+ placeholder="e.g. Main Story, Endgame"
+ className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600"
+ />
+
+