Compare commits

..

2 Commits

Author SHA1 Message Date
c8dd4414b5 Mark bean rb0p as completed
Some checks failed
CI / backend-lint (push) Successful in 14s
CI / actions-lint (push) Failing after 6s
CI / frontend-lint (push) Successful in 22s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 18:17:54 +01:00
8cfa074ea6 Migrate pre-commit hooks from pre-commit to prek
Replace the Python-based pre-commit framework with prek (Rust) for
faster hook execution. Convert .pre-commit-config.yaml to prek.toml,
remove pre-commit from dev dependencies, and apply ruff auto-fixes
(UP037: remove unnecessary string quotes in type annotations).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 18:17:23 +01:00
18 changed files with 94 additions and 82 deletions

View File

@@ -1,11 +1,11 @@
--- ---
# nuzlocke-tracker-l12w # nuzlocke-tracker-l12w
title: Add condition badges for boss Pokemon mechanics title: Add condition badges for boss Pokemon mechanics
status: in-progress status: completed
type: feature type: feature
priority: normal priority: normal
created_at: 2026-02-16T20:11:02Z created_at: 2026-02-16T20:11:02Z
updated_at: 2026-02-16T20:11:52Z updated_at: 2026-02-16T20:17:40Z
--- ---
Add visible badges on boss Pokemon that have mechanic-related conditions (Mega Evolution, Gigantamax, Dynamax, Terastallize). Create a ConditionBadge component following the EncounterMethodBadge pattern and integrate it into BossDefeatModal and BossTeamPreview. Add visible badges on boss Pokemon that have mechanic-related conditions (Mega Evolution, Gigantamax, Dynamax, Terastallize). Create a ConditionBadge component following the EncounterMethodBadge pattern and integrate it into BossDefeatModal and BossTeamPreview.

View File

@@ -1,19 +1,18 @@
--- ---
# nuzlocke-tracker-rb0p # nuzlocke-tracker-rb0p
title: Implement pre-commit hooks for linting title: Implement pre-commit hooks for linting
status: todo status: completed
type: task type: task
priority: high priority: high
created_at: 2026-02-10T12:05:39Z created_at: 2026-02-10T12:05:39Z
updated_at: 2026-02-10T12:05:39Z updated_at: 2026-02-17T17:17:32Z
--- ---
Set up pre-commit hooks to automatically run linting before every commit, catching issues before they reach the pipeline. Set up pre-commit hooks to automatically run linting before every commit, catching issues before they reach the pipeline.
## Checklist ## Checklist
- [ ] Install and configure a pre-commit framework (e.g. `pre-commit` for Python, `husky` + `lint-staged` for the frontend, or a unified approach) - [x] Install and configure a pre-commit framework — migrated from `pre-commit` to `prek` (Rust)
- [ ] Add backend hook: `ruff check` + `ruff format --check` on staged Python files - [x] Add backend hook: `ruff check --fix` + `ruff format` on staged Python files
- [ ] Add frontend hook: `eslint` + `tsc` on staged TS/TSX files - [x] Add frontend hook: `oxlint`, `oxfmt --check`, and `tsc -b` on staged TS/TSX files
- [ ] Update CI workflow to replace lint checks with test runs once the test suite is in place - [x] Document the pre-commit setup in CLAUDE.md
- [ ] Document the pre-commit setup in CLAUDE.md or DEPLOYMENT.md

View File

@@ -1,34 +0,0 @@
repos:
# Backend (Python) — ruff linting + formatting
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.0
hooks:
- id: ruff
args: [--fix]
files: ^backend/
- id: ruff-format
files: ^backend/
# Frontend (TypeScript/React) — local hooks using project node_modules
- repo: local
hooks:
- id: oxlint
name: oxlint
entry: npx oxlint -c frontend/.oxlintrc.json
language: system
files: ^frontend/src/.*\.(ts|tsx)$
pass_filenames: true
- id: oxfmt
name: oxfmt
entry: npx oxfmt --check --config frontend/.oxfmtrc.json
language: system
files: ^frontend/src/.*\.(ts|tsx)$
pass_filenames: true
- id: tsc
name: tsc
entry: bash -c 'cd frontend && npx tsc -b'
language: system
files: ^frontend/src/.*\.(ts|tsx)$
pass_filenames: false

View File

@@ -10,9 +10,9 @@
# Pre-commit Hooks # Pre-commit Hooks
This project uses [pre-commit](https://pre-commit.com/) to run linting and formatting checks before each commit. This project uses [prek](https://prek.j178.dev/) (Rust-based pre-commit framework) to run linting and formatting checks before each commit.
**Setup:** `pip install pre-commit && pre-commit install` **Setup:** `prek install`
**Hooks configured:** **Hooks configured:**
- **Backend:** `ruff check --fix` and `ruff format` on Python files under `backend/` - **Backend:** `ruff check --fix` and `ruff format` on Python files under `backend/`

View File

@@ -19,7 +19,7 @@ dependencies = [
dev = [ dev = [
"ruff==0.15.0", "ruff==0.15.0",
"ty==0.0.17", "ty==0.0.17",
"pre-commit==4.5.1",
"pytest==9.0.2", "pytest==9.0.2",
"pytest-asyncio==1.3.0", "pytest-asyncio==1.3.0",
"httpx==0.28.1", "httpx==0.28.1",

View File

@@ -37,10 +37,10 @@ class BossBattle(Base):
ForeignKey("games.id"), index=True, default=None ForeignKey("games.id"), index=True, default=None
) )
version_group: Mapped["VersionGroup"] = relationship(back_populates="boss_battles") version_group: Mapped[VersionGroup] = relationship(back_populates="boss_battles")
after_route: Mapped["Route | None"] = relationship() after_route: Mapped[Route | None] = relationship()
game: Mapped["Game | None"] = relationship() game: Mapped[Game | None] = relationship()
pokemon: Mapped[list["BossPokemon"]] = relationship( pokemon: Mapped[list[BossPokemon]] = relationship(
back_populates="boss_battle", cascade="all, delete-orphan" back_populates="boss_battle", cascade="all, delete-orphan"
) )

View File

@@ -16,8 +16,8 @@ class BossPokemon(Base):
order: Mapped[int] = mapped_column(SmallInteger) order: Mapped[int] = mapped_column(SmallInteger)
condition_label: Mapped[str | None] = mapped_column(String(100)) condition_label: Mapped[str | None] = mapped_column(String(100))
boss_battle: Mapped["BossBattle"] = relationship(back_populates="pokemon") boss_battle: Mapped[BossBattle] = relationship(back_populates="pokemon")
pokemon: Mapped["Pokemon"] = relationship() pokemon: Mapped[Pokemon] = relationship()
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<BossPokemon(id={self.id}, boss_battle_id={self.boss_battle_id}, pokemon_id={self.pokemon_id})>" return f"<BossPokemon(id={self.id}, boss_battle_id={self.boss_battle_id}, pokemon_id={self.pokemon_id})>"

View File

@@ -23,8 +23,8 @@ class BossResult(Base):
attempts: Mapped[int] = mapped_column(SmallInteger, default=1) attempts: Mapped[int] = mapped_column(SmallInteger, default=1)
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
run: Mapped["NuzlockeRun"] = relationship(back_populates="boss_results") run: Mapped[NuzlockeRun] = relationship(back_populates="boss_results")
boss_battle: Mapped["BossBattle"] = relationship() boss_battle: Mapped[BossBattle] = relationship()
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<BossResult(id={self.id}, run_id={self.run_id}, boss_battle_id={self.boss_battle_id}, result='{self.result}')>" return f"<BossResult(id={self.id}, run_id={self.run_id}, boss_battle_id={self.boss_battle_id}, result='{self.result}')>"

View File

@@ -28,12 +28,12 @@ class Encounter(Base):
DateTime(timezone=True), server_default=func.now() DateTime(timezone=True), server_default=func.now()
) )
run: Mapped["NuzlockeRun"] = relationship(back_populates="encounters") run: Mapped[NuzlockeRun] = relationship(back_populates="encounters")
route: Mapped["Route"] = relationship(back_populates="encounters") route: Mapped[Route] = relationship(back_populates="encounters")
pokemon: Mapped["Pokemon"] = relationship( pokemon: Mapped[Pokemon] = relationship(
foreign_keys=[pokemon_id], back_populates="encounters" foreign_keys=[pokemon_id], back_populates="encounters"
) )
current_pokemon: Mapped["Pokemon | None"] = relationship( current_pokemon: Mapped[Pokemon | None] = relationship(
foreign_keys=[current_pokemon_id] foreign_keys=[current_pokemon_id]
) )

View File

@@ -19,8 +19,8 @@ class Evolution(Base):
) # catch-all for other conditions ) # catch-all for other conditions
region: Mapped[str | None] = mapped_column(String(30)) region: Mapped[str | None] = mapped_column(String(30))
from_pokemon: Mapped["Pokemon"] = relationship(foreign_keys=[from_pokemon_id]) from_pokemon: Mapped[Pokemon] = relationship(foreign_keys=[from_pokemon_id])
to_pokemon: Mapped["Pokemon"] = relationship(foreign_keys=[to_pokemon_id]) to_pokemon: Mapped[Pokemon] = relationship(foreign_keys=[to_pokemon_id])
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<Evolution(id={self.id}, from={self.from_pokemon_id}, to={self.to_pokemon_id}, trigger='{self.trigger}')>" return f"<Evolution(id={self.id}, from={self.from_pokemon_id}, to={self.to_pokemon_id}, trigger='{self.trigger}')>"

View File

@@ -22,8 +22,8 @@ class Game(Base):
ForeignKey("version_groups.id"), index=True ForeignKey("version_groups.id"), index=True
) )
version_group: Mapped["VersionGroup | None"] = relationship(back_populates="games") version_group: Mapped[VersionGroup | None] = relationship(back_populates="games")
runs: Mapped[list["NuzlockeRun"]] = relationship(back_populates="game") runs: Mapped[list[NuzlockeRun]] = relationship(back_populates="game")
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<Game(id={self.id}, name='{self.name}')>" return f"<Game(id={self.id}, name='{self.name}')>"

View File

@@ -23,7 +23,7 @@ class Genlocke(Base):
DateTime(timezone=True), server_default=func.now() DateTime(timezone=True), server_default=func.now()
) )
legs: Mapped[list["GenlockeLeg"]] = relationship( legs: Mapped[list[GenlockeLeg]] = relationship(
back_populates="genlocke", order_by="GenlockeLeg.leg_order" back_populates="genlocke", order_by="GenlockeLeg.leg_order"
) )
@@ -45,6 +45,6 @@ class GenlockeLeg(Base):
leg_order: Mapped[int] = mapped_column(SmallInteger) leg_order: Mapped[int] = mapped_column(SmallInteger)
retired_pokemon_ids: Mapped[list[int] | None] = mapped_column(JSONB, default=None) retired_pokemon_ids: Mapped[list[int] | None] = mapped_column(JSONB, default=None)
genlocke: Mapped["Genlocke"] = relationship(back_populates="legs") genlocke: Mapped[Genlocke] = relationship(back_populates="legs")
game: Mapped["Game"] = relationship() game: Mapped[Game] = relationship()
run: Mapped["NuzlockeRun | None"] = relationship() run: Mapped[NuzlockeRun | None] = relationship()

View File

@@ -24,9 +24,9 @@ class NuzlockeRun(Base):
hof_encounter_ids: Mapped[list[int] | None] = mapped_column(JSONB, default=None) hof_encounter_ids: Mapped[list[int] | None] = mapped_column(JSONB, default=None)
naming_scheme: Mapped[str | None] = mapped_column(String(50), nullable=True) naming_scheme: Mapped[str | None] = mapped_column(String(50), nullable=True)
game: Mapped["Game"] = relationship(back_populates="runs") game: Mapped[Game] = relationship(back_populates="runs")
encounters: Mapped[list["Encounter"]] = relationship(back_populates="run") encounters: Mapped[list[Encounter]] = relationship(back_populates="run")
boss_results: Mapped[list["BossResult"]] = relationship(back_populates="run") boss_results: Mapped[list[BossResult]] = relationship(back_populates="run")
def __repr__(self) -> str: def __repr__(self) -> str:
return ( return (

View File

@@ -15,10 +15,10 @@ class Pokemon(Base):
types: Mapped[list[str]] = mapped_column(ARRAY(String(20))) types: Mapped[list[str]] = mapped_column(ARRAY(String(20)))
sprite_url: Mapped[str | None] = mapped_column(String(500)) sprite_url: Mapped[str | None] = mapped_column(String(500))
route_encounters: Mapped[list["RouteEncounter"]] = relationship( route_encounters: Mapped[list[RouteEncounter]] = relationship(
back_populates="pokemon" back_populates="pokemon"
) )
encounters: Mapped[list["Encounter"]] = relationship( encounters: Mapped[list[Encounter]] = relationship(
foreign_keys="[Encounter.pokemon_id]", back_populates="pokemon" foreign_keys="[Encounter.pokemon_id]", back_populates="pokemon"
) )

View File

@@ -23,17 +23,17 @@ class Route(Base):
) )
pinwheel_zone: Mapped[int | None] = mapped_column(SmallInteger, default=None) pinwheel_zone: Mapped[int | None] = mapped_column(SmallInteger, default=None)
version_group: Mapped["VersionGroup"] = relationship(back_populates="routes") version_group: Mapped[VersionGroup] = relationship(back_populates="routes")
route_encounters: Mapped[list["RouteEncounter"]] = relationship( route_encounters: Mapped[list[RouteEncounter]] = relationship(
back_populates="route", cascade="all, delete-orphan" back_populates="route", cascade="all, delete-orphan"
) )
encounters: Mapped[list["Encounter"]] = relationship(back_populates="route") encounters: Mapped[list[Encounter]] = relationship(back_populates="route")
# Self-referential relationships for route grouping # Self-referential relationships for route grouping
parent: Mapped["Route | None"] = relationship( parent: Mapped[Route | None] = relationship(
back_populates="children", remote_side=[id] back_populates="children", remote_side=[id]
) )
children: Mapped[list["Route"]] = relationship( children: Mapped[list[Route]] = relationship(
back_populates="parent", cascade="all, delete-orphan" back_populates="parent", cascade="all, delete-orphan"
) )

View File

@@ -25,9 +25,9 @@ class RouteEncounter(Base):
min_level: Mapped[int] = mapped_column(SmallInteger) min_level: Mapped[int] = mapped_column(SmallInteger)
max_level: Mapped[int] = mapped_column(SmallInteger) max_level: Mapped[int] = mapped_column(SmallInteger)
route: Mapped["Route"] = relationship(back_populates="route_encounters") route: Mapped[Route] = relationship(back_populates="route_encounters")
pokemon: Mapped["Pokemon"] = relationship(back_populates="route_encounters") pokemon: Mapped[Pokemon] = relationship(back_populates="route_encounters")
game: Mapped["Game"] = relationship() game: Mapped[Game] = relationship()
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<RouteEncounter(route_id={self.route_id}, pokemon_id={self.pokemon_id}, method='{self.encounter_method}')>" return f"<RouteEncounter(route_id={self.route_id}, pokemon_id={self.pokemon_id}, method='{self.encounter_method}')>"

View File

@@ -11,9 +11,9 @@ class VersionGroup(Base):
name: Mapped[str] = mapped_column(String(100)) name: Mapped[str] = mapped_column(String(100))
slug: Mapped[str] = mapped_column(String(100), unique=True) slug: Mapped[str] = mapped_column(String(100), unique=True)
games: Mapped[list["Game"]] = relationship(back_populates="version_group") games: Mapped[list[Game]] = relationship(back_populates="version_group")
routes: Mapped[list["Route"]] = relationship(back_populates="version_group") routes: Mapped[list[Route]] = relationship(back_populates="version_group")
boss_battles: Mapped[list["BossBattle"]] = relationship( boss_battles: Mapped[list[BossBattle]] = relationship(
back_populates="version_group" back_populates="version_group"
) )

47
prek.toml Normal file
View File

@@ -0,0 +1,47 @@
# Configuration file for `prek`, a git hook framework written in Rust.
# See https://prek.j178.dev for more information.
#:schema https://www.schemastore.org/prek.json
[[repos]]
repo = "https://github.com/astral-sh/ruff-pre-commit"
rev = "v0.15.0"
hooks = [
{
id = "ruff",
args = ["--fix"],
files = "^backend/"
},
{
id = "ruff-format",
files = "^backend/"
}
]
[[repos]]
repo = "local"
hooks = [
{
id = "oxlint",
name = "oxlint",
entry = "npx oxlint -c frontend/.oxlintrc.json",
language = "system",
files = '^frontend/src/.*\.(ts|tsx)$',
pass_filenames = true
},
{
id = "oxfmt",
name = "oxfmt",
entry = "npx oxfmt --check --config frontend/.oxfmtrc.json",
language = "system",
files = '^frontend/src/.*\.(ts|tsx)$',
pass_filenames = true
},
{
id = "tsc",
name = "tsc",
entry = "bash -c 'cd frontend && npx tsc -b'",
language = "system",
files = '^frontend/src/.*\.(ts|tsx)$',
pass_filenames = false
}
]