fix: make sure calculations in booking flow are correct
This commit is contained in:
committed by
Michael Zetterberg
parent
3e0f503314
commit
a222ecfc5c
@@ -0,0 +1,3 @@
|
|||||||
|
export default function ConfirmedBookingSlot() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -75,6 +75,9 @@ export default function PriceDetailsModal() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkInDate = dt(fromDate).format("YYYY-MM-DD")
|
||||||
|
const checkOutDate = dt(toDate).format("YYYY-MM-DD")
|
||||||
|
|
||||||
const bookingTotal = rooms.reduce(
|
const bookingTotal = rooms.reduce(
|
||||||
(acc, room) => {
|
(acc, room) => {
|
||||||
if (room) {
|
if (room) {
|
||||||
@@ -89,7 +92,7 @@ export default function PriceDetailsModal() {
|
|||||||
{ price: 0, priceExVat: 0, vatAmount: 0 }
|
{ price: 0, priceExVat: 0, vatAmount: 0 }
|
||||||
)
|
)
|
||||||
|
|
||||||
const diff = dt(toDate).diff(fromDate, "days")
|
const diff = dt(checkOutDate).diff(checkInDate, "days")
|
||||||
const nights = intl.formatMessage(
|
const nights = intl.formatMessage(
|
||||||
{ id: "{totalNights, plural, one {# night} other {# nights}}" },
|
{ id: "{totalNights, plural, one {# night} other {# nights}}" },
|
||||||
{ totalNights: diff }
|
{ totalNights: diff }
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ export default function ReceiptRoom({
|
|||||||
</Body>
|
</Body>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
{room.breakfast || room.breakfastIncluded ? (
|
||||||
<div className={styles.entry}>
|
<div className={styles.entry}>
|
||||||
<Body>{intl.formatMessage({ id: "Breakfast buffet" })}</Body>
|
<Body>{intl.formatMessage({ id: "Breakfast buffet" })}</Body>
|
||||||
{(room.rateDefinition.breakfastIncluded ?? room.breakfastIncluded) ? (
|
{(room.rateDefinition.breakfastIncluded ?? room.breakfastIncluded) ? (
|
||||||
@@ -172,12 +173,13 @@ export default function ReceiptRoom({
|
|||||||
<Body color="uiTextHighContrast">
|
<Body color="uiTextHighContrast">
|
||||||
{formatPrice(
|
{formatPrice(
|
||||||
intl,
|
intl,
|
||||||
room.breakfast.totalPrice * room.adults,
|
room.breakfast.totalPrice,
|
||||||
room.breakfast.currency
|
room.breakfast.currency
|
||||||
)}
|
)}
|
||||||
</Body>
|
</Body>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
) : null}
|
||||||
</article>
|
</article>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
type TrackingSDKPaymentInfo,
|
type TrackingSDKPaymentInfo,
|
||||||
} from "@/types/components/tracking"
|
} from "@/types/components/tracking"
|
||||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||||
|
import { RateEnum } from "@/types/enums/rate"
|
||||||
import type { Room } from "@/types/stores/booking-confirmation"
|
import type { Room } from "@/types/stores/booking-confirmation"
|
||||||
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
||||||
import type { RateDefinition } from "@/types/trpc/routers/hotel/roomAvailability"
|
import type { RateDefinition } from "@/types/trpc/routers/hotel/roomAvailability"
|
||||||
@@ -19,11 +20,11 @@ import type { Lang } from "@/constants/languages"
|
|||||||
function getRate(cancellationRule: RateDefinition["cancellationRule"] | null) {
|
function getRate(cancellationRule: RateDefinition["cancellationRule"] | null) {
|
||||||
switch (cancellationRule) {
|
switch (cancellationRule) {
|
||||||
case "CancellableBefore6PM":
|
case "CancellableBefore6PM":
|
||||||
return "flex"
|
return RateEnum.flex
|
||||||
case "Changeable":
|
case "Changeable":
|
||||||
return "change"
|
return RateEnum.change
|
||||||
case "NotCancellable":
|
case "NotCancellable":
|
||||||
return "save"
|
return RateEnum.save
|
||||||
default:
|
default:
|
||||||
return "-"
|
return "-"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,13 +42,11 @@ export default function Breakfast() {
|
|||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const methods = useForm<BreakfastFormSchema>({
|
const methods = useForm<BreakfastFormSchema>({
|
||||||
defaultValues: breakfastSelection
|
|
||||||
? { breakfast: breakfastSelection }
|
|
||||||
: undefined,
|
|
||||||
criteriaMode: "all",
|
criteriaMode: "all",
|
||||||
mode: "all",
|
mode: "all",
|
||||||
resolver: zodResolver(breakfastFormSchema),
|
resolver: zodResolver(breakfastFormSchema),
|
||||||
reValidateMode: "onChange",
|
reValidateMode: "onChange",
|
||||||
|
values: breakfastSelection ? { breakfast: breakfastSelection } : undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ import styles from "./ui.module.css"
|
|||||||
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
|
||||||
import type { RoomRate } from "@/types/components/hotelReservation/enterDetails/details"
|
import type { RoomRate } from "@/types/components/hotelReservation/enterDetails/details"
|
||||||
import type { EnterDetailsSummaryProps } from "@/types/components/hotelReservation/summary"
|
import type { EnterDetailsSummaryProps } from "@/types/components/hotelReservation/summary"
|
||||||
|
import { CurrencyEnum } from "@/types/enums/currency"
|
||||||
|
|
||||||
|
const notDisplayableCurrencies = [
|
||||||
|
CurrencyEnum.CC,
|
||||||
|
CurrencyEnum.POINTS,
|
||||||
|
CurrencyEnum.Voucher,
|
||||||
|
CurrencyEnum.Unknown,
|
||||||
|
]
|
||||||
|
|
||||||
export default function SummaryUI({
|
export default function SummaryUI({
|
||||||
booking,
|
booking,
|
||||||
@@ -81,6 +89,10 @@ export default function SummaryUI({
|
|||||||
"redemption" in roomOneRoomRate ||
|
"redemption" in roomOneRoomRate ||
|
||||||
"voucher" in roomOneRoomRate
|
"voucher" in roomOneRoomRate
|
||||||
|
|
||||||
|
const isSameCurrency = totalPrice.requested
|
||||||
|
? totalPrice.requested.currency === totalPrice.local.currency
|
||||||
|
: false
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.summary}>
|
<section className={styles.summary}>
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
@@ -160,6 +172,10 @@ export default function SummaryUI({
|
|||||||
guestsParts.push(childrenMsg)
|
guestsParts.push(childrenMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hideBedCurrency = notDisplayableCurrencies.includes(
|
||||||
|
room.roomPrice.perStay.local.currency
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment key={idx}>
|
<Fragment key={idx}>
|
||||||
<div
|
<div
|
||||||
@@ -262,7 +278,9 @@ export default function SummaryUI({
|
|||||||
{formatPrice(
|
{formatPrice(
|
||||||
intl,
|
intl,
|
||||||
0,
|
0,
|
||||||
room.roomPrice.perStay.local.currency
|
hideBedCurrency
|
||||||
|
? ""
|
||||||
|
: room.roomPrice.perStay.local.currency
|
||||||
)}
|
)}
|
||||||
</Body>
|
</Body>
|
||||||
</div>
|
</div>
|
||||||
@@ -418,7 +436,7 @@ export default function SummaryUI({
|
|||||||
)}
|
)}
|
||||||
</Caption>
|
</Caption>
|
||||||
) : null}
|
) : null}
|
||||||
{totalPrice.requested && !isSpecialRate && (
|
{totalPrice.requested && !isSpecialRate && !isSameCurrency && (
|
||||||
<Caption color="uiTextMediumContrast">
|
<Caption color="uiTextMediumContrast">
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
{ id: "Approx. {value}" },
|
{ id: "Approx. {value}" },
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import styles from "./rateSummary.module.css"
|
|||||||
import type { Price } from "@/types/components/hotelReservation/price"
|
import type { Price } from "@/types/components/hotelReservation/price"
|
||||||
import type { RateSummaryProps } from "@/types/components/hotelReservation/selectRate/rateSummary"
|
import type { RateSummaryProps } from "@/types/components/hotelReservation/selectRate/rateSummary"
|
||||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||||
import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate"
|
import { RateEnum } from "@/types/enums/rate"
|
||||||
import { RateTypeEnum } from "@/types/enums/rateType"
|
import { RateTypeEnum } from "@/types/enums/rateType"
|
||||||
|
|
||||||
export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
|
export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
|
||||||
@@ -111,13 +111,13 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
|
|||||||
const payLater = intl.formatMessage({ id: "Pay later" })
|
const payLater = intl.formatMessage({ id: "Pay later" })
|
||||||
const payNow = intl.formatMessage({ id: "Pay now" })
|
const payNow = intl.formatMessage({ id: "Pay now" })
|
||||||
|
|
||||||
function getRateDetails(rate: Rate["rate"]) {
|
function getRateDetails(rate: RateEnum) {
|
||||||
switch (rate) {
|
switch (rate) {
|
||||||
case "change":
|
case RateEnum.change:
|
||||||
return `${freeBooking}, ${payNow}`
|
return `${freeBooking}, ${payNow}`
|
||||||
case "flex":
|
case RateEnum.flex:
|
||||||
return `${freeCancelation}, ${payLater}`
|
return `${freeCancelation}, ${payLater}`
|
||||||
case "save":
|
case RateEnum.save:
|
||||||
default:
|
default:
|
||||||
return `${nonRefundable}, ${payNow}`
|
return `${nonRefundable}, ${payNow}`
|
||||||
}
|
}
|
||||||
@@ -243,19 +243,29 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
|
|||||||
total,
|
total,
|
||||||
{ features, packages: roomPackages, product }
|
{ features, packages: roomPackages, product }
|
||||||
) => {
|
) => {
|
||||||
if (!("member" in product) || !product.member) {
|
const memberExists =
|
||||||
|
"member" in product && product.member
|
||||||
|
const publicExists =
|
||||||
|
"public" in product && product.public
|
||||||
|
if (!memberExists) {
|
||||||
|
if (!publicExists) {
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
const memberPrice =
|
}
|
||||||
product.member.localPrice.pricePerStay
|
|
||||||
if (!memberPrice) {
|
const price =
|
||||||
|
product.member?.localPrice.pricePerStay ||
|
||||||
|
product.public?.localPrice.pricePerStay
|
||||||
|
|
||||||
|
if (!price) {
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasSelectedPetRoom = roomPackages.includes(
|
const hasSelectedPetRoom = roomPackages.includes(
|
||||||
RoomPackageCodeEnum.PET_ROOM
|
RoomPackageCodeEnum.PET_ROOM
|
||||||
)
|
)
|
||||||
if (!hasSelectedPetRoom) {
|
if (!hasSelectedPetRoom) {
|
||||||
return total + memberPrice
|
return total + price
|
||||||
}
|
}
|
||||||
const isPetRoom = features.find(
|
const isPetRoom = features.find(
|
||||||
(feature) =>
|
(feature) =>
|
||||||
@@ -265,7 +275,7 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
|
|||||||
isPetRoom && petRoomPackage
|
isPetRoom && petRoomPackage
|
||||||
? Number(petRoomPackage.localPrice.totalPrice)
|
? Number(petRoomPackage.localPrice.totalPrice)
|
||||||
: 0
|
: 0
|
||||||
return total + memberPrice + petRoomPrice
|
return total + price + petRoomPrice
|
||||||
},
|
},
|
||||||
0
|
0
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -34,24 +34,32 @@ export function calculateTotalPrice(
|
|||||||
const isPetRoom = room.features.find(
|
const isPetRoom = room.features.find(
|
||||||
(feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
|
(feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
|
||||||
)
|
)
|
||||||
let petRoomPrice = 0
|
let petRoomPriceLocal = 0
|
||||||
if (
|
if (
|
||||||
petRoomPackage &&
|
petRoomPackage &&
|
||||||
isPetRoom &&
|
isPetRoom &&
|
||||||
room.packages.includes(RoomPackageCodeEnum.PET_ROOM)
|
room.packages.includes(RoomPackageCodeEnum.PET_ROOM)
|
||||||
) {
|
) {
|
||||||
petRoomPrice = Number(petRoomPackage.localPrice.totalPrice)
|
petRoomPriceLocal = Number(petRoomPackage.localPrice.totalPrice)
|
||||||
|
}
|
||||||
|
let petRoomPriceRequested = 0
|
||||||
|
if (
|
||||||
|
petRoomPackage &&
|
||||||
|
isPetRoom &&
|
||||||
|
room.packages.includes(RoomPackageCodeEnum.PET_ROOM)
|
||||||
|
) {
|
||||||
|
petRoomPriceRequested = Number(petRoomPackage.requestedPrice.totalPrice)
|
||||||
}
|
}
|
||||||
|
|
||||||
total.local.currency = rate.localPrice.currency
|
total.local.currency = rate.localPrice.currency
|
||||||
total.local.price =
|
total.local.price =
|
||||||
total.local.price + rate.localPrice.pricePerStay + petRoomPrice
|
total.local.price + rate.localPrice.pricePerStay + petRoomPriceLocal
|
||||||
|
|
||||||
if (rate.localPrice.regularPricePerStay) {
|
if (rate.localPrice.regularPricePerStay) {
|
||||||
total.local.regularPrice =
|
total.local.regularPrice =
|
||||||
(total.local.regularPrice || 0) +
|
(total.local.regularPrice || 0) +
|
||||||
rate.localPrice.regularPricePerStay +
|
rate.localPrice.regularPricePerStay +
|
||||||
petRoomPrice
|
petRoomPriceLocal
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rate.requestedPrice) {
|
if (rate.requestedPrice) {
|
||||||
@@ -69,13 +77,13 @@ export function calculateTotalPrice(
|
|||||||
total.requested.price =
|
total.requested.price =
|
||||||
total.requested.price +
|
total.requested.price +
|
||||||
rate.requestedPrice.pricePerStay +
|
rate.requestedPrice.pricePerStay +
|
||||||
petRoomPrice
|
petRoomPriceRequested
|
||||||
|
|
||||||
if (rate.requestedPrice.regularPricePerStay) {
|
if (rate.requestedPrice.regularPricePerStay) {
|
||||||
total.requested.regularPrice =
|
total.requested.regularPrice =
|
||||||
(total.requested.regularPrice || 0) +
|
(total.requested.regularPrice || 0) +
|
||||||
rate.requestedPrice.regularPricePerStay +
|
rate.requestedPrice.regularPricePerStay +
|
||||||
petRoomPrice
|
petRoomPriceRequested
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import { useRoomContext } from "@/contexts/SelectRate/Room"
|
|||||||
|
|
||||||
import styles from "./selectedRoomPanel.module.css"
|
import styles from "./selectedRoomPanel.module.css"
|
||||||
|
|
||||||
import type { Rate } from "@/types/components/hotelReservation/selectRate/selectRate"
|
|
||||||
import { CurrencyEnum } from "@/types/enums/currency"
|
import { CurrencyEnum } from "@/types/enums/currency"
|
||||||
|
import { RateEnum } from "@/types/enums/rate"
|
||||||
|
|
||||||
export default function SelectedRoomPanel() {
|
export default function SelectedRoomPanel() {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
@@ -43,13 +43,13 @@ export default function SelectedRoomPanel() {
|
|||||||
const payLater = intl.formatMessage({ id: "Pay later" })
|
const payLater = intl.formatMessage({ id: "Pay later" })
|
||||||
const payNow = intl.formatMessage({ id: "Pay now" })
|
const payNow = intl.formatMessage({ id: "Pay now" })
|
||||||
|
|
||||||
function getRateTitle(rate: Rate["rate"]) {
|
function getRateTitle(rate: RateEnum) {
|
||||||
switch (rate) {
|
switch (rate) {
|
||||||
case "change":
|
case RateEnum.change:
|
||||||
return `${freeBooking}, ${payNow}`
|
return `${freeBooking}, ${payNow}`
|
||||||
case "flex":
|
case RateEnum.flex:
|
||||||
return `${freeCancelation}, ${payLater}`
|
return `${freeCancelation}, ${payLater}`
|
||||||
case "save":
|
case RateEnum.save:
|
||||||
default:
|
default:
|
||||||
return `${nonRefundable}, ${payNow}`
|
return `${nonRefundable}, ${payNow}`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,5 +27,5 @@ export function getBreakfastMessage(
|
|||||||
return msgs.notIncluded
|
return msgs.notIncluded
|
||||||
}
|
}
|
||||||
|
|
||||||
return msgs.noSelection
|
return msgs.notIncluded
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import { useIntl } from "react-intl"
|
|||||||
import CampaignRateCard from "@scandic-hotels/design-system/CampaignRateCard"
|
import CampaignRateCard from "@scandic-hotels/design-system/CampaignRateCard"
|
||||||
import NoRateAvailableCard from "@scandic-hotels/design-system/NoRateAvailableCard"
|
import NoRateAvailableCard from "@scandic-hotels/design-system/NoRateAvailableCard"
|
||||||
|
|
||||||
import { useRatesStore } from "@/stores/select-rate"
|
|
||||||
|
|
||||||
import { useRoomContext } from "@/contexts/SelectRate/Room"
|
import { useRoomContext } from "@/contexts/SelectRate/Room"
|
||||||
import useRateTitles from "@/hooks/booking/useRateTitles"
|
import useRateTitles from "@/hooks/booking/useRateTitles"
|
||||||
|
|
||||||
@@ -30,7 +28,6 @@ export default function Campaign({
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const { roomAvailability, roomNr, selectedFilter, selectedRate } =
|
const { roomAvailability, roomNr, selectedFilter, selectedRate } =
|
||||||
useRoomContext()
|
useRoomContext()
|
||||||
const bookingCode = useRatesStore((state) => state.booking.bookingCode)
|
|
||||||
const rateTitles = useRateTitles()
|
const rateTitles = useRateTitles()
|
||||||
|
|
||||||
let isCampaignRate = false
|
let isCampaignRate = false
|
||||||
@@ -85,11 +82,11 @@ export default function Campaign({
|
|||||||
)
|
)
|
||||||
|
|
||||||
let bannerText = intl.formatMessage({ id: "Campaign" })
|
let bannerText = intl.formatMessage({ id: "Campaign" })
|
||||||
if (bookingCode) {
|
if (product.bookingCode) {
|
||||||
bannerText = bookingCode
|
bannerText = product.bookingCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if (product.rateDefinition?.breakfastIncluded) {
|
if (product.rateDefinition.breakfastIncluded) {
|
||||||
bannerText = `${bannerText} ∙ ${intl.formatMessage({ id: "Breakfast included" })}`
|
bannerText = `${bannerText} ∙ ${intl.formatMessage({ id: "Breakfast included" })}`
|
||||||
} else {
|
} else {
|
||||||
bannerText = `${bannerText} ∙ ${intl.formatMessage({ id: "Breakfast excluded" })}`
|
bannerText = `${bannerText} ∙ ${intl.formatMessage({ id: "Breakfast excluded" })}`
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default function Code({
|
|||||||
|
|
||||||
return code.map((product) => {
|
return code.map((product) => {
|
||||||
let bannerText = ""
|
let bannerText = ""
|
||||||
if (product.breakfastIncluded) {
|
if (product.rateDefinition.breakfastIncluded) {
|
||||||
bannerText = `${bookingCode} ∙ ${intl.formatMessage({ id: "Breakfast included" })}`
|
bannerText = `${bookingCode} ∙ ${intl.formatMessage({ id: "Breakfast included" })}`
|
||||||
} else {
|
} else {
|
||||||
bannerText = `${bookingCode} ∙ ${intl.formatMessage({ id: "Breakfast excluded" })}`
|
bannerText = `${bookingCode} ∙ ${intl.formatMessage({ id: "Breakfast excluded" })}`
|
||||||
@@ -141,7 +141,8 @@ export default function Code({
|
|||||||
petRoomPackage
|
petRoomPackage
|
||||||
)
|
)
|
||||||
|
|
||||||
const comparisonRate = regularPricePerNight.totalPrice
|
const comparisonRate =
|
||||||
|
+regularPricePerNight.totalPrice > +pricePerNight.totalPrice
|
||||||
? {
|
? {
|
||||||
price: regularPricePerNight.totalPrice,
|
price: regularPricePerNight.totalPrice,
|
||||||
unit: localPrice.currency,
|
unit: localPrice.currency,
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export default function Redemptions({
|
|||||||
|
|
||||||
const notEnoughPoints = rates.every((rate) => rate.isDisabled)
|
const notEnoughPoints = rates.every((rate) => rate.isDisabled)
|
||||||
const firstRedemption = redemptions[0]
|
const firstRedemption = redemptions[0]
|
||||||
const bannerText = firstRedemption.breakfastIncluded
|
const bannerText = firstRedemption.rateDefinition.breakfastIncluded
|
||||||
? `${rewardNight} ∙ ${breakfastIncluded}`
|
? `${rewardNight} ∙ ${breakfastIncluded}`
|
||||||
: `${rewardNight} ∙ ${breakfastExcluded}`
|
: `${rewardNight} ∙ ${breakfastExcluded}`
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export default function Regular({
|
|||||||
unit: `${standard!.localPrice.currency}/${night}`,
|
unit: `${standard!.localPrice.currency}/${night}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (standardPricePerNight.totalRequestedPrice) {
|
if (standardPricePerNight.totalRequestedPrice && !isUserLoggedIn) {
|
||||||
approximateStandardRatePrice = standardPricePerNight.totalRequestedPrice
|
approximateStandardRatePrice = standardPricePerNight.totalRequestedPrice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,34 +10,29 @@ export function isSelectedPriceProduct(
|
|||||||
selectedRate: SelectedRate | null,
|
selectedRate: SelectedRate | null,
|
||||||
roomTypeCode: string
|
roomTypeCode: string
|
||||||
) {
|
) {
|
||||||
if (!selectedRate) {
|
if (!selectedRate || roomTypeCode !== selectedRate.roomTypeCode) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const { member, public: standard } = product
|
const { member, public: standard } = product
|
||||||
let selectedRateMember: PriceProduct["member"] = null
|
let isSelected = false
|
||||||
if ("member" in selectedRate.product) {
|
if (
|
||||||
selectedRateMember = selectedRate.product.member
|
"member" in selectedRate.product &&
|
||||||
|
selectedRate.product.member &&
|
||||||
|
member
|
||||||
|
) {
|
||||||
|
isSelected = selectedRate.product.member.rateCode === member.rateCode
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedRatePublic: PriceProduct["public"] = null
|
if (
|
||||||
if ("public" in selectedRate.product) {
|
"public" in selectedRate.product &&
|
||||||
selectedRatePublic = selectedRate.product.public
|
selectedRate.product.public &&
|
||||||
|
standard
|
||||||
|
) {
|
||||||
|
isSelected = selectedRate.product.public.rateCode === standard.rateCode
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedRateIsMember =
|
return isSelected
|
||||||
member &&
|
|
||||||
selectedRateMember &&
|
|
||||||
member.rateCode === selectedRateMember.rateCode
|
|
||||||
|
|
||||||
const selectedRateIsPublic =
|
|
||||||
standard &&
|
|
||||||
selectedRatePublic &&
|
|
||||||
standard.rateCode === selectedRatePublic.rateCode
|
|
||||||
return !!(
|
|
||||||
(selectedRateIsMember || selectedRateIsPublic) &&
|
|
||||||
selectedRate.roomTypeCode === roomTypeCode
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSelectedCorporateCheque(
|
export function isSelectedCorporateCheque(
|
||||||
|
|||||||
@@ -42,6 +42,15 @@ export default function RoomProvider({
|
|||||||
const dontShowRegularRates =
|
const dontShowRegularRates =
|
||||||
hasRedemptionRates || hasCorporateChequeOrVoucherRates
|
hasRedemptionRates || hasCorporateChequeOrVoucherRates
|
||||||
|
|
||||||
|
// Since input would be the same on single room as already
|
||||||
|
// done in useRoomsAvailability hook, data is already present
|
||||||
|
// and thus runs the appendRegularRates updater resulting in
|
||||||
|
// duplicate data
|
||||||
|
const enabled = !!(
|
||||||
|
booking.bookingCode &&
|
||||||
|
selectedFilter === BookingCodeFilterEnum.All &&
|
||||||
|
!dontShowRegularRates
|
||||||
|
)
|
||||||
// Extra query needed to fetch regular rates upon user
|
// Extra query needed to fetch regular rates upon user
|
||||||
// selecting to view all rates.
|
// selecting to view all rates.
|
||||||
// TODO: Setup route to handle singular availability call
|
// TODO: Setup route to handle singular availability call
|
||||||
@@ -58,22 +67,18 @@ export default function RoomProvider({
|
|||||||
roomStayStartDate: booking.fromDate,
|
roomStayStartDate: booking.fromDate,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: !!(
|
enabled,
|
||||||
booking.bookingCode &&
|
|
||||||
selectedFilter === BookingCodeFilterEnum.All &&
|
|
||||||
!dontShowRegularRates
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isFetched && !isFetching && data?.length) {
|
if (isFetched && !isFetching && data?.length && enabled) {
|
||||||
const regularRates = data[0]
|
const regularRates = data[0]
|
||||||
if ("roomConfigurations" in regularRates) {
|
if ("roomConfigurations" in regularRates) {
|
||||||
appendRegularRates(regularRates.roomConfigurations)
|
appendRegularRates(regularRates.roomConfigurations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [appendRegularRates, data, isFetched, isFetching])
|
}, [appendRegularRates, data, enabled, isFetched, isFetching])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RoomContext.Provider
|
<RoomContext.Provider
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { rateDefinitionSchema } from "./schemas/roomAvailability/rateDefinition"
|
|||||||
|
|
||||||
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||||
|
import { RateEnum } from "@/types/enums/rate"
|
||||||
import { RateTypeEnum } from "@/types/enums/rateType"
|
import { RateTypeEnum } from "@/types/enums/rateType"
|
||||||
import type {
|
import type {
|
||||||
AdditionalData,
|
AdditionalData,
|
||||||
@@ -112,11 +113,11 @@ export const hotelsAvailabilitySchema = z.object({
|
|||||||
function getRate(rate: RateDefinition) {
|
function getRate(rate: RateDefinition) {
|
||||||
switch (rate.cancellationRule) {
|
switch (rate.cancellationRule) {
|
||||||
case "CancellableBefore6PM":
|
case "CancellableBefore6PM":
|
||||||
return "flex"
|
return RateEnum.flex
|
||||||
case "Changeable":
|
case "Changeable":
|
||||||
return "change"
|
return RateEnum.change
|
||||||
case "NotCancellable":
|
case "NotCancellable":
|
||||||
return "save"
|
return RateEnum.save
|
||||||
default:
|
default:
|
||||||
console.info(
|
console.info(
|
||||||
`Unknown cancellationRule [${rate.cancellationRule}]. This should never happen!`
|
`Unknown cancellationRule [${rate.cancellationRule}]. This should never happen!`
|
||||||
@@ -276,7 +277,6 @@ export const roomsAvailabilitySchema = z
|
|||||||
} else {
|
} else {
|
||||||
product.bookingCode = undefined
|
product.bookingCode = undefined
|
||||||
}
|
}
|
||||||
product.breakfastIncluded = rateDefinition.breakfastIncluded
|
|
||||||
product.rate = rate
|
product.rate = rate
|
||||||
product.rateDefinition = rateDefinition
|
product.rateDefinition = rateDefinition
|
||||||
|
|
||||||
@@ -292,7 +292,9 @@ export const roomsAvailabilitySchema = z
|
|||||||
if ("corporateCheque" in product) {
|
if ("corporateCheque" in product) {
|
||||||
const rateDetails = getRateDetails(product)
|
const rateDetails = getRateDetails(product)
|
||||||
if (rateDetails) {
|
if (rateDetails) {
|
||||||
breakfastIncluded.push(rateDetails.breakfastIncluded)
|
breakfastIncluded.push(
|
||||||
|
rateDetails.rateDefinition.breakfastIncluded
|
||||||
|
)
|
||||||
room.code.push({
|
room.code.push({
|
||||||
...rateDetails,
|
...rateDetails,
|
||||||
corporateCheque: product.corporateCheque,
|
corporateCheque: product.corporateCheque,
|
||||||
@@ -304,7 +306,9 @@ export const roomsAvailabilitySchema = z
|
|||||||
if ("voucher" in product) {
|
if ("voucher" in product) {
|
||||||
const rateDetails = getRateDetails(product)
|
const rateDetails = getRateDetails(product)
|
||||||
if (rateDetails) {
|
if (rateDetails) {
|
||||||
breakfastIncluded.push(rateDetails.breakfastIncluded)
|
breakfastIncluded.push(
|
||||||
|
rateDetails.rateDefinition.breakfastIncluded
|
||||||
|
)
|
||||||
room.code.push({
|
room.code.push({
|
||||||
...rateDetails,
|
...rateDetails,
|
||||||
voucher: product.voucher,
|
voucher: product.voucher,
|
||||||
@@ -319,7 +323,9 @@ export const roomsAvailabilitySchema = z
|
|||||||
for (const redemption of product) {
|
for (const redemption of product) {
|
||||||
const rateDetails = getRateDetails(redemption)
|
const rateDetails = getRateDetails(redemption)
|
||||||
if (rateDetails) {
|
if (rateDetails) {
|
||||||
breakfastIncluded.push(rateDetails.breakfastIncluded)
|
breakfastIncluded.push(
|
||||||
|
rateDetails.rateDefinition.breakfastIncluded
|
||||||
|
)
|
||||||
room.redemptions.push({
|
room.redemptions.push({
|
||||||
...redemption,
|
...redemption,
|
||||||
...rateDetails,
|
...rateDetails,
|
||||||
@@ -336,24 +342,26 @@ export const roomsAvailabilitySchema = z
|
|||||||
) {
|
) {
|
||||||
const memberRate = product.member
|
const memberRate = product.member
|
||||||
const publicRate = product.public
|
const publicRate = product.public
|
||||||
const rateCode = publicRate?.rateCode ?? memberRate?.rateCode
|
|
||||||
const rateDetails = getRateDetails(product)
|
const rateDetails = getRateDetails(product)
|
||||||
const rateDetailsMember = getRateDetails({
|
const rateDetailsMember = getRateDetails({
|
||||||
...product,
|
...product,
|
||||||
public: null,
|
public: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (rateDetails && rateCode) {
|
if (rateDetails) {
|
||||||
|
if (publicRate) {
|
||||||
|
breakfastIncluded.push(
|
||||||
|
rateDetails.rateDefinition.breakfastIncluded
|
||||||
|
)
|
||||||
|
}
|
||||||
if (rateDetailsMember) {
|
if (rateDetailsMember) {
|
||||||
breakfastIncludedMember.push(
|
breakfastIncludedMember.push(
|
||||||
rateDetailsMember.breakfastIncluded
|
rateDetailsMember.rateDefinition.breakfastIncluded
|
||||||
)
|
)
|
||||||
rateDetails.rateDefinitionMember =
|
rateDetails.rateDefinitionMember =
|
||||||
rateDetailsMember.rateDefinition
|
rateDetailsMember.rateDefinition
|
||||||
}
|
}
|
||||||
const rateDefinition = findRateDefintion(rateCode)
|
switch (rateDetails.rateDefinition.rateType) {
|
||||||
if (rateDefinition) {
|
|
||||||
switch (rateDefinition.rateType) {
|
|
||||||
case RateTypeEnum.PublicPromotion:
|
case RateTypeEnum.PublicPromotion:
|
||||||
room.campaign.push({
|
room.campaign.push({
|
||||||
...rateDetails,
|
...rateDetails,
|
||||||
@@ -375,7 +383,6 @@ export const roomsAvailabilitySchema = z
|
|||||||
public: publicRate,
|
public: publicRate,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,10 @@ export const breakfastPackageSchema = z.object({
|
|||||||
description: z.string(),
|
description: z.string(),
|
||||||
localPrice: packagePriceSchema,
|
localPrice: packagePriceSchema,
|
||||||
requestedPrice: packagePriceSchema,
|
requestedPrice: packagePriceSchema,
|
||||||
packageType: z.literal(PackageTypeEnum.BreakfastAdult),
|
packageType: z.enum([
|
||||||
|
PackageTypeEnum.BreakfastAdult,
|
||||||
|
PackageTypeEnum.BreakfastChildren,
|
||||||
|
]),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const ancillaryPackageSchema = z.object({
|
export const ancillaryPackageSchema = z.object({
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ import {
|
|||||||
} from "../productTypePrice"
|
} from "../productTypePrice"
|
||||||
import { rateDefinitionSchema } from "./rateDefinition"
|
import { rateDefinitionSchema } from "./rateDefinition"
|
||||||
|
|
||||||
|
import { RateEnum } from "@/types/enums/rate"
|
||||||
|
|
||||||
const baseProductSchema = z.object({
|
const baseProductSchema = z.object({
|
||||||
// transform empty string to undefined
|
// transform empty string to undefined
|
||||||
bookingCode: z
|
bookingCode: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.transform((val) => val),
|
.transform((val) => val),
|
||||||
// Is breakfast included on product
|
|
||||||
breakfastIncluded: z.boolean().default(false),
|
|
||||||
// Used to set the rate that we use to chose titles etc.
|
// Used to set the rate that we use to chose titles etc.
|
||||||
rate: z.enum(["change", "flex", "save"]).default("save"),
|
rate: z.nativeEnum(RateEnum).default(RateEnum.save),
|
||||||
rateDefinition: rateDefinitionSchema.optional().transform((val) =>
|
rateDefinition: rateDefinitionSchema.optional().transform((val) =>
|
||||||
val
|
val
|
||||||
? val
|
? val
|
||||||
@@ -42,7 +42,6 @@ const baseProductSchema = z.object({
|
|||||||
function mapBaseProduct(baseProduct: typeof baseProductSchema._type) {
|
function mapBaseProduct(baseProduct: typeof baseProductSchema._type) {
|
||||||
return {
|
return {
|
||||||
bookingCode: baseProduct.bookingCode,
|
bookingCode: baseProduct.bookingCode,
|
||||||
breakfastIncluded: baseProduct.breakfastIncluded,
|
|
||||||
rate: baseProduct.rate,
|
rate: baseProduct.rate,
|
||||||
rateDefinition: baseProduct.rateDefinition,
|
rateDefinition: baseProduct.rateDefinition,
|
||||||
rateDefinitionMember: baseProduct.rateDefinitionMember,
|
rateDefinitionMember: baseProduct.rateDefinitionMember,
|
||||||
@@ -97,14 +96,12 @@ export const redemptionsProduct = z
|
|||||||
data.map(
|
data.map(
|
||||||
({
|
({
|
||||||
bookingCode,
|
bookingCode,
|
||||||
breakfastIncluded,
|
|
||||||
rate,
|
rate,
|
||||||
rateDefinition,
|
rateDefinition,
|
||||||
rateDefinitionMember,
|
rateDefinitionMember,
|
||||||
...redemption
|
...redemption
|
||||||
}) => ({
|
}) => ({
|
||||||
bookingCode,
|
bookingCode,
|
||||||
breakfastIncluded,
|
|
||||||
rate,
|
rate,
|
||||||
rateDefinition,
|
rateDefinition,
|
||||||
rateDefinitionMember,
|
rateDefinitionMember,
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ export function calcTotalPrice(
|
|||||||
? (room.breakfast.localPrice?.price ?? 0)
|
? (room.breakfast.localPrice?.price ?? 0)
|
||||||
: 0
|
: 0
|
||||||
|
|
||||||
const roomFeaturesTotal = room.roomFeatures?.reduce(
|
const roomFeaturesTotal = (room.roomFeatures || []).reduce(
|
||||||
(total, pkg) => {
|
(total, pkg) => {
|
||||||
if (pkg.requestedPrice.totalPrice) {
|
if (pkg.requestedPrice.totalPrice) {
|
||||||
total.requestedPrice = add(
|
total.requestedPrice = add(
|
||||||
@@ -445,45 +445,72 @@ export function calcTotalPrice(
|
|||||||
{ local: 0, requestedPrice: 0 }
|
{ local: 0, requestedPrice: 0 }
|
||||||
)
|
)
|
||||||
|
|
||||||
const result: Price = {
|
if (roomPrice.perStay.requested) {
|
||||||
requested: roomPrice.perStay.requested
|
if (!acc.requested) {
|
||||||
? {
|
acc.requested = {
|
||||||
currency: roomPrice.perStay.requested.currency,
|
currency: roomPrice.perStay.requested.currency,
|
||||||
price: add(
|
price: 0,
|
||||||
acc.requested?.price ?? 0,
|
|
||||||
roomPrice.perStay.requested.price,
|
|
||||||
breakfastRequestedPrice * room.adults * nights
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
: undefined,
|
|
||||||
local: {
|
|
||||||
currency: roomPrice.perStay.local.currency,
|
|
||||||
price: add(
|
|
||||||
acc.local.price,
|
|
||||||
roomPrice.perStay.local.price,
|
|
||||||
breakfastLocalPrice * room.adults * nights,
|
|
||||||
roomFeaturesTotal?.local ?? 0
|
|
||||||
),
|
|
||||||
regularPrice: add(
|
|
||||||
acc.local.regularPrice,
|
|
||||||
roomPrice.perStay.local.regularPrice,
|
|
||||||
breakfastLocalPrice * room.adults * nights,
|
|
||||||
roomFeaturesTotal?.requestedPrice ?? 0
|
|
||||||
),
|
|
||||||
additionalPrice: add(
|
|
||||||
acc.local.additionalPrice,
|
|
||||||
roomPrice.perStay.local.additionalPrice,
|
|
||||||
breakfastLocalPrice * room.adults * nights,
|
|
||||||
roomFeaturesTotal?.local ?? 0
|
|
||||||
),
|
|
||||||
additionalPriceCurrency: roomPrice.perStay.local
|
|
||||||
.additionalPriceCurrency
|
|
||||||
? roomPrice.perStay.local.additionalPriceCurrency
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
acc.requested.price = add(
|
||||||
|
acc.requested.price,
|
||||||
|
roomPrice.perStay.requested.price,
|
||||||
|
breakfastRequestedPrice * room.adults * nights
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: Come back and verify on CC, PTS, Voucher
|
||||||
|
if (roomPrice.perStay.requested.additionalPrice) {
|
||||||
|
acc.requested.additionalPrice = add(
|
||||||
|
acc.requested.additionalPrice,
|
||||||
|
roomPrice.perStay.requested.additionalPrice
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
roomPrice.perStay.requested.additionalPriceCurrency &&
|
||||||
|
!acc.requested.additionalPriceCurrency
|
||||||
|
) {
|
||||||
|
acc.requested.additionalPriceCurrency =
|
||||||
|
roomPrice.perStay.requested.additionalPriceCurrency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const breakfastLocalTotalPrice =
|
||||||
|
breakfastLocalPrice * room.adults * nights
|
||||||
|
|
||||||
|
acc.local.price = add(
|
||||||
|
acc.local.price,
|
||||||
|
roomPrice.perStay.local.price,
|
||||||
|
breakfastLocalTotalPrice,
|
||||||
|
roomFeaturesTotal.local
|
||||||
|
)
|
||||||
|
|
||||||
|
if (roomPrice.perStay.local.regularPrice) {
|
||||||
|
acc.local.regularPrice = add(
|
||||||
|
acc.local.regularPrice,
|
||||||
|
roomPrice.perStay.local.regularPrice,
|
||||||
|
breakfastLocalTotalPrice,
|
||||||
|
roomFeaturesTotal.local
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roomPrice.perStay.local.additionalPrice) {
|
||||||
|
acc.local.additionalPrice = add(
|
||||||
|
acc.local.additionalPrice,
|
||||||
|
roomPrice.perStay.local.additionalPrice
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
roomPrice.perStay.local.additionalPriceCurrency &&
|
||||||
|
!acc.local.additionalPriceCurrency
|
||||||
|
) {
|
||||||
|
acc.local.additionalPriceCurrency =
|
||||||
|
roomPrice.perStay.local.additionalPriceCurrency
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requested: undefined,
|
requested: undefined,
|
||||||
|
|||||||
@@ -168,100 +168,19 @@ export function createDetailsStore(
|
|||||||
currentRoom.steps[StepEnum.breakfast].isValid = true
|
currentRoom.steps[StepEnum.breakfast].isValid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentTotalPriceRequested = state.totalPrice.requested
|
currentRoom.room.breakfast = breakfast
|
||||||
let stateTotalRequestedPrice = 0
|
|
||||||
if (currentTotalPriceRequested) {
|
|
||||||
stateTotalRequestedPrice =
|
|
||||||
currentTotalPriceRequested.price ?? 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const stateTotalLocalPrice = state.totalPrice.local.price
|
|
||||||
const stateTotalLocalRegularPrice =
|
|
||||||
state.totalPrice.local.regularPrice
|
|
||||||
|
|
||||||
const addToTotalPrice =
|
|
||||||
(currentRoom.room.breakfast === undefined ||
|
|
||||||
currentRoom.room.breakfast === false) &&
|
|
||||||
!!breakfast
|
|
||||||
|
|
||||||
const subtractFromTotalPrice =
|
|
||||||
currentRoom.room.breakfast && breakfast === false
|
|
||||||
|
|
||||||
const nights = dt(state.booking.toDate).diff(
|
const nights = dt(state.booking.toDate).diff(
|
||||||
state.booking.fromDate,
|
state.booking.fromDate,
|
||||||
"days"
|
"days"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (addToTotalPrice) {
|
state.totalPrice = calcTotalPrice(
|
||||||
const breakfastTotalRequestedPrice =
|
state.rooms,
|
||||||
breakfast.requestedPrice.price *
|
currentRoom.room.roomPrice.perStay.local.currency,
|
||||||
currentRoom.room.adults *
|
isMember,
|
||||||
nights
|
nights
|
||||||
const breakfastTotalPrice =
|
)
|
||||||
breakfast.localPrice.price *
|
|
||||||
currentRoom.room.adults *
|
|
||||||
nights
|
|
||||||
state.totalPrice = {
|
|
||||||
requested: state.totalPrice.requested && {
|
|
||||||
currency: state.totalPrice.requested.currency,
|
|
||||||
price:
|
|
||||||
stateTotalRequestedPrice + breakfastTotalRequestedPrice,
|
|
||||||
},
|
|
||||||
local: {
|
|
||||||
currency: breakfast.localPrice.currency,
|
|
||||||
price: stateTotalLocalPrice ?? 0 + breakfastTotalPrice,
|
|
||||||
regularPrice: stateTotalLocalRegularPrice
|
|
||||||
? stateTotalLocalRegularPrice + breakfastTotalPrice
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subtractFromTotalPrice) {
|
|
||||||
let currency = state.totalPrice.local.currency
|
|
||||||
let currentBreakfastTotalPrice = 0
|
|
||||||
let currentBreakfastTotalRequestedPrice = 0
|
|
||||||
if (currentRoom.room.breakfast) {
|
|
||||||
currentBreakfastTotalPrice =
|
|
||||||
currentRoom.room.breakfast.localPrice.price *
|
|
||||||
currentRoom.room.adults *
|
|
||||||
nights
|
|
||||||
currentBreakfastTotalRequestedPrice =
|
|
||||||
currentRoom.room.breakfast.requestedPrice.totalPrice *
|
|
||||||
currentRoom.room.adults *
|
|
||||||
nights
|
|
||||||
currency = currentRoom.room.breakfast.localPrice.currency
|
|
||||||
}
|
|
||||||
|
|
||||||
let requestedPrice =
|
|
||||||
stateTotalRequestedPrice -
|
|
||||||
currentBreakfastTotalRequestedPrice
|
|
||||||
if (requestedPrice < 0) {
|
|
||||||
requestedPrice = 0
|
|
||||||
}
|
|
||||||
let localPrice =
|
|
||||||
stateTotalLocalPrice - currentBreakfastTotalPrice
|
|
||||||
if (localPrice < 0) {
|
|
||||||
localPrice = 0
|
|
||||||
}
|
|
||||||
let regularPrice = stateTotalLocalRegularPrice
|
|
||||||
? stateTotalLocalRegularPrice - currentBreakfastTotalPrice
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
state.totalPrice = {
|
|
||||||
requested: state.totalPrice.requested && {
|
|
||||||
currency: state.totalPrice.requested.currency,
|
|
||||||
price: requestedPrice,
|
|
||||||
},
|
|
||||||
local: {
|
|
||||||
currency,
|
|
||||||
price: localPrice,
|
|
||||||
regularPrice,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRoom.room.breakfast = breakfast
|
|
||||||
|
|
||||||
const isAllStepsCompleted = checkRoomProgress(
|
const isAllStepsCompleted = checkRoomProgress(
|
||||||
state.rooms[idx].steps
|
state.rooms[idx].steps
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ import type {
|
|||||||
RoomConfiguration,
|
RoomConfiguration,
|
||||||
} from "@/types/trpc/routers/hotel/roomAvailability"
|
} from "@/types/trpc/routers/hotel/roomAvailability"
|
||||||
|
|
||||||
export function findProduct(rateCode: string, product: Product) {
|
export function findProduct(
|
||||||
|
rateCode: string,
|
||||||
|
product: Product,
|
||||||
|
counterRateCode = ""
|
||||||
|
) {
|
||||||
if ("corporateCheque" in product) {
|
if ("corporateCheque" in product) {
|
||||||
return product.corporateCheque.rateCode === rateCode
|
return product.corporateCheque.rateCode === rateCode
|
||||||
}
|
}
|
||||||
@@ -18,21 +22,35 @@ export function findProduct(rateCode: string, product: Product) {
|
|||||||
return product.voucher.rateCode === rateCode
|
return product.voucher.rateCode === rateCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("public" in product && product.public) {
|
const memberExists = "member" in product
|
||||||
return product.public.rateCode === rateCode
|
const publicExists = "public" in product
|
||||||
|
const isRegularRate = memberExists && publicExists
|
||||||
|
if (isRegularRate) {
|
||||||
|
let isProduct = false
|
||||||
|
if (product.member) {
|
||||||
|
isProduct =
|
||||||
|
product.member.rateCode === rateCode ||
|
||||||
|
product.member.rateCode === counterRateCode
|
||||||
}
|
}
|
||||||
|
if (product.public) {
|
||||||
if ("member" in product && product.member) {
|
isProduct =
|
||||||
return product.member.rateCode === rateCode
|
product.public.rateCode === rateCode ||
|
||||||
|
product.public.rateCode === counterRateCode
|
||||||
|
}
|
||||||
|
return isProduct
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findProductInRoom(rateCode: string, room: RoomConfiguration) {
|
export function findProductInRoom(
|
||||||
|
rateCode: string,
|
||||||
|
room: RoomConfiguration,
|
||||||
|
counterRateCode = ""
|
||||||
|
) {
|
||||||
if (room.campaign.length) {
|
if (room.campaign.length) {
|
||||||
const campaignProduct = room.campaign.find((product) =>
|
const campaignProduct = room.campaign.find((product) =>
|
||||||
findProduct(rateCode, product)
|
findProduct(rateCode, product, counterRateCode)
|
||||||
)
|
)
|
||||||
if (campaignProduct) {
|
if (campaignProduct) {
|
||||||
return campaignProduct
|
return campaignProduct
|
||||||
@@ -40,7 +58,7 @@ export function findProductInRoom(rateCode: string, room: RoomConfiguration) {
|
|||||||
}
|
}
|
||||||
if (room.code.length) {
|
if (room.code.length) {
|
||||||
const codeProduct = room.code.find((product) =>
|
const codeProduct = room.code.find((product) =>
|
||||||
findProduct(rateCode, product)
|
findProduct(rateCode, product, counterRateCode)
|
||||||
)
|
)
|
||||||
if (codeProduct) {
|
if (codeProduct) {
|
||||||
return codeProduct
|
return codeProduct
|
||||||
@@ -56,7 +74,7 @@ export function findProductInRoom(rateCode: string, room: RoomConfiguration) {
|
|||||||
}
|
}
|
||||||
if (room.regular.length) {
|
if (room.regular.length) {
|
||||||
const regularProduct = room.regular.find((product) =>
|
const regularProduct = room.regular.find((product) =>
|
||||||
findProduct(rateCode, product)
|
findProduct(rateCode, product, counterRateCode)
|
||||||
)
|
)
|
||||||
if (regularProduct) {
|
if (regularProduct) {
|
||||||
return regularProduct
|
return regularProduct
|
||||||
@@ -66,6 +84,7 @@ export function findProductInRoom(rateCode: string, room: RoomConfiguration) {
|
|||||||
|
|
||||||
export function findSelectedRate(
|
export function findSelectedRate(
|
||||||
rateCode: string,
|
rateCode: string,
|
||||||
|
counterRateCode: string,
|
||||||
roomTypeCode: string,
|
roomTypeCode: string,
|
||||||
rooms: RoomConfiguration[] | AvailabilityError
|
rooms: RoomConfiguration[] | AvailabilityError
|
||||||
) {
|
) {
|
||||||
@@ -76,7 +95,7 @@ export function findSelectedRate(
|
|||||||
if (room.roomTypeCode !== roomTypeCode) {
|
if (room.roomTypeCode !== roomTypeCode) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return findProductInRoom(rateCode, room)
|
return findProductInRoom(rateCode, room, counterRateCode)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export function createRatesStore({
|
|||||||
const roomConfiguration = roomConfigurations?.[idx]
|
const roomConfiguration = roomConfigurations?.[idx]
|
||||||
const selectedRoom = findSelectedRate(
|
const selectedRoom = findSelectedRate(
|
||||||
room.rateCode,
|
room.rateCode,
|
||||||
|
room.counterRateCode,
|
||||||
room.roomTypeCode,
|
room.roomTypeCode,
|
||||||
roomConfiguration
|
roomConfiguration
|
||||||
)
|
)
|
||||||
@@ -79,7 +80,11 @@ export function createRatesStore({
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const product = findProductInRoom(room.rateCode, selectedRoom)
|
const product = findProductInRoom(
|
||||||
|
room.rateCode,
|
||||||
|
selectedRoom,
|
||||||
|
room.counterRateCode
|
||||||
|
)
|
||||||
if (product) {
|
if (product) {
|
||||||
rateSummary[idx] = {
|
rateSummary[idx] = {
|
||||||
features: selectedRoom.features,
|
features: selectedRoom.features,
|
||||||
@@ -121,13 +126,18 @@ export function createRatesStore({
|
|||||||
const selectedRate =
|
const selectedRate =
|
||||||
findSelectedRate(
|
findSelectedRate(
|
||||||
room.rateCode,
|
room.rateCode,
|
||||||
|
room.counterRateCode,
|
||||||
room.roomTypeCode,
|
room.roomTypeCode,
|
||||||
roomConfiguration
|
roomConfiguration
|
||||||
) ?? null
|
) ?? null
|
||||||
|
|
||||||
let product = null
|
let product = null
|
||||||
if (selectedRate) {
|
if (selectedRate) {
|
||||||
product = findProductInRoom(room.rateCode, selectedRate)
|
product = findProductInRoom(
|
||||||
|
room.rateCode,
|
||||||
|
selectedRate,
|
||||||
|
room.counterRateCode
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since features are fetched async based on query string, we need to read from query string to apply correct filtering
|
// Since features are fetched async based on query string, we need to read from query string to apply correct filtering
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { RateEnum } from "@/types/enums/rate"
|
||||||
import type {
|
import type {
|
||||||
Product,
|
Product,
|
||||||
RoomConfiguration,
|
RoomConfiguration,
|
||||||
@@ -35,7 +36,7 @@ export type Rate = {
|
|||||||
priceName?: string
|
priceName?: string
|
||||||
priceTerm?: string
|
priceTerm?: string
|
||||||
product: Product
|
product: Product
|
||||||
rate: "change" | "flex" | "save"
|
rate: RateEnum
|
||||||
roomRates?: {
|
roomRates?: {
|
||||||
rate: Rate
|
rate: Rate
|
||||||
roomIndex: number
|
roomIndex: number
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Lang } from "@/constants/languages"
|
import type { Lang } from "@/constants/languages"
|
||||||
import type { MembershipLevel } from "@/constants/membershipLevels"
|
import type { MembershipLevel } from "@/constants/membershipLevels"
|
||||||
|
import type { RateEnum } from "../enums/rate"
|
||||||
|
|
||||||
export enum TrackingChannelEnum {
|
export enum TrackingChannelEnum {
|
||||||
"scandic-friends" = "scandic-friends",
|
"scandic-friends" = "scandic-friends",
|
||||||
@@ -54,7 +55,7 @@ export type TrackingSDKUserData =
|
|||||||
export type TrackingSDKHotelInfo = {
|
export type TrackingSDKHotelInfo = {
|
||||||
ageOfChildren?: string // "10", "2,5,10"
|
ageOfChildren?: string // "10", "2,5,10"
|
||||||
ancillaries?: Ancillary[]
|
ancillaries?: Ancillary[]
|
||||||
analyticsRateCode?: "flex" | "change" | "save" | string
|
analyticsRateCode?: RateEnum | string
|
||||||
arrivalDate?: string
|
arrivalDate?: string
|
||||||
availableResults?: number // Number of hotels to choose from after a city search
|
availableResults?: number // Number of hotels to choose from after a city search
|
||||||
bedType?: string
|
bedType?: string
|
||||||
|
|||||||
5
apps/scandic-web/types/enums/rate.ts
Normal file
5
apps/scandic-web/types/enums/rate.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export enum RateEnum {
|
||||||
|
change = "change",
|
||||||
|
flex = "flex",
|
||||||
|
save = "save",
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { BedTypeSelection } from "@/types/components/hotelReservation/enterDetails/bedType"
|
import type { BedTypeSelection } from "@/types/components/hotelReservation/enterDetails/bedType"
|
||||||
import type { RoomRate } from "@/types/components/hotelReservation/enterDetails/details"
|
import type { RoomRate } from "@/types/components/hotelReservation/enterDetails/details"
|
||||||
|
import type { RateEnum } from "@/types/enums/rate"
|
||||||
import type { Packages } from "@/types/requests/packages"
|
import type { Packages } from "@/types/requests/packages"
|
||||||
|
|
||||||
export interface Room {
|
export interface Room {
|
||||||
@@ -10,7 +11,7 @@ export interface Room {
|
|||||||
mustBeGuaranteed: boolean
|
mustBeGuaranteed: boolean
|
||||||
memberMustBeGuaranteed?: boolean
|
memberMustBeGuaranteed?: boolean
|
||||||
packages: Packages | null
|
packages: Packages | null
|
||||||
rate: "change" | "flex" | "save"
|
rate: RateEnum
|
||||||
rateDefinitionTitle: string
|
rateDefinitionTitle: string
|
||||||
rateDetails: string[]
|
rateDetails: string[]
|
||||||
rateTitle?: string
|
rateTitle?: string
|
||||||
|
|||||||
@@ -120,7 +120,9 @@ export default function CodeRateCard({
|
|||||||
<div className={`${styles.rateRow} ${styles.comparisonRate}`}>
|
<div className={`${styles.rateRow} ${styles.comparisonRate}`}>
|
||||||
<Typography variant="Title/Subtitle/md">
|
<Typography variant="Title/Subtitle/md">
|
||||||
<p>
|
<p>
|
||||||
<span className={styles.strikethrough}>{rate.price}</span>{' '}
|
<span className={styles.strikethrough}>
|
||||||
|
{comparisonRate.price}
|
||||||
|
</span>{' '}
|
||||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||||
<span className={styles.strikethrough}>
|
<span className={styles.strikethrough}>
|
||||||
{comparisonRate.unit}
|
{comparisonRate.unit}
|
||||||
|
|||||||
Reference in New Issue
Block a user