Board time-slider now filters, day-tabs stop blocking (TIRREDESIGN-8 + 11)
TIRREDESIGN-11: the board + schedule endpoints ignore the raw 4-digit HHMM query values the slider produces and only honour HH:MM:SS (Angular formats via ApiFormatterService.formatTime). Normalise both at the API layer so the slider actually narrows results; '2400' collapses to '23:59:59' since midnight-of-next-day isn't representable. TIRREDESIGN-8: the 31-day availability bitmask is always anchored to today (Angular parity — updateCalendar() uses new Date() - 1). We were passing params.date as the anchor, which shifted the window forward every time the user picked a future day and caused earlier DayTabs to fall outside the returned bitmask, grey-listing days that still have flights.
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
@@ -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<IBoardResponse>(`flights/v1.1/${client.locale}/board`, query, signal);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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<IScheduleResponse>(
|
||||
`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);
|
||||
|
||||
Reference in New Issue
Block a user