Apply velvet-table redesign, fix game lifecycle and history bugs
Frontend: - Dark green/gold "velvet table" visual redesign across the whole app (Auth, Lobby, GameList, GameTable, History, GameOver, modals), with Playfair Display/DM Sans typography and a centralized Tailwind palette. - Desktop game table fit-scales to fill the window; mobile gets overlapping hand/trick layouts and larger touch-friendly cards. - Standings sidebar now groups completed rounds by series with a per-series subtotal row, struck-through tips on missed bids. - History page rewritten into a scoreboard-style detail view (player totals beside names, series grouped 2-up on desktop / stacked on mobile) and gained game names, completed/abandoned status, and a button to reopen a prematurely-ended game back into the lobby. Backend: - Fix started games being deleted from memory (and vanishing from everyone's lobby) when all players disconnect; only `end_game` tears down a started game now. - Fix a crash writing a timezone-aware datetime into the naive `ended_at` Postgres column. - Add `reopen_game`/`restore_game` to un-end a prematurely-ended game from history and resume it from the lobby. - Let any seated player end an abandoned game once the host is offline, not just the host, so the game isn't stuck forever. - Expose SERIES_PER_GAME/ROUNDS_PER_SERIES as named constants on the engine so the persistence layer derives game-completion rules from bridzik.py instead of re-encoding them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,8 +17,9 @@ export default function GuessControls({ cardsInRound, guesses, myOrder, activePl
|
||||
|
||||
if (!isMyTurn) {
|
||||
return (
|
||||
<p className="text-gray-400 text-sm text-center py-2">
|
||||
Caka sa na tip: <span className="text-white font-semibold">{activePlayerName}</span>
|
||||
<p className="text-center text-sm text-green-dim py-3">
|
||||
Čaká sa na tip:{' '}
|
||||
<span className="font-serif text-gold">{activePlayerName}</span>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
@@ -26,8 +27,8 @@ export default function GuessControls({ cardsInRound, guesses, myOrder, activePl
|
||||
const options = Array.from({ length: cardsInRound + 1 }, (_, i) => i);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-2 py-2">
|
||||
<p className="text-sm text-gray-300">Tvoj tip (pocet kopok):</p>
|
||||
<div className="flex flex-col items-center gap-3 py-3">
|
||||
<p className="font-serif italic text-gold-dim text-[14px]">Zadaj svoj tip (počet kopiek)</p>
|
||||
<div className="flex flex-wrap gap-2 justify-center">
|
||||
{options.map((n) => {
|
||||
const isForbidden = n === forbidden;
|
||||
@@ -36,12 +37,12 @@ export default function GuessControls({ cardsInRound, guesses, myOrder, activePl
|
||||
key={n}
|
||||
disabled={isForbidden}
|
||||
onClick={() => emit.addGuess(n)}
|
||||
title={isForbidden ? 'Zakázaná hodnota (suma = počet kopok)' : undefined}
|
||||
title={isForbidden ? 'Zakázaná hodnota (súčet = počet kopiek)' : undefined}
|
||||
className={[
|
||||
'w-10 h-10 rounded-lg font-bold text-lg border-2 transition-colors',
|
||||
'w-11 h-11 rounded-full font-serif text-lg border-2 transition-colors',
|
||||
isForbidden
|
||||
? 'border-red-700 text-red-700 opacity-40 cursor-not-allowed'
|
||||
: 'border-blue-400 text-blue-200 hover:bg-blue-600 hover:border-blue-600 active:scale-95',
|
||||
? 'border-[#5a2a2a] text-[#7a4040] opacity-40 cursor-not-allowed'
|
||||
: 'border-gold/50 text-gold hover:bg-gold hover:text-table hover:border-gold active:scale-95',
|
||||
].join(' ')}
|
||||
>
|
||||
{n}
|
||||
|
||||
Reference in New Issue
Block a user