Use CityAutocomplete for OnlineBoardFilter Route tab departure + arrival
This commit is contained in:
@@ -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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user