/** * Session-scoped store for cross-section filter state (TZ §4.1.8 Table 10). * In-memory only — cleared on page reload. Projection functions implement * the exact field mappings from Table 10 when a user navigates between * Online-Board ↔ Schedule. Map filter is stored separately and is NEVER * projected from / to Board or Schedule (per TZ §4.1.1 ¶12). */ export interface BoardFilterSnapshot { mode: "route" | "flight-number" | "departure" | "arrival"; departure?: string; arrival?: string; flightNumber?: string; date: string; // yyyyMMdd timeFrom: string; // HHmm timeTo: string; // HHmm searchExecuted: boolean; } export interface ScheduleFilterSnapshot { mode: "route"; departure?: string; arrival?: string; dateFrom: string; // yyyyMMdd dateTo: string; timeFrom: string; timeTo: string; onlyDirect: boolean; showReturn: boolean; returnDateFrom?: string; returnDateTo?: string; returnTimeFrom?: string; returnTimeTo?: string; searchExecuted: boolean; } export interface MapFilterSnapshot { departure: string | null; arrival: string | null; date: string | null; showInternal: boolean; showInternational: boolean; showTransfers: boolean; } // Module-level in-memory state let board: BoardFilterSnapshot | null = null; let schedule: ScheduleFilterSnapshot | null = null; let map: MapFilterSnapshot | null = null; export function setBoardFilter(s: BoardFilterSnapshot): void { board = s; } export function getBoardFilter(): BoardFilterSnapshot | null { return board; } export function setScheduleFilter(s: ScheduleFilterSnapshot): void { schedule = s; } export function getScheduleFilter(): ScheduleFilterSnapshot | null { return schedule; } export function setMapFilter(s: MapFilterSnapshot): void { map = s; } export function getMapFilter(): MapFilterSnapshot | null { return map; } export function resetCrossSectionStore(): void { board = null; schedule = null; map = null; } function currentWeekBounds(base: Date): { start: string; end: string } { const d = new Date(base); d.setHours(0, 0, 0, 0); const dow = (d.getDay() + 6) % 7; // 0 = Mon .. 6 = Sun d.setDate(d.getDate() - dow); const start = new Date(d); const end = new Date(d); end.setDate(end.getDate() + 6); const y = (x: Date) => `${x.getFullYear()}${String(x.getMonth() + 1).padStart(2, "0")}${String(x.getDate()).padStart(2, "0")}`; return { start: y(start), end: y(end) }; } function todayYyyymmdd(): string { const d = new Date(); return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`; } /** TZ Table 10: Board → Schedule projection. Preserves cities + dateFrom=current-week. */ export function projectBoardToSchedule(b: BoardFilterSnapshot): ScheduleFilterSnapshot { const base = b.date && /^\d{8}$/.test(b.date) ? new Date(Number(b.date.slice(0, 4)), Number(b.date.slice(4, 6)) - 1, Number(b.date.slice(6, 8))) : new Date(); const week = currentWeekBounds(base); return { mode: "route", ...(b.departure ? { departure: b.departure } : {}), ...(b.arrival ? { arrival: b.arrival } : {}), dateFrom: week.start, dateTo: week.end, timeFrom: "0000", timeTo: "2400", onlyDirect: false, showReturn: false, searchExecuted: false, }; } /** TZ Table 10: Schedule → Board projection. Preserves cities + date=today. */ export function projectScheduleToBoard(s: ScheduleFilterSnapshot): BoardFilterSnapshot { return { mode: "route", ...(s.departure ? { departure: s.departure } : {}), ...(s.arrival ? { arrival: s.arrival } : {}), date: todayYyyymmdd(), timeFrom: "0000", timeTo: "2400", searchExecuted: false, }; }