From eda44d42181b48b9d425478d966fa7c97889e64e Mon Sep 17 00:00:00 2001 From: gnezim Date: Wed, 6 May 2026 12:53:21 +0300 Subject: [PATCH] Align flights map date window with Angular --- .../components/FlightsMapStartPage.test.tsx | 45 ++++++++++++++----- .../components/FlightsMapStartPage.tsx | 39 +++++++++++++--- 2 files changed, 68 insertions(+), 16 deletions(-) diff --git a/src/features/flights-map/components/FlightsMapStartPage.test.tsx b/src/features/flights-map/components/FlightsMapStartPage.test.tsx index 1e063278..b61bae02 100644 --- a/src/features/flights-map/components/FlightsMapStartPage.test.tsx +++ b/src/features/flights-map/components/FlightsMapStartPage.test.tsx @@ -8,7 +8,10 @@ import { FlightsMapStartPage } from "./FlightsMapStartPage.js"; import type * as DictionariesModuleNS from "@/shared/dictionaries/index.js"; import { transformDictionaries } from "@/shared/dictionaries/index.js"; import type { IDictionaries, IRawDictionaries } from "@/shared/dictionaries/index.js"; -import type { FlightsMapSearchParams } from "../types.js"; +import type { + FlightsMapCalendarParams, + FlightsMapSearchParams, +} from "../types.js"; import { resetCrossSectionStore, setMapFilter, @@ -82,8 +85,12 @@ vi.mock("../hooks/useFlightsMapSearch.js", () => ({ }, })); +const calendarCalls: Array = []; vi.mock("../hooks/useFlightsMapCalendar.js", () => ({ - useFlightsMapCalendar: () => ({ availableDays: [] }), + useFlightsMapCalendar: (params: FlightsMapCalendarParams | null) => { + calendarCalls.push(params); + return { availableDays: [] }; + }, })); const dictState: { @@ -984,6 +991,7 @@ describe("§4.1.24.5 R44: routes API uses [-1d, +6mo] date window", () => { beforeEach(() => { lastMapFilterProps = null; searchCalls.length = 0; + calendarCalls.length = 0; dictState.dictionaries = buildDictionaries(); dictState.loading = false; dictState.error = null; @@ -1000,21 +1008,36 @@ describe("§4.1.24.5 R44: routes API uses [-1d, +6mo] date window", () => { expect(callsWithDeparture).toHaveLength(0); }); - it("4.1.24-R44: dateFrom/dateTo span today to +6 months when departure is set", () => { + it("4.1.24-R44: dateFrom starts yesterday and dateTo spans to +6 months when departure is set", () => { resetCrossSectionStore(); setMapFilter({ departure: "MOW", arrival: null, date: null, showInternal: false, showInternational: false, showTransfers: false, }); - render(); + render(); const callsWithDeparture = searchCalls.filter((p) => p?.departure === "MOW"); - if (callsWithDeparture.length > 0) { - const call = callsWithDeparture[0]!; - const today = new Date(); - const expectedYear = today.getFullYear().toString(); - expect(call.dateFrom.slice(0, 4)).toBe(expectedYear); - expect(call.dateTo.slice(0, 4)).toMatch(/^\d{4}$/); - } + expect(callsWithDeparture).not.toHaveLength(0); + expect(callsWithDeparture[0]).toMatchObject({ + departure: "MOW", + dateFrom: "20260505", + dateTo: "20261105", + }); + }); + + it("4.1.24-R44: calendar availability request starts from the same yesterday anchor", () => { + resetCrossSectionStore(); + setMapFilter({ + departure: "MOW", arrival: null, date: null, + showInternal: false, showInternational: false, showTransfers: false, + }); + render(); + + const callsWithDeparture = calendarCalls.filter((p) => p?.departure === "MOW"); + expect(callsWithDeparture).not.toHaveLength(0); + expect(callsWithDeparture[0]).toMatchObject({ + departure: "MOW", + date: "20260505", + }); }); }); diff --git a/src/features/flights-map/components/FlightsMapStartPage.tsx b/src/features/flights-map/components/FlightsMapStartPage.tsx index fd864598..0c13e3cf 100644 --- a/src/features/flights-map/components/FlightsMapStartPage.tsx +++ b/src/features/flights-map/components/FlightsMapStartPage.tsx @@ -63,6 +63,18 @@ function addMonthsYyyymmdd(base: string, months: number): string { return `${ry}${rm}${rd}`; } +function addDaysYyyymmdd(base: string, days: number): string { + const y = Number(base.slice(0, 4)); + const m = Number(base.slice(4, 6)) - 1; + const d = Number(base.slice(6, 8)); + const date = new Date(y, m, d); + date.setDate(date.getDate() + days); + const ry = date.getFullYear().toString(); + const rm = (date.getMonth() + 1).toString().padStart(2, "0"); + const rd = date.getDate().toString().padStart(2, "0"); + return `${ry}${rm}${rd}`; +} + // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- @@ -94,6 +106,10 @@ export const FlightsMapStartPage: FC = ({ // there but always called to keep hook order stable. const fallbackTodayYmd = useRef(todayYyyymmdd()).current; const todayYmd = today ?? fallbackTodayYmd; + const searchDateFromYmd = useMemo( + () => addDaysYyyymmdd(todayYmd, -1), + [todayYmd], + ); const { t } = useTranslation(); const { language } = useLocale(); @@ -171,23 +187,36 @@ export const FlightsMapStartPage: FC = ({ return { departure: filterState.departure, arrival: filterState.arrival, - dateFrom: todayYmd, - dateTo: addMonthsYyyymmdd(todayYmd, 6), + // Angular starts the flights-map destinations window at minDateCalendar + // (today - 1 day). Keeping this aligned preserves Moscow domestic + // airport-internal directions such as DME-SVO and ZIA-SVO. + dateFrom: searchDateFromYmd, + dateTo: addMonthsYyyymmdd(searchDateFromYmd, 6), connections: filterState.arrival ? effectiveConnections : 0, }; - }, [filterState.departure, filterState.arrival, effectiveConnections, todayYmd]); + }, [ + filterState.departure, + filterState.arrival, + effectiveConnections, + searchDateFromYmd, + ]); // Build calendar params const calendarParams = useMemo(() => { if (!filterState.departure) return null; return { - date: todayYmd, + date: searchDateFromYmd, departure: filterState.departure, arrival: filterState.arrival, connections: filterState.arrival ? filterState.connections : false, }; - }, [filterState.departure, filterState.arrival, filterState.connections, todayYmd]); + }, [ + filterState.departure, + filterState.arrival, + filterState.connections, + searchDateFromYmd, + ]); const { routes, loading, error } = useFlightsMapSearch(searchParams); const { availableDays } = useFlightsMapCalendar(calendarParams);