The API without a date param returns today's operating data directly,
invalidating the previous assumption that today's date was always missing.
- Add fetchToday(apiId, revalidate?) to sixflags.ts — calls the dateless
endpoint with optional ISR cache
- Extract parseApiDay() helper shared by scrapeMonth and fetchToday
- Update upsertDay WHERE clause: >= date('now') so today can be updated
(was > date('now'), which froze today after first write)
- scrape.ts: add a today-scrape pass after the monthly loop so each run
always writes fresh today data to the DB
- app/page.tsx: fetch live today data for all parks (5-min ISR) and merge
into the data map before computing open/closing/weatherDelay status
- app/park/[id]/page.tsx: prefer live today data from API for todayData
so weather delays and hour changes surface within 5 minutes
- scrapeRidesForDay: update comment only — role unchanged (QT fallback)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Park open indicator now derives from scheduled hours, not ride counts.
Parks with queue-times coverage but 0 open rides (e.g. storm) show a
"⛈ Weather Delay" notice instead of a ride count on both desktop and mobile.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parks in the 1-hour buffer after scheduled close now show amber instead
of green: the dot on the desktop calendar turns yellow, and the mobile
card badge changes from "Open today" (green) to "Closing" (amber).
- getOperatingStatus() replaces isWithinOperatingWindow's inline logic,
returning "open" | "closing" | "closed"; isWithinOperatingWindow now
delegates to it so all callers are unchanged
- closingParkIds[] is computed server-side and threaded through
HomePageClient → WeekCalendar/MobileCardList → ParkRow/ParkCard
- New --color-closing-* CSS variables mirror the green palette in amber
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Moves the coaster toggle out of WeekNav and into the homepage header
top-right as "🎢 Coaster Mode", alongside the parks open badge
- State is stored in localStorage ("coasterMode") so the preference
persists across sessions and page refreshes
- Dropped the ?coasters=1 URL param entirely; the server always fetches
both rideCounts and coasterCounts, and HomePageClient picks which to
display client-side — no flash or server round-trip on toggle
- Individual park pages: LiveRidePanel reads localStorage on mount and
pre-selects the Coasters Only filter when Coaster Mode is active
- Extracted HomePageClient (client component) to own the full homepage
UI; page.tsx is now pure data-fetching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- "parks open" → "parks open this week" for clarity
- Week calendar cells: stack hours above tz abbreviation (smaller,
dimmer) instead of inline to avoid overflow in tight 130px columns
- Mobile park cards: tz abbreviation inline but smaller/dimmer (60% opacity)
- Month calendar: same two-line stacking in compact day cells
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- isWithinOperatingWindow now accepts an IANA timezone and reads the
current time in the park's local timezone via Intl.DateTimeFormat,
fixing false positives when the server runs in UTC but parks store
hours in local time (e.g. Pacific parks showing open at 6:50 AM EDT)
- Remove the 1-hour pre-open buffer so parks are not marked open before
their doors actually open; retain the 1-hour post-close grace period
- Add getTimezoneAbbr() helper to derive the short tz label (EDT, PDT…)
- All hours labels now display with the local timezone abbreviation
(e.g. "10am – 6pm PDT") in WeekCalendar, ParkCard, and ParkMonthCalendar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 🎢 Coasters button in nav bar (URL-driven: ?coasters=1)
- When active, swaps ride counts for coaster counts per park
- Label switches between "X rides operating" / "X coasters operating"
- Arrow key navigation preserves coaster filter state
- Only shown when coaster data exists in park-meta
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- "X rides open" → "X rides operating" (desktop + mobile)
- Green glowing dot next to park name when actively operating
- Hours text in calendar cells: larger (0.78rem) and bolder (600)
- Parks open badge: green tint when parks are open, larger text
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract isWithinOperatingWindow() to lib/env.ts (shared)
- Park detail page: always fetch Queue-Times, but force all rides
closed when outside the ±1h operating window
- LiveRidePanel: always show closed ride count badge (not just when
some rides are also open); label reads "X rides total" when none
are open vs "X closed / down" when some are
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fetch Queue-Times ride counts for parks open today (5min cache)
- Only shown within 1h before open to 1h after scheduled close
- Count displayed on the right of the park name/location cell (desktop)
and below the open badge (mobile)
- Whole park cell is now a clickable link
- Hover warms the park cell background; no row-wide highlight
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
new Date().toISOString() returns UTC, causing the calendar to advance
to the next day at 8pm EDT / 7pm EST. getTodayLocal() reads local
wall-clock time and rolls back one day before 3am so the calendar
stays on the current day through the night.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the cramped 7-column day grid with a clean open-days list.
Each card now shows:
- Park name + "Open today" / "Closed today" badge in the header
- One row per open day (Today, Monday, Friday...) with full hours
- Today row highlighted in amber; passholder days labeled inline
- Whole card is a tap target linking to the park detail page
Also:
- Hide the legend below sm breakpoint (not needed on phones)
- Reduce horizontal padding to 16px on mobile (was 24px)
- Tighten MobileCardList vertical spacing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- public/logo.svg: amber coaster silhouette (lift hill + vertical loop +
camelback) on transparent background; used in header and README
- app/icon.tsx: PNG favicon via Next.js ImageResponse (works in all
browsers including Safari); renders simplified hill + loop on dark
rounded-square background
- app/page.tsx: logo img added next to "Thoosie Calendar" title in header
- README.md: logo displayed at top of document
- Remove app/icon.svg (replaced by icon.tsx → PNG)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename app title and header from "Six Flags Calendar" to "Thoosie Calendar"
- Update layout metadata title and description
- Update README title
- Add app/icon.svg favicon: amber T on dark background, picked up
automatically by Next.js App Router
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Visual overhaul:
- Warmer color system with amber accent for Today, better text hierarchy
- Row hover highlighting, sticky column shadow on horizontal scroll
- Closed cells replaced with dot (·) instead of "Closed" text
- Regional grouping (Northeast/Southeast/Midwest/Texas & South/West)
- Two-row header with park count badge and WeekNav on separate lines
- Amber "Today" button in WeekNav when off current week
- Mobile card layout (< 1024px) with 7-day grid per park; table on desktop
- Skeleton loading state via app/loading.tsx
Park detail pages (/park/[id]):
- Month calendar view with ← → navigation via ?month= param
- Live ride status fetched from Six Flags API (cached 1h)
- Ride hours only shown when they differ from park operating hours
- Fallback to nearest upcoming open day when today is dropped by API,
including cross-month fallback for end-of-month edge case
Data layer:
- Park type gains region field; parks.ts exports groupByRegion()
- db.ts gains getParkMonthData() for single-park month queries
- sixflags.ts gains scrapeRidesForDay() returning RidesFetchResult
with rides, dataDate, isExact, and parkHoursLabel
Removed: CalendarGrid.tsx, MonthNav.tsx (dead code)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Next.js 15 + Tailwind CSS v4 week calendar showing Six Flags park hours.
Scrapes the internal CloudFront API, stores results in SQLite.
Includes Dockerfile (Debian/Playwright-compatible), docker-compose, and
Gitea Actions pipeline that builds and pushes to the container registry.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>