Session 8: Frontend Stripe cutover, soccer pages, sport selector, grade result cards, beta badge
This commit is contained in:
+105
-1
@@ -4,7 +4,111 @@
|
||||
2026-06-10
|
||||
|
||||
## Current Phase
|
||||
SHIP BUILD v7.2 — Soccer Intelligence + World Cup 2026 (Session 7j)
|
||||
SHIP BUILD v8.0 — Frontend Stripe Cutover + Soccer Pages (Session 8)
|
||||
|
||||
## Session 8 (2026-06-10) — SHIPPED
|
||||
|
||||
Frontend layer that connects users to the Session 7h–7j backend.
|
||||
NexaPay → Stripe cutover on the pricing flow + a `/soccer` page that
|
||||
exposes the soccer intelligence pipeline.
|
||||
|
||||
### Files created (frontend)
|
||||
- `web/src/app/api/odds/soccer/[league]/route.ts` — Next.js proxy →
|
||||
Express `GET /api/odds/soccer/:league`. Validates league against the
|
||||
9 accepted codes upstream so a typo bounces at the Next boundary.
|
||||
- `web/src/app/soccer/page.tsx` — live soccer odds feed. Hosts
|
||||
`SportSelector`, fetches `/api/odds/soccer/:league`, groups props by
|
||||
match → stat type. "Grade" button triggers inline scan via
|
||||
`/api/scan` (sport: Soccer) and renders the result through
|
||||
`SoccerGradeResult`. Soccer-only page; switching the selector to
|
||||
another sport bounces to `/scan`.
|
||||
- `web/src/app/upgrade/success/page.tsx` — Stripe success landing.
|
||||
Reads `session_id`, refreshes AuthContext so the new tier flips
|
||||
immediately. Does NOT verify against Stripe from the client (no
|
||||
secret key on the browser) — the webhook is the source of truth.
|
||||
- `web/src/app/upgrade/cancel/page.tsx` — Stripe cancel landing.
|
||||
- `web/src/components/SportSelector.tsx` — pill tabs (NBA/WNBA/MLB/
|
||||
Soccer); Soccer reveals a sub-row of the 9 league codes matching
|
||||
Express's `SOCCER_SPORT_KEYS`. Emits `{ sport, league? }` via
|
||||
`onChange` — pure UI, no fetches.
|
||||
- `web/src/components/SoccerGradeResult.tsx` — soccer-themed result
|
||||
card. Parses the engine's reasoning summary into visual chips
|
||||
(⚽ goals/90, 📊 xG, 🎯 penalty taker, 🏹 free-kick taker, ⛳ corner
|
||||
taker, 🏔️ altitude, 🟨 referee, ⏱️ minutes discount, 🛡️ opponent
|
||||
defense, 🏆 tournament pedigree). Color-coded by tone
|
||||
(positive / caution / warning / neutral). Free-tier responses
|
||||
(carrying `tier_gated: true`) render the chip row blurred under an
|
||||
upgrade CTA; the structured grade + confidence + edge stay visible.
|
||||
Kept separate from `GradeCard` so the NBA/MLB/WNBA path is
|
||||
untouched.
|
||||
|
||||
### Files modified (frontend)
|
||||
- `web/src/app/api/checkout/route.ts` — full rewrite. Was a NexaPay
|
||||
payment-link creator; is now a thin proxy that forwards `{ tier,
|
||||
founder_code? }` + bearer to Express `/api/stripe/checkout`.
|
||||
Response remap: `checkout_url` → `url` for callsite compat; both
|
||||
fields shipped so either reads cleanly.
|
||||
- `web/src/app/api/scan/route.ts` — accepts `Soccer` sport in addition
|
||||
to NBA/MLB/WNBA. Soccer stat-type allowlist mirrors the backend
|
||||
`VALID_STAT_TYPES` (goals, shots_on_target, shots, tackles, cards,
|
||||
corners, saves, goals_conceded, passes, clean_sheet, assists).
|
||||
- `web/src/components/Pricing.tsx` — CTAs converted from `<a href>` to
|
||||
onClick handlers. Uses `useAuth()` for the bearer token, POSTs to
|
||||
`/api/checkout`, `window.location.assign` to the returned Stripe URL.
|
||||
Loading state on the active tier, inline error banner. Anonymous
|
||||
visitors bounce to `/signup?return=/%23pricing`. Footnote rewritten
|
||||
from "NexaPay" to "Stripe (test mode while we onboard founders)".
|
||||
- `web/src/components/Nav.tsx` — small BETA tag next to the wordmark.
|
||||
Glitch-styled, monospace, low-opacity green border. Renders on every
|
||||
page that mounts Nav.
|
||||
|
||||
### Files modified (backend — ONE allowed change)
|
||||
- `src/services/stripeService.js` — `success_url` / `cancel_url`
|
||||
point at the frontend (`NEXT_PUBLIC_SITE_URL` with `BASE_URL`
|
||||
fallback, default `http://localhost:3000`). Previously the routes
|
||||
pointed at the Express origin which would have 404'd the redirect.
|
||||
New URLs:
|
||||
- `${frontendUrl}/upgrade/success?session_id={CHECKOUT_SESSION_ID}`
|
||||
- `${frontendUrl}/upgrade/cancel`
|
||||
All 23 Stripe tests still pass (none asserted on the URL strings).
|
||||
|
||||
### Files modified (docs)
|
||||
- `docs/SYSTEM-MANIFEST.md` — `/api/odds/soccer/[league]` row in
|
||||
Next.js routes, new section listing the three new Next.js pages,
|
||||
the Session 7h "dual-provider divergence" callout flipped from
|
||||
open-work to ✅ complete.
|
||||
- `BUILD-STATE.md` — Session 8 entry.
|
||||
|
||||
### Honest verification status
|
||||
|
||||
Build-verified (passed `web/npm run build` after every component):
|
||||
- All TypeScript types resolve
|
||||
- All routes prerender / build correctly (24 pages, 30+ API routes)
|
||||
- No ESLint errors
|
||||
|
||||
NOT runtime-verified in this session (I have no browser to click
|
||||
through):
|
||||
- Actual Stripe checkout redirect end-to-end (test mode card flow)
|
||||
- Soccer odds rendering with live data (depends on
|
||||
`FOOTBALL_DATA_API_KEY` being set in prod and the daily prefetch
|
||||
having run)
|
||||
- SoccerGradeResult signal parsing against a real engine response
|
||||
(signal-chip regex tested against the exact phrasing
|
||||
`buildSoccerReasoningLines` emits in `analyzeViaEngine1.js`, but
|
||||
not against live engine output)
|
||||
- AuthContext.refresh() actually triggering a profile re-read after
|
||||
the Stripe redirect
|
||||
|
||||
These are the expected next-session sanity checks once Coolify
|
||||
deploys this build.
|
||||
|
||||
### Quality gates
|
||||
- `npm test` (backend): **1173 / 1173 passing**, 91 suites, 0 regressions
|
||||
from Session 7j baseline
|
||||
- `web/npm run build`: clean — all new routes prerendered, no type errors
|
||||
- License audit: only permissive licenses
|
||||
|
||||
---
|
||||
|
||||
## Session 7j (2026-06-10) — SHIPPED
|
||||
|
||||
|
||||
Reference in New Issue
Block a user