feat: amber indicator during post-close wind-down buffer
All checks were successful
Build and Deploy / Build & Push (push) Successful in 2m22s

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>
This commit is contained in:
Josh Wright
2026-04-05 09:06:45 -04:00
parent 090f4d876d
commit 53297a7cff
7 changed files with 57 additions and 14 deletions

View File

@@ -55,10 +55,21 @@ export function getTimezoneAbbr(timezone: string): string {
* compared to Pacific time regardless of where the server is running.
*/
export function isWithinOperatingWindow(hoursLabel: string, timezone: string): boolean {
return getOperatingStatus(hoursLabel, timezone) !== "closed";
}
/**
* Returns the park's current operating status relative to its scheduled hours:
* "open" — within the scheduled open window
* "closing" — past scheduled close but within the 1-hour wind-down buffer
* "closed" — outside the window entirely
* Falls back to "open" when the label can't be parsed.
*/
export function getOperatingStatus(hoursLabel: string, timezone: string): "open" | "closing" | "closed" {
const m = hoursLabel.match(
/^(\d+)(?::(\d+))?(am|pm)\s*[-]\s*(\d+)(?::(\d+))?(am|pm)$/i
);
if (!m) return true;
if (!m) return "open";
const toMinutes = (h: string, min: string | undefined, period: string) => {
let hours = parseInt(h, 10);
const minutes = min ? parseInt(min, 10) : 0;
@@ -80,5 +91,7 @@ export function isWithinOperatingWindow(hoursLabel: string, timezone: string): b
const min = parseInt(parts.find((p) => p.type === "minute")?.value ?? "0", 10);
const nowMin = (h % 24) * 60 + min;
return nowMin >= openMin && nowMin <= closeMin + 60;
if (nowMin >= openMin && nowMin <= closeMin) return "open";
if (nowMin > closeMin && nowMin <= closeMin + 60) return "closing";
return "closed";
}