tss site blah

This commit is contained in:
FURRO404
2026-06-20 21:58:27 -07:00
parent 1eb0f1ffc8
commit 45142f280a
5 changed files with 83 additions and 16 deletions
+4
View File
@@ -6,6 +6,10 @@ It reads two SQLite databases:
- `TSS_BATTLES_DB` for `tss_battles.db` (matches, players, and the `match_logs` table) - `TSS_BATTLES_DB` for `tss_battles.db` (matches, players, and the `match_logs` table)
- `TSS_TEAMS_DB` for `tss_teams.db` - `TSS_TEAMS_DB` for `tss_teams.db`
- `TSS_TOURNAMENTS_DB` for `tss_tournaments.db`
If any of these are unset, the backend first looks under `STORAGE_VOL_PATH`
before falling back to the current working directory.
- `BACKEND_HOST` bind host, default `127.0.0.1` - `BACKEND_HOST` bind host, default `127.0.0.1`
- `BACKEND_ALLOWED_ORIGINS` comma-separated browser origins allowed by CORS - `BACKEND_ALLOWED_ORIGINS` comma-separated browser origins allowed by CORS
+7 -3
View File
@@ -2299,9 +2299,13 @@ fn lookup_vehicle_icon(icons: &HashMap<String, String>, cdk: &str) -> String {
} }
fn resolve_db_path(env_key: &str, default_file: &str) -> PathBuf { fn resolve_db_path(env_key: &str, default_file: &str) -> PathBuf {
let raw = env::var(env_key).unwrap_or_else(|_| default_file.to_string()); let path = if let Ok(raw) = env::var(env_key) {
let expanded = expand_home(&raw); PathBuf::from(expand_home(&raw))
let path = PathBuf::from(expanded); } else if let Ok(storage) = env::var("STORAGE_VOL_PATH") {
PathBuf::from(expand_home(&storage)).join(default_file)
} else {
PathBuf::from(default_file)
};
if path.is_absolute() { if path.is_absolute() {
path path
} else { } else {
+1
View File
@@ -10,6 +10,7 @@ BACKEND_HOST=127.0.0.1
BACKEND_ALLOWED_ORIGINS=https://example.com BACKEND_ALLOWED_ORIGINS=https://example.com
TSS_BATTLES_DB=./tss_battles.db TSS_BATTLES_DB=./tss_battles.db
TSS_TEAMS_DB=./tss_teams.db TSS_TEAMS_DB=./tss_teams.db
TSS_TOURNAMENTS_DB=./tss_tournaments.db
# Vehicle name translation + icon caches (shared STORAGE/CACHE, built by the bots). # Vehicle name translation + icon caches (shared STORAGE/CACHE, built by the bots).
# The backend loads these at startup to translate vehicle_internal (cdk) -> name # The backend loads these at startup to translate vehicle_internal (cdk) -> name
+27 -10
View File
@@ -3714,10 +3714,11 @@ function tournamentStatusLabel(status) {
return raw.charAt(0).toUpperCase() + raw.slice(1) return raw.charAt(0).toUpperCase() + raw.slice(1)
} }
function sideFromMatch(match) { function sideFromMatch(match, context = {}) {
const bracket = String(match?.type_bracket || '').toLowerCase()
if (context.hasLoserSide && bracket.includes('semifinal')) return 'loser'
const side = String(match?.side || '').toLowerCase() const side = String(match?.side || '').toLowerCase()
if (side) return side if (side) return side
const bracket = String(match?.type_bracket || '').toLowerCase()
if (bracket.includes('swiss')) return 'swiss' if (bracket.includes('swiss')) return 'swiss'
if (bracket.includes('group')) return 'group' if (bracket.includes('group')) return 'group'
if (bracket.includes('looser') || bracket.includes('loser')) return 'loser' if (bracket.includes('looser') || bracket.includes('loser')) return 'loser'
@@ -3813,8 +3814,15 @@ function TournamentsPage({ navigate }) {
function groupMatchesBySide(matches) { function groupMatchesBySide(matches) {
const bySide = new Map() const bySide = new Map()
const context = {
hasLoserSide: matches.some((match) => {
const bracket = String(match?.type_bracket || '').toLowerCase()
const side = String(match?.side || '').toLowerCase()
return bracket.includes('looser') || bracket.includes('loser') || side === 'loser'
}),
}
matches.forEach((match) => { matches.forEach((match) => {
const side = sideFromMatch(match) const side = sideFromMatch(match, context)
if (!bySide.has(side)) bySide.set(side, []) if (!bySide.has(side)) bySide.set(side, [])
bySide.get(side).push(match) bySide.get(side).push(match)
}) })
@@ -3862,7 +3870,7 @@ function TournamentMatchCard({ match, navigate }) {
const bWon = winner && teamB && winner === teamB.toLowerCase() const bWon = winner && teamB && winner === teamB.toLowerCase()
const battles = Array.isArray(match.battles) ? match.battles : [] const battles = Array.isArray(match.battles) ? match.battles : []
const teamRow = (name, score, won) => ( const teamRow = (name, score, won, emptyLabel = 'TBD') => (
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
{name ? ( {name ? (
<button <button
@@ -3876,16 +3884,17 @@ function TournamentMatchCard({ match, navigate }) {
{name} {name}
</button> </button>
) : ( ) : (
<span className="min-w-0 truncate font-semibold text-text-muted">TBD</span> <span className="min-w-0 truncate font-semibold text-text-muted">{emptyLabel}</span>
)} )}
<span className={`shrink-0 tabular-nums ${won ? 'text-win' : 'text-text-soft'}`}>{formatNumber(score)}</span> <span className={`shrink-0 tabular-nums ${won ? 'text-win' : 'text-text-soft'}`}>{formatNumber(score)}</span>
</div> </div>
) )
const emptyLabel = match.status === 'bye' ? 'BYE' : 'TBD'
return ( return (
<div className="rounded-md border border-border bg-bg p-2.5 text-sm shadow-sm"> <div className="rounded-md border border-border bg-bg p-2.5 text-sm shadow-sm">
{teamRow(teamA, match.score_a, aWon)} {teamRow(teamA, match.score_a, aWon, emptyLabel)}
<div className="mt-1">{teamRow(teamB, match.score_b, bWon)}</div> <div className="mt-1">{teamRow(teamB, match.score_b, bWon, emptyLabel)}</div>
<div className="mt-2 flex items-center justify-between gap-2 text-[10px] font-semibold uppercase tracking-wide text-text-muted"> <div className="mt-2 flex items-center justify-between gap-2 text-[10px] font-semibold uppercase tracking-wide text-text-muted">
<span>{tournamentStatusLabel(match.status)}</span> <span>{tournamentStatusLabel(match.status)}</span>
{match.position !== null && match.position !== undefined ? <span>Slot {Number(match.position) + 1}</span> : null} {match.position !== null && match.position !== undefined ? <span>Slot {Number(match.position) + 1}</span> : null}
@@ -3923,19 +3932,27 @@ function TournamentBracketSide({ side, navigate }) {
<div> <div>
<h3 className="mb-3 text-sm font-semibold uppercase tracking-wide text-fury-cyan">{side.label}</h3> <h3 className="mb-3 text-sm font-semibold uppercase tracking-wide text-fury-cyan">{side.label}</h3>
<div className="overflow-x-auto pb-2"> <div className="overflow-x-auto pb-2">
<div className="flex gap-4"> <div className="tournament-bracket-grid">
{rounds.map((round, roundIndex) => ( {rounds.map((round, roundIndex) => (
<div className="flex min-w-[190px] flex-col gap-3" key={round.round}> <div className="tournament-round-column" key={round.round}>
<p className="text-xs font-semibold uppercase tracking-wide text-text-muted"> <p className="text-xs font-semibold uppercase tracking-wide text-text-muted">
{roundLabel(side.raw, round.round, roundIndex, rounds.length)} {roundLabel(side.raw, round.round, roundIndex, rounds.length)}
</p> </p>
<div className="flex flex-1 flex-col justify-around gap-3"> <div className="flex flex-1 flex-col justify-around gap-3">
{round.matches.map((match) => ( {round.matches.map((match) => (
<TournamentMatchCard <div
className={[
'tournament-match-node',
roundIndex > 0 ? 'tournament-match-node-left' : '',
roundIndex < rounds.length - 1 ? 'tournament-match-node-right' : '',
].filter(Boolean).join(' ')}
key={`${match.type_bracket}-${match.match_id}`} key={`${match.type_bracket}-${match.match_id}`}
>
<TournamentMatchCard
match={match} match={match}
navigate={navigate} navigate={navigate}
/> />
</div>
))} ))}
</div> </div>
</div> </div>
+41
View File
@@ -831,6 +831,47 @@ h3 {
font-size: 0.62rem; font-size: 0.62rem;
} }
.tournament-bracket-grid {
display: flex;
gap: 2.6rem;
min-width: max-content;
}
.tournament-round-column {
position: relative;
display: flex;
width: 190px;
min-width: 190px;
flex-direction: column;
gap: 0.75rem;
}
.tournament-match-node {
position: relative;
z-index: 1;
}
.tournament-match-node::before,
.tournament-match-node::after {
position: absolute;
top: 50%;
z-index: 0;
display: block;
height: 1px;
width: 1.3rem;
background: color-mix(in srgb, var(--color-border) 78%, var(--color-fury-violet));
content: "";
pointer-events: none;
}
.tournament-match-node-left::before {
right: calc(100% + 0.05rem);
}
.tournament-match-node-right::after {
left: calc(100% + 0.05rem);
}
@keyframes scrollPulse { @keyframes scrollPulse {
0% { 0% {
transform: translateY(-100%); transform: translateY(-100%);