From 0cdf8c849d7b1ff678f9671eef47b58af84827ec Mon Sep 17 00:00:00 2001 From: gnezim Date: Thu, 16 Apr 2026 09:20:26 +0300 Subject: [PATCH] Match Angular form controls: time range slider, date placeholders, button colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Online board filter: replace time input fields with PrimeReact Slider matching Angular's p-slider range selector. Initialize dates to null so placeholder "ДД.ММ.ГГГГ" shows instead of today's date. Schedule filter: same time slider replacement, add missing "Только прямые рейсы" (direct flights only) checkbox, date placeholders. Error page: fix "На главную" button to use outlined style (transparent bg + blue border) matching Angular's blue-home class. Remove max-width on description text. --- .../components/OnlineBoardFilter.scss | 29 ++--- .../components/OnlineBoardFilter.tsx | 50 ++++---- .../schedule/components/ScheduleStartPage.tsx | 114 ++++++++++-------- src/routes/error/[code]/page.scss | 10 +- 4 files changed, 106 insertions(+), 97 deletions(-) diff --git a/src/features/online-board/components/OnlineBoardFilter.scss b/src/features/online-board/components/OnlineBoardFilter.scss index 4db80c70..66cb92fe 100644 --- a/src/features/online-board/components/OnlineBoardFilter.scss +++ b/src/features/online-board/components/OnlineBoardFilter.scss @@ -178,27 +178,24 @@ } } - .time-selector { + .wrapper--time-selector { margin-top: vars.$space-xl; - &__inputs { - display: flex; - align-items: center; - gap: vars.$space-m; - } - - &__separator { + .time-selector__label { + font-size: fonts.$font-size-s; color: colors.$gray; - font-size: fonts.$font-size-l; + margin-bottom: vars.$space-s; } - .input--time { - flex: 1; - height: vars.$standard-button-height; - border: 1px solid colors.$border-input; - border-radius: vars.$border-radius; - padding: 0 vars.$space-m; - font-size: fonts.$font-size-l; + .time-selector { + padding: 0 vars.$space-s; + } + + .time-selector__value { + font-size: fonts.$font-size-s; + color: colors.$gray; + margin-top: vars.$space-s; + text-align: right; } } diff --git a/src/features/online-board/components/OnlineBoardFilter.tsx b/src/features/online-board/components/OnlineBoardFilter.tsx index c94b8e15..7349f5fa 100644 --- a/src/features/online-board/components/OnlineBoardFilter.tsx +++ b/src/features/online-board/components/OnlineBoardFilter.tsx @@ -11,12 +11,19 @@ import { type FC, useState, useCallback, type FormEvent } from "react"; import { useNavigate, useParams } from "@modern-js/runtime/router"; import { Calendar } from "primereact/calendar"; +import { Slider, type SliderChangeEvent } from "primereact/slider"; import { AutoComplete, type AutoCompleteCompleteEvent } from "primereact/autocomplete"; import { useTranslation } from "@/i18n/provider.js"; import { useCitySearch, type CitySuggestion } from "@/shared/hooks/useCitySearch.js"; import { buildOnlineBoardUrl } from "../url.js"; import "./OnlineBoardFilter.scss"; +function minutesToTime(minutes: number): string { + const h = Math.floor(minutes / 60); + const m = minutes % 60; + return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`; +} + type AccordionTab = "flight" | "route"; function dateToYyyymmdd(value: Date): string { @@ -48,15 +55,14 @@ export const OnlineBoardFilter: FC = () => { // Flight number fields const [flightNumber, setFlightNumber] = useState(""); - const [flightDate, setFlightDate] = useState(new Date()); + const [flightDate, setFlightDate] = useState(null); const [flightNumberError, setFlightNumberError] = useState(null); // Route fields const [routeDeparture, setRouteDeparture] = useState(""); const [routeArrival, setRouteArrival] = useState(""); - const [routeDate, setRouteDate] = useState(new Date()); - const [timeFrom, setTimeFrom] = useState(""); - const [timeTo, setTimeTo] = useState(""); + const [routeDate, setRouteDate] = useState(null); + const [timeRange, setTimeRange] = useState<[number, number]>([0, 1440]); // City autocomplete search const { suggestions: routeDepSuggestions, search: searchRouteDep } = useCitySearch(); @@ -192,8 +198,9 @@ export const OnlineBoardFilter: FC = () => { setFlightDate(e.value as Date)} + onChange={(e) => setFlightDate(e.value as Date | null)} dateFormat="dd.mm.yy" + placeholder={t("SHARED.DATE_FORMAT")} showIcon className="input--filter" data-testid="date-input" @@ -293,8 +300,9 @@ export const OnlineBoardFilter: FC = () => { setRouteDate(e.value as Date)} + onChange={(e) => setRouteDate(e.value as Date | null)} dateFormat="dd.mm.yy" + placeholder={t("SHARED.DATE_FORMAT")} showIcon className="input--filter" data-testid="date-input" @@ -303,25 +311,21 @@ export const OnlineBoardFilter: FC = () => { -
- -
- setTimeFrom(e.target.value)} - data-testid="time-from-input" - /> - - setTimeTo(e.target.value)} - data-testid="time-to-input" +
+
{t("SHARED.FLIGHT_TIME")}
+
+ setTimeRange(e.value as [number, number])} + range + min={0} + max={1440} + step={60} />
+
+ {minutesToTime(timeRange[0])} — {minutesToTime(timeRange[1])} +
diff --git a/src/features/schedule/components/ScheduleStartPage.tsx b/src/features/schedule/components/ScheduleStartPage.tsx index 3cee8041..80646cb4 100644 --- a/src/features/schedule/components/ScheduleStartPage.tsx +++ b/src/features/schedule/components/ScheduleStartPage.tsx @@ -10,6 +10,7 @@ import { type FC, useState, useCallback, type FormEvent } from "react"; import { useNavigate, useParams } from "@modern-js/runtime/router"; import { Calendar } from "primereact/calendar"; +import { Slider, type SliderChangeEvent } from "primereact/slider"; import { AutoComplete, type AutoCompleteCompleteEvent } from "primereact/autocomplete"; import { useTranslation } from "@/i18n/provider.js"; import { useCitySearch, type CitySuggestion } from "@/shared/hooks/useCitySearch.js"; @@ -21,6 +22,12 @@ import type { PopularRequest } from "@/features/popular-requests/types.js"; import { buildScheduleUrl } from "../url.js"; import "./ScheduleStartPage.scss"; +function minutesToTime(minutes: number): string { + const h = Math.floor(minutes / 60); + const m = minutes % 60; + return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`; +} + function dateToYyyymmdd(value: Date): string { const y = value.getFullYear().toString(); const m = (value.getMonth() + 1).toString().padStart(2, "0"); @@ -44,15 +51,14 @@ export const ScheduleStartPage: FC = () => { const [departureAirport, setDepartureAirport] = useState(""); const [arrivalAirport, setArrivalAirport] = useState(""); - const [dateFrom, setDateFrom] = useState(today); - const [dateTo, setDateTo] = useState(addDays(today, 7)); - const [timeFrom, setTimeFrom] = useState(""); - const [timeTo, setTimeTo] = useState(""); + const [dateFrom, setDateFrom] = useState(null); + const [dateTo, setDateTo] = useState(null); + const [timeRange, setTimeRange] = useState<[number, number]>([0, 1440]); + const [directOnly, setDirectOnly] = useState(false); const [isRoundTrip, setIsRoundTrip] = useState(false); - const [returnDateFrom, setReturnDateFrom] = useState(addDays(today, 7)); - const [returnDateTo, setReturnDateTo] = useState(addDays(today, 14)); - const [returnTimeFrom, setReturnTimeFrom] = useState(""); - const [returnTimeTo, setReturnTimeTo] = useState(""); + const [returnDateFrom, setReturnDateFrom] = useState(null); + const [returnDateTo, setReturnDateTo] = useState(null); + const [returnTimeRange, setReturnTimeRange] = useState<[number, number]>([0, 1440]); // City autocomplete search const { suggestions: departureSuggestions, search: searchDeparture } = useCitySearch(); @@ -87,8 +93,8 @@ export const ScheduleStartPage: FC = () => { const outbound: { departure: string; arrival: string; dateFrom: string; dateTo: string; timeFrom?: string; timeTo?: string } = { departure: dep, arrival: arr, dateFrom: dateFromParam, dateTo: dateToParam, }; - if (timeFrom) outbound.timeFrom = timeFrom; - if (timeTo) outbound.timeTo = timeTo; + if (timeRange[0] > 0) outbound.timeFrom = minutesToTime(timeRange[0]).replace(":", ""); + if (timeRange[1] < 1440) outbound.timeTo = minutesToTime(timeRange[1]).replace(":", ""); if (isRoundTrip) { if (!returnDateFrom || !returnDateTo) return; @@ -98,8 +104,8 @@ export const ScheduleStartPage: FC = () => { const inbound: { departure: string; arrival: string; dateFrom: string; dateTo: string; timeFrom?: string; timeTo?: string } = { departure: arr, arrival: dep, dateFrom: retDateFromParam, dateTo: retDateToParam, }; - if (returnTimeFrom) inbound.timeFrom = returnTimeFrom; - if (returnTimeTo) inbound.timeTo = returnTimeTo; + if (returnTimeRange[0] > 0) inbound.timeFrom = minutesToTime(returnTimeRange[0]).replace(":", ""); + if (returnTimeRange[1] < 1440) inbound.timeTo = minutesToTime(returnTimeRange[1]).replace(":", ""); url = buildScheduleUrl({ type: "roundtrip", @@ -115,7 +121,7 @@ export const ScheduleStartPage: FC = () => { void navigate(`/${lang}/${url}`); }, - [departureAirport, arrivalAirport, dateFrom, dateTo, timeFrom, timeTo, isRoundTrip, returnDateFrom, returnDateTo, returnTimeFrom, returnTimeTo, navigate, lang], + [departureAirport, arrivalAirport, dateFrom, dateTo, timeRange, directOnly, isRoundTrip, returnDateFrom, returnDateTo, returnTimeRange, navigate, lang], ); const handlePopularRequestClick = useCallback((_request: PopularRequest) => { @@ -164,8 +170,9 @@ export const ScheduleStartPage: FC = () => { setDateFrom(e.value as Date)} + onChange={(e) => setDateFrom(e.value as Date | null)} dateFormat="dd.mm.yy" + placeholder={t("SHARED.DATE_FORMAT")} showIcon className="input--filter" inputId="schedule-date-from" @@ -177,8 +184,9 @@ export const ScheduleStartPage: FC = () => { setDateTo(e.value as Date)} + onChange={(e) => setDateTo(e.value as Date | null)} dateFormat="dd.mm.yy" + placeholder={t("SHARED.DATE_FORMAT")} showIcon className="input--filter" inputId="schedule-date-to" @@ -186,27 +194,33 @@ export const ScheduleStartPage: FC = () => { />
-
- -
- setTimeFrom(e.target.value)} - className="input--filter" - data-testid="time-from-input" - /> - - setTimeTo(e.target.value)} - className="input--filter" - data-testid="time-to-input" +
+
{t("SHARED.DEPARTURE_TIME")}
+
+ setTimeRange(e.value as [number, number])} + range + min={0} + max={1440} + step={60} />
+
+ {minutesToTime(timeRange[0])} — {minutesToTime(timeRange[1])} +
+
+ +
+
@@ -249,27 +263,21 @@ export const ScheduleStartPage: FC = () => { />
-
- -
- setReturnTimeFrom(e.target.value)} - className="input--filter" - data-testid="return-time-from-input" - /> - - setReturnTimeTo(e.target.value)} - className="input--filter" - data-testid="return-time-to-input" +
+
{t("SHARED.RETURN_FLIGHT_TIME")}
+
+ setReturnTimeRange(e.value as [number, number])} + range + min={0} + max={1440} + step={60} />
+
+ {minutesToTime(returnTimeRange[0])} — {minutesToTime(returnTimeRange[1])} +
)} diff --git a/src/routes/error/[code]/page.scss b/src/routes/error/[code]/page.scss index 637171d4..f58c3e79 100644 --- a/src/routes/error/[code]/page.scss +++ b/src/routes/error/[code]/page.scss @@ -71,7 +71,6 @@ } &__description { - max-width: 480px; color: colors.$light-gray; line-height: 1.5; } @@ -175,12 +174,13 @@ } &--secondary { - background-color: colors.$blue-dark; - color: colors.$white; - border: none; + background: transparent; + color: colors.$blue-light; + border: 1.3px solid colors.$blue-light; &:hover { - opacity: 0.9; + border-color: colors.$blue; + color: colors.$blue; } }