diff --git a/app/page.tsx b/app/page.tsx index 3af8f25..6af2169 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -63,12 +63,16 @@ export default async function HomePage({ searchParams }: PageProps) { let rideCounts: Record = {}; let coasterCounts: Record = {}; let closingParkIds: string[] = []; + let openParkIds: string[] = []; + let weatherDelayParkIds: string[] = []; if (weekDates.includes(today)) { + // Parks within operating hours right now (for open dot — independent of ride counts) const openTodayParks = PARKS.filter((p) => { const dayData = data[p.id]?.[today]; - if (!dayData?.isOpen || !QUEUE_TIMES_IDS[p.id] || !dayData.hoursLabel) return false; + if (!dayData?.isOpen || !dayData.hoursLabel) return false; return isWithinOperatingWindow(dayData.hoursLabel, p.timezone); }); + openParkIds = openTodayParks.map((p) => p.id); closingParkIds = openTodayParks .filter((p) => { const dayData = data[p.id]?.[today]; @@ -77,17 +81,23 @@ export default async function HomePage({ searchParams }: PageProps) { : false; }) .map((p) => p.id); + // Only fetch ride counts for parks that have queue-times coverage + const trackedParks = openTodayParks.filter((p) => QUEUE_TIMES_IDS[p.id]); const results = await Promise.all( - openTodayParks.map(async (p) => { + trackedParks.map(async (p) => { const coasterSet = getCoasterSet(p.id, parkMeta); const result = await fetchLiveRides(QUEUE_TIMES_IDS[p.id], coasterSet, 300); - const rideCount = result ? result.rides.filter((r) => r.isOpen).length : 0; + const rideCount = result ? result.rides.filter((r) => r.isOpen).length : null; const coasterCount = result ? result.rides.filter((r) => r.isOpen && r.isCoaster).length : 0; return { id: p.id, rideCount, coasterCount }; }) ); + // Parks with queue-times coverage but 0 open rides = likely weather delay + weatherDelayParkIds = results + .filter(({ rideCount }) => rideCount === 0) + .map(({ id }) => id); rideCounts = Object.fromEntries( - results.filter(({ rideCount }) => rideCount > 0).map(({ id, rideCount }) => [id, rideCount]) + results.filter(({ rideCount }) => rideCount != null && rideCount > 0).map(({ id, rideCount }) => [id, rideCount!]) ); coasterCounts = Object.fromEntries( results.filter(({ coasterCount }) => coasterCount > 0).map(({ id, coasterCount }) => [id, coasterCount]) @@ -103,7 +113,9 @@ export default async function HomePage({ searchParams }: PageProps) { data={data} rideCounts={rideCounts} coasterCounts={coasterCounts} + openParkIds={openParkIds} closingParkIds={closingParkIds} + weatherDelayParkIds={weatherDelayParkIds} hasCoasterData={hasCoasterData} scrapedCount={scrapedCount} /> diff --git a/components/HomePageClient.tsx b/components/HomePageClient.tsx index 12f55b6..0b64b24 100644 --- a/components/HomePageClient.tsx +++ b/components/HomePageClient.tsx @@ -50,7 +50,9 @@ interface HomePageClientProps { data: Record>; rideCounts: Record; coasterCounts: Record; + openParkIds: string[]; closingParkIds: string[]; + weatherDelayParkIds: string[]; hasCoasterData: boolean; scrapedCount: number; } @@ -63,7 +65,9 @@ export function HomePageClient({ data, rideCounts, coasterCounts, + openParkIds, closingParkIds, + weatherDelayParkIds, hasCoasterData, scrapedCount, }: HomePageClientProps) { @@ -231,7 +235,9 @@ export function HomePageClient({ today={today} rideCounts={activeCounts} coastersOnly={coastersOnly} + openParkIds={openParkIds} closingParkIds={closingParkIds} + weatherDelayParkIds={weatherDelayParkIds} /> @@ -244,7 +250,9 @@ export function HomePageClient({ grouped={grouped} rideCounts={activeCounts} coastersOnly={coastersOnly} + openParkIds={openParkIds} closingParkIds={closingParkIds} + weatherDelayParkIds={weatherDelayParkIds} /> diff --git a/components/MobileCardList.tsx b/components/MobileCardList.tsx index a35c1d7..10664f0 100644 --- a/components/MobileCardList.tsx +++ b/components/MobileCardList.tsx @@ -10,10 +10,12 @@ interface MobileCardListProps { today: string; rideCounts?: Record; coastersOnly?: boolean; + openParkIds?: string[]; closingParkIds?: string[]; + weatherDelayParkIds?: string[]; } -export function MobileCardList({ grouped, weekDates, data, today, rideCounts, coastersOnly, closingParkIds }: MobileCardListProps) { +export function MobileCardList({ grouped, weekDates, data, today, rideCounts, coastersOnly, openParkIds, closingParkIds, weatherDelayParkIds }: MobileCardListProps) { return (
{Array.from(grouped.entries()).map(([region, parks]) => ( @@ -55,7 +57,9 @@ export function MobileCardList({ grouped, weekDates, data, today, rideCounts, co today={today} openRideCount={rideCounts?.[park.id]} coastersOnly={coastersOnly} + isOpen={openParkIds?.includes(park.id)} isClosing={closingParkIds?.includes(park.id)} + isWeatherDelay={weatherDelayParkIds?.includes(park.id)} /> ))}
diff --git a/components/ParkCard.tsx b/components/ParkCard.tsx index a269b5b..c2fb5b1 100644 --- a/components/ParkCard.tsx +++ b/components/ParkCard.tsx @@ -10,12 +10,14 @@ interface ParkCardProps { today: string; openRideCount?: number; coastersOnly?: boolean; + isOpen?: boolean; isClosing?: boolean; + isWeatherDelay?: boolean; } const DOW = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; -export function ParkCard({ park, weekDates, parkData, today, openRideCount, coastersOnly, isClosing }: ParkCardProps) { +export function ParkCard({ park, weekDates, parkData, today, openRideCount, coastersOnly, isOpen, isClosing, isWeatherDelay }: ParkCardProps) { const openDays = weekDates.filter((d) => parkData[d]?.isOpen && parkData[d]?.hoursLabel); const tzAbbr = getTimezoneAbbr(park.timezone); const isOpenToday = openDays.includes(today); @@ -87,6 +89,16 @@ export function ParkCard({ park, weekDates, parkData, today, openRideCount, coas Closed today )} + {isOpenToday && isWeatherDelay && ( +
+ ⛈ Weather Delay +
+ )} {isOpenToday && openRideCount !== undefined && (
; // pre-grouped parks (if provided, renders region headers) rideCounts?: Record; // parkId → open ride/coaster count for today coastersOnly?: boolean; - closingParkIds?: string[]; // parks in the post-close wind-down buffer + openParkIds?: string[]; + closingParkIds?: string[]; + weatherDelayParkIds?: string[]; } const DOW = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; @@ -189,7 +191,9 @@ function ParkRow({ parkData, rideCounts, coastersOnly, + openParkIds, closingParkIds, + weatherDelayParkIds, }: { park: Park; parkIdx: number; @@ -198,11 +202,15 @@ function ParkRow({ parkData: Record; rideCounts?: Record; coastersOnly?: boolean; + openParkIds?: string[]; closingParkIds?: string[]; + weatherDelayParkIds?: string[]; }) { const rowBg = parkIdx % 2 === 0 ? "var(--color-bg)" : "var(--color-surface)"; const tzAbbr = getTimezoneAbbr(park.timezone); + const isOpen = openParkIds?.includes(park.id) ?? false; const isClosing = closingParkIds?.includes(park.id) ?? false; + const isWeatherDelay = weatherDelayParkIds?.includes(park.id) ?? false; return ( {park.name} - {rideCounts?.[park.id] !== undefined && ( + {isOpen && (
- {rideCounts?.[park.id] !== undefined && ( + {isWeatherDelay && ( +
+ ⛈ Weather Delay +
+ )} + {!isWeatherDelay && rideCounts?.[park.id] !== undefined && (
{rideCounts[park.id]} {coastersOnly ? (rideCounts[park.id] === 1 ? "coaster" : "coasters") @@ -271,7 +284,7 @@ function ParkRow({ ); } -export function WeekCalendar({ parks, weekDates, data, grouped, rideCounts, coastersOnly, closingParkIds }: WeekCalendarProps) { +export function WeekCalendar({ parks, weekDates, data, grouped, rideCounts, coastersOnly, openParkIds, closingParkIds, weatherDelayParkIds }: WeekCalendarProps) { const today = getTodayLocal(); const parsedDates = weekDates.map(parseDate); @@ -387,7 +400,9 @@ export function WeekCalendar({ parks, weekDates, data, grouped, rideCounts, coas parkData={data[park.id] ?? {}} rideCounts={rideCounts} coastersOnly={coastersOnly} + openParkIds={openParkIds} closingParkIds={closingParkIds} + weatherDelayParkIds={weatherDelayParkIds} /> ))}