feat: detect passholder preview days and filter plain buyouts
All checks were successful
Build and Deploy / Build & Push (push) Successful in 3m9s

- Buyout days are now treated as closed unless they carry a Passholder
  Preview event, in which case they surface as a distinct purple cell
  in the UI showing "Passholder" + hours
- DB gains a special_type column (auto-migrated on next startup)
- scrape.ts threads specialType through to upsertDay
- debug.ts now shows events, isBuyout, isPassholderPreview, and
  specialType in the parsed result section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 10:53:05 -04:00
parent 7c28d8f89f
commit 91e09b0548
6 changed files with 114 additions and 26 deletions

View File

@@ -34,6 +34,7 @@ export interface DayResult {
date: string; // YYYY-MM-DD
isOpen: boolean;
hoursLabel?: string;
specialType?: "passholder_preview";
}
function sleep(ms: number) {
@@ -49,6 +50,7 @@ function parseApiDate(d: string): string {
interface ApiOperatingItem {
timeFrom: string; // "10:30" 24h
timeTo: string; // "20:00" 24h
isBuyout?: boolean;
}
interface ApiOperating {
@@ -56,9 +58,14 @@ interface ApiOperating {
items: ApiOperatingItem[];
}
interface ApiEvent {
extEventName: string;
}
interface ApiDay {
date: string;
isParkClosed: boolean;
events?: ApiEvent[];
operatings?: ApiOperating[];
}
@@ -135,9 +142,18 @@ export async function scrapeMonth(
item?.timeFrom && item?.timeTo
? `${fmt24(item.timeFrom)} ${fmt24(item.timeTo)}`
: undefined;
// If the API says open but no hours are available, treat as closed
const isOpen = !d.isParkClosed && hoursLabel !== undefined;
return { date, isOpen, hoursLabel };
const isPassholderPreview = d.events?.some((e) =>
e.extEventName.toLowerCase().includes("passholder preview")
) ?? false;
const isBuyout = item?.isBuyout ?? false;
// Buyout days are private events — treat as closed unless it's a passholder preview
const isOpen = !d.isParkClosed && hoursLabel !== undefined && (!isBuyout || isPassholderPreview);
const specialType: DayResult["specialType"] = isPassholderPreview ? "passholder_preview" : undefined;
return { date, isOpen, hoursLabel: isOpen ? hoursLabel : undefined, specialType };
});
}