From eea8d92212a1c7e00105050770050bd68b8b1f1b Mon Sep 17 00:00:00 2001 From: gnezim Date: Sun, 19 Apr 2026 11:43:01 +0300 Subject: [PATCH] =?UTF-8?q?Resolve=20IATA=20=E2=86=92=20city=20+=20today?= =?UTF-8?q?=20=E2=86=92=20'=D0=A1=D0=B5=D0=B3=D0=BE=D0=B4=D0=BD=D1=8F'=20i?= =?UTF-8?q?n=20search-page=20SEO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On /ru/onlineboard/route/MOW-LED-20260419 (and /departure/, /arrival/) the H1 already read 'Маршрут: Москва - Санкт-Петербург, Сегодня' but document.title and meta[name=description] carried the raw 'MOW - LED 19.04.2026' because SeoHead runs at the route level with URL-only params. Angular ships the resolved city names + 'Сегодня' in both. Add a useEffect in OnlineBoardSearchPage that, once the dictionary hook returns, overwrites document.title + meta description using the same describeStation/dateLabel helpers that feed the H1. Route, departure, and arrival search types all get handled; flight-number search is unchanged. --- .../components/OnlineBoardSearchPage.tsx | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/features/online-board/components/OnlineBoardSearchPage.tsx b/src/features/online-board/components/OnlineBoardSearchPage.tsx index 8784d092..e53b427c 100644 --- a/src/features/online-board/components/OnlineBoardSearchPage.tsx +++ b/src/features/online-board/components/OnlineBoardSearchPage.tsx @@ -13,7 +13,7 @@ */ import type { FC } from "react"; -import { useCallback } from "react"; +import { useCallback, useEffect } from "react"; import { useNavigate, useParams } from "@modern-js/runtime/router"; import { useTranslation } from "@/i18n/provider.js"; import { FlightList } from "@/ui/flights/FlightList.js"; @@ -227,6 +227,59 @@ export const OnlineBoardSearchPage: FC = ({ searchHeading = t("BOARD.TITLE"); } + // Rewrite document.title + meta description once city names resolve, + // matching Angular's SEO (route/departure/arrival titles use resolved + // city names and 'Сегодня' for today's date, not raw IATA codes + ISO). + useEffect(() => { + if (!dictionaries) return; + const depCity = + params.type === "route" ? describeStation(params.departure) : undefined; + const arrCity = + params.type === "route" ? describeStation(params.arrival) : undefined; + const stationCity = + params.type === "departure" || params.type === "arrival" + ? describeStation(params.station) + : undefined; + const dateForSeo = dateLabel; + let title: string | null = null; + let desc: string | null = null; + if (params.type === "route" && depCity && arrCity) { + title = t("SEO.BOARD.ROUTE-SEARCH.TITLE", { + departureCity: depCity, + arrivalCity: arrCity, + date: dateForSeo, + }); + desc = t("SEO.BOARD.ROUTE-SEARCH.DESCRIPTION", { + departureCity: depCity, + arrivalCity: arrCity, + date: dateForSeo, + }); + } else if (params.type === "departure" && stationCity) { + title = t("SEO.BOARD.DEPARTURE-SEARCH.TITLE", { + departureCity: stationCity, + date: dateForSeo, + }); + desc = t("SEO.BOARD.DEPARTURE-SEARCH.DESCRIPTION", { + departureCity: stationCity, + date: dateForSeo, + }); + } else if (params.type === "arrival" && stationCity) { + title = t("SEO.BOARD.ARRIVAL-SEARCH.TITLE", { + arrivalCity: stationCity, + date: dateForSeo, + }); + desc = t("SEO.BOARD.ARRIVAL-SEARCH.DESCRIPTION", { + arrivalCity: stationCity, + date: dateForSeo, + }); + } + if (title) document.title = title; + if (desc) { + const meta = document.querySelector('meta[name="description"]'); + if (meta) meta.setAttribute("content", desc); + } + }, [dictionaries, params, dateLabel, t]); + // Data fetching const searchParams = toSearchParams(params); const { flights, loading, error, refresh } = useOnlineBoard(searchParams);