From c30b63cd5dec6579ef8b6f9aad1afe407514666b Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Wed, 27 May 2026 00:39:46 -0400 Subject: [PATCH] =?UTF-8?q?burger=20Sea=20sub-btn:=20first-draw=20--priYl?= =?UTF-8?q?=20glow=20handoff=20(phase=203/3)=20=E2=80=94=20TDD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final slice of the Sea sub-btn rollout (phase 1 = .active wiring 3ae85b9; phase 2 = modal extraction + CONT DRAW 6fbeed7). Adds a --priYl + --ninUser glow that rides the affordance chain to teach the user where to click pre-first-draw. ## The handoff chain burger → click → sea_btn → click → .sea-select → click → end - Modal close (Esc / backdrop / DEL guard-OK) restarts the cycle on the burger. - Burger fan close w/o sea_btn click ALSO restarts on the burger. - AUTO DRAW guard-OK ends the cycle permanently (user found the path). - `#id_sea_action_btn` data-state → 'gate-view' (last card landed via ANY path — AUTO DRAW or manual FLIP) ALSO ends permanently. ## SCSS `static_src/scss/_burger.scss` — `.glow-handoff` on burger / sea_btn = --priYl color + border + --ninUser glow. `static_src/scss/_gameboard.scss` — `.glow-handoff` on .sea-select = --terUser border + --ninUser glow (no font-color change per spec). ## Server side `apps/gameboard/views.py` — new `sea_first_draw_pending = show_picker and not hand_non_empty`. True when picker is active w. an empty hand (paid-draw entry, or page reload of a freshly-entered picker). The FREE-DRAW → picker transition fires client-side w. show_picker=False on the rendered template, so the FREE DRAW JS handler seeds the burger glow itself in that path. `templates/apps/gameboard/_partials/_burger.html` — `#id_burger_btn` conditionally renders `class="glow-handoff"` when `sea_first_draw_pending`. `templates/apps/gameboard/my_sea.html` — FREE DRAW transition handler adds `.glow-handoff` to burger at the same SEAT_ANIM_MS moment data-phase swaps to 'picker' (covers the client-side path). ## JS state machine `templates/apps/gameboard/my_sea.html` — new inline IIFE owns the .glow-handoff transitions: - `burger.click` → if .glow-handoff on burger, transfer to sea_btn. - `sea_btn.click` → if .glow-handoff on sea_btn, transfer to .sea-select. - `.sea-select.click` → end this cycle (just clear the glow; cycle restarts on next modal open). - AUTO DRAW guard-OK (via doc-level click listener) → sets `autoDrawConfirmed`. - Modal `hidden`-attr observer: AUTO DRAW path → endPermanently; any other close (Esc / backdrop / DEL) → startOnBurger (skip if glow already permanently ended). - Burger `class`-attr observer: fan closes (`.active` removed) while glow on sea_btn → restart on burger. - `#id_sea_action_btn` `data-state`-attr observer: flips to 'gate-view' (last card landed via ANY path — AUTO DRAW finishing OR manual FLIP filling the final slot) → endPermanently. The data-state observer makes the "stop glowing when all slots filled" guarantee async + decoupled from how the cards arrived. ## CONT DRAW polish (drag-in from prior commit's spec gap) `apps/gameboard/views.py` — `show_cont_draw` now additionally requires `bool(active_draw.hand)` (at least one card drawn). Pre-draw NVM-to-landing falls through to the existing 3-way state machine (PAID DRAW / GATE VIEW / FREE DRAW) instead of misleading w. CONT DRAW that lands back on an empty picker. ## Tests (4 new ITs) `apps/gameboard/tests/integrated/test_views.py::MySeaViewTest`: - `test_burger_renders_glow_handoff_class_when_sea_first_draw_pending` — paid-draw entry to picker w. empty hand → burger has .glow-handoff. - `test_burger_omits_glow_handoff_when_hand_non_empty` — mid-draw → no .glow-handoff. - `test_burger_omits_glow_handoff_on_landing` — landing → no .glow-handoff (FREE DRAW handler seeds client-side instead). - `test_force_landing_hides_cont_draw_when_hand_empty` — pre-first-draw NVM → no CONT DRAW. (JS state-machine behaviour is verified visually; not Jasmine-tested since the IIFE lives inline on my_sea.html, not as a separate module.) ## Verification All 1374 IT+UT green (+4 from Phase 3). Visual verification of glow handoff + hand-complete auto-end confirmed. Code architected by Disco DeDisco Git commit message Co-Authored-By: Claude Sonnet 4.6 --- .../gameboard/tests/integrated/test_views.py | 78 +++++++++++ src/apps/gameboard/views.py | 13 +- src/static_src/scss/_burger.scss | 19 +++ src/static_src/scss/_gameboard.scss | 10 ++ .../apps/gameboard/_partials/_burger.html | 2 +- src/templates/apps/gameboard/my_sea.html | 121 ++++++++++++++++++ 6 files changed, 240 insertions(+), 3 deletions(-) diff --git a/src/apps/gameboard/tests/integrated/test_views.py b/src/apps/gameboard/tests/integrated/test_views.py index 9f7f5c1..043f805 100644 --- a/src/apps/gameboard/tests/integrated/test_views.py +++ b/src/apps/gameboard/tests/integrated/test_views.py @@ -1157,6 +1157,84 @@ class MySeaViewTest(TestCase): self.assertNotContains(response, 'id="id_my_sea_cont_draw_btn"') self.assertContains(response, 'id="id_draw_sea_btn"') + def test_burger_renders_glow_handoff_class_when_sea_first_draw_pending(self): + """When picker phase is active + hand is still empty (paid-draw + entry, or page reload of an empty picker), the burger btn renders + w. .glow-handoff so the first-draw nudge cycle starts on it. + Picker w. an empty hand requires the paid-draw context — the + FREE-DRAW client-side transition seeds the class via JS instead + (see my_sea.html DRAW SEA handler).""" + from apps.epic.models import personal_sig_cards + from apps.gameboard.models import MySeaDraw + from apps.lyric.models import Token + from datetime import timedelta + from django.utils import timezone + sig = personal_sig_cards(self.user)[0] + self.user.significator = sig + self.user.save(update_fields=["significator"]) + free_tok = Token.objects.create( + user=self.user, token_type=Token.FREE, + expires_at=timezone.now() + timedelta(days=30), + ) + MySeaDraw.objects.create( + user=self.user, spread="situation-action-outcome", + significator_id=sig.id, hand=[], + deposit_token_id=free_tok.pk, + deposit_reserved_at=timezone.now(), + ) + response = self.client.get(reverse("my_sea") + "?phase=picker") + # Burger btn rendered w. .glow-handoff (server-side seed) + self.assertContains( + response, + ' {% endblock content %}