ai generated solutions to our ai generated problems

This commit is contained in:
Heidi
2026-06-19 23:33:01 +01:00
parent 6745a2ff81
commit 9263d6f8eb
4 changed files with 278 additions and 1 deletions
+131 -1
View File
@@ -15,6 +15,7 @@ const apiEndpoints = {
viewerEvent: '/api/viewers/event',
viewerDelete: '/api/viewers/delete',
teams: '/api/tss/leaderboard/teams?limit=100',
players: '/api/tss/leaderboard/players?limit=100',
homeTeams: '/api/tss/leaderboard/teams?limit=4',
teamsHealth: '/api/tss/leaderboard/teams?limit=1',
recentGames: '/api/tss/games/recent?limit=50',
@@ -29,6 +30,7 @@ const apiEndpoints = {
const navItems = [
{ path: '/', label: 'Home' },
{ path: '/teams', label: 'Team Leaderboard' },
{ path: '/players', label: 'Player Leaderboard' },
{ path: '/battle-logs', label: 'Battle Logs' },
{ path: '/viewers', label: 'Viewers' },
{ path: '/docs', label: 'Setup' },
@@ -74,6 +76,7 @@ async function fetchJson(path, signal) {
function parseRoute(pathname = window.location.pathname) {
if (pathname === '/') return { page: 'home', teamName: '' }
if (pathname === '/teams') return { page: 'teams', teamName: '' }
if (pathname === '/players') return { page: 'players', teamName: '' }
if (pathname === '/uptime') return { page: 'uptime', teamName: '' }
if (pathname === '/viewers') return { page: 'viewers', teamName: '' }
if (pathname === '/privacy') return { page: 'privacy', teamName: '' }
@@ -102,6 +105,10 @@ function gamePath(gameId) {
return `/games/${encodeURIComponent(gameId)}`
}
function playerPath(uid) {
return `/players/${encodeURIComponent(uid)}`
}
function formatNumber(value) {
return numberFormat.format(Number(value || 0))
}
@@ -420,6 +427,7 @@ function deviceType() {
function routeLabel(route) {
if (route.page === 'team' && route.teamName) return `Team: ${route.teamName}`
if (route.page === 'teams') return 'Team Leaderboard'
if (route.page === 'players') return 'Player Leaderboard'
if (route.page === 'battle-logs') return 'Battle Logs'
if (route.page === 'game') return route.gameId ? `Game ${route.gameId}` : 'Game'
if (route.page === 'uptime') return 'Uptime'
@@ -437,6 +445,7 @@ function currentPublicOrigin() {
function canonicalPathForRoute(route) {
if (route.page === 'team' && route.teamName) return teamPath(route.teamName)
if (route.page === 'teams') return '/teams'
if (route.page === 'players') return '/players'
if (route.page === 'battle-logs') return '/battle-logs'
if (route.page === 'game' && route.gameId) return gamePath(route.gameId)
if (route.page === 'uptime') return '/uptime'
@@ -496,6 +505,12 @@ function seoForRoute(route, profileDetail = null) {
robots: 'index, follow',
path: '/teams',
},
players: {
title: "TSS Player Leaderboard | Toothless' TSS Bot",
description: 'Browse the TSS player leaderboard, compare War Thunder player score, kills, win rate, KDR, and battle activity.',
robots: 'index, follow',
path: '/players',
},
'battle-logs': {
title: "TSS Battle Logs | Toothless' TSS Bot",
description: 'Read recent TSS battle logs with team names, map history, player counts, battle times, and War Thunder match context.',
@@ -912,6 +927,7 @@ function App() {
function AppContent() {
const [route, setRoute] = useState(() => parseRoute())
const [leaderboard, setLeaderboard] = useState({ status: 'idle', data: null, error: null })
const [playerLeaderboard, setPlayerLeaderboard] = useState({ status: 'idle', data: null, error: null })
const [homeTeams, setHomeTeams] = useState({ status: 'idle', data: null, error: null })
const [live, setLive] = useState({ status: 'idle', data: null, error: null, updatedAt: 0 })
const [uptime, setUptime] = useState({ status: 'idle', checks: [], history: [], updatedAt: null })
@@ -935,6 +951,10 @@ function AppContent() {
() => leaderboard.data?.teams || leaderboard.data?.squadrons || [],
[leaderboard.data],
)
const players = useMemo(
() => playerLeaderboard.data?.players || [],
[playerLeaderboard.data],
)
const teamsToWatch = useMemo(
() => homeTeams.data?.teams || homeTeams.data?.squadrons || teams.slice(0, 4),
[homeTeams.data, teams],
@@ -1187,6 +1207,24 @@ function AppContent() {
return () => controller.abort()
}, [leaderboard.status, route.page])
useEffect(() => {
if (route.page !== 'players') return
if (playerLeaderboard.status === 'ready' || playerLeaderboard.status === 'loading') return
const controller = new AbortController()
setPlayerLeaderboard({ status: 'loading', data: null, error: null })
fetchJson(apiEndpoints.players, controller.signal)
.then((data) => setPlayerLeaderboard({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setPlayerLeaderboard({ status: 'error', data: null, error: error.message })
}
})
return () => controller.abort()
}, [playerLeaderboard.status, route.page])
useEffect(() => {
if (route.page !== 'home') return
if (homeTeams.status === 'ready' || homeTeams.status === 'loading') return
@@ -1558,6 +1596,8 @@ function AppContent() {
const activeNavPath =
route.page === 'team'
? '/teams'
: route.page === 'player' || route.page === 'players'
? '/players'
: route.page === 'battle-logs' || route.page === 'game'
? '/battle-logs'
: route.page === 'viewers'
@@ -1586,6 +1626,26 @@ function AppContent() {
}
}, [route.page])
useEffect(() => {
if (route.page !== 'players') return
const controller = new AbortController()
const timer = window.setInterval(() => {
fetchJson(apiEndpoints.players, controller.signal)
.then((data) => setPlayerLeaderboard({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setPlayerLeaderboard((current) => ({ ...current, error: error.message }))
}
})
}, 60000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page])
return (
<main className="min-h-screen bg-bg text-text">
<header
@@ -1654,6 +1714,9 @@ function AppContent() {
{route.page === 'teams' ? (
<TeamsPage leaderboard={leaderboard} navigate={navigate} teams={teams} />
) : null}
{route.page === 'players' ? (
<PlayersPage leaderboard={playerLeaderboard} navigate={navigate} players={players} />
) : null}
{route.page === 'team' ? (
<TeamProfilePage
navigate={navigate}
@@ -2723,6 +2786,73 @@ function TeamsPage({ leaderboard, navigate, teams }) {
)
}
function PlayersPage({ leaderboard, navigate, players }) {
return (
<section className="space-y-6 pt-24 sm:pt-28">
<div>
<h1 className="text-3xl font-bold">Player Leaderboard</h1>
<p className="mt-2 text-sm text-text-soft">
{leaderboard.status === 'loading'
? 'Loading player leaderboard'
: leaderboard.error || `${players.length} players returned`}
</p>
</div>
<div className="overflow-hidden rounded-lg border border-border bg-fury-white shadow-sm">
<div className="overflow-x-auto">
<div className="min-w-[900px]">
{players.length ? (
<div className="grid grid-cols-[4rem_minmax(220px,1fr)_repeat(7,92px)] gap-3 border-b border-surface px-5 py-3 text-xs font-semibold uppercase tracking-wide text-text-soft">
<p>Rank</p>
<p>Player</p>
<p className="text-right">Score</p>
<p className="text-right">Battles</p>
<p className="text-right">Kills</p>
<p className="text-right">Assists</p>
<p className="text-right">WR</p>
<p className="text-right">KDR</p>
<p className="text-right">Teams</p>
</div>
) : null}
{players.map((player, index) => (
<button
className="grid w-full grid-cols-[4rem_minmax(220px,1fr)_repeat(7,92px)] gap-3 border-b border-surface px-5 py-4 text-left text-sm transition hover:bg-surface"
key={player.uid}
onClick={() => navigate(playerPath(player.uid))}
type="button"
>
<p className="font-semibold text-fury-cyan">#{index + 1}</p>
<div className="min-w-0">
<p className="truncate text-base font-semibold">{player.nick || player.uid}</p>
<p className="truncate text-xs text-text-soft">
{player.uid} · last seen {formatDate(player.last_seen)}
</p>
</div>
<p className="text-right font-semibold">{formatNumber(player.score)}</p>
<p className="text-right">{formatNumber(player.total_battles)}</p>
<p className="text-right">{formatNumber(player.total_kills)}</p>
<p className="text-right">{formatNumber(player.assists)}</p>
<p className="text-right">{Number(player.win_rate || 0).toFixed(1)}%</p>
<p className="text-right">{Number(player.kdr || 0).toFixed(2)}</p>
<p className="text-right">{formatNumber(player.teams_seen)}</p>
</button>
))}
</div>
</div>
{!players.length ? (
<p className="px-5 py-10 text-sm text-text-soft">
{leaderboard.status === 'loading'
? 'Loading player leaderboard'
: leaderboard.error || 'No players returned'}
</p>
) : null}
</div>
</section>
)
}
function TeamProfilePage({ navigate, profile, requestedTeam, teams }) {
const detail = profile.detail.data
const summary = detail?.team_summary || detail?.squadron_summary
@@ -2872,7 +3002,7 @@ function GamePage({ gameId, navigate }) {
<button
className="grid w-full grid-cols-[minmax(220px,1fr)_80px_repeat(5,88px)] gap-3 px-5 py-2 text-left text-sm transition hover:bg-surface"
key={player.uid}
onClick={() => navigate(`/players/${encodeURIComponent(player.uid)}`)}
onClick={() => navigate(playerPath(player.uid))}
type="button"
>
<div className="min-w-0 pl-8 sm:pl-12">