Files
Sdk_TV_app/templates/tournament.html
T
2026-06-13 10:40:56 +02:00

1198 lines
46 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title data-i18n="tournament.tournament_management">Tournament Management</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">
<link rel="stylesheet" href="/static/css/pages/tournament.css">
<script src="/static/js/i18n.js"></script>
</head>
<body>
<div class="navbar">
<div class="navbar-title">🏆 <span data-i18n="tournament.tournament_management">Tournament Management</span></div>
<div class="navbar-controls">
<a href="/tournament" class="nav-btn active">🏆 <span data-i18n="navigation.tournament">Tournament</span></a>
{% if tournament_state %}
<a href="/tournament/draft" class="nav-btn">📋 Razpored</a>
<a href="/results/calculator" class="nav-btn">🎯 <span data-i18n="navigation.calculator">Results Calculator</span></a>
{% endif %}
<a href="/" class="nav-btn">✕</a>
</div>
</div>
<div class="container">
<!-- League/Tournament Management Section -->
<div class="section">
{% if league_state and not league_state.league_finished %}
<h2 class="section-title" data-i18n="league.league_management">🏆 Upravljanje Lige</h2>
<!-- League Info Card -->
<div class="league-tournament-container {% if not tournament_state and league_state.current_tournament >= league_state.total_tournaments %}single-card no-stretch{% endif %}">
<!-- League Card -->
<div class="league-tournament-card league">
<h3>🏆 <span data-i18n="league.league_active">Liga (Aktivna)</span></h3>
<div class="unified-status league">
<span data-i18n="league.league_active">Liga je Aktivna</span>
</div>
<div class="compact-info-grid">
<div class="compact-info-item">
<div class="compact-info-label" data-i18n="tournament.tournament_type">Tip</div>
<div class="compact-info-value">
{% if league_state.tournament_type == '40_targets' %}
💪 40
{% elif league_state.tournament_type == '4_targets' %}
🎯 4
{% else %}
⚡ 20
{% endif %}
</div>
</div>
<div class="compact-info-item">
<div class="compact-info-label" data-i18n="tournament.participants">Igralci</div>
<div class="compact-info-value">{{ league_state.participants|length }}</div>
</div>
<div class="compact-info-item">
<div class="compact-info-label" data-i18n="tournament.created">Začeto</div>
<div class="compact-info-value">{{ league_state.created_at[:10] }}</div>
</div>
</div>
<!-- Tournament Progress Timeline -->
<div class="rounds-progress-bar">
<div class="rounds-progress-title">
📊 <span data-i18n="league.tournaments">Turnirji</span>
</div>
<div class="rounds-progress-horizontal">
{% for i in range(1, league_state.total_tournaments + 1) %}
{% set is_completed = i <= league_state.completed_tournaments|length %}
{% set is_active = (i == league_state.current_tournament and tournament_state) or (i == league_state.current_tournament and not tournament_state and not is_completed) %}
<div class="round-progress-dot {% if is_completed %}completed{% elif is_active %}active{% else %}pending{% endif %}" title="Tournament {{ i }}">
{{ i }}
</div>
{% endfor %}
</div>
</div>
{% if not tournament_state and league_state.current_tournament < league_state.total_tournaments %}
<div class="action-buttons" style="margin-top: auto; padding-top: 16px;">
{% if league_state.completed_tournaments|length > 0 %}
<a href="/results" class="action-btn info">
📊 <span data-i18n="tournament.view_current_results">View Current Standings</span>
</a>
{% endif %}
<button class="action-btn success" onclick="startNextTournament()">
🚀 <span data-i18n="league.start_tournament_number">Začni Turnir</span> {{ league_state.current_tournament + 1 }}
</button>
<button class="action-btn danger" onclick="stopAndDeleteLeague()">
🛑 <span data-i18n="league.stop_league">Ustavi Ligo</span>
</button>
</div>
{% endif %}
</div>
<!-- Joker Card (shown when no tournament is active) -->
{% if not tournament_state and league_state.current_tournament < league_state.total_tournaments %}
<div class="league-tournament-card joker" id="jokerSection">
<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: 12px; color: #856404; font-size: 0.85rem;" 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-players-card">
<div class="joker-list">
{% for player_id, participant in league_state.participants.items() %}
<div class="joker-player {% if participant.joker_used %}used{% endif %}">
<span>{{ participant.name }}</span>
<input type="checkbox"
class="joker-checkbox"
id="joker_{{ player_id }}"
{% if participant.joker_used %}disabled checked{% endif %}
data-player-id="{{ player_id }}">
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
<!-- Tournament Card (only shown when tournament is active) -->
{% if tournament_state %}
<div class="league-tournament-card tournament">
<h3>🎯 <span data-i18n="tournament.active_tournament">Trenutni Turnir</span></h3>
<div class="unified-status tournament">
<span data-i18n="tournament.active_tournament">Turnir je Aktiven</span>
</div>
<div class="compact-info-grid">
<div class="compact-info-item">
<div class="compact-info-label" data-i18n="players.total_players">Igralci</div>
<div class="compact-info-value">{{ tournament_state.total_players }}</div>
</div>
<div class="compact-info-item">
<div class="compact-info-label" data-i18n="tournament.total_rounds">Skupaj Krogov</div>
<div class="compact-info-value">{{ tournament_state.total_rounds }}</div>
</div>
<div class="compact-info-item">
<div class="compact-info-label" data-i18n="tournament.current_round">Trenutni Krog</div>
<div class="compact-info-value">{{ tournament_state.current_round }}</div>
</div>
</div>
<!-- Round Progress Timeline -->
<div class="rounds-progress-bar">
<div class="rounds-progress-title">
📊 <span data-i18n="tournament.round">Krogovi</span> - {{ tournament_state.current_round }}/{{ tournament_state.total_rounds }}
</div>
<div class="rounds-progress-horizontal">
{% for i in range(1, tournament_state.total_rounds + 1) %}
{% set is_completed = i < tournament_state.current_round %}
{% set is_active = i == tournament_state.current_round %}
<div class="round-progress-dot {% if is_completed %}completed{% elif is_active %}active{% else %}pending{% endif %}" title="Round {{ i }}">
{{ i }}
</div>
{% endfor %}
</div>
</div>
<div class="action-buttons" style="margin-top: auto; padding-top: 12px;">
<button class="action-btn danger" onclick="cancelLeagueTournament()">🗑️ Ponastavi tekmo</button>
</div>
</div>
{% endif %}
</div>
{% if not tournament_state and league_state.current_tournament >= league_state.total_tournaments %}
<div class="warning">
<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 %}
{% elif league_state and league_state.league_finished %}
<h2 class="section-title">🏁 League Completed</h2>
<div class="league-status">
<div class="league-active">🏆 League Completed!</div>
<div class="league-info">
<div class="info-item">
<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
{% elif league_state.tournament_type == '4_targets' %}
4 Targets
{% else %}
20 Targets
{% endif %}
</div>
</div>
<div class="info-item">
<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" data-i18n="tournament.tournaments">Turnirji</div>
<div class="info-value">{{ league_state.total_tournaments }}</div>
</div>
<div class="info-item">
<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">🏆 <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" data-i18n="tournament.tournament_management">🎯 Upravljanje Turnirja</h2>
<div class="league-tournament-container single-card">
<!-- Tournament Card -->
<div class="league-tournament-card tournament">
<h3>🎯 <span data-i18n="tournament.active_tournament">Trenutni Turnir</span></h3>
<div class="unified-status tournament">
<span data-i18n="tournament.active_tournament">Turnir je Aktiven</span>
</div>
<div class="compact-info-grid">
<div class="compact-info-item">
<div class="compact-info-label" data-i18n="tournament.tournament_type">Tip</div>
<div class="compact-info-value">
{% if tournament_state.tournament_type == '40_targets' %}
💪 40
{% elif tournament_state.tournament_type == '4_targets' %}
🎯 4
{% else %}
⚡ 20
{% endif %}
</div>
</div>
<div class="compact-info-item">
<div class="compact-info-label" data-i18n="players.total_players">Igralci</div>
<div class="compact-info-value">{{ tournament_state.total_players }}</div>
</div>
<div class="compact-info-item">
<div class="compact-info-label" data-i18n="tournament.total_rounds">Skupaj Krogov</div>
<div class="compact-info-value">{{ tournament_state.total_rounds }}</div>
</div>
<div class="compact-info-item">
<div class="compact-info-label" data-i18n="tournament.current_round">Trenutni Krog</div>
<div class="compact-info-value">{{ tournament_state.current_round }}</div>
</div>
</div>
<!-- Round Progress Timeline -->
<div class="rounds-progress-bar">
<div class="rounds-progress-title">
📊 <span data-i18n="tournament.round">Kroogi</span> - {{ tournament_state.current_round }}/{{ tournament_state.total_rounds }}
</div>
<div class="rounds-progress-horizontal">
{% for i in range(1, tournament_state.total_rounds + 1) %}
{% set is_completed = i < tournament_state.current_round %}
{% set is_active = i == tournament_state.current_round %}
<div class="round-progress-dot {% if is_completed %}completed{% elif is_active %}active{% else %}pending{% endif %}" title="Round {{ i }}">
{{ i }}
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="action-buttons" style="margin-top: 20px;">
<button class="action-btn danger" onclick="resetTournament()">🗑️ <span data-i18n="tournament.reset_tournament">Reset Tournament</span></button>
</div>
{% else %}
<!-- Two-column layout for default state -->
<div class="default-layout">
<!-- LEFT: Tournament Setup -->
<div class="default-left">
<h2 class="section-title" data-i18n="league.setup">🏁 Nastavitev</h2>
<!-- Tournament Type Selection -->
<div class="tournament-type-selection">
<div class="type-options type-options-vertical">
<div class="type-option" data-type="4_targets" onclick="selectTournamentType('4_targets')">
<input type="radio" name="tournament_type" value="4_targets">
<div class="type-option-text">
<div class="type-name">🎯 4 Targets</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>
<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-option-text">
<div class="type-name">⚡ 20 Targets</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>
<div class="type-option" data-type="40_targets" onclick="selectTournamentType('40_targets')">
<input type="radio" name="tournament_type" value="40_targets">
<div class="type-option-text">
<div class="type-name">💪 40 Targets</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 class="type-option" data-type="nabijanje" onclick="selectTournamentType('nabijanje')">
<input type="radio" name="tournament_type" value="nabijanje">
<div class="type-option-text">
<div class="type-name">💥 Nabijanje</div>
<div class="type-description">Prosti format z nastavljivo konfiguracijo tarč in strelov</div>
</div>
</div>
</div>
</div>
<div class="player-count">
<span class="enabled-count" id="enabledCount">0</span> players enabled
</div>
<div class="action-buttons">
<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;">
<strong>Note:</strong> You need at least 1 enabled player to start.
</div>
</div>
<!-- RIGHT: Player Management -->
<div class="default-right">
<h2 class="section-title" data-i18n="players.player_management">👥 Upravljanje Igralcev</h2>
<!-- Add Player -->
<div class="add-player-row">
<input type="text"
id="newPlayerName"
data-i18n="[placeholder]league.enter_player_name"
placeholder="Vnesite ime igralca..."
maxlength="50"
onkeypress="handleAddPlayerKeypress(event)">
<button class="add-btn" id="addPlayerBtn" onclick="addPlayer()">
<span data-i18n="players.add_player">Dodaj</span>
</button>
</div>
<!-- Toolbar: search + filters -->
<div class="player-toolbar">
<div class="search-container">
<input type="text"
class="search-input"
id="playerSearch"
data-i18n="[placeholder]league.search_players_placeholder"
placeholder="Išči..."
oninput="filterPlayers()">
<span class="search-icon">🔍</span>
</div>
<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">✓</button>
<button class="filter-btn" data-filter="disabled" onclick="setFilter('disabled')" data-i18n="players.disabled">✗</button>
</div>
<!-- Bulk actions + stats -->
<div class="bulk-strip">
<input type="checkbox" id="selectAllCheckbox" onchange="toggleSelectAll()" title="Select all">
<button class="bulk-btn" onclick="enableSelected()" data-i18n="general.enable_selected">Omogoči</button>
<button class="bulk-btn danger" onclick="disableSelected()" data-i18n="general.disable_selected">Onemogoči</button>
<div class="stats-bar" style="margin:0; flex:1; justify-content:flex-end;">
<span><strong class="stat-enabled" id="enabledPlayersCount">0</strong> ✓</span>
<span><strong class="stat-disabled" id="disabledPlayersCount">0</strong> ✗</span>
<span><strong id="totalPlayersCount">0</strong> skupaj</span>
</div>
</div>
<!-- Player pill list -->
<div class="player-list" id="playerList">
<!-- Pills rendered by JS -->
</div>
<div class="no-results" id="noResults" style="display: none;">
<span data-i18n="league.no_players_found">Ni najdenih igralcev.</span>
</div>
</div><!-- end default-right -->
</div><!-- end default-layout -->
{% endif %}
</div><!-- end section -->
</div>
<!-- Confirmation Modal -->
<div class="modal-overlay" id="confirmationModal">
<div class="confirmation-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-buttons">
<button class="modal-btn cancel" onclick="closeModal()">Cancel</button>
<button class="modal-btn confirm" id="confirmBtn" onclick="confirmAction()">Confirm</button>
</div>
</div>
</div>
<script>
let players = {{ players|tojson }};
const leagueActive = {{ 'true' if league_state and not league_state.league_finished else 'false' }};
const tournamentActive = {{ 'true' if tournament_state else 'false' }};
let pendingAction = null;
let editingPlayer = null;
let selectedTournamentType = '20_targets';
let currentFilter = 'all';
let searchTerm = '';
let selectedPlayers = new Set();
// Tournament type selection
function selectTournamentType(type) {
selectedTournamentType = type;
document.querySelectorAll('.type-option').forEach(option => {
option.classList.remove('selected');
});
event.currentTarget.classList.add('selected');
const radio = event.currentTarget.querySelector('input[type="radio"]');
if (radio) radio.checked = true;
const isNabijanje = type === 'nabijanje';
document.getElementById('startLeagueBtn').style.display = isNabijanje ? 'none' : '';
document.getElementById('startSingleBtn').textContent = isNabijanje
? '💥 Odpri Nabijanje'
: '🏅 ' + (window._i18n?.['league.start_single_tournament'] || 'Začni Posamezen Turnir');
}
// Filter and search functions
function setFilter(filter) {
currentFilter = filter;
// Update filter buttons
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.classList.remove('active');
});
document.querySelector(`[data-filter="${filter}"]`).classList.add('active');
renderPlayerTable();
}
function filterPlayers() {
searchTerm = document.getElementById('playerSearch').value.toLowerCase();
renderPlayerTable();
}
function getFilteredPlayers() {
let filtered = players.filter(player => {
// Search filter
if (searchTerm && !player.name.toLowerCase().includes(searchTerm)) {
return false;
}
// Status filter
if (currentFilter === 'enabled' && !player.enabled) {
return false;
}
if (currentFilter === 'disabled' && player.enabled) {
return false;
}
return true;
});
// Sort by enabled status first, then by name
filtered.sort((a, b) => {
if (a.enabled && !b.enabled) return -1;
if (!a.enabled && b.enabled) return 1;
return a.name.localeCompare(b.name);
});
return filtered;
}
// Selection functions
function togglePlayerSelection(playerId, checked) {
if (checked) {
selectedPlayers.add(playerId);
} else {
selectedPlayers.delete(playerId);
}
updateSelectAllCheckbox();
}
function toggleSelectAll() {
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
const visiblePlayers = getFilteredPlayers();
if (selectAllCheckbox.checked) {
visiblePlayers.forEach(player => selectedPlayers.add(player.id));
} else {
visiblePlayers.forEach(player => selectedPlayers.delete(player.id));
}
renderPlayerTable();
}
function selectAllVisible() {
const visiblePlayers = getFilteredPlayers();
visiblePlayers.forEach(player => selectedPlayers.add(player.id));
renderPlayerTable();
}
function updateSelectAllCheckbox() {
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
const visiblePlayers = getFilteredPlayers();
const selectedVisible = visiblePlayers.filter(p => selectedPlayers.has(p.id));
if (selectedVisible.length === 0) {
selectAllCheckbox.checked = false;
selectAllCheckbox.indeterminate = false;
} else if (selectedVisible.length === visiblePlayers.length) {
selectAllCheckbox.checked = true;
selectAllCheckbox.indeterminate = false;
} else {
selectAllCheckbox.checked = false;
selectAllCheckbox.indeterminate = true;
}
}
// Bulk actions
function enableSelected() {
if (selectedPlayers.size === 0) {
alert(t('league.no_players_selected'));
return;
}
if (!confirm(`Enable ${selectedPlayers.size} selected players?`)) {
return;
}
bulkUpdatePlayers(Array.from(selectedPlayers), true);
}
function disableSelected() {
if (selectedPlayers.size === 0) {
alert(t('league.no_players_selected'));
return;
}
if (!confirm(`Disable ${selectedPlayers.size} selected players?`)) {
return;
}
bulkUpdatePlayers(Array.from(selectedPlayers), false);
}
async function bulkUpdatePlayers(playerIds, enabled) {
try {
for (const playerId of playerIds) {
const response = await fetch(`/api/players/${playerId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ enabled: enabled })
});
if (response.ok) {
const player = players.find(p => p.id === playerId);
if (player) {
player.enabled = enabled;
}
}
}
selectedPlayers.clear();
renderPlayerTable();
updateStats();
} catch (error) {
console.error('Error bulk updating players:', error);
alert('Error updating players. Please try again.');
}
}
// Update stats display
function updateStats() {
const totalCount = players.length;
const enabledCount = players.filter(p => p.enabled).length;
const disabledCount = totalCount - enabledCount;
const visibleCount = getFilteredPlayers().length;
const setEl = (id, val) => { const el = document.getElementById(id); if (el) el.textContent = val; };
setEl('totalPlayersCount', totalCount);
setEl('enabledPlayersCount', enabledCount);
setEl('disabledPlayersCount', disabledCount);
setEl('visiblePlayersCount', visibleCount);
setEl('enabledCount', enabledCount);
// Update button states
const startLeagueBtn = document.getElementById('startLeagueBtn');
const startSingleBtn = document.getElementById('startSingleBtn');
const warningMsg = document.getElementById('warningMessage');
if (startLeagueBtn && startSingleBtn && warningMsg) {
if (enabledCount < 1) {
startLeagueBtn.disabled = true;
startSingleBtn.disabled = true;
warningMsg.style.display = 'block';
} else {
startLeagueBtn.disabled = false;
startSingleBtn.disabled = false;
warningMsg.style.display = 'none';
}
}
}
// Render the player list as pills
function renderPlayerTable() {
const list = document.getElementById('playerList');
const noResults = document.getElementById('noResults');
// Check if playerList exists before proceeding (it may not exist on all pages)
if (!list) {
return;
}
const filteredPlayers = getFilteredPlayers();
list.innerHTML = '';
if (filteredPlayers.length === 0) {
if (noResults) noResults.style.display = 'block';
updateSelectAllCheckbox();
updateStats();
return;
}
if (noResults) noResults.style.display = 'none';
filteredPlayers.forEach(player => {
const row = document.createElement('div');
row.className = `player-row${player.enabled ? '' : ' disabled-player'}${selectedPlayers.has(player.id) ? ' selected' : ''}`;
row.setAttribute('data-player-id', player.id);
if (editingPlayer === player.id) {
row.innerHTML = `
<span class="pr-dot"></span>
<span class="pr-name" id="playerName${player.id}">
<input type="text" value="${player.name}" maxlength="50"
onblur="savePlayerName(${player.id})"
onkeydown="handleEditKeypress(event, ${player.id})"
autofocus>
</span>
<div class="pr-actions" style="opacity:1;">
<button class="pr-btn" onclick="editPlayer(${player.id})" title="Shrani">💾</button>
</div>
`;
} else {
row.innerHTML = `
<input type="checkbox" class="pr-check"
${selectedPlayers.has(player.id) ? 'checked' : ''}
onclick="event.stopPropagation()"
onchange="togglePlayerSelection(${player.id}, this.checked)">
<span class="pr-dot" onclick="event.stopPropagation(); togglePlayer(${player.id})" title="${player.enabled ? 'Onemogoči' : 'Omogoči'}"></span>
<span class="pr-name" id="playerName${player.id}">${player.name}</span>
<div class="pr-actions">
<button class="pr-btn" onclick="event.stopPropagation(); editPlayer(${player.id})" title="Uredi">✏️</button>
<button class="pr-btn del" onclick="event.stopPropagation(); confirmDeletePlayer(${player.id})" title="Izbriši">🗑</button>
</div>
`;
// Row click toggles selection
row.addEventListener('click', () => {
const cb = row.querySelector('.pr-check');
cb.checked = !cb.checked;
togglePlayerSelection(player.id, cb.checked);
});
}
list.appendChild(row);
});
updateSelectAllCheckbox();
updateStats();
}
// Add new player
async function addPlayer() {
const nameInput = document.getElementById('newPlayerName');
const name = nameInput.value.trim();
if (!name) {
alert('Please enter a player name');
return;
}
const addBtn = document.getElementById('addPlayerBtn');
addBtn.disabled = true;
addBtn.textContent = 'Adding...';
try {
const response = await fetch('/api/players/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: name })
});
if (response.ok) {
const result = await response.json();
players.push(result.player);
nameInput.value = '';
renderPlayerTable();
} else {
const error = await response.json();
alert('Failed to add player: ' + error.message);
}
} catch (error) {
console.error('Error adding player:', error);
alert('Error adding player. Please try again.');
} finally {
addBtn.disabled = false;
addBtn.innerHTML = '<span data-i18n="players.add_player">Dodaj Igralca</span>';
translatePage();
}
}
// Handle Enter key in add player input
function handleAddPlayerKeypress(event) {
if (event.key === 'Enter') {
event.preventDefault();
addPlayer();
}
}
// Edit player name
function editPlayer(playerId) {
const player = players.find(p => p.id === playerId);
if (!player) return;
if (editingPlayer === playerId) {
// Save changes
const nameElement = document.getElementById(`playerName${playerId}`);
const input = nameElement ? nameElement.querySelector('input') : null;
if (input) {
const newName = input.value.trim();
if (newName && newName !== player.name) {
updatePlayerName(playerId, newName);
} else {
editingPlayer = null;
renderPlayerTable();
}
}
} else {
// Start editing — re-render the whole list so the pill shows input
editingPlayer = playerId;
renderPlayerTable();
// Focus the input after render
setTimeout(() => {
const nameElement = document.getElementById(`playerName${playerId}`);
const input = nameElement ? nameElement.querySelector('input') : null;
if (input) input.focus();
}, 20);
}
}
function savePlayerName(playerId) {
editPlayer(playerId);
}
function handleEditKeypress(event, playerId) {
if (event.key === 'Enter') {
event.preventDefault();
editPlayer(playerId);
} else if (event.key === 'Escape') {
editingPlayer = null;
renderPlayerTable();
}
}
// Update player name
async function updatePlayerName(playerId, newName) {
try {
const response = await fetch(`/api/players/${playerId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: newName.trim() })
});
if (response.ok) {
// Update local player data
const player = players.find(p => p.id === playerId);
if (player) {
player.name = newName.trim();
}
editingPlayer = null;
renderPlayerTable();
} else {
alert('Failed to update player name');
editingPlayer = null;
renderPlayerTable();
}
} catch (error) {
console.error('Error updating player:', error);
editingPlayer = null;
renderPlayerTable();
}
}
// Update a single player row without re-rendering the entire list
function updatePlayerRow(playerId) {
const player = players.find(p => p.id === playerId);
if (!player) return;
const row = document.querySelector(`.player-row[data-player-id="${playerId}"]`);
if (!row) return;
if (player.enabled) {
row.classList.remove('disabled-player');
} else {
row.classList.add('disabled-player');
}
updateStats();
}
async function togglePlayer(playerId) {
const player = players.find(p => p.id === playerId);
if (!player) return;
const newEnabled = !player.enabled;
try {
const response = await fetch(`/api/players/${playerId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ enabled: newEnabled })
});
if (response.ok) {
player.enabled = newEnabled;
updatePlayerRow(playerId);
} else {
alert('Failed to update player status');
}
} catch (error) {
console.error('Error toggling player:', error);
alert('Error updating player. Please try again.');
}
}
// Confirm delete player
function confirmDeletePlayer(playerId) {
const player = players.find(p => p.id === playerId);
if (!player) return;
showModal(
'Delete Player',
`Are you sure you want to permanently delete "${player.name}"? This action cannot be undone.`,
() => deletePlayer(playerId)
);
}
// Delete player
async function deletePlayer(playerId) {
try {
const response = await fetch(`/api/players/${playerId}/delete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
players = players.filter(p => p.id !== playerId);
selectedPlayers.delete(playerId);
renderPlayerTable();
closeModal();
} else {
alert('Failed to delete player');
}
} catch (error) {
console.error('Error deleting player:', error);
alert('Error deleting player. Please try again.');
}
}
// Start league
async function startLeague() {
const enabledPlayers = players.filter(p => p.enabled);
if (enabledPlayers.length < 1) {
alert('Need at least 1 enabled player to start league');
return;
}
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(t('messages.confirm_start_league', { players: enabledPlayers.length, format: formatText }))) {
return;
}
const startBtn = document.getElementById('startLeagueBtn');
startBtn.disabled = true;
startBtn.textContent = 'Starting League...';
try {
const response = await fetch('/api/league/start', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ tournament_type: selectedTournamentType })
});
if (response.ok) {
const result = await response.json();
alert('League started successfully!');
window.location.reload();
} else {
const error = await response.json();
alert('Failed to start league: ' + error.message);
}
} catch (error) {
console.error('Error starting league:', error);
alert('Error starting league. Please try again.');
} finally {
startBtn.disabled = false;
startBtn.setAttribute('data-i18n', 'league.start_league_5_tournaments');
startBtn.textContent = '🎖️ Začni Ligo (5 Turnirjev)';
translatePage();
}
}
// Start single tournament
async function startSingleTournament() {
if (selectedTournamentType === 'nabijanje') {
window.location.href = '/calculator';
return;
}
const enabledPlayers = players.filter(p => p.enabled);
if (enabledPlayers.length < 1) {
alert('Need at least 1 enabled player to start tournament');
return;
}
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(t('messages.confirm_start_tournament', { players: enabledPlayers.length, format: formatText }))) {
return;
}
const startBtn = document.getElementById('startSingleBtn');
startBtn.disabled = true;
startBtn.textContent = 'Starting...';
try {
const response = await fetch('/api/tournament/start', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ tournament_type: selectedTournamentType })
});
if (response.ok) {
const result = await response.json();
alert('Tournament started successfully!');
window.location.reload();
} else {
const error = await response.json();
alert('Failed to start tournament: ' + error.message);
}
} catch (error) {
console.error('Error starting tournament:', error);
alert('Error starting tournament. Please try again.');
} finally {
startBtn.disabled = false;
startBtn.textContent = '🎯 Start Single Tournament';
}
}
// Start next tournament in league
async function startNextTournament() {
// Get selected joker players
const jokerCheckboxes = document.querySelectorAll('.joker-checkbox:checked:not(:disabled)');
const jokerPlayers = Array.from(jokerCheckboxes).map(cb => parseInt(cb.dataset.playerId));
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;
}
try {
const response = await fetch('/api/league/tournament/start', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ joker_players: jokerPlayers })
});
if (response.ok) {
const result = await response.json();
alert('Tournament started successfully!');
window.location.reload();
} else {
const error = await response.json();
alert('Failed to start tournament: ' + error.message);
}
} catch (error) {
console.error('Error starting tournament:', error);
alert('Error starting tournament. Please try again.');
}
}
// Reset league
async function resetLeague() {
if (!confirm('Are you sure you want to reset the league? This will delete all league and tournament data.')) {
return;
}
try {
const response = await fetch('/api/league/reset', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
alert('League reset successfully!');
window.location.reload();
} else {
alert('Failed to reset league');
}
} catch (error) {
console.error('Error resetting league:', error);
alert('Error resetting league. Please try again.');
}
}
// Stop league (pause current tournament and keep league data)
async function stopLeague() {
if (!confirm('Are you sure you want to stop the league? The current tournament will be stopped but league data will be kept.')) {
return;
}
try {
const response = await fetch('/api/tournament/reset', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
alert('Tournament stopped successfully! League is paused.');
window.location.reload();
} else {
alert('Failed to stop tournament');
}
} catch (error) {
console.error('Error stopping tournament:', error);
alert('Error stopping tournament. Please try again.');
}
}
// Stop and delete league
async function stopAndDeleteLeague() {
if (!confirm('Are you sure you want to stop and delete this league? This action cannot be undone. All league data will be deleted.')) {
return;
}
try {
const response = await fetch('/api/league/reset', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
alert('League has been stopped and deleted successfully!');
window.location.reload();
} else {
const error = await response.json();
alert('Failed to delete league: ' + (error.message || 'Unknown error'));
}
} catch (error) {
console.error('Error deleting league:', error);
alert('Error deleting league. Please try again.');
}
}
// Reset tournament
async function resetTournament() {
if (!confirm('Are you sure you want to reset the tournament? This will delete tournament data.')) {
return;
}
try {
const response = await fetch('/api/tournament/reset', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
alert('Tournament reset successfully!');
window.location.reload();
} else {
alert('Failed to reset tournament');
}
} catch (error) {
console.error('Error resetting tournament:', error);
alert('Error resetting tournament. Please try again.');
}
}
// Cancel tournament within a league (rolls back tournament counter)
async function cancelLeagueTournament() {
if (!confirm('Ponastavi tekmo? Liga ostane na istem turnirju.')) {
return;
}
try {
const response = await fetch('/api/tournament/cancel', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
if (response.ok) {
window.location.reload();
} else {
alert('Napaka pri ponastavitvi tekme.');
}
} catch (error) {
console.error('Error cancelling tournament:', error);
alert('Napaka. Poskusite znova.');
}
}
// Modal functions
function showModal(title, message, confirmCallback) {
document.getElementById('modalTitle').textContent = title;
document.getElementById('modalMessage').textContent = message;
pendingAction = confirmCallback;
document.getElementById('confirmationModal').classList.add('active');
}
function closeModal() {
document.getElementById('confirmationModal').classList.remove('active');
pendingAction = null;
}
function confirmAction() {
if (pendingAction) {
pendingAction();
}
}
// Click outside modal to close
document.getElementById('confirmationModal').addEventListener('click', function(e) {
if (e.target === this) {
closeModal();
}
});
// Initialize page
document.addEventListener('DOMContentLoaded', function() {
// Wait for translations to be ready before rendering the player table
window.addEventListener('i18nReady', function() {
renderPlayerTable();
// Focus on add player input if no active league/tournament
const nameInput = document.getElementById('newPlayerName');
if (nameInput && !leagueActive && !tournamentActive) {
nameInput.focus();
}
console.log('🏆 Tournament Management loaded');
console.log('League active:', leagueActive);
console.log('Tournament active:', tournamentActive);
{% if league_state %}
console.log('📊 League State Debug:');
console.log(' - Completed tournaments:', {{ league_state.completed_tournaments|length }});
console.log(' - Total tournaments:', {{ league_state.total_tournaments }});
console.log(' - Current tournament:', {{ league_state.current_tournament }});
console.log(' - Completed list:', {{ league_state.completed_tournaments|tojson }});
{% endif %}
}, { once: true });
});
</script>
</body>
</html>