import type { Metadata } from "next"; import { BackToCalendarLink } from "@/components/BackToCalendarLink"; import { notFound } from "next/navigation"; import { PARK_MAP } from "@/lib/parks"; import { ParkMonthCalendar } from "@/components/ParkMonthCalendar"; import { LiveRidePanel } from "@/components/LiveRidePanel"; import type { RideStatus, RidesFetchResult } from "@/lib/scrapers/sixflags"; import type { LiveRidesResult } from "@/lib/scrapers/queuetimes"; import { getTodayLocal } from "@/lib/env"; import { apiFetch } from "@/lib/api"; import type { DayData } from "@/lib/types"; interface PageProps { params: Promise<{ id: string }>; searchParams: Promise<{ month?: string }>; } interface CalendarMonthResponse { parkId: string; year: number; month: number; monthData: Record; today: string; } interface RidesResponse { parkId: string; today: string; parkOpenToday: boolean; withinWindow: boolean; isWeatherDelay: boolean; liveRides: LiveRidesResult | null; scheduleFallback: RidesFetchResult | null; } export async function generateMetadata({ params }: PageProps): Promise { const { id } = await params; const park = PARK_MAP.get(id); if (!park) return { title: "Park not found | Thoosie Calendar" }; const title = `${park.name} | Thoosie Calendar`; const description = `Operating hours and live ride status for ${park.name} (${park.location.city}, ${park.location.state}).`; return { title, description, openGraph: { title, description }, }; } function parseMonthParam(param: string | undefined): string { if (param && /^\d{4}-\d{2}$/.test(param)) { const [y, m] = param.split("-").map(Number); if (y >= 2020 && y <= 2030 && m >= 1 && m <= 12) { return param; } } return getTodayLocal().slice(0, 7); } export default async function ParkPage({ params, searchParams }: PageProps) { const { id } = await params; const { month: monthParam } = await searchParams; const park = PARK_MAP.get(id); if (!park) notFound(); const monthStr = parseMonthParam(monthParam); const [year, month] = monthStr.split("-").map(Number); const [calendarData, ridesData] = await Promise.all([ apiFetch( `/api/calendar/${id}/month?month=${monthStr}`, { revalidate: 300 }, ), apiFetch( `/api/parks/${id}/rides`, { revalidate: 60 }, ), ]); if (!calendarData) { return ; } const { monthData, today } = calendarData; const parkOpenToday = ridesData?.parkOpenToday ?? false; const isWeatherDelay = ridesData?.isWeatherDelay ?? false; const liveRides = ridesData?.liveRides ?? null; const ridesResult = ridesData?.scheduleFallback ?? null; return (
{/* ── Header ─────────────────────────────────────────────────────────── */}
{park.name} {park.location.city}, {park.location.state}
{/* ── Month Calendar ───────────────────────────────────────────────── */}
{/* ── Ride Status ─────────────────────────────────────────────────── */}
via queue-times.com {liveRides.rides.some((r) => r.hasFastLane) && ( · Fast Lane via sixflags.com )} ) : undefined}> Rides {liveRides ? ( ) : ridesResult && !ridesResult.isExact ? ( {formatShortDate(ridesResult.dataDate)} ) : ( Today )} {liveRides ? ( ) : ( )}
); } // ── Helpers ──────────────────────────────────────────────────────────────── function formatShortDate(iso: string): string { return new Date(iso + "T00:00:00").toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric", }); } // ── Sub-components ───────────────────────────────────────────────────────── function SectionHeading({ children, aside }: { children: React.ReactNode; aside?: React.ReactNode }) { return (

{children}

{aside}
); } function LiveBadge() { return ( Live ); } // ── Schedule ride list (Six Flags operating-hours API fallback) ──────────── function RideList({ ridesResult, parkOpenToday, }: { ridesResult: RidesFetchResult | null; parkOpenToday: boolean; }) { if (!parkOpenToday) { return Park is closed today — no ride schedule available.; } if (!ridesResult || ridesResult.rides.length === 0) { return Ride schedule is not yet available from the API.; } const { rides, isExact, dataDate, parkHoursLabel } = ridesResult; const openRides = rides.filter((r) => r.isOpen); const closedRides = rides.filter((r) => !r.isOpen); return (
{/* Summary badge row */}
{openRides.length} open
{closedRides.length > 0 && (
{closedRides.length} closed / unscheduled
)} {!isExact && ( Showing {formatShortDate(dataDate)} — live schedule updates daily )}
{/* Two-column grid */}
{openRides.map((ride) => )} {closedRides.map((ride) => )}
); } function RideRow({ ride, parkHoursLabel }: { ride: RideStatus; parkHoursLabel?: string }) { const showHours = ride.isOpen && ride.hoursLabel && ride.hoursLabel !== parkHoursLabel; return (
{ride.name}
{showHours && ( {ride.hoursLabel} )}
); } function Callout({ children }: { children: React.ReactNode }) { return (
{children}
); } function DataUnavailable({ parkName }: { parkName: string }) { return (

{parkName} data is unavailable

We could not reach the backend. Refresh in a moment.

); }