refactor: update player handling and dashboard display in client and server

This commit is contained in:
2025-09-04 10:32:59 +02:00
parent b567187a77
commit edaf9ea94e
4 changed files with 161 additions and 99 deletions

View File

@@ -46,7 +46,8 @@ let state = {
const el = (id) => document.getElementById(id);
const $lobby = el('lobby');
const $room = el('room');
const $players = el('players');
// Removed old players chip list; using dashboard only
const $dashboardList = el('dashboardList');
const $roomId = el('roomId');
const $status = el('status');
const $guesser = el('guesser');
@@ -129,22 +130,38 @@ function showToast(msg) {
// Show my current name (from server if available) or fallback to stored value
const me = room.players.find(p=>p.id===state.playerId);
if ($nameDisplay) $nameDisplay.textContent = (me?.name || localStorage.getItem('playerName') || '-');
$players.innerHTML = room.players.map(p => {
const badges = [
p.id===room.hostId ? '<span class="ml-1 text-amber-600">⭐</span>' : '',
p.ready ? '<span class="ml-1 text-emerald-600">✓</span>' : '',
!p.connected ? '<span class="ml-1 text-rose-600">(off)</span>' : '',
].join('');
return `<span class="inline-flex items-center gap-1 px-2 py-1 rounded-full border border-slate-300 dark:border-slate-700 text-sm">${escapeHtml(p.name)}${badges}</span>`;
}).join(' ');
// Old players chip list removed
// Dashboard rows
if ($dashboardList) {
$dashboardList.innerHTML = room.players.map(p => {
const connected = p.connected ? '<span class="text-emerald-600">online</span>' : '<span class="text-rose-600">offline</span>';
const ready = p.ready ? '<span class="text-emerald-600">bereit</span>' : '<span class="text-slate-400">-</span>';
const score = (room.state.timeline?.[p.id]?.length) ?? 0;
const isMe = p.id === state.playerId;
return `
<tr class="align-top">
<td class="py-2 pr-3">
<div class="inline-flex items-center gap-1">
<span>${escapeHtml(p.name)}</span>${p.spectator ? ' <span title="Zuschauer">👻</span>' : ''}
${p.id===room.hostId ? '<span title="Host" class="text-amber-600">\u2B50</span>' : ''}
${isMe ? '<span title="Du" class="text-indigo-600">(du)</span>' : ''}
</div>
</td>
<td class="py-2 pr-3">${connected}</td>
<td class="py-2 pr-3">${ready}</td>
<td class="py-2 pr-3 font-semibold tabular-nums">${score}</td>
</tr>`;
}).join('');
}
const myTl = room.state.timeline?.[state.playerId] || [];
$timeline.innerHTML = myTl.map(t => {
const title = escapeHtml(t.title || t.trackId || 'Unbekannt');
const artist = t.artist ? escapeHtml(t.artist) : '';
const year = (t.year ?? '?');
const badgeStyle = badgeColorForYear(year);
return `
<div class="flex items-center gap-2 border border-slate-200 dark:border-slate-800 rounded-lg px-3 py-2 bg-white text-slate-900 dark:bg-slate-800 dark:text-slate-100 shadow-sm" title="${title}${artist? ' — '+artist : ''} (${year})">
<div class="font-bold tabular-nums bg-indigo-600 text-white rounded-md px-2 py-0.5 min-w-[3ch] text-center">${year}</div>
<div class="font-bold tabular-nums text-white rounded-md px-2 py-0.5 min-w-[3ch] text-center" style="${badgeStyle}">${year}</div>
<div class="leading-tight">
<div class="font-semibold">${title}</div>
<div class="text-sm text-slate-600 dark:text-slate-300">${artist}</div>
@@ -216,6 +233,18 @@ function shortName(id) {
function escapeHtml(s) { return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }
// Stable distinct color per year for the year badge
function badgeColorForYear(y) {
const val = (y === undefined || y === null) ? '?' : y;
if (val === '?' || Number.isNaN(Number(val))) {
// Neutral slate for unknown years
return 'background-color: hsl(215 16% 34%);';
}
const n = Number(val);
const hue = ((n * 23) % 360 + 360) % 360; // spread hues deterministically
return `background-color: hsl(${hue} 70% 42%);`;
}
function handleMessage(ev) {
const msg = JSON.parse(ev.data);
if (msg.type === 'connected') {

View File

@@ -46,9 +46,29 @@
<div class="text-slate-700 dark:text-slate-300">Dein Name: <strong id="nameDisplay" class="font-semibold"></strong></div>
<!-- Expandable Dashboard: player statuses and scores -->
<div>
<h3 class="text-sm font-semibold text-slate-500 uppercase tracking-wide">Spieler</h3>
<div id="players" class="mt-2 flex flex-wrap gap-2"></div>
<details id="dashboard" class="rounded-lg border border-slate-200 dark:border-slate-800 bg-white/60 dark:bg-slate-900/40 p-3">
<summary class="cursor-pointer select-none text-sm font-semibold text-slate-700 dark:text-slate-300 flex items-center gap-2">
<span class="inline-flex h-5 w-5 items-center justify-center rounded-full bg-indigo-600 text-white text-[11px] font-bold">i</span>
Dashboard: Spielerstatus & Punkte
</summary>
<div class="mt-3 overflow-x-auto">
<table class="min-w-full text-sm">
<thead class="text-left text-slate-500 dark:text-slate-400">
<tr>
<th class="py-2 pr-3">Spieler</th>
<th class="py-2 pr-3">Verbindung</th>
<th class="py-2 pr-3">Ready</th>
<th class="py-2 pr-3">Score</th>
</tr>
</thead>
<tbody id="dashboardList" class="divide-y divide-slate-200 dark:divide-slate-800"></tbody>
</table>
</div>
</details>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 items-start">