Resolve IATA → city + today → 'Сегодня' in search-page SEO

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.
This commit is contained in:
2026-04-19 11:43:01 +03:00
parent 2ae25e630a
commit eea8d92212
@@ -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<OnlineBoardSearchPageProps> = ({
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);