Use CityAutocomplete for OnlineBoardFilter Route tab departure + arrival

This commit is contained in:
2026-04-17 15:11:47 +03:00
parent ba302c6b03
commit b8d5de6ca7
2 changed files with 37 additions and 55 deletions
@@ -12,9 +12,9 @@ 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 { CityAutocomplete } from "@/ui/city-autocomplete/index.js";
import { useDictionaries } from "@/shared/dictionaries/index.js";
import { buildOnlineBoardUrl } from "../url.js";
import "./OnlineBoardFilter.scss";
@@ -72,6 +72,7 @@ export const OnlineBoardFilter: FC<OnlineBoardFilterProps> = ({
const navigate = useNavigate();
const routeParams = useParams<{ lang: string }>();
const lang = routeParams.lang ?? "ru";
const { dictionaries } = useDictionaries(lang);
const [activeTab, setActiveTab] = useState<AccordionTab>(initialTab ?? "route");
@@ -83,34 +84,22 @@ export const OnlineBoardFilter: FC<OnlineBoardFilterProps> = ({
const [flightNumberError, setFlightNumberError] = useState<string | null>(null);
// Route fields
const [routeDeparture, setRouteDeparture] = useState<CitySuggestion | string>(initialDeparture ?? "");
const [routeArrival, setRouteArrival] = useState<CitySuggestion | string>(initialArrival ?? "");
const [routeDepartureCode, setRouteDepartureCode] = useState<string>(initialDeparture ?? "");
const [routeArrivalCode, setRouteArrivalCode] = useState<string>(initialArrival ?? "");
const [routeDate, setRouteDate] = useState<Date | null>(
initialDate ? yyyymmddToDate(initialDate) : new Date(),
);
const [timeRange, setTimeRange] = useState<[number, number]>([0, 1440]);
// City autocomplete search
const { suggestions: routeDepSuggestions, search: searchRouteDep } = useCitySearch();
const { suggestions: routeArrSuggestions, search: searchRouteArr } = useCitySearch();
const handleRouteDepSearch = useCallback((event: AutoCompleteCompleteEvent) => {
void searchRouteDep(event.query);
}, [searchRouteDep]);
const handleRouteArrSearch = useCallback((event: AutoCompleteCompleteEvent) => {
void searchRouteArr(event.query);
}, [searchRouteArr]);
const handleTabClick = useCallback((tab: AccordionTab) => {
setActiveTab((prev) => (prev === tab ? prev : tab));
}, []);
const handleExchange = useCallback(() => {
const prevDep = routeDeparture;
setRouteDeparture(routeArrival);
setRouteArrival(prevDep);
}, [routeDeparture, routeArrival]);
const prevDep = routeDepartureCode;
setRouteDepartureCode(routeArrivalCode);
setRouteArrivalCode(prevDep);
}, [routeDepartureCode, routeArrivalCode]);
const handleFlightSubmit = useCallback(
(e: FormEvent) => {
@@ -138,17 +127,13 @@ export const OnlineBoardFilter: FC<OnlineBoardFilterProps> = ({
if (!routeDate) return;
const dateParam = dateToYyyymmdd(routeDate);
const depCode = typeof routeDeparture === "string"
? routeDeparture.trim().toUpperCase()
: routeDeparture.code;
const arrCode = typeof routeArrival === "string"
? routeArrival.trim().toUpperCase()
: routeArrival.code;
const depCode = routeDepartureCode.trim().toUpperCase();
const arrCode = routeArrivalCode.trim().toUpperCase();
if (!depCode || !arrCode) return;
const url = buildOnlineBoardUrl({ type: "route", departure: depCode, arrival: arrCode, date: dateParam });
void navigate(`/${lang}/${url}`);
},
[routeDeparture, routeArrival, routeDate, navigate, lang],
[routeDepartureCode, routeArrivalCode, routeDate, navigate, lang],
);
return (
@@ -275,21 +260,13 @@ export const OnlineBoardFilter: FC<OnlineBoardFilterProps> = ({
<div className="p-accordion-content">
<form onSubmit={handleRouteSubmit} data-testid="search-form">
<div className="filter-content">
<label className="label--filter">
{t("SHARED.DEPARTURE_CITY")}
</label>
<AutoComplete
value={routeDeparture}
suggestions={routeDepSuggestions}
completeMethod={handleRouteDepSearch}
field="name"
dropdown
onChange={(e) => setRouteDeparture(e.value as CitySuggestion | string)}
<CityAutocomplete
label={t("SHARED.DEPARTURE_CITY")}
placeholder={t("SHARED.CITY_PLACEHOLDER")}
className="input--filter"
inputClassName="input--filter"
data-testid="route-departure-input"
inputId="route-departure-input"
value={routeDepartureCode}
onChange={setRouteDepartureCode}
dictionaries={dictionaries}
testIdPrefix="route-departure"
/>
<div className="change-container">
@@ -305,21 +282,13 @@ export const OnlineBoardFilter: FC<OnlineBoardFilterProps> = ({
</button>
</div>
<label className="label--filter">
{t("SHARED.ARRIVAL_CITY")}
</label>
<AutoComplete
value={routeArrival}
suggestions={routeArrSuggestions}
completeMethod={handleRouteArrSearch}
field="name"
dropdown
onChange={(e) => setRouteArrival(e.value as CitySuggestion | string)}
<CityAutocomplete
label={t("SHARED.ARRIVAL_CITY")}
placeholder={t("SHARED.CITY_PLACEHOLDER")}
className="input--filter"
inputClassName="input--filter"
data-testid="route-arrival-input"
inputId="route-arrival-input"
value={routeArrivalCode}
onChange={setRouteArrivalCode}
dictionaries={dictionaries}
testIdPrefix="route-arrival"
/>
<div className="calendar">
@@ -51,6 +51,19 @@ vi.mock("@/shared/hooks/useSearchHistory.js", () => ({
useSearchHistory: () => ({ items: [], add: vi.fn(), clear: vi.fn() }),
}));
vi.mock("@/shared/dictionaries/index.js", () => ({
useDictionaries: () => ({ dictionaries: null, loading: false, error: null }),
}));
vi.mock("@/ui/city-autocomplete/index.js", () => ({
CityAutocomplete: (props: Record<string, unknown>) => (
<input
data-testid={`${(props["testIdPrefix"] as string) ?? "city-autocomplete"}-input`}
placeholder={props["placeholder"] as string}
/>
),
}));
// ---------------------------------------------------------------------------
// Pure function: buildPopularRequestQueryParams
// ---------------------------------------------------------------------------