From 7b7e80520ae4dd5450df7274b1af69de49064b2e Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Wed, 20 May 2026 01:34:03 -0400 Subject: [PATCH] =?UTF-8?q?My=20Sea=20iter=204c:=20drop=20LOCK=20HAND=20?= =?UTF-8?q?=E2=86=92=20AUTO=20DRAW=20+=20GATE=20VIEW;=20quota=20committed?= =?UTF-8?q?=20at=20first=20card=20draw=20(irrevocable);=20DEL=20clears=20h?= =?UTF-8?q?and=20but=20preserves=20row=20as=20quota=20tracker;=20per-place?= =?UTF-8?q?ment=20/lock=20POST=20upsert;=20lazy=20stale-row=20cleanup;=20s?= =?UTF-8?q?ig=20polarity=20+=20.btn-disabled=20=E2=86=92=20=C3=97;=20landi?= =?UTF-8?q?ng=20aperture=20bg=20revert=20to=20--priUser=20=E2=80=94=20Spri?= =?UTF-8?q?nt=205=20iter=204c=20of=20My=20Sea=20roadmap=20=E2=80=94=20TDD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major refactor of the iter-4b skeleton ahead of Sprint 6's token costs. Iter 4b's LOCK HAND model let users freely DEL + LOCK in a loop, bypassing the 1/day quota; iter 4c closes that loophole by committing quota at first-card-draw (manual via FLIP OR auto via AUTO DRAW) + preserving the MySeaDraw row through DEL so the 24h clock keeps running. ## Server `MySeaDraw` now plays double-duty: hand storage AND 24h quota tracker. - `HAND_SIZE_BY_SPREAD` module dict maps each spread slug to its expected hand size (mirrors DRAW_ORDER in JS). - `is_hand_complete` / `is_hand_empty` props drive view branching + template button states. - `delete_stale()` classmethod hard-deletes rows older than FREE_DRAW_COOLDOWN_HOURS. Called lazily from `active_draw_for` on every view access (rides user traffic; no scheduler needed) + via the new `delete_stale_my_sea_draws` management command (cron backstop). - `active_draw_for` prunes user's stale rows before lookup — auto-cleanup at the 24h mark per user spec ("sink 'em all at the 24hr mark and reinstate the FREE DRAW btn"). `my_sea_lock` is now a true upsert: - First POST creates the row (quota commit). - Subsequent POSTs UPDATE the existing row's hand (per-placement cadence — server stays current so navigate-away mid-draw still persists). - Spread-mismatch (attempted spread switch within quota window) → 409. - Empty/malformed hand → 400. - Response carries `{ok, next_free_draw_at, hand_complete}` for JS state transitions. `my_sea_delete` no longer deletes the row — clears the `hand` JSON only. `created_at` preserved so landing renders GATE VIEW (not FREE DRAW) until the row expires. Idempotent. `my_sea_gate` new stub view — returns 404 for now; lets the template wire up GATE VIEW button URLs in advance. Sprint 6 will replace this w. the gatekeeper token-deposit UX. `my_sea` view branches: 1. No sig → sign-gate 2. Active draw + non-empty hand (mid or complete) → picker phase w. saved hand 3. Active draw + empty hand (post-DEL) → landing phase w. GATE VIEW btn 4. No active draw → landing phase w. FREE DRAW btn ## Template + UX - Picker form col: removed LOCK HAND. Replaced w. `#id_sea_action_btn` — same DOM node, label + behavior keyed on `data-state`: - `auto-draw` → label "AUTO DRAW"; click opens shared guard portal ("Auto deal cards?"); OK → fill remaining slots client-side + single-POST commit to server (per user spec: "commit all six draws in the same POST" so navigate-away mid-animation still persists). - `gate-view` → label "GATE VIEW"; click navigates to /gameboard/my-sea/gate/ (Sprint 6). - JS transitions auto-draw → gate-view automatically when the hand fills (via FLIP or AUTO DRAW completion). - DEL btn: server-renders `.btn-disabled` pre-completion (per spec, the 1/day quota commits at first-card-draw — can't be refunded by an early DEL). JS removes `.btn-disabled` on hand completion. Post-completion click opens the shared guard portal; CONFIRM POSTs the delete endpoint (which clears hand server-side) + reloads to GATE VIEW landing. - Deck stacks remain click-responsive post-completion so the user sees the disabled-FLIP feedback (signalling "no more draws"); the FLIP click is gated on `_locked` flag. - Landing: primary nav btn is FREE DRAW (no active draw) or GATE VIEW (active draw exists w. empty hand). Both render as ` + {% if quota_spent %} + + {% else %} + + {% endif %} @@ -86,7 +94,7 @@ {# exist in one DOM). FLIP click delegates to SeaDeal. #} {# openStage(), which fills the slot AND opens the portaled #} {# stage modal w. SPIN / FYI controls. #} -