import { useEffect, useMemo, useRef, useState } from 'react'
import Tree from '../Tree/Tree'
import FallingLeaves from '../Tree/FallingLeaves'
const numberFormat = new Intl.NumberFormat('en-GB')
const dateFormat = new Intl.DateTimeFormat('en-GB', {
dateStyle: 'medium',
timeStyle: 'short',
})
const apiEndpoints = {
health: '/health',
teams: '/api/tss/leaderboard/teams?limit=100',
teamsHealth: '/api/tss/leaderboard/teams?limit=1',
resolve: (name) => `/api/tss/teams/resolve?name=${encodeURIComponent(name)}`,
detail: (name) => `/api/tss/teams/${encodeURIComponent(name)}`,
history: (name) => `/api/tss/teams/${encodeURIComponent(name)}/history`,
games: (name) => `/api/tss/teams/${encodeURIComponent(name)}/games`,
}
const navItems = [
{ path: '/', label: 'Home' },
{ path: '/teams', label: 'Team leaderboard' },
{ path: '/battle-logs', label: 'Battle Logs' },
]
async function fetchJson(path, signal) {
const response = await fetch(path, {
signal,
headers: { Accept: 'application/json' },
})
const body = await response.json().catch(() => null)
if (!response.ok) {
throw new Error(body?.error || `Request failed with ${response.status}`)
}
return body
}
function parseRoute(pathname = window.location.pathname) {
if (pathname === '/') return { page: 'home', teamName: '' }
if (pathname === '/teams') return { page: 'teams', teamName: '' }
if (pathname === '/uptime') return { page: 'uptime', teamName: '' }
if (pathname.startsWith('/teams/')) {
const teamName = decodeURIComponent(pathname.slice('/teams/'.length))
return { page: 'team', teamName }
}
if (pathname === '/battle-logs' || pathname === '/live') return { page: 'battle-logs', teamName: '' }
return { page: 'home', teamName: '' }
}
function teamPath(name) {
return `/teams/${encodeURIComponent(name)}`
}
function formatNumber(value) {
return numberFormat.format(Number(value || 0))
}
function formatDate(timestamp) {
if (!timestamp) return 'Unknown time'
return dateFormat.format(new Date(Number(timestamp) * 1000))
}
function bestTeamName(team) {
return team?.tag_name || team?.short_name || team?.long_name || ''
}
async function fetchRecentTssGames(teams, signal) {
const teamNames = teams.map(bestTeamName).filter(Boolean).slice(0, 12)
if (!teamNames.length) {
return { matches: [] }
}
const responses = await Promise.allSettled(
teamNames.map((name) => fetchJson(apiEndpoints.games(name), signal).then((data) => ({ name, data }))),
)
const bySession = new Map()
responses.forEach((result) => {
if (result.status !== 'fulfilled') return
const { name, data } = result.value
;(data.games || []).forEach((game) => {
if (!game.session_id) return
const existing = bySession.get(game.session_id)
const currentTimestamp = Number(game.timestamp || 0)
if (existing && Number(existing.timestamp || 0) >= currentTimestamp) return
bySession.set(game.session_id, {
...game,
team_name: data.tag_name || name,
long_name: data.long_name || '',
})
})
})
return {
matches: Array.from(bySession.values())
.sort((a, b) => Number(b.timestamp || 0) - Number(a.timestamp || 0))
.slice(0, 50),
}
}
function Stat({ label, value }) {
return (
)
}
function App() {
const [route, setRoute] = useState(() => parseRoute())
const [leaderboard, setLeaderboard] = useState({ status: 'idle', data: null, error: null })
const [live, setLive] = useState({ status: 'idle', data: null, error: null })
const [uptime, setUptime] = useState({ status: 'idle', checks: [], history: [], updatedAt: null })
const [teamQuery, setTeamQuery] = useState('')
const [searchHint, setSearchHint] = useState({ status: 'idle', name: '' })
const [profile, setProfile] = useState({
teamName: '',
detail: { status: 'idle', data: null, error: null },
history: { status: 'idle', data: null, error: null },
games: { status: 'idle', data: null, error: null },
})
const teams = useMemo(
() => leaderboard.data?.teams || leaderboard.data?.squadrons || [],
[leaderboard.data],
)
const matches = live.data?.matches || []
function navigate(path) {
window.history.pushState({}, '', path)
setRoute(parseRoute(path))
}
useEffect(() => {
const onPopState = () => setRoute(parseRoute())
window.addEventListener('popstate', onPopState)
return () => window.removeEventListener('popstate', onPopState)
}, [])
useEffect(() => {
const title =
route.page === 'team' && route.teamName
? `${route.teamName} | Toothless' TSS Bot`
: route.page === 'teams'
? "Team leaderboard | Toothless' TSS Bot"
: route.page === 'battle-logs'
? "Battle Logs | Toothless' TSS Bot"
: route.page === 'uptime'
? "Uptime | Toothless' TSS Bot"
: "Toothless' TSS Bot"
document.title = title
}, [route.page, route.teamName])
useEffect(() => {
const onKeyDown = (event) => {
if (event.key === 'Escape') {
navigate('/')
}
}
window.addEventListener('keydown', onKeyDown)
return () => window.removeEventListener('keydown', onKeyDown)
}, [])
useEffect(() => {
const query = teamQuery.trim()
if (query.length < 2) {
setSearchHint({ status: 'idle', name: '' })
return
}
const controller = new AbortController()
const timer = window.setTimeout(() => {
setSearchHint({ status: 'loading', name: '' })
fetchJson(apiEndpoints.resolve(query), controller.signal)
.then((data) => {
const name = data.tag_name || data.short_name || data.long_name || query
setSearchHint({ status: 'ready', name })
})
.catch(() => {
if (!controller.signal.aborted) {
setSearchHint({ status: 'error', name: '' })
}
})
}, 350)
return () => {
window.clearTimeout(timer)
controller.abort()
}
}, [teamQuery])
useEffect(() => {
if (!['home', 'teams', 'team', 'battle-logs'].includes(route.page)) return
if (leaderboard.status === 'ready' || leaderboard.status === 'loading') return
const controller = new AbortController()
setLeaderboard({ status: 'loading', data: null, error: null })
fetchJson(apiEndpoints.teams, controller.signal)
.then((data) => setLeaderboard({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setLeaderboard({ status: 'error', data: null, error: error.message })
}
})
return () => controller.abort()
}, [leaderboard.status, route.page])
useEffect(() => {
if (!['home', 'teams', 'team', 'battle-logs'].includes(route.page)) return
const controller = new AbortController()
const timer = window.setInterval(() => {
fetchJson(apiEndpoints.teams, controller.signal)
.then((data) => setLeaderboard({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setLeaderboard((current) => ({ ...current, error: error.message }))
}
})
}, 60000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page])
useEffect(() => {
if (!['home', 'battle-logs'].includes(route.page)) return
if (!teams.length) return
const controller = new AbortController()
setLive((current) =>
current.status === 'ready' ? current : { status: 'loading', data: null, error: null },
)
fetchRecentTssGames(teams, controller.signal)
.then((data) => setLive({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setLive({ status: 'error', data: null, error: error.message })
}
})
return () => controller.abort()
}, [route.page, teams])
useEffect(() => {
if (!['home', 'battle-logs'].includes(route.page)) return
if (!teams.length) return
const controller = new AbortController()
const timer = window.setInterval(() => {
fetchRecentTssGames(teams, controller.signal)
.then((data) => setLive({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setLive((current) => ({ ...current, error: error.message }))
}
})
}, 15000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page, teams])
useEffect(() => {
if (route.page !== 'team' || !route.teamName) return
const controller = new AbortController()
setProfile({
teamName: route.teamName,
detail: { status: 'loading', data: null, error: null },
history: { status: 'loading', data: null, error: null },
games: { status: 'loading', data: null, error: null },
})
Promise.allSettled([
fetchJson(apiEndpoints.detail(route.teamName), controller.signal),
fetchJson(apiEndpoints.history(route.teamName), controller.signal),
fetchJson(apiEndpoints.games(route.teamName), controller.signal),
]).then(([detailResult, historyResult, gamesResult]) => {
if (controller.signal.aborted) return
setProfile({
teamName: route.teamName,
detail:
detailResult.status === 'fulfilled'
? { status: 'ready', data: detailResult.value, error: null }
: { status: 'error', data: null, error: detailResult.reason.message },
history:
historyResult.status === 'fulfilled'
? { status: 'ready', data: historyResult.value, error: null }
: { status: 'error', data: null, error: historyResult.reason.message },
games:
gamesResult.status === 'fulfilled'
? { status: 'ready', data: gamesResult.value, error: null }
: { status: 'error', data: null, error: gamesResult.reason.message },
})
})
return () => controller.abort()
}, [route.page, route.teamName])
useEffect(() => {
if (route.page !== 'uptime') return
const controller = new AbortController()
async function checkUptime() {
setUptime((current) => ({
status: current.status === 'ready' ? 'refreshing' : 'loading',
checks: current.checks,
history: current.history,
updatedAt: current.updatedAt,
}))
const startedAt = performance.now()
const healthResult = await fetchJson(apiEndpoints.health, controller.signal)
.then(() => ({ ok: true, label: 'Operational' }))
.catch((error) => ({ ok: false, label: error.message }))
const apiResult = await fetchJson(apiEndpoints.teamsHealth, controller.signal)
.then((data) => {
const teamCount = data.teams?.length || data.squadrons?.length || 0
return { ok: true, label: `${teamCount} sample team${teamCount === 1 ? '' : 's'} returned` }
})
.catch((error) => ({ ok: false, label: error.message }))
if (controller.signal.aborted) return
setUptime((current) => {
const checks = [
{
name: 'Website',
detail: 'App shell and static assets',
ok: true,
label: 'Online',
latency: Math.round(performance.now() - startedAt),
},
{
name: 'Health endpoint',
detail: apiEndpoints.health,
ok: healthResult.ok,
label: healthResult.label,
latency: Math.round(performance.now() - startedAt),
},
{
name: 'TSS data proxy',
detail: apiEndpoints.teamsHealth,
ok: apiResult.ok,
label: apiResult.label,
latency: Math.round(performance.now() - startedAt),
},
]
const onlineChecks = checks.filter((check) => check.ok).length
return {
status: 'ready',
updatedAt: Date.now(),
checks,
history: [
...current.history,
{
timestamp: Date.now(),
onlineChecks,
totalChecks: checks.length,
ok: onlineChecks === checks.length,
},
].slice(-48),
}
})
}
checkUptime()
const timer = window.setInterval(checkUptime, 30000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page])
useEffect(() => {
if (route.page !== 'team' || !route.teamName) return
const controller = new AbortController()
const timer = window.setInterval(() => {
Promise.allSettled([
fetchJson(apiEndpoints.detail(route.teamName), controller.signal),
fetchJson(apiEndpoints.history(route.teamName), controller.signal),
fetchJson(apiEndpoints.games(route.teamName), controller.signal),
]).then(([detailResult, historyResult, gamesResult]) => {
if (controller.signal.aborted) return
setProfile((current) => ({
teamName: route.teamName,
detail:
detailResult.status === 'fulfilled'
? { status: 'ready', data: detailResult.value, error: null }
: current.detail,
history:
historyResult.status === 'fulfilled'
? { status: 'ready', data: historyResult.value, error: null }
: current.history,
games:
gamesResult.status === 'fulfilled'
? { status: 'ready', data: gamesResult.value, error: null }
: current.games,
}))
})
}, 60000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page, route.teamName])
const topTeamName = bestTeamName(teams[0])
const searchPlaceholder =
searchHint.status === 'ready' ? `Found ${searchHint.name}` : topTeamName || 'Search teams'
async function handleTeamSearch(event) {
event.preventDefault()
const name = teamQuery.trim()
if (!name) return
try {
const resolved = await fetchJson(apiEndpoints.resolve(name))
navigate(teamPath(resolved.tag_name || resolved.short_name || resolved.long_name || name))
} catch {
navigate(teamPath(name))
}
}
const activeNavPath =
route.page === 'team'
? '/teams'
: route.page === 'battle-logs'
? '/battle-logs'
: window.location.pathname
return (
{route.page === 'home' ? (
) : null}
{route.page === 'teams' ? (
) : null}
{route.page === 'team' ? (
) : null}
{route.page === 'battle-logs' ? : null}
{route.page === 'uptime' ? : null}
)
}
function Footer({ navigate }) {
return (
)
}
function Landing({ live, matches, navigate }) {
const treeRef = useRef(null)
return (
BorisBot got nothin on THIS
Toothless' TSS Bot
Powered by Spectra. TSS analytics.
)
}
function RecentGamesSection({ live, matches, navigate }) {
const recentMatches = matches.slice(0, 6)
return (
Recent activity
Latest games
{recentMatches.map((match) => (
{match.map_name || 'Unknown map'}
{formatDate(match.timestamp)}
{match.result || 'Unknown'}
{match.team_name || 'TSS team'}
{formatNumber(match.player_count)} players
{formatNumber(match.stats?.ground_kills)} ground
{formatNumber(match.stats?.air_kills)} air
{formatNumber(match.stats?.deaths)} deaths
))}
{!recentMatches.length ? (
{live.status === 'loading' ? 'Loading latest games' : live.error || 'No games returned'}
) : null}
)
}
function PixelMountains() {
const canvasRef = useRef(null)
useEffect(() => {
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
const WORLD_W = 1920
const WORLD_H = 900
function interpolate(points, x) {
for (let i = 0; i < points.length - 1; i++) {
const [x0, y0] = points[i]
const [x1, y1] = points[i + 1]
if (x >= x0 && x <= x1) {
const t = (x - x0) / Math.max(1, x1 - x0)
return y0 + (y1 - y0) * t
}
}
return points.at(-1)[1]
}
function drawMountain(points, color, jitter = 0) {
const width = WORLD_W
const height = WORLD_H
ctx.fillStyle = color
for (let x = 0; x < width; x++) {
const wave = jitter
? Math.sin(x * 0.08) * jitter + Math.sin(x * 0.021) * jitter * 1.8
: 0
const y = Math.round(interpolate(points, x) + wave)
ctx.fillRect(x, y, 1, height - y)
}
}
function draw() {
const width = WORLD_W
const height = WORLD_H
canvas.width = WORLD_W
canvas.height = WORLD_H
ctx.imageSmoothingEnabled = false
ctx.clearRect(0, 0, WORLD_W, WORLD_H)
drawMountain(
[
[0, height * 0.82],
[width * 0.12, height * 0.73],
[width * 0.26, height * 0.66],
[width * 0.39, height * 0.76],
[width * 0.55, height * 0.58],
[width * 0.64, height * 0.46],
[width * 0.73, height * 0.62],
[width * 0.86, height * 0.56],
[width, height * 0.64],
],
'#fcfbcf',
1.1,
)
drawMountain(
[
[0, height * 0.86],
[width * 0.15, height * 0.78],
[width * 0.31, height * 0.81],
[width * 0.43, height * 0.69],
[width * 0.52, height * 0.76],
[width * 0.62, height * 0.43],
[width * 0.69, height * 0.31],
[width * 0.78, height * 0.48],
[width, height * 0.5],
],
'#fff2e6',
1.6,
)
drawMountain(
[
[0, height * 0.94],
[width * 0.17, height * 0.9],
[width * 0.32, height * 0.92],
[width * 0.47, height * 0.83],
[width * 0.58, height * 0.89],
[width * 0.66, height * 0.61],
[width * 0.71, height * 0.5],
[width * 0.84, height * 0.7],
[width, height * 0.68],
],
'#fee5cd',
2.1,
)
drawMountain(
[
[0, height * 0.98],
[width * 0.17, height * 0.96],
[width * 0.34, height * 0.99],
[width * 0.48, height * 0.93],
[width * 0.62, height * 0.97],
[width * 0.72, height * 0.88],
[width * 0.86, height * 0.95],
[width, height * 0.93],
],
'#fdca9b',
1.4,
)
}
draw()
}, [])
return
}
function TeamsPage({ leaderboard, navigate, teams }) {
return (
Team leaderboard
{leaderboard.status === 'loading'
? 'Loading leaderboard'
: leaderboard.error || `${teams.length} teams returned`}
{teams.map((team, index) => {
const name = bestTeamName(team)
return (
)
})}
{!teams.length ? (
{leaderboard.status === 'loading' ? 'Loading leaderboard' : 'No teams returned'}
) : null}
)
}
function TeamProfilePage({ navigate, profile, requestedTeam, teams }) {
const detail = profile.detail.data
const summary = detail?.team_summary || detail?.squadron_summary
const players = detail?.players || []
const games = profile.games.data?.games || []
const history = profile.history.data?.history || []
const ratingHourly = profile.history.data?.rating_hourly || []
const latestRating = ratingHourly.at(-1)?.rating || summary?.points?.total_points
const leaderboardTeam = teams.find((team) => bestTeamName(team) === requestedTeam)
const displayName = detail?.tag_name || bestTeamName(leaderboardTeam) || requestedTeam
const longName = detail?.long_name || leaderboardTeam?.long_name || ''
return (
Team profile
{displayName}
{profile.detail.error || longName || profile.detail.status}
Rating {formatNumber(latestRating)}
Clan {detail?.clan_id || leaderboardTeam?.clan_id || 'n/a'}
{detail?.data_set || 'tss'}
)
}
function RosterTable({ players, status }) {
const sortedPlayers = [...players].sort((a, b) => {
return (b.total_kills || 0) - (a.total_kills || 0) || String(a.nick || '').localeCompare(b.nick || '')
})
return (
Roster
{formatNumber(sortedPlayers.length)} players
{sortedPlayers.map((player) => (
{player.nick || player.uid}
{player.uid} · {formatNumber(player.points || player.sqb_points)} pts
{formatNumber(player.total_battles)} battles
{formatNumber(player.total_kills)} kills
{Number(player.win_rate || 0).toFixed(1)}% WR
{Number(player.kdr || 0).toFixed(1)} KDR
{formatNumber(player.assists)} assists
))}
{!sortedPlayers.length ? (
{status === 'loading' ? 'Loading roster' : 'No roster rows returned'}
) : null}
)
}
function RatingPanel({ history, ratingHourly, status }) {
const recentHistory = history.slice(-8)
const firstRating = ratingHourly[0]?.rating || 0
const latestRating = ratingHourly.at(-1)?.rating || 0
const ratingChange = latestRating - firstRating
return (
History
{ratingHourly.length ? `${formatNumber(ratingHourly.length)} rating snapshots` : status}
= 0 ? '+' : ''}${formatNumber(ratingChange)}`}
/>
{recentHistory.map((item) => (
{item.period}
{formatNumber(item.battles)} battles
{Number(item.win_rate || 0).toFixed(1)}% WR
))}
{!recentHistory.length ? (
{status === 'loading' ? 'Loading history' : 'No history rows returned'}
) : null}
)
}
function BattleResults({ games, status }) {
return (
Battle results
{formatNumber(games.length)} battles returned
{games.map((game) => (
{game.map_name || 'Unknown map'}
{formatDate(game.timestamp)} · {game.session_id}
{game.result || 'Unknown'}
{formatNumber(game.player_count)} players
{formatNumber(game.stats?.ground_kills)} ground
{formatNumber(game.stats?.air_kills)} air
{formatNumber(game.stats?.assists)} assists
{formatNumber(game.stats?.deaths)} deaths
))}
{!games.length ? (
{status === 'loading' ? 'Loading battle results' : 'No battle results returned'}
) : null}
)
}
function BattleLogsPage({ live, matches }) {
return (
Battle Logs
{live.status === 'loading' ? 'Loading battles' : live.error || `${matches.length} battles returned`}
{matches.map((match) => (
{match.map_name || 'Unknown map'}
{formatDate(match.timestamp)} · {match.session_id}
{match.team_name || 'TSS team'}
{match.long_name || 'TSS battle record'}
{match.result || 'Unknown'}
{formatNumber(match.player_count)} players
{formatNumber(match.stats?.ground_kills)} ground
{formatNumber(match.stats?.air_kills)} air
{formatNumber(match.stats?.deaths)} deaths
))}
{!matches.length ? (
{live.status === 'loading' ? 'Loading battles' : 'No battles returned'}
) : null}
)
}
function UptimePage({ uptime }) {
const checks = uptime.checks
const history = uptime.history
const operationalCount = checks.filter((check) => check.ok).length
const allOperational = checks.length > 0 && operationalCount === checks.length
const updatedAt = uptime.updatedAt ? dateFormat.format(new Date(uptime.updatedAt)) : 'Not checked yet'
const onlineSamples = history.filter((sample) => sample.ok).length
const availability = history.length ? (onlineSamples / history.length) * 100 : 0
return (
Website uptime
{allOperational ? 'All systems operational' : 'Status check'}
Last checked {updatedAt}. Refreshes every 30 seconds while this page is open.
{uptime.status === 'loading' ? 'Checking' : `${operationalCount}/${checks.length || 3} online`}
Availability timeline
Last {formatNumber(history.length)} checks from this browser session
{history.length ? `${availability.toFixed(1)}% uptime` : 'Waiting for first sample'}
{history.map((sample) => {
const height = `${Math.max(12, (sample.onlineChecks / sample.totalChecks) * 100)}%`
return (
)
})}
{!history.length ? (
Checking website status
) : null}
Uptime
Downtime or degraded
{checks.map((check) => (
{check.name}
{check.detail}
{check.ok ? 'Online' : 'Issue'}
{check.label}
{formatNumber(check.latency)} ms response window
))}
{!checks.length ? (
Checking website status
) : null}
)
}
export default App