feat: UI redesign with park detail pages and ride status
Some checks failed
Build and Deploy / Build & Push (push) Failing after 22s

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>
This commit is contained in:
2026-04-04 11:53:06 -04:00
parent 5f82407fea
commit e48038c399
17 changed files with 1605 additions and 442 deletions

View File

@@ -99,6 +99,42 @@ export function getDateRange(
return result;
}
/**
* Returns scraped DayData for a single park for an entire month.
* Shape: { 'YYYY-MM-DD': DayData }
*/
export function getParkMonthData(
db: Database.Database,
parkId: string,
year: number,
month: number,
): Record<string, DayData> {
const prefix = `${year}-${String(month).padStart(2, "0")}`;
const rows = db
.prepare(
`SELECT date, is_open, hours_label, special_type
FROM park_days
WHERE park_id = ? AND date LIKE ? || '-%'
ORDER BY date`
)
.all(parkId, prefix) as {
date: string;
is_open: number;
hours_label: string | null;
special_type: string | null;
}[];
const result: Record<string, DayData> = {};
for (const row of rows) {
result[row.date] = {
isOpen: row.is_open === 1,
hoursLabel: row.hours_label,
specialType: row.special_type,
};
}
return result;
}
/** Returns a map of parkId → boolean[] (index 0 = day 1) for a given month. */
export function getMonthCalendar(
db: Database.Database,