Add tournament archives and results for multiple events

- Created JSON files for tournament archives on 2025-07-28 and 2025-07-29, including detailed player scores and shot results.
- Added a new tournament results file summarizing the outcomes of the tournament held on 2025-07-29, including participant scores and completion status.
This commit is contained in:
bl3kunja-FW
2025-08-02 15:27:32 +02:00
parent 75ac46c23c
commit 5c7f255a02
45 changed files with 6840 additions and 1534 deletions
+547
View File
@@ -0,0 +1,547 @@
<!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">
<style>
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
background: #f5f5f5;
font-family: Arial, sans-serif;
height: 100vh;
overflow: hidden;
}
.navbar {
background: white;
color: black;
padding: 15px 25px;
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);
height: 70px;
flex-shrink: 0;
}
.navbar-title {
font-size: 1.8rem;
font-weight: bold;
color: #333;
}
.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: #007bff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
color: #007bff;
}
.nav-btn.primary {
background: #007bff;
border-color: #0056b3;
color: white;
}
.nav-btn.primary:hover {
background: #0056b3;
color: white;
}
.main-container {
height: calc(100vh - 70px);
display: flex;
flex-direction: column;
padding: 15px;
gap: 15px;
}
.tournament-header {
background: white;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 15px 20px;
text-align: center;
flex-shrink: 0;
}
.tournament-title {
font-size: 1.5rem;
font-weight: bold;
color: #333;
margin-bottom: 6px;
}
.tournament-stats {
color: #666;
font-size: 0.9rem;
margin-bottom: 12px;
}
.tournament-controls {
display: flex;
justify-content: center;
align-items: center;
gap: 15px;
}
.round-nav-btn {
background: #f8f9fa;
border: 2px solid #e9ecef;
cursor: pointer;
padding: 8px 14px;
border-radius: 8px;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
color: #333;
font-weight: bold;
font-size: 0.85rem;
}
.round-nav-btn:hover {
background: #e9ecef;
border-color: #007bff;
color: #007bff;
}
.round-nav-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none !important;
}
.current-round-display {
font-size: 1rem;
font-weight: bold;
color: #007bff;
margin: 0 15px;
min-width: 140px;
text-align: center;
}
/* Rounds Container - Vertical stack */
.rounds-container {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
min-height: 0;
overflow-y: auto;
padding: 5px;
}
.round-row {
background: white;
border-radius: 10px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: all 0.2s ease;
display: flex;
flex-shrink: 0;
min-height: 0;
}
.round-row.current {
border-left: 4px solid #007bff;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.25);
}
.round-row.completed {
border-left: 4px solid #28a745;
opacity: 0.95;
}
.round-row.waiting {
opacity: 0.8;
}
.round-header {
background: #f8f9fa;
border-right: 1px solid #e9ecef;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 10px 15px;
min-width: 120px;
flex-shrink: 0;
}
.round-row.current .round-header {
background: #e3f2fd;
}
.round-row.completed .round-header {
background: #e8f5e8;
}
.round-title {
font-size: 1rem;
font-weight: bold;
color: #333;
margin-bottom: 4px;
}
.round-badge {
padding: 3px 8px;
border-radius: 10px;
font-size: 0.65rem;
font-weight: bold;
text-transform: uppercase;
}
.current-badge {
background: #007bff;
color: white;
}
.completed-badge {
background: #28a745;
color: white;
}
.waiting-badge {
background: #6c757d;
color: white;
}
.positions-container {
flex: 1;
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 8px;
padding: 12px;
}
.position-card {
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
border: 2px solid #e9ecef;
border-radius: 8px;
padding: 8px 6px;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
transition: all 0.3s ease;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
min-height: 60px;
}
.position-card.filled {
border-color: #28a745;
background: linear-gradient(135deg, #f8fff9 0%, #ffffff 100%);
box-shadow: 0 3px 10px rgba(40, 167, 69, 0.15);
}
.position-card.empty {
border-color: #dee2e6;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
opacity: 0.8;
}
.position-number {
font-size: 1.2rem;
font-weight: 700;
color: #007bff;
margin-bottom: 4px;
line-height: 1;
}
.position-card.empty .position-number {
color: #6c757d;
}
.player-name {
font-size: 1rem;
font-weight: 600;
color: #2c3e50;
line-height: 1.2;
margin-bottom: 4px;
word-wrap: break-word;
max-width: 100%;
text-align: center;
}
.position-card.empty .player-name {
color: #6c757d;
font-style: italic;
font-weight: 500;
}
.player-id {
background: #28a745;
color: white;
padding: 2px 6px;
border-radius: 8px;
font-size: 0.65rem;
font-weight: 600;
display: inline-block;
letter-spacing: 0.3px;
}
/* No Tournament State */
.no-tournament {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: #666;
}
.no-tournament h2 {
color: #333;
margin-bottom: 15px;
font-size: 1.5rem;
}
.no-tournament p {
margin-bottom: 20px;
font-size: 1.1rem;
}
/* Auto-refresh indicator */
.refresh-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: rgba(0, 123, 255, 0.9);
color: white;
padding: 8px 15px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: bold;
opacity: 0;
transition: opacity 0.3s ease;
z-index: 999;
}
.refresh-indicator.show {
opacity: 1;
}
</style>
</head>
<body>
<div class="navbar">
<div class="navbar-title">🏆 Tournament Draft</div>
<div class="navbar-controls">
<a href="/" class="nav-btn">← Dashboard</a>
<a href="/tournament" class="nav-btn">⚙️ Manage</a>
</div>
</div>
<div class="main-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>
{% if tournament.current_round %}
<div class="tournament-controls">
<button class="round-nav-btn" id="prevRoundBtn" onclick="changeRound(-1)" title="Previous Round">
← Previous
</button>
<span class="current-round-display">Round {{ tournament.current_round }} of {{ tournament.total_rounds }}</span>
<button class="round-nav-btn" id="nextRoundBtn" onclick="changeRound(1)" title="Next Round">
Next →
</button>
</div>
{% endif %}
</div>
<div class="rounds-container" data-total-rounds="{{ tournament.total_rounds }}">
{% 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-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>
{% if is_current %}
<div class="round-badge current-badge">Current</div>
{% elif is_completed %}
<div class="round-badge completed-badge">Done</div>
{% else %}
<div class="round-badge waiting-badge">Wait</div>
{% endif %}
</div>
<div class="positions-container">
{% 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>
{% 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>
</div>
{% endif %}
</div>
<!-- Auto-refresh indicator -->
<div class="refresh-indicator" id="refreshIndicator">
🔄 Updating...
</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 }};
function updateRoundNavigation() {
if (tournamentActive) {
const prevBtn = document.getElementById('prevRoundBtn');
const nextBtn = document.getElementById('nextRoundBtn');
if (prevBtn) prevBtn.disabled = currentRound <= 1;
if (nextBtn) nextBtn.disabled = currentRound >= totalRounds;
}
}
// Change round function
async function changeRound(direction) {
if (!tournamentActive) return;
const newRound = currentRound + direction;
if (newRound < 1 || newRound > totalRounds) return;
// Disable buttons during request
const prevBtn = document.getElementById('prevRoundBtn');
const nextBtn = document.getElementById('nextRoundBtn');
if (prevBtn) prevBtn.disabled = true;
if (nextBtn) nextBtn.disabled = true;
try {
const response = await fetch(`/api/tournament/round/${newRound}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
currentRound = newRound;
updateRoundNavigation();
// Reload page to get new round players
setTimeout(() => {
window.location.reload();
}, 300);
} else {
console.error('Failed to change round');
alert('Failed to change round. Please try again.');
updateRoundNavigation();
}
} catch (error) {
console.error('Error changing round:', error);
alert('Error changing round. Please try again.');
updateRoundNavigation();
}
}
// Auto-refresh functionality
function setupAutoRefresh() {
setInterval(() => {
if (document.visibilityState === 'visible' && tournamentActive) {
const indicator = document.getElementById('refreshIndicator');
indicator.classList.add('show');
setTimeout(() => {
window.location.reload();
}, 1000);
}
}, 15000); // 15 seconds
}
// Keyboard shortcuts
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
window.location.href = '/';
} else if (event.key === 'r' || event.key === 'R') {
event.preventDefault();
window.location.reload();
} else if (tournamentActive) {
if (event.key === 'ArrowLeft') {
event.preventDefault();
changeRound(-1);
} else if (event.key === 'ArrowRight') {
event.preventDefault();
changeRound(1);
}
}
});
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updateRoundNavigation();
setupAutoRefresh();
// Scroll to current round
setTimeout(() => {
const currentRoundRow = document.querySelector('.round-row.current');
if (currentRoundRow) {
currentRoundRow.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
}, 500);
console.log('🖥️ PC Tournament Draft (Vertical Rows) loaded');
console.log('🏆 Tournament active:', tournamentActive);
console.log(`📊 Displaying ${totalRounds} rounds in vertical layout`);
});
</script>
</body>
</html>