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">Verbindung</th>
<th class="py-2 pr-3">Ready</th> <th class="py-2 pr-3">Ready</th>
<th class="py-2 pr-3">Score</th> <th class="py-2 pr-3">Score</th>
<th class="py-2 pr-3">Tokens</th>
</tr> </tr>
</thead> </thead>
<tbody id="dashboardList" class="divide-y divide-slate-200 dark:divide-slate-800"></tbody> <tbody id="dashboardList" class="divide-y divide-slate-200 dark:divide-slate-800"></tbody>
@@ -144,99 +145,117 @@
</div> </div>
</div> </div>
<div id="nowPlaying" <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
class="hidden rounded-lg border border-slate-200 dark:border-slate-800 p-4 bg-slate-50/60 dark:bg-slate-800/60"> <!-- Player Card -->
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3"> <div id="nowPlaying"
<div class="text-lg font-semibold"> 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">
<strong id="npTitle">&nbsp;</strong><span id="npArtist"></span><span id="npYear" <h3 class="text-lg font-semibold mb-2">Musik-Player</h3>
class="text-slate-500"></span> <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>
<div id="revealBanner" class="hidden"></div>
</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 --> <!-- Guess Card -->
<div class="mt-4 w-full"> <div id="guessBox"
<div class="relative h-2 rounded-full bg-slate-200 dark:bg-slate-700 overflow-hidden"> class="rounded-lg border border-slate-200 dark:border-slate-800 p-4 bg-white/70 dark:bg-slate-900/60">
<div id="progressFill" class="absolute left-0 top-0 h-full bg-indigo-600" style="width: 0%"></div> <h3 class="text-lg font-semibold">Raten</h3>
</div> <p class="text-sm text-slate-500 dark:text-slate-400 mb-2">Gib Titel und Künstler ein und drücke Abschicken.
</div> </p>
<form id="answerForm" class="mt-2 w-full flex flex-col gap-2">
<!-- Controls (Play/Pause restricted to guesser) --> <label class="text-sm">
<div id="mediaControls" class="hidden mt-4 flex items-center gap-3"> <span class="text-slate-700 dark:text-slate-300">Titel</span>
<button id="playBtn" <input id="guessTitle" name="title" placeholder="Songtitel" autocomplete="off"
class="h-10 px-4 rounded-lg bg-emerald-600 hover:bg-emerald-700 text-white font-medium"> 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" />
Play </label>
</button> <label class="text-sm">
<button id="pauseBtn" class="h-10 px-4 rounded-lg bg-rose-600 hover:bg-rose-700 text-white font-medium"> <span class="text-slate-700 dark:text-slate-300">Künstler</span>
Pause <input id="guessArtist" name="artist" placeholder="Künstler" autocomplete="off"
</button> 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" />
</div> </label>
<div class="flex items-center gap-2">
<!-- 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>
<button id="submitAnswer" type="submit" <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 Abschicken
</button> </button>
</form> <div id="answerResult" class="text-sm"></div>
<div id="answerResult" class="mt-1 text-sm"></div> </div>
</div> </form>
</div> </div>
<div class="mt-3 flex flex-wrap items-center gap-3">
<div id="placeArea" class="hidden flex items-center gap-2"> <!-- Position Card (under Guess, beside Player) -->
<label class="text-sm text-slate-600 dark:text-slate-300">Position: <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" <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> class="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"
<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">
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
Platzieren </button>
</button> </div>
</div> <div id="nextArea" class="hidden">
<div id="nextArea" class="hidden"> <button id="nextBtn"
<button id="nextBtn" class="h-10 px-4 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-white font-medium"> class="h-10 px-4 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-white font-medium">
Next Next
</button> </button>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -248,9 +267,7 @@
</div> </div>
</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>
</div> </div>

View File

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

View File

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