2026-03-09 14:40:34 -04:00
|
|
|
|
.token {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: inline-block;
|
2026-03-09 22:42:30 -04:00
|
|
|
|
cursor: help;
|
2026-03-09 14:40:34 -04:00
|
|
|
|
color: rgba(var(--terUser), 1);
|
|
|
|
|
|
|
|
|
|
|
|
.token-tooltip {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: 125%;
|
2026-03-09 23:48:20 -04:00
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translateX(-50%);
|
2026-03-09 14:40:34 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-16 00:14:47 -04:00
|
|
|
|
&:hover .token-tooltip {
|
|
|
|
|
|
display: block; // legacy fallback; .tt is JS-portal-only (no CSS hover)
|
2026-03-09 14:40:34 -04:00
|
|
|
|
}
|
2026-03-09 23:48:20 -04:00
|
|
|
|
}
|
2026-03-10 14:11:53 -04:00
|
|
|
|
|
2026-03-11 00:58:24 -04:00
|
|
|
|
.token--empty {
|
|
|
|
|
|
cursor: help;
|
|
|
|
|
|
|
|
|
|
|
|
> i { opacity: 0.4; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
aperture architecture: lift the page-locking foundation (html/body/.container overflow:hidden + flex-column + min-height:0; .row flex-shrink:0) from 5 per-page SCSS files into _base.scss — was opt-in per page via `body.page-billboard` / `page-dashboard` / `page-gameboard` / `page-sky` / `page-wallet` etc., with 5 near-identical `html:has(body.page-X) { overflow: hidden }` + `body.page-X { … }` blocks duplicating the same rules; any page that forgot to set `page_class` in its view context (e.g. `epic.tarot_deck` — never set) rendered without the aperture, letting applet borders + titles clip past the fixed navbar/footer sidebars at narrower viewports; foundation now universal, page-specific overrides stay scoped — gameboard keeps `.container { overflow: clip }` (Firefox seat-tooltip scroll-anchoring quirk) + billboard/dashboard/gameboard keep `.row { margin-bottom: -1rem }` (h2-row tightening); page_class context vars + body class hooks preserved (FTs at test_bud_btn.py:370 / :379 still assert on them); regression gate: 60 layout-sensitive FTs (billboard, my_buds, bud_btn, applet_my_posts, dashboard, wallet, gameboard, layout_and_styling, jasmine) + 43 room FTs (gatekeeper_bud_btn, room_gatekeeper, room_sky_select, sharing) all green
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:16:12 -04:00
|
|
|
|
// Aperture foundation lives universally in _base.scss; nothing
|
|
|
|
|
|
// wallet-specific to override.
|
2026-03-11 00:58:24 -04:00
|
|
|
|
|
|
|
|
|
|
.wallet-page {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.wallet-tokens {
|
|
|
|
|
|
display: flex;
|
2026-03-11 14:50:08 -04:00
|
|
|
|
flex-direction: column;
|
2026-03-11 00:58:24 -04:00
|
|
|
|
overflow: visible;
|
|
|
|
|
|
|
2026-03-11 14:50:08 -04:00
|
|
|
|
.token-row {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-evenly;
|
|
|
|
|
|
overflow: visible;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 00:58:24 -04:00
|
|
|
|
.token {
|
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.token:hover .token-tooltip { display: none; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#id_payment_methods {
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-10 14:11:53 -04:00
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
|
.token .token-tooltip {
|
|
|
|
|
|
width: 13rem;
|
|
|
|
|
|
max-width: 90vw;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
transform: none;
|
|
|
|
|
|
}
|
2026-03-11 00:58:24 -04:00
|
|
|
|
|
|
|
|
|
|
.wallet-tokens .token-tooltip {
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
|
}
|
2026-03-10 14:11:53 -04:00
|
|
|
|
}
|
feat: wallet Shop applet — tile grid + BUY-ITEM microbutton + Stripe.js wiring — Chunk 4 of [[project-wallet-shop-expansion]]. The shop applet (slug `wallet-shop`, seeded in Chunk 2) now renders the catalog as a horizontal grid of `.shop-tile` icons: `tithe-1` ($1, fa-piggy-bank), `tithe-5` ($4, fa-piggy-bank w. `×5` badge), `band-1` ($20, fa-ring). Each tile hosts a hover-portaled tooltip carrying name + description + price + a `.tt-microbutton-portal` w. a `.btn-primary` BUY ITEM button — clicking opens `#id_guard_portal` w. "Buy {name} for ${price}?" prompt; confirming triggers Stripe.js confirmCardPayment then POSTs to /shop/confirm + reloads. Items where the user's owned-count has hit `max_owned` (eg. BAND, owned=1, cap=1) render w. `.btn-disabled` + × glyph + "Already owned" microtooltip text — visible-but-unbuyable per the locked decision. View context — `wallet` view + `toggle_wallet_applets` view both pass `shop_items` (decorated w. per-user `.available` via the new `_shop_items_for(user)` helper) + `default_payment_method_id` + `stripe_publishable_key`. SCSS — `.wallet-shop` (flex column wrapping `.shop-grid` flex row), `.shop-tile` (inline-flex tooltip target), `.shop-badge` (2rem circle, --quaUser glyph on --quiUser bg, top-right corner per spec), `.tt-microbutton-portal` (column-flex, BUY btn + 'Already owned' caption styling). JS in `wallet-shop.js` exposes a singleton `WalletShop` module (matching the project's `Brief` / `SeaDeal` / `StageCard` module pattern) w. a tested `initWalletShop()` method — uses event delegation on the shop root (so portal-relocated buy btns still hit the handler) + a DOM-keyed `data-shop-wired` flag (not a module-level boolean) so per-test fixture rebuilds re-wire cleanly. Wired into `wallet.html` after `wallet.js`. **TDD** — 5 Jasmine specs in `WalletShopSpec.js`: T1 click-on-enabled-BUY opens guard w. correct prompt; T2 click-on-disabled-BUY no-op; T3 onConfirm POSTs `shop_item_slug` to `/shop/buy`; T4 init idempotent (calling twice doesn't double-wire); T5 missing-root no-throw. **2 Jasmine traps caught**: (a) `spyOn(window, 'fetch')` collides if another spec already spied on fetch — switched to save+restore via per-test `_origFetch` capture; (b) T3 async pollution — sync assertion passed, `afterEach` restored `window.Stripe=undefined`, then `_doBuy`'s async continuation hit `Stripe(pubKey)` and threw "Unhandled promise rejection". Fixed by T3-local fetch mock returning a never-resolving promise so the chain pauses at the first await. **3 new FTs** in `test_dash_wallet.py`: tiles + icons + ×5 badge + tooltip prose; BUY click opens guard portal + NVM dismisses; BAND-already-owned shows disabled BUY w. 'Already owned' microtext (reads via `textContent` since `.tt` is `display: none`). FT trap caught: `TransactionTestCase` wipes both migration-seeded Applets + ShopItems → setUp must re-seed both manually (mirrors `test_shop_views.py`'s `_seed_starting_items` pattern). 1208 IT/UT + 9 wallet FTs + 5 Jasmine specs green
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 01:15:05 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ── Wallet Shop applet ───────────────────────────────────────────────────────
|
|
|
|
|
|
// Mimics `.wallet-tokens` (horizontal row of tooltipped icons) but each tile
|
|
|
|
|
|
// carries an admin-defined catalog item + an optional `.shop-badge` (eg "×5"
|
|
|
|
|
|
// for the bundle) + a BUY-ITEM microbutton hosted in the tooltip portal.
|
|
|
|
|
|
// JS wiring lives in `apps/dashboard/static/apps/dashboard/wallet-shop.js`.
|
|
|
|
|
|
|
|
|
|
|
|
.wallet-shop {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
overflow: visible;
|
|
|
|
|
|
|
|
|
|
|
|
.shop-grid {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-evenly;
|
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
|
overflow: visible;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.shop-tile {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
|
color: rgba(var(--terUser), 1);
|
|
|
|
|
|
cursor: help;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ×5 badge — top-right corner, --quaUser glyph on --quiUser bg, 2rem circle.
|
|
|
|
|
|
// Per-locked spec from [[project-wallet-shop-expansion]].
|
|
|
|
|
|
.shop-badge {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: -0.5rem;
|
|
|
|
|
|
right: -0.75rem;
|
|
|
|
|
|
width: 2rem;
|
|
|
|
|
|
height: 2rem;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: rgba(var(--quiUser), 1);
|
|
|
|
|
|
color: rgba(var(--quaUser), 1);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Microtooltip — the buy-btn lives inside the main tooltip portal, styled
|
|
|
|
|
|
// like Game Kit's `#id_mini_tooltip_portal` (Equipped/Unequipped/In-Use).
|
|
|
|
|
|
// Hover persistence (cursor moves from tile → portal → microbutton without
|
|
|
|
|
|
// dismissing the tooltip) is handled by `wallet-shop.js`.
|
|
|
|
|
|
.tt-microbutton-portal {
|
|
|
|
|
|
margin-top: 0.5rem;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.25rem;
|
|
|
|
|
|
|
|
|
|
|
|
.tt-buy-btn {
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
padding: 0.25rem 0.75rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tt-already-owned {
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
color: rgba(var(--terUser), 0.85);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|