Session 17: Audit response — checkout 401 fix, hero prop 404 fix, Slate parsing fix, ALL tab cascade isolation, cookie/nav/footer/autocomplete polish (1438 tests)

This commit is contained in:
Kev
2026-06-11 21:22:59 -04:00
parent 73b65a0248
commit beaf8b2a61
14 changed files with 681 additions and 25 deletions
+10 -5
View File
@@ -1,11 +1,16 @@
import { NextResponse } from 'next/server';
// Session 17 — Next.js App Router refuses to compile a route that
// exports BOTH `dynamic = 'force-dynamic'` AND `revalidate`. The two
// modes are mutually exclusive: force-dynamic skips static
// generation; revalidate gates ISR. Session 16 shipped both, which
// silently broke the route at build time (production audit found a
// hard 404 on /api/hero-prop).
//
// We keep `force-dynamic` (we want the random-prop pick to vary per
// request) and emit the 15-minute cache via the response's
// Cache-Control header — Coolify's reverse proxy honors it.
export const dynamic = 'force-dynamic';
// Cache the response for 15 minutes (server-side) so cold visitors
// don't trigger a fresh grade on every page load. The cache header
// is what most CDNs / Coolify reverse proxies honor; Next.js itself
// already opts into dynamic rendering via `dynamic = 'force-dynamic'`.
export const revalidate = 900;
const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:3000';
const HERO_FETCH_TIMEOUT_MS = 6000;
+6
View File
@@ -4,6 +4,11 @@ import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/contexts/AuthContext';
import Hero from '@/components/Hero';
// Session 17 — game-count strip mounted between the hero and the
// existing LivePropsStrip. Shows "X NBA · Y WNBA · Z MLB games
// being graded right now" with a signup CTA. Hides itself when
// every sport returns zero (off-hours / upstream outages).
import TonightsSlate from '@/components/TonightsSlate';
import LivePropsStrip from '@/components/LivePropsStrip';
import Features from '@/components/Features';
import HowItWorks from '@/components/HowItWorks';
@@ -34,6 +39,7 @@ export default function Home() {
return (
<>
<Hero />
<TonightsSlate />
<LivePropsStrip />
<Features />
<HowItWorks />
+16
View File
@@ -340,6 +340,22 @@ export default function ScanPage() {
}}
autoComplete="off"
/>
{/* Session 17 — show "no results" when the search ran but
returned nothing. Audit reported a silent dropdown failure;
this gives the user feedback when the upstream player
service is offline or the spelling didn't match. */}
{playerQuery.trim().length >= 2 && playerSuggestions.length === 0 && playerQuery !== selectedPlayer && (
<div
className="surface-elevated"
style={{
position: 'absolute', top: '100%', left: 0, right: 0,
marginTop: 4, zIndex: 20, padding: 12,
fontSize: 12, color: 'var(--text-tertiary)',
}}
>
No {sport} players matched &ldquo;{playerQuery}&rdquo;. Check spelling or try a partial name.
</div>
)}
{playerSuggestions.length > 0 && playerQuery !== selectedPlayer && (
<div
className="surface-elevated"