wallet shop: free ($0) RWS + Fiorentine decks — FREE ITEM claim unlocks to Game Kit — TDD
- model: DeckVariant.free_in_shop flag (0015 schema); data migration 0016
seeds RWS + Minchiate Fiorentine True (Earthman stays False — it's auto-
granted at signup, not shopped)
- view: _free_decks_for decorates the free-in-shop catalog w. a per-user
.owned flag; shop_claim_free POST endpoint adds the deck to unlocked_decks
(idempotent M2M add) — the free_in_shop filter is the guard that stops the
$0 endpoint unlocking paid/auto-granted decks (404 otherwise). free_decks
wired into both the wallet view + toggle_wallet_applets HX context
- url: wallet/shop/claim (action, no trailing slash)
- template: free-deck tiles reuse the deck's own Game Kit tooltip prose
(name / card-count / description / stock-version line) + a $0 .tt-price
pinned top-right like paid tiles; .tt-micro carries .tt-free-btn (FREE
ITEM) or the same .tt-already-owned pill once owned; reuses
_deck_stack_icon.html
- js: wallet-shop.js _onFreeClick → _doClaimFree POSTs deck_slug → reload
(server-rendered owned pill, same posture as the BUY reload). No guard
portal — free = one-click. Rides the SAME delegated roots as BUY +
idempotent wiring
- css: FREE ITEM wraps to 2 lines like BUY ITEM (extend the mini-portal
.tt-buy-btn white-space:normal rule to .tt-free-btn); shop deck tiles get
the Game Kit fan-out on hover/active by adding .shop-tile-deck to the
.deck-stack-icon splay trigger list — DRY, no transform duplication
- tests: 8 ITs (shop_claim_free behaviors + free_decks context owned flag);
FT claims RWS → 'Already owned' swap → id_kit_tarot_deck appears in Game
Kit; 3 Jasmine specs F1-F3 (claim POST / no-guard / idempotent wiring);
679 dashboard+epic green, no regressions
- trap: hover-hidden microtooltip btn → .text is '' under Selenium; read
get_attribute('textContent') instead [[feedback-selenium-opacity-zero]]
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -61,5 +61,44 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% comment %}
|
||||
Free decks (RWS + Minchiate Fiorentine) — $0 shop items that grant a
|
||||
DeckVariant instead of a token. They reuse the DECK's own Game Kit
|
||||
tooltip prose (name / card-count / description / stock-version line)
|
||||
rather than the ShopItem tooltip, plus a `.tt-price` of "$0". The
|
||||
micro carries a `.tt-free-btn` (claim) or the same `.tt-already-owned`
|
||||
pill once unlocked. wallet-shop.js POSTs the claim to /shop/claim,
|
||||
which adds the deck to `unlocked_decks` (→ Game Kit on /gameboard/).
|
||||
{% endcomment %}
|
||||
{% for deck in free_decks %}
|
||||
<div
|
||||
id="id_shop_{{ deck.slug }}"
|
||||
class="shop-tile shop-tile-deck"
|
||||
data-deck-slug="{{ deck.slug }}"
|
||||
>
|
||||
{% include "apps/gameboard/_partials/_deck_stack_icon.html" %}
|
||||
<div class="tt">
|
||||
<h4 class="tt-title">
|
||||
<span>{{ deck.name }}</span>
|
||||
<span class="tt-price">$0</span>
|
||||
</h4>
|
||||
<p class="tt-description">{{ deck.card_count }}-card Tarot deck{% if deck.is_polarized %} <span class="tt-x2">(×2)</span>{% endif %}</p>
|
||||
{% if deck.description %}<p class="tt-shoptalk"><em>{{ deck.description }}</em></p>{% endif %}
|
||||
<p class="tt-shoptalk">Stock version <span class="tt-subcounter">(0 substitutions)</span></p>
|
||||
</div>
|
||||
<div class="tt-micro">
|
||||
{% if deck.owned %}
|
||||
<span class="tt-already-owned">Already owned</span>
|
||||
{% else %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary tt-free-btn"
|
||||
data-deck-slug="{{ deck.slug }}"
|
||||
data-item-name="{{ deck.name }}"
|
||||
>FREE ITEM</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user