diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx index 23651a7cc..68bf17f6a 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(standard)/details/page.tsx @@ -93,6 +93,7 @@ export default async function DetailsPage({ rooms.push({ bedTypes: roomAvailability.bedTypes, breakfastIncluded: roomAvailability.breakfastIncluded, + cancellationRule: roomAvailability.cancellationRule, cancellationText: roomAvailability.cancellationText, mustBeGuaranteed: roomAvailability.mustBeGuaranteed, packages, @@ -196,7 +197,6 @@ export default async function DetailsPage({ hotel.merchantInformationData.alternatePaymentOptions } supportedCards={hotel.merchantInformationData.cards} - mustBeGuaranteed={isCardOnlyPayment} /> diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/MixedRatePaymentBreakdown/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/MixedRatePaymentBreakdown/index.tsx new file mode 100644 index 000000000..be2ba3890 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/MixedRatePaymentBreakdown/index.tsx @@ -0,0 +1,136 @@ +import React from "react" +import { useIntl } from "react-intl" + +import Body from "@/components/TempDesignSystem/Text/Body" +import Caption from "@/components/TempDesignSystem/Text/Caption" +import { formatPrice } from "@/utils/numberFormatting" + +import { + calculateTotalRoomPrice, + hasFlexibleRate, + hasPrepaidRate, +} from "../helpers" + +import styles from "./mixedRatePaymentBreakdown.module.css" + +import type { RoomState } from "@/types/stores/enter-details" + +type PaymentBreakdownState = { + roomsWithPrepaidRate: number[] + roomsWithFlexRate: number[] + payNowPrice: number + payNowComparisonPrice: number + payAtCheckInPrice: number + payAtCheckInComparisonPrice: number +} + +interface MixedRatePaymentBreakdownProps { + rooms: RoomState[] + currency: string +} + +export default function MixedRatePaymentBreakdown({ + rooms, + currency, +}: MixedRatePaymentBreakdownProps) { + const intl = useIntl() + const payNowTitle = intl.formatMessage({ id: "Pay now" }) + const payAtCheckInTitle = intl.formatMessage({ id: "Pay at check-in" }) + + const initialState: PaymentBreakdownState = { + roomsWithPrepaidRate: [], + roomsWithFlexRate: [], + payNowPrice: 0, + payNowComparisonPrice: 0, + payAtCheckInPrice: 0, + payAtCheckInComparisonPrice: 0, + } + + const { + roomsWithPrepaidRate, + roomsWithFlexRate, + payNowPrice, + payNowComparisonPrice, + payAtCheckInPrice, + payAtCheckInComparisonPrice, + } = rooms.reduce((acc, room, idx) => { + if (hasPrepaidRate(room)) { + acc.roomsWithPrepaidRate.push(idx) + const { totalPrice, comparisonPrice } = calculateTotalRoomPrice(room) + acc.payNowPrice += totalPrice + acc.payNowComparisonPrice += comparisonPrice + } + if (hasFlexibleRate(room)) { + acc.roomsWithFlexRate.push(idx) + const { totalPrice, comparisonPrice } = calculateTotalRoomPrice(room) + acc.payAtCheckInPrice += totalPrice + acc.payAtCheckInComparisonPrice += comparisonPrice + } + return acc + }, initialState) + + return ( +
+ + +
+ ) +} + +interface PaymentCardProps { + title: string + price: number + comparisonPrice: number + currency: string + roomIndexes: number[] +} + +function PaymentCard({ + title, + price, + comparisonPrice, + currency, + roomIndexes, +}: PaymentCardProps) { + const intl = useIntl() + const isMemberRateApplied = price < comparisonPrice + + return ( +
+ + {title}{" "} + + /{" "} + {intl.formatMessage( + { id: "Room {roomIndex}" }, + { + roomIndex: roomIndexes.map((idx) => idx + 1).join(" & "), + } + )} + + + + {formatPrice(intl, price, currency)} + {isMemberRateApplied && comparisonPrice ? ( + {formatPrice(intl, comparisonPrice, currency)} + ) : null} + +
+ ) +} diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/MixedRatePaymentBreakdown/mixedRatePaymentBreakdown.module.css b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/MixedRatePaymentBreakdown/mixedRatePaymentBreakdown.module.css new file mode 100644 index 000000000..94e2f12c8 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/MixedRatePaymentBreakdown/mixedRatePaymentBreakdown.module.css @@ -0,0 +1,36 @@ +.container { + display: flex; + gap: var(--Spacing-x1); +} + +.card { + display: flex; + flex-direction: column; + flex-grow: 1; + background-color: var(--Scandic-Blue-00); + padding: var(--Spacing-x-one-and-half); + border: 1px solid rgba(0, 0, 0, 0.05); + border-radius: var(--Corner-radius-Medium); +} + +.cardTitle { + text-transform: uppercase; +} + +.cardTitle > span { + color: var(--UI-Text-Placeholder); +} + +.card.inactive { + background-color: transparent; +} + +.priceItem { + display: flex; + gap: var(--Spacing-x1); +} + +.priceItem > span { + font-weight: 400; + text-decoration: line-through; +} diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx index 28318c110..4f20e7470 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx @@ -38,6 +38,8 @@ import { trackPaymentEvent } from "@/utils/tracking" import { bedTypeMap } from "../../utils" import PriceChangeDialog from "../PriceChangeDialog" import GuaranteeDetails from "./GuaranteeDetails" +import { hasFlexibleRate, hasPrepaidRate, isPaymentMethodEnum } from "./helpers" +import MixedRatePaymentBreakdown from "./MixedRatePaymentBreakdown" import PaymentOption from "./PaymentOption" import { type PaymentFormData, paymentSchema } from "./schema" @@ -51,14 +53,9 @@ const retryInterval = 2000 export const formId = "submit-booking" -function isPaymentMethodEnum(value: string): value is PaymentMethodEnum { - return Object.values(PaymentMethodEnum).includes(value as PaymentMethodEnum) -} - export default function PaymentClient({ otherPaymentOptions, savedCreditCards, - mustBeGuaranteed, }: PaymentClientProps) { const router = useRouter() const lang = useLang() @@ -90,6 +87,11 @@ export default function PaymentClient({ const { toDate, fromDate, hotelId } = booking + const mustBeGuaranteed = rooms.every((r) => r.room.mustBeGuaranteed) + const hasPrepaidRates = rooms.some(hasPrepaidRate) + const hasFlexRates = rooms.some(hasFlexibleRate) + const hasMixedRates = hasPrepaidRates && hasFlexRates + usePaymentFailedToast() const methods = useForm({ @@ -351,6 +353,15 @@ export default function PaymentClient({ ) : null} + + {hasMixedRates ? ( + + {intl.formatMessage({ + id: "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.", + })} + + ) : null} + {savedCreditCards?.length ? (
@@ -374,6 +385,7 @@ export default function PaymentClient({
) : null} +
{savedCreditCards?.length ? ( @@ -399,7 +411,14 @@ export default function PaymentClient({ /> ))} + {hasMixedRates ? ( + + ) : null}
+
{intl.formatMessage( diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/helpers.ts b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/helpers.ts new file mode 100644 index 000000000..ebc327754 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/helpers.ts @@ -0,0 +1,44 @@ +import { CancellationRuleEnum, PaymentMethodEnum } from "@/constants/booking" + +import type { RoomState } from "@/types/stores/enter-details" + +export function isPaymentMethodEnum(value: string): value is PaymentMethodEnum { + return Object.values(PaymentMethodEnum).includes(value) +} + +export function hasFlexibleRate({ room }: RoomState): boolean { + return room.cancellationRule === CancellationRuleEnum.CancellableBefore6PM +} + +export function hasPrepaidRate({ room }: RoomState): boolean { + return room.cancellationRule !== CancellationRuleEnum.CancellableBefore6PM +} + +export function calculateTotalRoomPrice({ room }: RoomState) { + let totalPrice = room.roomPrice.perStay.local.price + + if (room.breakfast) { + totalPrice += Number(room.breakfast.localPrice.totalPrice) * room.adults + } + + if (room.roomFeatures) { + room.roomFeatures.forEach((pkg) => { + totalPrice += Number(pkg.localPrice.price) + }) + } + + let comparisonPrice = totalPrice + + const isMember = room.guest.join || room.guest.membershipNo + if (isMember) { + const publicPrice = room.roomRate.publicRate?.localPrice.pricePerStay ?? 0 + const memberPrice = room.roomRate.memberRate?.localPrice.pricePerStay ?? 0 + const diff = publicPrice - memberPrice + comparisonPrice = totalPrice + diff + } + + return { + totalPrice, + comparisonPrice, + } +} diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/index.tsx index edbf4cb3e..b88cce4b5 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/index.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/index.tsx @@ -6,7 +6,6 @@ import type { PaymentProps } from "@/types/components/hotelReservation/enterDeta export default async function Payment({ otherPaymentOptions, - mustBeGuaranteed, supportedCards, }: PaymentProps) { const savedCreditCards = await getSavedPaymentCardsSafely({ @@ -17,7 +16,6 @@ export default async function Payment({ ) } diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Summary/summary.test.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Summary/summary.test.tsx index 65515f3d8..1bc22b012 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Summary/summary.test.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Summary/summary.test.tsx @@ -54,6 +54,7 @@ const rooms: RoomState[] = [ bedTypes: [], breakfast: breakfastPackage, breakfastIncluded: false, + cancellationRule: "", cancellationText: "Non-refundable", childrenInRoom: [{ bed: ChildBedMapEnum.IN_EXTRA_BED, age: 5 }], guest: guestDetailsNonMember, @@ -64,6 +65,7 @@ const rooms: RoomState[] = [ roomType: "Standard", roomTypeCode: "QS", isAvailable: true, + mustBeGuaranteed: false, }, steps: { [StepEnum.selectBed]: { @@ -92,6 +94,7 @@ const rooms: RoomState[] = [ bedTypes: [], breakfast: undefined, breakfastIncluded: false, + cancellationRule: "", cancellationText: "Non-refundable", childrenInRoom: [], guest: guestDetailsMember, @@ -102,6 +105,7 @@ const rooms: RoomState[] = [ roomType: "Standard", roomTypeCode: "QS", isAvailable: true, + mustBeGuaranteed: false, }, steps: { [StepEnum.selectBed]: { diff --git a/apps/scandic-web/i18n/dictionaries/da.json b/apps/scandic-web/i18n/dictionaries/da.json index b6cadb3dd..150c583ea 100644 --- a/apps/scandic-web/i18n/dictionaries/da.json +++ b/apps/scandic-web/i18n/dictionaries/da.json @@ -65,6 +65,7 @@ "As our {level}": "Som vores {level}", "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Da dette er et ophold med flere værelser, skal annullereringen gennemføres af personen, der har booket opholdet. Bedes du ringe til vores kundeservice på 08-517 517 00, hvis du har brug for yderligere hjælp.", "At a cost": "Mod betaling", + "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.": "Din booking inkluderer værelser med forskellige vilkår, vi vil blive opkræve en del af bookingen nu og resten vil blive indsamlet ved check-in.", "At latest": "Senest", "At the hotel": "På hotellet", "Attractions": "Attraktioner", @@ -493,6 +494,7 @@ "Parking / Garage": "Parkering / Garage", "Parking can be reserved in advance": "Parkering kan reserveres på forhånd", "Password": "Adgangskode", + "Pay at check-in": "Betal ved check-in", "Pay later": "Betal senere", "Pay now": "Betal nu", "Pay the member price of {amount} for Room {roomNr}": "Betal medlemsprisen på {amount} til værelse {roomNr}", diff --git a/apps/scandic-web/i18n/dictionaries/de.json b/apps/scandic-web/i18n/dictionaries/de.json index 3e0115325..92aeaa63f 100644 --- a/apps/scandic-web/i18n/dictionaries/de.json +++ b/apps/scandic-web/i18n/dictionaries/de.json @@ -65,6 +65,7 @@ "As our {level}": "Als unser {level}", "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Da dies ein Mehrzimmer-Aufenthalt ist, muss die Stornierung von der Person, die die Buchung getätigt hat, durchgeführt werden. Bitte rufen Sie uns unter der Telefonnummer 08-517 517 00 an, wenn Sie weitere Hilfe benötigen.", "At a cost": "Gegen Gebühr", + "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.": "Ihre Buchung enthält Zimmer mit unterschiedlichen Bedingungen, wir werden einen Teil der Buchung jetzt belasten und den Rest bei der Anreise durch die Reception erheben.", "At latest": "Spätestens", "At the hotel": "Im Hotel", "Attraction": "Attraktion", @@ -493,6 +494,7 @@ "Parking / Garage": "Parken / Garage", "Parking can be reserved in advance": "Parkplätze können im Voraus reserviert werden", "Password": "Passwort", + "Pay at check-in": "Beim Check-in bezahlen", "Pay later": "Später bezahlen", "Pay now": "Jetzt bezahlen", "Pay the member price of {amount} for Room {roomNr}": "Zahlen Sie den Mitgliedspreis von {amount} für Zimmer {roomNr}", diff --git a/apps/scandic-web/i18n/dictionaries/en.json b/apps/scandic-web/i18n/dictionaries/en.json index 820c64143..b097ec756 100644 --- a/apps/scandic-web/i18n/dictionaries/en.json +++ b/apps/scandic-web/i18n/dictionaries/en.json @@ -68,6 +68,7 @@ "As our {level}": "As our {level}", "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.", "At a cost": "At a cost", + "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.": "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.", "At latest": "At latest", "At the hotel": "At the hotel", "Attractions": "Attractions", @@ -499,6 +500,7 @@ "Parking / Garage": "Parking / Garage", "Parking can be reserved in advance": "Parking can be reserved in advance", "Password": "Password", + "Pay at check-in": "Pay at check-in", "Pay later": "Pay later", "Pay now": "Pay now", "Pay the member price of {amount} for Room {roomNr}": "Pay the member price of {amount} for Room {roomNr}", diff --git a/apps/scandic-web/i18n/dictionaries/fi.json b/apps/scandic-web/i18n/dictionaries/fi.json index 3fdc97138..a3bf96414 100644 --- a/apps/scandic-web/i18n/dictionaries/fi.json +++ b/apps/scandic-web/i18n/dictionaries/fi.json @@ -64,6 +64,7 @@ "As our {level}": "{level}-etu", "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Koska tämä on monihuoneinen majoitus, peruutus on tehtävä henkilölle, joka teki varauksen. Ota yhteyttä asiakaspalveluun apua varten, jos tarvitset lisää apua.", "At a cost": "Maksua vastaan", + "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.": "Sinun varauksessasi on huoneita eri hinnoilla, me veloitamme osan varauksesta nyt ja loput tarkistukseen tapahtuvan tarkistuksen yhteydessä.", "At latest": "Viimeistään", "At the hotel": "Hotellissa", "Attractions": "Nähtävyydet", @@ -492,6 +493,7 @@ "Parking / Garage": "Pysäköinti / Autotalli", "Parking can be reserved in advance": "Pysäköintipaikan voi varata etukäteen", "Password": "Salasana", + "Pay at check-in": "Maksa tarkistuksessa", "Pay later": "Maksa myöhemmin", "Pay now": "Maksa nyt", "Pay the member price of {amount} for Room {roomNr}": "Maksa jäsenhinta {amount} varten Huone {roomNr}", diff --git a/apps/scandic-web/i18n/dictionaries/no.json b/apps/scandic-web/i18n/dictionaries/no.json index ce137c4d1..0f6278542 100644 --- a/apps/scandic-web/i18n/dictionaries/no.json +++ b/apps/scandic-web/i18n/dictionaries/no.json @@ -64,6 +64,7 @@ "As our {level}": "Som vår {level}", "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Som dette er et ophold med flere rom, må annullereringen gjøres av personen som booket opholdet. Vennligst ring 08-517 517 00 til vår kundeservice hvis du trenger mer hjelp.", "At a cost": "Mot betaling", + "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.": "Din bestilling inkluderer rom med ulike vilkår, vi vil belaste en del av bestillingen nå og den resterende vil bli samlet inn ved check-in.", "At latest": "Senest", "At the hotel": "På hotellet", "Attractions": "Attraksjoner", @@ -491,6 +492,7 @@ "Parking / Garage": "Parkering / Garasje", "Parking can be reserved in advance": "Parkering kan reserveres på forhånd", "Password": "Passord", + "Pay at check-in": "Betal ved check-in", "Pay later": "Betal senere", "Pay now": "Betal nå", "Pay the member price of {amount} for Room {roomNr}": "Betal medlemsprisen på {amount} for rom {roomNr}", diff --git a/apps/scandic-web/i18n/dictionaries/sv.json b/apps/scandic-web/i18n/dictionaries/sv.json index e3efe16c8..6963d2738 100644 --- a/apps/scandic-web/i18n/dictionaries/sv.json +++ b/apps/scandic-web/i18n/dictionaries/sv.json @@ -64,6 +64,7 @@ "As our {level}": "Som vår {level}", "As this is a multiroom stay, the cancellation has to be done by the person who made the booking. Please call 08-517 517 00 to talk to our customer service if you would need further assistance.": "Då detta är en vistelse med flera rum måste avbokningen göras av personen som bokade vistelsen. Kontakta vår kundsupport på 08-517 517 00 om du behöver mer hjälp.", "At a cost": "Mot en kostnad", + "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.": "Din bokning innehåller rum med olika villkor, vi kommer att debitera en del av bokningen nu och resten kommer att samlas in vid check-in.", "At latest": "Senast", "At the hotel": "På hotellet", "Attractions": "Sevärdheter", @@ -491,6 +492,7 @@ "Parking / Garage": "Parkering / Garage", "Parking can be reserved in advance": "Parkering kan reserveras i förväg", "Password": "Lösenord", + "Pay at check-in": "Betala vid check-in", "Pay later": "Betala senare", "Pay now": "Betala nu", "Pay the member price of {amount} for Room {roomNr}": "Betala medlemspriset på {amount} för rum {roomNr}", diff --git a/apps/scandic-web/providers/EnterDetailsProvider.tsx b/apps/scandic-web/providers/EnterDetailsProvider.tsx index 958201ca4..5de251389 100644 --- a/apps/scandic-web/providers/EnterDetailsProvider.tsx +++ b/apps/scandic-web/providers/EnterDetailsProvider.tsx @@ -39,6 +39,7 @@ export default function EnterDetailsProvider({ .map((room) => ({ isAvailable: room.isAvailable, breakfastIncluded: !!room.breakfastIncluded, + cancellationRule: room.cancellationRule, cancellationText: room.cancellationText, rateDetails: room.rateDetails, rateTitle: room.rateTitle, @@ -54,6 +55,7 @@ export default function EnterDetailsProvider({ description: room.bedTypes[0].description, } : undefined, + mustBeGuaranteed: room.mustBeGuaranteed, })), vat, } diff --git a/apps/scandic-web/server/routers/hotels/query.ts b/apps/scandic-web/server/routers/hotels/query.ts index feed76797..f967edb35 100644 --- a/apps/scandic-web/server/routers/hotels/query.ts +++ b/apps/scandic-web/server/routers/hotels/query.ts @@ -788,6 +788,7 @@ export const hotelQueryRouter = router({ return { selectedRoom, rateDetails: rateDefinition?.generalTerms, + cancellationRule: rateDefinition?.cancellationRule, cancellationText: rateDefinition?.cancellationText ?? "", mustBeGuaranteed: !!rateDefinition?.mustBeGuaranteed, breakfastIncluded: !!rateDefinition?.breakfastIncluded, diff --git a/apps/scandic-web/types/components/hotelReservation/enterDetails/payment.ts b/apps/scandic-web/types/components/hotelReservation/enterDetails/payment.ts index 4ed28ea7b..f60db741b 100644 --- a/apps/scandic-web/types/components/hotelReservation/enterDetails/payment.ts +++ b/apps/scandic-web/types/components/hotelReservation/enterDetails/payment.ts @@ -3,7 +3,6 @@ import type { PaymentMethodEnum } from "@/constants/booking" export interface PaymentProps { otherPaymentOptions: PaymentMethodEnum[] - mustBeGuaranteed: boolean supportedCards: PaymentMethodEnum[] } diff --git a/apps/scandic-web/types/providers/details/room.ts b/apps/scandic-web/types/providers/details/room.ts index 3af385d00..6112c47f4 100644 --- a/apps/scandic-web/types/providers/details/room.ts +++ b/apps/scandic-web/types/providers/details/room.ts @@ -5,8 +5,9 @@ import type { Packages } from "@/types/requests/packages" export interface Room { bedTypes?: BedTypeSelection[] breakfastIncluded?: boolean + cancellationRule?: string cancellationText: string - mustBeGuaranteed?: boolean + mustBeGuaranteed: boolean packages: Packages | null rateDetails: string[] rateTitle?: string diff --git a/apps/scandic-web/types/stores/enter-details.ts b/apps/scandic-web/types/stores/enter-details.ts index c77e19042..55b101e60 100644 --- a/apps/scandic-web/types/stores/enter-details.ts +++ b/apps/scandic-web/types/stores/enter-details.ts @@ -27,12 +27,14 @@ export interface InitialRoomData { bedTypes: BedTypeSelection[] breakfastIncluded: boolean cancellationText: string + cancellationRule?: string rateDetails: string[] | undefined rateTitle?: string roomFeatures: Packages | null roomRate: RoomRate roomType: string roomTypeCode: string + mustBeGuaranteed: boolean } export type RoomStep = {