Add leaderboard component to UI and API endpoint to fetch player rankings.

This commit is contained in:
IM23a-spirgif
2025-06-20 19:43:15 +02:00
parent 754422d043
commit 3893c144bf
4 changed files with 122 additions and 11 deletions
+35 -11
View File
@@ -87,6 +87,29 @@ app.post("/register", (request, response) => {
response.json({lower_username, token});
});
app.get("/leaderboard", authenticateToken, (req, res) => {
const users = get_data();
const sorted = users.sort((a, b) => b.money - a.money);
const top10 = sorted.slice(0, 10).map(u => ({
username: u.username,
money: u.money,
}));
const username = req.user.username;
const position = sorted.findIndex(u => u.username === username) + 1;
const self = sorted.find(u => u.username === username);
res.json({
leaderboard: top10,
self: {
username: self.username,
money: self.money,
position
}
});
});
app.get("/login", (request, response) => {
response.sendFile(path.join(__dirname, "./public/login.html"));
})
@@ -173,20 +196,21 @@ async function createLobby() {
await fetch(`https://www.deckofcardsapi.com/api/deck/${lobbyId}/pile/players/add/?cards=`)
return lobbyId;
}
app.post("/leave-lobby", (req, res) => {
const { lobbyId, username } = req.body;
const {lobbyId, username} = req.body;
if (!lobbyId || !username) {
return res.status(400).json({ error: "Lobby ID und Username sind erforderlich!" });
return res.status(400).json({error: "Lobby ID und Username sind erforderlich!"});
}
if (!playerMap[lobbyId]) {
return res.status(404).json({ error: "Lobby nicht gefunden!" });
return res.status(404).json({error: "Lobby nicht gefunden!"});
}
playerMap[lobbyId] = playerMap[lobbyId].filter(player => player !== username);
if (playerMap[lobbyId].length === 0) {
delete playerMap[lobbyId];
console.log(`Leere Lobby ${lobbyId} wurde gelöscht.`);
}
res.json({ message: `Lobby ${lobbyId} aktualisiert.` });
res.json({message: `Lobby ${lobbyId} aktualisiert.`});
});
setInterval(() => removeEmptyLobbies(playerMap), 10000);
@@ -245,7 +269,7 @@ app.get("/lobby/join", authenticateToken, async (req, res) => {
const users = get_data();
const user = users.find(u => u.username === username);
if (!user) {
return res.status(401).json({ error: "User doesn't exist" });
return res.status(401).json({error: "User doesn't exist"});
}
if (lobbyId in playerMap) {
await joinLobby(lobbyId, user.username);
@@ -254,10 +278,10 @@ app.get("/lobby/join", authenticateToken, async (req, res) => {
}
user.lobbyId = lobbyId;
save_data(users);
res.json({ lobbyId, username });
res.json({lobbyId, username});
} else {
console.log("Lobby with " + lobbyId + " not found.");
return res.status(404).json({ error: "Lobby not found" });
return res.status(404).json({error: "Lobby not found"});
}
});
@@ -266,19 +290,19 @@ app.delete("/lobby/leave", authenticateToken, async (req, res) => {
const username = req.query.username;
if (!lobbyId || !username) {
return res.status(400).json({ error: "Missing lobbyId or username" });
return res.status(400).json({error: "Missing lobbyId or username"});
}
if (lobbyId in playerMap) {
const index = playerMap[lobbyId].indexOf(username);
if (index > -1) {
playerMap[lobbyId].splice(index, 1);
return res.json({ message: "Successfully left lobby" });
return res.json({message: "Successfully left lobby"});
} else {
return res.status(404).json({ error: "User not in lobby" });
return res.status(404).json({error: "User not in lobby"});
}
}
res.status(404).json({ error: "Lobby not found" });
res.status(404).json({error: "Lobby not found"});
});
+30
View File
@@ -149,4 +149,34 @@ document.addEventListener("DOMContentLoaded", async () => {
joinError.classList.remove("hidden");
}
});
async function loadLeaderboard() {
try {
const res = await fetch("/leaderboard");
if (res.ok) {
const data = await res.json();
const list = document.getElementById("leaderboard-list");
const yourRank = document.getElementById("your-rank");
list.innerHTML = "";
data.leaderboard.forEach((player, i) => {
const item = document.createElement("li");
item.textContent = `${i + 1}. ${player.username} - $${player.money}`;
if (player.username === data.self.username) {
item.classList.add("you");
item.textContent += " 👑";
}
list.appendChild(item);
});
yourRank.textContent = `You are ranked #${data.self.position} with $${data.self.money}`;
} else {
console.error("Failed to load leaderboard.");
}
} catch (err) {
console.error("Leaderboard error:", err);
}
}
await loadLeaderboard();
});
+9
View File
@@ -7,6 +7,15 @@
</head>
<body>
<canvas id="matrix-canvas"></canvas>
<div class="leaderboard">
<h3>🏆 High Rollers</h3>
<ul id="leaderboard-list">
<li>Loading...</li>
</ul>
<div id="your-rank" class="your-rank"></div>
</div>
<button class="logout" id="logout">Logout</button>
<div class="balance">
Balance: $<span id="balance">0</span>
+48
View File
@@ -137,3 +137,51 @@ button.logout {
background: transparent;
pointer-events: none;
}
.leaderboard {
position: absolute;
top: 100px;
left: 20px;
background: rgba(0, 0, 0, 0.7);
border: 2px solid #0f0;
border-radius: 10px;
padding: 20px;
width: 200px;
color: #0f0;
font-family: monospace;
box-shadow: 0 0 10px #0f0;
z-index: 1;
}
.leaderboard h3 {
margin-top: 0;
font-size: 18px;
text-align: center;
text-shadow: 0 0 5px #0f0;
}
.leaderboard ul {
list-style: none;
padding: 0;
margin: 0;
}
.leaderboard li {
margin: 5px 0;
font-size: 14px;
text-shadow: 0 0 3px #0f0;
}
.leaderboard li.you {
font-weight: bold;
color: #ff0;
text-shadow: 0 0 5px #ff0;
}
.your-rank {
margin-top: 10px;
font-size: 14px;
color: #fff;
text-align: center;
text-shadow: 0 0 3px #0f0;
}