Files
SixFlagsSuperCalendar/components/ParkCard.tsx
Josh Wright fd45309891
All checks were successful
Build and Deploy / Build & Push (push) Successful in 51s
polish: clarify parks open badge; improve timezone display
- "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>
2026-04-05 08:22:06 -04:00

172 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from "next/link";
import type { Park } from "@/lib/scrapers/types";
import type { DayData } from "@/lib/db";
import { getTimezoneAbbr } from "@/lib/env";
interface ParkCardProps {
park: Park;
weekDates: string[]; // 7 dates YYYY-MM-DD, SunSat
parkData: Record<string, DayData>;
today: string;
openRideCount?: number;
coastersOnly?: boolean;
}
const DOW = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
export function ParkCard({ park, weekDates, parkData, today, openRideCount, coastersOnly }: ParkCardProps) {
const openDays = weekDates.filter((d) => parkData[d]?.isOpen && parkData[d]?.hoursLabel);
const tzAbbr = getTimezoneAbbr(park.timezone);
const isOpenToday = openDays.includes(today);
return (
<Link
href={`/park/${park.id}`}
data-park={park.name.toLowerCase()}
style={{ textDecoration: "none", display: "block" }}
>
<div className="park-card" style={{
background: "var(--color-surface)",
border: "1px solid var(--color-border)",
borderRadius: 12,
overflow: "hidden",
}}>
{/* ── Card header ───────────────────────────────────────────────────── */}
<div style={{
padding: "14px 16px 12px",
display: "flex",
alignItems: "flex-start",
justifyContent: "space-between",
gap: 12,
}}>
<div>
<div style={{
fontSize: "0.95rem",
fontWeight: 600,
color: "var(--color-text)",
lineHeight: 1.2,
}}>
{park.name}
</div>
<div style={{
fontSize: "0.72rem",
color: "var(--color-text-muted)",
marginTop: 3,
}}>
{park.location.city}, {park.location.state}
</div>
</div>
<div style={{ display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 5, flexShrink: 0 }}>
{isOpenToday ? (
<div style={{
background: "var(--color-open-bg)",
border: "1px solid var(--color-open-border)",
borderRadius: 20,
padding: "4px 10px",
fontSize: "0.65rem",
fontWeight: 700,
color: "var(--color-open-text)",
whiteSpace: "nowrap",
letterSpacing: "0.03em",
}}>
Open today
</div>
) : (
<div style={{
background: "transparent",
border: "1px solid var(--color-border)",
borderRadius: 20,
padding: "4px 10px",
fontSize: "0.65rem",
fontWeight: 500,
color: "var(--color-text-muted)",
whiteSpace: "nowrap",
}}>
Closed today
</div>
)}
{isOpenToday && openRideCount !== undefined && (
<div style={{
fontSize: "0.65rem",
color: "var(--color-open-hours)",
fontWeight: 500,
textAlign: "right",
}}>
{openRideCount} {coastersOnly
? (openRideCount === 1 ? "coaster" : "coasters")
: (openRideCount === 1 ? "ride" : "rides")} operating
</div>
)}
</div>
</div>
{/* ── Open days list ────────────────────────────────────────────────── */}
{openDays.length > 0 && (
<div style={{ borderTop: "1px solid var(--color-border-subtle)" }}>
{openDays.map((date, i) => {
const dow = new Date(date + "T00:00:00").getDay();
const isToday = date === today;
const dayData = parkData[date];
const isPH = dayData.specialType === "passholder_preview";
const isLast = i === openDays.length - 1;
return (
<div
key={date}
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "9px 16px",
background: isToday ? "var(--color-today-bg)" : "transparent",
borderBottom: isLast ? "none" : "1px solid var(--color-border-subtle)",
}}
>
<span style={{
fontSize: "0.82rem",
fontWeight: isToday ? 600 : 400,
color: isToday
? "var(--color-today-text)"
: "var(--color-text-secondary)",
}}>
{isToday ? "Today" : DOW[dow]}
</span>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
{isPH && (
<span style={{
fontSize: "0.58rem",
fontWeight: 700,
color: "var(--color-ph-label)",
letterSpacing: "0.05em",
textTransform: "uppercase",
}}>
Passholder
</span>
)}
<span style={{
fontSize: "0.82rem",
fontWeight: isToday ? 600 : 500,
color: isPH
? "var(--color-ph-hours)"
: isToday
? "var(--color-today-text)"
: "var(--color-open-hours)",
}}>
{dayData.hoursLabel}{" "}
<span style={{ fontSize: "0.68rem", fontWeight: 400, opacity: 0.6 }}>
{tzAbbr}
</span>
</span>
</div>
</div>
);
})}
</div>
)}
</div>
</Link>
);
}