Session C (night2): the aggregator mounted — Explore hub, lens panels, one filter
- StreaksPanel renders THE LENS (interpreted read + tonight difficulty) + snapshot grade letters; streaks are FREE for every tier per the product definition (the picture is free, the read is paid) — only hot lists keep the free top-3 gate. - One stat selection now filters ALL layers: card props narrow together with streaks + hot lists (Slate activeStat → slateGameToCardData). - /explore = server SEO shell (daily-indexable: 'MLB Hit Streaks, Hot Hitters & Stat Leaders') + ExploreHub client: leaders + full streaks + hot lists, real-tier gated, defaults to the in-season sport. - Landing teaser fixed: it pointed at NBA (off-season → self-hid forever); now MLB top-3 through the lens — the anonymous-visitor hook actually shows. - Player dossier: ACTIVE STREAKS block (free, lens reads) via new ?player= filter on /api/streaks/:sport. - Schedule layer already the Slate base (mergeSlate) — verified, no change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,26 +1,44 @@
|
||||
// Session 42 — Stats Explorer (/explore) page (bonus design section 07).
|
||||
// Session 42 — Stats Explorer; Session 60 (night2/C) — the AGGREGATOR hub.
|
||||
// The page is now a server shell (SEO metadata) around the ExploreHub client
|
||||
// component: leaders + full streaks + hot lists = the "entire picture" page.
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const WEB = path.join(__dirname, '..', '..', 'web', 'src');
|
||||
const src = fs.readFileSync(path.join(WEB, 'app', 'explore', 'page.tsx'), 'utf8');
|
||||
const hub = fs.readFileSync(path.join(WEB, 'components', 'ExploreHub.tsx'), 'utf8');
|
||||
const shell = fs.readFileSync(path.join(WEB, 'app', 'explore', 'page.tsx'), 'utf8');
|
||||
|
||||
describe('Stats Explorer page', () => {
|
||||
describe('Stats Explorer hub (client)', () => {
|
||||
it('consumes the real /api/stats/leaders endpoint', () => {
|
||||
expect(src).toContain('/api/stats/leaders');
|
||||
expect(hub).toContain('/api/stats/leaders');
|
||||
});
|
||||
it('has NBA/MLB/WNBA sport tabs + a player search filter', () => {
|
||||
expect(src).toContain('NBA');
|
||||
expect(src).toContain('MLB');
|
||||
expect(src).toContain('WNBA');
|
||||
expect(src).toContain('Search players');
|
||||
expect(hub).toContain('NBA');
|
||||
expect(hub).toContain('MLB');
|
||||
expect(hub).toContain('WNBA');
|
||||
expect(hub).toContain('Search players');
|
||||
});
|
||||
it('rows link to the player profile', () => {
|
||||
expect(src).toContain('playerHref');
|
||||
expect(hub).toContain('playerHref');
|
||||
});
|
||||
it('handles loading / error / empty states', () => {
|
||||
expect(src).toContain("'loading'");
|
||||
expect(src).toContain("'error'");
|
||||
expect(src).toContain('No graded props');
|
||||
expect(hub).toContain("'loading'");
|
||||
expect(hub).toContain("'error'");
|
||||
expect(hub).toContain('No graded props');
|
||||
});
|
||||
// Session 60 — the aggregator: full streaks + hot lists on one page,
|
||||
// gated by the REAL tier (streaks free for all; hot lists top-3 free).
|
||||
it('mounts the streaks + hot-list aggregator with the real tier', () => {
|
||||
expect(hub).toContain('<StreaksPanel sport={sport} tier={tier}');
|
||||
expect(hub).toContain('<HotListPanel sport={sport} tier={tier}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Explore page shell (server, SEO)', () => {
|
||||
it('is a server component carrying daily-indexable metadata', () => {
|
||||
expect(shell).not.toContain("'use client'");
|
||||
expect(shell).toContain('export const metadata');
|
||||
expect(shell).toContain('Hit Streaks');
|
||||
expect(shell).toContain('<ExploreHub />');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user