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:
Kev
2026-07-11 00:47:01 -04:00
parent 2c9cbca7bd
commit 80481d6f6a
9 changed files with 275 additions and 148 deletions
+4 -1
View File
@@ -90,9 +90,12 @@ router.get('/:sport', async (req, res) => {
}
const stat = req.query.stat ? String(req.query.stat).toLowerCase() : 'all';
const limit = req.query.limit ? Math.max(0, parseInt(req.query.limit, 10) || 0) : 0;
// Session 60 (night2/C) — a player's active streaks for the dossier page.
const playerKey = req.query.player ? nameKey(String(req.query.player).slice(0, 60)) : null;
try {
const roster = await loadRosterLogs(sport);
let roster = await loadRosterLogs(sport);
if (playerKey) roster = roster.filter((p) => nameKey(p.name) === playerKey);
const streaks = streaksService.computeStreaks(roster, sport, { stat });
// Session 60 — form heat (hot hitters/sluggers/shooters) joins the feed.
const heat = streaksService.computeFormHeat(roster, sport, {})