Files
python-tdd/src/templates/apps/dashboard/wallet.html

20 lines
935 B
HTML
Raw Normal View History

{% extends "core/base.html" %}
{% load static %}
{% block title_text %}Dashwallet{% endblock title_text %}
fluid root rem + landscape aperture: html font-size = clamp(14px, 2.4vmin, 22px) so 1rem scales w. viewport (rotation-invariant via vmin); --sidebar-w + --h2-col-w CSS vars unify navbar/footer/h2 sizing; container margin-left = sidebar + h2-col-w in landscape so applets clip cleanly under the rotated wordmark; h2 markup splits into two spans (45/55 horizontal title); drop the disparate min-height font-size jumps + 1800px sidebar-doubling overrides - html { font-size: clamp(14px, 2.4vmin, 22px) } — single sliding scale; everything in rem (sidebar widths, h2 font-size, paddings) scales together. Phone rotation swaps width/height but vmin stays the same → 1rem stays the same → navbar/footer/h2 hold their size between portrait + landscape. - :root --sidebar-w: 5rem (replaces the locally-scoped $sidebar-w SCSS var that lived inside @media blocks); --h2-col-w: 3rem for the rotated wordmark column in landscape. var(--sidebar-w) + var(--h2-col-w) are the only knobs that move the layout. - Landscape container: margin-left = calc(var(--sidebar-w) + var(--h2-col-w)); margin-right = var(--sidebar-w). Applets are now clipped INSIDE the h2 column, so the rotated "BILLPOST" / "DASHBOARD" wordmark never has content bleeding behind it (the original complaint). - h2 markup refactor across 13 templates: <span>BILL</span><span>POST</span> instead of <span>BILL</span>POST. Portrait styling: display: flex; first span flex 0 0 45% + --quaUser colour; second span flex 0 0 55% + --secUser inherited. Per-span text-align: justify + text-justify: inter-character keeps the inter-letter spacing within each span. Landscape resets the flex (single rotated wordmark, not split). - Drop the four h2 font-size jumps (min-height: 400/500/800px) — single font-size: 3rem now scales fluidly via root rem. Drop the @media (orientation: landscape) and (max-width: 1100px) h1 override (rem-fluid handles cramped widths). Drop the entire @media (orientation: landscape) and (min-width: 1800px) sidebar-doubling block in _base.scss / _applets.scss / _bud.scss — the rem clamp ceiling already caps the size. - _bud.scss + _applets.scss: bud-btn / bud-panel / bud-suggestions / gear-btn / applet menus all switch to var(--sidebar-w)-based positioning; landscape rules are single (no per-breakpoint duplication). - Per-spec tradeoff: non-.btn-primary buttons (BYE / NVM / OK / kit-btn / etc.) inherit rem-fluid like everything else and will scale slightly w. viewport. User explicitly OK'd this — they don't need to stay px-fixed. - 852 ITs + 24 layout/navbar/bud FTs green; existing geometry assertions are relative or categorical (not exact-px) so the rem clamp doesn't surface failures at the 800x1200 FT viewport. 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-09 00:14:14 -04:00
{% block header_text %}<span>Dash</span><span>wallet</span>{% endblock header_text %}
{% block content %}
<div class="wallet-page">
{% include "apps/applets/_partials/_gear.html" with menu_id="id_wallet_applet_menu" %}
{% include "apps/wallet/_partials/_applets.html" %}
</div>
<div id="id_tooltip_portal" class="token-tooltip"></div>
feat: wallet Shop polish — microtooltip extraction, Shop-first ordering, DRY tooltip styling, writs rebalance, "no expiry" on all items. Visual-pass tweaks landing atop the 5-chunk Shop rollout (commits 8e476f5 → d28cf7b). **Microtooltip extraction**: `.tt-microbutton-portal` (Chunk 4's wrap-inside-`.tt`) replaced w. a sibling `.tt-micro` div on each `.shop-tile`. `wallet.js`'s `initWalletTooltips` clones BOTH into separate portals on hover — `.tt` → `#id_tooltip_portal` (main card), `.tt-micro` → `#id_mini_tooltip_portal` (small italic pill at bottom-right of main, mirroring Game Kit's Equipped/Unequipped/In-Use mini portal). Hover persistence covers both portals + the source tile w. a 200ms grace timer cancelled by mouseenter on any of the 3 zones. Capped items (BAND-owned) render NO btn at all — just "Already owned" microtext (mirrors Game Kit's status-only "Equipped" pill rather than the disabled-× pattern that lived in Chunk 4). **Tooltip-pin on guard open**: `WalletTooltips.pin()` / `.unpin()` exposed on window; `wallet-shop.js`'s BUY click calls `pin()` before `showGuard()` + both `onConfirm` / `onDismiss` callbacks call `unpin()` → the item tooltip stays visible behind the guard's "Buy {name} for ${price}?" prompt instead of orphaning. **Shop-first applet ordering**: new `Applet.display_order` field (default 100, lower = earlier; PK tie-break preserves legacy insertion-order for the existing 3 applets); seed migration sets `wallet-shop.display_order=10` so Shop renders atop Balances/Tokens/Payment. `applet_context()` updated to `.order_by("display_order", "pk")`. New `WalletAppletOrderTest` (2 ITs) pins Shop-first DOM order + view-context list. **DRY tooltip styling**: shop tooltip now uses the same 4-slot `.tt-title` / `.tt-description` / `.tt-shoptalk` / `.tt-expiry` classes as the Tokens row. New `ShopItem.shoptalk` field for the italic flavor line (band-1 = "Unlimited free entry (BYOB)" split out of description; tithes blank). New `ShopItem.tooltip_expiry()` method returns "no expiry" — eternal-stock convention (all current items; seasonal listings could override later). **Writs rebalance**: locked 2026-05-22 — tithe-1 144→12 writs, tithe-5 750→60 writs. Description text updated in lockstep ("1 Tithe Token + 12 Writs" / "5 Tithe Tokens + 60 Writs"). **Badge tweak**: ×N badge shrunk 2rem → 1.5rem + nudged further off-tile (top: -0.7rem, right: -1rem) so most of the underlying icon stays visible. **SCSS**: `.tt-micro` hidden in source DOM (portal-only); `#id_mini_tooltip_portal` mostly mirrors gameboard's mini at `_gameboard.scss:140` but allows BUY-btn label to wrap onto multiple lines (`white-space: normal` on `.tt-buy-btn`); `.tt-already-owned` styled w. `--secUser` italic at 0.85rem to match Game Kit pills. **Migrations** — 5 new: `lyric/0010_repricing_tithe_writs` (writs + description), `lyric/0011_shopitem_shoptalk` (schema), `lyric/0012_seed_shop_shoptalk` (band split), `applets/0012_applet_display_order` (schema), `applets/0013_wallet_shop_display_order` (Shop atop). All idempotent. **TDD** — 5 new ITs across `test_shop_models.py` (`shoptalk` default + per-item assertions, `tooltip_expiry` method, updated tithe writs values, `WalletAppletOrderTest`), 1 new FT (`test_shop_buy_guard_portal_pins_item_tooltip` — programmatically dispatches mouseenter/mouseleave to exercise the pin/unpin race), 3 new Jasmine specs (T6 pin-on-click, T7 unpin-on-confirm, T8 unpin-on-dismiss). Existing FT band-owned assertion switched to `.tt-micro` (no `.tt-buy-btn` present), Jasmine T2 rewritten to assert no btn renders. **3 traps caught** mid-build: (a) multi-line `{# #}` comment leaked into DOM again (cf [[feedback-django-comments-single-line-only]]) — pinned the trap; (b) `spyOn(window, 'fetch')` Jasmine double-spy collision (cf trapped previously); (c) async pollution where `afterEach` restores `window.Stripe=undefined` before `_doBuy`'s continuation hits it — fixed by per-test never-resolving fetch mock. 1211 IT/UT + 9 wallet FTs green; Jasmine SpecRunner verified visually (FT hangs Selenium-side on spec count). Pipeline will sweep all FTs Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 02:21:10 -04:00
{# Microtooltip for the Shop applet's BUY-ITEM btn / 'Already owned' note. #}
{# Mirrors gameboard.html's mini portal (Equipped/Unequipped/In-Use). #}
<div id="id_mini_tooltip_portal" class="token-tooltip token-tooltip--mini"></div>
<script src="https://js.stripe.com/v3/"></script>
<script src="{% static "apps/dashboard/wallet.js" %}"></script>
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
<script src="{% static "apps/dashboard/wallet-shop.js" %}"></script>
{% endblock content %}