diff --git a/src/features/online-board/api.test.ts b/src/features/online-board/api.test.ts index 48396cc5..14aabd5f 100644 --- a/src/features/online-board/api.test.ts +++ b/src/features/online-board/api.test.ts @@ -105,7 +105,10 @@ describe("searchFlights", () => { expect(url.searchParams.get("arrival")).toBe("JFK"); }); - it("includes timeFrom and timeTo when provided", async () => { + // TIRREDESIGN-11: the backend ignores the raw 4-digit HHMM and only + // honours HH:MM:SS. Convert at the API layer (Angular parity — + // ApiFormatterService.formatTime). '2400' collapses to '23:59:59'. + it("normalises timeFrom/timeTo to HH:MM:SS before sending", async () => { const { client, mockFetch } = createMockClient(BOARD_RESPONSE); await searchFlights(client, { @@ -116,8 +119,23 @@ describe("searchFlights", () => { }); const url = extractUrl(mockFetch); - expect(url.searchParams.get("timeFrom")).toBe("0800"); - expect(url.searchParams.get("timeTo")).toBe("2000"); + expect(url.searchParams.get("timeFrom")).toBe("08:00:00"); + expect(url.searchParams.get("timeTo")).toBe("20:00:00"); + }); + + it("collapses timeTo='2400' to '23:59:59'", async () => { + const { client, mockFetch } = createMockClient(BOARD_RESPONSE); + + await searchFlights(client, { + dateFrom: "20250115", + dateTo: "20250116", + timeFrom: "0000", + timeTo: "2400", + }); + + const url = extractUrl(mockFetch); + expect(url.searchParams.get("timeFrom")).toBe("00:00:00"); + expect(url.searchParams.get("timeTo")).toBe("23:59:59"); }); it("omits optional params when not provided", async () => { diff --git a/src/features/online-board/api.ts b/src/features/online-board/api.ts index 6234a877..7a2cc306 100644 --- a/src/features/online-board/api.ts +++ b/src/features/online-board/api.ts @@ -59,6 +59,21 @@ export interface CalendarParams { // API functions // --------------------------------------------------------------------------- +/** + * Convert a 4-digit 'HHMM' string to the backend's expected 'HH:MM:SS' + * form (Angular parity — ApiFormatterService.formatTime). + * + * TIRREDESIGN-11: the URL carries the slider range as '1000'/'1200' but + * the board endpoint ignores numeric 'HHMM' values; it only honours + * 'HH:MM:SS'. '2400' collapses to '23:59:59' since midnight of the next + * day isn't representable in the format. + */ +function formatApiTime(hhmm: string | undefined): string | undefined { + if (!hhmm || hhmm.length !== 4) return undefined; + if (hhmm === "2400") return "23:59:59"; + return `${hhmm.slice(0, 2)}:${hhmm.slice(2, 4)}:00`; +} + /** * Search flights on the online board. * Maps to: `GET /board?flightNumber=...&dateFrom=...&dateTo=...&...` @@ -76,8 +91,10 @@ export async function searchFlights( if (params.flightNumber) query["flightNumber"] = params.flightNumber; if (params.departure) query["departure"] = params.departure; if (params.arrival) query["arrival"] = params.arrival; - if (params.timeFrom) query["timeFrom"] = params.timeFrom; - if (params.timeTo) query["timeTo"] = params.timeTo; + const tf = formatApiTime(params.timeFrom); + const tt = formatApiTime(params.timeTo); + if (tf) query["timeFrom"] = tf; + if (tt) query["timeTo"] = tt; return client.get(`flights/v1.1/${client.locale}/board`, query, signal); } diff --git a/src/features/online-board/components/OnlineBoardSearchPage.tsx b/src/features/online-board/components/OnlineBoardSearchPage.tsx index c01e3c5c..f0e0a8bb 100644 --- a/src/features/online-board/components/OnlineBoardSearchPage.tsx +++ b/src/features/online-board/components/OnlineBoardSearchPage.tsx @@ -128,12 +128,24 @@ function toSearchParams( /** * Convert parsed params into calendar API params. + * + * TIRREDESIGN-8: the 31-day availability bitmask is always anchored to + * today (Angular parity — `updateCalendar()` builds `date = new Date(); + * date.setDate(date.getDate()-1)` regardless of the URL-selected day). + * Using `params.date` as the anchor shifts the window forward as the + * user navigates and causes DayTabs inside the -1/+14 range to fall + * outside the returned bitmask, grey-listing them even when flights run + * that day. */ function toCalendarParams( params: OnlineBoardSearchPageProps["params"], ): CalendarParams { + const today = new Date(); + const y = today.getFullYear().toString(); + const m = (today.getMonth() + 1).toString().padStart(2, "0"); + const d = today.getDate().toString().padStart(2, "0"); const base: CalendarParams = { - date: formatDateForApi(params.date), + date: `${y}-${m}-${d}T00:00:00`, searchType: params.type as FlightRequestType, }; diff --git a/src/features/schedule/api.ts b/src/features/schedule/api.ts index 448837e2..6417c2a4 100644 --- a/src/features/schedule/api.ts +++ b/src/features/schedule/api.ts @@ -42,8 +42,13 @@ export async function searchSchedule( dateTo: addOneDayIso(params.dateTo), connections: String(params.connections ?? 1), }; - if (params.timeFrom) query["timeFrom"] = params.timeFrom; - if (params.timeTo) query["timeTo"] = params.timeTo; + // TIRREDESIGN-11: the slider carries its value as four-digit 'HHMM' but + // the endpoint only honours the colon-delimited 'HH:MM:SS' form (Angular's + // ApiFormatterService.formatTime). '2400' collapses to '23:59:59'. + const tf = formatApiTime(params.timeFrom); + const tt = formatApiTime(params.timeTo); + if (tf) query["timeFrom"] = tf; + if (tt) query["timeTo"] = tt; if (params.attribute) query["attribute"] = String(params.attribute); return client.get( `flights/1/${client.locale}/schedule`, @@ -52,6 +57,12 @@ export async function searchSchedule( ); } +function formatApiTime(hhmm: string | undefined): string | undefined { + if (!hhmm || hhmm.length !== 4) return undefined; + if (hhmm === "2400") return "23:59:59"; + return `${hhmm.slice(0, 2)}:${hhmm.slice(2, 4)}:00`; +} + /** Return the date one day after a yyyy-MM-dd string (passthrough on bad input). */ function addOneDayIso(iso: string): string { const m = /^(\d{4})-(\d{2})-(\d{2})(.*)$/.exec(iso);