92 lines
2.9 KiB
JavaScript
92 lines
2.9 KiB
JavaScript
// leaderboard.js - leaderboard page logic
|
|
|
|
import { getLeaderboard } from "./api.js";
|
|
import { getLobbyName, getPlayerName } from "./storage.js";
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
const currentPlayer = getPlayerName();
|
|
const body = document.getElementById("lb-body");
|
|
|
|
/**
|
|
* Escape HTML special characters to prevent XSS.
|
|
* @param {string} str - Raw user-provided string.
|
|
* @returns {string} HTML-safe string.
|
|
*/
|
|
function escHtml(str) {
|
|
return String(str)
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">");
|
|
}
|
|
|
|
/** Render the leaderboard from backend data. */
|
|
async function render() {
|
|
const lobbyName = getLobbyName();
|
|
let board = [];
|
|
try {
|
|
const data = await getLeaderboard(lobbyName);
|
|
board = data.leaderboard || [];
|
|
} catch (_error) {
|
|
body.innerHTML = `
|
|
<div class="lb-empty">
|
|
<p class="lb-empty__text">Could not load the leaderboard. Make sure the backend is running.</p>
|
|
<a href="lobby.html" class="btn btn--primary btn--sm">Back to lobby</a>
|
|
</div>`;
|
|
return;
|
|
}
|
|
|
|
body.innerHTML = "";
|
|
|
|
if (!board.length) {
|
|
body.innerHTML = `
|
|
<div class="lb-empty">
|
|
<div class="lb-empty__icon">🏁</div>
|
|
<p class="lb-empty__text">No games played yet. Be the first on the board!</p>
|
|
<a href="lobby.html" class="btn btn--primary btn--sm">Play now →</a>
|
|
</div>`;
|
|
return;
|
|
}
|
|
|
|
const medals = ["🥇", "🥈", "🥉"];
|
|
const scoreClasses = ["gold", "silver", "bronze"];
|
|
|
|
board.forEach((entry, index) => {
|
|
const rank = index + 1;
|
|
const playerName = entry.playerName || entry.name || "Anonymous";
|
|
const isYou = playerName === currentPlayer;
|
|
const date = entry.date
|
|
? new Date(entry.date).toLocaleDateString("en-CH", {
|
|
day: "2-digit",
|
|
month: "short",
|
|
})
|
|
: "—";
|
|
const rounds = (entry.scores || []).join(" · ") || "—";
|
|
|
|
const row = document.createElement("div");
|
|
row.className = `lb-row${rank <= 3 ? ` rank-${rank}` : ""}`;
|
|
row.style.animationDelay = `${index * 0.05}s`;
|
|
|
|
row.innerHTML = `
|
|
<span class="lb-rank">${rank <= 3 ? `<span class="lb-medal">${medals[rank - 1]}</span>` : rank}</span>
|
|
<span class="lb-name${isYou ? " is-you" : ""}">${escHtml(playerName)}</span>
|
|
<span class="lb-rounds hide-sm">${escHtml(rounds)}</span>
|
|
<span class="lb-date hide-sm">${date}</span>
|
|
<span class="lb-score${rank <= 3 ? ` ${scoreClasses[rank - 1]}` : ""}">${entry.totalScore}</span>
|
|
`;
|
|
body.appendChild(row);
|
|
});
|
|
|
|
const latest = board.find(
|
|
(entry) => (entry.playerName || entry.name) === currentPlayer,
|
|
);
|
|
if (latest) {
|
|
document.getElementById("your-bar").style.display = "flex";
|
|
document.getElementById("your-bar-name").textContent =
|
|
latest.playerName || latest.name;
|
|
document.getElementById("your-bar-score").textContent = latest.totalScore;
|
|
}
|
|
}
|
|
|
|
render();
|
|
});
|