diff --git a/.beans/nuzlocke-tracker-94hx--add-sort-options-to-run-team-overview.md b/.beans/nuzlocke-tracker-94hx--add-sort-options-to-run-team-overview.md new file mode 100644 index 0000000..b310462 --- /dev/null +++ b/.beans/nuzlocke-tracker-94hx--add-sort-options-to-run-team-overview.md @@ -0,0 +1,29 @@ +--- +# nuzlocke-tracker-94hx +title: Add sort options to run team overview +status: completed +type: feature +priority: normal +created_at: 2026-02-09T10:03:49Z +updated_at: 2026-02-09T11:09:33Z +--- + +The Active Team / Final Team section on the run dashboard (`RunDashboard.tsx`) currently displays Pokemon in whatever order encounters arrive from the backend — there is no explicit sorting. Add a sort dropdown so the user can choose how their team is ordered. + +## Sort options +- **Route order** (default) — sort by the route's `order` field, matching game progression +- **Catch level** — sort by `catchLevel`, ascending +- **Species name** — sort alphabetically by the display Pokemon's name (accounting for evolutions via `currentPokemon`) +- **National Dex** — sort by the display Pokemon's `nationalDex` number + +## Scope +- Frontend-only change — all data is already available in the `EncounterDetail` objects +- Add a small sort control (dropdown or segmented buttons) above the team grid +- Persist the selected sort in component state (no need for localStorage) +- Apply the same sort options to both the Active Team and Graveyard sections + +## Checklist +- [x] Add sort state and sort logic to `RunDashboard.tsx` +- [x] Add sort dropdown/control above the team grid +- [x] Apply sorting to both `alive` and `dead` encounter arrays +- [x] Verify sort works correctly with evolved Pokemon (use `currentPokemon ?? pokemon` for name/dex) \ No newline at end of file diff --git a/frontend/src/pages/RunDashboard.tsx b/frontend/src/pages/RunDashboard.tsx index fe8db42..2d5472f 100644 --- a/frontend/src/pages/RunDashboard.tsx +++ b/frontend/src/pages/RunDashboard.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useMemo, useState } from 'react' import { useParams, Link } from 'react-router-dom' import { useRun, useUpdateRun } from '../hooks/useRuns' import { useGameRoutes } from '../hooks/useGames' @@ -6,6 +6,28 @@ import { useCreateEncounter, useUpdateEncounter } from '../hooks/useEncounters' import { StatCard, PokemonCard, RuleBadges, StatusChangeModal, EndRunModal } from '../components' import type { RunStatus, EncounterDetail } from '../types' +type TeamSortKey = 'route' | 'level' | 'species' | 'dex' + +function sortEncounters(encounters: EncounterDetail[], key: TeamSortKey): EncounterDetail[] { + return [...encounters].sort((a, b) => { + switch (key) { + case 'route': + return a.route.order - b.route.order + case 'level': + return (a.catchLevel ?? 0) - (b.catchLevel ?? 0) + case 'species': { + const nameA = (a.currentPokemon ?? a.pokemon).name + const nameB = (b.currentPokemon ?? b.pokemon).name + return nameA.localeCompare(nameB) + } + case 'dex': + return (a.currentPokemon ?? a.pokemon).nationalDex - (b.currentPokemon ?? b.pokemon).nationalDex + default: + return 0 + } + }) +} + const statusStyles: Record = { active: 'bg-green-100 text-green-800 dark:bg-green-900/40 dark:text-green-300', completed: @@ -32,6 +54,7 @@ export function RunDashboard() { const [selectedEncounter, setSelectedEncounter] = useState(null) const [showEndRun, setShowEndRun] = useState(false) + const [teamSort, setTeamSort] = useState('route') if (isLoading) { return ( @@ -58,11 +81,13 @@ export function RunDashboard() { } const isActive = run.status === 'active' - const alive = run.encounters.filter( - (e) => e.status === 'caught' && e.faintLevel === null, + const alive = useMemo( + () => sortEncounters(run.encounters.filter((e) => e.status === 'caught' && e.faintLevel === null), teamSort), + [run.encounters, teamSort], ) - const dead = run.encounters.filter( - (e) => e.status === 'caught' && e.faintLevel !== null, + const dead = useMemo( + () => sortEncounters(run.encounters.filter((e) => e.status === 'caught' && e.faintLevel !== null), teamSort), + [run.encounters, teamSort], ) const visitedRoutes = new Set(run.encounters.map((e) => e.routeId)).size const totalRoutes = routes?.length @@ -172,9 +197,23 @@ export function RunDashboard() { {/* Active Team */}
-

- {isActive ? 'Active Team' : 'Final Team'} -

+
+

+ {isActive ? 'Active Team' : 'Final Team'} +

+ {alive.length > 1 && ( + + )} +
{alive.length === 0 ? (

No pokemon caught yet — head to encounters to start building your