Feat/enter details tracking * feat: fixed bug in enter details tracking * Sidepeek events, lowestroomPrice and analyticsRateCode * Cleanup and fixed bug * Fixed analyticsratecode * Merge master * merge master * Removed console logs * Added ancillaries tracking to enter details * Added ancillary on confirmation page * Removed console log * Merge branch 'master' into feat/enter-details-tracking * Refactor searchparams * Hard code values for breakfast ancillary Approved-by: Joakim Jäderberg
235 lines
6.6 KiB
TypeScript
235 lines
6.6 KiB
TypeScript
"use client"
|
|
|
|
import { useSearchParams } from "next/navigation"
|
|
import { useCallback, useEffect, useMemo, useState } from "react"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { trackLowestRoomPrice } from "@/utils/tracking"
|
|
|
|
import RoomFilter from "../RoomFilter"
|
|
import RoomSelection from "../RoomSelection"
|
|
import { filterDuplicateRoomTypesByLowestPrice } from "./utils"
|
|
|
|
import styles from "./rooms.module.css"
|
|
|
|
import {
|
|
type DefaultFilterOptions,
|
|
RoomPackageCodeEnum,
|
|
type RoomPackageCodes,
|
|
} from "@/types/components/hotelReservation/selectRate/roomFilter"
|
|
import type { SelectRateProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
|
|
import type {
|
|
Rate,
|
|
RateCode,
|
|
} from "@/types/components/hotelReservation/selectRate/selectRate"
|
|
import type { RoomConfiguration } from "@/server/routers/hotels/output"
|
|
|
|
export default function Rooms({
|
|
roomsAvailability,
|
|
roomCategories = [],
|
|
availablePackages,
|
|
hotelType,
|
|
isUserLoggedIn,
|
|
}: SelectRateProps) {
|
|
const searchParams = useSearchParams()
|
|
|
|
const hotelId = searchParams.get("hotel")
|
|
const arrivalDate = searchParams.get("fromDate")
|
|
const departureDate = searchParams.get("toDate")
|
|
|
|
const intl = useIntl()
|
|
|
|
const visibleRooms: RoomConfiguration[] = useMemo(() => {
|
|
const deduped = filterDuplicateRoomTypesByLowestPrice(
|
|
roomsAvailability.roomConfigurations
|
|
)
|
|
|
|
const separated = deduped.reduce<{
|
|
available: RoomConfiguration[]
|
|
notAvailable: RoomConfiguration[]
|
|
}>(
|
|
(acc, curr) => {
|
|
if (curr.status === "NotAvailable") {
|
|
return { ...acc, notAvailable: [...acc.notAvailable, curr] }
|
|
}
|
|
return { ...acc, available: [...acc.available, curr] }
|
|
},
|
|
{ available: [], notAvailable: [] }
|
|
)
|
|
|
|
return [...separated.available, ...separated.notAvailable]
|
|
}, [roomsAvailability.roomConfigurations])
|
|
|
|
const [selectedRate, setSelectedRate] = useState<RateCode | undefined>(
|
|
undefined
|
|
)
|
|
const [selectedPackages, setSelectedPackages] = useState<RoomPackageCodes[]>(
|
|
[]
|
|
)
|
|
|
|
const defaultPackages: DefaultFilterOptions[] = useMemo(
|
|
() => [
|
|
{
|
|
code: RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
|
|
description: intl.formatMessage({ id: "Accessible Room" }),
|
|
itemCode: availablePackages.find(
|
|
(pkg) => pkg.code === RoomPackageCodeEnum.ACCESSIBILITY_ROOM
|
|
)?.itemCode,
|
|
},
|
|
{
|
|
code: RoomPackageCodeEnum.ALLERGY_ROOM,
|
|
description: intl.formatMessage({ id: "Allergy Room" }),
|
|
itemCode: availablePackages.find(
|
|
(pkg) => pkg.code === RoomPackageCodeEnum.ALLERGY_ROOM
|
|
)?.itemCode,
|
|
},
|
|
{
|
|
code: RoomPackageCodeEnum.PET_ROOM,
|
|
description: intl.formatMessage({ id: "Pet Room" }),
|
|
itemCode: availablePackages.find(
|
|
(pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM
|
|
)?.itemCode,
|
|
},
|
|
],
|
|
[availablePackages, intl]
|
|
)
|
|
|
|
const handleFilter = useCallback(
|
|
(filter: Record<RoomPackageCodeEnum, boolean | undefined>) => {
|
|
const filteredPackages = Object.keys(filter).filter(
|
|
(key) => filter[key as RoomPackageCodeEnum]
|
|
) as RoomPackageCodeEnum[]
|
|
|
|
setSelectedPackages(filteredPackages)
|
|
},
|
|
[]
|
|
)
|
|
|
|
const filteredRooms = useMemo(() => {
|
|
return visibleRooms.filter((room) =>
|
|
selectedPackages.every((filteredPackage) =>
|
|
room.features.some((feature) => feature.code === filteredPackage)
|
|
)
|
|
)
|
|
}, [visibleRooms, selectedPackages])
|
|
|
|
const rooms = useMemo(() => {
|
|
if (selectedPackages.length === 0) {
|
|
return {
|
|
...roomsAvailability,
|
|
roomConfigurations: visibleRooms,
|
|
}
|
|
}
|
|
|
|
return {
|
|
...roomsAvailability,
|
|
roomConfigurations: [...filteredRooms],
|
|
}
|
|
}, [roomsAvailability, visibleRooms, selectedPackages, filteredRooms])
|
|
|
|
const rateSummary: Rate | null = useMemo(() => {
|
|
const room = filteredRooms.find(
|
|
(room) => room.roomTypeCode === selectedRate?.roomTypeCode
|
|
)
|
|
|
|
if (!room) return null
|
|
|
|
const product = room.products.find(
|
|
(product) =>
|
|
product.productType.public.rateCode === selectedRate?.publicRateCode
|
|
)
|
|
|
|
if (!product) return null
|
|
|
|
const petRoomPackage =
|
|
(selectedPackages.includes(RoomPackageCodeEnum.PET_ROOM) &&
|
|
availablePackages.find(
|
|
(pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM
|
|
)) ||
|
|
undefined
|
|
|
|
const features = filteredRooms.find((room) =>
|
|
room.features.some(
|
|
(feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
|
|
)
|
|
)?.features
|
|
|
|
const roomType = roomCategories.find((roomCategory) =>
|
|
roomCategory.roomTypes.some(
|
|
(roomType) => roomType.code === room.roomTypeCode
|
|
)
|
|
)
|
|
|
|
const rateSummary: Rate = {
|
|
features: petRoomPackage && features ? features : [],
|
|
priceName: selectedRate?.name,
|
|
priceTerm: selectedRate?.paymentTerm,
|
|
public: product.productType.public,
|
|
member: product.productType.member,
|
|
roomType: roomType?.name || room.roomType,
|
|
roomTypeCode: room.roomTypeCode,
|
|
}
|
|
|
|
return rateSummary
|
|
}, [
|
|
filteredRooms,
|
|
availablePackages,
|
|
selectedPackages,
|
|
selectedRate,
|
|
roomCategories,
|
|
])
|
|
|
|
useEffect(() => {
|
|
if (rateSummary) return
|
|
if (!selectedRate) return
|
|
|
|
setSelectedRate(undefined)
|
|
}, [rateSummary, selectedRate])
|
|
|
|
useEffect(() => {
|
|
const pricesWithCurrencies = rooms.roomConfigurations.flatMap((room) =>
|
|
room.products.map((product) => ({
|
|
price: product.productType.public.localPrice.pricePerNight,
|
|
currency: product.productType.public.localPrice.currency,
|
|
}))
|
|
)
|
|
|
|
const cheapestPrice = pricesWithCurrencies.reduce(
|
|
(minPrice, { price }) => Math.min(minPrice, price),
|
|
Infinity
|
|
)
|
|
|
|
const currency = pricesWithCurrencies.find(
|
|
({ price }) => price === cheapestPrice
|
|
)?.currency
|
|
|
|
trackLowestRoomPrice({
|
|
hotelId,
|
|
arrivalDate,
|
|
departureDate,
|
|
lowestPrice: cheapestPrice,
|
|
currency: currency,
|
|
})
|
|
}, [arrivalDate, departureDate, hotelId, rooms.roomConfigurations])
|
|
|
|
return (
|
|
<div className={styles.content}>
|
|
<RoomFilter
|
|
numberOfRooms={rooms.roomConfigurations.length}
|
|
onFilter={handleFilter}
|
|
filterOptions={defaultPackages}
|
|
/>
|
|
<RoomSelection
|
|
roomsAvailability={rooms}
|
|
roomCategories={roomCategories}
|
|
availablePackages={availablePackages}
|
|
selectedPackages={selectedPackages}
|
|
setRateCode={setSelectedRate}
|
|
rateSummary={rateSummary}
|
|
hotelType={hotelType}
|
|
isUserLoggedIn={isUserLoggedIn}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|