Files
Sdk_TV_app/templates/league_scoreboard_display.html
T
2025-11-14 17:03:30 +01:00

1240 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title data-i18n="league.league_results">League Results</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/static/css/base.css">
<link rel="stylesheet" href="/static/css/navbar.css">
<link rel="stylesheet" href="/static/css/buttons.css">
<link rel="stylesheet" href="/static/css/components.css">
<link rel="stylesheet" href="/static/css/responsive.css">
<style>
/* League scoreboard specific styles */
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
background: #f5f5f5;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
height: 100vh;
overflow: hidden;
color: #333;
}
.navbar {
background: white;
border-bottom: 1px solid #e1e5e9;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
padding: 15px 25px;
display: flex;
align-items: center;
justify-content: space-between;
}
.navbar-logo {
height: 40px;
max-width: 120px;
object-fit: contain;
}
.navbar-title {
font-size: 1.8rem;
font-weight: bold;
color: #333;
margin-left: 15px;
}
.navbar-brand {
display: flex;
align-items: center;
}
.navbar-controls {
display: flex;
gap: 12px;
align-items: center;
}
.nav-btn {
background: #f8f9fa;
border: 2px solid #e9ecef;
cursor: pointer;
padding: 12px 20px;
border-radius: 8px;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
color: #333;
text-decoration: none;
font-weight: bold;
font-size: 0.9rem;
}
.nav-btn:hover {
background: #e9ecef;
border-color: #28a745;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
color: #28a745;
}
.nav-btn.primary {
background: #28a745;
border-color: #1e7e34;
color: white;
}
.nav-btn.primary:hover {
background: #1e7e34;
color: white;
}
/* Main League Layout */
.league-container {
height: calc(100vh - 90px);
display: grid;
grid-template-columns: 1fr 3fr;
gap: 20px;
padding: 20px;
}
/* Left Column - Header & Champions */
.left-column {
display: flex;
flex-direction: column;
gap: 20px;
}
.league-header {
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
padding: 25px;
text-align: center;
flex-shrink: 0;
color: white;
position: relative;
overflow: hidden;
}
/* Dynamic header colors based on tournament type */
.league-header.tournament-4_targets {
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
}
.league-header.tournament-20_targets {
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%);
}
.league-header.tournament-40_targets {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.league-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%, rgba(255, 255, 255, 0.05) 100%);
pointer-events: none;
}
.league-header * {
position: relative;
z-index: 2;
}
.header-logo {
height: 70px;
max-width: 180px;
object-fit: contain;
margin-bottom: 20px;
filter: brightness(1.2) contrast(1.1);
background-color: white;
padding: 10px;
border-radius: 8px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.league-title {
font-size: 2.2rem;
font-weight: 700;
color: rgb(255, 255, 255);
margin-bottom: 10px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.league-subtitle {
font-size: 1.1rem;
color: rgba(255, 255, 255, 0.95);
margin-bottom: 20px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.league-meta {
display: flex;
justify-content: space-around;
gap: 15px;
}
.meta-item {
text-align: center;
}
.meta-number {
font-size: 1.6rem;
font-weight: 700;
color: #ffffff;
display: block;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.meta-label {
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.9);
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 500;
}
/* League Champion Section */
.champion-section {
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 25px;
flex: 1;
display: flex;
flex-direction: column;
}
.champion-title {
text-align: center;
font-size: 1.4rem;
font-weight: 700;
color: #2c3e50;
margin-bottom: 20px;
}
.champion-container {
display: flex;
flex-direction: column;
gap: 15px;
flex: 1;
}
.champion-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;
border-left: 5px solid;
}
.champion-card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}
.champion-card.rank-1 {
border-left-color: #ffd700;
background: linear-gradient(135deg, #fff9e6 0%, #ffffff 100%);
}
.champion-card.rank-2 {
border-left-color: #c0c0c0;
background: linear-gradient(135deg, #f5f5f5 0%, #ffffff 100%);
}
.champion-card.rank-3 {
border-left-color: #cd7f32;
background: linear-gradient(135deg, #fdf6f0 0%, #ffffff 100%);
}
.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;
}
.champion-card.rank-1 .rank-number { color: #b8860b; }
.champion-card.rank-2 .rank-number { color: #696969; }
.champion-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-details {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.participant-id {
background: #28a745;
color: white;
padding: 3px 10px;
border-radius: 12px;
font-size: 0.8rem;
font-weight: bold;
display: inline-block;
}
.joker-badge {
background: #ffc107;
color: #856404;
padding: 3px 8px;
border-radius: 8px;
font-size: 0.7rem;
font-weight: bold;
display: inline-block;
}
.score-display {
text-align: right;
min-width: 100px;
}
.final-score {
font-size: 2rem;
font-weight: bold;
color: #28a745;
line-height: 1;
}
.total-score {
font-size: 0.9rem;
color: #666;
margin-top: 2px;
}
.tens-count {
font-size: 0.9rem;
color: #ffc107;
font-weight: bold;
margin-top: 2px;
}
.score-label {
font-size: 0.8rem;
color: #666;
text-transform: uppercase;
font-weight: bold;
}
/* Right Column - League Table */
.right-column {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
display: flex;
flex-direction: column;
}
.table-header {
background: #f8f9fa;
padding: 15px 20px;
border-bottom: 1px solid #dee2e6;
flex-shrink: 0;
}
.table-title {
font-size: 1.25rem;
font-weight: 600;
color: #2c3e50;
margin: 0;
}
.table-container {
flex: 1;
overflow-y: auto;
}
.league-table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
}
.league-table th,
.league-table td {
padding: 7px 5px;
text-align: center;
border-bottom: 1px solid #f1f3f4;
border-right: 1px solid #f1f3f4;
}
.league-table th {
background: #f8f9fa;
font-weight: 600;
color: #495057;
text-transform: uppercase;
font-size: 0.6rem;
letter-spacing: 0.5px;
position: sticky;
top: 0;
z-index: 10;
}
.league-table th.player-col {
text-align: left;
width: 120px;
}
.league-table th.tournament-col {
width: 70px;
}
.league-table th.final-col {
width: 80px;
background: #e3f2fd;
}
.league-table th.tens-col {
width: 70px;
background: #fff3cd;
}
.league-table tbody tr:hover {
background: #f8f9fa;
}
.league-table tbody tr:nth-child(1) {
background: #fff9e6;
}
.league-table tbody tr:nth-child(1):hover {
background: #fff3cd;
}
.league-table tbody tr:nth-child(2) {
background: #f5f5f5;
}
.league-table tbody tr:nth-child(2):hover {
background: #e9ecef;
}
.league-table tbody tr:nth-child(3) {
background: #fdf6f0;
}
.league-table tbody tr:nth-child(3):hover {
background: #f8f1e6;
}
.rank-cell {
font-size: 0.9rem;
font-weight: 700;
width: 45px;
text-align: center;
}
.rank-1 { color: #b8860b; }
.rank-2 { color: #6c757d; }
.rank-3 { color: #8b4513; }
.player-cell {
text-align: left !important;
padding-left: 10px !important;
}
.player-name {
font-size: 0.85rem;
font-weight: 600;
color: #2c3e50;
word-break: keep-all;
overflow-wrap: break-word;
hyphens: none;
}
.player-id {
background: #28a745;
color: white;
padding: 2px 5px;
border-radius: 5px;
font-size: 0.6rem;
font-weight: 500;
display: inline-block;
margin-top: 2px;
}
.tournament-score {
font-size: 0.8rem;
font-weight: 600;
color: #333;
min-width: 35px;
}
.tournament-score.joker {
background: #ffc107;
color: #856404;
padding: 2px 5px;
border-radius: 3px;
font-size: 0.65rem;
font-weight: 600;
}
.tournament-score.excluded {
background: #dc3545;
color: white;
padding: 2px 5px;
border-radius: 3px;
text-decoration: line-through;
opacity: 0.8;
font-size: 0.65rem;
}
.tournament-score.counted {
background: #28a745;
color: white;
padding: 2px 5px;
border-radius: 3px;
font-weight: 600;
font-size: 0.7rem;
}
.final-score-cell {
background: #e3f2fd !important;
font-size: 1rem;
font-weight: 700;
color: #1976d2;
}
.tens-count-cell {
background: #fff3cd !important;
font-size: 0.9rem;
font-weight: 700;
color: #856404;
}
/* Tournament Headers */
.tournament-header-group {
background: #e3f2fd;
border-bottom: 2px solid #1976d2;
}
.tournament-header-group th {
background: #e3f2fd;
color: #1976d2;
font-weight: 700;
}
.tens-header-group {
background: #fff3cd;
border-bottom: 2px solid #856404;
}
.tens-header-group th {
background: #fff3cd;
color: #856404;
font-weight: 700;
}
/* Calculation Legend */
.calculation-legend {
background: #f8f9fa;
border-top: 1px solid #dee2e6;
padding: 15px 20px;
flex-shrink: 0;
}
.legend-title {
font-size: 0.9rem;
font-weight: 600;
color: #2c3e50;
margin-bottom: 10px;
}
.legend-items {
display: flex;
gap: 15px;
flex-wrap: wrap;
align-items: center;
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 0.75rem;
color: #495057;
}
.legend-box {
width: 18px;
height: 14px;
border-radius: 3px;
display: inline-block;
}
.legend-joker { background: #ffc107; }
.legend-excluded { background: #dc3545; }
.legend-counted { background: #28a745; }
.legend-final { background: #1976d2; }
.legend-tens { background: #856404; }
/* League Stats Footer */
.stats-footer {
background: white;
border-top: 1px solid #dee2e6;
padding: 10px 20px;
display: flex;
justify-content: space-around;
font-size: 0.8rem;
color: #6c757d;
flex-shrink: 0;
}
.stat-item {
text-align: center;
}
.stat-value {
font-weight: 600;
color: #28a745;
}
/* Mobile responsive */
@media (max-width: 768px) {
.league-container {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
}
.league-meta {
gap: 10px;
}
.meta-number {
font-size: 1.4rem;
}
.champion-section {
padding: 20px 15px;
}
.league-table {
font-size: 0.7rem;
}
.league-table th,
.league-table td {
padding: 5px 3px;
}
}
/* PRINT STYLES */
@media print {
.navbar {
display: none !important;
}
html, body {
height: auto !important;
overflow: visible !important;
background: white !important;
margin: 0;
padding: 20px;
}
.league-container {
display: block !important;
height: auto !important;
padding: 0 !important;
gap: 0 !important;
margin: 0 !important;
}
.left-column {
display: block !important;
margin-bottom: 0;
}
.league-header {
background: white !important;
color: #333 !important;
box-shadow: none !important;
border: 2px solid #ddd !important;
padding: 20px;
margin-bottom: 20px;
}
.league-header::before {
display: none !important;
}
.header-logo {
height: 60px !important;
max-width: 160px !important;
background-color: transparent !important;
padding: 0 !important;
border: none !important;
filter: none !important;
margin-bottom: 15px;
}
.league-title {
font-size: 24pt !important;
font-weight: bold !important;
color: #333 !important;
text-shadow: none !important;
margin-bottom: 10px;
}
.league-subtitle {
font-size: 14pt !important;
color: #666 !important;
text-shadow: none !important;
margin-bottom: 15px;
}
.league-meta {
color: #333 !important;
}
.meta-number {
color: #333 !important;
text-shadow: none !important;
font-size: 16pt !important;
}
.meta-label {
color: #666 !important;
font-size: 10pt !important;
}
.champion-section {
display: none !important;
}
.right-column {
background: white !important;
border-radius: 0 !important;
box-shadow: none !important;
overflow: visible !important;
display: block !important;
margin: 0 !important;
padding: 0 !important;
}
.table-header {
display: none !important;
}
.league-table {
margin-top: 0 !important;
}
.print-title {
display: none !important;
}
.table-title {
display: none !important;
}
}
</style>
<script src="/static/js/i18n.js"></script>
</head>
<body>
<div class="navbar">
<div class="navbar-brand">
<div class="navbar-title">🎖️ <span data-i18n="league.league_results">League Results</span></div>
</div>
<div class="navbar-controls">
<a href="/" class="nav-btn">📺 <span data-i18n="navigation.dashboard">Dashboard</span></a>
<button class="nav-btn" onclick="exportLeagueJSON()">💾 <span data-i18n="general.export">Export JSON</span></button>
<button class="nav-btn" onclick="window.print()">🖨️ <span data-i18n="general.print">Print</span></button>
</div>
</div>
<div class="league-container">
<!-- Left Column -->
<div class="left-column">
<!-- League Header -->
<div class="league-header tournament-{{ league.tournament_type if league and league.tournament_type else '20_targets' }}">
<img src="/static/logo.png" alt="Logo" class="header-logo" onerror="this.style.display='none'" />
<div class="league-title">
{% if league and league.tournament_type == '4_targets' %}
🎯 <span data-i18n="league.league_championship">League Championship</span>
{% elif league and league.tournament_type == '40_targets' %}
💪 <span data-i18n="league.league_championship">League Championship</span>
{% else %}
<span data-i18n="league.league_championship">League Championship</span>
{% endif %}
</div>
<div class="league-subtitle" data-i18n="league.final_rankings_best_4_of_5">Final Rankings - Best 4 of 5 Tournaments</div>
<div class="league-meta">
<div class="meta-item">
<span class="meta-number" id="participantCount">0</span>
<span class="meta-label" data-i18n="players.players">Igralci</span>
</div>
<div class="meta-item">
<span class="meta-number" id="tournamentCount">5</span>
<span class="meta-label" data-i18n="tournament.tournaments">Turnirji</span>
</div>
<div class="meta-item">
<span class="meta-number" id="targetCount">0</span>
<span class="meta-label" data-i18n="tournament.targets">Targets</span>
</div>
<div class="meta-item">
<span class="meta-number" id="topScore">0</span>
<span class="meta-label" data-i18n="results.highest_score">Highest Score</span>
</div>
<div class="meta-item">
<span class="meta-number" id="mostTens">0</span>
<span class="meta-label" data-i18n="results.most_tens">Most 10s</span>
</div>
</div>
</div>
<!-- Champion Section -->
<div class="champion-section" id="championSection">
<div class="champion-title" data-i18n="league.league_champions">🎖️ League Champions</div>
<div class="champion-container" id="championContainer">
<!-- Champion cards will be generated by JavaScript -->
</div>
</div>
</div>
<!-- Right Column -->
<div class="right-column">
<div class="table-header">
<h3 class="table-title" data-i18n="league.5_tournament_league">📊 5 Turnirska Liga - Štejejo Najboljši 4</h3>
<!-- Print-only title -->
<div class="print-title" style="display: none;">
🎖️ <span data-i18n="league.league_results">League Results</span>
</div>
</div>
<div class="table-container">
<table class="league-table">
<thead>
<tr>
<th rowspan="2" class="rank-cell" data-i18n="results.position">Rank</th>
<th rowspan="2" class="player-col" data-i18n="league.participant">Udeleženec</th>
<th colspan="5" class="tournament-header-group" data-i18n="league.tournament_scores">Tournament Scores</th>
<th rowspan="2" class="final-col"><span data-i18n="league.final">Final</span><br><small>(<span data-i18n="league.best_4">Best 4</span>)</small></th>
<th rowspan="2" class="tens-col"><span data-i18n="league.total_10s">10k</span></th>
</tr>
<tr class="tournament-header-group">
<th class="tournament-col">T1</th>
<th class="tournament-col">T2</th>
<th class="tournament-col">T3</th>
<th class="tournament-col">T4</th>
<th class="tournament-col">T5</th>
</tr>
</thead>
<tbody id="leagueTableBody">
<!-- Table rows will be generated by JavaScript -->
</tbody>
</table>
</div>
<div class="calculation-legend">
<div class="legend-title" data-i18n="league.scoring_legend">📖 Legenda Točkovanja:</div>
<div class="legend-items">
<div class="legend-item">
<span class="legend-box legend-counted"></span>
<span data-i18n="league.counted_score">Counted Score</span>
</div>
<div class="legend-item">
<span class="legend-box legend-excluded"></span>
<span data-i18n="league.excluded_worst">Excluded (Worst)</span>
</div>
<div class="legend-item">
<span class="legend-box legend-joker"></span>
<span data-i18n="league.joker_used">{{ translations.league.joker_used if translations else 'Joker Used' }}</span>
</div>
<div class="legend-item">
<span class="legend-box legend-final"></span>
<span data-i18n="league.final_score">Final Score</span>
</div>
<div class="legend-item">
<span class="legend-box legend-tens"></span>
<span data-i18n="league.total_10s_tiebreaker">Total 10s (Tiebreaker)</span>
</div>
</div>
</div>
<div class="stats-footer">
<div class="stat-item">
<div class="stat-value" id="footerParticipants">0</div>
<div data-i18n="players.players">Igralci</div>
</div>
<div class="stat-item">
<div class="stat-value" id="footerTournaments">5</div>
<div data-i18n="tournament.tournaments">Turnirji</div>
</div>
<div class="stat-item">
<div class="stat-value" id="footerHighest">0</div>
<div data-i18n="results.highest_score">Highest Score</div>
</div>
<div class="stat-item">
<div class="stat-value" id="footerAverage">0</div>
<div data-i18n="league.average_final">Povprečni Končni</div>
</div>
<div class="stat-item">
<div class="stat-value" id="footerMostTens">0</div>
<div data-i18n="results.most_tens">Most Tens</div>
</div>
<div class="stat-item">
<div class="stat-value" id="footerDate">Today</div>
<div data-i18n="general.date">Datum</div>
</div>
</div>
</div>
</div>
<script>
// Get data from template
const participants = {{ participants|tojson }};
const league = {{ league|tojson }};
console.log('League participants:', participants);
console.log('League data:', league);
// Calculate 10s from targets in tournament results
function calculateTensFromTargets(targets) {
let tensCount = 0;
if (!targets || typeof targets !== 'object') {
return 0;
}
for (let targetNum in targets) {
const target = targets[targetNum];
if (target && typeof target === 'object') {
for (let shotKey in target) {
if (shotKey.startsWith('shot') && target[shotKey] === 10) {
tensCount++;
}
}
}
}
return tensCount;
}
// Calculate total 10s across all tournaments for a participant
function calculateTotalTensForParticipant(participant) {
let totalTens = 0;
// Method 1: Check if backend already calculated total_tens
if (participant.total_tens !== undefined && participant.total_tens !== null) {
return participant.total_tens;
}
// Method 2: Try to sum up tens_count from tournament_results
if (participant.tournament_results && Array.isArray(participant.tournament_results)) {
participant.tournament_results.forEach(result => {
if (result.participated && result.tens_count !== undefined) {
totalTens += result.tens_count;
}
});
if (totalTens > 0) {
return totalTens;
}
}
// Method 3: Try to calculate from targets data (usually not available)
if (participant.tournament_results && Array.isArray(participant.tournament_results)) {
participant.tournament_results.forEach(result => {
if (result.targets && result.participated) {
totalTens += calculateTensFromTargets(result.targets);
}
});
}
return totalTens;
}
// Process participant data and add calculated fields
function processParticipants() {
if (!participants || !Array.isArray(participants)) {
console.error('Invalid participants data:', participants);
return [];
}
return participants.map(participant => {
const totalTens = calculateTotalTensForParticipant(participant);
return {
...participant,
total_tens: totalTens
};
});
}
// Calculate best 4 scoring logic for 5 tournaments
function calculateBest4Logic(participant) {
if (!participant.tournament_results || !Array.isArray(participant.tournament_results)) {
return { allScores: [], best4: [], excluded: [], excludedIndices: [] };
}
const participatedResults = participant.tournament_results.filter(r => r.participated);
const allScores = participatedResults.map(r => r.score);
if (allScores.length <= 4) {
return { allScores, best4: allScores, excluded: [], excludedIndices: [] };
}
// Create array of score objects with original indices
const scoresWithIndices = allScores.map((score, index) => ({ score, originalIndex: index }));
// Sort by score (highest first)
const sortedScoresWithIndices = [...scoresWithIndices].sort((a, b) => b.score - a.score);
// Take best 4 and excluded (worst)
const best4WithIndices = sortedScoresWithIndices.slice(0, 4);
const excludedWithIndices = sortedScoresWithIndices.slice(4);
const best4 = best4WithIndices.map(item => item.score);
const excluded = excludedWithIndices.map(item => item.score);
const excludedIndices = excludedWithIndices.map(item => item.originalIndex);
return { allScores, best4, excluded, excludedIndices };
}
// Generate champion HTML
function generateChampionHTML(processedParticipants) {
const topThree = processedParticipants.slice(0, 3);
return topThree.map(participant => {
const suffix = participant.rank === 1 ? 'st' :
participant.rank === 2 ? 'nd' :
participant.rank === 3 ? 'rd' : 'th';
const medal = participant.rank === 1 ? '🥇' :
participant.rank === 2 ? '🥈' :
participant.rank === 3 ? '🥉' : '';
return `
<div class="champion-card rank-${participant.rank}">
<div class="rank-display">
<div class="rank-number">${participant.rank}</div>
<div class="rank-suffix">${suffix}</div>
<div class="medal">${medal}</div>
</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="tens-count">🎯 ${participant.total_tens} × 10</div>
</div>
</div>
`;
}).join('');
}
// Generate table HTML for 5 tournaments only
function generateTableHTML(processedParticipants) {
return processedParticipants.map(participant => {
const rankClass = participant.rank <= 3 ? `rank-${participant.rank}` : 'rank-other';
const medal = participant.rank === 1 ? '🥇' :
participant.rank === 2 ? '🥈' :
participant.rank === 3 ? '🥉' : '';
const best4Logic = calculateBest4Logic(participant);
// Generate ONLY 5 tournament score cells (T1-T5)
let tournamentCells = '';
let participatedTournamentIndex = 0; // Track index in participated tournaments only
for (let tournamentNum = 1; tournamentNum <= 5; tournamentNum++) {
const result = participant.tournament_results?.find(r => r.tournament === tournamentNum);
if (!result) {
tournamentCells += '<td>-</td>';
} else if (!result.participated || result.joker) {
tournamentCells += '<td><span class="tournament-score joker">🃏</span></td>';
} else {
const score = result.score;
// Check if this specific tournament index should be excluded
const isExcluded = best4Logic.excludedIndices.includes(participatedTournamentIndex) && best4Logic.allScores.length > 4;
const scoreClass = isExcluded ? 'excluded' : 'counted';
tournamentCells += `<td><span class="tournament-score ${scoreClass}">${score}</span></td>`;
participatedTournamentIndex++; // Only increment for participated tournaments
}
}
return `
<tr>
<td class="rank-cell ${rankClass}">
${participant.rank} ${medal}
</td>
<td class="player-cell">
<div class="player-name">${participant.name}</div>
</td>
${tournamentCells}
<td class="final-score-cell">${participant.final_score}</td>
<td class="tens-count-cell">🎯 ${participant.total_tens}</td>
</tr>
`;
}).join('');
}
// Update statistics
function updateStatistics(processedParticipants) {
const count = processedParticipants.length;
const totalTournaments = 5; // Fixed to 5 tournaments
const tournamentType = league ? league.tournament_type : '20_targets';
const targetCount = tournamentType === '40_targets' ? 40 :
tournamentType === '4_targets' ? 4 : 20;
const topScore = count > 0 ? processedParticipants[0].final_score : 0;
const mostTens = count > 0 ? Math.max(...processedParticipants.map(p => p.total_tens)) : 0;
const averageFinal = count > 0 ? Math.round(processedParticipants.reduce((sum, p) => sum + p.final_score, 0) / count) : 0;
// Format date from YYYY-MM-DD to DD-MM-YY
function formatFooterDate(dateString) {
if (!dateString) return '';
const parts = dateString.split('-');
if (parts.length !== 3) return dateString;
const year = parts[0].slice(-2);
const month = parts[1];
const day = parts[2];
return `${day}-${month}-${year}`;
}
const currentDate = league && league.finished_at ?
formatFooterDate(league.finished_at.substring(0, 10)) :
formatFooterDate(new Date().toISOString().substring(0, 10));
// Update header stats
document.getElementById('participantCount').textContent = count;
document.getElementById('targetCount').textContent = targetCount;
document.getElementById('topScore').textContent = topScore;
document.getElementById('mostTens').textContent = mostTens;
// Update footer stats
document.getElementById('footerParticipants').textContent = count;
document.getElementById('footerHighest').textContent = topScore;
document.getElementById('footerAverage').textContent = averageFinal;
document.getElementById('footerMostTens').textContent = mostTens;
document.getElementById('footerDate').textContent = currentDate;
}
// Initialize the page
function initializePage() {
try {
const processedParticipants = processParticipants();
if (processedParticipants.length === 0) {
console.error('No participants to display');
document.getElementById('championSection').style.display = 'none';
return;
}
// Generate champion cards (top 3 only)
if (processedParticipants.length >= 3) {
document.getElementById('championContainer').innerHTML = generateChampionHTML(processedParticipants);
} else {
document.getElementById('championSection').style.display = 'none';
}
// Generate table
document.getElementById('leagueTableBody').innerHTML = generateTableHTML(processedParticipants);
// Update statistics
updateStatistics(processedParticipants);
console.log('League results initialized successfully');
} catch (error) {
console.error('Error initializing league results:', error);
}
}
// Initialize when page loads
document.addEventListener('DOMContentLoaded', initializePage);
// Export league data as JSON
function exportLeagueJSON() {
const leagueData = {{ league | tojson | safe }};
// Wrap in archive format for compatibility
const archiveData = {
league: leagueData,
archived_at: new Date().toISOString()
};
const dataStr = JSON.stringify(archiveData, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
// Generate filename with league info
const leagueId = leagueData.league_id || 'league';
const date = new Date().toISOString().slice(0, 10);
link.download = `${leagueId}_${date}.json`;
link.click();
URL.revokeObjectURL(url);
}
// Keyboard shortcuts
document.addEventListener('keydown', function(event) {
if (event.key === 'r' || event.key === 'R') {
event.preventDefault();
window.location.reload();
} else if (event.key === 'p' || event.key === 'P') {
event.preventDefault();
window.print();
}
});
</script>
</body>
</html>