import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { closeMobileFilter, setActiveSearchField, setFieldValue, setFromDate, setToDate, setSearchResults as setSearchResultsAction, setSearchResults } from '../../store/qsm-slice'; import { QSMRootState } from '../../store/qsm-store'; import SearchInput from '../search-input'; import DateRangePicker from '../date-range-picker'; import TravelInput from '../travel-input'; import { format } from 'date-fns'; import QSMConfigurationContext from '../../qsm-configuration-context'; import { BaseFieldConfig, TypeaheadOption } from '../../types'; import { getTranslations } from '../../../shared/utils/localization-util'; const MobileFilterModal: React.FC = () => { const { datesIcon, languageCode } = useContext(QSMConfigurationContext); const translations = getTranslations(languageCode ?? 'en-GB'); const dispatch = useDispatch(); const { mobileFilterType, mobileDatePickerMode, activeSearchFieldProps, fromDate, toDate, searchResults, departureAirport, destinationAirport, returnAirport, destination } = useSelector((state: QSMRootState) => state.qsm); const searchInputRef = useRef(null); const [inputValue, setInputValue] = useState(''); /* ---------------------------------------------------------------- */ /* Sync local state when a new field is opened */ /* ---------------------------------------------------------------- */ useEffect(() => { if (activeSearchFieldProps) { setInputValue(activeSearchFieldProps.value || ''); // setSearchResultsLocal([]); // hasTypedRef.current = false; } }, [activeSearchFieldProps]); useEffect(() => { if (mobileFilterType !== 'search') return; requestAnimationFrame(() => { const input = searchInputRef.current; if (!input) return; input.focus(); input.setSelectionRange(input.value.length, input.value.length); }); }, [mobileFilterType, activeSearchFieldProps?.fieldKey]); /* ---------------------------------------------------------------- */ /* Helpers */ /* ---------------------------------------------------------------- */ const closeModal = () => { // hasTypedRef.current = false; dispatch(closeMobileFilter()); }; const findConfig = (all: BaseFieldConfig[], key: string): BaseFieldConfig | undefined => { for (const config of all) { if (config.fieldKey === key) { return config; } } return undefined; }; const config = useMemo(() => { if (!activeSearchFieldProps) return undefined; const searchFields = [departureAirport, destinationAirport, returnAirport, destination].filter((field): field is BaseFieldConfig => field !== undefined); return findConfig(searchFields, activeSearchFieldProps.fieldKey); }, [departureAirport, destinationAirport, returnAirport, destination, activeSearchFieldProps]); const match = useCallback( (input: string) => { if (!input) { return []; } const lowered = input.toLowerCase(); return activeSearchFieldProps?.options.filter( (option) => option.value.toLowerCase().includes(lowered) || option.iataCode?.toLowerCase().includes(lowered) ); }, [activeSearchFieldProps?.options] ); const handleInputChange = useCallback( (input: string) => { setInputValue(input); if (!activeSearchFieldProps) return; dispatch(setFieldValue({ fieldKey: activeSearchFieldProps.fieldKey, value: input })); dispatch(setSearchResults([])); dispatch(setActiveSearchField(activeSearchFieldProps.fieldKey)); if (config?.onChange) { config.onChange(input); return; } const filtered = match(input) ?? []; dispatch(setSearchResults(filtered)); }, [dispatch, activeSearchFieldProps, match] ); useEffect(() => { if (!activeSearchFieldProps) return; if (!inputValue) return; dispatch(setSearchResults(match(inputValue) ?? [])); }, [activeSearchFieldProps?.options]); const handleLocationSelect = (option: TypeaheadOption) => { if (activeSearchFieldProps) { const { fieldKey } = activeSearchFieldProps; dispatch(setFieldValue({ fieldKey, value: option.value })); dispatch(setSearchResultsAction([])); dispatch(setActiveSearchField(null)); } dispatch(closeMobileFilter()); }; /* ---------------------------------------------------------------- */ /* Render helpers */ /* ---------------------------------------------------------------- */ const renderDatePickerModal = () => { const hasValidDates = mobileDatePickerMode === 'single' ? Boolean(fromDate) : Boolean(fromDate && toDate); return (
{/* header */}
{translations.QSM.CHOOSE_DATES}
{/* date inputs + picker */}
{/* Departure */}
{/* footer */}
); }; const renderSearchInputModal = () => { if (!activeSearchFieldProps) { return null; } return (
{/* header */}
{activeSearchFieldProps.label}
{/*
*/} {/* input + dropdown */}
{/*
*/} {/* empty state */} {/* {searchResultsLocal.length === 0 && (
Geen resultaten Typ een plaatsnaam om te zoeken
)} */}
); }; const renderTravelerModal = () => (
{/* header */}
{translations.QSM.TRAVELERS}
); /* ---------------------------------------------------------------- */ /* Switch modal type */ /* ---------------------------------------------------------------- */ switch (mobileFilterType) { case 'search': return renderSearchInputModal(); case 'date': return renderDatePickerModal(); case 'traveler': return renderTravelerModal(); default: return null; } }; export default MobileFilterModal;