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:
@@ -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;
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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 “{playerQuery}”. Check spelling or try a partial name.
|
||||
</div>
|
||||
)}
|
||||
{playerSuggestions.length > 0 && playerQuery !== selectedPlayer && (
|
||||
<div
|
||||
className="surface-elevated"
|
||||
|
||||
Reference in New Issue
Block a user