diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index b2e1c81..5d41b2c 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,4 +1,4 @@ -import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' import Tree, { prewarmTreeCanvas } from '../Tree/Tree' import FallingLeaves from '../Tree/FallingLeaves' import ReplayCanvasPanel from './ReplayCanvas' @@ -3771,9 +3771,14 @@ function TournamentsPage({ navigate }) { type="button" >
-

- {tournament.name || `Tournament ${tournament.tournament_id}`} -

+
+

+ {tournament.name || `Tournament ${tournament.tournament_id}`} +

+ + {tournamentFormatMeta(tournament.format).label} + +

TID {tournament.tournament_id} · {tournamentDateRange(tournament.date_start, tournament.date_end)}

@@ -3794,37 +3799,74 @@ function TournamentsPage({ navigate }) { ) } -function TournamentMatchCard({ match, navigate }) { +function TournamentMatchCard({ match, navigate, onHover }) { const winner = displayTeamName(match.winner_name).toLowerCase() const teamA = displayTeamName(match.team_a_name) const teamB = displayTeamName(match.team_b_name) const aWon = winner && teamA && winner === teamA.toLowerCase() const bWon = winner && teamB && winner === teamB.toLowerCase() + const decided = Boolean(winner) const battles = Array.isArray(match.battles) ? match.battles : [] + const fire = onHover || (() => {}) + const lastKey = useRef('') - const teamRow = (name, score, won, emptyLabel = 'TBD') => ( -
- {name ? ( - - ) : ( - {emptyLabel} - )} - {formatNumber(score)} -
- ) + // Hover a team name → light just that team's run (green). Hover anywhere else on + // the card → light both teams' runs (winner green, loser red). A registered team + // that forfeited (technical walkover) shows gold rather than red. + const matchHighlight = () => { + const map = {} + if (teamA) map[teamA.toLowerCase()] = !decided || aWon ? 'win' : 'loss' + if (teamB) map[teamB.toLowerCase()] = !decided || bWon ? 'win' : 'loss' + return Object.keys(map).length ? map : null + } + const handleOver = (event) => { + const teamEl = event.target.closest('[data-team]') + const map = teamEl ? { [teamEl.dataset.team.toLowerCase()]: 'win' } : matchHighlight() + const key = map ? Object.entries(map).map(([k, v]) => `${k}:${v}`).sort().join('|') : '' + if (key === lastKey.current) return + lastKey.current = key + fire(map) + } + const handleLeave = () => { + lastKey.current = '' + fire(null) + } + + const teamRow = (name, score, won, emptyLabel = 'TBD') => { + const lost = decided && name && !won + const noShow = lost && match.status === 'technical' + const nameColor = won ? 'text-win' : noShow ? 'text-fury-violet' : lost ? 'text-loss' : 'text-text' + const scoreColor = won ? 'text-win' : noShow ? 'text-fury-violet' : lost ? 'text-loss' : 'text-text-soft' + return ( +
+ {name ? ( + + ) : ( + {emptyLabel} + )} + {formatNumber(score)} +
+ ) + } const emptyLabel = match.status === 'bye' ? 'BYE' : 'TBD' return ( -
+
{teamRow(teamA, match.score_a, aWon, emptyLabel)}
{teamRow(teamB, match.score_b, bWon, emptyLabel)}
@@ -3838,7 +3880,11 @@ function TournamentMatchCard({ match, navigate }) {