Schedule details: summary header, fix mini-list duplicates, fix timeline times
The schedule details page now renders Angular's <schedule-details-header> summary block (badges per flight + share/last-update + full-route timeline) between the day-tabs strip and the per-leg cards, so a connecting itinerary like SU 6188 + SU 6341 surfaces both flight numbers and the combined Moscow→Murmansk timeline up top instead of jumping straight from the date tabs to the first-leg detail card. Mini-list duplicate fix: when the sibling search returned 0 matches the fallback path used to leak the URL-parsed per-leg breakdown into the rail, producing a first-leg-only row stacked next to the synthesized combined row. Now the fallback is empty — the mini-list just shows the (synthesized) current flight on its own. FullRouteTimeline now uses the API's pre-formatted .localTime instead of the full ISO .local, so 00:30 / 02:00 shows up instead of 2026-04-26T00:30:00+03:00. useAppSettings.buyTicketMaxHours: parse <n>d as well as <n>h (Angular ships 330d for buyPeriod.max). Without this the Buy button hides for any flight more than ~3 days out. Plumbed sortMode/onSortChange/hideColumnHeaders through DayGroupedFlightList so the sticky ScheduleColumnHeaders and the inner list stay in sync (removes 2 TS errors in ScheduleSearchPage).
This commit is contained in:
@@ -109,9 +109,25 @@ describe("useAppSettings", () => {
|
||||
const { result } = renderHook(() => useAppSettings());
|
||||
|
||||
await waitFor(() => expect(result.current.loading).toBe(false));
|
||||
// Defaults match Angular (flightStatusAvailableFrom: 2h, buyPeriod.min: 2h, buyPeriod.max: 72h).
|
||||
// Defaults match Angular (flightStatusAvailableFrom: 2h, buyPeriod.min: 2h, buyPeriod.max: 330d).
|
||||
expect(result.current.flightStatusAvailableFromHours).toBe(2);
|
||||
expect(result.current.buyTicketMinHours).toBe(2);
|
||||
expect(result.current.buyTicketMaxHours).toBe(72);
|
||||
expect(result.current.buyTicketMaxHours).toBe(330 * 24);
|
||||
});
|
||||
|
||||
it("parses buyPeriod.max in days (Angular API returns '330d')", async () => {
|
||||
const response: AppSettingsResponse = {
|
||||
uiOptions: {
|
||||
buttons: {
|
||||
buyTicket: { period: { min: "2h", max: "330d" } },
|
||||
},
|
||||
},
|
||||
};
|
||||
mockGetAppSettings.mockResolvedValue(response);
|
||||
|
||||
const { result } = renderHook(() => useAppSettings());
|
||||
await waitFor(() => expect(result.current.loading).toBe(false));
|
||||
expect(result.current.buyTicketMinHours).toBe(2);
|
||||
expect(result.current.buyTicketMaxHours).toBe(330 * 24);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,9 +10,10 @@ const HOURS_PATTERN = /^(\d+)h$/;
|
||||
// boardSearchFrom: 1, boardSearchTo: 7,
|
||||
// scheduleSearchFrom: 1, scheduleSearchTo: 330,
|
||||
// flightStatusAvailableFrom: 2 (hours), buyPeriod.min: 2h, buyPeriod.max: 330d
|
||||
// These are used when the UI-options API fails or is unreachable —
|
||||
// keeping them aligned with Angular avoids a silent calendar-window
|
||||
// mismatch in offline / error paths.
|
||||
// `buyTicketMaxHours` mirrors Angular's `330d` → 330 * 24 = 7920 hours;
|
||||
// without this the Buy button hides for any flight more than ~3 days
|
||||
// out, which made the schedule details page look like 'no Купить
|
||||
// button' for the typical search-2-weeks-ahead flow.
|
||||
const DEFAULTS = {
|
||||
onlineboardSearchFrom: 1,
|
||||
onlineboardSearchTo: 7,
|
||||
@@ -20,7 +21,7 @@ const DEFAULTS = {
|
||||
scheduleSearchTo: 330,
|
||||
flightStatusAvailableFromHours: 2,
|
||||
buyTicketMinHours: 2,
|
||||
buyTicketMaxHours: 72,
|
||||
buyTicketMaxHours: 330 * 24,
|
||||
} as const;
|
||||
|
||||
function parsePattern(
|
||||
@@ -42,6 +43,19 @@ function parseHours(value: string | undefined, fallback: number): number {
|
||||
return parsePattern(value, HOURS_PATTERN, fallback);
|
||||
}
|
||||
|
||||
/** Parse an `<n>h` or `<n>d` duration into hours. Days expand to 24×n.
|
||||
* Used for `buyPeriod.max` which Angular's settings API returns as
|
||||
* `"330d"` (any other consumer in the React codebase that needs to
|
||||
* accept either unit should use this rather than parseHours). */
|
||||
function parseHoursOrDays(value: string | undefined, fallback: number): number {
|
||||
if (!value) return fallback;
|
||||
const hMatch = HOURS_PATTERN.exec(value);
|
||||
if (hMatch?.[1]) return parseInt(hMatch[1], 10);
|
||||
const dMatch = DAYS_PATTERN.exec(value);
|
||||
if (dMatch?.[1]) return parseInt(dMatch[1], 10) * 24;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
export interface UseAppSettingsResult {
|
||||
onlineboardSearchFrom: number;
|
||||
onlineboardSearchTo: number;
|
||||
@@ -84,8 +98,8 @@ export function useAppSettings(): UseAppSettingsResult {
|
||||
fs?.availableFrom,
|
||||
DEFAULTS.flightStatusAvailableFromHours,
|
||||
),
|
||||
buyTicketMinHours: parseHours(bt?.period?.min, DEFAULTS.buyTicketMinHours),
|
||||
buyTicketMaxHours: parseHours(bt?.period?.max, DEFAULTS.buyTicketMaxHours),
|
||||
buyTicketMinHours: parseHoursOrDays(bt?.period?.min, DEFAULTS.buyTicketMinHours),
|
||||
buyTicketMaxHours: parseHoursOrDays(bt?.period?.max, DEFAULTS.buyTicketMaxHours),
|
||||
});
|
||||
setLoading(false);
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user