Fix schedule aircraft link target

This commit is contained in:
2026-05-14 17:22:11 +03:00
parent b3d242e7e0
commit 6cf57596bf
4 changed files with 107 additions and 29 deletions
@@ -473,7 +473,10 @@ export const ScheduleDetailsPage: FC<ScheduleDetailsPageProps> = ({
<FlightCard flight={flight} direction="schedule" />
<IFlyWarning flightNumber={flight.flightId.flightNumber} />
{renderBody(flight)}
<ScheduleLegDetails flight={flight as unknown as ISimpleFlight} />
<ScheduleLegDetails
flight={flight as unknown as ISimpleFlight}
locale={locale}
/>
{/* Angular hides the weekly operating schedule on
multi-leg chains; keep it on direct flights. */}
@@ -42,6 +42,30 @@ function makeFlight(meal: MealType[] | undefined): ISimpleFlight {
}
describe("ScheduleLegDetails Питание sub-icons", () => {
it("links the aircraft title to Angular's Aeroflot plane park URL", () => {
render(<ScheduleLegDetails flight={makeFlight([])} locale="ru-ru" />);
const link = screen.getByRole("link", { name: "Sukhoi SuperJet 100" });
expect(link.getAttribute("href")).toBe(
"http://www.aeroflot.ru/cms/ru/flight/plane_park",
);
expect(link.getAttribute("target")).toBe("_blank");
expect(link.getAttribute("rel")).toBe("noopener noreferrer");
});
it("uses the language prefix from locale for the plane park URL", () => {
render(<ScheduleLegDetails flight={makeFlight([])} locale="en-us" />);
expect(
screen
.getByRole("link", { name: "Sukhoi SuperJet 100" })
.getAttribute("href"),
).toBe(
"http://www.aeroflot.ru/cms/en/flight/plane_park",
);
});
it("renders no meal-class sub-icons when equipment.meal is empty", () => {
render(<ScheduleLegDetails flight={makeFlight([])} />);
expect(screen.queryByText("FOOD.ECONOMY")).toBeNull();
@@ -3,7 +3,7 @@
* each flight leg. Mirrors Angular's `flight-details-wrapper` accordion
* (schedule view): two rows when the data exists.
*
* 1. Борт — aircraft type with a link to its Aeroflot info page.
* 1. Борт — aircraft type with a link to Aeroflot's aircraft park.
* 2. Питание на борту — meal-class sub-icons (Эконом / Комфорт /
* Бизнес), each rendered ONLY when the API's
* `equipment.meal[]` array contains the matching `type`. Matches
@@ -25,18 +25,18 @@ import "./ScheduleLegDetails.scss";
export interface ScheduleLegDetailsProps {
flight: ISimpleFlight;
locale?: string;
}
/** Slug used for the aircraft catalog deep-link on www.aeroflot.ru. */
function aircraftSlug(title: string | undefined | null): string | null {
if (!title) return null;
return title
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/[^a-z0-9-]/g, "");
function aircraftParkHref(locale: string | undefined): string {
const language = locale?.split("-")[0] || "ru";
return `http://www.aeroflot.ru/cms/${language}/flight/plane_park`;
}
export const ScheduleLegDetails: FC<ScheduleLegDetailsProps> = ({ flight }) => {
export const ScheduleLegDetails: FC<ScheduleLegDetailsProps> = ({
flight,
locale,
}) => {
const { t } = useTranslation();
const [expanded, setExpanded] = useState<boolean>(true);
@@ -47,13 +47,7 @@ export const ScheduleLegDetails: FC<ScheduleLegDetailsProps> = ({ flight }) => {
leg.equipment?.aircraft?.actual?.title ??
leg.equipment?.aircraft?.scheduled?.title ??
"";
const slug = aircraftSlug(aircraft);
// External Aeroflot page that describes the aircraft model. Angular
// uses a curated slug map; the simple kebab-case works for common
// models (e.g. sukhoi-superjet-100).
const planeUrl = slug
? `https://www.aeroflot.ru/ru-ru/about/aircrafts/${slug}`
: null;
const planeUrl = aircraftParkHref(locale);
return (
<div className="schedule-leg-details" data-testid="schedule-leg-details">
@@ -117,18 +111,14 @@ export const ScheduleLegDetails: FC<ScheduleLegDetailsProps> = ({ flight }) => {
<span className="schedule-leg-details__label">
{t("SHARED.PLANE")}
</span>
{planeUrl ? (
<a
className="schedule-leg-details__link"
href={planeUrl}
target="_blank"
rel="noopener noreferrer"
>
{aircraft}
</a>
) : (
<span className="schedule-leg-details__value">{aircraft}</span>
)}
<a
className="schedule-leg-details__link"
href={planeUrl}
target="_blank"
rel="noopener noreferrer"
>
{aircraft}
</a>
</div>
)}