diff --git a/apps/react/src/app/features/online-board/components/online-board-filter.tsx b/apps/react/src/app/features/online-board/components/online-board-filter.tsx index 5075e3198..c6208858f 100644 --- a/apps/react/src/app/features/online-board/components/online-board-filter.tsx +++ b/apps/react/src/app/features/online-board/components/online-board-filter.tsx @@ -6,10 +6,21 @@ import { FlightNumberFilter } from './flight-number-filter' import { RouteFilter } from './route-filter' import './online-board-filter.scss' -export const OnlineBoardFilter: React.FC = () => { +interface OnlineBoardFilterProps { + initialSearchParams?: { + departure?: string + arrival?: string + date?: string | Date + returnDate?: string | Date + flightNumber?: string + tripType?: string + } +} + +export const OnlineBoardFilter: React.FC = ({ initialSearchParams }) => { const { t } = useTranslation() const navigate = useNavigate() - const [activeTab, setActiveTab] = useState(1) + const [activeTab, setActiveTab] = useState(initialSearchParams?.flightNumber ? 0 : 1) const handleFlightNumberSearch = (flightNumber: string, date: Date) => { // Navigate to flight number search results @@ -17,9 +28,15 @@ export const OnlineBoardFilter: React.FC = () => { navigate(`/onlineboard/flight/${params}`) } - const handleRouteSearch = (departure: string, arrival: string, date: Date) => { + const handleRouteSearch = (departure: string, arrival: string, date: Date, returnDate?: Date | null, tripType?: string) => { // Navigate to route search results - const params = btoa(JSON.stringify({ departure, arrival, date: date.toISOString() })) + const params = btoa(JSON.stringify({ + departure, + arrival, + date: date.toISOString(), + ...(returnDate && { returnDate: returnDate.toISOString() }), + ...(tripType && { tripType }), + })) navigate(`/onlineboard/route/${params}`) } @@ -39,7 +56,14 @@ export const OnlineBoardFilter: React.FC = () => { header={t('BOARD.ROUTE')} data-testid="route-filter" > - + diff --git a/apps/react/src/app/features/online-board/components/route-filter.scss b/apps/react/src/app/features/online-board/components/route-filter.scss index d289bfb8a..5d7d87fff 100644 --- a/apps/react/src/app/features/online-board/components/route-filter.scss +++ b/apps/react/src/app/features/online-board/components/route-filter.scss @@ -26,3 +26,47 @@ .route-filter__search-button { margin-top: 8px; } + +.route-filter__display { + margin-top: 4px; + padding: 4px 8px; + background-color: #f5f5f5; + border-radius: 4px; + font-size: 12px; + color: #666; +} + +.route-filter__trip-type { + display: flex; + flex-direction: column; + gap: 8px; +} + +.route-filter__trip-options { + display: flex; + gap: 16px; + + label { + display: flex; + align-items: center; + gap: 6px; + font-size: 14px; + cursor: pointer; + font-weight: normal; + text-transform: none; + + input[type='radio'] { + cursor: pointer; + } + } +} + +.route-filter__actions { + display: flex; + gap: 8px; + margin-top: 16px; + + button { + flex: 1; + } +} diff --git a/apps/react/src/app/features/online-board/components/route-filter.tsx b/apps/react/src/app/features/online-board/components/route-filter.tsx index 819e68e22..de7eeb853 100644 --- a/apps/react/src/app/features/online-board/components/route-filter.tsx +++ b/apps/react/src/app/features/online-board/components/route-filter.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useState, useEffect, useCallback } from 'react' import { Button } from 'primereact/button' import { useTranslation } from 'react-i18next' import { CityAutocomplete, City } from '@app/components/city-autocomplete' @@ -7,7 +7,12 @@ import { TimeSelector } from '@app/components/time-selector' import './route-filter.scss' export interface RouteFilterProps { - onSearch: (departure: string, arrival: string, date: Date) => void + onSearch?: (departure: string, arrival: string, date: Date, returnDate?: Date | null, tripType?: string) => void + initialDeparture?: string + initialArrival?: string + initialDate?: Date | string + initialReturnDate?: Date | string + initialTripType?: string } // Validation service @@ -22,21 +27,69 @@ const validateRouteParams = (departure: City | null, arrival: City | null, date: return true } -export const RouteFilter: React.FC = ({ onSearch }) => { +// Validate single departure city for auto-search +const validateMinimalRouteParams = (departure: City | null): boolean => { + return departure !== null && validateCityCode(departure.code) +} + +// Mock city database for finding cities by code +const MOCK_CITIES: City[] = [ + { code: 'SVO', name: 'Moscow Sheremetyevo', country: 'Russia' }, + { code: 'LED', name: 'Saint Petersburg Pulkovo', country: 'Russia' }, + { code: 'AER', name: 'Sochi', country: 'Russia' }, + { code: 'DME', name: 'Moscow Domodedovo', country: 'Russia' }, + { code: 'VKO', name: 'Moscow Vnukovo', country: 'Russia' }, +] + +const getCityByCode = (code?: string): City | null => { + if (!code) return null + return MOCK_CITIES.find(c => c.code === code.toUpperCase()) || null +} + +export const RouteFilter: React.FC = ({ + onSearch, + initialDeparture, + initialArrival, + initialDate, + initialReturnDate, + initialTripType +}) => { const { t } = useTranslation() - const [departure, setDeparture] = useState(null) - const [arrival, setArrival] = useState(null) - const [date, setDate] = useState(new Date()) + const [departure, setDeparture] = useState(getCityByCode(initialDeparture)) + const [arrival, setArrival] = useState(getCityByCode(initialArrival)) + const [date, setDate] = useState( + initialDate + ? (typeof initialDate === 'string' ? new Date(initialDate) : initialDate) + : new Date() + ) + const [returnDate, setReturnDate] = useState( + initialReturnDate + ? (typeof initialReturnDate === 'string' ? new Date(initialReturnDate) : initialReturnDate) + : null + ) + const [tripType, setTripType] = useState<'one-way' | 'round-trip'>( + (initialTripType as 'one-way' | 'round-trip') || 'round-trip' + ) const [startTime, setStartTime] = useState('00:00') const [endTime, setEndTime] = useState('23:59') const isSearchDisabled = !validateRouteParams(departure, arrival, date) - const handleSearch = () => { - if (validateRouteParams(departure, arrival, date)) { - onSearch(departure!.code, arrival!.code, date!) + // Auto-search when departure or arrival city is selected + useEffect(() => { + if ((validateMinimalRouteParams(departure) || validateMinimalRouteParams(arrival)) && onSearch) { + // Make an immediate search with whatever cities are available + onSearch(departure?.code || '', arrival?.code || '', date || new Date()) } - } + }, [departure, arrival]) + + const handleSearch = useCallback(() => { + if (validateRouteParams(departure, arrival, date) && onSearch) { + // Pass all search parameters including return date for round trips + const handler = onSearch as (departure: string, arrival: string, date: Date, returnDate?: Date | null, tripType?: string) => void + handler(departure!.code, arrival!.code, date!, returnDate, tripType) + } + }, [departure, arrival, date, returnDate, tripType, onSearch]) const handleSwap = () => { const temp = departure @@ -44,6 +97,13 @@ export const RouteFilter: React.FC = ({ onSearch }) => { setArrival(temp) } + const handleClear = () => { + setDeparture(null) + setArrival(null) + setDate(new Date()) + setReturnDate(null) + } + return (
@@ -54,6 +114,11 @@ export const RouteFilter: React.FC = ({ onSearch }) => { placeholder={t('SHARED.SELECT_CITY')} data-testid="departure-input" /> + {departure && ( +
+ {departure.code} +
+ )}
+ +
+ +
+ + +
@@ -78,10 +174,31 @@ export const RouteFilter: React.FC = ({ onSearch }) => { + {date && ( +
+ {date.toLocaleDateString()} +
+ )}
+ {tripType === 'round-trip' && ( +
+ + + {returnDate && ( +
+ {returnDate.toLocaleDateString()} +
+ )} +
+ )} + = ({ onSearch }) => { data-testid="time-selector" /> -