Files
SixFlagsSuperCalendar/components/charts/WaitTimeTodayChart.tsx
T
josh 44d079efb9
Build and Deploy / Build & Push (push) Successful in 1m8s
fix: render today's wait chart in viewer's local time + close stale live state
Two related polish fixes for the ride detail page:

1. Wait-time chart x-axis now uses Intl.DateTimeFormat with no timezone
   argument, so an Eastern-time user viewing a Pacific park sees ET on
   the axis. Backend now sends recorded_at (UTC) alongside local_time.

2. Ride-history endpoint now applies the same operating-window gate the
   /rides route uses. Queue-Times keeps reporting yesterday's last wait
   with isOpen=true overnight, which made the "Right now" pill show a
   live wait time when the park was actually closed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 08:23:00 -04:00

98 lines
3.0 KiB
TypeScript

"use client";
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from "recharts";
export interface TodaySample {
recordedAt: string;
localTime: string;
isOpen: boolean;
waitMinutes: number | null;
fastLaneMinutes: number | null;
}
interface Props {
samples: TodaySample[];
hasFastLane: boolean;
}
const TIME_FMT = new Intl.DateTimeFormat([], {
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
export default function WaitTimeTodayChart({ samples, hasFastLane }: Props) {
// Map samples: closed periods → null so Recharts breaks the line.
// X-axis time is rendered in the viewer's local timezone (Intl with no
// tz arg) so an Eastern-time user sees ET regardless of which park.
const data = samples.map((s) => ({
time: TIME_FMT.format(new Date(s.recordedAt)),
wait: s.isOpen ? s.waitMinutes : null,
fl: s.isOpen ? s.fastLaneMinutes : null,
}));
// Show every Nth tick on the X axis so labels don't overlap.
const tickInterval = Math.max(1, Math.floor(data.length / 8));
return (
<div style={{ width: "100%", height: 280 }}>
<ResponsiveContainer>
<LineChart data={data} margin={{ top: 8, right: 16, left: 0, bottom: 4 }}>
<CartesianGrid stroke="#272727" strokeDasharray="3 3" vertical={false} />
<XAxis
dataKey="time"
stroke="#737373"
tick={{ fontSize: 11, fill: "#737373" }}
interval={tickInterval}
tickLine={false}
/>
<YAxis
stroke="#737373"
tick={{ fontSize: 11, fill: "#737373" }}
tickLine={false}
axisLine={false}
label={{ value: "min", position: "insideLeft", angle: -90, offset: 16, style: { fontSize: 10, fill: "#737373" } }}
/>
<Tooltip
contentStyle={{
background: "#1c1c1c",
border: "1px solid #333",
borderRadius: 6,
fontSize: "0.75rem",
color: "#f5f5f5",
}}
labelStyle={{ color: "#b0b0b0" }}
formatter={(value, name) => {
if (value === null || value === undefined) return ["—", name];
return [`${value} min`, name];
}}
/>
<Legend wrapperStyle={{ fontSize: "0.72rem", paddingTop: 4 }} />
<Line
name="Wait"
type="monotone"
dataKey="wait"
stroke="#4ade80"
strokeWidth={2}
dot={false}
connectNulls={false}
isAnimationActive={false}
/>
{hasFastLane && (
<Line
name="Fast Lane"
type="monotone"
dataKey="fl"
stroke="#ff4d8d"
strokeWidth={2}
dot={false}
connectNulls={false}
isAnimationActive={false}
/>
)}
</LineChart>
</ResponsiveContainer>
</div>
);
}