Add sort options to run team overview

Add a dropdown to sort Active Team and Graveyard by route order,
catch level, species name, or national dex number.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Julian Tabel
2026-02-09 12:11:54 +01:00
parent c67621295a
commit bc9bcf4c4b
2 changed files with 76 additions and 8 deletions

View File

@@ -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<RunStatus, string> = {
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<EncounterDetail | null>(null)
const [showEndRun, setShowEndRun] = useState(false)
const [teamSort, setTeamSort] = useState<TeamSortKey>('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 */}
<div className="mb-6">
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
{isActive ? 'Active Team' : 'Final Team'}
</h2>
<div className="flex items-center justify-between mb-3">
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
{isActive ? 'Active Team' : 'Final Team'}
</h2>
{alive.length > 1 && (
<select
value={teamSort}
onChange={(e) => setTeamSort(e.target.value as TeamSortKey)}
className="text-sm border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-1.5 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
>
<option value="route">Route Order</option>
<option value="level">Catch Level</option>
<option value="species">Species Name</option>
<option value="dex">National Dex</option>
</select>
)}
</div>
{alive.length === 0 ? (
<p className="text-gray-500 dark:text-gray-400 text-sm">
No pokemon caught yet head to encounters to start building your