room GATE VIEW: keep all six position circles solid — never fade with role-assigned
The gatekeeper's GATE VIEW is the canonical "who holds which position" surface, but it reused _table_positions.html, which paints the role-assigned fade-out class (opacity:0 / scale .5) server-side from pos.role_assigned (slot <= assigned_count). So as gamers got seated during Role Select, the gate-view circles vanished one-by-one in lockstep with the table-hex — and by the time SCAN SIGS appeared (all six roles assigned) the gate showed an empty hex. The disappear-as- seated animation is meant as a TABLE-HEX-only cue. Fix: _table_positions.html now suppresses role-assigned when its new persist_circles flag is set; room_gate.html includes the partial with persist_circles=True. room.html passes no flag (→ falsy), so the table-hex keeps the fade animation untouched. No JS reads role-assigned in the gate view (role-select.js isn't loaded there), so the server-side guard is sufficient. TDD: PositionTooltipRenderTest.test_gate_view_circles_never_fade_when_roles_assigned — assigns all six roles, asserts the gate view keeps six .gate-slot circles with NO role-assigned. Verified live via Claudezilla on a SIG_SELECT setup_sig_session room. 47 render/gate ITs green (RoleSelectRenderingTest still asserts the table-hex DOES fade). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -714,6 +714,22 @@ class PositionTooltipRenderTest(TestCase):
|
||||
self.assertNotIn(self.gamers[1].email, content) # g2@test.io
|
||||
self.assertNotIn(self.viewer.email, content) # disco@test.io
|
||||
|
||||
def test_gate_view_circles_never_fade_when_roles_assigned(self):
|
||||
# The gatekeeper's GATE VIEW is the canonical "who holds which
|
||||
# position" surface: it must ALWAYS show all six circles, even after
|
||||
# gamers get seated and the main table-hex has faded their circles away
|
||||
# one-by-one (the role-assigned animation). So when every role is
|
||||
# assigned — the moment SCAN SIGS appears on the hex — the gate view
|
||||
# keeps all six circles solid, with NO role-assigned (fade-out) class.
|
||||
roles = ["PC", "NC", "EC", "SC", "AC", "BC"]
|
||||
for i, gamer in enumerate(self.gamers, start=1):
|
||||
TableSeat.objects.create(
|
||||
room=self.room, gamer=gamer, slot_number=i, role=roles[i - 1],
|
||||
)
|
||||
content = self._gate_content()
|
||||
self.assertEqual(content.count('class="gate-slot'), 6)
|
||||
self.assertNotIn("role-assigned", content)
|
||||
|
||||
|
||||
class PositionTooltipCarteRenderTest(TestCase):
|
||||
"""CARTE-solo render contract: a single gamer owns all six slots — their
|
||||
|
||||
@@ -6,9 +6,13 @@
|
||||
{# appended AFTER `role-assigned` so the `gate-slot filled role-assigned` #}
|
||||
{# substring (RoleSelectRenderingTest) stays intact, and `class` stays first #}
|
||||
{# (before data-slot) for the class-attr regex IT. #}
|
||||
{# `persist_circles` (passed True by room_gate.html) suppresses the #}
|
||||
{# role-assigned fade-out so the GATE VIEW always keeps all six circles — #}
|
||||
{# the disappear-as-seated animation is a table-hex-only cue. Undefined #}
|
||||
{# (→ falsy) on the room.html include, which keeps the animation. #}
|
||||
<div class="position-strip">
|
||||
{% for pos in gate_positions %}
|
||||
<div class="gate-slot{% if pos.slot.status == 'EMPTY' %} empty{% elif pos.slot.status == 'FILLED' %} filled{% elif pos.slot.status == 'RESERVED' %} reserved{% endif %}{% if pos.role_assigned %} role-assigned{% endif %}{% if pos.state_class %} {{ pos.state_class }}{% endif %}"
|
||||
<div class="gate-slot{% if pos.slot.status == 'EMPTY' %} empty{% elif pos.slot.status == 'FILLED' %} filled{% elif pos.slot.status == 'RESERVED' %} reserved{% endif %}{% if pos.role_assigned and not persist_circles %} role-assigned{% endif %}{% if pos.state_class %} {{ pos.state_class }}{% endif %}"
|
||||
data-slot="{{ pos.slot.slot_number }}"{% if pos.slot.gamer %} data-user-id="{{ pos.slot.gamer.id }}" data-tt-title="{{ pos.slot.gamer|at_handle }}" data-tt-description="the {{ pos.slot.gamer.active_title_display }}" data-tt-shoptalk="{{ pos.shoptalk }}" data-tt-tokens="{{ pos.tokens }}" data-tt-token-types="{{ pos.token_types|join:'|' }}"{% if pos.expiry %} data-tt-expiry="expires {{ pos.expiry|relative_ts }}"{% endif %}{% if pos.sign_rank %} data-tt-sign-rank="{{ pos.sign_rank }}" data-tt-sign-suit="{{ pos.sign_suit_icon }}"{% endif %}{% endif %}>
|
||||
<span class="slot-number">{{ pos.slot.slot_number }}</span>
|
||||
<span class="slot-gamer">{% if pos.slot.gamer %}{{ pos.slot.gamer|at_handle }}{% else %}empty{% endif %}</span>
|
||||
|
||||
@@ -83,7 +83,11 @@
|
||||
{# Position circles + their hover tooltips — the gate-view rendered no #}
|
||||
{# circles before this sprint (the headline gap). Reuses the shared #}
|
||||
{# _table_positions partial fed by the merged _gate_context. #}
|
||||
{% include "apps/gameboard/_partials/_table_positions.html" %}
|
||||
{# persist_circles=True keeps ALL six circles solid here: the gatekeeper #}
|
||||
{# is the canonical "who holds which position" surface, so it must never #}
|
||||
{# fade a circle away as roles get assigned (the role-assigned animation #}
|
||||
{# is the TABLE-HEX cue only). They stay up to + including SCAN SIGS. #}
|
||||
{% include "apps/gameboard/_partials/_table_positions.html" with persist_circles=True %}
|
||||
{% include "apps/gameboard/_partials/_position_tooltip.html" %}
|
||||
|
||||
{# NVM nav-backs one step to the table hex (not out to /gameboard/). #}
|
||||
|
||||
Reference in New Issue
Block a user