228 lines
7.1 KiB
HTML
228 lines
7.1 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<!-- Meta Tags -->
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico" />
|
||
|
||
<!-- Stylesheets -->
|
||
<link rel="stylesheet" href="leaderboard.css" />
|
||
<title>Ninjacross Timer - Leaderboard</title>
|
||
</head>
|
||
<body>
|
||
<!-- Modern Notification Toast -->
|
||
<div
|
||
id="notificationBubble"
|
||
class="notification-toast"
|
||
style="display: none"
|
||
>
|
||
<div class="notification-icon">
|
||
<span id="notificationIcon">✓</span>
|
||
</div>
|
||
<div class="notification-body">
|
||
<div class="notification-title" id="notificationTitle">Erfolg</div>
|
||
<div class="notification-message" id="notificationText">Bereit</div>
|
||
</div>
|
||
<button class="notification-close" onclick="hideNotification()">
|
||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||
<path
|
||
d="M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z"
|
||
/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Zurück Button -->
|
||
<a href="/" class="back-btn">🏠</a>
|
||
|
||
<div class="container">
|
||
<!-- Header Section -->
|
||
<div class="header">
|
||
<h1>🏆 Leaderboard</h1>
|
||
</div>
|
||
|
||
<div class="content">
|
||
<!-- Leaderboard Section -->
|
||
<div id="leaderboard-container" class="leaderboard-container">
|
||
<div class="loading">Lade Leaderboard...</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- JavaScript Code -->
|
||
<script>
|
||
let leaderboardData = [];
|
||
let lastUpdateTime = null;
|
||
|
||
// Seite laden
|
||
window.onload = function () {
|
||
loadLeaderboard();
|
||
// Leaderboard alle 5 Sekunden aktualisieren
|
||
setInterval(loadLeaderboard, 5000);
|
||
};
|
||
|
||
// Leaderboard laden
|
||
async function loadLeaderboard() {
|
||
try {
|
||
const response = await fetch("/api/leaderboard-full");
|
||
const data = await response.json();
|
||
leaderboardData = data.leaderboard || [];
|
||
lastUpdateTime = new Date();
|
||
updateLeaderboardDisplay();
|
||
} catch (error) {
|
||
console.error("Fehler beim Laden des Leaderboards:", error);
|
||
showMessage("Fehler beim Laden des Leaderboards", "error");
|
||
}
|
||
}
|
||
|
||
// Leaderboard anzeigen
|
||
function updateLeaderboardDisplay() {
|
||
const container = document.getElementById("leaderboard-container");
|
||
container.innerHTML = "";
|
||
|
||
if (leaderboardData.length === 0) {
|
||
container.innerHTML =
|
||
'<div class="no-entries">Noch keine Zeiten erfasst</div>';
|
||
return;
|
||
}
|
||
|
||
// Alle Einträge anzeigen
|
||
const displayData = leaderboardData;
|
||
|
||
// Erstelle zwei Reihen
|
||
const row1 = document.createElement("div");
|
||
row1.className = "leaderboard-row";
|
||
const row2 = document.createElement("div");
|
||
row2.className = "leaderboard-row";
|
||
|
||
displayData.forEach((entry, index) => {
|
||
const entryDiv = document.createElement("div");
|
||
entryDiv.className = "leaderboard-entry";
|
||
|
||
// Podium-Plätze hervorheben
|
||
if (index === 0) {
|
||
entryDiv.classList.add("gold");
|
||
} else if (index === 1) {
|
||
entryDiv.classList.add("silver");
|
||
} else if (index === 2) {
|
||
entryDiv.classList.add("bronze");
|
||
}
|
||
|
||
const rankSpan = document.createElement("span");
|
||
rankSpan.className = "rank";
|
||
rankSpan.textContent = entry.rank + ".";
|
||
|
||
const nameSpan = document.createElement("span");
|
||
nameSpan.className = "name";
|
||
nameSpan.textContent = entry.name;
|
||
|
||
const timeSpan = document.createElement("span");
|
||
timeSpan.className = "time";
|
||
timeSpan.textContent = entry.timeFormatted;
|
||
|
||
entryDiv.appendChild(rankSpan);
|
||
entryDiv.appendChild(nameSpan);
|
||
entryDiv.appendChild(timeSpan);
|
||
|
||
// Erste 5 Einträge in die erste Reihe, nächste 5 in die zweite Reihe
|
||
if (index < 5) {
|
||
row1.appendChild(entryDiv);
|
||
} else {
|
||
row2.appendChild(entryDiv);
|
||
}
|
||
});
|
||
|
||
container.appendChild(row1);
|
||
if (displayData.length > 5) {
|
||
container.appendChild(row2);
|
||
}
|
||
}
|
||
|
||
// Moderne Notification anzeigen
|
||
function showMessage(message, type = "info") {
|
||
console.log("showMessage called:", message, type);
|
||
const toast = document.getElementById("notificationBubble");
|
||
const icon = document.getElementById("notificationIcon");
|
||
const title = document.getElementById("notificationTitle");
|
||
const text = document.getElementById("notificationText");
|
||
|
||
if (!toast || !icon || !title || !text) {
|
||
console.error("Notification elements not found!");
|
||
return;
|
||
}
|
||
|
||
// Clear any existing timeout
|
||
if (window.notificationTimeout) {
|
||
clearTimeout(window.notificationTimeout);
|
||
}
|
||
|
||
// Set content
|
||
text.textContent = message;
|
||
|
||
// Set type-specific styling and content
|
||
toast.className = "notification-toast";
|
||
switch (type) {
|
||
case "success":
|
||
toast.classList.add("success");
|
||
icon.textContent = "✓";
|
||
title.textContent = "Erfolg";
|
||
break;
|
||
case "error":
|
||
toast.classList.add("error");
|
||
icon.textContent = "✕";
|
||
title.textContent = "Fehler";
|
||
break;
|
||
case "info":
|
||
toast.classList.add("info");
|
||
icon.textContent = "ℹ";
|
||
title.textContent = "Information";
|
||
break;
|
||
case "warning":
|
||
toast.classList.add("warning");
|
||
icon.textContent = "⚠";
|
||
title.textContent = "Warnung";
|
||
break;
|
||
default:
|
||
toast.classList.add("info");
|
||
icon.textContent = "ℹ";
|
||
title.textContent = "Information";
|
||
}
|
||
|
||
// Show toast with animation
|
||
toast.style.display = "flex";
|
||
// Force reflow
|
||
toast.offsetHeight;
|
||
// Add show class after a small delay to ensure display is set
|
||
setTimeout(() => {
|
||
toast.classList.add("show");
|
||
}, 10);
|
||
|
||
// Auto-hide after 5 seconds
|
||
window.notificationTimeout = setTimeout(() => {
|
||
hideNotification();
|
||
}, 5000);
|
||
}
|
||
|
||
// Notification verstecken mit Animation
|
||
function hideNotification() {
|
||
const toast = document.getElementById("notificationBubble");
|
||
if (!toast) return;
|
||
|
||
// Clear timeout if exists
|
||
if (window.notificationTimeout) {
|
||
clearTimeout(window.notificationTimeout);
|
||
}
|
||
|
||
// Remove show class for animation
|
||
toast.classList.remove("show");
|
||
|
||
// Hide after animation completes
|
||
setTimeout(() => {
|
||
toast.style.display = "none";
|
||
}, 400); // Match CSS transition duration
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|