Session 14: Africa checkout, Tank01 NBA/MLB wiring, WNBA+MLB odds proxies, OAuth icons, loading skeletons (1330 tests)
This commit is contained in:
+126
-1
@@ -4,7 +4,132 @@
|
||||
2026-06-10
|
||||
|
||||
## Current Phase
|
||||
SHIP BUILD v13.0 — The Slate (browse-first dashboard) + OAuth providers + Africa geo (Session 13)
|
||||
SHIP BUILD v14.0 — Africa checkout + Tank01 wiring + WNBA/MLB odds + UX polish (Session 14)
|
||||
|
||||
## Session 14 (2026-06-11) — SHIPPED
|
||||
|
||||
### Phase 1 — Africa tier checkout
|
||||
|
||||
- `src/services/stripeService.js` — `PRICE_MAP.africa` added (reads
|
||||
`STRIPE_PRICE_AFRICA`, null when unset). `getPriceId('africa')`
|
||||
returns the new `PRICE_UNCONFIGURED` sentinel when the env var
|
||||
isn't set. `createCheckoutSession` translates the sentinel to a
|
||||
503 with `code: 'tier_unconfigured'` so the frontend can render a
|
||||
helpful message instead of a generic failure.
|
||||
- `src/routes/stripe.js` — validation whitelist extended:
|
||||
`['africa', 'analyst', 'desk']`. The catch block recognizes
|
||||
`err.code === 'tier_unconfigured'` and surfaces it cleanly.
|
||||
- Tests: +6 (3 integration around `/api/stripe/checkout` for the
|
||||
africa tier, 3 unit around `getPriceId('africa')` and the
|
||||
exported sentinel).
|
||||
- **DB CHECK constraint blocker from Session 12 still applies** —
|
||||
Stripe webhook writes of `tier='africa'` to `users.tier` /
|
||||
`user_profiles.tier` will 23514 until the manual SQL drops + re-
|
||||
adds the constraint with 'africa' included. Validation-layer fix
|
||||
is in place; the migration is the next step.
|
||||
|
||||
### Phase 2 + 3 — Tank01 NBA + MLB wired into computeFeatures
|
||||
|
||||
Architectural choice: cache-read path only on the user request
|
||||
path. The Tank01 adapters (Session 9) already wrap their primitives
|
||||
behind Redis with TTL'd `tank01:*` keys. The new
|
||||
`src/services/intelligence/tank01Augment.js` reads those keys
|
||||
directly without ever calling RapidAPI — that keeps the user
|
||||
request path off the 1000/mo free-tier budget. A daily prefetch
|
||||
(future session) will populate the keys; until then the augmentor
|
||||
returns empty objects and the existing ESPN-derived features stand
|
||||
alone.
|
||||
|
||||
- `augmentNbaFeatures({gameId, playerName, ymd})` reads
|
||||
`tank01:nba:boxscore:{gameId}` and `tank01:nba:odds:{ymd}`,
|
||||
surfaces `t01_pts/reb/ast/threes/blk/stl/tov/minutes/_final` for
|
||||
the named player when present, plus a `t01_market_present`
|
||||
marker when daily odds are cached.
|
||||
- `augmentMlbFeatures({gameId, batterName, batterId, pitcherId,
|
||||
pitcherName, ymd})` reads `tank01:mlb:bvp:{batterId}:{pitcherId}`
|
||||
and surfaces BvP signals (`t01_bvp_pa/ab/h/hr/so` + derived
|
||||
`t01_bvp_so_rate`). Best-effort fallbacks: name-only markers when
|
||||
IDs are absent (future ID resolution), daily-scoreboard presence
|
||||
marker when pitcher is unknown.
|
||||
- `computeFeatures.js` calls both augmentors after `safeGetFeatures`
|
||||
and merges the result with `Object.assign`. Wrapped in try/catch
|
||||
so a Redis hiccup never poisons a grade.
|
||||
- Tests: 13 new in `tests/unit/tank01Augment.test.js`. Existing
|
||||
computeFeatures + soccerBranch suites still green (no
|
||||
regressions).
|
||||
|
||||
### Phase 4 — WNBA + MLB odds proxies
|
||||
|
||||
- `oddsService.SPORT_KEYS` — added `wnba: 'basketball_wnba'` and
|
||||
`mlb: 'baseball_mlb'`. Off-season odds-api responses return empty
|
||||
arrays which the Slate handles cleanly.
|
||||
- `src/routes/odds.js` — new `buildSportRoute()` factory drives
|
||||
`/api/odds/wnba` and `/api/odds/mlb` (clones of the existing
|
||||
`/api/odds/nba` handler).
|
||||
- Next.js proxies: `web/src/app/api/odds/{nba,wnba,mlb}/route.ts`
|
||||
(the NBA one was also missing — Slate had been pointing at a
|
||||
non-existent route).
|
||||
- `Slate.tsx` `FETCH_URLS` — WNBA + MLB no longer flagged as
|
||||
unsupported. ALL tab fans out to all four sports via
|
||||
`Promise.allSettled`.
|
||||
|
||||
### Phase 5 — UX polish
|
||||
|
||||
- `web/src/components/OAuthIcons.tsx` — inline SVGs for Google G,
|
||||
Apple silhouette, X glyph. ~1 KB each, no icon library import.
|
||||
- Login + signup pages wire icons into the OAuth buttons with a
|
||||
shared layout helper.
|
||||
- Slate loading state — bare "Loading the slate…" text replaced
|
||||
with three shimmer-skeleton placeholder cards approximating
|
||||
GameCard dimensions. `@keyframes vyndr-shimmer` added to
|
||||
`globals.css` so other loading surfaces can reuse the animation.
|
||||
- Empty state messaging — the Slate's empty-result case already
|
||||
shows a "Scan it manually →" CTA from Session 13; Session 14
|
||||
preserves that path.
|
||||
- Mobile nav — added a subtle "Scan manually →" tertiary link in
|
||||
the mobile hamburger panel. The desktop nav stays clean (the
|
||||
Slate IS the scan surface there).
|
||||
|
||||
### Tests added (Session 14)
|
||||
| Suite | Tests |
|
||||
|----------------------------------------|-------|
|
||||
| `tests/unit/tank01Augment.test.js` | 13 |
|
||||
| `tests/integration/stripe.test.js` extended (Africa checkout) | +3 |
|
||||
| `tests/unit/stripeService.test.js` extended (Africa getPriceId) | +3 |
|
||||
| **Session 14 total** | **19** |
|
||||
|
||||
### Quality gates
|
||||
- `npm test`: **1330 / 1330 passing** (1311 + 19), 103 suites, 0 regressions
|
||||
- `web/npm run build`: clean — all four odds proxies prerender
|
||||
- License audit: third-party deps remain permissive
|
||||
|
||||
### Honest gaps
|
||||
- Tank01 cache keys are not yet populated by any prefetch — the
|
||||
augmentor wiring is in place but reads will miss until a daily
|
||||
prefetch script lands. The augmentor returns `{}` on miss, so
|
||||
grades work exactly as before until the keys populate.
|
||||
- Africa-tier writes to users.tier will still 23514 (CHECK
|
||||
violation) post-checkout. The DB constraint migration remains a
|
||||
manual SQL step from Session 12.
|
||||
- `STRIPE_PRICE_AFRICA` env var is not set in Coolify yet. Until
|
||||
it is, `/api/stripe/checkout` returns 503 with
|
||||
`code: 'tier_unconfigured'` for `tier:'africa'`.
|
||||
- WNBA odds: odds-api may not always carry props during off-season.
|
||||
Slate degrades cleanly (empty `props` array + empty state UX).
|
||||
- OAuth: Google works (if Supabase Site URL + Redirect URLs are
|
||||
configured). Apple + X buttons render with their icons but the
|
||||
redirect won't succeed until provider configuration lands in the
|
||||
Supabase dashboard (Apple Developer Service ID + key; X OAuth
|
||||
2.0 client).
|
||||
|
||||
### Coolify env (Session 14 additions)
|
||||
|
||||
```
|
||||
# New, required to unblock Africa checkout end-to-end:
|
||||
STRIPE_PRICE_AFRICA=price_... # After creating the product in Stripe dashboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session 13 (2026-06-11) — SHIPPED
|
||||
|
||||
|
||||
Reference in New Issue
Block a user