Enhance print layouts with branded headers and fix navigation consistency
- Replace plain print headers with full branded headers including logo - Add dynamic tournament-type styling (🎯 4-target, ⚡ 20-target, 💪 40-target) - Remove border lines and optimize spacing for clean print appearance - Fix emoji positioning in league championship headers - Standardize navigation with proper active button indicators - Add missing translation keys for calculator instructions - Update print media queries for professional document output Print improvements: - Logo and branding now appear on printed results - Consistent 20px spacing between header and table - Clean white background with subtle borders - Optimized typography for print readability Navigation fixes: - Added active button highlighting across all PC pages - Consistent navigation order: Dashboard → Tournament → Player Analysis → Archive → Draft → Calculator - Fixed draft page active indicator 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> This commit message covers all the major improvements we made: - Print layout enhancements with branded headers - Navigation standardization and active indicators - Translation fixes - Visual styling improvements - Professional document output optimization
This commit is contained in:
+43
-19
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Tournament Draft</title>
|
||||
<title data-i18n="draft.tournament_draft">Tournament Draft</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
* {
|
||||
@@ -76,6 +76,17 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
height: calc(100vh - 70px);
|
||||
display: flex;
|
||||
@@ -353,32 +364,36 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar">
|
||||
<div class="navbar-title">🏆 Tournament Draft</div>
|
||||
<div class="navbar-title">🏆 <span data-i18n="draft.tournament_draft">Tournament Draft</span></div>
|
||||
<div class="navbar-controls">
|
||||
<a href="/" class="nav-btn">← Dashboard</a>
|
||||
<a href="/tournament" class="nav-btn">⚙️ Manage</a>
|
||||
<a href="/" class="nav-btn">📺 <span data-i18n="navigation.dashboard">Dashboard</span></a>
|
||||
<a href="/tournament" class="nav-btn">🏆 <span data-i18n="navigation.tournament">Tournament</span></a>
|
||||
<a href="/archive/player-analysis" class="nav-btn">👤 <span data-i18n="players.player_analysis">Player Analysis</span></a>
|
||||
<a href="/archive" class="nav-btn">📚 <span data-i18n="navigation.archive">Archive</span></a>
|
||||
<a href="/tournament/draft" class="nav-btn active">📋 <span data-i18n="tournament.view_draft">Draft</span></a>
|
||||
<a href="/results/calculator" class="nav-btn">🎯 <span data-i18n="navigation.calculator">Results Calculator</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-container">
|
||||
{% if tournament %}
|
||||
<div class="tournament-header">
|
||||
<div class="tournament-title">🎯 Shooting Tournament</div>
|
||||
<div class="tournament-title">🎯 <span data-i18n="draft.shooting_tournament">Shooting Tournament</span></div>
|
||||
<div class="tournament-stats">
|
||||
{{ tournament.total_players }} players • {{ tournament.total_rounds }} rounds
|
||||
{{ tournament.total_players }} <span data-i18n="draft.players">players</span> • {{ tournament.total_rounds }} <span data-i18n="draft.rounds">rounds</span>
|
||||
{% if tournament.current_round %}
|
||||
• Currently on Round {{ tournament.current_round }}
|
||||
• <span data-i18n="draft.currently_on_round">Currently on Round</span> {{ tournament.current_round }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if tournament.current_round %}
|
||||
<div class="tournament-controls">
|
||||
<button class="round-nav-btn" id="prevRoundBtn" onclick="changeRound(-1)" title="Previous Round">
|
||||
← Previous
|
||||
← <span data-i18n="draft.previous">Previous</span>
|
||||
</button>
|
||||
<span class="current-round-display">Round {{ tournament.current_round }} of {{ tournament.total_rounds }}</span>
|
||||
<span class="current-round-display"><span data-i18n="draft.round">Round</span> {{ tournament.current_round }} <span data-i18n="draft.of">of</span> {{ tournament.total_rounds }}</span>
|
||||
<button class="round-nav-btn" id="nextRoundBtn" onclick="changeRound(1)" title="Next Round">
|
||||
Next →
|
||||
<span data-i18n="draft.next">Next</span> →
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -390,13 +405,13 @@
|
||||
{% set is_completed = tournament.current_round > round.round_number %}
|
||||
<div class="round-row {{ 'current' if is_current else ('completed' if is_completed else 'waiting') }}">
|
||||
<div class="round-header">
|
||||
<div class="round-title">Round {{ round.round_number }}</div>
|
||||
<div class="round-title"><span data-i18n="draft.round">Round</span> {{ round.round_number }}</div>
|
||||
{% if is_current %}
|
||||
<div class="round-badge current-badge">Current</div>
|
||||
<div class="round-badge current-badge" data-i18n="draft.current">Current</div>
|
||||
{% elif is_completed %}
|
||||
<div class="round-badge completed-badge">Done</div>
|
||||
<div class="round-badge completed-badge" data-i18n="draft.done">Done</div>
|
||||
{% else %}
|
||||
<div class="round-badge waiting-badge">Wait</div>
|
||||
<div class="round-badge waiting-badge" data-i18n="draft.wait">Wait</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -409,7 +424,7 @@
|
||||
<div class="player-name">{{ player.name }}</div>
|
||||
<div class="player-id">ID: {{ player.id }}</div>
|
||||
{% else %}
|
||||
<div class="player-name">Empty</div>
|
||||
<div class="player-name" data-i18n="draft.empty">Empty</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -420,16 +435,16 @@
|
||||
|
||||
{% else %}
|
||||
<div class="no-tournament">
|
||||
<h2>No Active Tournament</h2>
|
||||
<p>Go to Tournament Management to set up players and start a tournament.</p>
|
||||
<a href="/tournament" class="nav-btn primary">🏆 Set Up Tournament</a>
|
||||
<h2 data-i18n="draft.no_active_tournament">No Active Tournament</h2>
|
||||
<p data-i18n="draft.setup_tournament_message">Go to Tournament Management to set up players and start a tournament.</p>
|
||||
<a href="/tournament" class="nav-btn primary">🏆 <span data-i18n="draft.set_up_tournament">Set Up Tournament</span></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Auto-refresh indicator -->
|
||||
<div class="refresh-indicator" id="refreshIndicator">
|
||||
🔄 Updating...
|
||||
🔄 <span data-i18n="draft.updating">Updating...</span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -543,5 +558,14 @@
|
||||
console.log(`📊 Displaying ${totalRounds} rounds in vertical layout`);
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Include i18n support -->
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
<script>
|
||||
// Initialize language selector and i18n
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
translatePage();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -385,7 +385,7 @@
|
||||
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="/" class="control-btn close-btn" title="Close Fullscreen">✕</a>
|
||||
<a href="#" onclick="goBack()" class="control-btn close-btn" title="Close Fullscreen">✕</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -666,7 +666,7 @@
|
||||
document.addEventListener('keydown', function(event) {
|
||||
switch(event.key) {
|
||||
case 'Escape':
|
||||
window.location.href = '/';
|
||||
goBack();
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
@@ -761,6 +761,18 @@
|
||||
document.getElementById('fullscreenStream').addEventListener('dragstart', function(e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
function goBack() {
|
||||
// Check if user came from mobile by looking at referrer or user agent
|
||||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
const referrer = document.referrer;
|
||||
|
||||
if (isMobile || referrer.includes('/mobile/')) {
|
||||
window.location.href = '/mobile/streams';
|
||||
} else {
|
||||
window.location.href = '/';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+786
-329
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,769 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>📱 Tournament Draft</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimal-ui, viewport-fit=cover">
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #f5f5f5;
|
||||
font-family: Arial, sans-serif;
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.mobile-navbar {
|
||||
background: white;
|
||||
color: black;
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 2px solid #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 1.4rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
background: #f8f9fa;
|
||||
border: 2px solid #e9ecef;
|
||||
cursor: pointer;
|
||||
padding: 8px 12px;
|
||||
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.8rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
min-width: 44px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-btn:hover {
|
||||
background: #e9ecef;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-1px);
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
background: #f1f3f4;
|
||||
border-color: #dadce0;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 20px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.tournament-header {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 25px 20px;
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tournament-title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tournament-stats {
|
||||
color: #666;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.view-only-badge {
|
||||
background: #e3f2fd;
|
||||
border: 2px solid #2196f3;
|
||||
color: #1976d2;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.rounds-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.round-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.round-card.current {
|
||||
border-left: 5px solid #007bff;
|
||||
box-shadow: 0 6px 20px rgba(0, 123, 255, 0.2);
|
||||
}
|
||||
|
||||
.round-card.completed {
|
||||
border-left: 5px solid #28a745;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.round-card.waiting {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.round-header {
|
||||
padding: 20px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.round-card.current .round-header {
|
||||
background: #f0f8ff;
|
||||
}
|
||||
|
||||
.round-card.completed .round-header {
|
||||
background: #f8fff9;
|
||||
}
|
||||
|
||||
.round-title {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.round-badge {
|
||||
padding: 8px 15px;
|
||||
border-radius: 15px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.current-badge {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.completed-badge {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.waiting-badge {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.players-grid {
|
||||
padding: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.position-card {
|
||||
background: #f8f9fa;
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 12px;
|
||||
padding: 18px 15px;
|
||||
text-align: center;
|
||||
min-height: 90px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.position-card.filled {
|
||||
border-color: #28a745;
|
||||
background: #f8fff9;
|
||||
}
|
||||
|
||||
.position-card.empty {
|
||||
border-color: #6c757d;
|
||||
background: #f1f3f4;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.position-number {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.position-card.empty .position-number {
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.player-name {
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
line-height: 1.2;
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
.position-card.empty .player-name {
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.player-id {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
margin-top: 8px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Mobile landscape optimization */
|
||||
@media (orientation: landscape) and (max-height: 500px) {
|
||||
.mobile-navbar {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.tournament-header {
|
||||
padding: 20px 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tournament-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.tournament-stats {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.players-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.position-card {
|
||||
padding: 12px 10px;
|
||||
min-height: 70px;
|
||||
}
|
||||
|
||||
.position-number {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.player-name {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.player-id {
|
||||
font-size: 0.7rem;
|
||||
padding: 3px 8px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 6px 10px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.tournament-header {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.tournament-title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.tournament-stats {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.round-header {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.round-title {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.players-grid {
|
||||
padding: 15px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.position-card {
|
||||
padding: 15px 12px;
|
||||
min-height: 85px;
|
||||
}
|
||||
|
||||
.position-number {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.player-name {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.player-id {
|
||||
font-size: 0.75rem;
|
||||
padding: 3px 8px;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 6px 8px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.round-change-indicator {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
background: rgba(40, 167, 69, 0.9);
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
z-index: 999;
|
||||
box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3);
|
||||
}
|
||||
|
||||
.round-change-indicator.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tournament-summary {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.summary-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.summary-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.summary-value {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.smart-refresh-badge {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Tournament status indicator */
|
||||
.tournament-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
margin-left: 5px;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
/* Results available indicator */
|
||||
.results-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: #28a745;
|
||||
color: white;
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
/* No tournament message */
|
||||
.no-tournament-message {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 40px 20px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.no-tournament-message h2 {
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.no-tournament-message p {
|
||||
margin-bottom: 20px;
|
||||
font-size: 1rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="mobile-navbar">
|
||||
<div class="navbar-title">
|
||||
📋 Draft
|
||||
{% if tournament %}
|
||||
{% if tournament.current_round %}
|
||||
<span class="tournament-indicator">R{{ tournament.current_round }}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="navbar-controls">
|
||||
<!-- Always show all 3 navigation buttons -->
|
||||
<a href="/mobile/streams" class="nav-btn">📷</a>
|
||||
<a href="/mobile/draft" class="nav-btn active">📋</a>
|
||||
<a href="/mobile/results" class="nav-btn">🏆</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
{% if tournament %}
|
||||
<div class="tournament-header">
|
||||
<div class="tournament-title">🎯 Shooting Tournament</div>
|
||||
<div class="tournament-stats">
|
||||
{{ tournament.total_players }} players • {{ tournament.total_rounds }} rounds
|
||||
{% if tournament.current_round %}
|
||||
• Currently on Round {{ tournament.current_round }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounds-container">
|
||||
{% for round in tournament.rounds %}
|
||||
{% set is_current = tournament.current_round == round.round_number %}
|
||||
{% set is_completed = tournament.current_round > round.round_number %}
|
||||
<div class="round-card {{ 'current' if is_current else ('completed' if is_completed else 'waiting') }}">
|
||||
<div class="round-header">
|
||||
<div class="round-title">Round {{ round.round_number }}</div>
|
||||
{% if is_current %}
|
||||
<div class="round-badge current-badge">Current</div>
|
||||
{% elif is_completed %}
|
||||
<div class="round-badge completed-badge">Completed</div>
|
||||
{% else %}
|
||||
<div class="round-badge waiting-badge">Waiting</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="players-grid">
|
||||
{% for position in range(1, 7) %}
|
||||
{% set player = round.players[position-1] if position <= round.players|length else none %}
|
||||
<div class="position-card {{ 'filled' if player else 'empty' }}">
|
||||
<div class="position-number">{{ position }}</div>
|
||||
{% if player %}
|
||||
<div class="player-name">{{ player.name }}</div>
|
||||
<div class="player-id">ID: {{ player.id }}</div>
|
||||
{% else %}
|
||||
<div class="player-name">Empty</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Tournament Summary -->
|
||||
<div class="tournament-summary">
|
||||
<div class="summary-title">📊 Tournament Summary</div>
|
||||
<div class="summary-grid">
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Total Players:</span>
|
||||
<span class="summary-value">{{ tournament.total_players }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Total Rounds:</span>
|
||||
<span class="summary-value">{{ tournament.total_rounds }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Current Round:</span>
|
||||
<span class="summary-value">{{ tournament.current_round or 'Not set' }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Updates:</span>
|
||||
<span class="summary-value" style="color: #28a745; font-weight: bold;">On Round Change</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Created:</span>
|
||||
<span class="summary-value">{{ tournament.created_at[:10] if tournament.created_at else 'Unknown' }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Status:</span>
|
||||
<span class="summary-value" style="color: #28a745; font-weight: bold;">Active</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- No tournament available -->
|
||||
<div class="no-tournament-message">
|
||||
<h2>📋 No Active Tournament</h2>
|
||||
<p>No tournament is currently running. Check the Results tab for completed tournaments or use the dashboard to start a new tournament.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Round change indicator -->
|
||||
<div class="round-change-indicator" id="roundChangeIndicator">
|
||||
🆕 New Round Starting...
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Tournament state
|
||||
const tournamentActive = {{ 'true' if tournament else 'false' }};
|
||||
let currentRound = {{ tournament.current_round if tournament else 1 }};
|
||||
const totalRounds = {{ tournament.total_rounds if tournament else 1 }};
|
||||
|
||||
// Manual refresh function
|
||||
function manualRefresh() {
|
||||
const indicator = document.getElementById('roundChangeIndicator');
|
||||
indicator.textContent = '🔄 Refreshing...';
|
||||
indicator.classList.add('show');
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function scrollToCurrentRound() {
|
||||
const currentRound = document.querySelector('.round-card.current');
|
||||
if (currentRound) {
|
||||
setTimeout(() => {
|
||||
currentRound.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center'
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
function addMobileTouchFeedback() {
|
||||
const touchElements = document.querySelectorAll('.nav-btn:not(.disabled), .round-card');
|
||||
|
||||
touchElements.forEach(element => {
|
||||
element.addEventListener('touchstart', function(e) {
|
||||
if (!this.disabled) {
|
||||
this.style.transform = 'scale(0.98)';
|
||||
this.style.transition = 'transform 0.1s ease';
|
||||
}
|
||||
});
|
||||
|
||||
element.addEventListener('touchend', function(e) {
|
||||
if (!this.disabled) {
|
||||
setTimeout(() => {
|
||||
this.style.transform = '';
|
||||
this.style.transition = 'transform 0.2s ease';
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
element.addEventListener('touchcancel', function(e) {
|
||||
if (!this.disabled) {
|
||||
this.style.transform = '';
|
||||
this.style.transition = 'transform 0.2s ease';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupRoundChangeDetection() {
|
||||
if (!tournamentActive) return;
|
||||
|
||||
setInterval(async () => {
|
||||
if (document.visibilityState !== 'visible') return;
|
||||
|
||||
try {
|
||||
const response = await fetch(window.location.pathname + '?check=1');
|
||||
if (response.ok) {
|
||||
const pageContent = await response.text();
|
||||
|
||||
const roundMatch = pageContent.match(/Currently on Round\s*(\d+)/);
|
||||
if (roundMatch) {
|
||||
const serverCurrentRound = parseInt(roundMatch[1]);
|
||||
|
||||
if (serverCurrentRound !== currentRound) {
|
||||
console.log(`📱 Round changed from ${currentRound} to ${serverCurrentRound}`);
|
||||
|
||||
const indicator = document.getElementById('roundChangeIndicator');
|
||||
indicator.textContent = '🆕 New Round Starting...';
|
||||
indicator.classList.add('show');
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Round check failed:', error);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(event) {
|
||||
if (event.key === 'r' || event.key === 'R') {
|
||||
event.preventDefault();
|
||||
manualRefresh();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
addMobileTouchFeedback();
|
||||
scrollToCurrentRound();
|
||||
setupRoundChangeDetection();
|
||||
|
||||
console.log('📱 Mobile Draft with Fixed Navigation loaded');
|
||||
console.log('🏆 Tournament active:', tournamentActive);
|
||||
console.log('🔍 Round change detection active');
|
||||
});
|
||||
|
||||
window.addEventListener('focus', function() {
|
||||
console.log('📱 App regained focus - checking for round changes');
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const response = await fetch(window.location.pathname + '?check=1');
|
||||
if (response.ok) {
|
||||
const pageContent = await response.text();
|
||||
const roundMatch = pageContent.match(/Currently on Round\s*(\d+)/);
|
||||
if (roundMatch) {
|
||||
const serverCurrentRound = parseInt(roundMatch[1]);
|
||||
if (serverCurrentRound !== currentRound) {
|
||||
manualRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Focus round check failed:', error);
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,934 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>📱 League Results</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #f5f5f5;
|
||||
font-family: Arial, sans-serif;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.mobile-navbar {
|
||||
background: white;
|
||||
color: black;
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 2px solid #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 1.4rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
background: #f8f9fa;
|
||||
border: 2px solid #e9ecef;
|
||||
cursor: pointer;
|
||||
padding: 8px 12px;
|
||||
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.8rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
min-width: 44px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-btn:hover {
|
||||
background: #e9ecef;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-1px);
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 20px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.league-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
padding: 25px 20px;
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.league-title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.league-subtitle {
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.league-badge {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.league-stats {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 25px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.explanation-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: #856404;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.explanation-text {
|
||||
color: #856404;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.result-card.podium {
|
||||
border-left: 5px solid;
|
||||
}
|
||||
|
||||
.result-card.rank-1 {
|
||||
border-left-color: #ffd700;
|
||||
background: linear-gradient(135deg, #fff9e6 0%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.result-card.rank-2 {
|
||||
border-left-color: #c0c0c0;
|
||||
background: linear-gradient(135deg, #f5f5f5 0%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.result-card.rank-3 {
|
||||
border-left-color: #cd7f32;
|
||||
background: linear-gradient(135deg, #fdf6f0 0%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.result-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.rank-display {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.rank-number {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.result-card.rank-1 .rank-number { color: #b8860b; }
|
||||
.result-card.rank-2 .rank-number { color: #696969; }
|
||||
.result-card.rank-3 .rank-number { color: #8b4513; }
|
||||
|
||||
.rank-suffix {
|
||||
font-size: 0.7rem;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.medal {
|
||||
font-size: 1.5rem;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.participant-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.participant-name {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.participant-details {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.participant-id {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
padding: 3px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.score-display {
|
||||
text-align: right;
|
||||
min-width: 90px;
|
||||
}
|
||||
|
||||
.final-score {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
color: #28a745;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.total-score {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.score-label {
|
||||
font-size: 0.8rem;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tournament-breakdown {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.breakdown-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.breakdown-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tournament-item {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tournament-number {
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.tournament-score {
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.tournament-item.joker {
|
||||
background: #fff3cd;
|
||||
border-color: #ffc107;
|
||||
}
|
||||
|
||||
.tournament-item.joker .tournament-score {
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.tournament-item.excluded {
|
||||
background: #f8d7da;
|
||||
border-color: #dc3545;
|
||||
}
|
||||
|
||||
.tournament-item.excluded .tournament-score {
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.tournament-item.counted {
|
||||
background: #d1edf1;
|
||||
border-color: #28a745;
|
||||
}
|
||||
|
||||
.tournament-item.counted .tournament-score {
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.scoring-legend {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.legend-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.legend-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.legend-rule {
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid #007bff;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.legend-items-mobile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.legend-item-mobile {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 0.9rem;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.legend-dot {
|
||||
font-size: 1.2rem;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.congratulations {
|
||||
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
padding: 25px 20px;
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.congrats-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.congrats-message {
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* Mobile landscape optimization */
|
||||
@media (orientation: landscape) and (max-height: 500px) {
|
||||
.mobile-navbar {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.league-header {
|
||||
padding: 20px 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.league-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.league-subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
padding: 15px;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.rank-number {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.participant-name {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.final-score {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 6px 10px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.league-header {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.league-title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.league-subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
padding: 15px;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.rank-display {
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.rank-number {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.participant-name {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.score-display {
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.final-score {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.congratulations {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.congrats-title {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.congrats-message {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.breakdown-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tournament-item {
|
||||
padding: 10px 8px;
|
||||
}
|
||||
|
||||
.tournament-score {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.legend-rule {
|
||||
padding: 10px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.legend-item-mobile {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.league-stats {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 6px 8px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation for results appearance */
|
||||
.result-card {
|
||||
animation: slideInUp 0.5s ease forwards;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
.result-card:nth-child(1) { animation-delay: 0.1s; }
|
||||
.result-card:nth-child(2) { animation-delay: 0.2s; }
|
||||
.result-card:nth-child(3) { animation-delay: 0.3s; }
|
||||
.result-card:nth-child(4) { animation-delay: 0.4s; }
|
||||
.result-card:nth-child(5) { animation-delay: 0.5s; }
|
||||
.result-card:nth-child(6) { animation-delay: 0.6s; }
|
||||
|
||||
@keyframes slideInUp {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Celebration confetti effect */
|
||||
.confetti {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.confetti-piece {
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #ffd700;
|
||||
animation: confetti-fall 3s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes confetti-fall {
|
||||
0% {
|
||||
transform: translateY(-100vh) rotate(0deg);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(100vh) rotate(720deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* League status indicator */
|
||||
.league-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: #28a745;
|
||||
color: white;
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
margin-left: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="mobile-navbar">
|
||||
<div class="navbar-title">
|
||||
🏆 League
|
||||
<span class="league-indicator">FINAL</span>
|
||||
</div>
|
||||
<div class="navbar-controls">
|
||||
<a href="/mobile/streams" class="nav-btn">📷</a>
|
||||
<a href="/mobile/results" class="nav-btn active">🏆</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="league-header">
|
||||
<div class="league-title">🏆 League Championship</div>
|
||||
<div class="league-subtitle">
|
||||
Final Results - Best 5 of 6 Tournaments
|
||||
</div>
|
||||
<div class="league-badge">
|
||||
{% if league.tournament_type == '40_targets' %}
|
||||
40 Targets Format
|
||||
{% else %}
|
||||
20 Targets Format
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="league-stats">
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ participants|length if participants else 0 }}</div>
|
||||
<div class="stat-label">Participants</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ league.total_tournaments }}</div>
|
||||
<div class="stat-label">Tournaments</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{% if participants and participants|length > 0 %}{{ participants[0].final_score }}{% else %}0{% endif %}</div>
|
||||
<div class="stat-label">Highest Score</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ league.finished_at[:10] if league.finished_at else 'Today' }}</div>
|
||||
<div class="stat-label">Completed</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if participants and participants|length > 0 %}
|
||||
<div class="congratulations">
|
||||
<div class="congrats-title">🎉 League Complete!</div>
|
||||
<div class="congrats-message">
|
||||
Congratulations to {{ participants[0].name }} for winning the league!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="results-list">
|
||||
{% for participant in participants %}
|
||||
<div class="result-card {{ 'podium rank-' + participant.rank|string if participant.rank <= 3 else '' }}">
|
||||
<div class="rank-display">
|
||||
<div class="rank-number">{{ participant.rank }}</div>
|
||||
<div class="rank-suffix">
|
||||
{% if participant.rank == 1 %}st
|
||||
{% elif participant.rank == 2 %}nd
|
||||
{% elif participant.rank == 3 %}rd
|
||||
{% else %}th
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if participant.rank == 1 %}
|
||||
<div class="medal">🥇</div>
|
||||
{% elif participant.rank == 2 %}
|
||||
<div class="medal">🥈</div>
|
||||
{% elif participant.rank == 3 %}
|
||||
<div class="medal">🥉</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="participant-info">
|
||||
<div class="participant-name">{{ participant.name }}</div>
|
||||
<div class="participant-details">
|
||||
<div class="participant-id">ID: {{ participant.id }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="score-display">
|
||||
<div class="final-score">{{ participant.final_score }}</div>
|
||||
<div class="total-score">Total: {{ participant.total_score }}</div>
|
||||
<div class="score-label">Final Score</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Tournament Breakdown for Top Players -->
|
||||
<!-- Fixed: Replace min(3, participants|length) with proper Jinja2 logic -->
|
||||
{% set max_breakdowns = 3 %}
|
||||
{% if participants|length < 3 %}
|
||||
{% set max_breakdowns = participants|length %}
|
||||
{% endif %}
|
||||
|
||||
{% for i in range(max_breakdowns) %}
|
||||
{% set participant = participants[i] %}
|
||||
<div class="tournament-breakdown">
|
||||
<div class="breakdown-title">
|
||||
{% if participant.rank == 1 %}🥇{% elif participant.rank == 2 %}🥈{% elif participant.rank == 3 %}🥉{% endif %}
|
||||
{{ participant.name }}'s Tournament History
|
||||
</div>
|
||||
<div class="breakdown-grid">
|
||||
{% for tournament_num in range(1, 7) %}
|
||||
{% set result = participant.tournament_results
|
||||
| selectattr("tournament", "equalto", tournament_num)
|
||||
| list
|
||||
| first %}
|
||||
|
||||
{% if result %}
|
||||
{% if (result.joker is defined and result.joker) or (result.participated is defined and not result.participated) %}
|
||||
<div class="tournament-item joker">
|
||||
<div class="tournament-number">T{{ tournament_num }}</div>
|
||||
<div class="tournament-score">🃏 Joker</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% set all_participated_scores = participant.tournament_results
|
||||
| selectattr("participated", "defined")
|
||||
| selectattr("participated")
|
||||
| selectattr("score", "defined")
|
||||
| map(attribute="score")
|
||||
| list %}
|
||||
{% set sorted_scores = all_participated_scores | sort(reverse=true) %}
|
||||
{% set is_excluded = sorted_scores | length > 5 and result.score == sorted_scores[-1] %}
|
||||
|
||||
{% if is_excluded %}
|
||||
<div class="tournament-item excluded">
|
||||
<div class="tournament-number">T{{ tournament_num }}</div>
|
||||
<div class="tournament-score">{{ result.score }} ❌</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="tournament-item counted">
|
||||
<div class="tournament-number">T{{ tournament_num }}</div>
|
||||
<div class="tournament-score">{{ result.score }} ✅</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="tournament-item">
|
||||
<div class="tournament-number">T{{ tournament_num }}</div>
|
||||
<div class="tournament-score">-</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Scoring Legend -->
|
||||
<div class="scoring-legend">
|
||||
<div class="legend-title">📖 How Final Scores Are Calculated</div>
|
||||
<div class="legend-content">
|
||||
<div class="legend-rule">
|
||||
<strong>🏆 League Rule:</strong> Best 5 out of 6 tournaments count toward final ranking
|
||||
</div>
|
||||
<div class="legend-rule">
|
||||
<strong>🃏 Joker System:</strong> Each player can skip 1 tournament without penalty
|
||||
</div>
|
||||
<div class="legend-items-mobile">
|
||||
<div class="legend-item-mobile">
|
||||
<span class="legend-dot counted">✅</span>
|
||||
<span>Counted toward final score</span>
|
||||
</div>
|
||||
<div class="legend-item-mobile">
|
||||
<span class="legend-dot excluded">❌</span>
|
||||
<span>Excluded (worst score)</span>
|
||||
</div>
|
||||
<div class="legend-item-mobile">
|
||||
<span class="legend-dot joker">🃏</span>
|
||||
<span>Joker used (skipped)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="result-card">
|
||||
<div style="text-align: center; width: 100%; color: #666;">
|
||||
<h3>No League Results Available</h3>
|
||||
<p>League results will appear here when the league is complete.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Confetti container -->
|
||||
<div class="confetti" id="confetti"></div>
|
||||
|
||||
<script>
|
||||
function createConfetti() {
|
||||
const confettiContainer = document.getElementById('confetti');
|
||||
const colors = ['#ffd700', '#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7'];
|
||||
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const confettiPiece = document.createElement('div');
|
||||
confettiPiece.className = 'confetti-piece';
|
||||
confettiPiece.style.left = Math.random() * 100 + '%';
|
||||
confettiPiece.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
|
||||
confettiPiece.style.animationDelay = Math.random() * 3 + 's';
|
||||
confettiPiece.style.animationDuration = (Math.random() * 2 + 2) + 's';
|
||||
confettiContainer.appendChild(confettiPiece);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
confettiContainer.innerHTML = '';
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function addMobileTouchFeedback() {
|
||||
const touchElements = document.querySelectorAll('.nav-btn:not(.disabled), .result-card');
|
||||
|
||||
touchElements.forEach(element => {
|
||||
element.addEventListener('touchstart', function(e) {
|
||||
if (!this.disabled) {
|
||||
this.style.transform = 'scale(0.98) translateY(0)';
|
||||
this.style.transition = 'transform 0.1s ease';
|
||||
}
|
||||
});
|
||||
|
||||
element.addEventListener('touchend', function(e) {
|
||||
if (!this.disabled) {
|
||||
setTimeout(() => {
|
||||
this.style.transform = '';
|
||||
this.style.transition = 'transform 0.2s ease';
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
element.addEventListener('touchcancel', function(e) {
|
||||
if (!this.disabled) {
|
||||
this.style.transform = '';
|
||||
this.style.transition = 'transform 0.2s ease';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showChampionCelebration() {
|
||||
{% if participants and participants|length > 0 %}
|
||||
createConfetti();
|
||||
|
||||
setTimeout(() => {
|
||||
const firstCard = document.querySelector('.result-card.rank-1');
|
||||
if (firstCard) {
|
||||
firstCard.style.boxShadow = '0 0 30px rgba(255, 215, 0, 0.5)';
|
||||
setTimeout(() => {
|
||||
firstCard.style.boxShadow = '';
|
||||
}, 2000);
|
||||
}
|
||||
}, 1000);
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(event) {
|
||||
if (event.key === 'r' || event.key === 'R') {
|
||||
event.preventDefault();
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
addMobileTouchFeedback();
|
||||
|
||||
setTimeout(() => {
|
||||
showChampionCelebration();
|
||||
}, 1500);
|
||||
|
||||
console.log('📱 Mobile League Results loaded');
|
||||
console.log('🏆 League participants:', {{ participants|length if participants else 0 }});
|
||||
console.log('🎯 Tournament type:', '{{ league.tournament_type }}');
|
||||
{% if participants and participants|length > 0 %}
|
||||
console.log('🏆 League Champion:', '{{ participants[0].name }}');
|
||||
console.log('🏆 Winning score:', {{ participants[0].final_score }});
|
||||
{% endif %}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,382 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,696 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>📱 Tournament Results</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #f5f5f5;
|
||||
font-family: Arial, sans-serif;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.mobile-navbar {
|
||||
background: white;
|
||||
color: black;
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 2px solid #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 1.4rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
background: #f8f9fa;
|
||||
border: 2px solid #e9ecef;
|
||||
cursor: pointer;
|
||||
padding: 8px 12px;
|
||||
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.8rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
min-width: 44px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-btn:hover {
|
||||
background: #e9ecef;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-1px);
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
background: #f1f3f4;
|
||||
border-color: #dadce0;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 20px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
padding: 25px 20px;
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.results-title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.results-subtitle {
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.results-badge {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.result-card.podium {
|
||||
border-left: 5px solid;
|
||||
}
|
||||
|
||||
.result-card.rank-1 {
|
||||
border-left-color: #ffd700;
|
||||
background: linear-gradient(135deg, #fff9e6 0%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.result-card.rank-2 {
|
||||
border-left-color: #c0c0c0;
|
||||
background: linear-gradient(135deg, #f5f5f5 0%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.result-card.rank-3 {
|
||||
border-left-color: #cd7f32;
|
||||
background: linear-gradient(135deg, #fdf6f0 0%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.result-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.rank-display {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.rank-number {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.result-card.rank-1 .rank-number { color: #b8860b; }
|
||||
.result-card.rank-2 .rank-number { color: #696969; }
|
||||
.result-card.rank-3 .rank-number { color: #8b4513; }
|
||||
|
||||
.rank-suffix {
|
||||
font-size: 0.7rem;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.medal {
|
||||
font-size: 1.5rem;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.participant-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.participant-name {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.participant-id {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
padding: 3px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.score-display {
|
||||
text-align: right;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.score-number {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
color: #28a745;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.score-label {
|
||||
font-size: 0.8rem;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tournament-info {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.info-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.congratulations {
|
||||
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
padding: 25px 20px;
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.congrats-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.congrats-message {
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* Mobile landscape optimization */
|
||||
@media (orientation: landscape) and (max-height: 500px) {
|
||||
.mobile-navbar {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
padding: 20px 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.results-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.results-subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
padding: 15px;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.rank-number {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.participant-name {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.score-number {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 6px 10px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.results-title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.results-subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
padding: 15px;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.rank-display {
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.rank-number {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.participant-name {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.score-display {
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
.score-number {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.congratulations {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.congrats-title {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.congrats-message {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 6px 8px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation for results appearance */
|
||||
.result-card {
|
||||
animation: slideInUp 0.5s ease forwards;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
.result-card:nth-child(1) { animation-delay: 0.1s; }
|
||||
.result-card:nth-child(2) { animation-delay: 0.2s; }
|
||||
.result-card:nth-child(3) { animation-delay: 0.3s; }
|
||||
.result-card:nth-child(4) { animation-delay: 0.4s; }
|
||||
.result-card:nth-child(5) { animation-delay: 0.5s; }
|
||||
.result-card:nth-child(6) { animation-delay: 0.6s; }
|
||||
|
||||
@keyframes slideInUp {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Celebration confetti effect */
|
||||
.confetti {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.confetti-piece {
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #ffd700;
|
||||
animation: confetti-fall 3s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes confetti-fall {
|
||||
0% {
|
||||
transform: translateY(-100vh) rotate(0deg);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(100vh) rotate(720deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Results status indicator */
|
||||
.results-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: #28a745;
|
||||
color: white;
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
margin-left: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="mobile-navbar">
|
||||
<div class="navbar-title">
|
||||
🏆 Results
|
||||
<span class="results-indicator">FINAL</span>
|
||||
</div>
|
||||
<div class="navbar-controls">
|
||||
<a href="/mobile/streams" class="nav-btn">📷</a>
|
||||
|
||||
{% if tournament_active %}
|
||||
<a href="/mobile/draft" class="nav-btn">📋</a>
|
||||
{% endif %}
|
||||
|
||||
<a href="/mobile/results" class="nav-btn active">🏆</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
{% if participants %}
|
||||
{% if participants|length > 0 %}
|
||||
<div class="congratulations">
|
||||
<div class="congrats-title">🎉 Tournament Complete!</div>
|
||||
<div class="congrats-message">
|
||||
Congratulations to {{ participants[0].name }} for winning!
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="results-list">
|
||||
{% for participant in participants %}
|
||||
<div class="result-card {{ 'podium rank-' + participant.rank|string if participant.rank <= 3 else '' }}">
|
||||
<div class="rank-display">
|
||||
<div class="rank-number">{{ participant.rank }}</div>
|
||||
<div class="rank-suffix">
|
||||
{% if participant.rank == 1 %}st
|
||||
{% elif participant.rank == 2 %}nd
|
||||
{% elif participant.rank == 3 %}rd
|
||||
{% else %}th
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if participant.rank == 1 %}
|
||||
<div class="medal">🥇</div>
|
||||
{% elif participant.rank == 2 %}
|
||||
<div class="medal">🥈</div>
|
||||
{% elif participant.rank == 3 %}
|
||||
<div class="medal">🥉</div>
|
||||
{% endif %}
|
||||
</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="score-label">Points</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="result-card">
|
||||
<div style="text-align: center; width: 100%; color: #666;">
|
||||
<h3>No Results Available</h3>
|
||||
<p>Tournament results will appear here when scoring is complete.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Tournament Info -->
|
||||
<div class="tournament-info">
|
||||
<div class="info-title">📊 Tournament Information</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<span class="info-label">Total Participants:</span>
|
||||
<span class="info-value">{{ participants|length if participants else 0 }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Tournament ID:</span>
|
||||
<span class="info-value">{{ results.tournament_id[:10] if results.tournament_id else 'Unknown' }}...</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Created:</span>
|
||||
<span class="info-value">{{ results.created_at[:10] if results.created_at else 'Unknown' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Status:</span>
|
||||
<span class="info-value" style="color: #28a745; font-weight: bold;">Completed</span>
|
||||
</div>
|
||||
{% if participants and participants|length > 0 %}
|
||||
<div class="info-item">
|
||||
<span class="info-label">Highest Score:</span>
|
||||
<span class="info-value" style="color: #28a745; font-weight: bold;">{{ participants[0].total_score }} points</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Winner:</span>
|
||||
<span class="info-value" style="color: #ffd700; font-weight: bold;">{{ participants[0].name }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confetti container -->
|
||||
<div class="confetti" id="confetti"></div>
|
||||
|
||||
<script>
|
||||
function createConfetti() {
|
||||
const confettiContainer = document.getElementById('confetti');
|
||||
const colors = ['#ffd700', '#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7'];
|
||||
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const confettiPiece = document.createElement('div');
|
||||
confettiPiece.className = 'confetti-piece';
|
||||
confettiPiece.style.left = Math.random() * 100 + '%';
|
||||
confettiPiece.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
|
||||
confettiPiece.style.animationDelay = Math.random() * 3 + 's';
|
||||
confettiPiece.style.animationDuration = (Math.random() * 2 + 2) + 's';
|
||||
confettiContainer.appendChild(confettiPiece);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
confettiContainer.innerHTML = '';
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function addMobileTouchFeedback() {
|
||||
const touchElements = document.querySelectorAll('.nav-btn:not(.disabled), .result-card');
|
||||
|
||||
touchElements.forEach(element => {
|
||||
element.addEventListener('touchstart', function(e) {
|
||||
if (!this.disabled) {
|
||||
this.style.transform = 'scale(0.98) translateY(0)';
|
||||
this.style.transition = 'transform 0.1s ease';
|
||||
}
|
||||
});
|
||||
|
||||
element.addEventListener('touchend', function(e) {
|
||||
if (!this.disabled) {
|
||||
setTimeout(() => {
|
||||
this.style.transform = '';
|
||||
this.style.transition = 'transform 0.2s ease';
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
element.addEventListener('touchcancel', function(e) {
|
||||
if (!this.disabled) {
|
||||
this.style.transform = '';
|
||||
this.style.transition = 'transform 0.2s ease';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showWinnerCelebration() {
|
||||
{% if participants and participants|length > 0 %}
|
||||
createConfetti();
|
||||
|
||||
setTimeout(() => {
|
||||
const firstCard = document.querySelector('.result-card.rank-1');
|
||||
if (firstCard) {
|
||||
firstCard.style.boxShadow = '0 0 30px rgba(255, 215, 0, 0.5)';
|
||||
setTimeout(() => {
|
||||
firstCard.style.boxShadow = '';
|
||||
}, 2000);
|
||||
}
|
||||
}, 1000);
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(event) {
|
||||
if (event.key === 'r' || event.key === 'R') {
|
||||
event.preventDefault();
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
addMobileTouchFeedback();
|
||||
|
||||
setTimeout(() => {
|
||||
showWinnerCelebration();
|
||||
}, 1500);
|
||||
|
||||
console.log('📱 Mobile Results with Smart Navbar loaded');
|
||||
console.log('🏆 Tournament Results:', {
|
||||
participants: {{ participants|length if participants else 0 }},
|
||||
{% if participants and participants|length > 0 %}
|
||||
winner: '{{ participants[0].name }}',
|
||||
winning_score: {{ participants[0].total_score }}
|
||||
{% endif %}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+81
-137
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>📱 Camera Streams</title>
|
||||
<title data-i18n="mobile.camera_streams">📱 Camera Streams</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimal-ui, viewport-fit=cover">
|
||||
<style>
|
||||
* {
|
||||
@@ -294,31 +294,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Floating Fullscreen Button */
|
||||
/* Floating fullscreen button */
|
||||
.floating-fullscreen-btn {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
background: #007bff;
|
||||
bottom: 30px;
|
||||
right: 30px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: rgba(0, 123, 255, 0.9);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
font-size: 1.2rem;
|
||||
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.4);
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(10px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.floating-fullscreen-btn:hover {
|
||||
background: #0056b3;
|
||||
background: rgba(0, 123, 255, 1);
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 6px 20px rgba(0, 123, 255, 0.5);
|
||||
box-shadow: 0 6px 20px rgba(0, 123, 255, 0.4);
|
||||
}
|
||||
|
||||
.floating-fullscreen-btn:active {
|
||||
@@ -327,59 +328,8 @@
|
||||
|
||||
.floating-fullscreen-btn.hidden {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Hide floating button when in fullscreen */
|
||||
:fullscreen .floating-fullscreen-btn,
|
||||
:-webkit-full-screen .floating-fullscreen-btn,
|
||||
:-moz-full-screen .floating-fullscreen-btn,
|
||||
:-ms-fullscreen .floating-fullscreen-btn {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Fullscreen adjustments */
|
||||
:fullscreen .mobile-navbar,
|
||||
:-webkit-full-screen .mobile-navbar,
|
||||
:-moz-full-screen .mobile-navbar,
|
||||
:-ms-fullscreen .mobile-navbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:fullscreen .container,
|
||||
:-webkit-full-screen .container,
|
||||
:-moz-full-screen .container,
|
||||
:-ms-fullscreen .container {
|
||||
padding: 10px;
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
}
|
||||
|
||||
html:fullscreen .mobile-navbar,
|
||||
html:-webkit-full-screen .mobile-navbar,
|
||||
html:-moz-full-screen .mobile-navbar,
|
||||
html:-ms-fullscreen .mobile-navbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
html:fullscreen .container,
|
||||
html:-webkit-full-screen .container,
|
||||
html:-moz-full-screen .container,
|
||||
html:-ms-fullscreen .container {
|
||||
padding: 10px;
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.floating-fullscreen-btn {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
/* Tournament status indicator */
|
||||
@@ -412,7 +362,7 @@
|
||||
<body>
|
||||
<div class="mobile-navbar">
|
||||
<div class="navbar-title">
|
||||
📷 Streams
|
||||
<span data-i18n="mobile.camera_streams">📷 Prenosi Kamer</span>
|
||||
{% if tournament_active %}
|
||||
<span class="tournament-indicator">LIVE</span>
|
||||
{% elif results_available %}
|
||||
@@ -420,10 +370,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="navbar-controls">
|
||||
<!-- Always show all 3 navigation buttons -->
|
||||
<a href="/mobile/streams" class="nav-btn active">📷</a>
|
||||
<a href="/mobile/draft" class="nav-btn">📋</a>
|
||||
<a href="/mobile/results" class="nav-btn">🏆</a>
|
||||
<!-- Mobile users stay on mobile - no desktop access -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -433,7 +380,7 @@
|
||||
{% set camera_id = i %}
|
||||
<div class="camera-option" onclick="openCameraFullscreen({{ camera_id }})">
|
||||
<div class="stream-preview">
|
||||
<div class="stream-loading">Loading...</div>
|
||||
<div class="stream-loading" data-i18n="general.loading">Nalaganje...</div>
|
||||
<img src="{{ streams[i-1].url }}"
|
||||
alt="Camera {{ camera_id }}"
|
||||
class="stream-img"
|
||||
@@ -454,8 +401,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Floating Fullscreen Button -->
|
||||
<button class="floating-fullscreen-btn" id="fullscreenBtn" onclick="toggleFullscreen()" title="Enter Fullscreen">
|
||||
<!-- Floating fullscreen button -->
|
||||
<button class="floating-fullscreen-btn" onclick="toggleFullscreen()" id="fullscreenBtn">
|
||||
⛶
|
||||
</button>
|
||||
|
||||
@@ -536,90 +483,69 @@
|
||||
if (event.key >= '1' && event.key <= '6') {
|
||||
const cameraId = parseInt(event.key);
|
||||
openCameraFullscreen(cameraId);
|
||||
} else if (event.key === 'f' || event.key === 'F') {
|
||||
event.preventDefault();
|
||||
toggleFullscreen();
|
||||
}
|
||||
});
|
||||
|
||||
// Fullscreen functionality
|
||||
function toggleFullscreen() {
|
||||
if (isFullscreen()) {
|
||||
exitFullscreen();
|
||||
const elem = document.documentElement;
|
||||
|
||||
if (!document.fullscreenElement) {
|
||||
if (elem.requestFullscreen) {
|
||||
elem.requestFullscreen();
|
||||
} else if (elem.mozRequestFullScreen) {
|
||||
elem.mozRequestFullScreen();
|
||||
} else if (elem.webkitRequestFullscreen) {
|
||||
elem.webkitRequestFullscreen();
|
||||
} else if (elem.msRequestFullscreen) {
|
||||
elem.msRequestFullscreen();
|
||||
}
|
||||
} else {
|
||||
enterFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
function isFullscreen() {
|
||||
return document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.msFullscreenElement;
|
||||
}
|
||||
|
||||
function enterFullscreen() {
|
||||
const element = document.documentElement;
|
||||
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen().catch(err => console.log('Fullscreen error:', err));
|
||||
} else if (element.webkitRequestFullscreen) {
|
||||
element.webkitRequestFullscreen();
|
||||
} else if (element.mozRequestFullScreen) {
|
||||
element.mozRequestFullScreen();
|
||||
} else if (element.msRequestFullscreen) {
|
||||
element.msRequestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
function exitFullscreen() {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen().catch(err => console.log('Exit fullscreen error:', err));
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleFullscreenChange() {
|
||||
const fullscreenBtn = document.getElementById('fullscreenBtn');
|
||||
const navbar = document.querySelector('.mobile-navbar');
|
||||
const isFS = isFullscreen();
|
||||
|
||||
if (fullscreenBtn) {
|
||||
if (isFS) {
|
||||
fullscreenBtn.innerHTML = '⛉';
|
||||
fullscreenBtn.title = 'Exit Fullscreen';
|
||||
fullscreenBtn.classList.add('hidden');
|
||||
|
||||
if (navbar) {
|
||||
navbar.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
fullscreenBtn.innerHTML = '⛶';
|
||||
fullscreenBtn.title = 'Enter Fullscreen';
|
||||
fullscreenBtn.classList.remove('hidden');
|
||||
|
||||
if (navbar) {
|
||||
navbar.style.display = 'flex';
|
||||
}
|
||||
if (!fullscreenBtn) return;
|
||||
|
||||
const isFullscreen = document.fullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.msFullscreenElement;
|
||||
|
||||
if (isFullscreen) {
|
||||
fullscreenBtn.textContent = '⛶';
|
||||
fullscreenBtn.classList.remove('hidden');
|
||||
} else {
|
||||
fullscreenBtn.textContent = '⛶';
|
||||
fullscreenBtn.classList.remove('hidden');
|
||||
|
||||
// If we're coming back from a fullscreen camera view, redirect to streams
|
||||
if (window.location.pathname.includes('/fullscreen/')) {
|
||||
window.location.href = '/mobile/streams';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
||||
document.addEventListener('webkitfullscreenchange', handleFullscreenChange);
|
||||
document.addEventListener('mozfullscreenchange', handleFullscreenChange);
|
||||
document.addEventListener('MSFullscreenChange', handleFullscreenChange);
|
||||
|
||||
// Initialize
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
addMobileTouchFeedback();
|
||||
handleFullscreenChange();
|
||||
|
||||
// Add fullscreen event listeners
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
||||
document.addEventListener('mozfullscreenchange', handleFullscreenChange);
|
||||
document.addEventListener('webkitfullscreenchange', handleFullscreenChange);
|
||||
document.addEventListener('msfullscreenchange', handleFullscreenChange);
|
||||
|
||||
console.log('📱 Mobile camera selection loaded with fixed navigation');
|
||||
console.log('🏆 Tournament active:', {{ 'true' if tournament_active else 'false' }});
|
||||
console.log('📊 Results available:', {{ 'true' if results_available else 'false' }});
|
||||
@@ -631,14 +557,17 @@
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// Only refresh streams if page is visible
|
||||
setInterval(() => {
|
||||
if (document.visibilityState !== 'visible') return;
|
||||
|
||||
const streams = document.querySelectorAll('.stream-img');
|
||||
streams.forEach(stream => {
|
||||
if (stream.complete && stream.naturalHeight === 0) {
|
||||
stream.src = stream.src.split('?')[0] + '?refresh=' + Date.now();
|
||||
}
|
||||
});
|
||||
}, 30000);
|
||||
}, 60000); // Reduced from 30s to 60s
|
||||
|
||||
if (window.innerHeight > window.innerWidth) {
|
||||
const hint = document.createElement('div');
|
||||
@@ -666,5 +595,20 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Internationalization Support -->
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
<script>
|
||||
// Initialize translations with server data
|
||||
if (typeof {{ translations|tojson }} !== 'undefined') {
|
||||
currentTranslations = {{ translations|tojson }};
|
||||
currentLanguage = '{{ current_language }}';
|
||||
|
||||
// Apply translations when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
translatePage();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+119
-127
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Tournament Archive</title>
|
||||
<title data-i18n="navigation.archive">📚 Arhiv</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
* {
|
||||
@@ -63,28 +63,17 @@
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.nav-btn.primary {
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.primary:hover {
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.success {
|
||||
background: #28a745;
|
||||
border-color: #1e7e34;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.success:hover {
|
||||
background: #1e7e34;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.danger {
|
||||
background: #dc3545;
|
||||
border-color: #c82333;
|
||||
@@ -172,36 +161,36 @@
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
/* Enhanced Stats Overview */
|
||||
.stats-overview {
|
||||
/* Stats Overview */
|
||||
.stats-badges {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
.stat-badge {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e9ecef;
|
||||
transition: transform 0.2s ease;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
.stat-badge:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1.8rem;
|
||||
.stat-number {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
@@ -209,23 +198,10 @@
|
||||
|
||||
.stat-label {
|
||||
color: #666;
|
||||
font-size: 0.8rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Target-specific stat cards */
|
||||
.stat-card.target-40 {
|
||||
border-left: 4px solid #dc3545;
|
||||
}
|
||||
|
||||
.stat-card.target-20 {
|
||||
border-left: 4px solid #ffc107;
|
||||
}
|
||||
|
||||
.stat-card.target-4 {
|
||||
border-left: 4px solid #28a745;
|
||||
}
|
||||
|
||||
/* Enhanced Archive Grid */
|
||||
.archive-grid {
|
||||
@@ -592,7 +568,7 @@
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.stats-overview {
|
||||
.stats-badges {
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
}
|
||||
|
||||
@@ -631,46 +607,47 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar">
|
||||
<div class="navbar-title">📚 Tournament Archive</div>
|
||||
<div class="navbar-title" data-i18n="navigation.archive">📚 Arhiv</div>
|
||||
<div class="navbar-controls">
|
||||
<a href="/" class="nav-btn">← Dashboard</a>
|
||||
<a href="/tournament" class="nav-btn">🏆 Tournament</a>
|
||||
<a href="/archive/player-analysis" class="nav-btn success">👤 Player Analysis</a>
|
||||
<a href="/" class="nav-btn">📺 <span data-i18n="navigation.dashboard">Dashboard</span></a>
|
||||
<a href="/tournament" class="nav-btn">🏆 <span data-i18n="navigation.tournament">Tournament</span></a>
|
||||
<a href="/archive/player-analysis" class="nav-btn">👤 <span data-i18n="players.player_analysis">Player Analysis</span></a>
|
||||
<a href="/archive" class="nav-btn active">📚 <span data-i18n="navigation.archive">Archive</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- Enhanced Stats Overview -->
|
||||
<div class="stats-overview">
|
||||
<div class="stat-card">
|
||||
<!-- Stats Overview -->
|
||||
<div class="stats-badges">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">🎖️</span>
|
||||
<div class="stat-value">{{ leagues|length if leagues else 0 }}</div>
|
||||
<div class="stat-label">Completed Leagues</div>
|
||||
<div class="stat-number">{{ leagues|length if leagues else 0 }}</div>
|
||||
<div class="stat-label" data-i18n="league.completed_leagues">Zaključene Lige</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">🏆</span>
|
||||
<div class="stat-value">{{ tournaments|length if tournaments else 0 }}</div>
|
||||
<div class="stat-label">Total Tournaments</div>
|
||||
<div class="stat-number">{{ tournaments|length if tournaments else 0 }}</div>
|
||||
<div class="stat-label" data-i18n="analysis.total_tournaments">Skupaj Turnirjev</div>
|
||||
</div>
|
||||
<div class="stat-card target-40">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">💪</span>
|
||||
<div class="stat-value">{{ tournaments|selectattr('tournament_type', 'equalto', '40_targets')|list|length if tournaments else 0 }}</div>
|
||||
<div class="stat-label">40-Target Tournaments</div>
|
||||
<div class="stat-number">{{ tournaments|selectattr('tournament_type', 'equalto', '40_targets')|list|length if tournaments else 0 }}</div>
|
||||
<div class="stat-label" data-i18n="tournament_types.40_target_tournaments">40-Tarčni Turnirji</div>
|
||||
</div>
|
||||
<div class="stat-card target-20">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">⚡</span>
|
||||
<div class="stat-value">{{ tournaments|selectattr('tournament_type', 'equalto', '20_targets')|list|length if tournaments else 0 }}</div>
|
||||
<div class="stat-label">20-Target Tournaments</div>
|
||||
<div class="stat-number">{{ tournaments|selectattr('tournament_type', 'equalto', '20_targets')|list|length if tournaments else 0 }}</div>
|
||||
<div class="stat-label" data-i18n="tournament_types.20_target_tournaments">20-Tarčni Turnirji</div>
|
||||
</div>
|
||||
<div class="stat-card target-4">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">🎯</span>
|
||||
<div class="stat-value">{{ tournaments|selectattr('tournament_type', 'equalto', '4_targets')|list|length if tournaments else 0 }}</div>
|
||||
<div class="stat-label">4-Target <br> Tournaments</div>
|
||||
<div class="stat-number">{{ tournaments|selectattr('tournament_type', 'equalto', '4_targets')|list|length if tournaments else 0 }}</div>
|
||||
<div class="stat-label" data-i18n="tournament_types.4_target_tournaments">4-Tarčni Turnirji</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">👥</span>
|
||||
<div class="stat-value">{{ stats.total_players if stats else 0 }}</div>
|
||||
<div class="stat-label">Active Players</div>
|
||||
<div class="stat-number">{{ stats.total_players if stats else 0 }}</div>
|
||||
<div class="stat-label" data-i18n="players.enabled_players">Omogočeni Igralci</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -678,23 +655,23 @@
|
||||
{% if leagues %}
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
<span>🎖️ League Championships</span>
|
||||
<span data-i18n="messages.league_championships">🎖️ Ligaška Prvenstva</span>
|
||||
</div>
|
||||
<div class="archive-grid" id="leagues-grid">
|
||||
{% for league in leagues %}
|
||||
<div class="archive-card league" data-status="{{ 'completed' if league.completed_tournaments == league.total_tournaments else 'incomplete' }}" data-date="{{ league.archived_at }}" onclick="viewLeague('{{ league.filename }}')">
|
||||
<div class="archive-header">
|
||||
<div>
|
||||
<div class="archive-title">League Championship</div>
|
||||
<div class="archive-subtitle">{{ league.created_at[:10] if league.created_at != 'Unknown' else 'Unknown Date' }}</div>
|
||||
<div class="archive-title" data-i18n="messages.league_championship">League Championship</div>
|
||||
<div class="archive-subtitle">{{ league.created_at[:10] if league.created_at != 'Unknown' else translations.messages.unknown_date if translations else 'Neznan Datum' }}</div>
|
||||
</div>
|
||||
<span class="archive-badge badge-league">League</span>
|
||||
<span class="archive-badge badge-league" data-i18n="league.league">Liga</span>
|
||||
</div>
|
||||
<div class="archive-content">
|
||||
<div class="archive-info">
|
||||
<div class="info-item">
|
||||
<span class="info-icon">👥</span>
|
||||
<span class="info-value">{{ league.participants_count }} players</span>
|
||||
<span class="info-value">{{ league.participants_count }} <span data-i18n="players.players_label">players</span></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">🎯</span>
|
||||
@@ -702,15 +679,15 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">📅</span>
|
||||
<span class="info-value">{{ league.archived_at[:10] if league.archived_at != 'Unknown' else 'Unknown Date' }}</span>
|
||||
<span class="info-value">{{ league.archived_at[:10] if league.archived_at != 'Unknown' else translations.messages.unknown_date if translations else 'Neznan Datum' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">✅</span>
|
||||
<span class="info-value">{{ league.completed_tournaments }}/{{ league.total_tournaments }} tournaments</span>
|
||||
<span class="info-value">{{ league.completed_tournaments }}/{{ league.total_tournaments }} <span data-i18n="tournament.tournaments">tournaments</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="archive-actions">
|
||||
<a href="/archive/league/{{ league.filename }}" class="action-btn view-btn">🏆 View</a>
|
||||
<a href="/archive/league/{{ league.filename }}" class="action-btn view-btn">🏆 <span data-i18n="general.view">View</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -727,23 +704,23 @@
|
||||
{% if tournaments_40 %}
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
<span>💪 40-Target Tournaments</span>
|
||||
<span data-i18n="tournament_types.40_target_tournaments">💪 40-Tarčni Turnirji</span>
|
||||
</div>
|
||||
<div class="archive-grid" id="tournaments-40-grid">
|
||||
{% for tournament in tournaments_40 %}
|
||||
<div class="archive-card tournament-40" data-status="{{ 'finished' if tournament.tournament_finished else 'incomplete' }}" data-date="{{ tournament.archived_at }}" data-targets="40" onclick="viewTournament('{{ tournament.filename }}')">
|
||||
<div class="archive-header">
|
||||
<div>
|
||||
<div class="archive-title">40-Target Tournament</div>
|
||||
<div class="archive-subtitle">{{ tournament.created_at[:10] if tournament.created_at != 'Unknown' else 'Unknown Date' }}</div>
|
||||
<div class="archive-title" data-i18n="messages.40_target_tournament">40-Tarčni Turnir</div>
|
||||
<div class="archive-subtitle">{{ tournament.created_at[:10] if tournament.created_at != 'Unknown' else translations.messages.unknown_date if translations else 'Neznan Datum' }}</div>
|
||||
</div>
|
||||
<span class="archive-badge badge-40-targets">40 Targets</span>
|
||||
<span class="archive-badge badge-40-targets" data-i18n="tournament_types.40_targets">40 Targets</span>
|
||||
</div>
|
||||
<div class="archive-content">
|
||||
<div class="archive-info">
|
||||
<div class="info-item">
|
||||
<span class="info-icon">👥</span>
|
||||
<span class="info-value">{{ tournament.participants_count }} players</span>
|
||||
<span class="info-value">{{ tournament.participants_count }} <span data-i18n="players.players_label">players</span></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">🎯</span>
|
||||
@@ -751,15 +728,15 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">📅</span>
|
||||
<span class="info-value">{{ tournament.archived_at[:10] if tournament.archived_at != 'Unknown' else 'Unknown Date' }}</span>
|
||||
<span class="info-value">{{ tournament.archived_at[:10] if tournament.archived_at != 'Unknown' else translations.messages.unknown_date if translations else 'Neznan Datum' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">🏁</span>
|
||||
<span class="info-value">{{ 'Finished' if tournament.tournament_finished else 'Incomplete' }}</span>
|
||||
<span class="info-value">{{ (translations.tournament.finished if translations else 'Finished') if tournament.tournament_finished else (translations.scoring.in_progress if translations else 'Incomplete') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="archive-actions">
|
||||
<a href="/archive/tournament/{{ tournament.filename }}" class="action-btn view-btn">📊 View</a>
|
||||
<a href="/archive/tournament/{{ tournament.filename }}" class="action-btn view-btn">📊 <span data-i18n="general.view">View</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -773,23 +750,23 @@
|
||||
{% if tournaments_20 %}
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
<span>⚡ 20-Target Tournaments</span>
|
||||
<span data-i18n="tournament_types.20_target_tournaments">⚡ 20-Tarčni Turnirji</span>
|
||||
</div>
|
||||
<div class="archive-grid" id="tournaments-20-grid">
|
||||
{% for tournament in tournaments_20 %}
|
||||
<div class="archive-card tournament-20" data-status="{{ 'finished' if tournament.tournament_finished else 'incomplete' }}" data-date="{{ tournament.archived_at }}" data-targets="20" onclick="viewTournament('{{ tournament.filename }}')">
|
||||
<div class="archive-header">
|
||||
<div>
|
||||
<div class="archive-title">20-Target Tournament</div>
|
||||
<div class="archive-subtitle">{{ tournament.created_at[:10] if tournament.created_at != 'Unknown' else 'Unknown Date' }}</div>
|
||||
<div class="archive-title" data-i18n="messages.20_target_tournament">20-Tarčni Turnir</div>
|
||||
<div class="archive-subtitle">{{ tournament.created_at[:10] if tournament.created_at != 'Unknown' else translations.messages.unknown_date if translations else 'Neznan Datum' }}</div>
|
||||
</div>
|
||||
<span class="archive-badge badge-20-targets">20 Targets</span>
|
||||
<span class="archive-badge badge-20-targets" data-i18n="tournament_types.20_targets">20 Targets</span>
|
||||
</div>
|
||||
<div class="archive-content">
|
||||
<div class="archive-info">
|
||||
<div class="info-item">
|
||||
<span class="info-icon">👥</span>
|
||||
<span class="info-value">{{ tournament.participants_count }} players</span>
|
||||
<span class="info-value">{{ tournament.participants_count }} <span data-i18n="players.players_label">players</span></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">🎯</span>
|
||||
@@ -797,15 +774,15 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">📅</span>
|
||||
<span class="info-value">{{ tournament.archived_at[:10] if tournament.archived_at != 'Unknown' else 'Unknown Date' }}</span>
|
||||
<span class="info-value">{{ tournament.archived_at[:10] if tournament.archived_at != 'Unknown' else translations.messages.unknown_date if translations else 'Neznan Datum' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">🏁</span>
|
||||
<span class="info-value">{{ 'Finished' if tournament.tournament_finished else 'Incomplete' }}</span>
|
||||
<span class="info-value">{{ (translations.tournament.finished if translations else 'Finished') if tournament.tournament_finished else (translations.scoring.in_progress if translations else 'Incomplete') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="archive-actions">
|
||||
<a href="/archive/tournament/{{ tournament.filename }}" class="action-btn view-btn">📊 View</a>
|
||||
<a href="/archive/tournament/{{ tournament.filename }}" class="action-btn view-btn">📊 <span data-i18n="general.view">View</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -819,23 +796,23 @@
|
||||
{% if tournaments_4 %}
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
<span>🎯 4-Target Tournaments</span>
|
||||
<span data-i18n="tournament_types.4_target_tournaments">🎯 4-Tarčni Turnirji</span>
|
||||
</div>
|
||||
<div class="archive-grid" id="tournaments-4-grid">
|
||||
{% for tournament in tournaments_4 %}
|
||||
<div class="archive-card tournament-4" data-status="{{ 'finished' if tournament.tournament_finished else 'incomplete' }}" data-date="{{ tournament.archived_at }}" data-targets="4" onclick="viewTournament('{{ tournament.filename }}')">
|
||||
<div class="archive-header">
|
||||
<div>
|
||||
<div class="archive-title">4-Target Tournament</div>
|
||||
<div class="archive-subtitle">{{ tournament.created_at[:10] if tournament.created_at != 'Unknown' else 'Unknown Date' }}</div>
|
||||
<div class="archive-title" data-i18n="messages.4_target_tournament">4-Tarčni Turnir</div>
|
||||
<div class="archive-subtitle">{{ tournament.created_at[:10] if tournament.created_at != 'Unknown' else translations.messages.unknown_date if translations else 'Neznan Datum' }}</div>
|
||||
</div>
|
||||
<span class="archive-badge badge-4-targets">4 Targets</span>
|
||||
<span class="archive-badge badge-4-targets" data-i18n="tournament_types.4_targets">4 Targets</span>
|
||||
</div>
|
||||
<div class="archive-content">
|
||||
<div class="archive-info">
|
||||
<div class="info-item">
|
||||
<span class="info-icon">👥</span>
|
||||
<span class="info-value">{{ tournament.participants_count }} players</span>
|
||||
<span class="info-value">{{ tournament.participants_count }} <span data-i18n="players.players_label">players</span></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">🎯</span>
|
||||
@@ -843,15 +820,15 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">📅</span>
|
||||
<span class="info-value">{{ tournament.archived_at[:10] if tournament.archived_at != 'Unknown' else 'Unknown Date' }}</span>
|
||||
<span class="info-value">{{ tournament.archived_at[:10] if tournament.archived_at != 'Unknown' else translations.messages.unknown_date if translations else 'Neznan Datum' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">🏁</span>
|
||||
<span class="info-value">{{ 'Finished' if tournament.tournament_finished else 'Incomplete' }}</span>
|
||||
<span class="info-value">{{ (translations.tournament.finished if translations else 'Finished') if tournament.tournament_finished else (translations.scoring.in_progress if translations else 'Incomplete') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="archive-actions">
|
||||
<a href="/archive/tournament/{{ tournament.filename }}" class="action-btn view-btn">📊 View</a>
|
||||
<a href="/archive/tournament/{{ tournament.filename }}" class="action-btn view-btn">📊 <span data-i18n="general.view">View</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -865,23 +842,23 @@
|
||||
{% if tournaments_other %}
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
<span>🏆 Other Tournaments</span>
|
||||
<span data-i18n="messages.other_tournaments">🏆 Drugi Turnirji</span>
|
||||
</div>
|
||||
<div class="archive-grid" id="tournaments-other-grid">
|
||||
{% for tournament in tournaments_other %}
|
||||
<div class="archive-card tournament-other" data-status="{{ 'finished' if tournament.tournament_finished else 'incomplete' }}" data-date="{{ tournament.archived_at }}" data-targets="other" onclick="viewTournament('{{ tournament.filename }}')">
|
||||
<div class="archive-header">
|
||||
<div>
|
||||
<div class="archive-title">Tournament ({{ tournament.tournament_type or 'Unknown' }})</div>
|
||||
<div class="archive-subtitle">{{ tournament.created_at[:10] if tournament.created_at != 'Unknown' else 'Unknown Date' }}</div>
|
||||
<div class="archive-title">{{ translations.tournament.tournament if translations else 'Turnir' }} ({{ tournament.tournament_type or (translations.messages.unknown if translations else 'Neznano') }})</div>
|
||||
<div class="archive-subtitle">{{ tournament.created_at[:10] if tournament.created_at != 'Unknown' else translations.messages.unknown_date if translations else 'Neznan Datum' }}</div>
|
||||
</div>
|
||||
<span class="archive-badge badge-tournament">Tournament</span>
|
||||
<span class="archive-badge badge-tournament" data-i18n="tournament.tournament">Turnir</span>
|
||||
</div>
|
||||
<div class="archive-content">
|
||||
<div class="archive-info">
|
||||
<div class="info-item">
|
||||
<span class="info-icon">👥</span>
|
||||
<span class="info-value">{{ tournament.participants_count }} players</span>
|
||||
<span class="info-value">{{ tournament.participants_count }} <span data-i18n="players.players_label">players</span></span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">🎯</span>
|
||||
@@ -889,15 +866,15 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">📅</span>
|
||||
<span class="info-value">{{ tournament.archived_at[:10] if tournament.archived_at != 'Unknown' else 'Unknown Date' }}</span>
|
||||
<span class="info-value">{{ tournament.archived_at[:10] if tournament.archived_at != 'Unknown' else translations.messages.unknown_date if translations else 'Neznan Datum' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">🏁</span>
|
||||
<span class="info-value">{{ 'Finished' if tournament.tournament_finished else 'Incomplete' }}</span>
|
||||
<span class="info-value">{{ (translations.tournament.finished if translations else 'Finished') if tournament.tournament_finished else (translations.scoring.in_progress if translations else 'Incomplete') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="archive-actions">
|
||||
<a href="/archive/tournament/{{ tournament.filename }}" class="action-btn view-btn">📊 View</a>
|
||||
<a href="/archive/tournament/{{ tournament.filename }}" class="action-btn view-btn">📊 <span data-i18n="general.view">View</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -913,9 +890,9 @@
|
||||
<div class="section">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">📚</div>
|
||||
<h3>No Archives Found</h3>
|
||||
<p>Complete some tournaments or leagues to see them archived here</p>
|
||||
<a href="/tournament" class="nav-btn primary" style="margin-top: 15px;">🏆 Start Tournament</a>
|
||||
<h3 data-i18n="messages.no_archives_found">Ni Najdenih Arhivov</h3>
|
||||
<p data-i18n="messages.complete_tournaments_msg">Zaključi nekaj turnirjev ali lig, da jih vidiš arhivirane tukaj</p>
|
||||
<a href="/tournament" class="nav-btn primary" style="margin-top: 15px;" data-i18n="messages.start_tournament">🏆 Začni Turnir</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -924,11 +901,11 @@
|
||||
<!-- Confirmation Modal -->
|
||||
<div class="modal-overlay" id="confirmationModal">
|
||||
<div class="modal">
|
||||
<div class="modal-title" id="modalTitle">Confirm Action</div>
|
||||
<div class="modal-message" id="modalMessage">Are you sure you want to proceed?</div>
|
||||
<div class="modal-title" id="modalTitle" data-i18n="messages.confirm_action">Potrdi Dejanje</div>
|
||||
<div class="modal-message" id="modalMessage" data-i18n="messages.are_you_sure_proceed">Ali ste prepričani, da želite nadaljevati?</div>
|
||||
<div class="modal-buttons">
|
||||
<button class="modal-btn cancel" onclick="closeModal()">Cancel</button>
|
||||
<button class="modal-btn confirm" id="confirmBtn" onclick="confirmAction()">Confirm</button>
|
||||
<button class="modal-btn cancel" onclick="closeModal()" data-i18n="general.cancel">Prekliči</button>
|
||||
<button class="modal-btn confirm" id="confirmBtn" onclick="confirmAction()" data-i18n="general.confirm">Potrdi</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -936,24 +913,24 @@
|
||||
<!-- Edit Modal -->
|
||||
<div class="modal-overlay" id="editModal">
|
||||
<div class="modal">
|
||||
<div class="modal-title" id="editModalTitle">Edit Archive</div>
|
||||
<div class="modal-title" id="editModalTitle" data-i18n="messages.edit_archive">Uredi Arhiv</div>
|
||||
<form class="edit-form" id="editForm">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Archive Name</label>
|
||||
<input type="text" class="form-input" id="editName" placeholder="Enter archive name">
|
||||
<label class="form-label" data-i18n="messages.archive_name">Ime Arhiva</label>
|
||||
<input type="text" class="form-input" id="editName" data-i18n="[placeholder]messages.enter_archive_name" placeholder="Vnesite ime arhiva">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Format Type</label>
|
||||
<label class="form-label" data-i18n="messages.format_type">Tip Formata</label>
|
||||
<select class="form-select" id="editType">
|
||||
<option value="single_elimination">Single Elimination</option>
|
||||
<option value="double_elimination">Double Elimination</option>
|
||||
<option value="round_robin">Round Robin</option>
|
||||
<option value="swiss">Swiss System</option>
|
||||
<option value="league">League Championship</option>
|
||||
<option value="single_elimination" data-i18n="messages.single_elimination">Ena Eliminacija</option>
|
||||
<option value="double_elimination" data-i18n="messages.double_elimination">Dvojna Eliminacija</option>
|
||||
<option value="round_robin" data-i18n="messages.round_robin">Vsak z Vsakim</option>
|
||||
<option value="swiss" data-i18n="messages.swiss_system">Švicarski Sistem</option>
|
||||
<option value="league" data-i18n="messages.league_championship">League Championship</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="targetCountGroup">
|
||||
<label class="form-label">Target Format</label>
|
||||
<label class="form-label" data-i18n="messages.target_format">Format Tarč</label>
|
||||
<select class="form-select" id="editTargetCount">
|
||||
<option value="4_targets">4 Targets (5 shots each)</option>
|
||||
<option value="20_targets">20 Targets (2 shots each)</option>
|
||||
@@ -966,8 +943,8 @@
|
||||
</div>
|
||||
</form>
|
||||
<div class="modal-buttons">
|
||||
<button class="modal-btn cancel" onclick="closeEditModal()">Cancel</button>
|
||||
<button class="modal-btn primary" onclick="saveEdit()">Save Changes</button>
|
||||
<button class="modal-btn cancel" onclick="closeEditModal()" data-i18n="general.cancel">Prekliči</button>
|
||||
<button class="modal-btn primary" onclick="saveEdit()" data-i18n="messages.save_changes">Shrani Spremembe</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -988,8 +965,8 @@
|
||||
// Delete function
|
||||
async function deleteArchive(type, filename) {
|
||||
showModal(
|
||||
'Delete Archive',
|
||||
`Are you sure you want to delete this ${type}? This action cannot be undone.`,
|
||||
t('messages.delete_archive'),
|
||||
`${t('messages.are_you_sure_delete')} ${type}? ${t('messages.action_cannot_undone')}.`,
|
||||
async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/archive/delete/${type}/${filename}`, {
|
||||
@@ -999,7 +976,7 @@
|
||||
const result = await response.json();
|
||||
|
||||
if (result.status === 'success') {
|
||||
alert('Archive deleted successfully');
|
||||
alert(t('messages.archive_deleted'));
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error deleting archive: ' + result.message);
|
||||
@@ -1072,5 +1049,20 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Internationalization Support -->
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
<script>
|
||||
// Initialize translations with server data
|
||||
if (typeof {{ translations|tojson }} !== 'undefined') {
|
||||
currentTranslations = {{ translations|tojson }};
|
||||
currentLanguage = '{{ current_language }}';
|
||||
|
||||
// Apply translations when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
translatePage();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,8 +3,25 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Player Analysis - Camera Dashboard</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
|
||||
<title data-i18n="players.player_analysis">👤 Player Analysis - Camera Dashboard</title>
|
||||
<script src="/static/js/chart.min.js"></script>
|
||||
<script>
|
||||
// Fallback: if Chart.js fails to load, create a simple notification
|
||||
if (typeof Chart === 'undefined') {
|
||||
console.warn('Chart.js failed to load - charts will be unavailable');
|
||||
window.Chart = {
|
||||
register: () => {},
|
||||
Chart: function() {
|
||||
return {
|
||||
update: () => {},
|
||||
destroy: () => {},
|
||||
data: {},
|
||||
options: {}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
@@ -65,14 +82,14 @@
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.nav-btn.secondary {
|
||||
background: #6c757d;
|
||||
border-color: #495057;
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.secondary:hover {
|
||||
background: #495057;
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -165,106 +182,223 @@
|
||||
/* Tournament Leaders Section */
|
||||
.tournament-leaders {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
gap: 20px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
gap: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.tournament-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 20px;
|
||||
padding: 35px;
|
||||
box-shadow:
|
||||
0 12px 40px rgba(0, 0, 0, 0.15),
|
||||
0 6px 20px rgba(0, 0, 0, 0.1);
|
||||
border: none;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.3s ease;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tournament-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, #ffc107, #fd7e14);
|
||||
.tournament-card:hover {
|
||||
box-shadow:
|
||||
0 15px 45px rgba(0, 0, 0, 0.15),
|
||||
0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Different color themes for each tournament type */
|
||||
.tournament-card[data-type="20_targets"] {
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%);
|
||||
}
|
||||
|
||||
.tournament-card[data-type="40_targets"] {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.tournament-card[data-type="4_targets"] {
|
||||
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
}
|
||||
|
||||
.tournament-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 28px;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.tournament-name {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
font-size: 1.6rem;
|
||||
font-weight: 800;
|
||||
color: white;
|
||||
text-shadow:
|
||||
0 2px 4px rgba(0, 0, 0, 0.3),
|
||||
0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tournament-card[data-type="20_targets"] .tournament-name::before {
|
||||
content: '⚡';
|
||||
font-size: 1.8rem;
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
|
||||
}
|
||||
|
||||
.tournament-card[data-type="40_targets"] .tournament-name::before {
|
||||
content: '💪';
|
||||
font-size: 1.8rem;
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
|
||||
}
|
||||
|
||||
.tournament-card[data-type="4_targets"] .tournament-name::before {
|
||||
content: '🎯';
|
||||
font-size: 1.8rem;
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
|
||||
}
|
||||
|
||||
.tournament-date {
|
||||
background: #f8f9fa;
|
||||
color: #666;
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
backdrop-filter: blur(10px);
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
border-radius: 25px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
box-shadow:
|
||||
0 4px 15px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.leaders-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 15px;
|
||||
gap: 25px;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.leader-category {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
background: #ffffff;
|
||||
border-radius: 16px;
|
||||
padding: 28px;
|
||||
text-align: center;
|
||||
border: 2px solid #e9ecef;
|
||||
transition: box-shadow 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.leader-category:hover {
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.leader-category {
|
||||
border-color: #e9ecef;
|
||||
}
|
||||
|
||||
/* Dynamic colors based on tournament type */
|
||||
.tournament-card[data-type="20_targets"] .leader-category::before {
|
||||
background: linear-gradient(90deg, #ff6b6b 0%, #ff8e53 100%);
|
||||
}
|
||||
|
||||
.tournament-card[data-type="40_targets"] .leader-category::before {
|
||||
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.tournament-card[data-type="4_targets"] .leader-category::before {
|
||||
background: linear-gradient(90deg, #11998e 0%, #38ef7d 100%);
|
||||
}
|
||||
|
||||
.leader-category::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
border-radius: 16px 16px 0 0;
|
||||
}
|
||||
|
||||
.category-title {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
color: #6c757d;
|
||||
margin-bottom: 18px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
letter-spacing: 1px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.leader-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.leader-name {
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.leader-score {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
font-size: 1.8rem;
|
||||
font-weight: 800;
|
||||
padding: 12px 20px;
|
||||
border-radius: 12px;
|
||||
border: 2px solid;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.leader-score.tens {
|
||||
color: #28a745;
|
||||
/* Score colors based on tournament type */
|
||||
.tournament-card[data-type="20_targets"] .leader-score {
|
||||
border-color: #ff6b6b;
|
||||
background: linear-gradient(135deg, #fff5f5 0%, #ffe8e8 100%);
|
||||
color: #e53e3e;
|
||||
}
|
||||
|
||||
/* Compact Stats Overview */
|
||||
.stats-overview {
|
||||
.tournament-card[data-type="40_targets"] .leader-score {
|
||||
border-color: #667eea;
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #e8ebff 100%);
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.tournament-card[data-type="4_targets"] .leader-score {
|
||||
border-color: #11998e;
|
||||
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
/* Yellow styling for 10s (Most 10s category) */
|
||||
.leader-category:nth-child(2) .leader-score {
|
||||
border-color: #fbbf24 !important;
|
||||
background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%) !important;
|
||||
color: #d97706 !important;
|
||||
}
|
||||
|
||||
.leader-category:nth-child(2)::before {
|
||||
background: linear-gradient(90deg, #fbbf24 0%, #f59e0b 100%) !important;
|
||||
}
|
||||
|
||||
/* Stats Overview */
|
||||
.stats-badges {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
.stat-badge {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 15px;
|
||||
@@ -274,7 +408,7 @@
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
.stat-badge:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
@@ -284,7 +418,7 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
.stat-number {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
@@ -562,16 +696,44 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.stats-overview {
|
||||
.stats-badges {
|
||||
grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
|
||||
}
|
||||
|
||||
.tournament-leaders {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.tournament-card {
|
||||
padding: 25px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tournament-name {
|
||||
font-size: 1.3rem;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tournament-header {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.leaders-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.leader-category {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.leader-score {
|
||||
font-size: 1.6rem;
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.tab-navigation {
|
||||
@@ -589,53 +751,55 @@
|
||||
<body>
|
||||
<!-- Navigation Bar -->
|
||||
<div class="navbar">
|
||||
<div class="navbar-title">🎯 Player Analysis</div>
|
||||
<div class="navbar-title" data-i18n="players.player_analysis">🎯 👤 Player Analysis</div>
|
||||
<div class="navbar-controls">
|
||||
<a href="/" class="nav-btn primary">← Dashboard</a>
|
||||
<a href="/archive" class="nav-btn secondary">📚 Archive</a>
|
||||
<a href="/" class="nav-btn">📺 <span data-i18n="navigation.dashboard">Dashboard</span></a>
|
||||
<a href="/tournament" class="nav-btn">🏆 <span data-i18n="navigation.tournament">Tournament</span></a>
|
||||
<a href="/archive/player-analysis" class="nav-btn active">👤 <span data-i18n="players.player_analysis">Player Analysis</span></a>
|
||||
<a href="/archive" class="nav-btn">📚 <span data-i18n="navigation.archive">Archive</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- Stats Overview -->
|
||||
<div class="stats-overview">
|
||||
<div class="stat-card">
|
||||
<div class="stats-badges">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">👥</span>
|
||||
<div class="stat-value" id="totalPlayers">Loading...</div>
|
||||
<div class="stat-label">Total Players</div>
|
||||
<div class="stat-number" id="totalPlayers">Loading...</div>
|
||||
<div class="stat-label" data-i18n="players.total_players">Total Players</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">🎯</span>
|
||||
<div class="stat-value" id="totalTournaments">Loading...</div>
|
||||
<div class="stat-label">Total Tournaments</div>
|
||||
<div class="stat-number" id="totalTournaments">Loading...</div>
|
||||
<div class="stat-label" data-i18n="analysis.total_tournaments">Total Tournaments</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">🏆</span>
|
||||
<div class="stat-value" id="score20Targets">Loading...</div>
|
||||
<div class="stat-label">Best 20 Targets</div>
|
||||
<div class="stat-number" id="score20Targets">Loading...</div>
|
||||
<div class="stat-label" data-i18n="tournament_types.20_targets">20 Targets</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">🎖️</span>
|
||||
<div class="stat-value" id="score40Targets">Loading...</div>
|
||||
<div class="stat-label">Best 40 Targets</div>
|
||||
<div class="stat-number" id="score40Targets">Loading...</div>
|
||||
<div class="stat-label" data-i18n="tournament_types.40_targets">40 Targets</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">🥇</span>
|
||||
<div class="stat-value" id="score4Targets">Loading...</div>
|
||||
<div class="stat-label">Best 4 Targets</div>
|
||||
<div class="stat-number" id="score4Targets">Loading...</div>
|
||||
<div class="stat-label" data-i18n="tournament_types.4_targets">4 Targets</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Navigation -->
|
||||
<div class="section">
|
||||
<div class="tab-navigation">
|
||||
<button class="tab-btn active" onclick="switchTab('tournament-leaders')">🏆 Overall Champions</button>
|
||||
<button class="tab-btn" onclick="switchTab('players')">👥 All Players</button>
|
||||
<button class="tab-btn active" onclick="switchTab('tournament-leaders')" data-i18n="analysis.overall_champions">🏆 Overall Champions</button>
|
||||
<button class="tab-btn" onclick="switchTab('players')" data-i18n="analysis.all_players">👥 All Players</button>
|
||||
</div>
|
||||
|
||||
<!-- Tournament Leaders Tab -->
|
||||
<div id="tournament-leaders" class="tab-content active">
|
||||
<div class="section-title">Overall Champions by Tournament Type</div>
|
||||
<div class="section-title" data-i18n="analysis.overview_champions">Overall Champions by Tournament Type</div>
|
||||
|
||||
<div class="tournament-leaders" id="tournamentLeaders">
|
||||
<div class="loading">
|
||||
@@ -647,19 +811,19 @@
|
||||
|
||||
<!-- Players Tab -->
|
||||
<div id="players" class="tab-content">
|
||||
<div class="section-title">Select a Player to Analyze</div>
|
||||
<div class="section-title" data-i18n="analysis.select_player">Izberi igralca za analizo</div>
|
||||
|
||||
<!-- Controls -->
|
||||
<div class="controls">
|
||||
<input type="text" class="search-box" id="searchBox" placeholder="🔍 Search players by name...">
|
||||
<input type="text" class="search-box" id="searchBox" data-i18n-placeholder="league.search_players_placeholder" placeholder="🔍 Search players by name...">
|
||||
|
||||
<select class="sort-select" id="sortSelect">
|
||||
<option value="name">Sort by Name</option>
|
||||
<option value="best_score">Sort by Best Score</option>
|
||||
<option value="average_score">Sort by Average Score</option>
|
||||
<option value="total_tournaments">Sort by Total Tournaments</option>
|
||||
<option value="total_leagues">Sort by Total Leagues</option>
|
||||
<option value="total_shots">Sort by Total Shots</option>
|
||||
<option value="name" data-i18n="analysis.sort_by_name">Sort by Name</option>
|
||||
<option value="best_score" data-i18n="analysis.sort_by_best_score">Sort by Best Score</option>
|
||||
<option value="average_score" data-i18n="analysis.sort_by_average_score">Sort by Average Score</option>
|
||||
<option value="total_tournaments" data-i18n="analysis.sort_by_total_tournaments">Sort by Total Tournaments</option>
|
||||
<option value="total_leagues" data-i18n="analysis.sort_by_total_leagues">Sort by Total Leagues</option>
|
||||
<option value="total_shots" data-i18n="analysis.sort_by_total_shots">Sort by Total Shots</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -724,7 +888,7 @@
|
||||
document.getElementById('tournamentLeaders').innerHTML = `
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">🎯</div>
|
||||
<h3>Unable to Load Tournament Data</h3>
|
||||
<h3 data-i18n="analysis.unable_load_data">Ne morem naložiti podatkov turnirja</h3>
|
||||
<p>${result.message || 'Please try refreshing the page'}</p>
|
||||
</div>
|
||||
`;
|
||||
@@ -745,48 +909,83 @@
|
||||
// Render tournament leaders
|
||||
function renderTournamentLeaders() {
|
||||
const leadersContainer = document.getElementById('tournamentLeaders');
|
||||
|
||||
|
||||
if (tournamentData.length === 0) {
|
||||
leadersContainer.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">🎯</div>
|
||||
<h3>No Tournament Data Available</h3>
|
||||
<h3 data-i18n="analysis.no_tournament_data">Ni podatkov o turnirju</h3>
|
||||
<p>Tournament results will appear here once available</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
leadersContainer.innerHTML = tournamentData.map(tournamentType => `
|
||||
<div class="tournament-card">
|
||||
// Sort tournament types by target count: 4, 20, 40
|
||||
const sortOrder = ['4_targets', '20_targets', '40_targets'];
|
||||
const sortedTournamentData = [...tournamentData].sort((a, b) => {
|
||||
const indexA = sortOrder.indexOf(a.id);
|
||||
const indexB = sortOrder.indexOf(b.id);
|
||||
|
||||
// If not in sortOrder, put at end
|
||||
if (indexA === -1 && indexB === -1) return 0;
|
||||
if (indexA === -1) return 1;
|
||||
if (indexB === -1) return -1;
|
||||
|
||||
return indexA - indexB;
|
||||
});
|
||||
|
||||
leadersContainer.innerHTML = sortedTournamentData.map(tournamentType => {
|
||||
// Get translated tournament name and description based on tournament type
|
||||
let translatedName = tournamentType.name;
|
||||
let translatedDescription = tournamentType.description;
|
||||
|
||||
switch(tournamentType.id) {
|
||||
case '4_targets':
|
||||
translatedName = t('tournament_types.4_targets');
|
||||
translatedDescription = t('tournament_types.4_targets_desc');
|
||||
break;
|
||||
case '20_targets':
|
||||
translatedName = t('tournament_types.20_targets');
|
||||
translatedDescription = t('tournament_types.20_targets_desc');
|
||||
break;
|
||||
case '40_targets':
|
||||
translatedName = t('tournament_types.40_targets');
|
||||
translatedDescription = t('tournament_types.40_targets_desc');
|
||||
break;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="tournament-card" data-type="${tournamentType.id}">
|
||||
<div class="tournament-header">
|
||||
<div class="tournament-name">${tournamentType.name}</div>
|
||||
<div class="tournament-date">${tournamentType.total_tournaments} tournament${tournamentType.total_tournaments !== 1 ? 's' : ''}</div>
|
||||
<div class="tournament-name">${translatedName}</div>
|
||||
<div class="tournament-date">${tournamentType.total_tournaments} ${tournamentType.total_tournaments === 1 ? t('tournament.tournament') : t('tournament.tournaments')}</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; margin-bottom: 15px; color: #666; font-size: 0.9rem; font-weight: 500;">
|
||||
${tournamentType.description}
|
||||
|
||||
<div style="text-align: center; margin-bottom: 28px; color: rgba(255, 255, 255, 0.95); font-size: 1rem; font-weight: 500; position: relative; z-index: 3; background: rgba(0, 0, 0, 0.15); padding: 14px 24px; border-radius: 14px; backdrop-filter: blur(15px); border: 1px solid rgba(255, 255, 255, 0.25); box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);">
|
||||
${translatedDescription}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="leaders-grid">
|
||||
<div class="leader-category">
|
||||
<div class="category-title">🏆 Best Score</div>
|
||||
<div class="category-title">🏆 ${t('analysis.best_score')}</div>
|
||||
<div class="leader-info">
|
||||
<div class="leader-name">${tournamentType.best_score.player_name || 'No data'}</div>
|
||||
<div class="leader-name">${tournamentType.best_score.player_name || t('messages.no_data')}</div>
|
||||
<div class="leader-score">${tournamentType.best_score.score || 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="leader-category">
|
||||
<div class="category-title">🎪 Most 10s</div>
|
||||
<div class="category-title">🎯 ${t('analysis.most_tens')}</div>
|
||||
<div class="leader-info">
|
||||
<div class="leader-name">${tournamentType.most_tens.player_name || 'No data'}</div>
|
||||
<div class="leader-score tens">${tournamentType.most_tens.tens || 0} 10s</div>
|
||||
<div class="leader-name">${tournamentType.most_tens.player_name || t('messages.no_data')}</div>
|
||||
<div class="leader-score tens">${tournamentType.most_tens.tens || 0}x 🎯</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// Update overall statistics
|
||||
@@ -795,12 +994,12 @@
|
||||
let score20Targets = 0;
|
||||
let score40Targets = 0;
|
||||
let score4Targets = 0;
|
||||
|
||||
|
||||
// Calculate statistics across all tournament types
|
||||
tournamentData.forEach(tournamentType => {
|
||||
totalTournaments += tournamentType.total_tournaments || 0;
|
||||
|
||||
// Get best scores for each tournament type
|
||||
|
||||
// Get best scores for each tournament type using the id field
|
||||
if (tournamentType.id === '20_targets' && tournamentType.best_score) {
|
||||
score20Targets = tournamentType.best_score.score || 0;
|
||||
} else if (tournamentType.id === '40_targets' && tournamentType.best_score) {
|
||||
@@ -809,7 +1008,7 @@
|
||||
score4Targets = tournamentType.best_score.score || 0;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
document.getElementById('totalTournaments').textContent = totalTournaments;
|
||||
document.getElementById('score20Targets').textContent = score20Targets || '-';
|
||||
document.getElementById('score40Targets').textContent = score40Targets || '-';
|
||||
@@ -819,16 +1018,16 @@
|
||||
// Get tournament type display name
|
||||
function getTournamentTypeDisplay(tournamentType) {
|
||||
const typeMap = {
|
||||
'20_targets': '20 Targets (2 shots each)',
|
||||
'40_targets': '40 Targets (2 shots each)',
|
||||
'4_targets': '4 Targets (5 shots each)'
|
||||
'20_targets': t('tournament_types.20_targets_full'),
|
||||
'40_targets': t('tournament_types.40_targets_full'),
|
||||
'4_targets': t('tournament_types.4_targets_full')
|
||||
};
|
||||
return typeMap[tournamentType] || tournamentType;
|
||||
}
|
||||
|
||||
// Format date for display
|
||||
function formatDate(dateString) {
|
||||
if (!dateString) return 'Unknown Date';
|
||||
if (!dateString) return t('analysis.unknown_date');
|
||||
|
||||
try {
|
||||
const date = new Date(dateString);
|
||||
@@ -847,7 +1046,7 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
return 'Unknown Date';
|
||||
return t('analysis.unknown_date');
|
||||
}
|
||||
|
||||
return date.toLocaleDateString('en-US', {
|
||||
@@ -954,30 +1153,27 @@
|
||||
onclick="viewPlayerStats(${player.id})">
|
||||
<div class="player-header">
|
||||
<div class="player-name">${player.name}</div>
|
||||
<div class="player-badge ${player.current_player ? 'current' : 'archived'}">
|
||||
${player.current_player ? 'Current' : 'Archived'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="player-stats">
|
||||
<div class="stat-item">
|
||||
<span class="icon">🎯</span>
|
||||
<span class="label">Tournaments:</span>
|
||||
<span class="label">${t('tournament.tournaments')}:</span>
|
||||
<span class="value">${player.stats?.total_tournaments || 0}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="icon">🏆</span>
|
||||
<span class="label">Leagues:</span>
|
||||
<span class="label">${t('league.league')}:</span>
|
||||
<span class="value">${player.stats?.total_leagues || 0}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="icon">📈</span>
|
||||
<span class="label">Best Score:</span>
|
||||
<span class="label">${t('analysis.best_score_label')}</span>
|
||||
<span class="value">${player.stats?.best_tournament_score || 0}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="icon">📊</span>
|
||||
<span class="label">Average:</span>
|
||||
<span class="label">${t('results.average_score')}</span>
|
||||
<span class="value">${Math.round(player.stats?.average_tournament_score || 0)}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1023,30 +1219,27 @@
|
||||
onclick="viewPlayerStats(${player.id})">
|
||||
<div class="player-header">
|
||||
<div class="player-name">${player.name}</div>
|
||||
<div class="player-badge ${player.enabled ? 'current' : 'archived'}">
|
||||
${player.enabled ? 'Current' : 'Archived'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="player-stats">
|
||||
<div class="stat-item">
|
||||
<span class="icon">🎯</span>
|
||||
<span class="label">Tournaments:</span>
|
||||
<span class="label">${t('tournament.tournaments')}:</span>
|
||||
<span class="value">Loading...</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="icon">🏆</span>
|
||||
<span class="label">Leagues:</span>
|
||||
<span class="label">${t('league.league')}:</span>
|
||||
<span class="value">Loading...</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="icon">📈</span>
|
||||
<span class="label">Best Score:</span>
|
||||
<span class="label">${t('analysis.best_score_label')}</span>
|
||||
<span class="value">Loading...</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="icon">📊</span>
|
||||
<span class="label">Average:</span>
|
||||
<span class="label">${t('results.average_score')}</span>
|
||||
<span class="value">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1125,5 +1318,20 @@
|
||||
// Initialize page when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', initializePage);
|
||||
</script>
|
||||
|
||||
<!-- Internationalization Support -->
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
<script>
|
||||
// Initialize translations with server data
|
||||
if (typeof {{ translations|tojson }} !== 'undefined') {
|
||||
currentTranslations = {{ translations|tojson }};
|
||||
currentLanguage = '{{ current_language }}';
|
||||
|
||||
// Apply translations when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
translatePage();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -15,9 +15,8 @@
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background: #f5f5f5;
|
||||
height: 100vh;
|
||||
min-height: 100vh;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Enhanced Navigation Bar */
|
||||
@@ -33,14 +32,14 @@
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 1.5rem;
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -66,34 +65,69 @@
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.nav-btn.primary {
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.primary:hover {
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Main Container */
|
||||
/* Standardized Container */
|
||||
.container {
|
||||
height: calc(100vh - 60px);
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
overflow: hidden;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Top Section - Stats and Charts */
|
||||
.top-section {
|
||||
/* Stats Overview */
|
||||
.stats-badges {
|
||||
display: grid;
|
||||
grid-template-columns: 320px 1fr;
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
gap: 15px;
|
||||
height: 50%;
|
||||
min-height: 280px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-badge {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e9ecef;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-badge:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #666;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Charts Section */
|
||||
.charts-section {
|
||||
min-height: 450px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Stats Panel */
|
||||
@@ -154,6 +188,7 @@
|
||||
padding: 18px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Tournament Type Buttons */
|
||||
@@ -283,11 +318,24 @@
|
||||
.chart-container {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-height: 200px;
|
||||
min-height: 400px;
|
||||
max-height: 500px;
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e9ecef;
|
||||
padding: 10px;
|
||||
padding: 15px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.chart-container canvas {
|
||||
max-width: calc(100% - 10px) !important;
|
||||
max-height: calc(100% - 10px) !important;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
@@ -308,25 +356,31 @@
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 15px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
min-height: 350px;
|
||||
max-height: 450px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.history-section {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 18px;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
min-height: 300px;
|
||||
max-height: 100%;
|
||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.history-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
min-height: 0;
|
||||
min-height: 200px;
|
||||
max-height: 320px;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
@@ -350,21 +404,41 @@
|
||||
|
||||
/* Tournament History Items */
|
||||
.history-item {
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e3f2fd 100%);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 8px;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e8f4fd 100%);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
margin-bottom: 12px;
|
||||
transition: all 0.3s ease;
|
||||
border-left: 3px solid #2196f3;
|
||||
border-left: 4px solid #2196f3;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border: 1px solid rgba(33, 150, 243, 0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.history-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #2196f3 0%, #64b5f6 100%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.history-item:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.history-item:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(33, 150, 243, 0.2);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(33, 150, 243, 0.15);
|
||||
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
|
||||
border-color: rgba(33, 150, 243, 0.2);
|
||||
}
|
||||
|
||||
.history-info {
|
||||
@@ -393,18 +467,38 @@
|
||||
|
||||
/* League History Items */
|
||||
.league-history-item {
|
||||
background: linear-gradient(135deg, #f3e5f5 0%, #e1bee7 100%);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 8px;
|
||||
background: linear-gradient(135deg, #f3e5f5 0%, #f0e6ff 100%);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
margin-bottom: 12px;
|
||||
transition: all 0.3s ease;
|
||||
border-left: 3px solid #9c27b0;
|
||||
border-left: 4px solid #9c27b0;
|
||||
border: 1px solid rgba(156, 39, 176, 0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.league-history-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #9c27b0 0%, #ba68c8 100%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.league-history-item:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.league-history-item:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(156, 39, 176, 0.2);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(156, 39, 176, 0.15);
|
||||
background: linear-gradient(135deg, #e1bee7 0%, #ce93d8 100%);
|
||||
border-color: rgba(156, 39, 176, 0.2);
|
||||
}
|
||||
|
||||
.league-header {
|
||||
@@ -504,7 +598,7 @@
|
||||
@media (max-width: 1200px) {
|
||||
.top-section {
|
||||
grid-template-columns: 1fr;
|
||||
height: 55%;
|
||||
min-height: 320px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,7 +616,7 @@
|
||||
}
|
||||
|
||||
.container {
|
||||
height: calc(100vh - 80px);
|
||||
min-height: calc(100vh - 100px);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@@ -535,8 +629,27 @@
|
||||
}
|
||||
|
||||
.top-section {
|
||||
height: auto;
|
||||
min-height: 200px;
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.bottom-section {
|
||||
min-height: 500px;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.history-section {
|
||||
min-height: 220px;
|
||||
}
|
||||
|
||||
.history-list {
|
||||
min-height: 150px;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
max-height: 350px;
|
||||
min-height: 300px;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -544,62 +657,65 @@
|
||||
<body>
|
||||
<!-- Navigation Bar -->
|
||||
<div class="navbar">
|
||||
<div class="navbar-title">📊 {{ player.name }} - Stats</div>
|
||||
<div class="navbar-title">📊 {{ player.name }} - <span data-i18n="players.player_stats">Stats</span></div>
|
||||
<div class="navbar-controls">
|
||||
<a href="/archive/player-analysis" class="nav-btn">👤 All Players</a>
|
||||
<a href="/archive" class="nav-btn">📚 Archive</a>
|
||||
<a href="/" class="nav-btn primary">🏠 Dashboard</a>
|
||||
<a href="/" class="nav-btn">🏠 <span data-i18n="navigation.dashboard">Dashboard</span></a>
|
||||
<a href="/tournament" class="nav-btn">🏆 <span data-i18n="navigation.tournament">Tournament</span></a>
|
||||
<a href="/archive/player-analysis" class="nav-btn active">👤 <span data-i18n="players.player_analysis">Player Analysis</span></a>
|
||||
<a href="/archive" class="nav-btn">📚 <span data-i18n="navigation.archive">Archive</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- Top Section - Stats and Charts -->
|
||||
<div class="top-section">
|
||||
<!-- Stats Panel -->
|
||||
<div class="stats-panel">
|
||||
<div class="panel-title">📊 Statistics</div>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ stats.total_tournaments }}</div>
|
||||
<div class="stat-label">Tournaments</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ stats.total_leagues }}</div>
|
||||
<div class="stat-label">Leagues</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ stats.best_tournament_score }}</div>
|
||||
<div class="stat-label">Best Score</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ stats.average_tournament_score|round|int if stats.average_tournament_score > 0 else 0 }}</div>
|
||||
<div class="stat-label">Average</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ stats.total_shots_fired|default(0) }}</div>
|
||||
<div class="stat-label">Total Shots</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ stats.worst_tournament_score if stats.worst_tournament_score > 0 else 0 }}</div>
|
||||
<div class="stat-label">Worst Score</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Stats Overview -->
|
||||
<div class="stats-badges">
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">🏆</span>
|
||||
<div class="stat-number">{{ stats.total_tournaments }}</div>
|
||||
<div class="stat-label" data-i18n="tournament.tournaments">Tournaments</div>
|
||||
</div>
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">🎖️</span>
|
||||
<div class="stat-number">{{ stats.total_leagues }}</div>
|
||||
<div class="stat-label" data-i18n="league.league">Leagues</div>
|
||||
</div>
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">⭐</span>
|
||||
<div class="stat-number">{{ stats.best_tournament_score }}</div>
|
||||
<div class="stat-label" data-i18n="results.best_score">Best Score</div>
|
||||
</div>
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">📊</span>
|
||||
<div class="stat-number">{{ stats.average_tournament_score|round|int if stats.average_tournament_score > 0 else 0 }}</div>
|
||||
<div class="stat-label" data-i18n="results.average_score">Average</div>
|
||||
</div>
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">🔫</span>
|
||||
<div class="stat-number">{{ stats.total_shots_fired|default(0) }}</div>
|
||||
<div class="stat-label" data-i18n="results.total_shots">Total Shots</div>
|
||||
</div>
|
||||
<div class="stat-badge">
|
||||
<span class="stat-icon">📉</span>
|
||||
<div class="stat-number">{{ stats.worst_tournament_score if stats.worst_tournament_score > 0 else 0 }}</div>
|
||||
<div class="stat-label" data-i18n="results.worst_score">Worst Score</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Performance Charts -->
|
||||
<!-- Charts Section -->
|
||||
<div class="charts-section">
|
||||
<div class="charts-panel">
|
||||
<div class="panel-title">📈 Performance by Tournament Type</div>
|
||||
|
||||
<div class="panel-title">📈 <span data-i18n="analysis.performance">Performance by Tournament Type</span></div>
|
||||
|
||||
<!-- Tournament Type Buttons -->
|
||||
<div class="tournament-type-buttons">
|
||||
<button class="type-btn active targets-40" data-type="40 Targets">
|
||||
<span>💪</span> 40 Targets
|
||||
<span>💪</span> <span data-i18n="tournament_types.40_targets">40 Targets</span>
|
||||
</button>
|
||||
<button class="type-btn targets-20" data-type="20 Targets">
|
||||
<span>⚡</span> 20 Targets
|
||||
<span>⚡</span> <span data-i18n="tournament_types.20_targets">20 Targets</span>
|
||||
</button>
|
||||
<button class="type-btn targets-4" data-type="4 Targets">
|
||||
<span>🎯</span> 4 Targets
|
||||
<span>🎯</span> <span data-i18n="tournament_types.4_targets">4 Targets</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -608,15 +724,15 @@
|
||||
<div class="chart-stats">
|
||||
<div class="chart-stat">
|
||||
<div class="chart-stat-value" id="gameCount">0</div>
|
||||
<div class="chart-stat-label">Games</div>
|
||||
<div class="chart-stat-label" data-i18n="tournament.tournaments">Games</div>
|
||||
</div>
|
||||
<div class="chart-stat">
|
||||
<div class="chart-stat-value" id="avgScore">0</div>
|
||||
<div class="chart-stat-label">Average</div>
|
||||
<div class="chart-stat-label" data-i18n="results.average_score">Average</div>
|
||||
</div>
|
||||
<div class="chart-stat">
|
||||
<div class="chart-stat-value" id="bestScore">0</div>
|
||||
<div class="chart-stat-label">Best</div>
|
||||
<div class="chart-stat-label" data-i18n="results.best_score">Best</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -680,14 +796,14 @@
|
||||
<div class="bottom-section">
|
||||
<!-- Tournament History -->
|
||||
<div class="history-section">
|
||||
<div class="panel-title">🎯 Tournament History</div>
|
||||
<div class="panel-title">🎯 <span data-i18n="analysis.tournament_history">Tournament History</span></div>
|
||||
{% if stats.tournament_history %}
|
||||
<div class="history-list">
|
||||
{% for tournament in stats.tournament_history[:10] %}
|
||||
<div class="history-item">
|
||||
<div class="history-info">
|
||||
<div class="history-date">{{ tournament.date[:10] if tournament.date != 'Unknown' else 'Unknown Date' }}</div>
|
||||
<div class="history-type">{{ tournament.tournament_type.replace('_', ' ')|title }} • {{ tournament.shots_fired }} shots</div>
|
||||
<div class="history-date">{{ tournament.date[:10] if tournament.date != 'Unknown' else (translations.analysis.unknown_date if translations else 'Unknown Date') }}</div>
|
||||
<div class="history-type">{{ tournament.tournament_type.replace('_', ' ')|title }} • {{ tournament.shots_fired }} {{ translations.results.shots if translations else 'shots' }}</div>
|
||||
</div>
|
||||
<div class="history-score">{{ tournament.score }}</div>
|
||||
</div>
|
||||
@@ -718,7 +834,7 @@
|
||||
<div class="history-date">{{ league.date[:10] if league.date != 'Unknown' else 'Unknown Date' }}</div>
|
||||
<div class="history-type">
|
||||
League Championship • {{ league.tournaments_participated }}/6 tournaments
|
||||
{% if league.joker_used %} • 🃏 Joker Used{% endif %}
|
||||
{% if league.joker_used %} • 🃏 {{ translations.league.joker_used if translations else 'Joker Used' }}{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="league-score-display">{{ league.final_score }}</div>
|
||||
@@ -732,7 +848,7 @@
|
||||
<div class="tournament-results">
|
||||
{% for result in league.tournament_results %}
|
||||
<span class="tournament-result {{ 'participated' if result.participated else 'joker' }}">
|
||||
T{{ result.tournament }}: {{ result.score if result.participated else 'Joker' }}
|
||||
T{{ result.tournament }}: {{ result.score if result.participated else (translations.league.joker_used if translations else 'Joker') }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -1122,5 +1238,20 @@
|
||||
// Initialize page when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', initializePage);
|
||||
</script>
|
||||
|
||||
<!-- Internationalization Support -->
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
<script>
|
||||
// Initialize translations with server data
|
||||
if (typeof {{ translations|tojson }} !== 'undefined') {
|
||||
currentTranslations = {{ translations|tojson }};
|
||||
currentLanguage = '{{ current_language }}';
|
||||
|
||||
// Apply translations when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
translatePage();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+279
-114
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Results Calculator</title>
|
||||
<title data-i18n="scoring.results_calculator">Results Calculator</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
* {
|
||||
@@ -62,28 +62,17 @@
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.nav-btn.primary {
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.primary:hover {
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.success {
|
||||
background: #28a745;
|
||||
border-color: #1e7e34;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.success:hover {
|
||||
background: #1e7e34;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.danger {
|
||||
background: #dc3545;
|
||||
border-color: #c82333;
|
||||
@@ -102,25 +91,75 @@
|
||||
}
|
||||
|
||||
.header-section {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 25px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
padding: 20px;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Dynamic header colors based on tournament type */
|
||||
.header-section.tournament-4_targets {
|
||||
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
}
|
||||
|
||||
.header-section.tournament-20_targets {
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%);
|
||||
}
|
||||
|
||||
.header-section.tournament-40_targets {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.header-section.tournament-default {
|
||||
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%);
|
||||
}
|
||||
|
||||
.header-section::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;
|
||||
}
|
||||
|
||||
.header-section * {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
height: 50px;
|
||||
max-width: 140px;
|
||||
object-fit: contain;
|
||||
margin-bottom: 15px;
|
||||
filter: brightness(1.2) contrast(1.1);
|
||||
background-color: white;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
color: rgb(255, 255, 255);
|
||||
margin-bottom: 8px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
color: #666;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1rem;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
margin-bottom: 15px;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
@@ -128,7 +167,7 @@
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.progress-item {
|
||||
@@ -136,15 +175,19 @@
|
||||
}
|
||||
|
||||
.progress-number {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
display: block;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
font-size: 0.75rem;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.global-actions {
|
||||
@@ -272,6 +315,19 @@
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.score-display {
|
||||
text-align: right;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.score-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.total-score {
|
||||
text-align: right;
|
||||
}
|
||||
@@ -288,6 +344,22 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.tens-count {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.tens-number {
|
||||
font-size: 1.4rem;
|
||||
font-weight: bold;
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
.tens-label {
|
||||
font-size: 0.8rem;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.completion-badge {
|
||||
padding: 6px 15px;
|
||||
border-radius: 15px;
|
||||
@@ -389,6 +461,23 @@
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.score-row {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.participant-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.participant-status {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.target-group {
|
||||
@@ -447,6 +536,12 @@
|
||||
background: #f8fff9;
|
||||
}
|
||||
|
||||
.shot-input.ten {
|
||||
border-color: #ffc107;
|
||||
background: #fff9e6;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.participant-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -548,33 +643,6 @@
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.participant-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.participant-status {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.global-actions {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.global-btn {
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading and saving indicators */
|
||||
.saving {
|
||||
opacity: 0.7;
|
||||
@@ -603,44 +671,61 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar">
|
||||
<div class="navbar-title">🎯 Results Calculator</div>
|
||||
<div class="navbar-title">🎯 <span data-i18n="scoring.results_calculator">Results Calculator</span></div>
|
||||
<div class="navbar-controls">
|
||||
<a href="/" class="nav-btn">← Dashboard</a>
|
||||
<a href="/tournament/draft" class="nav-btn">📋 Draft</a>
|
||||
<a href="/" class="nav-btn">📺 <span data-i18n="navigation.dashboard">Dashboard</span></a>
|
||||
<a href="/tournament" class="nav-btn">🏆 <span data-i18n="navigation.tournament">Tournament</span></a>
|
||||
<a href="/archive/player-analysis" class="nav-btn">👤 <span data-i18n="players.player_analysis">Player Analysis</span></a>
|
||||
<a href="/archive" class="nav-btn">📚 <span data-i18n="navigation.archive">Archive</span></a>
|
||||
<a href="/tournament/draft" class="nav-btn">📋 <span data-i18n="tournament.view_draft">Draft</span></a>
|
||||
<a href="/results/calculator" class="nav-btn active">🎯 <span data-i18n="navigation.calculator">Results Calculator</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="header-section">
|
||||
<div class="header-title">🎯 Tournament Scoring</div>
|
||||
<div class="header-section tournament-{{ results.tournament_type if results and results.tournament_type else 'default' }}">
|
||||
<img src="/static/logo.png" alt="Logo" class="header-logo" onerror="this.style.display='none'" />
|
||||
<div class="header-title">
|
||||
{% if results and results.tournament_type == '4_targets' %}
|
||||
🎯 <span data-i18n="scoring.tournament_scoring">Tournament Scoring</span>
|
||||
{% elif results and results.tournament_type == '40_targets' %}
|
||||
💪 <span data-i18n="scoring.tournament_scoring">Tournament Scoring</span>
|
||||
{% else %}
|
||||
⚡ <span data-i18n="scoring.tournament_scoring">Tournament Scoring</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="header-subtitle" id="tournamentSubtitle">
|
||||
{% if results.tournament_type == '40_targets' %}
|
||||
Enter scores for each participant (40 targets, 2 shots each). Score 0 = miss.
|
||||
<span data-i18n="scoring.enter_scores_40_targets">Enter scores for each participant (40 targets, 2 shots each). Score 0 = miss.</span>
|
||||
{% elif results.tournament_type == '4_targets' %}
|
||||
Enter scores for each participant (4 targets, 5 shots each). Score 0 = miss.
|
||||
<span data-i18n="scoring.enter_scores_4_targets">Enter scores for each participant (4 targets, 5 shots each). Score 0 = miss.</span>
|
||||
{% else %}
|
||||
Enter scores for each participant (20 targets, 2 shots each). Score 0 = miss.
|
||||
<span data-i18n="scoring.enter_scores_20_targets">Enter scores for each participant (20 targets, 2 shots each). Score 0 = miss.</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="progress-info">
|
||||
<div class="progress-item">
|
||||
<div class="progress-number" id="completedCount">0</div>
|
||||
<div class="progress-label">Completed</div>
|
||||
<div class="progress-label" data-i18n="scoring.completed">Zaključeno</div>
|
||||
</div>
|
||||
<div class="progress-item">
|
||||
<div class="progress-number" id="totalParticipants">{{ results.participants|length }}</div>
|
||||
<div class="progress-label">Total</div>
|
||||
<div class="progress-label" data-i18n="scoring.total">Skupaj</div>
|
||||
</div>
|
||||
<div class="progress-item">
|
||||
<div class="progress-number" id="totalShots">0</div>
|
||||
<div class="progress-label">Total Shots</div>
|
||||
<div class="progress-label" data-i18n="scoring.total_shots">Skupaj Strelov</div>
|
||||
</div>
|
||||
<div class="progress-item">
|
||||
<div class="progress-number" id="totalTens">0</div>
|
||||
<div class="progress-label" data-i18n="results.tens">Tens</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="global-actions">
|
||||
<button class="global-btn fill-all-btn" onclick="fillAllPlayersRandom()">
|
||||
🎲 Fill All Players Random
|
||||
🎲 <span data-i18n="scoring.fill_all_players_random">Napolni Vse Igralce Naključno</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -654,15 +739,23 @@
|
||||
<div class="participant-id">ID: {{ player_id }}</div>
|
||||
</div>
|
||||
<div class="participant-status">
|
||||
<div class="total-score">
|
||||
<div class="score-number" id="total-{{ player_id }}">{{ participant.total_score }}</div>
|
||||
<div class="score-label">Points</div>
|
||||
<div class="score-display">
|
||||
<div class="score-row">
|
||||
<div class="total-score">
|
||||
<div class="score-number" id="total-{{ player_id }}">{{ participant.total_score }}</div>
|
||||
<div class="score-label" data-i18n="scoring.points">Točke</div>
|
||||
</div>
|
||||
<div class="tens-count">
|
||||
<div class="tens-number" id="tens-{{ player_id }}">0</div>
|
||||
<div class="tens-label">10s</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="completion-badge" id="badge-{{ player_id }}">
|
||||
{% if participant.completed %}
|
||||
<span class="badge-completed">Completed</span>
|
||||
<span class="badge-completed" data-i18n="scoring.completed">Zaključeno</span>
|
||||
{% else %}
|
||||
<span class="badge-not-started">Not Started</span>
|
||||
<span class="badge-not-started" data-i18n="scoring.not_started">Ni Začeto</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="collapse-icon">▼</div>
|
||||
@@ -677,10 +770,10 @@
|
||||
|
||||
<div class="participant-actions">
|
||||
<button class="action-btn clear-btn" onclick="clearParticipant({{ player_id }})">
|
||||
🗑️ Clear All
|
||||
🗑️ <span data-i18n="scoring.clear">Počisti</span>
|
||||
</button>
|
||||
<button class="action-btn save-btn" onclick="saveParticipant({{ player_id }})">
|
||||
💾 Save Progress
|
||||
💾 <span data-i18n="scoring.save">Shrani</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -690,14 +783,11 @@
|
||||
</div>
|
||||
|
||||
<div class="finish-section">
|
||||
<div class="finish-title">🏁 Finish Tournament</div>
|
||||
<div class="finish-description">
|
||||
Complete scoring for all participants and finalize tournament results.
|
||||
</div>
|
||||
<div class="finish-title">🏁 <span data-i18n="scoring.finish_tournament">Zaključi Turnir</span></div>
|
||||
<div class="warning-message" id="finishWarning" style="display: none;">
|
||||
<strong>Warning:</strong> Not all participants have completed scores. Please ensure all scoring is complete before finishing.
|
||||
<strong data-i18n="scoring.warning">Opozorilo:</strong> <span data-i18n="scoring.finish_warning">Niso vsi udeleženci zaključili s točkovanjem. Prosimo, da poskrbite, da je vse točkovanje zaključeno pred zaključkom.</span>
|
||||
</div>
|
||||
<button class="finish-btn" id="finishBtn" onclick="finishTournament()">
|
||||
<button class="finish-btn" id="finishBtn" onclick="finishTournament()" data-i18n="scoring.finish_tournament_button">🏁 Finish Tournament & Show Results
|
||||
🏆 Finish Tournament & Show Results
|
||||
</button>
|
||||
</div>
|
||||
@@ -723,7 +813,51 @@
|
||||
shotsPerTarget = 2;
|
||||
}
|
||||
|
||||
console.log(`Tournament config: ${numTargets} targets, ${shotsPerTarget} shots each`);
|
||||
// Calculate 10s count for a player
|
||||
function calculateTensCount(playerId) {
|
||||
const playerIdStr = playerId.toString();
|
||||
const targets = results.participants[playerIdStr].targets;
|
||||
let tensCount = 0;
|
||||
|
||||
for (let target in targets) {
|
||||
const targetData = targets[target];
|
||||
for (let i = 1; i <= shotsPerTarget; i++) {
|
||||
const shotValue = targetData[`shot${i}`];
|
||||
if (shotValue === 10) {
|
||||
tensCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tensCount;
|
||||
}
|
||||
|
||||
// Update participant's 10s display
|
||||
function updateParticipantTens(playerId) {
|
||||
const tensCount = calculateTensCount(playerId);
|
||||
const tensElement = document.getElementById(`tens-${playerId}`);
|
||||
|
||||
if (tensElement) {
|
||||
tensElement.textContent = tensCount;
|
||||
|
||||
// Add visual feedback
|
||||
tensElement.style.color = '#ffc107';
|
||||
setTimeout(() => {
|
||||
tensElement.style.color = '';
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
// Update overall 10s count
|
||||
function updateOverallTens() {
|
||||
let totalTens = 0;
|
||||
|
||||
for (let playerId in results.participants) {
|
||||
totalTens += calculateTensCount(parseInt(playerId));
|
||||
}
|
||||
|
||||
document.getElementById('totalTens').textContent = totalTens;
|
||||
}
|
||||
|
||||
// Debounce function for auto-saving
|
||||
function debounce(func, wait, immediate) {
|
||||
@@ -761,7 +895,7 @@
|
||||
|
||||
html += `
|
||||
<input type="number"
|
||||
class="shot-input"
|
||||
class="shot-input ${value === 10 ? 'ten' : ''}"
|
||||
id="shot${shot}-${playerId}-${i}"
|
||||
data-player="${playerId}"
|
||||
data-target="${i}"
|
||||
@@ -833,16 +967,23 @@
|
||||
// Update target group styling
|
||||
updateTargetGroupStyling(playerId, targetNum);
|
||||
|
||||
// Recalculate total
|
||||
// Recalculate totals
|
||||
updateParticipantTotal(playerId);
|
||||
updateParticipantTens(playerId);
|
||||
updateParticipantStatus(playerId);
|
||||
updateOverallProgress();
|
||||
updateOverallTens();
|
||||
|
||||
// Visual feedback for input
|
||||
if (numValue !== null) {
|
||||
input.classList.add('valid');
|
||||
} else {
|
||||
input.classList.remove('valid');
|
||||
// Visual feedback for input - highlight 10s
|
||||
if (input) {
|
||||
input.classList.remove('valid', 'ten');
|
||||
if (numValue !== null) {
|
||||
if (numValue === 10) {
|
||||
input.classList.add('ten');
|
||||
} else {
|
||||
input.classList.add('valid');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-save with debounce
|
||||
@@ -944,12 +1085,12 @@
|
||||
card.classList.remove('completed', 'in-progress');
|
||||
if (isCompleted) {
|
||||
card.classList.add('completed');
|
||||
badge.innerHTML = '<span class="badge-completed">Completed</span>';
|
||||
badge.innerHTML = `<span class="badge-completed">${t('scoring.completed')}</span>`;
|
||||
} else if (isInProgress) {
|
||||
card.classList.add('in-progress');
|
||||
badge.innerHTML = '<span class="badge-in-progress">In Progress</span>';
|
||||
badge.innerHTML = `<span class="badge-in-progress">${t('scoring.in_progress')}</span>`;
|
||||
} else {
|
||||
badge.innerHTML = '<span class="badge-not-started">Not Started</span>';
|
||||
badge.innerHTML = `<span class="badge-not-started">${t('scoring.not_started')}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1012,12 +1153,12 @@
|
||||
if (!silent) {
|
||||
// Show success feedback
|
||||
const saveBtn = card.querySelector('.save-btn');
|
||||
const originalText = saveBtn.textContent;
|
||||
saveBtn.textContent = '✅ Saved';
|
||||
const originalText = saveBtn.innerHTML;
|
||||
saveBtn.innerHTML = `✅ ${t('scoring.all_saved')}`;
|
||||
saveBtn.style.background = '#28a745';
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
saveBtn.textContent = originalText;
|
||||
saveBtn.innerHTML = originalText;
|
||||
saveBtn.style.background = '';
|
||||
}, 2000);
|
||||
}
|
||||
@@ -1037,19 +1178,19 @@
|
||||
async function saveAllPlayers() {
|
||||
const saveAllBtn = document.querySelector('.save-all-btn');
|
||||
const originalText = saveAllBtn.textContent;
|
||||
saveAllBtn.textContent = '💾 Saving All...';
|
||||
saveAllBtn.textContent = `💾 ${t('scoring.saving_all')}`;
|
||||
saveAllBtn.disabled = true;
|
||||
|
||||
|
||||
try {
|
||||
const playerIds = Object.keys(results.participants);
|
||||
for (const playerId of playerIds) {
|
||||
await saveParticipant(parseInt(playerId), true);
|
||||
}
|
||||
|
||||
|
||||
// Show success feedback
|
||||
saveAllBtn.textContent = '✅ All Saved!';
|
||||
saveAllBtn.textContent = `✅ ${t('scoring.all_saved')}`;
|
||||
saveAllBtn.style.background = '#28a745';
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
saveAllBtn.textContent = originalText;
|
||||
saveAllBtn.style.background = '';
|
||||
@@ -1075,7 +1216,10 @@
|
||||
for (let i = 1; i <= numTargets; i++) {
|
||||
for (let shot = 1; shot <= shotsPerTarget; shot++) {
|
||||
const shotInput = document.getElementById(`shot${shot}-${playerId}-${i}`);
|
||||
if (shotInput) shotInput.value = '';
|
||||
if (shotInput) {
|
||||
shotInput.value = '';
|
||||
shotInput.classList.remove('valid', 'ten');
|
||||
}
|
||||
}
|
||||
|
||||
// Update data
|
||||
@@ -1090,8 +1234,10 @@
|
||||
}
|
||||
|
||||
updateParticipantTotal(playerId);
|
||||
updateParticipantTens(playerId);
|
||||
updateParticipantStatus(playerId);
|
||||
updateOverallProgress();
|
||||
updateOverallTens();
|
||||
|
||||
// Save changes
|
||||
saveParticipant(playerId);
|
||||
@@ -1117,7 +1263,15 @@
|
||||
const randomScore = Math.floor(Math.random() * 11);
|
||||
const shotInput = document.getElementById(`shot${shot}-${playerId}-${i}`);
|
||||
|
||||
if (shotInput) shotInput.value = randomScore;
|
||||
if (shotInput) {
|
||||
shotInput.value = randomScore;
|
||||
shotInput.classList.remove('valid', 'ten');
|
||||
if (randomScore === 10) {
|
||||
shotInput.classList.add('ten');
|
||||
} else {
|
||||
shotInput.classList.add('valid');
|
||||
}
|
||||
}
|
||||
target[`shot${shot}`] = randomScore;
|
||||
}
|
||||
|
||||
@@ -1129,8 +1283,10 @@
|
||||
}
|
||||
|
||||
updateParticipantTotal(playerId);
|
||||
updateParticipantTens(playerId);
|
||||
updateParticipantStatus(playerId);
|
||||
updateOverallProgress();
|
||||
updateOverallTens();
|
||||
}
|
||||
|
||||
async function fillAllPlayersRandom() {
|
||||
@@ -1140,7 +1296,7 @@
|
||||
|
||||
const fillAllBtn = document.querySelector('.fill-all-btn');
|
||||
const originalText = fillAllBtn.textContent;
|
||||
fillAllBtn.textContent = '🎲 Filling All...';
|
||||
fillAllBtn.textContent = `🎲 ${t('scoring.filling_all')}`;
|
||||
fillAllBtn.disabled = true;
|
||||
|
||||
try {
|
||||
@@ -1156,7 +1312,7 @@
|
||||
}
|
||||
|
||||
// Show success feedback
|
||||
fillAllBtn.textContent = '✅ All Filled!';
|
||||
fillAllBtn.textContent = `✅ ${t('scoring.all_filled')}`;
|
||||
fillAllBtn.style.background = '#28a745';
|
||||
|
||||
// Show notification
|
||||
@@ -1173,8 +1329,6 @@
|
||||
font-weight: bold;
|
||||
box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3);
|
||||
`;
|
||||
notification.textContent = '🎯 All players filled with random scores! Tournament ready to finish.';
|
||||
document.body.appendChild(notification);
|
||||
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(notification);
|
||||
@@ -1243,9 +1397,7 @@
|
||||
|
||||
// Initialize
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('Initial results structure:', results);
|
||||
console.log(`Tournament type: ${tournamentType}, Targets: ${numTargets}, Shots per target: ${shotsPerTarget}`);
|
||||
|
||||
|
||||
// Initialize targets for all players
|
||||
Object.keys(results.participants).forEach(playerId => {
|
||||
initializeTargetsForPlayer(parseInt(playerId));
|
||||
@@ -1255,9 +1407,11 @@
|
||||
updateTargetGroupStyling(parseInt(playerId), i);
|
||||
}
|
||||
updateParticipantStatus(parseInt(playerId));
|
||||
updateParticipantTens(parseInt(playerId));
|
||||
});
|
||||
|
||||
updateOverallProgress();
|
||||
updateOverallTens();
|
||||
|
||||
// Add input validation and navigation
|
||||
document.addEventListener('input', function(e) {
|
||||
@@ -1283,9 +1437,20 @@
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('🎯 Results Calculator initialized');
|
||||
console.log('📊 Participants:', Object.keys(results.participants).length);
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Internationalization Support -->
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
<script>
|
||||
// Initialize translations with server data
|
||||
if (typeof {{ translations|tojson }} !== 'undefined') {
|
||||
currentTranslations = {{ translations|tojson }};
|
||||
currentLanguage = '{{ current_language }}';
|
||||
}
|
||||
// Apply translations when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
translatePage();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
+398
-383
File diff suppressed because it is too large
Load Diff
+186
-114
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Tournament Management</title>
|
||||
<title data-i18n="tournament.tournament_management">Tournament Management</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
* {
|
||||
@@ -62,28 +62,17 @@
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.nav-btn.primary {
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.primary:hover {
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.success {
|
||||
background: #28a745;
|
||||
border-color: #1e7e34;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.success:hover {
|
||||
background: #1e7e34;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.danger {
|
||||
background: #dc3545;
|
||||
border-color: #c82333;
|
||||
@@ -193,21 +182,85 @@
|
||||
.type-option {
|
||||
background: white;
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.type-option:hover {
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 2px 8px rgba(0, 123, 255, 0.15);
|
||||
.type-option::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
border-radius: 12px 12px 0 0;
|
||||
background: #dee2e6;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.type-option.selected {
|
||||
border-color: #007bff;
|
||||
background: #f0f8ff;
|
||||
/* 4 Targets - Green theme */
|
||||
.type-option[data-type="4_targets"] {
|
||||
border-color: #11998e;
|
||||
}
|
||||
|
||||
.type-option[data-type="4_targets"]:hover {
|
||||
border-color: #0f766e;
|
||||
box-shadow: 0 4px 15px rgba(17, 153, 142, 0.25);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.type-option[data-type="4_targets"].selected {
|
||||
border-color: #0f766e;
|
||||
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
||||
}
|
||||
|
||||
.type-option[data-type="4_targets"]::before {
|
||||
background: linear-gradient(90deg, #11998e 0%, #38ef7d 100%);
|
||||
}
|
||||
|
||||
/* 20 Targets - Red theme */
|
||||
.type-option[data-type="20_targets"] {
|
||||
border-color: #ff6b6b;
|
||||
}
|
||||
|
||||
.type-option[data-type="20_targets"]:hover {
|
||||
border-color: #e53e3e;
|
||||
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.25);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.type-option[data-type="20_targets"].selected {
|
||||
border-color: #e53e3e;
|
||||
background: linear-gradient(135deg, #fff5f5 0%, #ffe8e8 100%);
|
||||
}
|
||||
|
||||
.type-option[data-type="20_targets"]::before {
|
||||
background: linear-gradient(90deg, #ff6b6b 0%, #ff8e53 100%);
|
||||
}
|
||||
|
||||
/* 40 Targets - Blue theme */
|
||||
.type-option[data-type="40_targets"] {
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.type-option[data-type="40_targets"]:hover {
|
||||
border-color: #5a67d8;
|
||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.25);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.type-option[data-type="40_targets"].selected {
|
||||
border-color: #5a67d8;
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #e8ebff 100%);
|
||||
}
|
||||
|
||||
.type-option[data-type="40_targets"]::before {
|
||||
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.type-option input[type="radio"] {
|
||||
@@ -294,11 +347,12 @@
|
||||
padding: 15px 30px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 1.1rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
transition: all 0.2s ease;
|
||||
min-width: 200px;
|
||||
text-decoration: none;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
@@ -909,16 +963,19 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar">
|
||||
<div class="navbar-title">🏆 Tournament Management</div>
|
||||
<div class="navbar-title">🏆 <span data-i18n="tournament.tournament_management">Tournament Management</span></div>
|
||||
<div class="navbar-controls">
|
||||
<a href="/" class="nav-btn">← Dashboard</a>
|
||||
<a href="/" class="nav-btn">📺 <span data-i18n="navigation.dashboard">Dashboard</span></a>
|
||||
<a href="/tournament" class="nav-btn active">🏆 <span data-i18n="navigation.tournament">Tournament</span></a>
|
||||
<a href="/archive/player-analysis" class="nav-btn">👤 <span data-i18n="players.player_analysis">Player Analysis</span></a>
|
||||
<a href="/archive" class="nav-btn">📚 <span data-i18n="navigation.archive">Archive</span></a>
|
||||
{% if league_state and not league_state.league_finished %}
|
||||
<a href="/results/calculator" class="nav-btn primary">🎯 Results Calculator</a>
|
||||
<button class="nav-btn danger" onclick="resetLeague()">🗑️ Reset League</button>
|
||||
<a href="/results/calculator" class="nav-btn">🎯 <span data-i18n="navigation.calculator">Results Calculator</span></a>
|
||||
<button class="nav-btn danger" onclick="resetLeague()"> <span data-i18n="league.reset_league">Reset League</span></button>
|
||||
{% elif tournament_state %}
|
||||
<a href="/tournament/draft" class="nav-btn">📋 View Draft</a>
|
||||
<a href="/results/calculator" class="nav-btn primary">🎯 Results Calculator</a>
|
||||
<button class="nav-btn danger" onclick="resetTournament()">🗑️ Reset Tournament</button>
|
||||
<a href="/tournament/draft" class="nav-btn">📋 <span data-i18n="tournament.view_draft">Draft</span></a>
|
||||
<a href="/results/calculator" class="nav-btn">🎯 <span data-i18n="navigation.calculator">Results Calculator</span></a>
|
||||
<button class="nav-btn danger" onclick="resetTournament()">🗑️ <span data-i18n="tournament.reset_tournament">Reset Tournament</span></button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -927,12 +984,12 @@
|
||||
<!-- League/Tournament Management Section -->
|
||||
<div class="section">
|
||||
{% if league_state and not league_state.league_finished %}
|
||||
<h2 class="section-title">🏆 League Management</h2>
|
||||
<h2 class="section-title" data-i18n="league.league_management">🏆 Upravljanje Lige</h2>
|
||||
<div class="league-status">
|
||||
<div class="league-active">🏆 League Active</div>
|
||||
<div class="league-active" data-i18n="league.league_active">🏆 League Active</div>
|
||||
<div class="league-info">
|
||||
<div class="info-item">
|
||||
<div class="info-label">Tournament Type</div>
|
||||
<div class="info-label" data-i18n="tournament.tournament_type">Tip Turnirja</div>
|
||||
<div class="info-value">
|
||||
{% if league_state.tournament_type == '40_targets' %}
|
||||
40 Targets
|
||||
@@ -944,19 +1001,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Current Tournament</div>
|
||||
<div class="info-label" data-i18n="tournament.current_tournament">Trenutni Turnir</div>
|
||||
<div class="info-value">{{ league_state.current_tournament }} / {{ league_state.total_tournaments }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Participants</div>
|
||||
<div class="info-label" data-i18n="tournament.participants">Udeleženci</div>
|
||||
<div class="info-value">{{ league_state.participants|length }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Completed</div>
|
||||
<div class="info-label" data-i18n="tournament.completed">Zaključeno</div>
|
||||
<div class="info-value">{{ league_state.completed_tournaments|length }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Created</div>
|
||||
<div class="info-label" data-i18n="tournament.created">Ustvarjeno</div>
|
||||
<div class="info-value">{{ league_state.created_at[:10] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -966,8 +1023,8 @@
|
||||
{% if league_state.current_tournament < league_state.total_tournaments %}
|
||||
<!-- Joker selection -->
|
||||
<div class="joker-section" id="jokerSection">
|
||||
<div class="joker-title">🃏 Joker Selection for Tournament {{ league_state.current_tournament + 1 }}</div>
|
||||
<p style="margin-bottom: 15px; color: #856404;">Select players who will use their joker (skip this tournament). Each player can only use their joker once per league.</p>
|
||||
<div class="joker-title">🃏 <span data-i18n="league.joker_selection_for_tournament">Izbira Jokerja za Turnir</span> {{ league_state.current_tournament + 1 }}</div>
|
||||
<p style="margin-bottom: 15px; color: #856404;" data-i18n="league.joker_instructions">Izberite igralce, ki bodo uporabili svojega Jokerja (preskočili ta turnir). Vsak igralec lahko uporabi svojega Žolna samo enkrat na ligo.</p>
|
||||
|
||||
<div class="joker-grid">
|
||||
{% for player_id, participant in league_state.participants.items() %}
|
||||
@@ -985,12 +1042,12 @@
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="action-btn success" onclick="startNextTournament()">
|
||||
🚀 Start Tournament {{ league_state.current_tournament + 1 }}
|
||||
🚀 <span data-i18n="league.start_tournament_number">Začni Turnir</span> {{ league_state.current_tournament + 1 }}
|
||||
</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="warning">
|
||||
<strong>League Complete!</strong> All tournaments scheduled. Finish current one to see final results.
|
||||
<strong data-i18n="league.league_complete">League Complete!</strong> <span data-i18n="league.league_complete_info">All tournaments planned. Finish current for final results.</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -1002,7 +1059,7 @@
|
||||
<div class="league-active">🏆 League Completed!</div>
|
||||
<div class="league-info">
|
||||
<div class="info-item">
|
||||
<div class="info-label">Tournament Type</div>
|
||||
<div class="info-label" data-i18n="tournament.tournament_type">Tip Turnirja</div>
|
||||
<div class="info-value">
|
||||
{% if league_state.tournament_type == '40_targets' %}
|
||||
40 Targets
|
||||
@@ -1014,32 +1071,32 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Participants</div>
|
||||
<div class="info-label" data-i18n="tournament.participants">Udeleženci</div>
|
||||
<div class="info-value">{{ league_state.participants|length }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Tournaments</div>
|
||||
<div class="info-label" data-i18n="tournament.tournaments">Turnirji</div>
|
||||
<div class="info-value">{{ league_state.total_tournaments }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Finished</div>
|
||||
<div class="info-label" data-i18n="tournament.finished">Zaključeno</div>
|
||||
<div class="info-value">{{ league_state.finished_at[:10] if league_state.finished_at else 'Today' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<a href="/results" class="action-btn success">🏆 View League Results</a>
|
||||
<button class="action-btn danger" onclick="resetLeague()">🗑️ Reset League</button>
|
||||
<a href="/results" class="action-btn success">🏆 <span data-i18n="league.view_league_results">View League Results</span></a>
|
||||
<button class="action-btn danger" onclick="resetLeague()">🗑️ <span data-i18n="league.reset_league">Reset League</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% elif not league_state and tournament_state %}
|
||||
<h2 class="section-title">🎯 Single Tournament Management</h2>
|
||||
<h2 class="section-title" data-i18n="tournament.tournament_management">🎯 Upravljanje Turnirja</h2>
|
||||
<div class="tournament-status">
|
||||
<div class="tournament-active">🎯 Tournament Active</div>
|
||||
<div class="tournament-active" data-i18n="tournament.active_tournament">🎯 Aktiven Turnir</div>
|
||||
<div class="league-info">
|
||||
<div class="info-item">
|
||||
<div class="info-label">Tournament Type</div>
|
||||
<div class="info-label" data-i18n="tournament.tournament_type">Tip Turnirja</div>
|
||||
<div class="info-value">
|
||||
{% if tournament_state.tournament_type == '40_targets' %}
|
||||
40 Targets
|
||||
@@ -1051,53 +1108,53 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Players</div>
|
||||
<div class="info-label" data-i18n="players.players_label">Igralci</div>
|
||||
<div class="info-value">{{ tournament_state.total_players }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Rounds</div>
|
||||
<div class="info-label" data-i18n="tournament.total_rounds">Skupaj Krogov</div>
|
||||
<div class="info-value">{{ tournament_state.total_rounds }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Current Round</div>
|
||||
<div class="info-label" data-i18n="tournament.current_round">Trenutni Krog</div>
|
||||
<div class="info-value">{{ tournament_state.current_round }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Created</div>
|
||||
<div class="info-label" data-i18n="tournament.created">Ustvarjeno</div>
|
||||
<div class="info-value">{{ tournament_state.created_at[:10] if tournament_state.created_at else 'Today' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<a href="/tournament/draft" class="action-btn">📋 View Draft</a>
|
||||
<a href="/results/calculator" class="action-btn success">🎯 Score Tournament</a>
|
||||
<a href="/" class="action-btn">📺 Dashboard</a>
|
||||
<button class="action-btn danger" onclick="resetTournament()">🗑️ Reset Tournament</button>
|
||||
<a href="/tournament/draft" class="action-btn">📋 <span data-i18n="navigation.draft">Žreb</span></a>
|
||||
<a href="/results/calculator" class="action-btn success">🎯 <span data-i18n="navigation.calculator">Results Calculator</span></a>
|
||||
<a href="/" class="action-btn">📺 <span data-i18n="navigation.dashboard">Nadzorna Plošča</span></a>
|
||||
<button class="action-btn danger" onclick="resetTournament()">🗑️ <span data-i18n="tournament.reset_tournament">Reset Tournament</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<h2 class="section-title">🏁 Setup</h2>
|
||||
<div class="league-inactive">No Active League or Tournament</div>
|
||||
<h2 class="section-title" data-i18n="league.setup">🏁 Nastavitev</h2>
|
||||
<div class="league-inactive" data-i18n="league.no_active_league_tournament">Ni Aktivne Lige ali Turnirja</div>
|
||||
|
||||
<!-- Tournament Type Selection -->
|
||||
<div class="tournament-type-selection">
|
||||
<div class="type-title">Select Tournament Type</div>
|
||||
<div class="type-title" data-i18n="league.select_tournament_type">Izberi Tip Turnirja</div>
|
||||
<div class="type-options">
|
||||
<div class="type-option" onclick="selectTournamentType('4_targets')">
|
||||
<div class="type-option" data-type="4_targets" onclick="selectTournamentType('4_targets')">
|
||||
<input type="radio" name="tournament_type" value="4_targets">
|
||||
<div class="type-name">🎯 4 Targets</div>
|
||||
<div class="type-description">Quick format with 4 targets, 5 shots each <br> (20 shots total)</div>
|
||||
<div class="type-description" data-i18n="tournament_types.4_targets_desc">Hitri format s 4 tarčami, 5 strelov na tarčo (20 strelov skupaj)</div>
|
||||
</div>
|
||||
<div class="type-option selected" onclick="selectTournamentType('20_targets')">
|
||||
<div class="type-option selected" data-type="20_targets" onclick="selectTournamentType('20_targets')">
|
||||
<input type="radio" name="tournament_type" value="20_targets" checked>
|
||||
<div class="type-name">⚡ 20 Targets</div>
|
||||
<div class="type-description">Standard format with 20 targets, 2 shots each (40 shots total)</div>
|
||||
<div class="type-description" data-i18n="tournament_types.20_targets_desc">Standardni format z 20 tarčami, 2 strela na tarčo (40 strelov skupaj)</div>
|
||||
</div>
|
||||
<div class="type-option" onclick="selectTournamentType('40_targets')">
|
||||
<div class="type-option" data-type="40_targets" onclick="selectTournamentType('40_targets')">
|
||||
<input type="radio" name="tournament_type" value="40_targets">
|
||||
<div class="type-name">💪 40 Targets</div>
|
||||
<div class="type-description">Extended format with 40 targets, 2 shots each (80 shots total)</div>
|
||||
<div class="type-description" data-i18n="tournament_types.40_targets_desc">Razširjeni format s 40 tarčami, 2 strela na tarčo (80 strelov skupaj)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1107,8 +1164,8 @@
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="action-btn success" id="startLeagueBtn" onclick="startLeague()">🏆 Start League (6 Tournaments)</button>
|
||||
<button class="action-btn" id="startSingleBtn" onclick="startSingleTournament()">🏅 Start Single Tournament</button>
|
||||
<button class="action-btn success" id="startLeagueBtn" onclick="startLeague()">🏆 <span data-i18n="league.start_league_5_tournaments">Začni Ligo (5 Turnirjev)</span></button>
|
||||
<button class="action-btn" id="startSingleBtn" onclick="startSingleTournament()">🏅 <span data-i18n="league.start_single_tournament">Začni Posamezen Turnir</span></button>
|
||||
</div>
|
||||
|
||||
<div class="warning" id="warningMessage" style="display: none;">
|
||||
@@ -1123,10 +1180,10 @@
|
||||
<h2 class="section-title">📋 Current Tournament</h2>
|
||||
|
||||
<div class="tournament-status">
|
||||
<div class="tournament-active">🎯 Tournament Active</div>
|
||||
<div class="tournament-active">🎯 <span data-i18n="tournament.active_tournament">Aktiven Turnir</span></div>
|
||||
<div class="tournament-info">
|
||||
<div class="info-item">
|
||||
<div class="info-label">Tournament Type</div>
|
||||
<div class="info-label" data-i18n="tournament.tournament_type">Tip Turnirja</div>
|
||||
<div class="info-value">
|
||||
{% if tournament_state.tournament_type == '40_targets' %}
|
||||
40 Targets
|
||||
@@ -1138,11 +1195,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Total Players</div>
|
||||
<div class="info-label" data-i18n="players.total_players">Skupaj Igralcev</div>
|
||||
<div class="info-value">{{ tournament_state.total_players }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Total Rounds</div>
|
||||
<div class="info-label" data-i18n="tournament.total_rounds">Skupaj Krogov</div>
|
||||
<div class="info-value">{{ tournament_state.total_rounds }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
@@ -1151,17 +1208,17 @@
|
||||
</div>
|
||||
{% if league_state %}
|
||||
<div class="info-item">
|
||||
<div class="info-label">League Tournament</div>
|
||||
<div class="info-label" data-i18n="tournament.league_tournament">League Tournament</div>
|
||||
<div class="info-value">{{ tournament_state.league_tournament_number or 'N/A' }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<a href="/tournament/draft" class="action-btn">📋 View Draft</a>
|
||||
<a href="/results/calculator" class="action-btn success">🎯 Score Tournament</a>
|
||||
<a href="/" class="action-btn">📺 Dashboard</a>
|
||||
<button class="action-btn danger" onclick="resetTournament()">🗑️ Reset Tournament</button>
|
||||
<a href="/tournament/draft" class="action-btn">📋 <span data-i18n="navigation.draft">Žreb</span></a>
|
||||
<a href="/results/calculator" class="action-btn success">🎯 <span data-i18n="navigation.calculator">Results Calculator</span></a>
|
||||
<a href="/" class="action-btn">📺 <span data-i18n="navigation.dashboard">Nadzorna Plošča</span></a>
|
||||
<button class="action-btn danger" onclick="resetTournament()">🗑️ <span data-i18n="tournament.reset_tournament">Ponastavi Turnir</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1170,19 +1227,19 @@
|
||||
<!-- Player Management Section - NEW LIST FORMAT -->
|
||||
{% if not league_state and not tournament_state %}
|
||||
<div class="section">
|
||||
<h2 class="section-title">👥 Player Management</h2>
|
||||
<h2 class="section-title" data-i18n="players.player_management">👥 Upravljanje Igralcev</h2>
|
||||
|
||||
<!-- Add Player Section -->
|
||||
<div class="add-player-section">
|
||||
<h3 style="margin: 0 0 15px 0; color: #333;">Add New Player</h3>
|
||||
<h3 style="margin: 0 0 15px 0; color: #333;" data-i18n="league.add_new_player">Dodaj Novega Igralca</h3>
|
||||
<div class="add-player-form">
|
||||
<input type="text"
|
||||
id="newPlayerName"
|
||||
placeholder="Enter player name..."
|
||||
data-i18n="[placeholder]league.enter_player_name" placeholder="Vnesite ime igralca..."
|
||||
maxlength="50"
|
||||
onkeypress="handleAddPlayerKeypress(event)">
|
||||
<button class="add-btn" id="addPlayerBtn" onclick="addPlayer()">
|
||||
➕ Add Player
|
||||
<span data-i18n="players.add_player">➕ Dodaj Igralca</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1193,21 +1250,21 @@
|
||||
<input type="text"
|
||||
class="search-input"
|
||||
id="playerSearch"
|
||||
placeholder="Search players by name..."
|
||||
data-i18n="[placeholder]league.search_players_placeholder" placeholder="Išči igralce po imenu..."
|
||||
oninput="filterPlayers()">
|
||||
<span class="search-icon">🔍</span>
|
||||
</div>
|
||||
|
||||
<div class="player-controls">
|
||||
<button class="filter-btn active" data-filter="all" onclick="setFilter('all')">All</button>
|
||||
<button class="filter-btn" data-filter="enabled" onclick="setFilter('enabled')">Enabled</button>
|
||||
<button class="filter-btn" data-filter="disabled" onclick="setFilter('disabled')">Disabled</button>
|
||||
<button class="filter-btn active" data-filter="all" onclick="setFilter('all')" data-i18n="general.all">Vse</button>
|
||||
<button class="filter-btn" data-filter="enabled" onclick="setFilter('enabled')" data-i18n="players.enabled">Omogočen</button>
|
||||
<button class="filter-btn" data-filter="disabled" onclick="setFilter('disabled')" data-i18n="players.disabled">Onemogočen</button>
|
||||
</div>
|
||||
|
||||
<div class="bulk-actions">
|
||||
<button class="bulk-btn" onclick="selectAllVisible()">Select All</button>
|
||||
<button class="bulk-btn" onclick="enableSelected()">Enable Selected</button>
|
||||
<button class="bulk-btn danger" onclick="disableSelected()">Disable Selected</button>
|
||||
<button class="bulk-btn" onclick="selectAllVisible()" data-i18n="general.select_all">Izberi Vse</button>
|
||||
<button class="bulk-btn" onclick="enableSelected()" data-i18n="general.enable_selected">Omogoči Izbrane</button>
|
||||
<button class="bulk-btn danger" onclick="disableSelected()" data-i18n="general.disable_selected">Onemogoči Izbrane</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1215,19 +1272,19 @@
|
||||
<div class="stats-summary">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number stat-total" id="totalPlayersCount">0</span>
|
||||
<span>Total Players</span>
|
||||
<span data-i18n="players.total_players">Skupaj Igralcev</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-number stat-enabled" id="enabledPlayersCount">0</span>
|
||||
<span>Enabled</span>
|
||||
<span data-i18n="players.enabled">Omogočeni</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-number stat-disabled" id="disabledPlayersCount">0</span>
|
||||
<span>Disabled</span>
|
||||
<span data-i18n="players.disabled">Onemogočeni</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-number" id="visiblePlayersCount">0</span>
|
||||
<span>Visible</span>
|
||||
<span data-i18n="general.visible">Vidni</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1239,10 +1296,10 @@
|
||||
<th style="width: 40px;">
|
||||
<input type="checkbox" id="selectAllCheckbox" onchange="toggleSelectAll()">
|
||||
</th>
|
||||
<th style="width: 60px;">ID</th>
|
||||
<th>Name</th>
|
||||
<th class="status-col" style="width: 120px;">Status</th>
|
||||
<th class="actions-col" style="width: 200px;">Actions</th>
|
||||
<th style="width: 60px;" data-i18n="league.id">ID</th>
|
||||
<th data-i18n="league.name">Ime</th>
|
||||
<th class="status-col" style="width: 120px;" data-i18n="general.status">Status</th>
|
||||
<th class="actions-col" style="width: 200px;" data-i18n="general.actions">Dejanja</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="playerTableBody">
|
||||
@@ -1251,7 +1308,7 @@
|
||||
</table>
|
||||
|
||||
<div class="no-results" id="noResults" style="display: none;">
|
||||
No players found matching your search criteria.
|
||||
<span data-i18n="league.no_players_found">Ni najdenih igralcev, ki bi ustrezali kriterijem iskanja.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1392,7 +1449,7 @@
|
||||
// Bulk actions
|
||||
function enableSelected() {
|
||||
if (selectedPlayers.size === 0) {
|
||||
alert('No players selected');
|
||||
alert(t('league.no_players_selected'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1405,7 +1462,7 @@
|
||||
|
||||
function disableSelected() {
|
||||
if (selectedPlayers.size === 0) {
|
||||
alert('No players selected');
|
||||
alert(t('league.no_players_selected'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1506,7 +1563,7 @@
|
||||
</td>
|
||||
<td class="player-status status-col">
|
||||
<span class="status-badge ${player.enabled ? 'status-enabled' : 'status-disabled'}">
|
||||
${player.enabled ? '✓ Enabled' : '✗ Disabled'}
|
||||
${player.enabled ? '✓' : '✗'}
|
||||
</span>
|
||||
</td>
|
||||
<td class="player-actions actions-col">
|
||||
@@ -1521,7 +1578,7 @@
|
||||
<button class="action-btn-small edit-btn-small"
|
||||
onclick="editPlayer(${player.id})"
|
||||
title="Edit Player Name">
|
||||
✏️ Edit
|
||||
✏️ <span data-i18n="league.edit">Uredi</span>
|
||||
</button>
|
||||
<button class="action-btn-small delete-btn-small"
|
||||
onclick="confirmDeletePlayer(${player.id})"
|
||||
@@ -1576,7 +1633,8 @@
|
||||
alert('Error adding player. Please try again.');
|
||||
} finally {
|
||||
addBtn.disabled = false;
|
||||
addBtn.textContent = '➕ Add Player';
|
||||
addBtn.innerHTML = '<span data-i18n="players.add_player">➕ Dodaj Igralca</span>';
|
||||
translatePage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1737,11 +1795,11 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const formatText = selectedTournamentType === '40_targets' ? '40 targets (80 shots)' :
|
||||
selectedTournamentType === '4_targets' ? '4 targets (20 shots)' :
|
||||
'20 targets (40 shots)';
|
||||
const formatText = selectedTournamentType === '40_targets' ? t('tournament_types.40_targets_full') :
|
||||
selectedTournamentType === '4_targets' ? t('tournament_types.4_targets_full') :
|
||||
t('tournament_types.20_targets_full');
|
||||
|
||||
if (!confirm(`Start league with ${enabledPlayers.length} players using ${formatText} format?`)) {
|
||||
if (!confirm(t('messages.confirm_start_league', { players: enabledPlayers.length, format: formatText }))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1771,7 +1829,9 @@
|
||||
alert('Error starting league. Please try again.');
|
||||
} finally {
|
||||
startBtn.disabled = false;
|
||||
startBtn.textContent = '🏆 Start League (6 Tournaments)';
|
||||
startBtn.setAttribute('data-i18n', 'league.start_league_5_tournaments');
|
||||
startBtn.textContent = '🎖️ Začni Ligo (5 Turnirjev)';
|
||||
translatePage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1784,11 +1844,11 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const formatText = selectedTournamentType === '40_targets' ? '40 targets (80 shots)' :
|
||||
selectedTournamentType === '4_targets' ? '4 targets (20 shots)' :
|
||||
'20 targets (40 shots)';
|
||||
const formatText = selectedTournamentType === '40_targets' ? t('tournament_types.40_targets_full') :
|
||||
selectedTournamentType === '4_targets' ? t('tournament_types.4_targets_full') :
|
||||
t('tournament_types.20_targets_full');
|
||||
|
||||
if (!confirm(`Start single tournament with ${enabledPlayers.length} players using ${formatText} format?`)) {
|
||||
if (!confirm(t('messages.confirm_start_tournament', { players: enabledPlayers.length, format: formatText }))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1828,7 +1888,10 @@
|
||||
const jokerCheckboxes = document.querySelectorAll('.joker-checkbox:checked:not(:disabled)');
|
||||
const jokerPlayers = Array.from(jokerCheckboxes).map(cb => parseInt(cb.dataset.playerId));
|
||||
|
||||
if (!confirm(`Start next tournament? ${jokerPlayers.length} players will use their joker.`)) {
|
||||
const confirmMessage = jokerPlayers.length === 1 ?
|
||||
t('league.start_tournament_confirm_single') :
|
||||
t('league.start_tournament_confirm_multiple').replace('{count}', jokerPlayers.length);
|
||||
if (!confirm(confirmMessage)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1948,5 +2011,14 @@
|
||||
console.log('Tournament active:', tournamentActive);
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Include i18n support -->
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
<script>
|
||||
// Initialize language selector and i18n
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
translatePage();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user