ScheduleFlightBody "Купить" button: wire onBuy → Aeroflot booking URL
DayGroupedFlightList gains an optional `onBuy` prop that forwards to
ScheduleFlightBody. ScheduleSearchPage implements handleBuy — matches
BoardDetailsHeader.BuyTicketButton: opens
`aeroflot.ru/sb/app/{lang}-{lang}#/search?routes={dep}.{yyyyMMdd}.{arr}`
in a new tab, using the first leg's airportCode + scheduled-departure
UTC for direct and multi-leg flights.
Previously the Buy button rendered but its click was `onBuy?.()` with
no handler wired, so nothing happened. The button text + wiring now
mirror Angular's `buy-ticket-button.component`.
This commit is contained in:
@@ -32,6 +32,8 @@ export interface DayGroupedFlightListProps {
|
||||
flights: ISimpleFlight[];
|
||||
loading?: boolean;
|
||||
onFlightClick?: (flight: ISimpleFlight) => void;
|
||||
/** Click handler for the `Купить` button inside each expanded flight body. */
|
||||
onBuy?: (flight: ISimpleFlight) => void;
|
||||
initialCurrentFlightId?: string | null;
|
||||
}
|
||||
|
||||
@@ -125,6 +127,7 @@ export const DayGroupedFlightList: FC<DayGroupedFlightListProps> = ({
|
||||
flights,
|
||||
loading,
|
||||
onFlightClick,
|
||||
onBuy,
|
||||
initialCurrentFlightId,
|
||||
}) => {
|
||||
const { language } = useLocale();
|
||||
@@ -236,9 +239,10 @@ export const DayGroupedFlightList: FC<DayGroupedFlightListProps> = ({
|
||||
<ScheduleFlightBody
|
||||
flight={f}
|
||||
{...(onFlightClick ? { onStatus: () => onFlightClick(f) } : {})}
|
||||
{...(onBuy ? { onBuy: () => onBuy(f) } : {})}
|
||||
/>
|
||||
),
|
||||
[onFlightClick],
|
||||
[onFlightClick, onBuy],
|
||||
);
|
||||
|
||||
if (loading) return <FlightListSkeleton count={5} />;
|
||||
|
||||
@@ -142,6 +142,30 @@ export const ScheduleSearchPage: FC<ScheduleSearchPageProps> = ({ params }) => {
|
||||
[locale, navigate],
|
||||
);
|
||||
|
||||
// `Купить` button — opens Aeroflot's booking flow in a new tab, same
|
||||
// URL shape as BoardDetailsHeader's BuyTicketButton:
|
||||
// https://www.aeroflot.ru/sb/app/{lang}-{lang}#/search?…&routes={dep}.{yyyyMMdd}.{arr}
|
||||
const handleBuy = useCallback(
|
||||
(flight: ISimpleFlight) => {
|
||||
const legs = flight.routeType === "Direct" ? [flight.leg] : flight.legs;
|
||||
const firstLeg = legs[0];
|
||||
const lastLeg = legs[legs.length - 1];
|
||||
if (!firstLeg || !lastLeg) return;
|
||||
const dep = firstLeg.departure.scheduled.airportCode;
|
||||
const arr = lastLeg.arrival.scheduled.airportCode;
|
||||
const depUtc = firstLeg.departure.times.scheduledDeparture.utc;
|
||||
const depDate = new Date(depUtc);
|
||||
const yyyy = depDate.getFullYear().toString();
|
||||
const mm = (depDate.getMonth() + 1).toString().padStart(2, "0");
|
||||
const dd = depDate.getDate().toString().padStart(2, "0");
|
||||
const url = `https://www.aeroflot.ru/sb/app/${language}-${language}#/search?adults=1&cabin=economy&children=0&infants=0&routes=${dep}.${yyyy}${mm}${dd}.${arr}&autosearch=Y`;
|
||||
if (typeof window !== "undefined") {
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
},
|
||||
[language],
|
||||
);
|
||||
|
||||
// Round-trip schedules render only one direction at a time, with a
|
||||
// `Туда / Обратно` switcher in the sticky header — matches Angular's
|
||||
// `schedule-direction-switch` (one-of-two button group). Default to
|
||||
@@ -394,6 +418,7 @@ export const ScheduleSearchPage: FC<ScheduleSearchPageProps> = ({ params }) => {
|
||||
flights={outboundSimple}
|
||||
loading={outboundLoading}
|
||||
onFlightClick={handleFlightClick}
|
||||
onBuy={handleBuy}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@@ -402,6 +427,7 @@ export const ScheduleSearchPage: FC<ScheduleSearchPageProps> = ({ params }) => {
|
||||
flights={inboundSimple}
|
||||
loading={inboundLoading}
|
||||
onFlightClick={handleFlightClick}
|
||||
onBuy={handleBuy}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user