Fix online board details date selection
This commit is contained in:
@@ -12,6 +12,7 @@ import { Link } from "@modern-js/runtime/router";
|
||||
import { useTranslation } from "@/i18n/provider.js";
|
||||
import type { ISimpleFlight, IFlightLeg } from "../../types.js";
|
||||
import { buildOnlineBoardUrl } from "../../url.js";
|
||||
import { getFlightSearchDate } from "../../flightSearchDate.js";
|
||||
import {
|
||||
formatLocalTime,
|
||||
formatDayMonthYear,
|
||||
@@ -51,7 +52,7 @@ export const FlightsMiniListItem = forwardRef<HTMLAnchorElement, FlightsMiniList
|
||||
carrier: flight.flightId.carrier,
|
||||
flightNumber: flight.flightId.flightNumber,
|
||||
...(flight.flightId.suffix ? { suffix: flight.flightId.suffix } : {}),
|
||||
date: flight.flightId.date,
|
||||
date: getFlightSearchDate(flight),
|
||||
})}`;
|
||||
|
||||
const depIso = getDepTimeIso(dep);
|
||||
|
||||
@@ -22,6 +22,7 @@ import { useOnlineBoard } from "../hooks/useOnlineBoard.js";
|
||||
import { parseDetailsRequestParam } from "@/shared/detailsRequestParam.js";
|
||||
import { buildFlightJsonLd } from "../json-ld.js";
|
||||
import { buildOnlineBoardUrl } from "../url.js";
|
||||
import { getFlightSearchDate } from "../flightSearchDate.js";
|
||||
import { useCityName, useStationDisplayName } from "@/shared/hooks/useDictionaries.js";
|
||||
import { FlightDetailsAccordion } from "./details-panels/FlightDetailsAccordion.js";
|
||||
import { FlightsMiniList } from "./FlightsMiniList/index.js";
|
||||
@@ -388,9 +389,9 @@ export const OnlineBoardDetailsPage: FC<OnlineBoardDetailsPageProps> = ({
|
||||
|
||||
// Pick the flight matching the URL's flightId (date-based match). The API
|
||||
// response may contain multiple flights with the same flight number on
|
||||
// different dates; we need the one the user actually navigated to.
|
||||
// different dates; match Angular's dateToSearchBy, not backend flightId.date.
|
||||
const flight =
|
||||
allFlights.find((f) => f.flightId.date === flightId.date) ?? firstFlight;
|
||||
allFlights.find((f) => getFlightSearchDate(f) === flightId.date) ?? firstFlight;
|
||||
|
||||
// Live updates via SignalR
|
||||
const { flight: liveFlight, connectionStatus } = useLiveFlightDetails(
|
||||
|
||||
@@ -36,6 +36,7 @@ import { useCalendarDays } from "../hooks/useCalendarDays.js";
|
||||
import { buildOnlineBoardUrl } from "../url.js";
|
||||
import { buildFlightListJsonLd } from "../json-ld.js";
|
||||
import { sortFlights } from "../sortFlights.js";
|
||||
import { getFlightSearchDate } from "../flightSearchDate.js";
|
||||
import {
|
||||
PobedaAuroraBanner,
|
||||
shouldShowPobedaAuroraBanner,
|
||||
@@ -388,13 +389,13 @@ export const OnlineBoardSearchPage: FC<OnlineBoardSearchPageProps> = ({
|
||||
carrier: flight.flightId.carrier,
|
||||
flightNumber: flight.flightId.flightNumber,
|
||||
suffix: flight.flightId.suffix,
|
||||
date: flight.flightId.date,
|
||||
date: getFlightSearchDate(flight),
|
||||
}
|
||||
: {
|
||||
type: "details",
|
||||
carrier: flight.flightId.carrier,
|
||||
flightNumber: flight.flightId.flightNumber,
|
||||
date: flight.flightId.date,
|
||||
date: getFlightSearchDate(flight),
|
||||
};
|
||||
const detailsUrl = buildOnlineBoardUrl(detailsParams);
|
||||
void navigate(`/${locale}/${detailsUrl}`);
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getFlightSearchDate } from "./flightSearchDate.js";
|
||||
import type { IDirectFlight, IMultiLegFlight, IFlightLeg } from "./types.js";
|
||||
|
||||
function leg(local: string): IFlightLeg {
|
||||
return {
|
||||
arrival: {
|
||||
scheduled: { airport: "", airportCode: "", city: "", cityCode: "", countryCode: "" },
|
||||
times: {
|
||||
scheduledArrival: {
|
||||
dayChange: { value: 0, title: "" },
|
||||
local,
|
||||
localTime: "",
|
||||
tzOffset: 0,
|
||||
utc: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
dayChange: 0,
|
||||
departure: {
|
||||
scheduled: { airport: "", airportCode: "", city: "", cityCode: "", countryCode: "" },
|
||||
checkingStatus: "Scheduled",
|
||||
times: {
|
||||
scheduledDeparture: {
|
||||
dayChange: { value: 0, title: "" },
|
||||
local,
|
||||
localTime: "",
|
||||
tzOffset: 0,
|
||||
utc: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
equipment: {},
|
||||
flags: { checkinAvailable: false, returnToAirport: false, routeChanged: false },
|
||||
flyingTime: "",
|
||||
index: 0,
|
||||
operatingBy: {},
|
||||
status: "Scheduled",
|
||||
updated: "",
|
||||
};
|
||||
}
|
||||
|
||||
describe("getFlightSearchDate", () => {
|
||||
it("uses scheduled local departure date instead of backend flightId.date", () => {
|
||||
const flight: IDirectFlight = {
|
||||
id: "SU6951",
|
||||
routeType: "Direct",
|
||||
flyingTime: "",
|
||||
operatingBy: {},
|
||||
status: "Arrived",
|
||||
flightId: {
|
||||
carrier: "SU",
|
||||
flightNumber: "6951",
|
||||
suffix: "",
|
||||
date: "2026-05-04",
|
||||
dateLT: "2026-05-05",
|
||||
},
|
||||
leg: leg("2026-05-05T00:30:00+03:00"),
|
||||
};
|
||||
|
||||
expect(getFlightSearchDate(flight)).toBe("20260505");
|
||||
});
|
||||
|
||||
it("uses the first leg for multi-leg flights", () => {
|
||||
const flight: IMultiLegFlight = {
|
||||
id: "SU100",
|
||||
routeType: "MultiLeg",
|
||||
flyingTime: "",
|
||||
operatingBy: {},
|
||||
status: "Scheduled",
|
||||
flightId: {
|
||||
carrier: "SU",
|
||||
flightNumber: "0100",
|
||||
suffix: "",
|
||||
date: "20260504",
|
||||
},
|
||||
legs: [
|
||||
leg("2026-05-05T23:30:00+03:00"),
|
||||
leg("2026-05-06T01:30:00+03:00"),
|
||||
],
|
||||
};
|
||||
|
||||
expect(getFlightSearchDate(flight)).toBe("20260505");
|
||||
});
|
||||
|
||||
it("falls back to dateLT and then flightId.date when leg date is unavailable", () => {
|
||||
const flight: IDirectFlight = {
|
||||
id: "SU42",
|
||||
routeType: "Direct",
|
||||
flyingTime: "",
|
||||
operatingBy: {},
|
||||
status: "Scheduled",
|
||||
flightId: {
|
||||
carrier: "SU",
|
||||
flightNumber: "0042",
|
||||
suffix: "",
|
||||
date: "2026-05-04",
|
||||
dateLT: "2026-05-05",
|
||||
},
|
||||
leg: leg("10:00"),
|
||||
};
|
||||
|
||||
expect(getFlightSearchDate(flight)).toBe("20260505");
|
||||
|
||||
const { dateLT: _dateLT, ...flightIdWithoutDateLt } = flight.flightId;
|
||||
const withoutDateLt: IDirectFlight = {
|
||||
...flight,
|
||||
flightId: flightIdWithoutDateLt,
|
||||
};
|
||||
|
||||
expect(getFlightSearchDate(withoutDateLt)).toBe("20260504");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { IFlightLeg, ISimpleFlight } from "./types.js";
|
||||
|
||||
function compactDate(value: string | undefined): string | null {
|
||||
if (!value) return null;
|
||||
if (/^\d{8}$/.test(value)) return value;
|
||||
|
||||
const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(value);
|
||||
if (!match) return null;
|
||||
|
||||
return `${match[1]}${match[2]}${match[3]}`;
|
||||
}
|
||||
|
||||
function getFirstLeg(flight: ISimpleFlight): IFlightLeg | null {
|
||||
if (flight.routeType === "Direct") return flight.leg;
|
||||
return flight.legs[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Angular's FlightDateUtils.getFlightDate derives the details URL date from
|
||||
* the first leg's scheduled local departure date, not from flightId.date.
|
||||
* Overnight flights can have flightId.date set to the previous backend
|
||||
* service day while dateLT / scheduled local departure belongs to the board
|
||||
* day the user clicked.
|
||||
*/
|
||||
export function getFlightSearchDate(flight: ISimpleFlight): string {
|
||||
const firstLeg = getFirstLeg(flight);
|
||||
|
||||
return (
|
||||
compactDate(firstLeg?.departure.times.scheduledDeparture.local) ??
|
||||
compactDate(flight.flightId.dateLT) ??
|
||||
compactDate(flight.flightId.date) ??
|
||||
flight.flightId.date.replace(/-/g, "")
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user