fix: significator_reversed=polarity bug + Pattern B name-swap rendering + qualifier-aware applet faces + sticky PAID DRAW + cooldown anchor on User + stat-block polarity unification across Sig/Sea/Fan/applets
Five-thread sprint atop 53cd7af; all 1238 IT/UT green (no FTs run per [[feedback-ft-run-discipline]]).
**Thread 1 — User.significator_reversed is the POLARITY axis, not orientation.** The saved sig was rendering as a gravity reversal when the user saved a levity emanation. Root cause: `my_sign.html` JS post-save load called `_toggleOrientation()` whenever `revInput.value==='1'` (SPIN-ing a card whose flag only meant "polarity=levity"); `_applet-my-sign.html` applied `.stage-card--reversed` + `keywords_reversed` for the same flag. Fix: JS drops the `_toggleOrientation()` call (saved sigs are always upright in their polarity, never spun); the applet drops the rotation class, swaps to `my-sign-applet-card--{levity,gravity}` modifier, and always renders `keywords_upright` / "Emanation". `data-polarity` cascades correctly. Memory: [[feedback-significator-reversed-is-polarity]].
**Thread 2 — qualifier rendering on the My Sign + My Sea applets.** Both applets were rendering name only — no qualifier word. Added `TarotCard.applet_face(polarity, reversed)` (model method) + `User.sig_face` (delegator for the saved sig) returning `{title, qualifier, qualifier_first}` payload that mirrors `populateCard` in `stage-card.js`. `latest_draw_slots()` augments each slot dict w. `face`. Templates render `.fan-card-qualifier` + `.fan-card-name` in the order the payload dictates (non-Major: qualifier-above-title; Major+qualifier: title-with-trailing-comma above qualifier; polarity-split: single-line title). Typography matched to title (same bold, same size, same color via `color: inherit` w. polarity-pin at 0,3,0 specificity to beat `_card-deck.scss:376-383`'s 0,2,0 `.fan-card-face .fan-card-name` rule that out-cascades when loaded after gameboard).
**Thread 3 — My Sea cooldown bugs.** Two: (a) PAID DRAW button reverted to FREE DRAW after one navigation cycle because `my_sea_paid_draw` deleted the row at commit time — without a row, `quota_spent=False` on next render. (b) Brief's "next free draw at" was anchored to the most recent paid draw, not the original free draw. Fix: new `User.last_free_draw_at` field (set in `my_sea_lock` when a fresh row lands AND user wasn't already in cooldown — i.e., this is a tokenless free draw); paid draws NEVER touch it. New `MySeaDraw.paid_through_at` field stamped at commit time + cleared in `my_sea_lock` when the first card of the paid session lands (one-shot credit per user-spec: "each redraw needs a new token"). `my_sea_paid_draw` no longer deletes the row — clears hand+deposit, sets `paid_through_at`, redirects to `?phase=picker`. View's landing button uses `show_paid_draw` (`deposit_reserved OR paid_through_at`) so PAID DRAW persists across navigation until the paid session's first card lands. Brief reads `user.next_free_draw_at` (= `last_free_draw_at + 24h`) w. row-fallback for legacy test fixtures. 11 new ITs (`MySeaCooldownAnchoredToFreeDrawTest`, `UserFreeDrawCooldownPropertyTest`, expanded `MySeaPhasePickerQueryParamTest`, expanded `my_sea_lock` tests). Existing `test_paid_draw_deletes_active_draw_row` rewritten as `test_paid_draw_preserves_row_and_sets_paid_through_at`. 1 new FT pinning the navigation-persistence regression. Memory: [[feedback-my-sea-cooldown-design]].
**Thread 4 — Pattern B / B' Major reversal name-swap.** Card 34's My Sea applet rendered the reversal as "Animal Powers, Patrilineage" (Patrilineage treated as a qualifier). User-locked semantics: for Majors w. BOTH polarity qualifiers AND a `reversal_qualifier`, the `reversal_qualifier` field carries the NAME SWAP for the reversal face; the polarity qualifier persists across both faces. Affected cards: 2-5 (Pope/Horseman), 10-15 (Elements), 22-33 (Zodiac → Houses), 34-35 (Lunars), 41 (Asteroid Belt). Pattern B': cards 16-18 (Realms — Disco Inferno → Shame etc.) reversal face drops the qualifier entirely; new `TarotCard.reversal_drops_qualifier` BooleanField marks these (set True on 16-18 via `epic/0010_set_reversal_drops_qualifier_realms.py` data migration). `applet_face()` + `stage-card.js::populateCard` both branch on `arcana==MAJOR AND reversal_qualifier AND polarity_qualifier` → Pattern B/B' rendering. Non-Major `reversal_qualifier` semantics unchanged (middle court: "Queen of Crowns" stays as title, "Vacant" renders as the reversal-face qualifier). New data attr `data-reversal-drops-qualifier` added to `my_sign.html`, `_sig_select_overlay.html`, `_tarot_fan.html` so stage-card.js can read it via dataset. `card_dict()` extended w. the same field. 3 new UTs (`TarotCardAppletFaceTest`: Pattern B name swap, Pattern B' qualifier drop, non-Major regression pin). Old `test_reversed_uses_reversal_qualifier_with_comma_for_major` deleted (it pinned the conflated old behavior).
**Thread 5 — unified card + stat-block polarity convention across all 6 surfaces** (Sig Select, Sea Select stage modal, Game Kit fan, My Sign applet, My Sea applet, room.html). User-locked: card and adjacent stat block always carry OPPOSITE-polarity bgs (gravity card --priUser → stat block --secUser; levity card --secUser → stat block --priUser). `.is-reversed` (SPIN) is preview-only — never shifts bg. Per-card scoping (NOT page-wide) — drawn sea cards each carry their own polarity from the deck stack; `.sea-stage--{gravity,levity}` parent rules + `.tarot-fan-wrap[data-polarity=...]` parent rules cascade to their respective stat blocks. `game-kit.js` `_populateStage` + `_flipActive` mirror `_polarity` onto `.tarot-fan-wrap` so SCSS can pick it up without touching the stat block directly. Sea-stat-block was previously stuck at --priUser regardless of polarity; fan-stage-block ditto. Both inverted now. Memory: [[feedback-card-polarity-convention]].
**Bundled polish across the same surfaces** (each one a small visible item the user spotted during the sprint):
- My Sign applet card: levity polarity flips bg to --secUser + border to --priUser + ink to --quiUser (matches page stage card at `_card-deck.scss:1002-1019`). Gravity stat block flips to --secUser bg w. --quiUser label ink + --priUser keyword ink (matches `_card-deck.scss:1042-1046`).
- Qualifier + title share typography (font-size, weight, polarity-color, text-wrap). `.fan-card-face { gap: 0 }` + `line-height: 1.15` so qualifier sits directly above title at the title's own line-height. `.fan-card-arcana { margin-top }` reserves breathing room below.
- `.fan-card-qualifier:empty { display: none }` collapses polarity-split / Major-no-qualifier cards cleanly.
**Memory recorded**:
1. [[feedback-ft-run-discipline]] — re-pinned 2026-05-23 after I burned a multi-minute full-FT-suite run mid-task. Default loop is IT/UT only. FT runs must be ONE test method by full dotted path; never a whole file; never re-run an already-green FT.
2. [[feedback-significator-reversed-is-polarity]] — the flag is polarity (FLIP), not orientation (SPIN); SPIN never persisted; saved sigs always upright in their polarity.
3. [[feedback-card-polarity-convention]] — opposite-polarity stat-block bg, per-card scoping, SPIN never shifts bg, the full color table.
4. [[feedback-my-sea-cooldown-design]] — cooldown anchored to User.last_free_draw_at, paid draws never reset it, paid_through_at is a sticky one-shot credit, button state machine.
**Files** (every uncommitted file folded in — session work + pre-existing modifications):
Models / migrations:
- `apps/epic/models.py` — `applet_face()` extended w. Pattern B/B' branches; new `reversal_drops_qualifier` BooleanField.
- `apps/epic/migrations/0009_reversal_drops_qualifier.py` — schema.
- `apps/epic/migrations/0010_set_reversal_drops_qualifier_realms.py` — data migration setting flag True on cards 16-18.
- `apps/epic/utils.py` — `card_dict` carries `reversal_drops_qualifier`.
- `apps/gameboard/models.py` — `paid_through_at` field; `latest_draw_slots()` attaches `face` payload per slot; `active_draw_for` docstring refreshed.
- `apps/gameboard/migrations/0003_myseadraw_paid_through_at.py` — schema.
- `apps/lyric/models.py` — `last_free_draw_at` field; `free_draw_cooldown_active` + `next_free_draw_at` props; `sig_face` delegator.
- `apps/lyric/migrations/0013_user_last_free_draw_at.py` — schema.
Views:
- `apps/gameboard/views.py` — `my_sea` view button state machine (`show_paid_draw` / `show_gate_view` / `show_picker`); `my_sea_lock` sets `last_free_draw_at` on free-draw + clears `paid_through_at` on paid-session first card; `my_sea_paid_draw` preserves row + stamps `paid_through_at`.
JS:
- `apps/epic/static/apps/epic/stage-card.js` — `fromDataset` reads `reversal_drops_qualifier`; `populateCard` branches Pattern B / B' for the reversal face.
- `apps/gameboard/static/apps/gameboard/game-kit.js` — mirrors `_polarity` onto `.tarot-fan-wrap` so SCSS can invert the fan-stage-block bg per active card.
Templates:
- `templates/apps/billboard/my_sign.html` — JS drops `_toggleOrientation()` on saved-sig load; sig-card grid carries `data-reversal-drops-qualifier`.
- `templates/apps/billboard/_partials/_applet-my-sign.html` — drops `stage-card--reversed`, adds polarity modifier, renders qualifier via `sig_face` payload, always shows Emanation keywords + label.
- `templates/apps/gameboard/_partials/_applet-my-sea.html` — renders qualifier via `slot.face` payload (Pattern B/B' aware).
- `templates/apps/gameboard/_partials/_sig_select_overlay.html` + `_tarot_fan.html` — `data-reversal-drops-qualifier` added to sig-card grid + fan cards.
- `templates/apps/gameboard/my_sea.html` — landing button form swaps to `show_paid_draw` / `show_gate_view` flags.
SCSS:
- `static_src/scss/_billboard.scss` — My Sign applet card polarity inversion (levity bg + ink), polarity stat-block inversion (gravity → --secUser bg), qualifier+title shared typography, polarity-aware ink via `color: inherit`.
- `static_src/scss/_card-deck.scss` — sea-stat-block polarity rules (`.sea-stage--gravity/levity .sea-stat-block`), fan-stage-block polarity rules (`.tarot-fan-wrap[data-polarity] .fan-stage-block`), comments documenting fallback bgs.
- `static_src/scss/_gameboard.scss` — `.my-sea-slot--filled.--gravity/--levity` pin `color: inherit` on `.fan-card-corner`, `.fan-card-qualifier`, `.fan-card-name`, `.fan-card-arcana` (0,3,0 beats global 0,2,0). Slot label keeps original wrap-sibling placement w. `z-index: 2` to render above the dotted bottom border on empty slots.
Tests:
- `apps/billboard/tests/integrated/test_views.py` — updated `test_my_sign_applet_renders_card_when_sig_set` to assert polarity modifier + qualifier text + Emanation-only; new `test_my_sign_applet_renders_gravity_qualifier_when_not_reversed`.
- `apps/epic/tests/unit/test_models.py` — `TarotCardAppletFaceTest` (Pattern B name swap, Pattern B' qualifier drop, non-Major regression pin, polarity-split, reversal qualifier fallback).
- `apps/gameboard/tests/integrated/test_views.py` — `MySeaCooldownAnchoredToFreeDrawTest` (5 tests pinning cooldown anchor on User, sticky PAID DRAW, paid-through credit consumption); `UserFreeDrawCooldownPropertyTest` (4 tests); expanded `MySeaPhasePickerQueryParamTest` w. paid-through-shows-PAID-DRAW-btn assertion; expanded `my_sea_lock` tests (free-draw-anchors-last_free_draw_at, paid-draw-leaves-anchor-alone, first-paid-card-consumes-credit); My Sea applet qualifier IT (Major comma format end-to-end).
- `functional_tests/test_game_my_sea.py` — `test_paid_draw_commits_token_and_redirects_to_picker` updated to assert row preservation + paid_through_at stamping; new `test_paid_draw_btn_persists_after_navigation_without_card_draw` pinning the user-reported regression.
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,21 +3,42 @@
|
||||
style="--applet-cols: {{ entry.applet.grid_cols }}; --applet-rows: {{ entry.applet.grid_rows }};"
|
||||
>
|
||||
<h2><a href="{% url 'billboard:my_sign' %}">My Sign</a></h2>
|
||||
<div class="my-sign-applet-body">
|
||||
<div class="my-sign-applet-body"
|
||||
data-polarity="{% if request.user.significator_reversed %}levity{% else %}gravity{% endif %}">
|
||||
{% if request.user.significator %}
|
||||
{% with card=request.user.significator %}
|
||||
{# Mirrors the my_sign.html `.sig-stage-card` layout — corner #}
|
||||
{# top-left, full name in the face, polarity-reversed mirror #}
|
||||
{# at the bottom (pre-rotated). Sized to fill the applet's #}
|
||||
{# vertical aperture via container queries in `_billboard.scss`. #}
|
||||
<div class="my-sign-applet-card{% if request.user.significator_reversed %} stage-card--reversed{% endif %}"
|
||||
{# top-left, name + polarity qualifier in the face, mirror #}
|
||||
{# corner bottom-right (pre-rotated). Sized to fill the #}
|
||||
{# applet's vertical aperture via container queries in #}
|
||||
{# `_billboard.scss`. `significator_reversed` is the POLARITY #}
|
||||
{# axis (True ↔ levity), so the saved sig is always upright #}
|
||||
{# in its polarity — no `.stage-card--reversed` rotation. #}
|
||||
<div class="my-sign-applet-card my-sign-applet-card--{% if request.user.significator_reversed %}levity{% else %}gravity{% endif %}"
|
||||
data-card-id="{{ card.id }}">
|
||||
<div class="fan-card-corner fan-card-corner--tl">
|
||||
<span class="fan-corner-rank">{{ card.corner_rank }}</span>
|
||||
{% if card.suit_icon %}<i class="fa-solid {{ card.suit_icon }}"></i>{% endif %}
|
||||
</div>
|
||||
<div class="fan-card-face">
|
||||
<p class="fan-card-name">{{ card.name_title }}</p>
|
||||
{# `request.user.sig_face` is the rendering payload from #}
|
||||
{# `TarotCard.applet_face()` — mirrors `populateCard` in #}
|
||||
{# `stage-card.js:135-144`: #}
|
||||
{# • Polarity-split (cards 48-49, trumps 19-21): #}
|
||||
{# single-line title, qualifier blank. #}
|
||||
{# • Major + qualifier: title carries a trailing #}
|
||||
{# comma + qualifier renders BELOW. #}
|
||||
{# • Non-Major (middle court, Schizo / Nomad w. no #}
|
||||
{# qualifier): qualifier renders ABOVE the title. #}
|
||||
{% with face=request.user.sig_face %}
|
||||
{% if face.qualifier_first %}
|
||||
<p class="fan-card-qualifier">{{ face.qualifier }}</p>
|
||||
<p class="fan-card-name">{{ face.title }}</p>
|
||||
{% else %}
|
||||
<p class="fan-card-name">{{ face.title }}</p>
|
||||
<p class="fan-card-qualifier">{{ face.qualifier }}</p>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<p class="fan-card-arcana">{{ card.get_arcana_display }}</p>
|
||||
</div>
|
||||
<div class="fan-card-corner fan-card-corner--br">
|
||||
@@ -26,28 +47,17 @@
|
||||
</div>
|
||||
</div>
|
||||
{# Stat block — same shape as my_sign.html's `.sig-stat-block` #}
|
||||
{# (Emanation/Reversal face label + keyword list) but no SPIN #}
|
||||
{# or FYI buttons since the applet is a read-only preview. The #}
|
||||
{# face shown is keyed off significator_reversed: True → #}
|
||||
{# reversal keywords (labelled "Reversal"), False → upright #}
|
||||
{# (labelled "Emanation"). Mirrors the FYI panel populated by #}
|
||||
{# `StageCard.populateKeywords` in my_sign.html's JS init. #}
|
||||
{# (Emanation face label + keyword list) but no SPIN/FYI btns #}
|
||||
{# since the applet is a read-only preview. Saved sigs persist #}
|
||||
{# only the polarity axis (FLIP), never the orientation axis #}
|
||||
{# (SPIN), so always render the upright/emanation face. #}
|
||||
<div class="my-sign-applet-stat-block">
|
||||
{% if request.user.significator_reversed %}
|
||||
<p class="stat-face-label">Reversal</p>
|
||||
<ul class="stat-keywords">
|
||||
{% for kw in card.keywords_reversed %}
|
||||
<li>{{ kw }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="stat-face-label">Emanation</p>
|
||||
<ul class="stat-keywords">
|
||||
{% for kw in card.keywords_upright %}
|
||||
<li>{{ kw }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<p class="stat-face-label">Emanation</p>
|
||||
<ul class="stat-keywords">
|
||||
{% for kw in card.keywords_upright %}
|
||||
<li>{{ kw }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
|
||||
@@ -139,6 +139,7 @@
|
||||
data-levity-qualifier="{{ card.levity_qualifier }}"
|
||||
data-gravity-qualifier="{{ card.gravity_qualifier }}"
|
||||
data-reversal-qualifier="{{ card.reversal_qualifier }}"
|
||||
data-reversal-drops-qualifier="{{ card.reversal_drops_qualifier|yesno:'true,false' }}"
|
||||
data-levity-emanation="{{ card.levity_emanation }}"
|
||||
data-gravity-emanation="{{ card.gravity_emanation }}"
|
||||
data-levity-reversal="{{ card.levity_reversal }}"
|
||||
@@ -379,22 +380,18 @@
|
||||
|
||||
// On-load: if user has a saved sig, populate the stage preview AND
|
||||
// reveal the stat block (via .sig-stage--frozen) so the saved card
|
||||
// appears alongside its emanation/reversal keywords — the page is
|
||||
// read-only on landing while a sig is committed (hex is server-side
|
||||
// hidden, DEL is the only action). The picker grid stays hidden
|
||||
// until SCAN SIGN — but SCAN SIGN itself is gone in this state, so
|
||||
// the user must DEL → reload to ever re-enter picker phase.
|
||||
// appears alongside its emanation keywords — the page is read-only
|
||||
// on landing while a sig is committed (hex is server-side hidden,
|
||||
// DEL is the only action). The picker grid stays hidden until SCAN
|
||||
// SIGN — but SCAN SIGN itself is gone in this state, so the user
|
||||
// must DEL → reload to ever re-enter picker phase.
|
||||
//
|
||||
// If the saved sig is reversed, also call _toggleOrientation() once
|
||||
// so the stage card visually rotates 180° + the stat block swaps to
|
||||
// its reversal face. The server-side `data-polarity` attribute on
|
||||
// .my-sign-page already reflects the reversed flag (drives polarity-
|
||||
// themed colors via the [data-polarity=...] CSS rules) but the
|
||||
// visual rotation lives in the `stage-card--reversed` class which
|
||||
// is JS-applied. Without this call the stage card lied: saved-
|
||||
// reversed sigs rendered upright on landing while the My Sign
|
||||
// applet (template-driven, reads significator_reversed directly)
|
||||
// correctly rotated them — surfaces disagreed.
|
||||
// `significator_reversed` is the POLARITY axis (reversed=True ↔
|
||||
// levity), already reflected in `data-polarity` on the page wrapper
|
||||
// and threaded into `_polarity()` so `_populateStage` paints the
|
||||
// correct levity/gravity qualifier on the upright face. The SPIN
|
||||
// axis (.stage-card--reversed rotation) is preview-only and is NOT
|
||||
// persisted — saved sigs always render upright in their polarity.
|
||||
var savedId = pageEl.dataset.currentCardId;
|
||||
if (savedId && grid) {
|
||||
var savedCardEl = grid.querySelector(
|
||||
@@ -402,9 +399,6 @@
|
||||
if (savedCardEl) {
|
||||
_populateStage(savedCardEl);
|
||||
stage.classList.add('sig-stage--frozen');
|
||||
if (revInput.value === '1') {
|
||||
_toggleOrientation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
<div class="my-sea-slot-wrap">
|
||||
{# Mirrors the my_sign.html `.sig-stage-card` layout — #}
|
||||
{# corner top-left, face w. name + arcana, mirror corner #}
|
||||
{# bottom-right. Sized to fill the applet height via #}
|
||||
{# container queries in `_gameboard.scss`. #}
|
||||
{# bottom-right. Label is a SIBLING of the slot inside #}
|
||||
{# the wrap so it sits BELOW the slot box (user-spec #}
|
||||
{# 2026-05-23: same position as the my_sea.html picker's #}
|
||||
{# `.sea-pos-label`). #}
|
||||
<div class="my-sea-slot my-sea-slot--filled my-sea-slot--{{ slot.polarity }}{% if slot.reversed %} my-sea-slot--reversed{% endif %}"
|
||||
data-position="{{ slot.position }}"
|
||||
data-card-id="{{ slot.card.id }}">
|
||||
@@ -33,7 +35,24 @@
|
||||
{% if slot.card.suit_icon %}<i class="fa-solid {{ slot.card.suit_icon }}"></i>{% endif %}
|
||||
</div>
|
||||
<div class="fan-card-face">
|
||||
<p class="fan-card-name">{{ slot.card.name_title }}</p>
|
||||
{# `slot.face` is the rendering payload from `TarotCard. #}
|
||||
{# applet_face()` — mirrors `populateCard` in #}
|
||||
{# `stage-card.js`: #}
|
||||
{# • Polarity-split (cards 19-21, 48-49): single-line #}
|
||||
{# title, qualifier blank. #}
|
||||
{# • Pattern B Major (2-5, 10-15, 22-35, 41): swapped #}
|
||||
{# reversal name + polarity qualifier carried. #}
|
||||
{# • Pattern B' Major (16-18): swapped reversal name, #}
|
||||
{# no qualifier on reversal. #}
|
||||
{# • Non-Major: qualifier ABOVE the title. #}
|
||||
{# Empty `.fan-card-qualifier` is hidden by `:empty` CSS. #}
|
||||
{% if slot.face.qualifier_first %}
|
||||
<p class="fan-card-qualifier">{{ slot.face.qualifier }}</p>
|
||||
<p class="fan-card-name">{{ slot.face.title }}</p>
|
||||
{% else %}
|
||||
<p class="fan-card-name">{{ slot.face.title }}</p>
|
||||
<p class="fan-card-qualifier">{{ slot.face.qualifier }}</p>
|
||||
{% endif %}
|
||||
<p class="fan-card-arcana">{{ slot.card.get_arcana_display }}</p>
|
||||
</div>
|
||||
<div class="fan-card-corner fan-card-corner--br">
|
||||
|
||||
@@ -77,6 +77,7 @@ Context: sig_cards, user_polarity, user_seat, sig_reserve_url, sig_reservations_
|
||||
data-levity-qualifier="{{ card.levity_qualifier }}"
|
||||
data-gravity-qualifier="{{ card.gravity_qualifier }}"
|
||||
data-reversal-qualifier="{{ card.reversal_qualifier }}"
|
||||
data-reversal-drops-qualifier="{{ card.reversal_drops_qualifier|yesno:'true,false' }}"
|
||||
data-levity-emanation="{{ card.levity_emanation }}"
|
||||
data-gravity-emanation="{{ card.gravity_emanation }}"
|
||||
data-levity-reversal="{{ card.levity_reversal }}"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
data-levity-qualifier="{{ card.levity_qualifier }}"
|
||||
data-gravity-qualifier="{{ card.gravity_qualifier }}"
|
||||
data-reversal-qualifier="{{ card.reversal_qualifier }}"
|
||||
data-reversal-drops-qualifier="{{ card.reversal_drops_qualifier|yesno:'true,false' }}"
|
||||
data-levity-emanation="{{ card.levity_emanation }}"
|
||||
data-gravity-emanation="{{ card.gravity_emanation }}"
|
||||
data-levity-reversal="{{ card.levity_reversal }}"
|
||||
|
||||
@@ -52,14 +52,21 @@
|
||||
<div class="table-hex-border">
|
||||
<div class="table-hex">
|
||||
<div class="table-center">
|
||||
{% if deposit_reserved %}
|
||||
{% if show_paid_draw %}
|
||||
{# PAID DRAW — two underlying states collapse into one #}
|
||||
{# button (user-spec 2026-05-23): #}
|
||||
{# • `deposit_reserved` → POST commits the deposited #}
|
||||
{# token via `my_sea_paid_draw` + redirects to picker. #}
|
||||
{# • `paid_through` (token already spent this cycle, #}
|
||||
{# hand still empty) → POST is a no-op commit branch #}
|
||||
{# in the view; the view redirects to picker anyway. #}
|
||||
<form method="POST" action="{% url 'my_sea_paid_draw' %}" style="display:contents">
|
||||
{% csrf_token %}
|
||||
<button type="submit"
|
||||
id="id_my_sea_paid_draw_btn"
|
||||
class="btn btn-primary">PAID<br>DRAW</button>
|
||||
</form>
|
||||
{% elif quota_spent %}
|
||||
{% elif show_gate_view %}
|
||||
<button id="id_my_sea_gate_view_btn"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
|
||||
Reference in New Issue
Block a user