feat(SW-718) Refactor select rate to support multiroom

This commit is contained in:
Pontus Dreij
2025-01-21 10:31:15 +01:00
parent 7d716dcf4a
commit edcf146ce1
27 changed files with 202 additions and 131 deletions

View File

@@ -1,6 +1,6 @@
import SkeletonShimmer from "@/components/SkeletonShimmer"
import { RoomCardSkeleton } from "../../SelectRate/RoomSelection/RoomCard/RoomCardSkeleton"
import { RoomCardSkeleton } from "../../SelectRate/RoomList/RoomCard/RoomCardSkeleton"
import styles from "./SelectHotelMapContainerSkeleton.module.css"

View File

@@ -8,7 +8,7 @@ import { selectHotel } from "@/constants/routes/hotelReservation"
import { useHotelFilterStore } from "@/stores/hotel-filters"
import { useHotelsMapStore } from "@/stores/hotels-map"
import { RoomCardSkeleton } from "@/components/HotelReservation/SelectRate/RoomSelection/RoomCard/RoomCardSkeleton"
import { RoomCardSkeleton } from "@/components/HotelReservation/SelectRate/RoomList/RoomCard/RoomCardSkeleton"
import { CloseIcon, CloseLargeIcon } from "@/components/Icons"
import InteractiveMap from "@/components/Maps/InteractiveMap"
import { BackToTopButton } from "@/components/TempDesignSystem/BackToTopButton"

View File

@@ -4,7 +4,6 @@ import { createElement, useCallback } from "react"
import { useIntl } from "react-intl"
import ToggleSidePeek from "@/components/HotelReservation/EnterDetails/SelectedRoom/ToggleSidePeek"
import FlexibilityOption from "@/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption"
import { getIconForFeatureCode } from "@/components/HotelReservation/utils"
import { ErrorCircleIcon } from "@/components/Icons"
import ImageGallery from "@/components/ImageGallery"
@@ -12,6 +11,7 @@ import Caption from "@/components/TempDesignSystem/Text/Caption"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import FlexibilityOption from "../FlexibilityOption"
import { cardVariants } from "./cardVariants"
import styles from "./roomCard.module.css"

View File

@@ -0,0 +1,38 @@
"use client"
import RoomCard from "./RoomCard"
import styles from "./roomSelection.module.css"
import type { RoomListProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
export default function RoomList({
roomsAvailability,
roomCategories,
availablePackages,
selectedPackages,
setRateCode,
hotelType,
}: RoomListProps) {
const { roomConfigurations, rateDefinitions } = roomsAvailability
return (
<div className={styles.wrapper}>
<ul className={styles.roomList}>
{roomConfigurations.map((roomConfiguration, index) => (
<RoomCard
hotelId={roomsAvailability.hotelId.toString()}
hotelType={hotelType}
rateDefinitions={rateDefinitions}
roomConfiguration={roomConfiguration}
roomCategories={roomCategories}
handleSelectRate={setRateCode}
selectedPackages={selectedPackages}
packages={availablePackages}
key={roomConfiguration.roomTypeCode}
/>
))}
</ul>
</div>
)
}

View File

@@ -1,92 +0,0 @@
"use client"
import { usePathname, useRouter, useSearchParams } from "next/navigation"
import { useMemo } from "react"
import { convertObjToSearchParams } from "@/utils/url"
import RateSummary from "./RateSummary"
import RoomCard from "./RoomCard"
import styles from "./roomSelection.module.css"
import type { RoomSelectionProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
export default function RoomSelection({
roomsAvailability,
roomCategories,
availablePackages,
selectedPackages,
isUserLoggedIn,
setRateCode,
rateSummary,
hotelType,
}: RoomSelectionProps) {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
const { roomConfigurations, rateDefinitions } = roomsAvailability
const queryParams = useMemo(() => {
// TODO: handle multiple rooms
const newSearchParams = convertObjToSearchParams(
{
rooms: [
{
roomTypeCode: rateSummary?.roomTypeCode,
rateCode: rateSummary?.public.rateCode,
counterRateCode: rateSummary?.member?.rateCode,
packages: selectedPackages,
},
],
},
searchParams
)
return newSearchParams
}, [searchParams, rateSummary, selectedPackages])
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
window.history.replaceState(
null,
"",
`${pathname}?${queryParams.toString()}`
)
router.push(`select-bed?${queryParams}`)
}
return (
<div className={styles.wrapper}>
<form
method="GET"
action={`select-bed?${searchParams}`}
onSubmit={handleSubmit}
>
<ul className={styles.roomList}>
{roomConfigurations.map((roomConfiguration, index) => (
<RoomCard
hotelId={roomsAvailability.hotelId.toString()}
hotelType={hotelType}
rateDefinitions={rateDefinitions}
roomConfiguration={roomConfiguration}
roomCategories={roomCategories}
handleSelectRate={setRateCode}
selectedPackages={selectedPackages}
packages={availablePackages}
key={roomConfiguration.roomTypeCode}
/>
))}
</ul>
{rateSummary && (
<RateSummary
rateSummary={rateSummary}
isUserLoggedIn={isUserLoggedIn}
packages={availablePackages}
roomsAvailability={roomsAvailability}
/>
)}
</form>
</div>
)
}

View File

@@ -0,0 +1,33 @@
import RoomFilter from "../RoomFilter"
import RoomList from "../RoomList"
import type { RoomSelectionPanelProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
export function RoomSelectionPanel({
rooms,
roomCategories,
availablePackages,
selectedPackages,
setSelectedRate,
hotelType,
handleFilter,
defaultPackages,
}: RoomSelectionPanelProps) {
return (
<>
<RoomFilter
numberOfRooms={rooms.roomConfigurations.length}
onFilter={handleFilter}
filterOptions={defaultPackages}
/>
<RoomList
roomsAvailability={rooms}
roomCategories={roomCategories}
availablePackages={availablePackages}
selectedPackages={selectedPackages}
setRateCode={setSelectedRate}
hotelType={hotelType}
/>
</>
)
}

View File

@@ -1,4 +1,4 @@
import { RoomCardSkeleton } from "../RoomSelection/RoomCard/RoomCardSkeleton"
import { RoomCardSkeleton } from "../RoomList/RoomCard/RoomCardSkeleton"
import styles from "./RoomsContainerSkeleton.module.css"

View File

@@ -1,14 +1,15 @@
"use client"
import { useSearchParams } from "next/navigation"
import { usePathname, useRouter, useSearchParams } from "next/navigation"
import { useCallback, useEffect, useMemo, useState } from "react"
import { useIntl } from "react-intl"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { trackLowestRoomPrice } from "@/utils/tracking"
import { convertObjToSearchParams } from "@/utils/url"
import RoomFilter from "../RoomFilter"
import RoomSelection from "../RoomSelection"
import RateSummary from "../RateSummary"
import { RoomSelectionPanel } from "../RoomSelectionPanel"
import { filterDuplicateRoomTypesByLowestPrice, parseRoomParams } from "./utils"
import styles from "./rooms.module.css"
@@ -32,6 +33,8 @@ export default function Rooms({
hotelType,
isUserLoggedIn,
}: SelectRateProps) {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
const hotelId = searchParams.get("hotel")
@@ -43,6 +46,8 @@ export default function Rooms({
[searchParams]
)
const isMultipleRooms = searchedRoomsAndGuests.length > 1
const intl = useIntl()
const visibleRooms: RoomConfiguration[] = useMemo(() => {
@@ -218,33 +223,94 @@ export default function Rooms({
})
}, [arrivalDate, departureDate, hotelId, rooms.roomConfigurations])
const queryParams = useMemo(() => {
// TODO: handle multiple rooms
const newSearchParams = convertObjToSearchParams(
{
rooms: [
{
roomTypeCode: rateSummary?.roomTypeCode,
rateCode: rateSummary?.public.rateCode,
counterRateCode: rateSummary?.member?.rateCode,
packages: selectedPackages,
},
],
},
searchParams
)
return newSearchParams
}, [searchParams, rateSummary, selectedPackages])
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
window.history.replaceState(
null,
"",
`${pathname}?${queryParams.toString()}`
)
router.push(`select-bed?${queryParams}`)
}
return (
<>
{searchedRoomsAndGuests.map((room, index) => (
<div key={index} className={styles.content}>
<Subtitle>
{`Room ${index + 1}, ${room.adults} adults`}
{room.children &&
room.children.length > 0 &&
`, ${room.children.length} children`}
</Subtitle>
<RoomFilter
numberOfRooms={rooms.roomConfigurations.length}
onFilter={handleFilter}
filterOptions={defaultPackages}
/>
<RoomSelection
roomsAvailability={rooms}
roomCategories={roomCategories}
availablePackages={availablePackages}
selectedPackages={selectedPackages}
setRateCode={setSelectedRate}
<div className={styles.content}>
{isMultipleRooms ? (
searchedRoomsAndGuests.map((room, index) => (
<div key={index}>
<Subtitle>
{intl.formatMessage(
{
id: room.children?.length
? "Room {roomIndex}, {adults} adults, {children} children"
: "Room {roomIndex}, {adults} adults",
},
{
roomIndex: index + 1,
adults: room.adults,
children: room.children?.length,
}
)}
</Subtitle>
<RoomSelectionPanel
rooms={rooms}
roomCategories={roomCategories}
availablePackages={availablePackages}
selectedPackages={selectedPackages}
setSelectedRate={setSelectedRate}
hotelType={hotelType}
handleFilter={handleFilter}
defaultPackages={defaultPackages}
/>
</div>
))
) : (
<RoomSelectionPanel
rooms={rooms}
roomCategories={roomCategories}
availablePackages={availablePackages}
selectedPackages={selectedPackages}
setSelectedRate={setSelectedRate}
hotelType={hotelType}
handleFilter={handleFilter}
defaultPackages={defaultPackages}
/>
)}
{rateSummary && (
<form
method="GET"
action={`select-bed?${searchParams}`}
onSubmit={handleSubmit}
>
<RateSummary
rateSummary={rateSummary}
hotelType={hotelType}
isUserLoggedIn={isUserLoggedIn}
packages={availablePackages}
roomsAvailability={roomsAvailability}
/>
</div>
))}
</>
</form>
)}
</div>
)
}

View File

@@ -381,6 +381,8 @@
"Room & Terms": "Værelse & Vilkår",
"Room charge": "Værelsesafgift",
"Room facilities": "Værelsesfaciliteter",
"Room {roomIndex}, {adults} adults": "Værelse {roomIndex}, {adults, plural, one {# voksen} other {# voksne}}",
"Room {roomIndex}, {adults} adults, {children} children": "Værelse {roomIndex}, {adults, plural, one {# voksen} other {# voksne}}, {children, plural, one {# barn} other {# børn}}",
"Rooms": "Værelser",
"Rooms & Guests": "Værelser & gæster",
"Sat-Sun Always open": "Lør-Søn Altid åben",

View File

@@ -376,10 +376,11 @@
"Restaurant & Bar": "Restaurant & Bar",
"Restaurants & Bars": "Restaurants & Bars",
"Retype new password": "Neues Passwort erneut eingeben",
"Room": "Zimmer",
"Room & Terms": "Zimmer & Bedingungen",
"Room charge": "Zimmerpreis",
"Room facilities": "Zimmerausstattung",
"Room {roomIndex}, {adults} adults": "Zimmer {roomIndex}, {adults, plural, one {# Erwachsene} other {# Erwachsene}}",
"Room {roomIndex}, {adults} adults, {children} children": "Zimmer {roomIndex}, {adults, plural, one {# Erwachsene} other {# Erwachsene}}, {children, plural, one {# Kind} other {# Kinder}}",
"Rooms": "Räume",
"Rooms & Guests": "Zimmer & Gäste",
"Sat-Sun Always open": "Sa-So Immer geöffnet",

View File

@@ -417,10 +417,11 @@
"Restaurant & Bar": "Restaurant & Bar",
"Restaurants & Bars": "Restaurants & Bars",
"Retype new password": "Retype new password",
"Room": "Room",
"Room & Terms": "Room & Terms",
"Room charge": "Room charge",
"Room facilities": "Room facilities",
"Room {roomIndex}, {adults} adults": "Room {roomIndex}, {adults, plural, one {# adult} other {# adults}}",
"Room {roomIndex}, {adults} adults, {children} children": "Room {roomIndex}, {adults, plural, one {# adult} other {# adults}}, {children, plural, one {# child} other {# children}}",
"Rooms": "Rooms",
"Rooms & Guests": "Rooms & Guests",
"Sat-Sun Always open": "Sat-Sun Always open",

View File

@@ -381,6 +381,8 @@
"Room & Terms": "Huone & Ehdot",
"Room charge": "Huonemaksu",
"Room facilities": "Huoneen varustelu",
"Room {roomIndex}, {adults} adults": "Huone {roomIndex}, {adults, plural, one {# vieras} other {# vieraita}}",
"Room {roomIndex}, {adults} adults, {children} children": "Huone {roomIndex}, {adults, plural, one {# vieras} other {# vieraita}}, {children, plural, one {# lapsi} other {# lapsia}}",
"Rooms": "Huoneet",
"Rooms & Guests": "Huoneet & Vieraat",
"Rooms & Guestss": "Huoneet & Vieraat",

View File

@@ -380,6 +380,8 @@
"Room & Terms": "Rom & Vilkår",
"Room charge": "Pris for rom",
"Room facilities": "Romfasiliteter",
"Room {roomIndex}, {adults} adults": "Rom {roomIndex}, {adults, plural, one {# voksen} other {# voksne}}",
"Room {roomIndex}, {adults} adults, {children} children": "Rom {roomIndex}, {adults, plural, one {# voksen} other {# voksne}}, {children, plural, one {# barn} other {# børn}}",
"Rooms": "Rom",
"Rooms & Guests": "Rom og gjester",
"Sat-Sun Always open": "Lør-Søn Alltid åpen",

View File

@@ -380,6 +380,8 @@
"Room & Terms": "Rum & Villkor",
"Room charge": "Rumspris",
"Room facilities": "Rumfaciliteter",
"Room {roomIndex}, {adults} adults": "Rum {roomIndex}, {adults, plural, one {# vuxen} other {# vuxna}}",
"Room {roomIndex}, {adults} adults, {children} children": "Rum {roomIndex}, {adults, plural, one {# vuxen} other {# vuxna}}, {children, plural, one {# barn} other {# barn}}",
"Rooms": "Rum",
"Rooms & Guests": "Rum och gäster",
"Sat-Sun Always open": "Lör-Sön Alltid öppet",

View File

@@ -1,18 +1,21 @@
import type { RoomData } from "@/types/hotel"
import type { SafeUser } from "@/types/user"
import type { RoomsAvailability } from "@/server/routers/hotels/output"
import type { RoomPackageCodes, RoomPackageData } from "./roomFilter"
import type { Rate, RateCode } from "./selectRate"
import type {
DefaultFilterOptions,
RoomPackage,
RoomPackageCodeEnum,
RoomPackageCodes,
RoomPackageData,
} from "./roomFilter"
import type { RateCode } from "./selectRate"
export interface RoomSelectionProps {
export interface RoomListProps {
roomsAvailability: RoomsAvailability
roomCategories: RoomData[]
availablePackages: RoomPackageData | undefined
selectedPackages: RoomPackageCodes[]
setRateCode: React.Dispatch<React.SetStateAction<RateCode | undefined>>
rateSummary: Rate | null
hotelType: string | undefined
isUserLoggedIn: boolean
}
export interface SelectRateProps {
@@ -22,3 +25,16 @@ export interface SelectRateProps {
hotelType: string | undefined
isUserLoggedIn: boolean
}
export interface RoomSelectionPanelProps {
rooms: RoomsAvailability
roomCategories: RoomData[]
availablePackages: RoomPackage[]
selectedPackages: RoomPackageCodes[]
setSelectedRate: React.Dispatch<React.SetStateAction<RateCode | undefined>>
hotelType: string | undefined
handleFilter: (
filter: Record<RoomPackageCodeEnum, boolean | undefined>
) => void
defaultPackages: DefaultFilterOptions[]
}