"use client" import Downshift from "downshift" import { ChangeEvent, FocusEvent, FormEvent, useCallback, useEffect, useReducer, } from "react" import { useFormContext, useWatch } from "react-hook-form" import { useIntl } from "react-intl" import SkeletonShimmer from "@/components/SkeletonShimmer" import Caption from "@/components/TempDesignSystem/Text/Caption" import Input from "../Input" import { init, localStorageKey, reducer, sessionStorageKey } from "./reducer" import SearchList from "./SearchList" import styles from "./search.module.css" import type { BookingWidgetSchema } from "@/types/components/bookingWidget" import { ActionType } from "@/types/components/form/bookingwidget" import type { SearchProps } from "@/types/components/search" import type { Location } from "@/types/trpc/routers/hotel/locations" const name = "search" export default function Search({ locations, handlePressEnter }: SearchProps) { const { register, setValue, unregister } = useFormContext() const intl = useIntl() const value = useWatch({ name }) const [state, dispatch] = useReducer( reducer, { defaultLocations: locations }, init ) const handleMatchLocations = useCallback( function (searchValue: string) { return locations.filter((location) => { return location.name.toLowerCase().includes(searchValue.toLowerCase()) }) }, [locations] ) function handleClearSearchHistory() { localStorage.removeItem(localStorageKey) dispatch({ type: ActionType.CLEAR_HISTORY_LOCATIONS }) } function dispatchInputValue(inputValue: string) { if (inputValue) { dispatch({ payload: { search: inputValue }, type: ActionType.SEARCH_LOCATIONS, }) } else { dispatch({ type: ActionType.CLEAR_SEARCH_LOCATIONS }) } } function handleOnChange( evt: FormEvent | ChangeEvent ) { const newValue = evt.currentTarget.value setValue(name, newValue) dispatchInputValue(value) } function handleOnFocus(evt: FocusEvent) { const searchValue = evt.currentTarget.value if (searchValue) { const matchingLocations = handleMatchLocations(searchValue) if (matchingLocations.length) { dispatch({ payload: { search: searchValue }, type: ActionType.SEARCH_LOCATIONS, }) } } } function handleOnSelect(selectedItem: Location | null) { if (selectedItem) { const stringified = JSON.stringify(selectedItem) setValue("location", encodeURIComponent(stringified)) sessionStorage.setItem(sessionStorageKey, stringified) setValue(name, selectedItem.name) const searchHistoryMap = new Map() searchHistoryMap.set(selectedItem.name, selectedItem) if (state.searchHistory) { state.searchHistory.forEach((location) => { searchHistoryMap.set(location.name, location) }) } const searchHistory: Location[] = [] searchHistoryMap.forEach((location) => { searchHistory.push(location) }) localStorage.setItem(localStorageKey, JSON.stringify(searchHistory)) dispatch({ payload: { location: selectedItem, searchHistory, }, type: ActionType.SELECT_ITEM, }) } else { sessionStorage.removeItem(sessionStorageKey) } } useEffect(() => { const searchData = typeof window !== "undefined" ? sessionStorage.getItem(sessionStorageKey) : undefined const searchHistory = typeof window !== "undefined" ? localStorage.getItem(localStorageKey) : null if (searchData || searchHistory) { dispatch({ payload: { searchData: searchData ? JSON.parse(searchData) : undefined, searchHistory: searchHistory ? JSON.parse(searchHistory) : null, }, type: ActionType.SET_STORAGE_DATA, }) } }, [dispatch]) const stayType = state.searchData?.type === "cities" ? "city" : "hotel" const stayValue = (value === state.searchData?.name && ((state.searchData?.type === "cities" && state.searchData?.name) || state.searchData?.id)) || "" useEffect(() => { if (stayType === "city") { unregister("hotel") setValue(stayType, stayValue, { shouldValidate: true, }) } else { unregister("city") setValue(stayType, Number(stayValue), { shouldValidate: true, }) } }, [stayType, stayValue, unregister, setValue]) return ( (value ? value.name : "")} onSelect={handleOnSelect} onInputValueChange={(inputValue) => dispatchInputValue(inputValue)} defaultHighlightedIndex={0} > {({ getInputProps, getItemProps, getLabelProps, getMenuProps, getRootProps, highlightedIndex, isOpen, openMenu, }) => (
{value ? ( // Adding hidden input to define hotel or city based on destination selection for basic form submit. ) : null}
)}
) } export function SearchSkeleton() { const intl = useIntl() return (
{intl.formatMessage({ id: "Where to" })}
) }