Board time-slider now filters, day-tabs stop blocking (TIRREDESIGN-8 + 11)
CI / ci (push) Has been cancelled
Deploy / build-and-deploy (push) Has been cancelled

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:
2026-04-22 14:44:19 +03:00
parent c2f2c9e089
commit 08f06ff1f4
4 changed files with 66 additions and 8 deletions
+21 -3
View File
@@ -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 () => {
+19 -2
View File
@@ -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,
};
+13 -2
View File
@@ -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);