wallet shop: free ($0) RWS + Fiorentine decks — FREE ITEM claim unlocks to Game Kit — TDD
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful

- 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:
Disco DeDisco
2026-05-30 14:51:21 -04:00
parent d8377b57bc
commit 86a349b64e
13 changed files with 495 additions and 7 deletions

View File

@@ -131,12 +131,20 @@ body.page-gameboard {
// card 1 stays put. Tooltip portal is wired to the same `.token:hover` /
// `.kit-bag-deck:hover` triggers via JS so splay + tooltip-appearance
// co-activate.
//
// `.shop-tile-deck` (the wallet Shop applet's free-deck tiles) rides the
// SAME splay transforms — added to this trigger list rather than re-
// declaring the card-2/card-3 offsets, so the shop decks fan out on
// hover/active exactly like Game Kit.
.token.deck-variant:hover .deck-stack-icon,
.token.deck-variant:active .deck-stack-icon,
.token.deck-variant:focus .deck-stack-icon,
.kit-bag-deck:hover .deck-stack-icon,
.kit-bag-deck:active .deck-stack-icon,
.kit-bag-deck:focus .deck-stack-icon {
.kit-bag-deck:focus .deck-stack-icon,
.shop-tile-deck:hover .deck-stack-icon,
.shop-tile-deck:active .deck-stack-icon,
.shop-tile-deck:focus .deck-stack-icon {
.deck-stack-icon__card--2 { transform: translate(-5px, -2px) rotate(-12deg); }
.deck-stack-icon__card--3 { transform: translate( 5px, -2px) rotate( 12deg); }
}