My Sea iter 6c: bud-btn invite stub + #id_my_sea_menu gear (NVM-only, %applet-menu-styled, on both /gameboard/my-sea/ and the gatekeeper) + PAID DRAW now deletes the row and redirects to ?phase=picker so the user drops straight into picking cards instead of looping back to GATE VIEW — Sprint 5 iter 6c of My Sea roadmap — TDD

Bundled fix for the PAID-DRAW-loops-to-GATE-VIEW bug surfaced 2026-05-20 in
live testing: previously the view reset `created_at = now()` + cleared the
hand, but the row's continued existence meant `quota_spent=True` on the
next render → landing rendered GATE VIEW → user clicked it → back to
gatekeeper → loop.

Now PAID DRAW does `active_draw.delete()` after debiting the token + then
redirects to `/gameboard/my-sea/?phase=picker`. The my_sea view honors
`?phase=picker` (only when no active_draw exists — can't bypass
post-DEL GATE VIEW) by forcing `show_picker=True` so the user lands in
the picker ready to draw. First card draw creates a fresh row w. fresh
`created_at`, starting the new 24h quota cycle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-20 09:47:47 -04:00
parent 1e37fe1475
commit 4417b8c972
9 changed files with 267 additions and 41 deletions

View File

@@ -0,0 +1,32 @@
{% load static %}
{# Sprint 6 iter 6c — bud-btn invite panel on the my-sea gatekeeper. #}
{# Mirrors `_bud_invite_panel.html` (room) but POSTs to a stub view — #}
{# real async multi-user invite is deferred to a future sprint, so OK #}
{# returns a 'Multiplayer my-sea coming soon' Brief banner. #}
<button id="id_bud_btn" type="button" aria-label="Invite a friend">
<i class="fa-solid fa-handshake"></i>
</button>
<div id="id_bud_panel">
<input id="id_recipient"
name="recipient"
type="text"
placeholder="friend@example.com or username"
autocomplete="off">
<button id="id_bud_ok" type="button" class="btn btn-confirm">OK</button>
</div>
<div id="id_bud_suggestions" class="bud-suggestions" hidden></div>
<script src="{% static 'apps/billboard/bud-autocomplete.js' %}"></script>
<script src="{% static 'apps/billboard/bud-btn.js' %}"></script>
<script>
bindBudBtn({
submitUrl: '{% url "my_sea_invite" %}',
autocompleteUrl: '{% url "billboard:search_buds" %}',
onSuccess: function (data) {
if (window.Brief && data.brief) Brief.showBanner(data.brief);
},
});
</script>

View File

@@ -0,0 +1,9 @@
{# Sprint 6 iter 6c — gear-btn on /gameboard/my-sea/ + the gatekeeper. #}
{# NVM-only menu (no DEL, no BYE) — gear is the "back out without #}
{# committing" affordance; NVM nav-backs to /gameboard/ mirroring the #}
{# room's gear-menu convention. Rendered unconditionally (no active- #}
{# draw guard) so fresh users + post-DEL states still see it. #}
<div id="id_my_sea_menu" style="display:none">
<a href="{% url 'gameboard' %}" class="btn btn-cancel">NVM</a>
</div>
{% include "apps/applets/_partials/_gear.html" with menu_id="id_my_sea_menu" %}

View File

@@ -900,5 +900,9 @@
</script>
{% endif %}
{% endif %}
{# Sprint 6 iter 6c — gear-btn lives on every my-sea page state #}
{# (sign-gate / landing / picker). NVM-only menu mirrors the #}
{# gatekeeper's gear; "back out to /gameboard/" affordance. #}
{% include "apps/gameboard/_partials/_my_sea_gear.html" %}
</div>
{% endblock content %}

View File

@@ -82,5 +82,19 @@
</div>
</div>
</div>
{# Sprint 6 iter 6c — bud-btn invite stub (multiplayer-coming-soon) #}
{# + gear-btn NVM-only menu. Both render outside .gate-modal so the #}
{# bud-btn lives at viewport fixed position (per `_bud.scss`) and #}
{# the gear-btn sits atop `#id_kit_btn` in the bottom-right corner. #}
{% include "apps/gameboard/_partials/_my_sea_bud_panel.html" %}
{% include "apps/gameboard/_partials/_my_sea_gear.html" %}
</div>
{% endblock content %}
{% block scripts %}
{# Brief module — needed by _my_sea_bud_panel's OK handler so the #}
{# 'Multiplayer my-sea coming soon' banner shows on a successful #}
{# (stub) invite. #}
<script src="{% static 'apps/dashboard/note.js' %}"></script>
{% endblock scripts %}