role-select UX: tray timing delays, seat/circle state polish, 394 ITs green
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

- _animationPending set before fetch (not in .then()) — blocks WS turn advance during in-flight request
- _placeCardDelay (3s) + _postTrayDelay (3s) give gamer time to see each step; both zeroed by _testReset()
- .role-confirmed class: full-opacity chair after placeCard completes; server-rendered on reload
- Slot circles disappear in join order (slot 1 first) via count-based logic, not role-label matching
- data-active-slot on card-stack; handleTurnChanged writes it for selectRole() to read
- #id_tray_wrap not rendered during gate phase ({% if room.table_status %})
- Tray slide/arc-in slowed to 1s for diagnostics; wobble kept at 0.45s
- Obsolete test_roles_revealed_simultaneously FT removed; T8 tray FT uses ROLE_SELECT room
- Jasmine macrotask flush pattern: await new Promise(r => setTimeout(r, 0)) after fetch .then()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-03-31 00:01:04 -04:00
parent a8592aeaec
commit 736b59b5c0
13 changed files with 833 additions and 400 deletions

View File

@@ -44,43 +44,6 @@
{% endif %}
</div>
<div class="gate-slots row">
{% for slot in slots %}
<div
class="gate-slot{% if slot.status == 'EMPTY' %} empty{% elif slot.status == 'FILLED' %} filled{% elif slot.status == 'RESERVED' %} reserved{% endif %}"
data-slot="{{ slot.slot_number }}"
>
<span class="slot-number">{{ slot.slot_number }}</span>
{% if slot.gamer %}
<span class="slot-gamer">{{ slot.gamer.email }}</span>
{% else %}
<span class="slot-gamer">empty</span>
{% endif %}
{% if slot.status == 'RESERVED' and slot.gamer == request.user %}
<form method="POST" action="{% url 'epic:confirm_token' room.id %}">
{% csrf_token %}
{% if is_last_slot %}
<button type="submit" class="btn btn-primary btn-xl">PICK ROLES</button>
{% else %}
<button type="submit" class="btn btn-confirm">OK</button>
{% endif %}
</form>
{% elif carte_active and slot.status == 'EMPTY' and slot.slot_number == carte_next_slot_number %}
<form method="POST" action="{% url 'epic:confirm_token' room.id %}">
{% csrf_token %}
<input type="hidden" name="slot_number" value="{{ slot.slot_number }}">
<button type="submit" class="drop-token-btn btn btn-confirm" aria-label="Fill slot {{ slot.slot_number }}">OK</button>
</form>
{% elif carte_active and slot.status == 'FILLED' and slot.slot_number == carte_nvm_slot_number %}
<form method="POST" action="{% url 'epic:release_slot' room.id %}">
{% csrf_token %}
<input type="hidden" name="slot_number" value="{{ slot.slot_number }}">
<button type="submit" class="slot-release-btn btn btn-cancel">NVM</button>
</form>
{% endif %}
</div>
{% endfor %}
</div>
{% if room.gate_status == 'OPEN' %}
<form method="POST" action="{% url 'epic:pick_roles' room.id %}" style="display:contents">
{% csrf_token %}

View File

@@ -1,12 +1,31 @@
{% for pos in gate_positions %}
<div class="table-position" data-slot="{{ pos.slot.slot_number }}" data-role-label="{{ pos.role_label }}">
<div class="position-body">
<i class="fa-solid fa-chair"></i>
<span class="position-role-label">{{ pos.role_label }}</span>
<div class="token-tooltip">
<h4>{% if pos.slot.gamer %}@{{ pos.slot.gamer.username|default:pos.slot.gamer.email }}{% else %}Empty Seat{% endif %}</h4>
<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 %}"
data-slot="{{ pos.slot.slot_number }}">
<span class="slot-number">{{ pos.slot.slot_number }}</span>
<span class="slot-gamer">{% if pos.slot.gamer %}{{ pos.slot.gamer.email }}{% else %}empty{% endif %}</span>
{% if pos.slot.status == 'RESERVED' and pos.slot.gamer == request.user %}
<form method="POST" action="{% url 'epic:confirm_token' room.id %}">
{% csrf_token %}
{% if is_last_slot %}
<button type="submit" class="btn btn-primary btn-xl">PICK ROLES</button>
{% else %}
<button type="submit" class="btn btn-confirm">OK</button>
{% endif %}
</form>
{% elif carte_active and pos.slot.status == 'EMPTY' and pos.slot.slot_number == carte_next_slot_number %}
<form method="POST" action="{% url 'epic:confirm_token' room.id %}">
{% csrf_token %}
<input type="hidden" name="slot_number" value="{{ pos.slot.slot_number }}">
<button type="submit" class="drop-token-btn btn btn-confirm" aria-label="Fill slot {{ pos.slot.slot_number }}">OK</button>
</form>
{% elif carte_active and pos.slot.status == 'FILLED' and pos.slot.slot_number == carte_nvm_slot_number %}
<form method="POST" action="{% url 'epic:release_slot' room.id %}">
{% csrf_token %}
<input type="hidden" name="slot_number" value="{{ pos.slot.slot_number }}">
<button type="submit" class="slot-release-btn btn btn-cancel">NVM</button>
</form>
{% endif %}
</div>
</div>
<i class="position-status-icon fa-solid {% if pos.slot.gamer %}fa-circle-check{% else %}fa-ban{% endif %}"></i>
{% endfor %}
</div>
{% endfor %}