From 8158a878dbada3503396f66f6c6d190f4a517fbc Mon Sep 17 00:00:00 2001 From: FURRO404 Date: Thu, 18 Jun 2026 00:41:05 -0700 Subject: [PATCH] =?UTF-8?q?feat(web):=20rebuild=20game=20detail=20?= =?UTF-8?q?=E2=80=94=20vehicles,=20icons,=20full=20scoreboard,=20logs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.jsx | 191 ++++++++++++++++++++++++++++++------------- 1 file changed, 134 insertions(+), 57 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index d92cb44..b1cf7d8 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -19,6 +19,7 @@ const apiEndpoints = { teamsHealth: '/api/tss/leaderboard/teams?limit=1', recentGames: '/api/tss/games/recent?limit=50', game: (gameId) => `/api/tss/games/${encodeURIComponent(gameId)}`, + gameLogs: (gameId) => `/api/tss/games/${encodeURIComponent(gameId)}/logs`, resolve: (name) => `/api/tss/teams/resolve?name=${encodeURIComponent(name)}`, searchTeams: (name) => `/api/tss/teams/search?q=${encodeURIComponent(name)}&limit=10`, detail: (name) => `/api/tss/teams/${encodeURIComponent(name)}`, @@ -116,6 +117,14 @@ function formatDate(timestamp) { return dateFormat.format(new Date(Number(timestamp) * 1000)) } +function formatDuration(seconds) { + const total = Math.round(Number(seconds || 0)) + if (!total) return '' + const m = Math.floor(total / 60) + const s = total % 60 + return `${m}:${String(s).padStart(2, '0')}` +} + function gameParticipants(game) { const winner = displayTeamName(game?.winning_team) const loser = displayTeamName(game?.losing_team) @@ -2769,14 +2778,18 @@ function TeamProfilePage({ navigate, profile, requestedTeam, teams }) { ) } +const SCOREBOARD_GRID = 'grid-cols-[minmax(220px,1fr)_repeat(6,minmax(52px,72px))]' + function GamePage({ gameId, navigate }) { const [gameState, setGameState] = useState({ status: 'loading', data: null, error: null }) + const [logs, setLogs] = useState({ chat_log: [], battle_log: [] }) useEffect(() => { if (!gameId) return const controller = new AbortController() setGameState({ status: 'loading', data: null, error: null }) + setLogs({ chat_log: [], battle_log: [] }) fetchJson(apiEndpoints.game(gameId), controller.signal) .then((data) => { @@ -2790,6 +2803,19 @@ function GamePage({ gameId, navigate }) { } }) + fetchJson(apiEndpoints.gameLogs(gameId), controller.signal) + .then((data) => { + if (!controller.signal.aborted) { + setLogs({ + chat_log: Array.isArray(data?.chat_log) ? data.chat_log : [], + battle_log: Array.isArray(data?.battle_log) ? data.battle_log : [], + }) + } + }) + .catch(() => { + // Logs are non-critical; leave them empty on failure. + }) + return () => controller.abort() }, [gameId]) @@ -2802,6 +2828,9 @@ function GamePage({ gameId, navigate }) { })) : gameParticipants(game) + const subtitle = [game?.mission_mode, game?.tournament_name].filter(Boolean).join(' · ') + const duration = formatDuration(game?.duration) + return (
+ {participants.map((participant) => { + const won = String(participant.result || '').toLowerCase() === 'win' + return ( +
+ -
- {(participant.players || []).map((player) => ( - - ))} +
+ {(participant.players || []).map((player) => ( + + ))} +
-
- ) - })} + ) + })} {!participants.length ? ( @@ -2900,6 +2959,24 @@ function GamePage({ gameId, navigate }) { ) : null} + + {logs.battle_log.length ? ( +
+ Battle Log +
+            {logs.battle_log.join('\n')}
+          
+
+ ) : null} + + {logs.chat_log.length ? ( +
+ Chat Log +
+            {logs.chat_log.join('\n')}
+          
+
+ ) : null}
) }