From b21ae2638b4ec2feebb424d945652ed389531804 Mon Sep 17 00:00:00 2001 From: gnezim Date: Mon, 20 Apr 2026 00:21:20 +0300 Subject: [PATCH] =?UTF-8?q?Add=20=D0=A2=D1=83=D0=B4=D0=B0/=D0=9E=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D1=82=D0=BD=D0=BE=20direction=20switch=20to=20roun?= =?UTF-8?q?d-trip=20schedule=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round-trip schedules used to render outbound and inbound lists stacked. Mirror Angular's schedule-direction-switch: keep both fetches running, but render only the active direction's list and add a button group to the sticky header that swaps which one is shown. WeekTabs track the active direction's week independently, and tab navigation updates whichever direction is currently active. --- .../components/ScheduleSearchPage.scss | 53 ++++++- .../components/ScheduleSearchPage.tsx | 129 ++++++++++++++---- 2 files changed, 151 insertions(+), 31 deletions(-) diff --git a/src/features/schedule/components/ScheduleSearchPage.scss b/src/features/schedule/components/ScheduleSearchPage.scss index c243f73a..f5d017dc 100644 --- a/src/features/schedule/components/ScheduleSearchPage.scss +++ b/src/features/schedule/components/ScheduleSearchPage.scss @@ -26,6 +26,57 @@ } &__inbound { - border-top: 1px solid colors.$border; + border-top: 0; + } +} + +// `Туда / Обратно` segmented switch in the sticky header for round- +// trip schedules. Mirrors Angular's `schedule-direction-switch`: two +// flat buttons with a plane icon, the active one filled white. +.schedule-direction-switch { + display: flex; + gap: 0; + background: #f6f9fd; + border-bottom: 1px solid colors.$border; + + &__btn { + flex: 1; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 12px 16px; + background: transparent; + border: 0; + border-right: 1px solid colors.$border; + color: #6b7280; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: background-color 120ms ease, color 120ms ease; + + &:last-child { border-right: 0; } + + &:hover { + background: #eef3fa; + color: colors.$blue; + } + + &--active { + background: #fff; + color: colors.$blue; + + .schedule-direction-switch__icon { color: colors.$blue; } + } + } + + &__icon { + color: #6b7280; + transition: color 120ms ease; + fill: currentColor; + + &--flipped { + transform: rotate(180deg); + } } } diff --git a/src/features/schedule/components/ScheduleSearchPage.tsx b/src/features/schedule/components/ScheduleSearchPage.tsx index c69b83b5..0c3f9e14 100644 --- a/src/features/schedule/components/ScheduleSearchPage.tsx +++ b/src/features/schedule/components/ScheduleSearchPage.tsx @@ -9,7 +9,7 @@ */ import type { FC } from "react"; -import { useCallback, useEffect } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useNavigate } from "@modern-js/runtime/router"; import { useLocale } from "@/i18n/useLocale.js"; import { useTranslation } from "@/i18n/provider.js"; @@ -122,6 +122,15 @@ export const ScheduleSearchPage: FC = ({ params }) => { const outbound = params.outbound; const inbound = params.type === "roundtrip" ? params.inbound : undefined; + // 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 + // outbound; selection is local UI state, not URL-bound. + const [direction, setDirection] = useState<"outbound" | "inbound">("outbound"); + const activeDirection = direction === "inbound" && inbound ? inbound : outbound; + const activeKind: "outbound" | "inbound" = + direction === "inbound" && inbound ? "inbound" : "outbound"; + // Persist this search into the `Вы искали` sidebar history. The hook // dedupes by URL so re-renders / week-tab clicks won't bloat storage. useEffect(() => { @@ -192,7 +201,8 @@ export const ScheduleSearchPage: FC = ({ params }) => { const _loading = outboundLoading || (inbound ? inboundLoading : false); - /** Navigate to a different week (Monday → Sunday range). */ + /** Navigate to a different week (Monday → Sunday range). Updates the + * active direction's params and preserves the other direction. */ const handleWeekChange = useCallback( (mondayYmd: string) => { const monday = new Date(mondayYmd); @@ -204,25 +214,33 @@ export const ScheduleSearchPage: FC = ({ params }) => { const day = String(d.getDate()).padStart(2, "0"); return `${y}${m}${day}`; }; - const newOutbound = { - ...outbound, - dateFrom: ymd(monday), - dateTo: ymd(sunday), - }; - const newParams: ScheduleParams = inbound - ? { type: "roundtrip", outbound: newOutbound, inbound } - : { type: "route", outbound: newOutbound }; + const next = { dateFrom: ymd(monday), dateTo: ymd(sunday) }; + let newParams: ScheduleParams; + if (inbound && activeKind === "inbound") { + newParams = { + type: "roundtrip", + outbound, + inbound: { ...inbound, ...next }, + }; + } else if (inbound) { + newParams = { + type: "roundtrip", + outbound: { ...outbound, ...next }, + inbound, + }; + } else { + newParams = { type: "route", outbound: { ...outbound, ...next } }; + } const url = buildScheduleUrl(newParams); void navigate(`/${locale}/${url}`); }, - [navigate, locale, outbound, inbound], + [navigate, locale, outbound, inbound, activeKind], ); - /** Convert the current outbound `dateFrom` (yyyyMMdd) → Monday yyyy-MM-dd - * so the WeekTabs highlights the right tab. The dateFrom is whatever - * the URL ships, so floor to the surrounding Monday. */ + /** Convert the active direction's `dateFrom` (yyyyMMdd) → Monday + * yyyy-MM-dd so the WeekTabs highlights the right tab. */ const selectedMonday = (() => { - const f = outbound.dateFrom; + const f = activeDirection.dateFrom; if (!f || f.length !== 8) return ""; const date = new Date( parseInt(f.slice(0, 4), 10), @@ -275,10 +293,64 @@ export const ScheduleSearchPage: FC = ({ params }) => { } stickyContent={ - + <> + + {inbound && ( +
+ + +
+ )} + } > {outboundError && ( @@ -291,18 +363,15 @@ export const ScheduleSearchPage: FC = ({ params }) => { )}
-
- -
- - {inbound && ( + {activeKind === "outbound" ? ( +
+ +
+ ) : (
-

- {t("SCHEDULE.RETURN")}: {inbound.departure} → {inbound.arrival} -