hex position indicators: chair icons at hex edge midpoints replace gate-slot circles

- Split .gate-overlay into .gate-backdrop (z-100, blur) + .gate-overlay modal (z-120) so .table-position elements (z-110) render above backdrop but below modal
- New _table_positions.html partial: 6 .table-position divs with .fa-chair, role label, and .fa-ban/.fa-circle-check status icons; included unconditionally in room.html
- New epic:room view at /gameboard/room/<uuid>/; gatekeeper redirects there when table_status set; pick_roles redirects there
- role-select.js: adds .active glow to position on selectRole(); swaps .fa-ban→.fa-circle-check in placeCard onComplete; handleTurnChanged clears stale .active from all positions
- FTs: PositionIndicatorsTest (5 tests) + RoleSelectTest 8a/8b (glow + check state)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-03-30 18:31:05 -04:00
parent 8b006be138
commit a8592aeaec
11 changed files with 370 additions and 35 deletions

View File

@@ -484,6 +484,104 @@ class RoleSelectTest(FunctionalTest):
)
# ------------------------------------------------------------------ #
# Test 8a — Position glows while role card is being placed #
# ------------------------------------------------------------------ #
def test_position_glows_when_role_card_confirmed(self):
"""Immediately after confirming a role pick, the matching
.table-position should receive .active (the glow state) before
the tray animation completes."""
founder, _ = User.objects.get_or_create(email="founder@test.io")
room = Room.objects.create(name="Position Glow Test", owner=founder)
_fill_room_via_orm(room, [
"founder@test.io", "amigo@test.io", "bud@test.io",
"pal@test.io", "dude@test.io", "bro@test.io",
])
room.table_status = Room.ROLE_SELECT
room.save()
for slot in room.gate_slots.order_by("slot_number"):
TableSeat.objects.create(
room=room, gamer=slot.gamer, slot_number=slot.slot_number,
)
room_url = f"{self.live_server_url}/gameboard/room/{room.id}/gate/"
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(room_url)
# Open fan, click first card (PC), confirm guard
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, ".card-stack[data-state='eligible']"
)
).click()
self.wait_for(lambda: self.browser.find_element(By.ID, "id_role_select"))
self.browser.find_element(By.CSS_SELECTOR, "#id_role_select .card").click()
self.confirm_guard()
# PC position gains .active immediately after confirmation
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, ".table-position[data-role-label='PC'].active"
)
)
# ------------------------------------------------------------------ #
# Test 8b — Position shows check icon after tray sequence ends #
# ------------------------------------------------------------------ #
def test_position_gets_check_when_tray_sequence_ends(self):
"""After the tray arc-in animation completes and the tray closes,
the PC .table-position should show .fa-circle-check and no .fa-ban."""
founder, _ = User.objects.get_or_create(email="founder@test.io")
room = Room.objects.create(name="Position Check Test", owner=founder)
_fill_room_via_orm(room, [
"founder@test.io", "amigo@test.io", "bud@test.io",
"pal@test.io", "dude@test.io", "bro@test.io",
])
room.table_status = Room.ROLE_SELECT
room.save()
for slot in room.gate_slots.order_by("slot_number"):
TableSeat.objects.create(
room=room, gamer=slot.gamer, slot_number=slot.slot_number,
)
room_url = f"{self.live_server_url}/gameboard/room/{room.id}/gate/"
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(room_url)
# Open fan, pick PC card, confirm guard
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, ".card-stack[data-state='eligible']"
)
).click()
self.wait_for(lambda: self.browser.find_element(By.ID, "id_role_select"))
self.browser.find_element(By.CSS_SELECTOR, "#id_role_select .card").click()
self.confirm_guard()
# Wait for tray animation to complete (tray closes)
self.wait_for(
lambda: self.assertFalse(
self.browser.execute_script("return Tray.isOpen()"),
"Tray should close after arc-in sequence"
)
)
# PC position now shows check icon, ban icon gone
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, ".table-position[data-role-label='PC'] .fa-circle-check"
)
)
self.assertEqual(
len(self.browser.find_elements(
By.CSS_SELECTOR, ".table-position[data-role-label='PC'] .fa-ban"
)),
0,
)
class RoleSelectTrayTest(FunctionalTest):
"""After confirming a role pick, the role card enters the tray grid and
the tray opens to reveal it.