Commit Graph

109 Commits

Author SHA1 Message Date
josh deb8e4169b Revert "feat: run full scrape on backend container start"
Build and Deploy / Build & Push (push) Successful in 1m18s
This reverts commit db668c0787.
2026-04-23 23:14:22 -04:00
josh 06b911917d fix: use explicit Eastern timezone for day boundary instead of system TZ
Build and Deploy / Build & Push (push) Successful in 2m50s
getTodayLocal() relied on system clock hours, which broke in the web
container (TZ defaulting to UTC) — the day flipped at 11 PM EDT (3 AM
UTC) instead of 3 AM Eastern. Now uses Intl.DateTimeFormat with an
explicit America/New_York timezone. Also replaced all toISOString()
date formatting with local-component helpers to avoid UTC conversion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 23:11:33 -04:00
josh db668c0787 feat: run full scrape on backend container start
Build and Deploy / Build & Push (push) Successful in 1m44s
Fires scrapeToday() then scrapeFullYear() as a background task on
startup so fresh deploys have data immediately instead of waiting
for the first cron tick. Staleness check makes warm restarts a no-op.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 22:28:07 -04:00
josh a53e3ffa9f docs: add comprehensive project documentation
Build and Deploy / Build & Push (push) Successful in 1m31s
Add docs/ folder with architecture, operations, API reference, and
development guides covering system design, deployment, troubleshooting,
all backend endpoints, and contributor workflows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 22:15:02 -04:00
josh 4922dce8ac docs: update README for web + backend architecture
Build and Deploy / Build & Push (push) Successful in 34s
Remove references to Playwright discovery, RCDB scraping, scraper
container, and npm run scripts. Document the new two-container setup,
tiered scheduling, backend API endpoints, and local dev workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 21:54:45 -04:00
josh c5c9f750a3 chore: update Docker and CI for web + backend architecture
Build and Deploy / Build & Push (push) Successful in 2m10s
Replace scraper container with backend API container. Web image no
longer mounts a data volume or ships SQLite. Backend image runs Hono
server with node-cron scheduler, owns the database exclusively.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 21:49:17 -04:00
josh 3815da2d3f refactor: make frontend a pure presentation layer fetching from backend API
Server components now fetch composed data from the backend instead of
directly querying SQLite and external APIs. Removes better-sqlite3
dependency from the frontend entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 21:43:59 -04:00
josh ccd35c4648 chore: remove old scraper scripts, replaced by backend scheduler
Delete scripts/scrape.ts and scripts/scrape-schedule.sh — their
functionality now lives in the backend's node-cron tiered scheduler
(backend/src/services/scheduler.ts + scraper.ts).

Remove scrape and scrape:force npm scripts from package.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 21:33:34 -04:00
josh 70b56158d4 feat: add Hono backend API server with tiered scheduler
Standalone Node.js backend that owns the SQLite database and serves
composed data via REST endpoints. Replaces the shell-scheduled scraper
with in-process node-cron tiered scheduling.

Backend structure:
- Hono HTTP server on port 3001 with CORS and request logging
- Singleton SQLite connection with WAL mode
- In-memory TTL cache for Queue-Times and fetchToday responses
- Comparison check on fetchToday (read-before-write, only upserts on change)

API endpoints:
- GET /api/calendar/week — week schedule + live ride counts for all parks
- GET /api/calendar/:parkId/month — month calendar for one park
- GET /api/parks — park list with metadata
- GET /api/parks/:id — single park detail
- GET /api/parks/:id/rides — live rides with Queue-Times/schedule fallback
- GET /api/status — health check, scrape stats
- POST /api/scrape/trigger — manual scrape (scope: today/month/upcoming/full)

Scheduler tiers:
- Tier 1: today — hourly (Mar-Dec)
- Tier 2: current month — every 6 hours
- Tier 3: upcoming — twice daily (3 AM + 3 PM)
- Tier 4: full year — daily at 3 AM

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 21:32:38 -04:00
josh 4652a92c29 refactor: hardcode API IDs and coaster lists, remove Playwright discovery
Embed Six Flags API IDs directly in the park registry and snapshot
coaster lists from park-meta.json into a TypeScript module. This
eliminates the Playwright-based discovery script, RCDB scraper, and
runtime dependency on park-meta.json — preparing for the backend
API transition.

- Add apiId field to Park type and all 24 park entries
- Create lib/coaster-data.ts with hardcoded coaster lists
- Update page components to use park.apiId and new getCoasterSet()
- Remove scripts/discover.ts, lib/scrapers/rcdb.ts, lib/park-meta.ts
- Remove data/park-meta.json from shared volume
- Remove playwright devDependency and discover npm script
- Simplify scripts/scrape.ts (no RCDB, no discovery checks)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 21:25:53 -04:00
josh 757c2a8d4f chore: gitignore parks.db
Build and Deploy / Build & Push (push) Successful in 1m39s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 21:02:37 -04:00
josh c5dc01b6ff adds unami
Build and Deploy / Build & Push (push) Successful in 8m48s
2026-04-23 21:00:34 -04:00
josh 0009af751f Update README.md
Build and Deploy / Build & Push (push) Successful in 19s
2026-04-05 17:39:24 -04:00
josh 4063ded9ec Update README.md
Build and Deploy / Build & Push (push) Successful in 1m9s
2026-04-05 17:34:18 -04:00
Josh Wright f0faff412c feat: use dateless Six Flags API endpoint for live today data
Build and Deploy / Build & Push (push) Successful in 17s
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>
2026-04-05 16:54:06 -04:00
Josh Wright 08db97faa8 polish: center-align ride count and weather delay text in park column
Build and Deploy / Build & Push (push) Successful in 53s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 16:07:24 -04:00
Josh Wright 054c82529b polish: center-align weather delay text so it stacks neatly within its box
Build and Deploy / Build & Push (push) Successful in 1m8s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 16:02:23 -04:00
Josh Wright 8437cadee0 polish: weather delay text matches ride count style — blue, no emoji
Build and Deploy / Build & Push (push) Successful in 1m14s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:59:01 -04:00
Josh Wright b4af83b879 fix: weather delay text wraps within its box, no longer collides with park name
Build and Deploy / Build & Push (push) Successful in 54s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:56:12 -04:00
Josh Wright b1204c95cb fix: ride count stays right-aligned, wraps within its own box, never drops below park name
Build and Deploy / Build & Push (push) Successful in 52s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:52:48 -04:00
Josh Wright a5b98f93e6 fix: constrain ride count width so text wraps within its own box
Build and Deploy / Build & Push (push) Successful in 56s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:48:53 -04:00
Josh Wright b2ef342bf4 fix: ride count now wraps below park name instead of colliding
Removed flex:1 from left side so ride count has a real minimum width to
trigger wrapping. Added whiteSpace:nowrap so flexbox knows when to wrap it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:45:49 -04:00
Josh Wright e405170c8b fix: allow ride count to wrap below park name on narrow mobile cards
Build and Deploy / Build & Push (push) Successful in 1m1s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:34:36 -04:00
Josh Wright fd99f6f390 fix: allow ride count to wrap below park name on narrow columns
Build and Deploy / Build & Push (push) Successful in 58s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:30:56 -04:00
Josh Wright 4e6040a781 fix: add right padding to table scroll container to clear scrollbar
Build and Deploy / Build & Push (push) Successful in 57s
Padding on the parent main element doesn't work with overflow:auto — the
fix belongs on the scrollable div wrapping the table itself.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:27:18 -04:00
Josh Wright 7904475ddc polish: add right padding to main content to clear scrollbar
Build and Deploy / Build & Push (push) Successful in 55s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:23:45 -04:00
Josh Wright a84bbcac31 polish: taller week calendar cells with more padding around pills
Build and Deploy / Build & Push (push) Successful in 51s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:20:41 -04:00
Josh Wright 569d0a41e2 polish: more padding and line spacing in month calendar pills, taller min row
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:19:46 -04:00
Josh Wright c6c32a168b polish: more breathing room inside month calendar day pills
Build and Deploy / Build & Push (push) Successful in 1m4s
Increased pill vertical padding (3px→5px) and internal line gaps (1-2px→2-3px)
so the stacked hours/timezone text feels less cramped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:14:01 -04:00
Josh Wright cba8218fe8 feat: replace dot with left border line on park rows/cards
Build and Deploy / Build & Push (push) Successful in 51s
Open parks get a colored left border (green/amber/blue) instead of a dot.
Region headers lose their accent line; distinguished by "— REGION —" format
with higher-contrast text instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:07:42 -04:00
Josh Wright 695feff443 fix: restore Weather Delay text in mobile card ride count area
Build and Deploy / Build & Push (push) Successful in 56s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:02:11 -04:00
Josh Wright f85cc084b7 feat: blue dot + Weather Delay notice for storm-closed parks
- Dot turns blue (instead of green) during weather delay on homepage
- Mobile card "Open today" badge becomes blue "⛈ Weather Delay"
- Park page LiveRidePanel shows a blue "⛈ Weather Delay — all rides currently closed" badge instead of "Not open yet — check back soon"
- Added --color-weather-* CSS variables (blue palette)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 15:01:37 -04:00
Josh Wright 32f0d05038 feat: show open dot based on hours, Weather Delay when queue-times shows 0 rides
Build and Deploy / Build & Push (push) Successful in 49s
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>
2026-04-05 14:56:54 -04:00
Josh Wright d84a15ad64 fix: restore 240px park column width — clamp() unreliable in col elements
Build and Deploy / Build & Push (push) Successful in 54s
The actual overflow fix was removing whiteSpace:nowrap from the td.
With that gone, 240px is sufficient and content wraps naturally when tight.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:48:55 -04:00
Josh Wright b26382f427 polish: clamp park column width, prevent park name line wrap
Build and Deploy / Build & Push (push) Successful in 59s
Column uses clamp(220px, 22%, 280px) — scales on small screens, caps at
280px on large ones. Park name gets whiteSpace:nowrap so it stays one line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:45:28 -04:00
Josh Wright 56c7b90262 fix: responsive park column — percentage width, no nowrap, original font sizes
Build and Deploy / Build & Push (push) Successful in 49s
Root cause was a hardcoded 240px column width + whiteSpace:nowrap that
prevented content from ever fitting on smaller displays. Now uses 25%
width so the column scales with the viewport, removed nowrap so text
wraps naturally when squeezed, and reverted clamp() back to fixed sizes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:41:46 -04:00
Josh Wright 5e4dd7403e fix: keep dot next to park name, scale all text with clamp() on small screens
Build and Deploy / Build & Push (push) Successful in 50s
Removed flex:1/minWidth:0 from name span so dot stays snug to the right
of the park name. Removed flexShrink:0 from ride count so both sides can
compress. All text uses clamp() to scale proportionally with viewport.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:34:30 -04:00
Josh Wright a717e122f0 fix: park name span flex-shrinks so dot and ride count never get crowded
Build and Deploy / Build & Push (push) Successful in 55s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:29:52 -04:00
Josh Wright 732390425f Revert "fix: stack ride count below city/state to prevent overflow on small displays"
This reverts commit a1694668d9.
2026-04-05 14:29:34 -04:00
Josh Wright a1694668d9 fix: stack ride count below city/state to prevent overflow on small displays
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:28:33 -04:00
Josh Wright f809f9171b fix: scale park column text with viewport width using clamp()
Build and Deploy / Build & Push (push) Successful in 56s
Park name, city/state, and ride count all use clamp() so they shrink
proportionally on smaller displays without truncating or overflowing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:25:52 -04:00
Josh Wright fa269db3ef fix: truncate park name with ellipsis to prevent clash with dot and ride count
Build and Deploy / Build & Push (push) Successful in 52s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:22:17 -04:00
Josh Wright ef3e57bd5a fix: prevent ride count overflow in park column on smaller displays
Build and Deploy / Build & Push (push) Successful in 54s
Let the park name side flex-shrink (minWidth:0, flex:1) so the ride count
always fits in the row without overflowing its column.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:12:33 -04:00
Josh Wright ed6d09f3bc Revert "fix: move ride count below park name to prevent overflow on small displays"
This reverts commit e2498af481.
2026-04-05 14:12:15 -04:00
Josh Wright e2498af481 fix: move ride count below park name to prevent overflow on small displays
Build and Deploy / Build & Push (push) Successful in 51s
Removes the space-between flex layout that pushed the count to the right
edge of a fixed-width column. Now stacks under city/state so it always
fits within the park name column regardless of screen size.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:10:45 -04:00
Josh Wright d7f046a4d6 feat: double-tap refresh at park opening — mark open, then fetch ride counts
Build and Deploy / Build & Push (push) Successful in 50s
Fire two refreshes per park: one at opening time to flip the open indicator,
and one 30s later to pick up queue-times ride counts once the API updates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 11:07:14 -04:00
Josh Wright 7c00ae5000 feat: schedule targeted refresh at each park's exact opening time
In addition to the 2-minute polling interval, compute milliseconds until
each park's opening time (from hoursLabel + park timezone) and schedule
a setTimeout to fire 30s after opening. This ensures the open indicator
and ride counts appear immediately rather than waiting up to 2 minutes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 11:06:03 -04:00
Josh Wright 7ee28c7ca3 feat: auto-refresh homepage data every 2 minutes on current week
Build and Deploy / Build & Push (push) Successful in 52s
Uses router.refresh() to re-run server data fetching (ride counts, open
status) without a full page reload. Only runs when viewing the current
week — no need to poll for past/future weeks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 11:00:14 -04:00
Josh Wright e7dac31d22 polish: increase gap between date number and hours pill in calendar cells
Build and Deploy / Build & Push (push) Successful in 50s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 09:43:09 -04:00
Josh Wright c25dafb14c fix: restore full-width pills in park calendar cells, text centered inside
Build and Deploy / Build & Push (push) Successful in 52s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 09:40:18 -04:00