Files
Sdk_TV_app/templates/mobile_menu.html
T
bl3kunja-FW 5c7f255a02 Add tournament archives and results for multiple events
- Created JSON files for tournament archives on 2025-07-28 and 2025-07-29, including detailed player scores and shot results.
- Added a new tournament results file summarizing the outcomes of the tournament held on 2025-07-29, including participant scores and completion status.
2025-08-02 15:27:32 +02:00

382 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>📱 Camera Dashboard</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-family: Arial, sans-serif;
min-height: 100vh;
overflow: hidden;
}
.loader-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
color: white;
padding: 20px;
}
.logo {
font-size: 4rem;
margin-bottom: 20px;
animation: pulse 2s infinite;
}
.title {
font-size: 2rem;
font-weight: bold;
margin-bottom: 10px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.subtitle {
font-size: 1.2rem;
opacity: 0.9;
margin-bottom: 30px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(255, 255, 255, 0.3);
border-top: 4px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 20px auto;
}
.status-text {
font-size: 1rem;
opacity: 0.8;
margin-top: 15px;
}
.quick-nav {
display: flex;
gap: 15px;
margin-top: 30px;
flex-wrap: wrap;
justify-content: center;
}
.quick-btn {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
border: 2px solid rgba(255, 255, 255, 0.3);
color: white;
padding: 12px 20px;
border-radius: 12px;
text-decoration: none;
font-weight: bold;
transition: all 0.3s ease;
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 8px;
}
.quick-btn:hover {
background: rgba(255, 255, 255, 0.3);
border-color: rgba(255, 255, 255, 0.5);
transform: translateY(-2px);
}
.quick-btn.disabled {
opacity: 0.5;
pointer-events: none;
}
.quick-btn.priority {
background: rgba(40, 167, 69, 0.8);
border-color: rgba(40, 167, 69, 1);
animation: priorityPulse 2s infinite;
}
.quick-btn.priority:hover {
background: rgba(40, 167, 69, 0.9);
border-color: rgba(40, 167, 69, 1);
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes priorityPulse {
0% { box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(40, 167, 69, 0); }
100% { box-shadow: 0 0 0 0 rgba(40, 167, 69, 0); }
}
.status-indicator {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 20px;
padding: 10px 20px;
margin: 20px 0;
font-size: 0.9rem;
color: white;
}
.status-indicator.tournament-active {
background: rgba(0, 123, 255, 0.8);
border-color: rgba(0, 123, 255, 1);
}
.status-indicator.results-available {
background: rgba(40, 167, 69, 0.8);
border-color: rgba(40, 167, 69, 1);
}
@media (max-width: 480px) {
.logo {
font-size: 3rem;
}
.title {
font-size: 1.7rem;
}
.subtitle {
font-size: 1.1rem;
}
.quick-nav {
flex-direction: column;
align-items: center;
}
.quick-btn {
width: 200px;
justify-content: center;
}
}
</style>
</head>
<body>
<div class="loader-container">
<div class="logo">📱</div>
<div class="title">Camera Dashboard</div>
<div class="subtitle">Loading mobile interface...</div>
<div class="loading-spinner"></div>
<div class="status-text" id="statusText">Detecting tournament state...</div>
<!-- Status Indicator -->
<div class="status-indicator" id="statusIndicator" style="display: none;">
<span id="statusMessage">Checking status...</span>
</div>
<!-- Quick navigation fallback -->
<div class="quick-nav" id="quickNav" style="display: none;">
<a href="/mobile/streams" class="quick-btn">
<span>📷</span>
<span>Camera Streams</span>
</a>
{% if tournament_active %}
<a href="/mobile/draft" class="quick-btn">
<span>📋</span>
<span>Tournament Draft</span>
</a>
{% endif %}
{% if results_available %}
<a href="/mobile/results" class="quick-btn priority">
<span>🏆</span>
<span>View Results</span>
</a>
{% elif tournament_active %}
<div class="quick-btn disabled">
<span>🏆</span>
<span>Results (Soon)</span>
</div>
{% endif %}
</div>
</div>
<script>
// Tournament state from server
const tournamentActive = {{ 'true' if tournament_active else 'false' }};
const resultsAvailable = {{ 'true' if results_available else 'false' }};
function updateStatus(message) {
const statusText = document.getElementById('statusText');
if (statusText) {
statusText.textContent = message;
}
}
function showStatusIndicator(message, type = '') {
const statusIndicator = document.getElementById('statusIndicator');
const statusMessage = document.getElementById('statusMessage');
if (statusIndicator && statusMessage) {
statusMessage.textContent = message;
statusIndicator.className = `status-indicator ${type}`;
statusIndicator.style.display = 'block';
}
}
function redirectToOptimalPage() {
// Determine the best page to redirect to based on current state
let targetUrl = '/mobile/streams'; // Default to streams
let reason = 'Camera streams';
let statusType = '';
if (resultsAvailable) {
targetUrl = '/mobile/results';
reason = 'Tournament results are ready!';
statusType = 'results-available';
showStatusIndicator('🏆 ' + reason, statusType);
} else if (tournamentActive) {
targetUrl = '/mobile/draft';
reason = 'Tournament is active';
statusType = 'tournament-active';
showStatusIndicator('🏆 ' + reason, statusType);
} else {
reason = 'Camera streams (default)';
showStatusIndicator('📷 ' + reason);
}
updateStatus(`Redirecting to ${reason.toLowerCase()}...`);
// Add a small delay for smooth UX
setTimeout(() => {
window.location.href = targetUrl;
}, 1800);
}
function showQuickNav() {
const quickNav = document.getElementById('quickNav');
const statusText = document.getElementById('statusText');
if (quickNav && statusText) {
statusText.textContent = 'Choose your destination:';
quickNav.style.display = 'flex';
// Show status indicator based on current state
if (resultsAvailable) {
showStatusIndicator('🏆 Tournament completed - results ready!', 'results-available');
} else if (tournamentActive) {
showStatusIndicator('🏆 Tournament in progress', 'tournament-active');
} else {
showStatusIndicator('📷 Ready to view camera streams');
}
}
}
// Auto-redirect logic
function initializeRedirect() {
console.log('📱 Mobile entry point loaded');
console.log('🏆 Tournament active:', tournamentActive);
console.log('📊 Results available:', resultsAvailable);
// Show status first
setTimeout(() => {
if (resultsAvailable) {
showStatusIndicator('🏆 Tournament completed - results ready!', 'results-available');
} else if (tournamentActive) {
showStatusIndicator('🏆 Tournament in progress', 'tournament-active');
} else {
showStatusIndicator('📷 Ready to view camera streams');
}
}, 800);
// Wait a moment, then redirect
setTimeout(() => {
redirectToOptimalPage();
}, 1500);
// Show quick nav as fallback after 4 seconds if redirect fails
setTimeout(() => {
showQuickNav();
}, 4000);
}
// Handle errors gracefully
function handleRedirectError() {
updateStatus('Connection issue detected');
setTimeout(() => {
showQuickNav();
}, 1000);
}
// Keyboard shortcuts
document.addEventListener('keydown', function(event) {
switch(event.key) {
case '1':
window.location.href = '/mobile/streams';
break;
case '2':
if (tournamentActive) {
window.location.href = '/mobile/draft';
}
break;
case '3':
if (resultsAvailable) {
window.location.href = '/mobile/results';
}
break;
case 'Escape':
showQuickNav();
break;
case 'r':
case 'R':
if (resultsAvailable) {
window.location.href = '/mobile/results';
}
break;
}
});
// Initialize when page loads
document.addEventListener('DOMContentLoaded', initializeRedirect);
// Handle network errors
window.addEventListener('error', handleRedirectError);
// Handle orientation changes gracefully
window.addEventListener('orientationchange', function() {
setTimeout(() => {
// Trigger a reflow to handle orientation change
document.body.style.display = 'none';
document.body.offsetHeight; // Trigger reflow
document.body.style.display = '';
}, 100);
});
// Handle visibility change (when user returns to app)
document.addEventListener('visibilitychange', function() {
if (!document.hidden && resultsAvailable) {
// If results are available and user returns to app, prioritize results
updateStatus('🏆 Redirecting to results...');
setTimeout(() => {
window.location.href = '/mobile/results';
}, 1000);
}
});
</script>
</body>
</html>