web: zadávání z telefonu a zápisník na notebooku
Flask, server-rendered HTML, formuláře přes POST/redirect/GET. Žádný build step, žádný JS framework. - / zakládá hru výběrem známých hráčů v pořadí u stolu - /game/<id> je zadávací pohled: kdo je na tahu, skóre, x/xx, průběžné pořadí, vrácení posledního zápisu - /board/<id> je zápisník po kolech; tahy před vynulováním se škrtnou červeně, sloupec hráče na tahu drží zvýrazňovač - tabule se doptává serveru každé dvě vteřiny a vyměňuje jen hlavičku a zápisník, QR s adresou zůstává mimo obnovovanou část - adresa pro telefon se hledá za běhu, QR se skládá ze čtverečků přímo do SVG, takže není potřeba Pillow - 16 funkčních testů přes Flask test client Vizuálně to stojí na tom, co appka nahrazuje: čtverečkovaný papír, modrá propiska zapisuje, červená škrtá, zvýrazňovač drží tah. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a45a82ae0e
commit
bcbbede258
11 files changed
+1149
No files matched your search
@@ -0,0 +1,109 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Nová hra{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="phone">
|
||||
<header class="masthead">
|
||||
<div>
|
||||
<p class="eyebrow">Počitadlo</p>
|
||||
<h1>Kostky</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if request.args.get('chyba') %}
|
||||
<p class="error">{{ request.args.get('chyba') }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if running %}
|
||||
<div class="notice">
|
||||
<span>Rozehraná hra {{ running }} čeká.</span>
|
||||
<a href="{{ url_for('game_view', game_id=running) }}">Pokračovat</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" action="{{ url_for('new_game') }}">
|
||||
<p class="eyebrow">Kdo hraje</p>
|
||||
<p class="hint">Ťukej jména v pořadí, jak sedíte kolem stolu.</p>
|
||||
|
||||
<div class="chips" id="chips">
|
||||
{% for player in players %}
|
||||
<button type="button" class="chip" aria-pressed="false" data-name="{{ player.name }}">
|
||||
{{ player.name }}<span class="seat"></span>
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="add-player">
|
||||
<input type="text" id="new-name" placeholder="Nový hráč" autocomplete="off">
|
||||
<button type="button" id="add">Přidat</button>
|
||||
</div>
|
||||
|
||||
<div class="field" style="margin-top: 1.5rem">
|
||||
<label for="final">Cílové skóre</label>
|
||||
<input type="number" name="final_score" id="final" value="{{ final_score }}" inputmode="numeric">
|
||||
</div>
|
||||
|
||||
<div id="chosen"></div>
|
||||
<button class="btn" type="submit">Začít hru</button>
|
||||
</form>
|
||||
|
||||
<p class="footer"><a href="{{ url_for('board_redirect') }}">Zápisník</a></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const chips = document.getElementById("chips");
|
||||
const chosen = [];
|
||||
|
||||
function render() {
|
||||
for (const chip of chips.querySelectorAll(".chip")) {
|
||||
const seat = chosen.indexOf(chip.dataset.name);
|
||||
chip.setAttribute("aria-pressed", seat > -1);
|
||||
chip.querySelector(".seat").textContent = seat > -1 ? seat + 1 : "";
|
||||
}
|
||||
|
||||
// Pořadí odeslaných polí je pořadí u stolu.
|
||||
document.getElementById("chosen").replaceChildren(...chosen.map(name => {
|
||||
const field = document.createElement("input");
|
||||
field.type = "hidden";
|
||||
field.name = "player";
|
||||
field.value = name;
|
||||
return field;
|
||||
}));
|
||||
}
|
||||
|
||||
chips.addEventListener("click", event => {
|
||||
const chip = event.target.closest(".chip");
|
||||
if (!chip) return;
|
||||
|
||||
const seat = chosen.indexOf(chip.dataset.name);
|
||||
if (seat > -1) chosen.splice(seat, 1);
|
||||
else chosen.push(chip.dataset.name);
|
||||
render();
|
||||
});
|
||||
|
||||
const field = document.getElementById("new-name");
|
||||
|
||||
function addPlayer() {
|
||||
const name = field.value.trim();
|
||||
if (!name) return;
|
||||
|
||||
if (!chips.querySelector(`[data-name="${CSS.escape(name)}"]`)) {
|
||||
const chip = document.createElement("button");
|
||||
chip.type = "button";
|
||||
chip.className = "chip";
|
||||
chip.dataset.name = name;
|
||||
chip.append(name, Object.assign(document.createElement("span"), { className: "seat" }));
|
||||
chips.append(chip);
|
||||
}
|
||||
|
||||
if (!chosen.includes(name)) chosen.push(name);
|
||||
field.value = "";
|
||||
render();
|
||||
}
|
||||
|
||||
document.getElementById("add").addEventListener("click", addPlayer);
|
||||
field.addEventListener("keydown", event => {
|
||||
if (event.key === "Enter") { event.preventDefault(); addPlayer(); }
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user