Schedule search → details links emit ?request= + details page shows leaf breadcrumb (TZ §4.1.2 row 11, §4.1.4 rows 11-13)
This commit is contained in:
@@ -423,7 +423,7 @@ export const OnlineBoardDetailsPage: FC<OnlineBoardDetailsPageProps> = ({
|
||||
// city (the station). We show only that city. See AGENTS.md Conflicts register.
|
||||
const detailsCrumbs = useMemo(() => {
|
||||
const baseCrumbs = [{ label: t("BOARD.TITLE"), url: `/${locale}/onlineboard` }];
|
||||
if (!parentRequest) return baseCrumbs;
|
||||
if (!parentRequest || parentRequest.area !== "onlineboard") return baseCrumbs;
|
||||
|
||||
const backUrl = (() => {
|
||||
switch (parentRequest.kind) {
|
||||
@@ -484,7 +484,7 @@ export const OnlineBoardDetailsPage: FC<OnlineBoardDetailsPageProps> = ({
|
||||
}, [parentRequest, locale, t]);
|
||||
|
||||
const parentParams = useMemo(() => {
|
||||
if (!parentRequest) return null;
|
||||
if (!parentRequest || parentRequest.area !== "onlineboard") return null;
|
||||
const d = parentRequest.date;
|
||||
const isoDate = `${d.slice(0, 4)}-${d.slice(4, 6)}-${d.slice(6, 8)}`;
|
||||
const dateFrom = `${isoDate}T00:00:00`;
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
*/
|
||||
|
||||
import type { FC } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { Link } from "@modern-js/runtime/router";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { Link, useSearchParams } from "@modern-js/runtime/router";
|
||||
import { useTranslation } from "@/i18n/provider.js";
|
||||
import { localeToLanguage, normalizeLocaleParam, DEFAULT_LANGUAGE } from "@/i18n/resolver.js";
|
||||
import { FlightCard } from "@/ui/flights/FlightCard.js";
|
||||
@@ -20,6 +20,8 @@ import { SeoHead } from "@/ui/seo/SeoHead.js";
|
||||
import { PageLayout } from "@/ui/layout/PageLayout.js";
|
||||
import { PageTabs } from "@/ui/layout/PageTabs.js";
|
||||
import { JsonLdRenderer } from "@/shared/seo/json-ld.js";
|
||||
import { parseDetailsRequestParam } from "@/shared/detailsRequestParam.js";
|
||||
import { buildScheduleUrl } from "../url.js";
|
||||
import { useScheduleDetails } from "../hooks/useScheduleDetails.js";
|
||||
import { buildScheduleDetailsSeo } from "../seo.js";
|
||||
import { buildScheduleFlightJsonLd } from "../json-ld.js";
|
||||
@@ -83,6 +85,72 @@ export const ScheduleDetailsPage: FC<ScheduleDetailsPageProps> = ({
|
||||
const pageTabs = <PageTabs viewType="schedule" />;
|
||||
const scheduleHref = `/${locale}/schedule`;
|
||||
|
||||
// Parse ?request= to build the leaf breadcrumb (TZ §4.1.4 Table 7 rows 11-13)
|
||||
const [searchParams] = useSearchParams();
|
||||
const parentRequest = useMemo(() => {
|
||||
const raw = searchParams.get("request");
|
||||
return raw ? parseDetailsRequestParam(raw) : null;
|
||||
}, [searchParams]);
|
||||
|
||||
const breadcrumbs = useMemo(() => {
|
||||
const baseCrumbs = [{ label: t("SCHEDULE.TITLE"), url: scheduleHref }];
|
||||
if (!parentRequest || parentRequest.area !== "schedule") return baseCrumbs;
|
||||
|
||||
// Build the back URL from the parsed request context
|
||||
const backUrl = `/${locale}/${buildScheduleUrl(
|
||||
parentRequest.returnTrip
|
||||
? {
|
||||
type: "roundtrip",
|
||||
outbound: {
|
||||
departure: parentRequest.departure,
|
||||
arrival: parentRequest.arrival,
|
||||
dateFrom: parentRequest.dateFrom,
|
||||
dateTo: parentRequest.dateTo,
|
||||
...(parentRequest.timeFrom && parentRequest.timeTo
|
||||
? { timeFrom: parentRequest.timeFrom, timeTo: parentRequest.timeTo }
|
||||
: {}),
|
||||
...(parentRequest.connections !== undefined
|
||||
? { connections: parentRequest.connections }
|
||||
: {}),
|
||||
},
|
||||
inbound: {
|
||||
departure: parentRequest.returnTrip.departure,
|
||||
arrival: parentRequest.returnTrip.arrival,
|
||||
dateFrom: parentRequest.returnTrip.dateFrom,
|
||||
dateTo: parentRequest.returnTrip.dateTo,
|
||||
...(parentRequest.returnTrip.timeFrom && parentRequest.returnTrip.timeTo
|
||||
? { timeFrom: parentRequest.returnTrip.timeFrom, timeTo: parentRequest.returnTrip.timeTo }
|
||||
: {}),
|
||||
...(parentRequest.returnTrip.connections !== undefined
|
||||
? { connections: parentRequest.returnTrip.connections }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
: {
|
||||
type: "route",
|
||||
outbound: {
|
||||
departure: parentRequest.departure,
|
||||
arrival: parentRequest.arrival,
|
||||
dateFrom: parentRequest.dateFrom,
|
||||
dateTo: parentRequest.dateTo,
|
||||
...(parentRequest.timeFrom && parentRequest.timeTo
|
||||
? { timeFrom: parentRequest.timeFrom, timeTo: parentRequest.timeTo }
|
||||
: {}),
|
||||
...(parentRequest.connections !== undefined
|
||||
? { connections: parentRequest.connections }
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
)}`;
|
||||
|
||||
const leafLabel = t("BREADCRUMBS.SCHEDULE-ROUTE", {
|
||||
departureCity: parentRequest.departure,
|
||||
arrivalCity: parentRequest.arrival,
|
||||
});
|
||||
|
||||
return [...baseCrumbs, { label: leafLabel, url: backUrl }];
|
||||
}, [parentRequest, locale, scheduleHref, t]);
|
||||
|
||||
// `Купить` button — opens Aeroflot's booking flow in a new tab.
|
||||
// Mirrors BoardDetailsHeader's BuyTicketButton / Schedule search page.
|
||||
const language =
|
||||
@@ -128,7 +196,7 @@ export const ScheduleDetailsPage: FC<ScheduleDetailsPageProps> = ({
|
||||
<PageLayout
|
||||
headerLeft={pageTabs}
|
||||
title={<h1 className="text--white page-title">{title}</h1>}
|
||||
breadcrumbs={[{ label: t("SCHEDULE.TITLE"), url: scheduleHref }]}
|
||||
breadcrumbs={breadcrumbs}
|
||||
>
|
||||
<section className="frame">
|
||||
<FlightListSkeleton count={flightIds.length} />
|
||||
@@ -142,7 +210,7 @@ export const ScheduleDetailsPage: FC<ScheduleDetailsPageProps> = ({
|
||||
<PageLayout
|
||||
headerLeft={pageTabs}
|
||||
title={<h1 className="text--white page-title">{title}</h1>}
|
||||
breadcrumbs={[{ label: t("SCHEDULE.TITLE"), url: scheduleHref }]}
|
||||
breadcrumbs={breadcrumbs}
|
||||
>
|
||||
<section className="frame">
|
||||
<div
|
||||
@@ -162,7 +230,7 @@ export const ScheduleDetailsPage: FC<ScheduleDetailsPageProps> = ({
|
||||
<PageLayout
|
||||
headerLeft={pageTabs}
|
||||
title={<h1 className="text--white page-title">{title}</h1>}
|
||||
breadcrumbs={[{ label: t("SCHEDULE.TITLE"), url: scheduleHref }]}
|
||||
breadcrumbs={breadcrumbs}
|
||||
>
|
||||
<section className="frame">
|
||||
<div
|
||||
|
||||
@@ -26,6 +26,7 @@ import { JsonLdRenderer } from "@/shared/seo/json-ld.js";
|
||||
import { useScheduleSearch } from "../hooks/useScheduleSearch.js";
|
||||
import { buildScheduleUrl } from "../url.js";
|
||||
import { buildFlightUrlParams } from "../../online-board/url.js";
|
||||
import { buildDetailsRequestParam } from "@/shared/detailsRequestParam.js";
|
||||
import { buildScheduleFlightListJsonLd } from "../json-ld.js";
|
||||
import type { ScheduleParams } from "../url.js";
|
||||
import type { IScheduleSearchRequest, ISimpleFlight } from "../types.js";
|
||||
@@ -137,9 +138,39 @@ export const ScheduleSearchPage: FC<ScheduleSearchPageProps> = ({ params }) => {
|
||||
...(flight.flightId.suffix ? { suffix: flight.flightId.suffix } : {}),
|
||||
date: flight.flightId.date,
|
||||
});
|
||||
void navigate(`/${locale}/schedule/${segment}`);
|
||||
const requestParam = buildDetailsRequestParam({
|
||||
area: "schedule",
|
||||
kind: "route",
|
||||
departure: outbound.departure,
|
||||
arrival: outbound.arrival,
|
||||
dateFrom: outbound.dateFrom,
|
||||
dateTo: outbound.dateTo,
|
||||
...(outbound.timeFrom && outbound.timeTo
|
||||
? { timeFrom: outbound.timeFrom, timeTo: outbound.timeTo }
|
||||
: {}),
|
||||
...(outbound.connections !== undefined
|
||||
? { connections: outbound.connections }
|
||||
: {}),
|
||||
...(inbound
|
||||
? {
|
||||
returnTrip: {
|
||||
departure: inbound.departure,
|
||||
arrival: inbound.arrival,
|
||||
dateFrom: inbound.dateFrom,
|
||||
dateTo: inbound.dateTo,
|
||||
...(inbound.timeFrom && inbound.timeTo
|
||||
? { timeFrom: inbound.timeFrom, timeTo: inbound.timeTo }
|
||||
: {}),
|
||||
...(inbound.connections !== undefined
|
||||
? { connections: inbound.connections }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
void navigate(`/${locale}/schedule/${segment}?request=${encodeURIComponent(requestParam)}`);
|
||||
},
|
||||
[locale, navigate],
|
||||
[locale, navigate, outbound, inbound],
|
||||
);
|
||||
|
||||
// `Купить` button — opens Aeroflot's booking flow in a new tab, same
|
||||
|
||||
@@ -418,6 +418,7 @@
|
||||
"FLIGHT-NUMBER": "",
|
||||
"DEPARTURE": "",
|
||||
"ARRIVAL": "",
|
||||
"ROUTE": ""
|
||||
"ROUTE": "",
|
||||
"SCHEDULE-ROUTE": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@
|
||||
"FLIGHT-NUMBER": "Flight: {flightNumber}",
|
||||
"DEPARTURE": "Departure: {city}",
|
||||
"ARRIVAL": "Arrival: {city}",
|
||||
"ROUTE": "Route: {departureCity}-{arrivalCity}"
|
||||
"ROUTE": "Route: {departureCity}-{arrivalCity}",
|
||||
"SCHEDULE-ROUTE": "{departureCity}-{arrivalCity}"
|
||||
},
|
||||
"DETAILS": {
|
||||
"REGISTRATION": "Check-in",
|
||||
|
||||
@@ -418,6 +418,7 @@
|
||||
"FLIGHT-NUMBER": "",
|
||||
"DEPARTURE": "",
|
||||
"ARRIVAL": "",
|
||||
"ROUTE": ""
|
||||
"ROUTE": "",
|
||||
"SCHEDULE-ROUTE": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +418,7 @@
|
||||
"FLIGHT-NUMBER": "",
|
||||
"DEPARTURE": "",
|
||||
"ARRIVAL": "",
|
||||
"ROUTE": ""
|
||||
"ROUTE": "",
|
||||
"SCHEDULE-ROUTE": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +418,7 @@
|
||||
"FLIGHT-NUMBER": "",
|
||||
"DEPARTURE": "",
|
||||
"ARRIVAL": "",
|
||||
"ROUTE": ""
|
||||
"ROUTE": "",
|
||||
"SCHEDULE-ROUTE": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +418,7 @@
|
||||
"FLIGHT-NUMBER": "",
|
||||
"DEPARTURE": "",
|
||||
"ARRIVAL": "",
|
||||
"ROUTE": ""
|
||||
"ROUTE": "",
|
||||
"SCHEDULE-ROUTE": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +418,7 @@
|
||||
"FLIGHT-NUMBER": "",
|
||||
"DEPARTURE": "",
|
||||
"ARRIVAL": "",
|
||||
"ROUTE": ""
|
||||
"ROUTE": "",
|
||||
"SCHEDULE-ROUTE": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@
|
||||
"FLIGHT-NUMBER": "Номер рейса: {flightNumber}",
|
||||
"DEPARTURE": "Вылет: {city}",
|
||||
"ARRIVAL": "Прилет: {city}",
|
||||
"ROUTE": "Маршрут: {departureCity}-{arrivalCity}"
|
||||
"ROUTE": "Маршрут: {departureCity}-{arrivalCity}",
|
||||
"SCHEDULE-ROUTE": "{departureCity}-{arrivalCity}"
|
||||
},
|
||||
"DETAILS": {
|
||||
"REGISTRATION": "Регистрация",
|
||||
|
||||
@@ -418,6 +418,7 @@
|
||||
"FLIGHT-NUMBER": "",
|
||||
"DEPARTURE": "",
|
||||
"ARRIVAL": "",
|
||||
"ROUTE": ""
|
||||
"ROUTE": "",
|
||||
"SCHEDULE-ROUTE": ""
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user