Files
python-tdd/src/templates/apps/gameboard/room_gate.html
Disco DeDisco e78ba730e3 room gate-view: reuse the gatekeeper token-slot modal — CONT GAME → hex when satisfied / rails-renew when lapsed — TDD
Redesign of the room gate-view per user-spec 2026-05-31: drop the custom
seat-circle + countdown; render the EXACT gatekeeper modal instead
(title panel + animated status-dots + token-slot rails + roles panel).

- roles-panel .btn-primary is CONT GAME (→ table hex, same target as the
  gear NVM) while the viewer's seat cost is current; absent once it
  lapses, reappears after renewal re-satisfies the cost
- .gate-status-text: "<n> Token(s) Deposited" (literal "(s)" + the shared
  . . . . dots loop) when satisfied; "Please Deposit Token" when not.
  <n> = the room's deposited (FILLED) slot count
- token slot: .claimed (static rails) when current; .active rails that
  POST to renew_token when lapsed
- seat circle + time-remaining removed — the hex's own .fa-chair carries
  seat status & user/seat tooltips land next sprint
- room_gate view trimmed to {room, cost_current, deposited_count,
  page_class}
- tests: RoomGateViewTest reworked (9) — CONT GAME→hex + deposited-count
  status + no renew-form when current; "Please Deposit Token" + renew
  rails + no CONT GAME when lapsed; NVM→hex; page-room; no seat/countdown
  markup. 510 epic tests green

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:21:58 -04:00

111 lines
5.7 KiB
HTML

{% extends "core/base.html" %}
{% load static %}
{% load lyric_extras %}
{% block title_text %}Game Gate{% endblock title_text %}
{% block header_text %}<span>Game</span><span>Gate</span>{% endblock header_text %}
{% block content %}
{# Room renewal gate-view (sprint 2026-05-31) — reuses the EXACT gatekeeper #}
{# token-slot modal (`_gatekeeper.html` chrome: title panel + status-dots + #}
{# token-slot rails + roles panel), reachable mid-game. Unlike the #}
{# gatekeeper (which redirects to the table once table_status is set), GATE #}
{# VIEW routes here at any time so a seated gamer can renew. #}
{# • cost current → roles-panel `.btn-primary` is CONT GAME (→ table hex, #}
{# same target as the gear NVM); status "<n> Token(s) Deposited . . . .".#}
{# • cost lapsed → no CONT GAME; the rails go active to RENEW; status #}
{# "Please Deposit Token . . . .". Renewing re-satisfies the cost and #}
{# the CONT GAME btn reappears. #}
{# No seat circle / countdown here — the table hex's own `.fa-chair` shows #}
{# seat status, and user/seat tooltips land next sprint (user-spec). #}
<div class="room-page room-gate-page" data-room-id="{{ room.id }}">
<div id="id_gate_wrapper" class="room-gate-wrapper">
<div class="gate-backdrop"></div>
<div class="gate-overlay room-gate-overlay">
<div class="gate-modal room-gate-modal" role="dialog" aria-label="Renewal Gate">
<div class="gate-title-panel">
<header class="gate-header">
<h1>{{ room.name }}</h1>
<div class="gate-status-wrap">
<span class="gate-status-text">{% if cost_current %}{{ deposited_count }} Token(s) Deposited{% else %}Please Deposit Token{% endif %}</span>
<span class="status-dots" aria-hidden="true">
<span></span><span></span><span></span><span></span>
</span>
</div>
</header>
</div>
<div class="gate-top-row">
<div class="gate-main-panel">
{# Cost current → claimed (static rails, token in the slot). #}
{# Cost lapsed → active rails that POST to renew_token. #}
<div class="token-slot{% if cost_current %} claimed{% else %} active{% endif %}">
{% if not cost_current %}
<form method="POST" action="{% url 'epic:renew_token' room.id %}" style="display:contents">
{% csrf_token %}
<button type="submit" class="token-rails" aria-label="Insert token to renew">
<span class="rail"></span>
<span class="rail"></span>
</button>
</form>
{% else %}
<div class="token-rails">
<span class="rail"></span>
<span class="rail"></span>
</div>
{% endif %}
<div class="token-panel">
<div class="token-denomination">1</div>
<span class="token-insert-label">INSERT TOKEN TO PLAY</span>
<span class="token-return-label">PUSH TO RETURN</span>
</div>
</div>
</div>
<div class="gate-roles-panel">
{% if cost_current %}
{# CONT GAME — same destination as the gear NVM (the table #}
{# hex). Non-destructive nav, so no confirm guard. Only #}
{# rendered while the token cost is satisfied. #}
<button type="button"
id="id_room_cont_game_btn"
class="launch-game-btn btn btn-primary"
onclick="window.location.href='{% url 'epic:room' room.id %}'">CONT<br>GAME</button>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{# NVM nav-backs one step to the table hex (not out to /gameboard/). #}
{% url 'epic:room' room.id as nvm_url %}
{% include "apps/gameboard/_partials/_room_gear.html" with nvm_url=nvm_url %}
{% include "apps/gameboard/_partials/_burger.html" %}
</div>
{% endblock content %}
{% block scripts %}
<script src="{% static 'apps/dashboard/note.js' %}"></script>
<script src="{% static 'apps/epic/burger-btn.js' %}"></script>
<script>
{# Status-dots animation identical loop to _gatekeeper.html so the #}
{# "Token(s) Deposited . . . ." / "Please Deposit Token . . . ." status #}
{# pulses the same way as the gather-flow gatekeeper. #}
(function () {
clearInterval(window._gateDots);
var wrap = document.querySelector('.status-dots');
if (!wrap) return;
var dots = wrap.querySelectorAll('span');
var n = 0;
window._gateDots = setInterval(function () {
if (!document.contains(wrap)) { clearInterval(window._gateDots); return; }
dots.forEach(function (d, i) { d.textContent = i < n ? '.' : ''; });
n = (n + 1) % 5;
}, 400);
}());
</script>
{% endblock scripts %}