feat: add tokens column to dashboard and update rendering logic
All checks were successful
Build and Push Docker Image / docker (push) Successful in 8s

This commit is contained in:
2025-09-05 15:21:48 +02:00
parent e70cbbb712
commit de2c7b0a3a
3 changed files with 107 additions and 91 deletions

View File

@@ -113,6 +113,7 @@
<th class="py-2 pr-3">Verbindung</th>
<th class="py-2 pr-3">Ready</th>
<th class="py-2 pr-3">Score</th>
<th class="py-2 pr-3">Tokens</th>
</tr>
</thead>
<tbody id="dashboardList" class="divide-y divide-slate-200 dark:divide-slate-800"></tbody>
@@ -144,99 +145,117 @@
</div>
</div>
<div id="nowPlaying"
class="hidden rounded-lg border border-slate-200 dark:border-slate-800 p-4 bg-slate-50/60 dark:bg-slate-800/60">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3">
<div class="text-lg font-semibold">
<strong id="npTitle">&nbsp;</strong><span id="npArtist"></span><span id="npYear"
class="text-slate-500"></span>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Player Card -->
<div id="nowPlaying"
class="hidden md:row-span-2 h-full rounded-lg border border-slate-200 dark:border-slate-800 p-4 bg-slate-50/60 dark:bg-slate-800/60">
<h3 class="text-lg font-semibold mb-2">Musik-Player</h3>
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3">
<div class="text-lg font-semibold">
<strong id="npTitle">&nbsp;</strong><span id="npArtist"></span><span id="npYear"
class="text-slate-500"></span>
</div>
<div id="revealBanner" class="hidden"></div>
</div>
<div class="mt-3">
<audio id="audio" preload="none" class="hidden"></audio>
<div class="flex flex-col items-center">
<!-- Record Disc -->
<div class="relative" style="width: 200px; height: 200px">
<img id="recordDisc" src="/hitstar.png" alt="Record"
class="w-full h-full rounded-full object-cover shadow-lg ring-2 ring-slate-300 dark:ring-slate-700" />
<!-- center hole overlay -->
<div class="pointer-events-none absolute inset-0 rounded-full" style="
background: radial-gradient(
circle at center,
transparent 0 14px,
rgba(0, 0, 0, 0.22) 14px,
transparent 16px
);
"></div>
<!-- buffering badge -->
<div id="bufferBadge"
class="absolute bottom-2 left-1/2 -translate-x-1/2 rounded bg-slate-900/80 text-white text-xs px-2 py-1 hidden">
Buffering…
</div>
</div>
<!-- Progress bar -->
<div class="mt-4 w-full">
<div class="relative h-2 rounded-full bg-slate-200 dark:bg-slate-700 overflow-hidden">
<div id="progressFill" class="absolute left-0 top-0 h-full bg-indigo-600" style="width: 0%"></div>
</div>
</div>
<!-- Controls (Play/Pause restricted to guesser) -->
<div id="mediaControls" class="hidden mt-4 flex items-center gap-3">
<button id="playBtn"
class="h-10 px-4 rounded-lg bg-emerald-600 hover:bg-emerald-700 text-white font-medium">
Play
</button>
<button id="pauseBtn" class="h-10 px-4 rounded-lg bg-rose-600 hover:bg-rose-700 text-white font-medium">
Pause
</button>
</div>
<!-- Volume (available to all players) -->
<div class="mt-3">
<label class="inline-flex items-center gap-2 text-sm text-slate-700 dark:text-slate-300">
Lautstärke
<input id="volumeSlider" type="range" min="0" max="1" step="0.01" class="w-40 accent-indigo-600" />
</label>
</div>
</div>
</div>
<div id="revealBanner" class="hidden"></div>
</div>
<div class="mt-3">
<audio id="audio" preload="none" class="hidden"></audio>
<div class="flex flex-col items-center">
<!-- Record Disc -->
<div class="relative" style="width: 200px; height: 200px">
<img id="recordDisc" src="/hitstar.png" alt="Record"
class="w-full h-full rounded-full object-cover shadow-lg ring-2 ring-slate-300 dark:ring-slate-700" />
<!-- center hole overlay -->
<div class="pointer-events-none absolute inset-0 rounded-full" style="
background: radial-gradient(
circle at center,
transparent 0 14px,
rgba(0, 0, 0, 0.22) 14px,
transparent 16px
);
"></div>
<!-- buffering badge -->
<div id="bufferBadge"
class="absolute bottom-2 left-1/2 -translate-x-1/2 rounded bg-slate-900/80 text-white text-xs px-2 py-1 hidden">
Buffering…
</div>
</div>
<!-- Progress bar -->
<div class="mt-4 w-full">
<div class="relative h-2 rounded-full bg-slate-200 dark:bg-slate-700 overflow-hidden">
<div id="progressFill" class="absolute left-0 top-0 h-full bg-indigo-600" style="width: 0%"></div>
</div>
</div>
<!-- Controls (Play/Pause restricted to guesser) -->
<div id="mediaControls" class="hidden mt-4 flex items-center gap-3">
<button id="playBtn"
class="h-10 px-4 rounded-lg bg-emerald-600 hover:bg-emerald-700 text-white font-medium">
Play
</button>
<button id="pauseBtn" class="h-10 px-4 rounded-lg bg-rose-600 hover:bg-rose-700 text-white font-medium">
Pause
</button>
</div>
<!-- Volume (available to all players) -->
<div class="mt-3">
<label class="inline-flex items-center gap-2 text-sm text-slate-700 dark:text-slate-300">
Lautstärke
<input id="volumeSlider" type="range" min="0" max="1" step="0.01" class="w-40 accent-indigo-600" />
</label>
</div>
<!-- Answer form: everyone can try to guess title & artist for a coin -->
<form id="answerForm" class="mt-4 w-full flex flex-col md:flex-row gap-2 md:items-end">
<label class="flex-1 text-sm">
<span class="text-slate-700 dark:text-slate-300">Titel</span>
<input id="guessTitle" name="title" placeholder="Songtitel" autocomplete="off"
class="mt-1 w-full h-10 rounded-lg border border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-800 px-3" />
</label>
<label class="flex-1 text-sm">
<span class="text-slate-700 dark:text-slate-300">Künstler</span>
<input id="guessArtist" name="artist" placeholder="Künstler" autocomplete="off"
class="mt-1 w-full h-10 rounded-lg border border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-800 px-3" />
</label>
<!-- Guess Card -->
<div id="guessBox"
class="rounded-lg border border-slate-200 dark:border-slate-800 p-4 bg-white/70 dark:bg-slate-900/60">
<h3 class="text-lg font-semibold">Raten</h3>
<p class="text-sm text-slate-500 dark:text-slate-400 mb-2">Gib Titel und Künstler ein und drücke Abschicken.
</p>
<form id="answerForm" class="mt-2 w-full flex flex-col gap-2">
<label class="text-sm">
<span class="text-slate-700 dark:text-slate-300">Titel</span>
<input id="guessTitle" name="title" placeholder="Songtitel" autocomplete="off"
class="mt-1 w-full h-11 rounded-lg border border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-800 px-3" />
</label>
<label class="text-sm">
<span class="text-slate-700 dark:text-slate-300">Künstler</span>
<input id="guessArtist" name="artist" placeholder="Künstler" autocomplete="off"
class="mt-1 w-full h-11 rounded-lg border border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-800 px-3" />
</label>
<div class="flex items-center gap-2">
<button id="submitAnswer" type="submit"
class="h-10 px-4 rounded-lg bg-emerald-600 hover:bg-emerald-700 text-white font-medium">
class="h-11 px-4 rounded-lg bg-emerald-600 hover:bg-emerald-700 text-white font-medium">
Abschicken
</button>
</form>
<div id="answerResult" class="mt-1 text-sm"></div>
</div>
<div id="answerResult" class="text-sm"></div>
</div>
</form>
</div>
<div class="mt-3 flex flex-wrap items-center gap-3">
<div id="placeArea" class="hidden flex items-center gap-2">
<label class="text-sm text-slate-600 dark:text-slate-300">Position:
<!-- Position Card (under Guess, beside Player) -->
<div id="positionBox"
class="rounded-lg border border-slate-200 dark:border-slate-800 p-4 bg-white/70 dark:bg-slate-900/60 md:col-start-2">
<h3 class="text-lg font-semibold">Position</h3>
<p class="text-sm text-slate-500 dark:text-slate-400 mb-2">Wähle die Position und klicke Platzieren.</p>
<div class="flex flex-wrap items-center gap-3">
<div id="placeArea" class="hidden flex items-center gap-2">
<select id="slotSelect"
class="ml-2 h-10 rounded-lg border border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-800 px-3"></select>
</label>
<button id="placeBtn"
class="h-10 px-4 rounded-lg bg-slate-900 hover:bg-slate-700 text-white font-medium dark:bg-slate-700 dark:hover:bg-slate-600">
Platzieren
</button>
</div>
<div id="nextArea" class="hidden">
<button id="nextBtn" class="h-10 px-4 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-white font-medium">
Next
</button>
class="h-10 rounded-lg border border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-800 px-3"></select>
<button id="placeBtn"
class="h-10 px-4 rounded-lg bg-slate-900 hover:bg-slate-700 text-white font-medium dark:bg-slate-700 dark:hover:bg-slate-600">
Platzieren
</button>
</div>
<div id="nextArea" class="hidden">
<button id="nextBtn"
class="h-10 px-4 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-white font-medium">
Next
</button>
</div>
</div>
</div>
</div>
@@ -248,9 +267,7 @@
</div>
</div>
<div class="flex items-center gap-3 text-slate-700 dark:text-slate-300">
Tokens: <span id="tokens" class="font-semibold">0</span>
</div>
</div>
</div>

View File

@@ -7,7 +7,6 @@ export const $nameDisplay = el('nameDisplay');
export const $status = el('status');
export const $guesser = el('guesser');
export const $timeline = el('timeline');
export const $tokens = el('tokens');
export const $audio = el('audio');
export const $np = el('nowPlaying');
export const $npTitle = el('npTitle');

View File

@@ -18,7 +18,6 @@ import {
$startGame,
$status,
$timeline,
$tokens,
} from './dom.js';
export function renderRoom(room) {
@@ -58,6 +57,7 @@ export function renderRoom(room) {
? '<span class="text-emerald-600">bereit</span>'
: '<span class="text-slate-400">-</span>';
const score = room.state.timeline?.[p.id]?.length ?? 0;
const tokens = room.state.tokens?.[p.id] ?? 0;
const isMe = p.id === state.playerId;
return `
<tr class="align-top">
@@ -71,6 +71,7 @@ export function renderRoom(room) {
<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>
<td class="py-2 pr-3 font-semibold tabular-nums">${tokens}</td>
</tr>`;
})
.join('');
@@ -93,7 +94,6 @@ export function renderRoom(room) {
`;
})
.join('');
$tokens.textContent = room.state.tokens?.[state.playerId] ?? 0;
if ($readyChk) {
const serverReady = !!me?.ready;
if (state.pendingReady === null || state.pendingReady === undefined) {