Files
bridzik/frontend/src/components/RulesModal.tsx
T
tim 2c2f07c2ec 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>
2026-07-01 00:11:42 +02:00

84 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
interface Props {
onClose: () => void;
}
export default function RulesModal({ onClose }: Props) {
return (
<div
className="fixed inset-0 z-50 flex items-start justify-center bg-black/70 p-4 overflow-y-auto"
onClick={onClose}
>
<div
className="relative bg-header border border-[#142018] rounded-2xl w-full max-w-lg my-6 p-6 text-sm leading-relaxed text-green-score shadow-[0_28px_88px_rgba(0,0,0,.65)]"
onClick={(e) => e.stopPropagation()}
>
<button
onClick={onClose}
className="absolute top-4 right-4 text-green-dim hover:text-gold text-xl leading-none"
>
</button>
<h1 className="font-serif text-2xl text-gold mb-4">Pravidlá hry Bridžik</h1>
<Section title="Karty">
<p>Hrá sa s <b>32-kartovým balíčkom</b> sedmových (slovenských/nemeckých) kariet.</p>
<p className="mt-2"><b>Farby:</b> červeň (), zeleň (), žaluď (), guľa ()</p>
<p className="mt-1 text-red-400 font-semibold">Červeň je vždy tromf (adut) prebíja každú inú farbu.</p>
<p className="mt-2"><b>Hodnoty</b> od najnižšej: VII · VIII · IX · X · J · Q · K · A</p>
</Section>
<Section title="Štruktúra hry">
<p>4 hráči · 4 série · 8 kôl v sérii</p>
<p className="mt-1">V každom kole dostane každý hráč <b>8 číslo_kola</b> kariet (8 1).</p>
<p className="mt-1">Sériu otvára hráč s rovnakým číslom ako séria. Každé ďalšie kolo posúva začínajúceho hráča o jedného.</p>
</Section>
<Section title="Priebeh kola">
<p className="font-semibold">1. Tipovanie</p>
<p className="mt-1">Každý hráč tipuje, koľko kopiek v kole získa (0 počet kopiek).</p>
<p className="mt-1 text-gold-dim">Pravidlo bridžika: súčet tipov nesmie presne rovnať počtu kopiek v kole posledný tipujúci nemôže zadať tip, ktorý by toto spôsobil.</p>
<p className="font-semibold mt-3">2. Hranie kariet</p>
<p className="mt-1">Prvú kopku otvára hráč s <b>najvyšším tipom</b>. Každú ďalšiu otvára víťaz predchádzajúcej kopky.</p>
<p className="font-semibold mt-3">Povinnosť priznať farbu:</p>
<ol className="mt-1 list-decimal list-inside space-y-1">
<li>Máš farbu vynesenej karty <b>musíš ju zahrať.</b></li>
<li>Nemáš ju, ale máš červeň <b>musíš zahrať červeň.</b></li>
<li>Nemáš ani jedno môžeš zahrať <b>ľubovoľnú</b> kartu.</li>
</ol>
<p className="font-semibold mt-3">Víťaz kopky:</p>
<ul className="mt-1 list-disc list-inside space-y-1">
<li>Ak padla červeň vyhráva <b>najvyššia červeň.</b></li>
<li>Ak nie vyhráva <b>najvyššia karta vynesenej farby.</b></li>
</ul>
</Section>
<Section title="Bodovanie">
<p>Po každom kole: ak sa tip <b>presne zhoduje</b> s počtom získaných kopiek <b>10 + tip</b> bodov, inak <b>0</b>.</p>
<p className="mt-1 text-green-dim">Príklad: tipoval 3, získal 3 13 bodov. Tipoval 3, získal 2 0 bodov.</p>
<p className="mt-2">Vyhráva hráč s najvyšším celkovým súčtom po 4 sériách.</p>
</Section>
<button
onClick={onClose}
className="mt-4 w-full py-2.5 rounded-xl border border-gold/30 text-gold hover:bg-gold hover:text-table font-serif font-semibold transition-colors"
>
Zavrieť
</button>
</div>
</div>
);
}
function Section({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="mb-4">
<h2 className="font-serif text-base text-gold mb-1">{title}</h2>
<div className="text-green-score">{children}</div>
</div>
);
}