From be0d5e686b62b8ab4fcf3091fde20c076bba7fbd Mon Sep 17 00:00:00 2001 From: gnezim Date: Thu, 30 Apr 2026 08:39:14 +0300 Subject: [PATCH] Fix map route reset after city reselection --- .../components/FlightsMapFilter.tsx | 13 ++++- .../components/FlightsMapStartPage.test.tsx | 26 ++++++++++ .../components/FlightsMapStartPage.tsx | 49 +++++++++++-------- .../flights-map/hooks/useFlightsMapSearch.ts | 1 + 4 files changed, 66 insertions(+), 23 deletions(-) diff --git a/src/features/flights-map/components/FlightsMapFilter.tsx b/src/features/flights-map/components/FlightsMapFilter.tsx index 2f7b60a5..2e8de136 100644 --- a/src/features/flights-map/components/FlightsMapFilter.tsx +++ b/src/features/flights-map/components/FlightsMapFilter.tsx @@ -178,7 +178,12 @@ export const FlightsMapFilter: FC = ({ }); return; } - onChange({ ...value, departure: code, arrival: undefined }); + onChange({ + ...value, + departure: code, + arrival: undefined, + connections: false, + }); }} dictionaries={dictionaries} onLocate={handleLocate} @@ -196,7 +201,11 @@ export const FlightsMapFilter: FC = ({ placeholder={t("FLIGHTS-MAP.FILTER_ARRIVAL_PLACEHOLDER")} value={value.arrival ?? ""} onChange={(code) => { - onChange({ ...value, arrival: code || undefined }); + onChange({ + ...value, + arrival: code || undefined, + connections: code ? value.connections : false, + }); }} dictionaries={dictionaries} testIdPrefix="fm-arrival" diff --git a/src/features/flights-map/components/FlightsMapStartPage.test.tsx b/src/features/flights-map/components/FlightsMapStartPage.test.tsx index 061db86c..1e063278 100644 --- a/src/features/flights-map/components/FlightsMapStartPage.test.tsx +++ b/src/features/flights-map/components/FlightsMapStartPage.test.tsx @@ -910,6 +910,32 @@ describe("§4.1.24.4: interactive map click sequence", () => { expect(filterValue["arrival"]).toBeUndefined(); }); + it("4.1.24-R39: third click returns to spider mode with connections reset", () => { + searchState.routes = [{ route: ["MOW", "LED"], isDirect: true }]; + setMapFilter({ + departure: "MOW", + arrival: "LED", + date: null, + showInternal: false, + showInternational: false, + showTransfers: true, + }); + + render(); + act(() => { + (lastMapCanvasProps!["onMarkerClick"] as (id: string) => void)("MOW"); + }); + + const filterValue = lastMapFilterProps!["value"] as Record; + expect(filterValue["departure"]).toBe("MOW"); + expect(filterValue["arrival"]).toBeUndefined(); + expect(filterValue["connections"]).toBe(false); + + const polylines = lastMapCanvasProps!["polylines"] as Array<{ cityIds: string[] }>; + expect(polylines).toHaveLength(1); + expect(polylines[0]!.cityIds).toEqual(["MOW", "LED"]); + }); + it("4.1.24-R37: buy-ticket popup appears after second click (arrival set)", () => { render(); act(() => { diff --git a/src/features/flights-map/components/FlightsMapStartPage.tsx b/src/features/flights-map/components/FlightsMapStartPage.tsx index 9887d0a1..fd864598 100644 --- a/src/features/flights-map/components/FlightsMapStartPage.tsx +++ b/src/features/flights-map/components/FlightsMapStartPage.tsx @@ -146,6 +146,19 @@ export const FlightsMapStartPage: FC = ({ filterState.connections ? 1 : 0, ); + const persistFilterState = useCallback((newState: IFlightsMapFilterState) => { + setFilterState(newState); + // TZ §4.1.8 / 4.1.1-R26: persist map filter independently from Board/Schedule. + setMapFilter({ + departure: newState.departure ?? null, + arrival: newState.arrival ?? null, + date: newState.date ?? null, + showInternal: newState.domestic, + showInternational: newState.international, + showTransfers: newState.connections, + }); + }, []); + // Sync user toggle → effective. useEffect(() => { setEffectiveConnections(filterState.connections ? 1 : 0); @@ -160,7 +173,7 @@ export const FlightsMapStartPage: FC = ({ arrival: filterState.arrival, dateFrom: todayYmd, dateTo: addMonthsYyyymmdd(todayYmd, 6), - connections: effectiveConnections, + connections: filterState.arrival ? effectiveConnections : 0, }; }, [filterState.departure, filterState.arrival, effectiveConnections, todayYmd]); @@ -172,7 +185,7 @@ export const FlightsMapStartPage: FC = ({ date: todayYmd, departure: filterState.departure, arrival: filterState.arrival, - connections: filterState.connections, + connections: filterState.arrival ? filterState.connections : false, }; }, [filterState.departure, filterState.arrival, filterState.connections, todayYmd]); @@ -197,39 +210,33 @@ export const FlightsMapStartPage: FC = ({ // Reflect fallback in the UI toggle once. useEffect(() => { - if (effectiveConnections === 1 && !filterState.connections) { + if (filterState.arrival && effectiveConnections === 1 && !filterState.connections) { setFilterState((prev) => ({ ...prev, connections: true })); } - }, [effectiveConnections, filterState.connections]); + }, [effectiveConnections, filterState.arrival, filterState.connections]); const handleFilterChange = useCallback((newState: IFlightsMapFilterState) => { - setFilterState(newState); - // TZ §4.1.8 / 4.1.1-R26: persist map filter independently from Board/Schedule. - setMapFilter({ - departure: newState.departure ?? null, - arrival: newState.arrival ?? null, - date: newState.date ?? null, - showInternal: newState.domestic, - showInternational: newState.international, - showTransfers: newState.connections, - }); - }, []); + persistFilterState(newState); + }, [persistFilterState]); const handleMarkerClick = useCallback( (markerId: string) => { if (!filterState.departure) { - setFilterState((prev): IFlightsMapFilterState => ({ ...prev, departure: markerId })); + persistFilterState({ ...filterState, departure: markerId }); } else if (!filterState.arrival && markerId !== filterState.departure) { - setFilterState((prev): IFlightsMapFilterState => ({ ...prev, arrival: markerId })); + persistFilterState({ ...filterState, arrival: markerId }); } else { - setFilterState((prev): IFlightsMapFilterState => ({ - ...prev, + persistFilterState({ + ...filterState, departure: markerId, arrival: undefined, - })); + // Angular fetchAndDrawSpider forces connections=false before drawing. + // Keep the disabled UI state and spider-mode route rendering aligned. + connections: false, + }); } }, - [filterState.departure, filterState.arrival], + [filterState, persistFilterState], ); const markers = useMemo(() => { diff --git a/src/features/flights-map/hooks/useFlightsMapSearch.ts b/src/features/flights-map/hooks/useFlightsMapSearch.ts index 6194da18..83caced4 100644 --- a/src/features/flights-map/hooks/useFlightsMapSearch.ts +++ b/src/features/flights-map/hooks/useFlightsMapSearch.ts @@ -50,6 +50,7 @@ export function useFlightsMapSearch( let cancelled = false; setLoading(true); setError(null); + setRoutes([]); searchDestinations(client, paramsRef.current) .then((response: IDestinationsResponse) => {