236 lines
12 KiB
HTML
236 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Turnierübersicht - Zuschauer</title>
|
|
<link rel="stylesheet" href="/css/style.css">
|
|
</head>
|
|
<body>
|
|
<nav>
|
|
<a href="/admin.html">Admin</a>
|
|
<a href="/referee.html">Schiedsrichter</a>
|
|
<a href="/spectator.html" class="active">Zuschauer</a>
|
|
</nav>
|
|
|
|
<div class="container">
|
|
<h1>Turnierübersicht</h1>
|
|
|
|
<section class="content-section">
|
|
<h2>Turnier auswählen</h2>
|
|
<label for="select-tournament">Aktuelles/Vergangenes Turnier:</label>
|
|
<select id="select-tournament" disabled>
|
|
<option value="">-- Bitte Turnier wählen --</option>
|
|
</select>
|
|
<p id="loading-tournaments-spectator" class="hidden">Lade Turniere...</p>
|
|
<div id="tournament-info" class="hidden">
|
|
<h3 id="selected-tournament-name"></h3>
|
|
<p id="selected-tournament-details"></p>
|
|
</div>
|
|
</section>
|
|
|
|
<div id="tournament-display" class="hidden">
|
|
|
|
<section class="content-section">
|
|
<h2>Live Spiele / Aktuelle Runde</h2>
|
|
<div id="live-matches-list">
|
|
<p><i>Live-Spiele werden hier angezeigt (Implementierung erforderlich).</i></p>
|
|
</div>
|
|
<p id="loading-live-matches" class="hidden">Lade Live-Spiele...</p>
|
|
</section>
|
|
|
|
<section class="content-section">
|
|
<h2>Turnierbaum / Gruppenphase</h2>
|
|
<div id="bracket-groups-visualization">
|
|
<p><i>Visualisierung des Turnierbaums oder der Gruppenphase wird hier angezeigt (Implementierung erforderlich).</i></p>
|
|
</div>
|
|
<p id="loading-bracket" class="hidden">Lade Turnierbaum/Gruppen...</p>
|
|
</section>
|
|
|
|
<section class="content-section">
|
|
<h2>Alle Spiele (Zeitplan)</h2>
|
|
<div id="all-matches-list">
|
|
<p><i>Liste aller vergangenen und zukünftigen Spiele wird hier angezeigt (Implementierung erforderlich).</i></p>
|
|
</div>
|
|
<p id="loading-all-matches" class="hidden">Lade alle Spiele...</p>
|
|
</section>
|
|
|
|
<section class="content-section">
|
|
<h2>Spieler & Statistiken</h2>
|
|
<div>
|
|
<label for="filter-player">Spieler suchen/filtern:</label>
|
|
<input type="text" id="filter-player" placeholder="Spielername eingeben...">
|
|
</div>
|
|
<div id="player-list">
|
|
<p><i>Spielerliste und Profile werden hier angezeigt (Implementierung erforderlich).</i></p>
|
|
</div>
|
|
<p id="loading-players" class="hidden">Lade Spieler...</p>
|
|
</section>
|
|
|
|
<section class="content-section">
|
|
<h2>Hinweise & Benachrichtigungen</h2>
|
|
<div id="notifications-area">
|
|
<p><i>Wichtige Hinweise oder Push-Benachrichtigungen werden hier angezeigt (Implementierung erforderlich).</i></p>
|
|
</div>
|
|
</section>
|
|
|
|
</div> <div id="spectator-error" class="error-message hidden"></div>
|
|
|
|
</div> <script>
|
|
// Basic JS for Spectator View
|
|
|
|
// --- DOM Elements ---
|
|
const selectTournament = document.getElementById('select-tournament');
|
|
const loadingTournamentsSpectator = document.getElementById('loading-tournaments-spectator');
|
|
const tournamentInfo = document.getElementById('tournament-info');
|
|
const selectedTournamentName = document.getElementById('selected-tournament-name');
|
|
const selectedTournamentDetails = document.getElementById('selected-tournament-details');
|
|
const tournamentDisplay = document.getElementById('tournament-display');
|
|
const spectatorError = document.getElementById('spectator-error');
|
|
// Add other display area elements (live matches list, bracket viz, etc.)
|
|
|
|
const API_BASE_URL_SPEC = '/api'; // Use relative path
|
|
|
|
const showMessageSpec = (element, message, isError = true) => {
|
|
element.textContent = message;
|
|
element.className = isError ? 'error-message' : 'success-message';
|
|
element.classList.remove('hidden');
|
|
};
|
|
|
|
const hideMessageSpec = (element) => {
|
|
element.classList.add('hidden');
|
|
element.textContent = '';
|
|
};
|
|
|
|
|
|
// --- Functions ---
|
|
|
|
// Fetch API (Simplified - assumes public endpoints or uses admin token if available for testing)
|
|
// IMPORTANT: Spectator view should ideally hit public, non-authenticated endpoints.
|
|
// For now, it might rely on the admin/referee being logged in in another tab,
|
|
// or backend needs specific public routes. Let's assume public routes exist or reuse token if present.
|
|
const fetchAPISpec = async (endpoint, options = {}) => {
|
|
const headers = { 'Content-Type': 'application/json', ...options.headers };
|
|
// Spectator view ideally shouldn't need a token, but we add it if present for now
|
|
const token = localStorage.getItem('authToken');
|
|
if (token) {
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
}
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL_SPEC}${endpoint}`, { ...options, headers });
|
|
if (!response.ok) {
|
|
let errorData;
|
|
try { errorData = await response.json(); } catch { errorData = { message: response.statusText }; }
|
|
// Don't auto-logout spectator on auth errors
|
|
throw new Error(errorData.message || `HTTP Error: ${response.status}`);
|
|
}
|
|
if (response.status === 204) return null;
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('API Fetch Error (Spectator):', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
|
|
// Load available tournaments into the dropdown
|
|
const loadAvailableTournaments = async () => {
|
|
loadingTournamentsSpectator.classList.remove('hidden');
|
|
selectTournament.disabled = true;
|
|
hideMessageSpec(spectatorError);
|
|
try {
|
|
// Assuming /api/tournaments is accessible without strict auth for spectators
|
|
// Or backend needs a specific public endpoint like /api/public/tournaments
|
|
const tournaments = await fetchAPISpec('/tournaments'); // Using the authenticated route for now
|
|
|
|
selectTournament.innerHTML = '<option value="">-- Bitte Turnier wählen --</option>'; // Reset
|
|
if (tournaments.length === 0) {
|
|
selectTournament.innerHTML += '<option value="" disabled>Keine Turniere verfügbar</option>';
|
|
} else {
|
|
tournaments.forEach(t => {
|
|
const option = document.createElement('option');
|
|
option.value = t.tournament_id;
|
|
option.textContent = `${t.name} (${t.date ? new Date(t.date).toLocaleDateString('de-DE') : 'Datum n.a.'})`;
|
|
selectTournament.appendChild(option);
|
|
});
|
|
selectTournament.disabled = false;
|
|
}
|
|
} catch (error) {
|
|
showMessageSpec(spectatorError, `Fehler beim Laden der Turniere: ${error.message}`);
|
|
selectTournament.innerHTML += '<option value="" disabled>Fehler beim Laden</option>';
|
|
} finally {
|
|
loadingTournamentsSpectator.classList.add('hidden');
|
|
}
|
|
};
|
|
|
|
// Load details for the selected tournament
|
|
const loadTournamentDetails = async (tournamentId) => {
|
|
console.log("Lade Details für Turnier:", tournamentId);
|
|
hideMessageSpec(spectatorError);
|
|
tournamentInfo.classList.add('hidden');
|
|
tournamentDisplay.classList.add('hidden'); // Hide match/player sections until loaded
|
|
|
|
// Show loading indicators for sections
|
|
document.getElementById('loading-live-matches').classList.remove('hidden');
|
|
document.getElementById('loading-bracket').classList.remove('hidden');
|
|
document.getElementById('loading-all-matches').classList.remove('hidden');
|
|
document.getElementById('loading-players').classList.remove('hidden');
|
|
|
|
|
|
try {
|
|
// 1. Fetch basic tournament info
|
|
const tournament = await fetchAPISpec(`/tournaments/${tournamentId}`);
|
|
selectedTournamentName.textContent = tournament.name;
|
|
const dateStr = tournament.date ? new Date(tournament.date).toLocaleDateString('de-DE') : 'N/A';
|
|
selectedTournamentDetails.textContent = `Ort: ${tournament.location || 'N/A'} | Datum: ${dateStr} | Typ: ${tournament.tournament_type}`;
|
|
// TODO: Display description, logo etc.
|
|
tournamentInfo.classList.remove('hidden');
|
|
|
|
// 2. Fetch related data (matches, players, etc.) - Implement these API calls
|
|
// const liveMatches = await fetchAPISpec(`/matches?tournament=${tournamentId}&status=ongoing`);
|
|
// const allMatches = await fetchAPISpec(`/matches?tournament=${tournamentId}`);
|
|
// const players = await fetchAPISpec(`/players?tournament=${tournamentId}`); // Needs backend support
|
|
// const bracketData = await fetchAPISpec(`/tournaments/${tournamentId}/bracket`); // Needs backend support
|
|
|
|
// TODO: Render the fetched data into the respective sections
|
|
document.getElementById('live-matches-list').innerHTML = `<p><i>Live-Spiele für ${tournament.name} laden... (TODO)</i></p>`;
|
|
document.getElementById('bracket-groups-visualization').innerHTML = `<p><i>Turnierbaum für ${tournament.name} laden... (TODO)</i></p>`;
|
|
document.getElementById('all-matches-list').innerHTML = `<p><i>Alle Spiele für ${tournament.name} laden... (TODO)</i></p>`;
|
|
document.getElementById('player-list').innerHTML = `<p><i>Spieler für ${tournament.name} laden... (TODO)</i></p>`;
|
|
|
|
|
|
tournamentDisplay.classList.remove('hidden'); // Show the details sections
|
|
|
|
} catch (error) {
|
|
showMessageSpec(spectatorError, `Fehler beim Laden der Turnierdetails: ${error.message}`);
|
|
tournamentDisplay.classList.add('hidden'); // Hide details on error
|
|
} finally {
|
|
// Hide loading indicators
|
|
document.getElementById('loading-live-matches').classList.add('hidden');
|
|
document.getElementById('loading-bracket').classList.add('hidden');
|
|
document.getElementById('loading-all-matches').classList.add('hidden');
|
|
document.getElementById('loading-players').classList.add('hidden');
|
|
}
|
|
};
|
|
|
|
// --- Event Listeners ---
|
|
selectTournament.addEventListener('change', (event) => {
|
|
const selectedId = event.target.value;
|
|
if (selectedId) {
|
|
loadTournamentDetails(selectedId);
|
|
} else {
|
|
tournamentInfo.classList.add('hidden');
|
|
tournamentDisplay.classList.add('hidden');
|
|
hideMessageSpec(spectatorError);
|
|
}
|
|
});
|
|
|
|
// TODO: Add event listener for player filter input
|
|
|
|
// --- Initial Load ---
|
|
loadAvailableTournaments();
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|