diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils.ts b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils.ts index 8fb30757b..527db7fc8 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils.ts +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils.ts @@ -6,7 +6,10 @@ import { getLang } from "@/i18n/serverContext" import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums" import type { AvailabilityInput } from "@/types/components/hotelReservation/selectHotel/availabilityInput" import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps" -import type { Filter } from "@/types/components/hotelReservation/selectHotel/hotelFilters" +import type { + CategorizedFilters, + Filter, +} from "@/types/components/hotelReservation/selectHotel/hotelFilters" import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate" import { type PointOfInterest, @@ -14,6 +17,15 @@ import { PointOfInterestGroupEnum, } from "@/types/hotel" +const hotelSurroundingsFilterNames = [ + "Hotel surroundings", + "Hotel omgivelser", + "Hotelumgebung", + "Hotellia lähellä", + "Hotellomgivelser", + "Omgivningar", +] + export async function fetchAvailableHotels( input: AvailabilityInput ): Promise { @@ -39,7 +51,7 @@ export async function fetchAvailableHotels( return await Promise.all(hotels) } -export function getFiltersFromHotels(hotels: HotelData[]) { +export function getFiltersFromHotels(hotels: HotelData[]): CategorizedFilters { const filters = hotels.flatMap((hotel) => hotel.hotelData.detailedFacilities) const uniqueFilterIds = [...new Set(filters.map((filter) => filter.id))] @@ -47,7 +59,21 @@ export function getFiltersFromHotels(hotels: HotelData[]) { .map((filterId) => filters.find((filter) => filter.id === filterId)) .filter((filter): filter is Filter => filter !== undefined) - return filterList + return filterList.reduce( + (acc, filter) => { + if (filter.filter && hotelSurroundingsFilterNames.includes(filter.filter)) + return { + facilityFilters: acc.facilityFilters, + surroundingsFilters: [...acc.surroundingsFilters, filter], + } + + return { + facilityFilters: [...acc.facilityFilters, filter], + surroundingsFilters: acc.surroundingsFilters, + } + }, + { facilityFilters: [], surroundingsFilters: [] } + ) } export function getPointOfInterests(hotels: HotelData[]): PointOfInterest[] { diff --git a/components/HotelReservation/HotelCard/index.tsx b/components/HotelReservation/HotelCard/index.tsx index 2bea7c895..293530998 100644 --- a/components/HotelReservation/HotelCard/index.tsx +++ b/components/HotelReservation/HotelCard/index.tsx @@ -1,3 +1,6 @@ +"use client" +import { useIntl } from "react-intl" + import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data" import { PriceTagIcon, ScandicLogoIcon } from "@/components/Icons" import TripAdvisorIcon from "@/components/Icons/TripAdvisor" @@ -17,8 +20,8 @@ import styles from "./hotelCard.module.css" import type { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps" -export default async function HotelCard({ hotel }: HotelCardProps) { - const intl = await getIntl() +export default function HotelCard({ hotel }: HotelCardProps) { + const intl = useIntl() const { hotelData } = hotel const { price } = hotel diff --git a/components/HotelReservation/HotelCardListing/index.tsx b/components/HotelReservation/HotelCardListing/index.tsx index fb17cb2a5..6cecce2d9 100644 --- a/components/HotelReservation/HotelCardListing/index.tsx +++ b/components/HotelReservation/HotelCardListing/index.tsx @@ -1,3 +1,7 @@ +"use client" +import { useSearchParams } from "next/navigation" +import { useMemo } from "react" + import Title from "@/components/TempDesignSystem/Text/Title" import HotelCard from "../HotelCard" @@ -7,12 +11,25 @@ import styles from "./hotelCardListing.module.css" import { HotelCardListingProps } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps" export default function HotelCardListing({ hotelData }: HotelCardListingProps) { - // TODO: filter with url params + const searchParams = useSearchParams() + + const hotels = useMemo(() => { + const appliedFilters = searchParams.get("filters")?.split(",") + if (!appliedFilters || appliedFilters.length === 0) return hotelData + + return hotelData.filter((hotel) => + appliedFilters.every((appliedFilterId) => + hotel.hotelData.detailedFacilities.some( + (facility) => facility.id.toString() === appliedFilterId + ) + ) + ) + }, [searchParams, hotelData]) return (
- {hotelData && hotelData.length ? ( - hotelData.map((hotel) => ( + {hotels.length ? ( + hotels.map((hotel) => ( )) ) : ( diff --git a/components/HotelReservation/SelectHotel/HotelFilter/index.tsx b/components/HotelReservation/SelectHotel/HotelFilter/index.tsx index ad32427c0..97096f909 100644 --- a/components/HotelReservation/SelectHotel/HotelFilter/index.tsx +++ b/components/HotelReservation/SelectHotel/HotelFilter/index.tsx @@ -1,5 +1,8 @@ "use client" +import { usePathname, useSearchParams } from "next/navigation" +import { useCallback, useEffect } from "react" +import { useForm } from "react-hook-form" import { useIntl } from "react-intl" import styles from "./hotelFilter.module.css" @@ -8,26 +11,77 @@ import { HotelFiltersProps } from "@/types/components/hotelReservation/selectHot export default function HotelFilter({ filters }: HotelFiltersProps) { const intl = useIntl() + const searchParams = useSearchParams() + const pathname = usePathname() - function handleOnChange() { - // TODO: Update URL with selected values - } + const { watch, handleSubmit, getValues, register } = useForm< + Record + >({ + defaultValues: searchParams + ?.get("filters") + ?.split(",") + .reduce((acc, curr) => ({ ...acc, [curr]: true }), {}), + }) + + const submitFilter = useCallback(() => { + const newSearchParams = new URLSearchParams(searchParams) + const values = Object.entries(getValues()) + .filter(([_, value]) => !!value) + .map(([key, _]) => key) + .join(",") + + if (values === "") { + newSearchParams.delete("filters") + } else { + newSearchParams.set("filters", values) + } + + if (values !== searchParams.values.toString()) { + window.history.replaceState( + null, + "", + `${pathname}?${newSearchParams.toString()}` + ) + } + }, [getValues, pathname, searchParams]) + + useEffect(() => { + const subscription = watch(() => handleSubmit(submitFilter)()) + return () => subscription.unsubscribe() + }, [handleSubmit, watch, submitFilter]) return (