import { Fragment } from "react"; import Link from "next/link"; import type { Park } from "@/lib/scrapers/types"; import type { DayData } from "@/lib/db"; import type { Region } from "@/lib/parks"; interface WeekCalendarProps { parks: Park[]; weekDates: string[]; // 7 dates, YYYY-MM-DD, Sun–Sat data: Record>; // parkId → date → DayData grouped?: Map; // pre-grouped parks (if provided, renders region headers) } const DOW = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; const MONTHS = [ "Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec", ]; function parseDate(iso: string) { const d = new Date(iso + "T00:00:00"); return { month: d.getMonth(), day: d.getDate(), dow: d.getDay(), isWeekend: d.getDay() === 0 || d.getDay() === 6, }; } function DayCell({ date, dayData, isToday, isWeekend, }: { date: string; dayData: DayData | undefined; isToday: boolean; isWeekend: boolean; }) { const base: React.CSSProperties = { padding: 0, textAlign: "center", verticalAlign: "middle", borderBottom: "1px solid var(--color-border)", borderLeft: "1px solid var(--color-border)", height: 56, background: isToday ? "var(--color-today-bg)" : isWeekend ? "var(--color-weekend-header)" : "transparent", borderLeftColor: isToday ? "var(--color-today-border)" : undefined, borderRightColor: isToday ? "var(--color-today-border)" : undefined, borderRight: isToday ? "1px solid var(--color-today-border)" : undefined, transition: "background 120ms ease", }; if (!dayData) { return ( ); } if (!dayData.isOpen || !dayData.hoursLabel) { return ( · ); } if (dayData.specialType === "passholder_preview") { return (
Passholder {dayData.hoursLabel}
); } return (
{dayData.hoursLabel}
); } function RegionHeader({ region, colSpan }: { region: string; colSpan: number }) { return ( {region} ); } function ParkRow({ park, parkIdx, weekDates, parsedDates, parkData, today, }: { park: Park; parkIdx: number; weekDates: string[]; parsedDates: ReturnType[]; parkData: Record; today: string; }) { const rowBg = parkIdx % 2 === 0 ? "var(--color-bg)" : "var(--color-surface)"; return ( {park.name}
{park.location.city}, {park.location.state}
{weekDates.map((date, i) => ( ))} ); } export function WeekCalendar({ parks, weekDates, data, grouped }: WeekCalendarProps) { const today = new Date().toISOString().slice(0, 10); const parsedDates = weekDates.map(parseDate); const firstMonth = parsedDates[0].month; const firstYear = new Date(weekDates[0] + "T00:00:00").getFullYear(); // Build ordered list of (region, parks[]) if grouped, otherwise treat all as one group const sections: Array<{ region: string | null; parks: Park[] }> = grouped ? Array.from(grouped.entries()).map(([region, ps]) => ({ region, parks: ps })) : [{ region: null, parks }]; const colSpan = weekDates.length + 1; // park col + 7 day cols return (
{weekDates.map((d) => ( ))} {/* Month header — only when week crosses a month boundary */} {parsedDates.some((d) => d.month !== firstMonth) && ( ); })} )} {/* Day header */} {weekDates.map((date, i) => { const pd = parsedDates[i]; const isToday = date === today; return ( ); })} {sections.map(({ region, parks: sectionParks }) => ( {region && ( )} {sectionParks.map((park, parkIdx) => ( ))} ))}
{weekDates.map((date, i) => { const pd = parsedDates[i]; const showMonth = i === 0 || pd.month !== parsedDates[i - 1].month; return ( {showMonth ? `${MONTHS[pd.month]} ${new Date(date + "T00:00:00").getFullYear() !== firstYear ? new Date(date + "T00:00:00").getFullYear() : ""}` : ""}
Park
{DOW[pd.dow]}
{pd.day}
); } // ── Shared styles ────────────────────────────────────────────────────────── const thParkStyle: React.CSSProperties = { position: "sticky", left: 0, zIndex: 10, padding: "10px 14px", textAlign: "left", borderBottom: "1px solid var(--color-border)", background: "var(--color-bg)", verticalAlign: "bottom", }; const thDayStyle: React.CSSProperties = { padding: "10px 8px 8px", textAlign: "center", fontWeight: 400, borderBottom: "1px solid var(--color-border)", borderLeft: "1px solid var(--color-border)", verticalAlign: "bottom", };