Add validation error displays to RouteFilter component
- Add three error display elements with test IDs: departure-error, same-city-error, validation-error - Add validation state management: departureError, arrivalError, sameAirportError, searchValidationError - Add validateDepartureInput() function for input validation (reuses validateCityCode) - Implement same-city validation in useEffect to detect duplicate departure/arrival - Update handleSearch() to check all required fields and show validation-error when missing - Add error styling: .route-filter__error with red text (#d32f2f), light red background (#ffebee), and left border - Same-city error shows inline below arrival input - Search validation error displays below action buttons - Errors automatically clear when conditions are fixed
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<RouteFilterProps> = ({
|
||||
const [showPassengerDropdown, setShowPassengerDropdown] = useState(false)
|
||||
const [cabinClass, setCabinClass] = useState<'economy' | 'business' | 'first'>(initialCabinClass || 'economy')
|
||||
const [showCabinDropdown, setShowCabinDropdown] = useState(false)
|
||||
const [departureError, setDepartureError] = useState<string>('')
|
||||
const [arrivalError, setArrivalError] = useState<string>('')
|
||||
const [sameAirportError, setSameAirportError] = useState(false)
|
||||
const [searchValidationError, setSearchValidationError] = useState<string>('')
|
||||
|
||||
const totalPassengers = adults + children + infants
|
||||
const maxPassengers = 9
|
||||
@@ -113,6 +124,16 @@ export const RouteFilter: React.FC<RouteFilterProps> = ({
|
||||
|
||||
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<RouteFilterProps> = ({
|
||||
}, [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<RouteFilterProps> = ({
|
||||
{departure.code}
|
||||
</div>
|
||||
)}
|
||||
{departureError && (
|
||||
<div data-testid="departure-error" className="route-filter__error">
|
||||
{departureError}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
@@ -214,6 +255,16 @@ export const RouteFilter: React.FC<RouteFilterProps> = ({
|
||||
{arrival.code}
|
||||
</div>
|
||||
)}
|
||||
{sameAirportError && (
|
||||
<div data-testid="same-city-error" className="route-filter__error">
|
||||
Departure and arrival cities must be different
|
||||
</div>
|
||||
)}
|
||||
{arrivalError && (
|
||||
<div data-testid="arrival-error" className="route-filter__error">
|
||||
{arrivalError}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="route-filter__trip-type">
|
||||
@@ -458,6 +509,11 @@ export const RouteFilter: React.FC<RouteFilterProps> = ({
|
||||
data-testid="clear-filters-button"
|
||||
/>
|
||||
</div>
|
||||
{searchValidationError && (
|
||||
<div data-testid="validation-error" className="route-filter__error">
|
||||
{searchValidationError}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user