Skip to content

Refactor the current YasMarina-based selection flow into a generic, route-driven race selection experience that supports multiple competitions, seasons, and races without page-per-race duplication. #189

@PhilipWoulfe

Description

@PhilipWoulfe

Principles

Race identity is data, not code structure
Competition differences live in config/rules, not separate page trees
Pages stay generic; components stay reusable
Keep migration incremental so existing YasMarina behavior remains a working reference until the generic flow is proven
Proposed Ticket Sequence

Introduce route-based race context
Scope:
Add route parameters for competition, season, and race
Stop hardcoding the current race identifier inside the page
Keep the current page implementation otherwise intact
Acceptance criteria:

The selection page can be opened with route parameters
The page resolves its race context from the route, not a constant
Existing YasMarina scenario still works through the new route
Existing tests are updated to render with route parameters where needed
Why first:

This is the smallest step that breaks the hardcoded race assumption
Rename YasMarina page into generic RaceSelection page
Scope:
Replace the race-specific page/component name with a generic one
Keep current orchestration behavior and current extracted components
Keep one compatibility route temporarily if useful
Acceptance criteria:

The main page/component name is race-agnostic
The route structure is generic and no longer YasMarina-specific
No behavior drift in load/save/lock/countdown flows
Existing page tests still pass after rename
Why second:

Once routing is generic, the page name should stop encoding domain data
Generalize page title and display metadata
Scope:
Remove hardcoded page title text like “Yas Marina GP 2025 Selection”
Load title and display content from race metadata/config
Keep race questions and deadline/banner display data-driven
Acceptance criteria:

Header/title comes from race/metadata, not literals in the page
A different race can render different content without code changes
Missing optional metadata degrades gracefully
Tests cover at least one metadata-present and one metadata-absent path
Why third:

This removes the visible last mile of race-specific UI coupling
Review service identity assumptions for multi-competition support
Scope:
Verify whether raceId is globally unique enough for multi-competition/multi-year scale
If not, extend typed service contracts and API endpoints to accept richer identity
Keep the frontend aligned with backend reality, not wishful assumptions
Acceptance criteria:

A documented decision exists:
either raceId is globally unique and safe
or services now accept competition/season/race context
No implicit cross-competition ambiguity remains
Service tests cover the chosen identity model
Why fourth:

This is the real scalability hinge; don’t build generic routing on ambiguous identifiers
Introduce a RaceSelectionContext model
Scope:
Create a small model/value object representing the resolved page context
Centralize parsing/validation of competition, season, race route values
Keep page logic from scattering route-param handling everywhere
Acceptance criteria:

Page orchestration uses one resolved context object
Invalid or incomplete route context yields a controlled error/empty state
Tests cover valid and invalid route context paths
Why fifth:

Keeps the generic page from becoming stringly-typed and messy
Abstract competition-specific rules
Scope:
Identify actual rule variability:
lock timing semantics
bet types
labels/copy
maybe different question structures later
Put those behind config or a strategy abstraction only if they truly vary
Acceptance criteria:

No fake abstractions added for hypothetical future differences
Any real competition-specific behavior is isolated from the page
Generic page remains mostly branch-free
Why sixth:

Prevents overengineering while still preparing for real variation
Convert YasMarina tests into generic RaceSelection tests
Scope:
Rename tests and fixtures away from “YasMarina” as architecture
Keep Yas Marina as one fixture/example, not the design center
Add at least one second-race scenario if the API data can support it
Acceptance criteria:

Test names describe generic race-selection behavior
At least one test proves route/data-driven behavior, not a hardcoded page identity
The suite still validates save/load/lock/countdown/error flows
Why seventh:

Tests define architecture. If tests stay race-specific, the code will drift back there.
Decide what remains of YasMarina-specific route support
Scope:
Either keep a compatibility route/redirect temporarily
Or remove it once navigation is updated
Acceptance criteria:

Navigation is unambiguous
No duplicate canonical routes unless intentionally temporary
Redirect behavior is tested if retained
Why last:

This is cleanup after the generic path is stable
Suggested PR Slices

PR A: Generic route context

Add route parameters
Remove hardcoded race constant from primary flow
Keep existing page name if that reduces churn
Goal: generic identity, minimal behavior change
PR B: Rename to RaceSelection

Rename page/component/tests
Keep behavior identical
Goal: architecture stops encoding YasMarina in names
PR C: Data-driven title and metadata

Remove remaining hardcoded race-specific text
Goal: visible UI becomes generic
PR D: Identity model hardening

Confirm or extend service/API identity for multi-competition scale
Goal: eliminate ambiguity before expansion
PR E: Competition-rule abstraction

Introduce only the abstractions needed by real differences
Goal: scale without cloning pages
PR F: Test and route cleanup

Genericize tests and remove temporary compatibility scaffolding
Goal: finalize the new architecture
Example ticket titles

refactor(web): parameterize race selection page by route context
refactor(web): rename YasMarinaSelection to RaceSelection
refactor(web): make race selection title and metadata data-driven
refactor(api/web): harden race identity model for multi-competition support
refactor(web): introduce RaceSelectionContext for route resolution
feat(web): isolate competition-specific selection rules behind config/strategy
test(web): convert YasMarina page tests into generic RaceSelection coverage
chore(web): remove or redirect legacy YasMarina-specific route
Safest migration order

Route params
Generic page rename
Data-driven display
Identity model validation/hardening
Competition rule abstraction
Test and route cleanup
That order minimizes risk because it changes:

identity first
naming second
behavior abstractions only after the generic path is already working
Definition of Done for the new epic

No page-per-race requirement for adding a new race
No hardcoded YasMarina identity in the core selection workflow
Generic route can serve multiple competitions/seasons/races
Typed service boundaries remain intact
Extracted presentation components remain reusable
Tests describe generic race-selection behavior

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions