import type { ITourItem } from 'src/types/tour'; import type { Theme, SxProps } from '@mui/material/styles'; import parse from 'autosuggest-highlight/parse'; import match from 'autosuggest-highlight/match'; import { useDebounce } from 'minimal-shared/hooks'; import { useState, useEffect, useCallback } from 'react'; import Avatar from '@mui/material/Avatar'; import TextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; import Link, { linkClasses } from '@mui/material/Link'; import InputAdornment from '@mui/material/InputAdornment'; import CircularProgress from '@mui/material/CircularProgress'; import Autocomplete, { autocompleteClasses, createFilterOptions } from '@mui/material/Autocomplete'; import { useRouter } from 'src/routes/hooks'; import { RouterLink } from 'src/routes/components'; import { _tours } from 'src/_mock'; import { Iconify } from 'src/components/iconify'; import { SearchNotFound } from 'src/components/search-not-found'; // ---------------------------------------------------------------------- type Props = { sx?: SxProps; redirectPath: (id: string) => string; }; export function TourSearch({ redirectPath, sx }: Props) { const router = useRouter(); const [searchQuery, setSearchQuery] = useState(''); const [selectedItem, setSelectedItem] = useState(null); const debouncedQuery = useDebounce(searchQuery); const { searchResults: options, searchLoading: loading } = useSearchData(debouncedQuery); const handleChange = useCallback( (item: ITourItem | null) => { setSelectedItem(item); if (item) { router.push(redirectPath(item.id)); } }, [router, redirectPath] ); const filterOptions = createFilterOptions({ matchFrom: 'any', stringify: (option: ITourItem) => `${option.name} ${option.destination}`, }); const paperStyles: SxProps = { width: 320, [`& .${autocompleteClasses.listbox}`]: { [`& .${autocompleteClasses.option}`]: { p: 0, [`& .${linkClasses.root}`]: { p: 0.75, gap: 1.5, width: 1, display: 'flex', alignItems: 'center', }, }, }, }; return ( handleChange(newValue)} onInputChange={(event, newValue) => setSearchQuery(newValue)} getOptionLabel={(option) => option.name} noOptionsText={} isOptionEqualToValue={(option, value) => option.id === value.id} slotProps={{ paper: { sx: paperStyles } }} sx={[{ width: { xs: 1, sm: 260 } }, ...(Array.isArray(sx) ? sx : [sx])]} renderInput={(params) => ( ), endAdornment: ( <> {loading ? : null} {params.InputProps.endAdornment} ), }, }} /> )} renderOption={(props, tour, { inputValue }) => { const matches = match(tour.name, inputValue); const parts = parse(tour.name, matches); return (
  • {parts.map((part, index) => ( {part.text} ))}
  • ); }} /> ); } // ---------------------------------------------------------------------- function useSearchData(searchQuery: string) { const [searchResults, setSearchResults] = useState([]); const [searchLoading, setSearchLoading] = useState(false); const fetchSearchResults = useCallback(async () => { setSearchLoading(true); try { await new Promise((resolve) => setTimeout(resolve, 500)); const results = _tours.filter(({ name, destination }) => [name, destination].some((field) => field?.toLowerCase().includes(searchQuery.toLowerCase()) ) ); setSearchResults(results); } catch (error) { console.error(error); } finally { setSearchLoading(false); } }, [searchQuery]); useEffect(() => { if (searchQuery) { fetchSearchResults(); } else { setSearchResults([]); } }, [fetchSearchResults, searchQuery]); return { searchResults, searchLoading }; }