feat: add multiroom signup

This commit is contained in:
Simon Emanuelsson
2025-02-17 15:10:48 +01:00
parent 95917e5e4f
commit 92c5566c59
78 changed files with 2035 additions and 1545 deletions

View File

@@ -9,48 +9,33 @@ import {
getSelectedRoomAvailability,
} from "@/lib/trpc/memoizedRequests"
import BedType from "@/components/HotelReservation/EnterDetails/BedType"
import Breakfast from "@/components/HotelReservation/EnterDetails/Breakfast"
import Details from "@/components/HotelReservation/EnterDetails/Details"
import HotelHeader from "@/components/HotelReservation/EnterDetails/Header"
import Payment from "@/components/HotelReservation/EnterDetails/Payment"
import SectionAccordion from "@/components/HotelReservation/EnterDetails/SectionAccordion"
import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom"
import Multiroom from "@/components/HotelReservation/EnterDetails/Room/Multiroom"
import RoomOne from "@/components/HotelReservation/EnterDetails/Room/One"
import DesktopSummary from "@/components/HotelReservation/EnterDetails/Summary/Desktop"
import MobileSummary from "@/components/HotelReservation/EnterDetails/Summary/Mobile"
import { generateChildrenString } from "@/components/HotelReservation/utils"
import Title from "@/components/TempDesignSystem/Text/Title"
import { getIntl } from "@/i18n"
import RoomProvider from "@/providers/Details/RoomProvider"
import EnterDetailsProvider from "@/providers/EnterDetailsProvider"
import { convertSearchParamsToObj } from "@/utils/url"
import styles from "./page.module.css"
import type { BedTypeSelection } from "@/types/components/hotelReservation/enterDetails/bedType"
import type { RoomRate } from "@/types/components/hotelReservation/enterDetails/details"
import { type SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
import { StepEnum } from "@/types/enums/step"
import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { LangParams, PageArgs } from "@/types/params"
import type { Packages } from "@/types/requests/packages"
export interface RoomData {
bedTypes?: BedTypeSelection[]
mustBeGuaranteed?: boolean
breakfastIncluded?: boolean
packages: Packages | null
cancellationText: string
rateDetails: string[]
roomType: string
roomRate: RoomRate
}
import type { Room } from "@/types/providers/details/room"
export default async function DetailsPage({
params: { lang },
searchParams,
}: PageArgs<LangParams, SelectRateSearchParams>) {
const intl = await getIntl()
const selectRoomParams = new URLSearchParams(searchParams)
selectRoomParams.delete("modifyRateIndex")
const booking = convertSearchParamsToObj<SelectRateSearchParams>(searchParams)
if ("modifyRateIndex" in booking) {
delete booking.modifyRateIndex
}
void getProfileSafely()
@@ -61,7 +46,7 @@ export default async function DetailsPage({
toDate: booking.toDate,
}
const breakfastPackages = await getBreakfastPackages(breakfastInput)
const roomsData: RoomData[] = []
const rooms: Room[] = []
for (let room of booking.rooms) {
const childrenAsString =
@@ -92,21 +77,22 @@ export default async function DetailsPage({
: null
const roomAvailability = await getSelectedRoomAvailability(
selectedRoomAvailabilityInput //
selectedRoomAvailabilityInput
)
if (!roomAvailability) {
continue // TODO: handle no room availability
}
roomsData.push({
rooms.push({
bedTypes: roomAvailability.bedTypes,
packages,
mustBeGuaranteed: roomAvailability.mustBeGuaranteed,
breakfastIncluded: roomAvailability.breakfastIncluded,
cancellationText: roomAvailability.cancellationText,
mustBeGuaranteed: roomAvailability.mustBeGuaranteed,
packages,
rateDetails: roomAvailability.rateDetails ?? [],
roomType: roomAvailability.selectedRoom.roomType,
roomTypeCode: roomAvailability.selectedRoom.roomTypeCode,
roomRate: {
memberRate: roomAvailability?.memberRate,
publicRate: roomAvailability.publicRate,
@@ -114,7 +100,7 @@ export default async function DetailsPage({
})
}
const isCardOnlyPayment = roomsData.some((room) => room?.mustBeGuaranteed)
const isCardOnlyPayment = rooms.some((room) => room?.mustBeGuaranteed)
const hotelData = await getHotel({
hotelId: booking.hotelId,
isCardOnlyPayment,
@@ -123,13 +109,13 @@ export default async function DetailsPage({
const user = await getProfileSafely()
// const userTrackingData = await getUserTracking()
if (!hotelData || !roomsData) {
if (!hotelData || !rooms) {
return notFound()
}
// const arrivalDate = new Date(booking.fromDate)
// const departureDate = new Date(booking.toDate)
const hotelAttributes = hotelData.hotel
const { hotel } = hotelData
// TODO: add tracking
// const initialHotelsTrackingData: TrackingSDKHotelInfo = {
@@ -147,111 +133,49 @@ export default async function DetailsPage({
// leadTime: differenceInCalendarDays(arrivalDate, new Date()),
// searchType: "hotel",
// bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
// country: hotelAttributes?.address.country,
// hotelID: hotelAttributes?.operaId,
// region: hotelAttributes?.address.city,
// country: hotel?.address.country,
// hotelID: hotel?.operaId,
// region: hotel?.address.city,
// }
const showBreakfastStep = Boolean(
breakfastPackages?.length && !roomsData[0]?.breakfastIncluded
)
const firstRoom = rooms[0]
const multirooms = rooms.slice(1)
return (
<EnterDetailsProvider
booking={booking}
showBreakfastStep={showBreakfastStep}
roomsData={roomsData}
breakfastPackages={breakfastPackages}
rooms={rooms}
searchParamsStr={selectRoomParams.toString()}
user={user}
vat={hotelAttributes.vat}
vat={hotel.vat}
>
<main>
<HotelHeader hotelData={hotelData} />
<div className={styles.container}>
<div className={styles.content}>
{roomsData.map((room, idx) => (
<section key={idx}>
{roomsData.length > 1 && (
<header className={styles.header}>
<Title level="h2" as="h4">
{intl.formatMessage({ id: "Room" })} {idx + 1}
</Title>
</header>
)}
<SelectedRoom
hotelId={booking.hotelId}
roomType={room.roomType}
roomTypeCode={booking.rooms[idx].roomTypeCode}
rateDescription={room.cancellationText}
roomIndex={idx}
searchParamsStr={selectRoomParams.toString()}
/>
{room.bedTypes ? (
<SectionAccordion
header={intl.formatMessage({ id: "Select bed" })}
label={intl.formatMessage({ id: "Request bedtype" })}
step={StepEnum.selectBed}
roomIndex={idx}
>
<BedType bedTypes={room.bedTypes} roomIndex={idx} />
</SectionAccordion>
) : null}
{showBreakfastStep ? (
<SectionAccordion
header={intl.formatMessage({ id: "Food options" })}
label={intl.formatMessage({
id: "Select breakfast options",
})}
step={StepEnum.breakfast}
roomIndex={idx}
>
<Breakfast packages={breakfastPackages!} roomIndex={idx} />
</SectionAccordion>
) : null}
<SectionAccordion
header={intl.formatMessage({ id: "Details" })}
step={StepEnum.details}
label={intl.formatMessage({ id: "Enter your details" })}
roomIndex={idx}
>
<Details
user={idx === 0 ? user : null}
memberPrice={{
currency:
room?.roomRate.memberRate?.localPrice.currency ?? "", // TODO: how to handle undefined,
price:
room?.roomRate.memberRate?.localPrice.pricePerNight ??
0, // TODO: how to handle undefined,
}}
roomIndex={idx}
/>
</SectionAccordion>
</section>
<RoomProvider idx={0} room={firstRoom}>
<RoomOne user={user} />
</RoomProvider>
{multirooms.map((room, idx) => (
// Need to start idx from 1 since first room is
// rendered above
<RoomProvider key={idx + 1} idx={idx + 1} room={room}>
<Multiroom />
</RoomProvider>
))}
<Suspense>
<Payment
user={user}
otherPaymentOptions={
hotelAttributes.merchantInformationData
.alternatePaymentOptions
hotel.merchantInformationData.alternatePaymentOptions
}
supportedCards={hotelAttributes.merchantInformationData.cards}
supportedCards={hotel.merchantInformationData.cards}
mustBeGuaranteed={isCardOnlyPayment}
/>
</Suspense>
</div>
<aside className={styles.summary}>
<MobileSummary
isMember={!!user}
breakfastIncluded={roomsData[0]?.breakfastIncluded ?? false}
/>
<DesktopSummary
isMember={!!user}
breakfastIncluded={roomsData[0]?.breakfastIncluded ?? false}
/>
<MobileSummary isMember={!!user} />
<DesktopSummary isMember={!!user} />
</aside>
</div>
</main>