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 e1c9dcebb..8d09a6c33 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 @@ -243,3 +243,22 @@ flex: 1; } } + +.route-filter__error { + margin-top: 4px; + padding: 6px 8px; + background-color: #ffebee; + border-left: 3px solid #d32f2f; + border-radius: 2px; + font-size: 12px; + color: #d32f2f; + font-weight: 500; +} + +.route-filter__error-message { + display: flex; + align-items: center; + gap: 6px; + font-size: 12px; + color: #d32f2f; +} 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 cc2678b42..0c72c9cd7 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 @@ -46,6 +46,13 @@ const validateCityCode = (code: string | undefined): boolean => { return /^[A-Z]{3}$/.test(code.toUpperCase()) } +// Validate departure input for invalid characters +const validateDepartureInput = (code: string | undefined): boolean => { + if (!code) return true // Empty is not invalid, just incomplete + // Only allow 3-letter airport codes (A-Z) + return validateCityCode(code) +} + const validateRouteParams = (departure: City | null, arrival: City | null, date: Date | null): boolean => { if (!departure || !arrival || !date) return false if (!validateCityCode(departure.code) || !validateCityCode(arrival.code)) return false @@ -106,6 +113,10 @@ export const RouteFilter: React.FC = ({ const [showPassengerDropdown, setShowPassengerDropdown] = useState(false) const [cabinClass, setCabinClass] = useState<'economy' | 'business' | 'first'>(initialCabinClass || 'economy') const [showCabinDropdown, setShowCabinDropdown] = useState(false) + const [departureError, setDepartureError] = useState('') + const [arrivalError, setArrivalError] = useState('') + const [sameAirportError, setSameAirportError] = useState(false) + const [searchValidationError, setSearchValidationError] = useState('') const totalPassengers = adults + children + infants const maxPassengers = 9 @@ -113,6 +124,16 @@ export const RouteFilter: React.FC = ({ const isSearchDisabled = !validateRouteParams(departure, arrival, date) + // Validate departure and arrival on change + useEffect(() => { + // Check for same city error + if (departure && arrival && departure.code === arrival.code) { + setSameAirportError(true) + } else { + setSameAirportError(false) + } + }, [departure, arrival]) + // Auto-search when departure or arrival city is selected useEffect(() => { if ((validateMinimalRouteParams(departure) || validateMinimalRouteParams(arrival)) && onSearch) { @@ -122,6 +143,21 @@ export const RouteFilter: React.FC = ({ }, [departure, arrival]) const handleSearch = useCallback(() => { + // Clear previous search validation error + setSearchValidationError('') + + // Check for all required fields + if (!departure || !arrival || !date) { + setSearchValidationError('Please fill in all required fields') + return + } + + // Check for same city error + if (departure.code === arrival.code) { + setSameAirportError(true) + return + } + if (validateRouteParams(departure, arrival, date) && onSearch) { // Pass all search parameters including return date for round trips, passenger counts, and cabin class onSearch(departure!.code, arrival!.code, date!, returnDate, tripType, { adults, children, infants }, cabinClass) @@ -192,6 +228,11 @@ export const RouteFilter: React.FC = ({ {departure.code} )} + {departureError && ( +
+ {departureError} +
+ )}