diff --git a/src/features/flights-map/components/FlightsMapStartPage.test.tsx b/src/features/flights-map/components/FlightsMapStartPage.test.tsx
index 012be519..c9f13c41 100644
--- a/src/features/flights-map/components/FlightsMapStartPage.test.tsx
+++ b/src/features/flights-map/components/FlightsMapStartPage.test.tsx
@@ -476,6 +476,28 @@ describe("FlightsMapStartPage — C.4 integration", () => {
expect(popups[1]!.content).toContain("https://www.aeroflot.ru/sb/app/ru-ru");
});
+ it("does not draw malformed multi-hop direct routes when transfers are off", () => {
+ searchState.routes = [
+ { route: ["SVO", "LED"], isDirect: true },
+ { route: ["SVO", "AER", "LED"], isDirect: true },
+ ];
+ setMapFilter({
+ departure: "MOW",
+ arrival: "LED",
+ date: null,
+ showInternal: false,
+ showInternational: false,
+ showTransfers: false,
+ });
+
+ render();
+
+ const polylines = lastMapCanvasProps!["polylines"] as Array<{ cityIds: string[] }>;
+ expect(polylines).toEqual([
+ expect.objectContaining({ cityIds: ["MOW", "LED"] }),
+ ]);
+ });
+
it("does not render popups in spider mode (departure only)", () => {
searchState.routes = [{ route: ["MOW", "LED"], isDirect: true }];
render();
diff --git a/src/features/flights-map/filterRoutes.test.ts b/src/features/flights-map/filterRoutes.test.ts
index d763462d..5c04144b 100644
--- a/src/features/flights-map/filterRoutes.test.ts
+++ b/src/features/flights-map/filterRoutes.test.ts
@@ -102,6 +102,49 @@ describe("filterRoutes — connections", () => {
});
});
+describe("filterRoutes — route mode without transfer-only toggle", () => {
+ it("drops malformed direct routes that contain intermediate cities", () => {
+ const routes: IFlightRoute[] = [
+ { route: ["SVO", "LED"], isDirect: true },
+ { route: ["SVO", "AER", "LED"], isDirect: true },
+ { route: ["SVO", "AER", "LED"], isDirect: false },
+ ];
+
+ const out = filterRoutes(
+ routes,
+ filter({ departure: "MOW", arrival: "LED", connections: false }),
+ d,
+ );
+
+ expect(out).toEqual([{ route: ["SVO", "LED"], isDirect: true }]);
+ });
+
+ it("keeps connecting routes in route mode when transfer-only toggle is active", () => {
+ const routes: IFlightRoute[] = [
+ { route: ["SVO", "LED"], isDirect: true },
+ { route: ["SVO", "AER", "LED"], isDirect: false },
+ ];
+
+ const out = filterRoutes(
+ routes,
+ filter({ departure: "MOW", arrival: "LED", connections: true }),
+ d,
+ );
+
+ expect(out).toEqual([{ route: ["SVO", "AER", "LED"], isDirect: false }]);
+ });
+
+ it("does not apply endpoint-direct filtering in spider mode", () => {
+ const routes: IFlightRoute[] = [
+ { route: ["SVO", "AER", "LED"], isDirect: true },
+ ];
+
+ const out = filterRoutes(routes, filter({ departure: "MOW" }), d);
+
+ expect(out).toEqual(routes);
+ });
+});
+
describe("filterRoutes — airport-code normalization", () => {
it("treats airport codes (SVO, JFK) as their city codes (MOW, NYC)", () => {
const routes: IFlightRoute[] = [
diff --git a/src/features/flights-map/filterRoutes.ts b/src/features/flights-map/filterRoutes.ts
index f9363972..98a7ec25 100644
--- a/src/features/flights-map/filterRoutes.ts
+++ b/src/features/flights-map/filterRoutes.ts
@@ -17,7 +17,10 @@ import type { IFlightRoute, IFlightsMapFilterState } from "./types.js";
export function filterRoutes(
routes: IFlightRoute[],
- filter: Pick,
+ filter: Pick<
+ IFlightsMapFilterState,
+ "departure" | "arrival" | "domestic" | "international" | "connections"
+ >,
dictionaries: IDictionaries,
): IFlightRoute[] {
const { domestic, international, connections } = filter;
@@ -32,6 +35,17 @@ export function filterRoutes(
r.route.some((code) => dictionaries.otherCityCodes.has(toCityCode(code)));
const hasConnections = (r: IFlightRoute): boolean => !r.isDirect;
+ const isEndpointDirectRoute = (r: IFlightRoute): boolean => {
+ if (!filter.departure || !filter.arrival) return true;
+ if (!r.isDirect) return false;
+
+ const normalized = r.route.map(toCityCode);
+ return (
+ normalized.length === 2 &&
+ normalized[0] === toCityCode(filter.departure) &&
+ normalized[1] === toCityCode(filter.arrival)
+ );
+ };
const predicates: Array<(r: IFlightRoute) => boolean> = [];
@@ -39,6 +53,7 @@ export function filterRoutes(
else if (international && !domestic) predicates.push(isInternational);
if (connections) predicates.push(hasConnections);
+ else if (filter.arrival) predicates.push(isEndpointDirectRoute);
if (predicates.length === 0) return routes;
return routes.filter((r) => predicates.every((p) => p(r)));