Files
AquaMasterMQTT/data/leaderboard.html
2025-09-23 20:07:35 +02:00

228 lines
7.1 KiB
HTML
Raw Permalink 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="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>