Files
Sdk_TV_app/templates/results_display.html
T

1113 lines
30 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title data-i18n="tournament.tournament_results">Tournament Results</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/static/css/base.css">
<link rel="stylesheet" href="/static/css/navbar.css">
<link rel="stylesheet" href="/static/css/buttons.css">
<link rel="stylesheet" href="/static/css/components.css">
<link rel="stylesheet" href="/static/css/responsive.css">
<style>
/* Results display specific styles */
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
background: #f5f5f5;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
height: 100vh;
overflow: hidden;
color: #333;
}
.navbar {
background: white;
border-bottom: 1px solid #e1e5e9;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
padding: 15px 25px;
display: flex;
align-items: center;
justify-content: space-between;
}
.navbar-logo {
height: 40px;
max-width: 120px;
object-fit: contain;
}
.navbar-title {
font-size: 1.8rem;
font-weight: bold;
color: #333;
margin-left: 15px;
}
.navbar-brand {
display: flex;
align-items: center;
}
.navbar-controls {
display: flex;
gap: 12px;
align-items: center;
}
.nav-btn {
background: #f8f9fa;
border: 2px solid #e9ecef;
cursor: pointer;
padding: 12px 20px;
border-radius: 8px;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
color: #333;
text-decoration: none;
font-weight: bold;
font-size: 0.9rem;
}
.nav-btn:hover {
background: #e9ecef;
border-color: #28a745;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
color: #28a745;
}
.nav-btn.primary {
background: #28a745;
border-color: #1e7e34;
color: white;
}
.nav-btn.primary:hover {
background: #1e7e34;
color: white;
}
/* Main TV Layout */
.tv-container {
height: calc(100vh - 90px);
display: grid;
grid-template-columns: 1fr 2fr;
gap: 20px;
padding: 20px;
}
/* Left Column - Header & Podium */
.left-column {
display: flex;
flex-direction: column;
gap: 0;
overflow: hidden;
flex: 1;
min-height: 0;
}
.results-header {
border-radius: 12px 12px 0 0;
box-shadow: none;
padding: 15px;
text-align: center;
flex-shrink: 0;
color: white;
position: relative;
overflow: hidden;
min-height: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
/* Dynamic header colors based on tournament type */
.results-header.tournament-4_targets {
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
}
.results-header.tournament-20_targets {
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%);
}
.results-header.tournament-40_targets {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.results-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%, rgba(255, 255, 255, 0.05) 100%);
pointer-events: none;
}
.results-header * {
position: relative;
z-index: 2;
}
.header-logo {
height: 60px;
max-width: 160px;
object-fit: contain;
margin-bottom: 12px;
filter: brightness(1.2) contrast(1.1);
background-color: white;
padding: 8px;
border-radius: 8px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.results-title {
font-size: 2rem;
font-weight: 700;
color: rgb(255, 255, 255);
margin-bottom: 6px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.results-subtitle {
font-size: 1rem;
color: rgba(255, 255, 255, 0.95);
margin-bottom: 12px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.results-meta {
display: flex;
justify-content: space-around;
gap: 12px;
}
.meta-item {
text-align: center;
}
.meta-number {
font-size: 1.2rem;
font-weight: 700;
color: #ffffff;
display: block;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.meta-label {
font-size: 0.65rem;
color: rgba(255, 255, 255, 0.9);
text-transform: uppercase;
letter-spacing: 0.3px;
font-weight: 500;
}
/* Modern Podium Design */
.podium-section {
background: white;
border-radius: 0 0 12px 12px;
padding: 12px;
flex: 1;
display: flex;
flex-direction: column;
overflow: visible;
min-height: 0;
box-shadow: none;
border: 1px solid #e9ecef;
}
.podium-title {
text-align: center;
font-size: 0.9rem;
font-weight: 700;
color: #2c3e50;
margin-bottom: 8px;
}
.podium-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 0.75fr 0.75fr 1.5fr;
gap: 10px;
overflow: hidden;
flex: 1;
min-height: 0;
}
.podium-card {
background: white;
border-radius: 12px;
box-shadow: none;
padding: 12px 12px 18px 12px;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
border: 1px solid #e9ecef;
border-top-width: 6px;
justify-content: flex-end;
}
.podium-card::before {
content: '';
position: absolute;
top: 6px;
left: 1px;
right: 1px;
bottom: 1px;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0) 100%);
pointer-events: none;
z-index: 1;
border-radius: 0 0 11px 11px;
}
.podium-card:hover {
box-shadow: none;
}
.podium-card.rank-1 {
border-top-color: #ffd700;
border-color: #ffd700;
background: linear-gradient(135deg, #fffbf0 0%, #fff9e6 100%);
grid-column: 2;
grid-row: 1 / 4;
}
.podium-card.rank-2 {
border-top-color: #c0c0c0;
border-color: #c0c0c0;
background: linear-gradient(135deg, #f5f5f5 0%, #f0f0f0 100%);
grid-column: 1;
grid-row: 2 / 4;
}
.podium-card.rank-3 {
border-top-color: #cd7f32;
border-color: #cd7f32;
background: linear-gradient(135deg, #fff5f0 0%, #ffe8dc 100%);
grid-column: 3;
grid-row: 3;
}
.rank-display {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
position: relative;
z-index: 2;
}
.medal {
font-size: 4.5rem;
line-height: 1;
}
.rank-number {
font-size: 1.9rem;
font-weight: 900;
color: #333;
line-height: 1;
}
.podium-card.rank-1 .rank-number { color: #b8860b; }
.podium-card.rank-2 .rank-number { color: #696969; }
.podium-card.rank-3 .rank-number { color: #8b4513; }
.participant-info {
flex: 1;
min-width: 0;
text-align: center;
position: relative;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.participant-name {
font-size: 1rem;
font-weight: 700;
color: #2c3e50;
margin: 0;
word-wrap: break-word;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
line-height: 1.2;
}
.participant-id {
display: none;
}
.score-display {
text-align: center;
position: relative;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
gap: 1px;
}
.score-number {
font-size: 1.8rem;
font-weight: 900;
color: #28a745;
line-height: 1;
}
.tens-count {
font-size: 1.2rem;
color: #ffc107;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.3px;
}
.score-label {
font-size: 0.6rem;
color: #999;
text-transform: uppercase;
font-weight: 600;
letter-spacing: 0.3px;
}
/* Right Column - Full Results Table */
.right-column {
background: white;
border-radius: 12px;
box-shadow: none;
overflow: hidden;
display: flex;
flex-direction: column;
border: 1px solid #e9ecef;
}
.table-header {
background: #f8f9fa;
padding: 8px 12px;
border-bottom: 1px solid #dee2e6;
flex-shrink: 0;
}
.table-title {
font-size: 0.95rem;
font-weight: 600;
color: #2c3e50;
margin: 0;
}
.table-container {
flex: 1;
overflow: auto;
min-height: 0;
}
.results-table {
width: 100%;
border-collapse: collapse;
font-size: 0.75rem;
}
.results-table th,
.results-table td {
padding: 4px 6px;
text-align: center;
border-bottom: 1px solid #f1f3f4;
border-right: 1px solid #f1f3f4;
}
.results-table th {
background: #f8f9fa;
font-weight: 600;
color: #495057;
text-transform: uppercase;
font-size: 0.65rem;
letter-spacing: 0.3px;
position: sticky;
top: 0;
z-index: 10;
}
.results-table th.rank-col {
width: auto;
}
.results-table th.player-col {
text-align: left;
width: auto;
}
.results-table th.score-col {
width: auto;
background: #e3f2fd;
}
.results-table th.tens-col {
width: auto;
background: #fff3cd;
}
.results-table tbody tr:hover {
background: #f8f9fa;
}
.results-table tbody tr:nth-child(1) {
background: #fff9e6;
}
.results-table tbody tr:nth-child(1):hover {
background: #fff3cd;
}
.results-table tbody tr:nth-child(2) {
background: #f5f5f5;
}
.results-table tbody tr:nth-child(2):hover {
background: #e9ecef;
}
.results-table tbody tr:nth-child(3) {
background: #fdf6f0;
}
.results-table tbody tr:nth-child(3):hover {
background: #f8f1e6;
}
.rank-cell {
font-size: 1rem;
font-weight: 700;
text-align: center;
}
.rank-1 { color: #b8860b; }
.rank-2 { color: #6c757d; }
.rank-3 { color: #8b4513; }
.results-table tbody {
display: table-row-group;
}
.player-cell {
text-align: left !important;
}
.player-name {
font-size: 0.75rem;
font-weight: 600;
color: #2c3e50;
}
.score-cell {
background: #e3f2fd !important;
font-size: 0.75rem;
font-weight: 700;
color: #1976d2;
}
.tens-cell {
background: #fff3cd !important;
font-size: 0.75rem;
font-weight: 700;
color: #856404;
}
/* Tournament Stats Footer */
.stats-footer {
background: white;
border-top: 1px solid #dee2e6;
padding: 6px 15px;
display: flex;
justify-content: space-around;
font-size: 0.7rem;
color: #6c757d;
flex-shrink: 0;
}
.stat-item {
text-align: center;
}
.stat-value {
font-weight: 600;
color: #28a745;
}
/* Responsive styles */
@media (max-width: 768px) {
.tv-container {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
}
.results-meta {
gap: 15px;
}
.meta-number {
font-size: 1.5rem;
}
.podium-section {
padding: 20px 15px;
}
.results-table {
font-size: 0.8rem;
}
.results-table th,
.results-table td {
padding: 8px 6px;
}
.tens-cell,
.score-cell {
width: 60px;
}
}
/* PRINT STYLES */
@media print {
.navbar,
.debug-info {
display: none !important;
}
html, body {
height: auto !important;
overflow: visible !important;
background: white !important;
margin: 0;
padding: 20px;
}
.tv-container {
display: block !important;
height: auto !important;
padding: 0 !important;
gap: 0 !important;
margin: 0 !important;
}
.left-column {
display: block !important;
margin-bottom: 0;
}
.results-header {
background: white !important;
color: #333 !important;
box-shadow: none !important;
border: 2px solid #ddd !important;
border-radius: 8px !important;
padding: 16px 20px !important;
margin-bottom: 16px;
overflow: visible !important;
position: static !important;
}
.results-header::before {
display: none !important;
}
.results-header * {
position: static !important;
z-index: auto !important;
}
.header-logo {
display: block !important;
height: 50px !important;
max-width: 140px !important;
background-color: transparent !important;
padding: 0 !important;
border: none !important;
filter: none !important;
backdrop-filter: none !important;
margin-bottom: 10px;
}
.results-title {
font-size: 20pt !important;
font-weight: bold !important;
color: #111 !important;
text-shadow: none !important;
margin-bottom: 4px;
}
.results-subtitle {
font-size: 11pt !important;
color: #555 !important;
text-shadow: none !important;
margin-bottom: 10px;
}
.results-meta {
color: #333 !important;
}
.meta-number {
font-size: 14pt !important;
color: #111 !important;
text-shadow: none !important;
}
.meta-label {
font-size: 9pt !important;
color: #666 !important;
}
.podium-section {
display: none !important;
}
.right-column {
background: white !important;
border-radius: 0 !important;
box-shadow: none !important;
overflow: visible !important;
display: block !important;
margin: 0 !important;
padding: 0 !important;
}
.table-header {
display: none !important;
}
.results-table {
margin-top: 0 !important;
}
.print-title {
display: none !important;
}
.table-title {
display: none !important;
}
}
</style>
<script src="/static/js/i18n.js"></script>
</head>
<body>
<div class="navbar">
<div class="navbar-brand">
<div class="navbar-title">🏆 <span data-i18n="tournament.tournament_results">Tournament Results</span></div>
</div>
<div class="navbar-controls">
<button class="nav-btn" onclick="exportResultsJSON()">💾 <span data-i18n="general.export">Export JSON</span></button>
<button class="nav-btn" onclick="printResults()">🖨️ <span data-i18n="general.print">Print</span></button>
<button class="nav-btn" id="btnTvView" onclick="toggleTvView()" title="Preklopi pogled na TV zaslonu">📺</button>
<a href="/" class="nav-btn"></a>
</div>
</div>
<div class="tv-container">
<!-- Left Column -->
<div class="left-column">
<!-- Header Section -->
<div class="results-header tournament-{{ results.tournament_type if results and results.tournament_type else '20_targets' }}">
<img src="/static/logo.png" alt="Logo" class="header-logo" onerror="this.style.display='none'" />
<div class="results-title">
{% if results and results.tournament_type == '4_targets' %}
🎯 <span data-i18n="tournament.tournament_results">Tournament Results</span>
{% elif results and results.tournament_type == '40_targets' %}
💪 <span data-i18n="tournament.tournament_results">Tournament Results</span>
{% else %}
<span data-i18n="tournament.tournament_results">Tournament Results</span>
{% endif %}
</div>
<div class="results-subtitle" data-i18n="results.final_results">Final Rankings & Scores</div>
<div class="results-meta">
<div class="meta-item">
<span class="meta-number" id="participantCount">{{ participants|length }}</span>
<span class="meta-label" data-i18n="tournament.participants">Participants</span>
</div>
<div class="meta-item">
<span class="meta-number" id="totalShots">0</span>
<span class="meta-label" data-i18n="results.total_shots">Total Shots</span>
</div>
<div class="meta-item">
<span class="meta-number" id="highestScore">0</span>
<span class="meta-label" data-i18n="results.highest_score">Highest Score</span>
</div>
<div class="meta-item">
<span class="meta-number" id="mostTens">0</span>
<span class="meta-label" data-i18n="results.most_tens">Most 10s</span>
</div>
</div>
</div>
<!-- Podium Section -->
<div class="podium-section" id="podiumSection">
<div class="podium-title">🏆 <span data-i18n="results.top_3_winners">Top 3 Winners</span></div>
<div class="podium-container" id="podiumContainer">
<!-- Podium cards will be generated by JavaScript -->
</div>
</div>
</div>
<!-- Right Column -->
<div class="right-column">
<div class="table-header">
<h3 class="table-title">📊 <span data-i18n="results.rankings">Complete Rankings</span></h3>
<!-- Print-only title -->
<div class="print-title" style="display: none;">
{% if results and results.tournament_type == '4_targets' %}
🎯 <span data-i18n="tournament.tournament_results">Tournament Results</span>
{% elif results and results.tournament_type == '40_targets' %}
💪 <span data-i18n="tournament.tournament_results">Tournament Results</span>
{% else %}
<span data-i18n="tournament.tournament_results">Tournament Results</span>
{% endif %}
</div>
</div>
<div class="table-container">
<table class="results-table">
<thead>
<tr>
<th class="rank-col" data-i18n="results.position">Rank</th>
<th class="player-col" data-i18n="players.player">Participant</th>
<th class="score-col" data-i18n="results.score">Score</th>
<th class="tens-col" data-i18n="results.tens">10s</th>
</tr>
</thead>
<tbody id="resultsTableBody">
<!-- Table rows will be generated by JavaScript -->
</tbody>
</table>
</div>
<div class="stats-footer">
<div class="stat-item">
<div class="stat-value" id="footerParticipants">0</div>
<div data-i18n="tournament.participants">Participants</div>
</div>
<div class="stat-item">
<div class="stat-value" id="footerHighest">0</div>
<div data-i18n="results.highest_score">Highest Score</div>
</div>
<div class="stat-item">
<div class="stat-value" id="footerAverage">0</div>
<div data-i18n="results.average_score">Average Score</div>
</div>
<div class="stat-item">
<div class="stat-value" id="footerMostTens">0</div>
<div data-i18n="results.most_tens">Most 10s</div>
</div>
<div class="stat-item">
<div class="stat-value" id="footerDate">Today</div>
<div data-i18n="general.date">Datum</div>
</div>
</div>
</div>
</div>
<script>
// Get data from template
const participants = {{ participants|tojson }};
const results = {{ results|tojson }};
console.log('Raw participants data:', participants);
console.log('Raw results data:', results);
// Calculate 10s from shot data
function calculateTensFromTargets(targets) {
let tensCount = 0;
if (!targets || typeof targets !== 'object') {
return 0;
}
for (let targetNum in targets) {
const target = targets[targetNum];
if (target && typeof target === 'object') {
for (let shotKey in target) {
if (shotKey.startsWith('shot') && target[shotKey] === 10) {
tensCount++;
}
}
}
}
return tensCount;
}
// Calculate 10s from participant data
function calculateTensForParticipant(participant) {
// Try different data structures
if (participant.targets) {
return calculateTensFromTargets(participant.targets);
}
// If no targets, check if we can get them from results
if (results && results.participants) {
const resultParticipant = results.participants[participant.id];
if (resultParticipant && resultParticipant.targets) {
return calculateTensFromTargets(resultParticipant.targets);
}
}
return 0;
}
// Process participant data and add calculated fields
function processParticipants() {
if (!participants || !Array.isArray(participants)) {
console.error('Invalid participants data:', participants);
return [];
}
return participants.map(participant => {
const tensCount = calculateTensForParticipant(participant);
console.log(`Player ${participant.name}: ${tensCount} tens`);
return {
...participant,
tens_count: tensCount
};
});
}
// Generate podium HTML
function generatePodiumHTML(processedParticipants) {
const topThree = processedParticipants.slice(0, 3);
return topThree.map(participant => {
const medal = participant.rank === 1 ? '🥇' :
participant.rank === 2 ? '🥈' :
participant.rank === 3 ? '🥉' : '';
return `
<div class="podium-card rank-${participant.rank}">
<div class="rank-display">
<div class="medal">${medal}</div>
</div>
<div class="participant-info">
<div class="participant-name">${participant.name}</div>
<div class="participant-id">ID: ${participant.id}</div>
</div>
<div class="score-display">
<div class="score-number">${participant.total_score}</div>
<div class="tens-count">🎯 ${participant.tens_count}</div>
</div>
</div>
`;
}).join('');
}
// Generate table HTML
function generateTableHTML(processedParticipants) {
return processedParticipants.map(participant => {
const rankClass = participant.rank <= 3 ? `rank-${participant.rank}` : 'rank-other';
const medal = participant.rank === 1 ? '🥇' :
participant.rank === 2 ? '🥈' :
participant.rank === 3 ? '🥉' : '';
return `
<tr>
<td class="rank-cell ${rankClass}">
${participant.rank} ${medal}
</td>
<td class="player-cell">
<div class="player-name">${participant.name}</div>
</td>
<td class="score-cell">${participant.total_score}</td>
<td class="tens-cell">🎯 ${participant.tens_count}</td>
</tr>
`;
}).join('');
}
// Update statistics
function updateStatistics(processedParticipants) {
const count = processedParticipants.length;
const totalShots = count * (results && results.tournament_type === '40_targets' ? 80 :
results && results.tournament_type === '4_targets' ? 20 : 40);
const highestScore = count > 0 ? processedParticipants[0].total_score : 0;
const mostTens = count > 0 ? Math.max(...processedParticipants.map(p => p.tens_count)) : 0;
const averageScore = count > 0 ? Math.round(processedParticipants.reduce((sum, p) => sum + p.total_score, 0) / count) : 0;
// Format date from YYYY-MM-DD to DD-MM-YY
function formatFooterDate(dateString) {
if (!dateString) return '';
const parts = dateString.split('-');
if (parts.length !== 3) return dateString;
const year = parts[0].slice(-2);
const month = parts[1];
const day = parts[2];
return `${day}-${month}-${year}`;
}
const currentDate = results && results.created_at ?
formatFooterDate(results.created_at.substring(0, 10)) :
formatFooterDate(new Date().toISOString().substring(0, 10));
// Update header stats
document.getElementById('participantCount').textContent = count;
document.getElementById('totalShots').textContent = totalShots;
document.getElementById('highestScore').textContent = highestScore;
document.getElementById('mostTens').textContent = mostTens;
// Update footer stats
document.getElementById('footerParticipants').textContent = count;
document.getElementById('footerHighest').textContent = highestScore;
document.getElementById('footerAverage').textContent = averageScore;
document.getElementById('footerMostTens').textContent = mostTens;
document.getElementById('footerDate').textContent = currentDate;
}
// Print function
function printResults() {
window.print();
}
// Initialize the page
function initializePage() {
try {
const processedParticipants = processParticipants();
if (processedParticipants.length === 0) {
console.error('No participants to display');
return;
}
// Generate podium (top 3 only)
if (processedParticipants.length >= 3) {
document.getElementById('podiumContainer').innerHTML = generatePodiumHTML(processedParticipants);
} else {
document.getElementById('podiumSection').style.display = 'none';
}
// Generate table
document.getElementById('resultsTableBody').innerHTML = generateTableHTML(processedParticipants);
// Update statistics
updateStatistics(processedParticipants);
console.log('Tournament results initialized successfully');
console.log('Processed participants:', processedParticipants);
} catch (error) {
console.error('Error initializing tournament results:', error);
}
}
// Export results as JSON
function exportResultsJSON() {
const tournamentData = {{ tournament | tojson | safe }};
const resultsData = {{ results | tojson | safe }};
// Wrap in archive format for compatibility
const archiveData = {
tournament: tournamentData,
results: resultsData,
archived_at: new Date().toISOString()
};
const dataStr = JSON.stringify(archiveData, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
// Generate filename with tournament info
const tournamentId = tournamentData.tournament_id || 'tournament';
const date = new Date().toISOString().slice(0, 10);
link.download = `${tournamentId}_${date}.json`;
link.click();
URL.revokeObjectURL(url);
}
// Initialize when page loads
document.addEventListener('DOMContentLoaded', initializePage);
// Keyboard shortcuts
document.addEventListener('keydown', function(event) {
if (event.key === 'r' || event.key === 'R') {
event.preventDefault();
window.location.reload();
} else if (event.key === 'p' || event.key === 'P') {
event.preventDefault();
window.print();
}
});
// ── TV VIEW TOGGLE ────────────────────────────────────────────────────────
const isTvDisplay = new URLSearchParams(window.location.search).get('tv') === '1';
let currentTvView = 'results';
function updateTvViewBtn(view) {
currentTvView = view;
const btn = document.getElementById('btnTvView');
if (!btn) return;
btn.style.background = (view === 'results') ? '#28a745' : '';
btn.style.color = (view === 'results') ? 'white' : '';
btn.style.borderColor= (view === 'results') ? '#1e7e34' : '';
}
async function toggleTvView() {
const newView = currentTvView === 'results' ? 'cameras' : 'results';
try {
await fetch('/api/tv/view', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ view: newView })
});
updateTvViewBtn(newView);
if (isTvDisplay && newView === 'cameras') {
window.location.href = '/';
}
} catch (e) { console.error(e); }
}
// Poll to stay in sync and follow back-redirect when in TV mode
let lastResultsStateHash = null;
setInterval(async () => {
if (document.visibilityState !== 'visible') return;
try {
const r = await fetch('/api/dashboard/state');
if (!r.ok) return;
const d = await r.json();
updateTvViewBtn(d.tv_view || 'cameras');
if (isTvDisplay && d.tv_view !== 'results') {
if (d.tv_view === 'draft' && d.tournament_active) {
window.location.href = '/tournament/draft?tv=1';
} else {
window.location.href = '/';
}
}
} catch (e) {}
}, 3000);
fetch('/api/dashboard/state').then(r => r.json()).then(d => updateTvViewBtn(d.tv_view || 'cameras')).catch(() => {});
</script>
</body>
</html>