Merged in fix/SW-3198-prices-select-rate (pull request #2763)
fix(SW-3198): fix striketrhough/regular prices, the same in enter details as select rate * fix(SW-3198): fix striketrhough/regular prices, the same in enter details as select rate * fix(SW-3198): remove additonalcost if calculating cost per room * fix(SW-3198): include bookingcode in specialrate * fix(SW-3198): remove console log * fix(SW-3198): add or operator * fix(SW-3198): capture total return value * fix(SW-3198): rename and move function Approved-by: Joakim Jäderberg Approved-by: Hrishikesh Vaipurkar
This commit is contained in:
@@ -150,8 +150,8 @@ export default async function DetailsPage(
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
<aside className={styles.summary}>
|
<aside className={styles.summary}>
|
||||||
<MobileSummary isMember={!!user} />
|
<MobileSummary isUserLoggedIn={!!user} />
|
||||||
<DesktopSummary isMember={!!user} />
|
<DesktopSummary isUserLoggedIn={!!user} />
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
<EnterDetailsTrackingWrapper
|
<EnterDetailsTrackingWrapper
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import SummaryUI from "./UI"
|
|||||||
|
|
||||||
import type { SummaryProps } from "@/types/components/hotelReservation/summary"
|
import type { SummaryProps } from "@/types/components/hotelReservation/summary"
|
||||||
|
|
||||||
export default function DesktopSummary({ isMember }: SummaryProps) {
|
export default function DesktopSummary({ isUserLoggedIn }: SummaryProps) {
|
||||||
const toggleSummaryOpen = useEnterDetailsStore(
|
const toggleSummaryOpen = useEnterDetailsStore(
|
||||||
(state) => state.actions.toggleSummaryOpen
|
(state) => state.actions.toggleSummaryOpen
|
||||||
)
|
)
|
||||||
@@ -27,7 +27,7 @@ export default function DesktopSummary({ isMember }: SummaryProps) {
|
|||||||
<SummaryUI
|
<SummaryUI
|
||||||
booking={booking}
|
booking={booking}
|
||||||
rooms={rooms}
|
rooms={rooms}
|
||||||
isMember={isMember}
|
isUserLoggedIn={isUserLoggedIn}
|
||||||
totalPrice={totalPrice}
|
totalPrice={totalPrice}
|
||||||
vat={vat}
|
vat={vat}
|
||||||
toggleSummaryOpen={toggleSummaryOpen}
|
toggleSummaryOpen={toggleSummaryOpen}
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ import styles from "./bottomSheet.module.css"
|
|||||||
|
|
||||||
interface SummaryBottomSheetProps
|
interface SummaryBottomSheetProps
|
||||||
extends PropsWithChildren<{
|
extends PropsWithChildren<{
|
||||||
isMember: boolean
|
isUserLoggedIn: boolean
|
||||||
}> {}
|
}> {}
|
||||||
|
|
||||||
export default function SummaryBottomSheet({
|
export default function SummaryBottomSheet({
|
||||||
children,
|
children,
|
||||||
isMember,
|
isUserLoggedIn,
|
||||||
}: SummaryBottomSheetProps) {
|
}: SummaryBottomSheetProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const scrollY = useRef(0)
|
const scrollY = useRef(0)
|
||||||
@@ -68,7 +68,7 @@ export default function SummaryBottomSheet({
|
|||||||
const containsBookingCodeRate = rooms.find(
|
const containsBookingCodeRate = rooms.find(
|
||||||
(r) => r && isBookingCodeRate(r.room.roomRate)
|
(r) => r && isBookingCodeRate(r.room.roomRate)
|
||||||
)
|
)
|
||||||
const showDiscounted = containsBookingCodeRate || isMember
|
const showDiscounted = containsBookingCodeRate || isUserLoggedIn
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper} data-open={isSummaryOpen}>
|
<div className={styles.wrapper} data-open={isSummaryOpen}>
|
||||||
@@ -103,13 +103,15 @@ export default function SummaryBottomSheet({
|
|||||||
</Typography>
|
</Typography>
|
||||||
{showDiscounted && totalPrice.local.regularPrice ? (
|
{showDiscounted && totalPrice.local.regularPrice ? (
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
<s className={styles.strikeThroughRate}>
|
<p>
|
||||||
{formatPrice(
|
<s className={styles.strikeThroughRate}>
|
||||||
intl,
|
{formatPrice(
|
||||||
totalPrice.local.regularPrice,
|
intl,
|
||||||
totalPrice.local.currency
|
totalPrice.local.regularPrice,
|
||||||
)}
|
totalPrice.local.currency
|
||||||
</s>
|
)}
|
||||||
|
</s>
|
||||||
|
</p>
|
||||||
</Typography>
|
</Typography>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import styles from "./mobile.module.css"
|
|||||||
|
|
||||||
import type { SummaryProps } from "@/types/components/hotelReservation/summary"
|
import type { SummaryProps } from "@/types/components/hotelReservation/summary"
|
||||||
|
|
||||||
export default function MobileSummary({ isMember }: SummaryProps) {
|
export default function MobileSummary({ isUserLoggedIn }: SummaryProps) {
|
||||||
const { isSummaryOpen, toggleSummaryOpen } = useEnterDetailsStore(
|
const { isSummaryOpen, toggleSummaryOpen } = useEnterDetailsStore(
|
||||||
(state) => ({
|
(state) => ({
|
||||||
isSummaryOpen: state.isSummaryOpen,
|
isSummaryOpen: state.isSummaryOpen,
|
||||||
@@ -29,7 +29,7 @@ export default function MobileSummary({ isMember }: SummaryProps) {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const showPromo =
|
const showPromo =
|
||||||
!isMember &&
|
!isUserLoggedIn &&
|
||||||
rooms.length === 1 &&
|
rooms.length === 1 &&
|
||||||
!rooms[0].room.guest.join &&
|
!rooms[0].room.guest.join &&
|
||||||
!rooms[0].room.guest.membershipNo
|
!rooms[0].room.guest.membershipNo
|
||||||
@@ -51,12 +51,12 @@ export default function MobileSummary({ isMember }: SummaryProps) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SummaryBottomSheet isMember={isMember}>
|
<SummaryBottomSheet isUserLoggedIn={isUserLoggedIn}>
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<SummaryUI
|
<SummaryUI
|
||||||
booking={booking}
|
booking={booking}
|
||||||
rooms={rooms}
|
rooms={rooms}
|
||||||
isMember={isMember}
|
isUserLoggedIn={isUserLoggedIn}
|
||||||
totalPrice={totalPrice}
|
totalPrice={totalPrice}
|
||||||
vat={vat}
|
vat={vat}
|
||||||
toggleSummaryOpen={toggleSummaryOpen}
|
toggleSummaryOpen={toggleSummaryOpen}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
|
|||||||
|
|
||||||
import { getFeatureDescription } from "@/components/HotelReservation/utils/getRoomFeatureDescription"
|
import { getFeatureDescription } from "@/components/HotelReservation/utils/getRoomFeatureDescription"
|
||||||
|
|
||||||
import { getMemberPrice, getPublicPrice } from "../utils"
|
|
||||||
import Breakfast from "./Breakfast"
|
import Breakfast from "./Breakfast"
|
||||||
|
|
||||||
import styles from "./room.module.css"
|
import styles from "./room.module.css"
|
||||||
@@ -23,8 +22,7 @@ interface RoomProps {
|
|||||||
room: RoomType
|
room: RoomType
|
||||||
roomNumber: number
|
roomNumber: number
|
||||||
roomCount: number
|
roomCount: number
|
||||||
isMember: boolean
|
isUserLoggedIn: boolean
|
||||||
isSpecialRate: boolean
|
|
||||||
nightsCount: number
|
nightsCount: number
|
||||||
defaultCurrency: CurrencyEnum
|
defaultCurrency: CurrencyEnum
|
||||||
}
|
}
|
||||||
@@ -33,8 +31,7 @@ export default function Room({
|
|||||||
room,
|
room,
|
||||||
roomNumber,
|
roomNumber,
|
||||||
roomCount,
|
roomCount,
|
||||||
isMember,
|
isUserLoggedIn,
|
||||||
isSpecialRate,
|
|
||||||
nightsCount,
|
nightsCount,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
}: RoomProps) {
|
}: RoomProps) {
|
||||||
@@ -61,16 +58,23 @@ export default function Room({
|
|||||||
const childBedCrib = childrenBeds?.get(ChildBedMapEnum.IN_CRIB)
|
const childBedCrib = childrenBeds?.get(ChildBedMapEnum.IN_CRIB)
|
||||||
const childBedExtraBed = childrenBeds?.get(ChildBedMapEnum.IN_EXTRA_BED)
|
const childBedExtraBed = childrenBeds?.get(ChildBedMapEnum.IN_EXTRA_BED)
|
||||||
|
|
||||||
const memberPrice = getMemberPrice(room.roomRate)
|
const isFirstRoomMember = roomNumber === 1 && isUserLoggedIn
|
||||||
const publicPrice = getPublicPrice(room.roomRate)
|
|
||||||
|
|
||||||
const isFirstRoomMember = roomNumber === 1 && isMember
|
|
||||||
const isOrWillBecomeMember = !!(
|
const isOrWillBecomeMember = !!(
|
||||||
room.guest.join ||
|
room.guest.join ||
|
||||||
room.guest.membershipNo ||
|
room.guest.membershipNo ||
|
||||||
isFirstRoomMember
|
isFirstRoomMember
|
||||||
)
|
)
|
||||||
const showMemberPrice = !!(isOrWillBecomeMember && memberPrice)
|
const showMemberPrice = !!(
|
||||||
|
isOrWillBecomeMember &&
|
||||||
|
"member" in room.roomRate &&
|
||||||
|
room.roomRate.member
|
||||||
|
)
|
||||||
|
const isSpecialRate =
|
||||||
|
"corporateCheque" in room.roomRate ||
|
||||||
|
"redemption" in room.roomRate ||
|
||||||
|
"voucher" in room.roomRate ||
|
||||||
|
room.roomRate.bookingCode ||
|
||||||
|
room.roomRate.rateDefinition.isCampaignRate
|
||||||
const showDiscounted = isSpecialRate || showMemberPrice
|
const showDiscounted = isSpecialRate || showMemberPrice
|
||||||
|
|
||||||
const adultsMsg = intl.formatMessage(
|
const adultsMsg = intl.formatMessage(
|
||||||
@@ -94,7 +98,7 @@ export default function Room({
|
|||||||
|
|
||||||
let rateDetails = room.rateDetails
|
let rateDetails = room.rateDetails
|
||||||
if (room.memberRateDetails) {
|
if (room.memberRateDetails) {
|
||||||
if (isMember || room.guest.join) {
|
if (showMemberPrice) {
|
||||||
rateDetails = room.memberRateDetails
|
rateDetails = room.memberRateDetails
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,15 +106,13 @@ export default function Room({
|
|||||||
const guests = guestsParts.join(", ")
|
const guests = guestsParts.join(", ")
|
||||||
const zeroPrice = formatPrice(intl, 0, defaultCurrency)
|
const zeroPrice = formatPrice(intl, 0, defaultCurrency)
|
||||||
|
|
||||||
let price = showMemberPrice
|
let price = formatPrice(
|
||||||
? formatPrice(intl, memberPrice.amount, memberPrice.currency)
|
intl,
|
||||||
: formatPrice(
|
room.roomPrice.perStay.local.price,
|
||||||
intl,
|
room.roomPrice.perStay.local.currency,
|
||||||
room.roomPrice.perStay.local.price,
|
room.roomPrice.perStay.local.additionalPrice,
|
||||||
room.roomPrice.perStay.local.currency,
|
room.roomPrice.perStay.local.additionalPriceCurrency
|
||||||
room.roomPrice.perStay.local.additionalPrice,
|
)
|
||||||
room.roomPrice.perStay.local.additionalPriceCurrency
|
|
||||||
)
|
|
||||||
|
|
||||||
let currency: string = room.roomPrice.perStay.local.currency
|
let currency: string = room.roomPrice.perStay.local.currency
|
||||||
const isVoucher = "voucher" in room.roomRate
|
const isVoucher = "voucher" in room.roomRate
|
||||||
@@ -164,12 +166,12 @@ export default function Room({
|
|||||||
>
|
>
|
||||||
{price}
|
{price}
|
||||||
</p>
|
</p>
|
||||||
{showDiscounted && publicPrice ? (
|
{showDiscounted && room.roomPrice.perStay.local.regularPrice ? (
|
||||||
<s className={styles.strikeThroughRate}>
|
<s className={styles.strikeThroughRate}>
|
||||||
{formatPrice(
|
{formatPrice(
|
||||||
intl,
|
intl,
|
||||||
publicPrice.amount,
|
room.roomPrice.perStay.local.regularPrice,
|
||||||
publicPrice.currency
|
currency
|
||||||
)}
|
)}
|
||||||
</s>
|
</s>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default function SummaryUI({
|
|||||||
booking,
|
booking,
|
||||||
rooms,
|
rooms,
|
||||||
totalPrice,
|
totalPrice,
|
||||||
isMember,
|
isUserLoggedIn,
|
||||||
vat,
|
vat,
|
||||||
toggleSummaryOpen,
|
toggleSummaryOpen,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
@@ -59,7 +59,7 @@ export default function SummaryUI({
|
|||||||
const roomOneGuest = rooms[0].room.guest
|
const roomOneGuest = rooms[0].room.guest
|
||||||
const showSignupPromo =
|
const showSignupPromo =
|
||||||
rooms.length === 1 &&
|
rooms.length === 1 &&
|
||||||
!isMember &&
|
!isUserLoggedIn &&
|
||||||
!roomOneGuest.membershipNo &&
|
!roomOneGuest.membershipNo &&
|
||||||
!roomOneGuest.join
|
!roomOneGuest.join
|
||||||
|
|
||||||
@@ -67,13 +67,8 @@ export default function SummaryUI({
|
|||||||
|
|
||||||
const roomOneRoomRate = rooms[0].room.roomRate
|
const roomOneRoomRate = rooms[0].room.roomRate
|
||||||
const isVoucherRate = "voucher" in roomOneRoomRate
|
const isVoucherRate = "voucher" in roomOneRoomRate
|
||||||
// In case of Redemption, voucher and Corporate cheque do not show approx price
|
|
||||||
const isSpecialRate =
|
|
||||||
"corporateCheque" in roomOneRoomRate ||
|
|
||||||
"redemption" in roomOneRoomRate ||
|
|
||||||
isVoucherRate
|
|
||||||
|
|
||||||
const priceDetailsRooms = mapToPrice(rooms, isMember)
|
const priceDetailsRooms = mapToPrice(rooms, isUserLoggedIn)
|
||||||
const isAllCampaignRate = rooms.every(
|
const isAllCampaignRate = rooms.every(
|
||||||
(room) => room.room.roomRate.rateDefinition.isCampaignRate
|
(room) => room.room.roomRate.rateDefinition.isCampaignRate
|
||||||
)
|
)
|
||||||
@@ -83,7 +78,7 @@ export default function SummaryUI({
|
|||||||
const containsBookingCodeRate = rooms.find(
|
const containsBookingCodeRate = rooms.find(
|
||||||
(r) => r && isBookingCodeRate(r.room.roomRate)
|
(r) => r && isBookingCodeRate(r.room.roomRate)
|
||||||
)
|
)
|
||||||
const showDiscounted = containsBookingCodeRate || isMember
|
const showDiscounted = containsBookingCodeRate || isUserLoggedIn
|
||||||
|
|
||||||
const totalCurrency = isVoucherRate
|
const totalCurrency = isVoucherRate
|
||||||
? CurrencyEnum.Voucher
|
? CurrencyEnum.Voucher
|
||||||
@@ -127,8 +122,7 @@ export default function SummaryUI({
|
|||||||
room={room}
|
room={room}
|
||||||
roomNumber={idx + 1}
|
roomNumber={idx + 1}
|
||||||
roomCount={rooms.length}
|
roomCount={rooms.length}
|
||||||
isMember={isMember}
|
isUserLoggedIn={isUserLoggedIn}
|
||||||
isSpecialRate={isSpecialRate}
|
|
||||||
nightsCount={nights}
|
nightsCount={nights}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -192,13 +186,15 @@ export default function SummaryUI({
|
|||||||
</Typography>
|
</Typography>
|
||||||
{showDiscounted && totalPrice.local.regularPrice ? (
|
{showDiscounted && totalPrice.local.regularPrice ? (
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
<s className={styles.strikeThroughRate}>
|
<p>
|
||||||
{formatPrice(
|
<s className={styles.strikeThroughRate}>
|
||||||
intl,
|
{formatPrice(
|
||||||
totalPrice.local.regularPrice,
|
intl,
|
||||||
totalPrice.local.currency
|
totalPrice.local.regularPrice,
|
||||||
)}
|
totalPrice.local.currency
|
||||||
</s>
|
)}
|
||||||
|
</s>
|
||||||
|
</p>
|
||||||
</Typography>
|
</Typography>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
@@ -223,7 +219,7 @@ export default function SummaryUI({
|
|||||||
alignCenter
|
alignCenter
|
||||||
/>
|
/>
|
||||||
<Divider className={styles.bottomDivider} color="Border/Divider/Subtle" />
|
<Divider className={styles.bottomDivider} color="Border/Divider/Subtle" />
|
||||||
{showSignupPromo && roomOneMemberPrice && !isMember ? (
|
{showSignupPromo && roomOneMemberPrice && !isUserLoggedIn ? (
|
||||||
<SignupPromoDesktop
|
<SignupPromoDesktop
|
||||||
memberPrice={roomOneMemberPrice}
|
memberPrice={roomOneMemberPrice}
|
||||||
badgeContent={"✌️"}
|
badgeContent={"✌️"}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { parsePhoneNumberFromString } from "libphonenumber-js"
|
import { parsePhoneNumberFromString } from "libphonenumber-js"
|
||||||
|
|
||||||
|
import { calculateRegularPrice } from "@scandic-hotels/booking-flow/utils/calculateRegularPrice"
|
||||||
import {
|
import {
|
||||||
sumPackages,
|
sumPackages,
|
||||||
sumPackagesRequestedPrice,
|
sumPackagesRequestedPrice,
|
||||||
@@ -495,17 +496,11 @@ export function getRegularPrice(
|
|||||||
(total, room, idx) => {
|
(total, room, idx) => {
|
||||||
const isMainRoomAndMember = idx === 0 && isMember
|
const isMainRoomAndMember = idx === 0 && isMember
|
||||||
const join = Boolean(room.guest.join || room.guest.membershipNo)
|
const join = Boolean(room.guest.join || room.guest.membershipNo)
|
||||||
const getMemberRate = isMainRoomAndMember || join
|
|
||||||
|
|
||||||
const memberRate = "member" in room.roomRate && room.roomRate.member
|
const memberRate = "member" in room.roomRate && room.roomRate.member
|
||||||
const publicRate = "public" in room.roomRate && room.roomRate.public
|
const publicRate = "public" in room.roomRate && room.roomRate.public
|
||||||
|
const useMemberRate = (isMainRoomAndMember || join) && memberRate
|
||||||
|
|
||||||
let rate
|
const rate = useMemberRate ? memberRate : publicRate
|
||||||
if (getMemberRate && memberRate) {
|
|
||||||
rate = memberRate
|
|
||||||
} else if (publicRate) {
|
|
||||||
rate = publicRate
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rate) {
|
if (!rate) {
|
||||||
return total
|
return total
|
||||||
@@ -547,61 +542,23 @@ export function getRegularPrice(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legend:
|
return calculateRegularPrice({
|
||||||
// - total.local.price = Total Price = Black price, what the user pays
|
total,
|
||||||
// - total.local.regularPrice = Regular Price = Strikethrough price (could potentially be none)
|
useMemberRate: !!useMemberRate,
|
||||||
// - total.requested.price = Requested Price = EUR approx price
|
regularMemberPrice: memberRate
|
||||||
|
? {
|
||||||
// We sometimes don't get all the required data to calculate the correct strikethrough total.
|
pricePerStay: memberRate.localPrice.pricePerNight,
|
||||||
// Therefore we try these different approach to get a number that is close
|
regularPricePerStay: memberRate.localPrice.regularPricePerStay,
|
||||||
// enough to the real number if all data would've been present.
|
}
|
||||||
if (getMemberRate && memberRate) {
|
: undefined,
|
||||||
if (publicRate) {
|
regularPublicPrice: publicRate
|
||||||
// #1 Member price uses public price as strikethrough
|
? {
|
||||||
total.local.regularPrice = add(
|
pricePerStay: publicRate.localPrice.pricePerNight,
|
||||||
total.local.regularPrice,
|
regularPricePerStay: publicRate.localPrice.regularPricePerStay,
|
||||||
publicRate.localPrice.pricePerStay,
|
}
|
||||||
additionalCost
|
: undefined,
|
||||||
)
|
additionalCost,
|
||||||
} else if (memberRate.localPrice.regularPricePerStay) {
|
})
|
||||||
// #2 Member price uses member regular price as strikethrough
|
|
||||||
total.local.regularPrice = add(
|
|
||||||
total.local.regularPrice,
|
|
||||||
memberRate.localPrice.regularPricePerStay,
|
|
||||||
additionalCost
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// #3 Member price uses member price as strikethrough
|
|
||||||
// NOTE: If all rooms end up using this, no strikethrough price is shown.
|
|
||||||
total.local.regularPrice = add(
|
|
||||||
total.local.regularPrice,
|
|
||||||
memberRate.localPrice.pricePerStay,
|
|
||||||
additionalCost
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (publicRate) {
|
|
||||||
if (publicRate.localPrice.regularPricePerStay) {
|
|
||||||
// #1 Public price uses public regular price as strikethrough
|
|
||||||
total.local.regularPrice = add(
|
|
||||||
total.local.regularPrice,
|
|
||||||
publicRate.localPrice.regularPricePerStay,
|
|
||||||
additionalCost
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// #2 Public price uses public price as strikethrough
|
|
||||||
// NOTE: If all rooms end up using this, no strikethrough price is shown.
|
|
||||||
total.local.regularPrice = add(
|
|
||||||
total.local.regularPrice,
|
|
||||||
publicRate.localPrice.pricePerStay,
|
|
||||||
additionalCost
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We cannot do anything, too much data is missing.
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
return total
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
local: {
|
local: {
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ export type RoomsData = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SummaryProps {
|
export interface SummaryProps {
|
||||||
isMember: boolean
|
isUserLoggedIn: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EnterDetailsSummaryProps {
|
export interface EnterDetailsSummaryProps {
|
||||||
booking: DetailsBooking
|
booking: DetailsBooking
|
||||||
isMember: boolean
|
isUserLoggedIn: boolean
|
||||||
totalPrice: Price
|
totalPrice: Price
|
||||||
vat: number
|
vat: number
|
||||||
rooms: RoomState[]
|
rooms: RoomState[]
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ export default function BoldRow({
|
|||||||
<td className={styles.price}>
|
<td className={styles.price}>
|
||||||
{isDiscounted && regularValue ? (
|
{isDiscounted && regularValue ? (
|
||||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
<s className={styles.strikeThroughRate}>{regularValue}</s>
|
<p>
|
||||||
|
<s className={styles.strikeThroughRate}>{regularValue}</s>
|
||||||
|
</p>
|
||||||
</Typography>
|
</Typography>
|
||||||
) : null}
|
) : null}
|
||||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ export default function LargeRow({
|
|||||||
{isDiscounted && regularPrice ? (
|
{isDiscounted && regularPrice ? (
|
||||||
<>
|
<>
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
<s className={styles.strikeThroughRate}>{regularPrice}</s>
|
<p>
|
||||||
|
<s className={styles.strikeThroughRate}>{regularPrice}</s>
|
||||||
|
</p>
|
||||||
</Typography>
|
</Typography>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ import styles from "./summaryContent.module.css"
|
|||||||
import type { Price } from "../../../../../../contexts/SelectRate/getTotalPrice"
|
import type { Price } from "../../../../../../contexts/SelectRate/getTotalPrice"
|
||||||
|
|
||||||
export type SelectRateSummaryProps = {
|
export type SelectRateSummaryProps = {
|
||||||
isMember: boolean
|
isUserLoggedIn: boolean
|
||||||
bookingCode?: string
|
bookingCode?: string
|
||||||
toggleSummaryOpen: () => void
|
toggleSummaryOpen: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SummaryContent({
|
export default function SummaryContent({
|
||||||
isMember,
|
isUserLoggedIn,
|
||||||
toggleSummaryOpen,
|
toggleSummaryOpen,
|
||||||
}: SelectRateSummaryProps) {
|
}: SelectRateSummaryProps) {
|
||||||
const { selectedRates, input } = useSelectRateContext()
|
const { selectedRates, input } = useSelectRateContext()
|
||||||
@@ -61,7 +61,7 @@ export default function SummaryContent({
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const showDiscounted = containsBookingCodeRate || isMember
|
const showDiscounted = containsBookingCodeRate || isUserLoggedIn
|
||||||
const totalRegularPrice = selectedRates?.totalPrice?.local?.regularPrice
|
const totalRegularPrice = selectedRates?.totalPrice?.local?.regularPrice
|
||||||
? selectedRates.totalPrice.local.regularPrice
|
? selectedRates.totalPrice.local.regularPrice
|
||||||
: 0
|
: 0
|
||||||
@@ -117,7 +117,7 @@ export default function SummaryContent({
|
|||||||
<Room
|
<Room
|
||||||
key={idx}
|
key={idx}
|
||||||
room={mapToRoom({
|
room={mapToRoom({
|
||||||
isMember,
|
isUserLoggedIn,
|
||||||
rate: room,
|
rate: room,
|
||||||
input,
|
input,
|
||||||
idx,
|
idx,
|
||||||
@@ -126,7 +126,7 @@ export default function SummaryContent({
|
|||||||
})}
|
})}
|
||||||
roomNumber={idx + 1}
|
roomNumber={idx + 1}
|
||||||
roomCount={selectedRates.rates.length}
|
roomCount={selectedRates.rates.length}
|
||||||
isMember={isMember}
|
isMember={isUserLoggedIn && idx === 0}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@@ -192,13 +192,15 @@ export default function SummaryContent({
|
|||||||
showStrikeThroughPrice &&
|
showStrikeThroughPrice &&
|
||||||
selectedRates.totalPrice.local.regularPrice ? (
|
selectedRates.totalPrice.local.regularPrice ? (
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
<s className={styles.strikeThroughRate}>
|
<p>
|
||||||
{formatPrice(
|
<s className={styles.strikeThroughRate}>
|
||||||
intl,
|
{formatPrice(
|
||||||
selectedRates.totalPrice.local.regularPrice,
|
intl,
|
||||||
selectedRates.totalPrice.local.currency
|
selectedRates.totalPrice.local.regularPrice,
|
||||||
)}
|
selectedRates.totalPrice.local.currency
|
||||||
</s>
|
)}
|
||||||
|
</s>
|
||||||
|
</p>
|
||||||
</Typography>
|
</Typography>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
@@ -217,7 +219,7 @@ export default function SummaryContent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mapped = mapToRoom({
|
const mapped = mapToRoom({
|
||||||
isMember,
|
isUserLoggedIn,
|
||||||
rate: room,
|
rate: room,
|
||||||
input,
|
input,
|
||||||
idx,
|
idx,
|
||||||
@@ -231,17 +233,26 @@ export default function SummaryContent({
|
|||||||
) {
|
) {
|
||||||
switch (room.type) {
|
switch (room.type) {
|
||||||
case "regular":
|
case "regular":
|
||||||
|
const memberLocalPrice = room.member?.localPrice
|
||||||
|
? {
|
||||||
|
...room.member.localPrice,
|
||||||
|
regularPricePerStay:
|
||||||
|
room.public?.localPrice?.pricePerStay ||
|
||||||
|
room.member.localPrice.regularPricePerStay,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
return {
|
||||||
|
regular:
|
||||||
|
isMember && memberLocalPrice
|
||||||
|
? memberLocalPrice
|
||||||
|
: room.public?.localPrice,
|
||||||
|
}
|
||||||
|
case "campaign":
|
||||||
return {
|
return {
|
||||||
regular: isMember
|
regular: isMember
|
||||||
? (room.member?.localPrice ?? room.public?.localPrice)
|
? (room.member?.localPrice ?? room.public?.localPrice)
|
||||||
: room.public?.localPrice,
|
: room.public?.localPrice,
|
||||||
}
|
}
|
||||||
case "campaign":
|
|
||||||
return {
|
|
||||||
campaign: isMember
|
|
||||||
? (room.member ?? room.public)
|
|
||||||
: room.public,
|
|
||||||
}
|
|
||||||
case "redemption":
|
case "redemption":
|
||||||
return {
|
return {
|
||||||
redemption: room.redemption,
|
redemption: room.redemption,
|
||||||
@@ -259,10 +270,19 @@ export default function SummaryContent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ("public" in room) {
|
if ("public" in room) {
|
||||||
|
const memberLocalPrice = room.member?.localPrice
|
||||||
|
? {
|
||||||
|
...room.member.localPrice,
|
||||||
|
regularPricePerStay:
|
||||||
|
room.public?.localPrice?.pricePerStay ||
|
||||||
|
room.member.localPrice.regularPricePerStay,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
return {
|
return {
|
||||||
regular: isMember
|
regular:
|
||||||
? (room.member?.localPrice ?? room.public?.localPrice)
|
isMember && memberLocalPrice
|
||||||
: room.public?.localPrice,
|
? memberLocalPrice
|
||||||
|
: room.public?.localPrice,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,7 +291,7 @@ export default function SummaryContent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const p = getPrice(room!, isMember)
|
const p = getPrice(room!, isUserLoggedIn && idx === 0)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...mapped,
|
...mapped,
|
||||||
@@ -293,7 +313,7 @@ export default function SummaryContent({
|
|||||||
vat={selectedRates.vat}
|
vat={selectedRates.vat}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!isMember && memberPrice ? (
|
{!isUserLoggedIn && memberPrice ? (
|
||||||
<SignupPromoDesktop
|
<SignupPromoDesktop
|
||||||
memberPrice={{
|
memberPrice={{
|
||||||
amount: memberPrice.localPrice.pricePerStay,
|
amount: memberPrice.localPrice.pricePerStay,
|
||||||
@@ -307,14 +327,14 @@ export default function SummaryContent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mapToRoom({
|
function mapToRoom({
|
||||||
isMember,
|
isUserLoggedIn,
|
||||||
rate,
|
rate,
|
||||||
input,
|
input,
|
||||||
idx,
|
idx,
|
||||||
getPriceForRoom,
|
getPriceForRoom,
|
||||||
rateTitles,
|
rateTitles,
|
||||||
}: {
|
}: {
|
||||||
isMember: boolean
|
isUserLoggedIn: boolean
|
||||||
rate: NonNullable<
|
rate: NonNullable<
|
||||||
ReturnType<typeof useSelectRateContext>["selectedRates"]["rates"][number]
|
ReturnType<typeof useSelectRateContext>["selectedRates"]["rates"][number]
|
||||||
>
|
>
|
||||||
@@ -323,6 +343,7 @@ function mapToRoom({
|
|||||||
getPriceForRoom: (roomIndex: number) => Price | null
|
getPriceForRoom: (roomIndex: number) => Price | null
|
||||||
rateTitles: ReturnType<typeof useRateTitles>
|
rateTitles: ReturnType<typeof useRateTitles>
|
||||||
}) {
|
}) {
|
||||||
|
const useMemberPrice = isUserLoggedIn && idx === 0
|
||||||
return {
|
return {
|
||||||
adults: input.data?.booking.rooms[idx].adults || 0,
|
adults: input.data?.booking.rooms[idx].adults || 0,
|
||||||
childrenInRoom: input.data?.booking.rooms[idx].childrenInRoom,
|
childrenInRoom: input.data?.booking.rooms[idx].childrenInRoom,
|
||||||
@@ -335,7 +356,7 @@ function mapToRoom({
|
|||||||
local: { price: -1, currency: CurrencyEnum.Unknown },
|
local: { price: -1, currency: CurrencyEnum.Unknown },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rateDetails: isMember
|
rateDetails: useMemberPrice
|
||||||
? (rate.rateDefinitionMember?.generalTerms ??
|
? (rate.rateDefinitionMember?.generalTerms ??
|
||||||
rate.rateDefinition.generalTerms)
|
rate.rateDefinition.generalTerms)
|
||||||
: rate.rateDefinition.generalTerms,
|
: rate.rateDefinition.generalTerms,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
|
|||||||
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
|
import { ChildBedMapEnum } from "@scandic-hotels/trpc/enums/childBedMapEnum"
|
||||||
|
|
||||||
import { isBookingCodeRate } from "../../utils"
|
import { isBookingCodeRate } from "../../utils"
|
||||||
import { getMemberPrice } from "../utils"
|
|
||||||
|
|
||||||
import styles from "./room.module.css"
|
import styles from "./room.module.css"
|
||||||
|
|
||||||
@@ -68,9 +67,7 @@ export default function Room({
|
|||||||
const childBedCrib = childrenBeds?.get(ChildBedMapEnum.IN_CRIB)
|
const childBedCrib = childrenBeds?.get(ChildBedMapEnum.IN_CRIB)
|
||||||
const childBedExtraBed = childrenBeds?.get(ChildBedMapEnum.IN_EXTRA_BED)
|
const childBedExtraBed = childrenBeds?.get(ChildBedMapEnum.IN_EXTRA_BED)
|
||||||
|
|
||||||
const memberPrice = getMemberPrice(room.roomRate)
|
const showDiscounted = isBookingCodeRate(room.roomRate) || isMember
|
||||||
const showMemberPrice = !!(isMember && memberPrice && roomNumber === 1)
|
|
||||||
const showDiscounted = isBookingCodeRate(room.roomRate) || showMemberPrice
|
|
||||||
|
|
||||||
const adultsMsg = intl.formatMessage(
|
const adultsMsg = intl.formatMessage(
|
||||||
{
|
{
|
||||||
@@ -130,25 +127,19 @@ export default function Room({
|
|||||||
[styles.discounted]: showDiscounted,
|
[styles.discounted]: showDiscounted,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{showMemberPrice
|
{formatPrice(
|
||||||
? formatPrice(
|
intl,
|
||||||
intl,
|
room.roomPrice.perStay.local.price,
|
||||||
memberPrice.amount,
|
room.roomPrice.perStay.local.currency,
|
||||||
memberPrice.currency
|
room.roomPrice.perStay.local.additionalPrice,
|
||||||
)
|
room.roomPrice.perStay.local.additionalPriceCurrency
|
||||||
: formatPrice(
|
)}
|
||||||
intl,
|
|
||||||
room.roomPrice.perStay.local.price,
|
|
||||||
room.roomPrice.perStay.local.currency,
|
|
||||||
room.roomPrice.perStay.local.additionalPrice,
|
|
||||||
room.roomPrice.perStay.local.additionalPriceCurrency
|
|
||||||
)}
|
|
||||||
</p>
|
</p>
|
||||||
{showDiscounted && room.roomPrice.perStay.local.price ? (
|
{showDiscounted && room.roomPrice.perStay.local.regularPrice ? (
|
||||||
<s className={styles.strikeThroughRate}>
|
<s className={styles.strikeThroughRate}>
|
||||||
{formatPrice(
|
{formatPrice(
|
||||||
intl,
|
intl,
|
||||||
room.roomPrice.perStay.local.price,
|
room.roomPrice.perStay.local.regularPrice,
|
||||||
room.roomPrice.perStay.local.currency
|
room.roomPrice.perStay.local.currency
|
||||||
)}
|
)}
|
||||||
</s>
|
</s>
|
||||||
|
|||||||
@@ -61,19 +61,12 @@ export function MobileSummary() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalRegularPrice = selectedRates.totalPrice.local?.regularPrice
|
|
||||||
? selectedRates.totalPrice.local.regularPrice
|
|
||||||
: 0
|
|
||||||
|
|
||||||
const showStrikeThroughPrice =
|
|
||||||
totalRegularPrice > selectedRates.totalPrice.local?.price
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper} data-open={isSummaryOpen}>
|
<div className={styles.wrapper} data-open={isSummaryOpen}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.summaryAccordion}>
|
<div className={styles.summaryAccordion}>
|
||||||
<SummaryContent
|
<SummaryContent
|
||||||
isMember={isUserLoggedIn}
|
isUserLoggedIn={isUserLoggedIn}
|
||||||
toggleSummaryOpen={toggleSummaryOpen}
|
toggleSummaryOpen={toggleSummaryOpen}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -106,17 +99,17 @@ export function MobileSummary() {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
{showDiscounted &&
|
{showDiscounted && selectedRates.totalPrice.local?.regularPrice ? (
|
||||||
showStrikeThroughPrice &&
|
|
||||||
selectedRates.totalPrice.local.regularPrice ? (
|
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
<s className={styles.strikeThroughRate}>
|
<p>
|
||||||
{formatPrice(
|
<s className={styles.strikeThroughRate}>
|
||||||
intl,
|
{formatPrice(
|
||||||
selectedRates.totalPrice.local.regularPrice,
|
intl,
|
||||||
selectedRates.totalPrice.local.currency
|
selectedRates.totalPrice.local?.regularPrice,
|
||||||
)}
|
selectedRates.totalPrice.local.currency
|
||||||
</s>
|
)}
|
||||||
|
</s>
|
||||||
|
</p>
|
||||||
</Typography>
|
</Typography>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import type { Product } from "@scandic-hotels/trpc/types/roomAvailability"
|
|
||||||
|
|
||||||
export function getMemberPrice(roomRate: Product) {
|
|
||||||
if ("member" in roomRate && roomRate.member) {
|
|
||||||
return {
|
|
||||||
amount: roomRate.member.localPrice.pricePerStay,
|
|
||||||
currency: roomRate.member.localPrice.currency,
|
|
||||||
pricePerNight: roomRate.member.localPrice.pricePerNight,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -230,7 +230,7 @@ export function SelectRateProvider({
|
|||||||
rate,
|
rate,
|
||||||
roomConfiguration: roomAvailability[ix]?.[0],
|
roomConfiguration: roomAvailability[ix]?.[0],
|
||||||
})),
|
})),
|
||||||
useMemberPrices: isUserLoggedIn,
|
isMember: isUserLoggedIn,
|
||||||
})
|
})
|
||||||
|
|
||||||
const getPriceForRoom = useCallback(
|
const getPriceForRoom = useCallback(
|
||||||
@@ -249,7 +249,8 @@ export function SelectRateProvider({
|
|||||||
selectedRates: [
|
selectedRates: [
|
||||||
{ rate, roomConfiguration: roomAvailability[roomIndex]?.[0] },
|
{ rate, roomConfiguration: roomAvailability[roomIndex]?.[0] },
|
||||||
],
|
],
|
||||||
useMemberPrices: isUserLoggedIn,
|
isMember: isUserLoggedIn && roomIndex === 0,
|
||||||
|
addAdditionalCost: false,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[selectedRates, roomAvailability, isUserLoggedIn]
|
[selectedRates, roomAvailability, isUserLoggedIn]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ describe("getTotalPrice", () => {
|
|||||||
it("should return null when no rates are selected", () => {
|
it("should return null when no rates are selected", () => {
|
||||||
const result = getTotalPrice({
|
const result = getTotalPrice({
|
||||||
selectedRates: [],
|
selectedRates: [],
|
||||||
useMemberPrices: false,
|
isMember: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||||
|
|
||||||
import { sumPackages } from "../../utils/SelectRate"
|
import { calculateRegularPrice } from "../../utils/calculateRegularPrice"
|
||||||
|
import { sumPackages, sumPackagesRequestedPrice } from "../../utils/SelectRate"
|
||||||
|
|
||||||
import type { RedemptionProduct } from "@scandic-hotels/trpc/types/roomAvailability"
|
import type { RedemptionProduct } from "@scandic-hotels/trpc/types/roomAvailability"
|
||||||
|
|
||||||
@@ -26,10 +27,12 @@ type SelectedRate = {
|
|||||||
|
|
||||||
export function getTotalPrice({
|
export function getTotalPrice({
|
||||||
selectedRates,
|
selectedRates,
|
||||||
useMemberPrices,
|
isMember,
|
||||||
|
addAdditionalCost = true,
|
||||||
}: {
|
}: {
|
||||||
selectedRates: Array<SelectedRate | null>
|
selectedRates: Array<SelectedRate | null>
|
||||||
useMemberPrices: boolean
|
isMember: boolean
|
||||||
|
addAdditionalCost?: boolean
|
||||||
}): Price | null {
|
}): Price | null {
|
||||||
const mainRoom = selectedRates[0]
|
const mainRoom = selectedRates[0]
|
||||||
const mainRoomRate = mainRoom?.rate
|
const mainRoomRate = mainRoom?.rate
|
||||||
@@ -42,7 +45,7 @@ export function getTotalPrice({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!mainRoomRate) {
|
if (!mainRoomRate) {
|
||||||
return calculateTotalPrice(summaryArray, useMemberPrices)
|
return calculateTotalPrice(summaryArray, isMember, addAdditionalCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case of reward night (redemption) or voucher only single room booking is supported by business rules
|
// In case of reward night (redemption) or voucher only single room booking is supported by business rules
|
||||||
@@ -59,14 +62,15 @@ export function getTotalPrice({
|
|||||||
return voucherPrice
|
return voucherPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
return calculateTotalPrice(summaryArray, useMemberPrices)
|
return calculateTotalPrice(summaryArray, isMember, addAdditionalCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateTotalPrice(
|
function calculateTotalPrice(
|
||||||
selectedRateSummary: OneLevelNonNullable<SelectedRate>[],
|
selectedRateSummary: OneLevelNonNullable<SelectedRate>[],
|
||||||
useMemberPrices: boolean
|
isMember: boolean,
|
||||||
|
addAdditionalCost: boolean
|
||||||
) {
|
) {
|
||||||
return selectedRateSummary.reduce<Price>(
|
const totalPrice = selectedRateSummary.reduce<Price>(
|
||||||
(total, room, idx) => {
|
(total, room, idx) => {
|
||||||
if (!room.rate || !("member" in room.rate) || !("public" in room.rate)) {
|
if (!room.rate || !("member" in room.rate) || !("public" in room.rate)) {
|
||||||
return total
|
return total
|
||||||
@@ -75,34 +79,25 @@ function calculateTotalPrice(
|
|||||||
const roomNr = idx + 1
|
const roomNr = idx + 1
|
||||||
const isMainRoom = roomNr === 1
|
const isMainRoom = roomNr === 1
|
||||||
|
|
||||||
const useMemberRate = isMainRoom && useMemberPrices && room.rate.member
|
const useMemberRate = isMainRoom && isMember && room.rate.member
|
||||||
const rate = useMemberRate ? room.rate.member : room.rate.public
|
const rate = useMemberRate ? room.rate.member : room.rate.public
|
||||||
|
const publicRate = room.rate.public
|
||||||
|
const memberRate = room.rate.member
|
||||||
|
|
||||||
if (!rate) {
|
if (!rate) {
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
const packagesPrice = room.roomConfiguration?.selectedPackages.reduce(
|
const packagesPrice = addAdditionalCost
|
||||||
(total, pkg) => {
|
? sumPackages(room.roomConfiguration?.selectedPackages)
|
||||||
total.local = total.local + pkg.localPrice.totalPrice
|
: { price: 0, currency: undefined }
|
||||||
if (pkg.requestedPrice.totalPrice) {
|
const packagesRequestedPrice = addAdditionalCost
|
||||||
total.requested = total.requested + pkg.requestedPrice.totalPrice
|
? sumPackagesRequestedPrice(room.roomConfiguration?.selectedPackages)
|
||||||
}
|
: { price: 0, currency: undefined }
|
||||||
return total
|
|
||||||
},
|
|
||||||
{ local: 0, requested: 0 }
|
|
||||||
)
|
|
||||||
|
|
||||||
total.local.currency = rate.localPrice.currency
|
total.local.currency = rate.localPrice.currency
|
||||||
total.local.price =
|
total.local.price =
|
||||||
total.local.price + rate.localPrice.pricePerStay + packagesPrice.local
|
total.local.price + rate.localPrice.pricePerStay + packagesPrice.price
|
||||||
|
|
||||||
if (rate.localPrice.regularPricePerStay) {
|
|
||||||
total.local.regularPrice =
|
|
||||||
(total.local.regularPrice || 0) +
|
|
||||||
rate.localPrice.regularPricePerStay +
|
|
||||||
packagesPrice.local
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rate.requestedPrice) {
|
if (rate.requestedPrice) {
|
||||||
if (!total.requested) {
|
if (!total.requested) {
|
||||||
@@ -119,17 +114,33 @@ function calculateTotalPrice(
|
|||||||
total.requested.price =
|
total.requested.price =
|
||||||
total.requested.price +
|
total.requested.price +
|
||||||
rate.requestedPrice.pricePerStay +
|
rate.requestedPrice.pricePerStay +
|
||||||
packagesPrice.requested
|
packagesRequestedPrice.price
|
||||||
|
|
||||||
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 +
|
||||||
packagesPrice.requested
|
packagesRequestedPrice.price
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return calculateRegularPrice({
|
||||||
|
total,
|
||||||
|
useMemberRate: !!useMemberRate,
|
||||||
|
regularMemberPrice: memberRate
|
||||||
|
? {
|
||||||
|
pricePerStay: memberRate.localPrice.pricePerNight,
|
||||||
|
regularPricePerStay: memberRate.localPrice.regularPricePerStay,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
regularPublicPrice: publicRate
|
||||||
|
? {
|
||||||
|
pricePerStay: publicRate.localPrice.pricePerNight,
|
||||||
|
regularPricePerStay: publicRate.localPrice.regularPricePerStay,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
|
||||||
return total
|
additionalCost: packagesPrice.price,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
local: {
|
local: {
|
||||||
@@ -140,6 +151,15 @@ function calculateTotalPrice(
|
|||||||
requested: undefined,
|
requested: undefined,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
totalPrice.local.regularPrice &&
|
||||||
|
totalPrice.local.price >= totalPrice.local.regularPrice
|
||||||
|
) {
|
||||||
|
totalPrice.local.regularPrice = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateRedemptionTotalPrice(
|
function calculateRedemptionTotalPrice(
|
||||||
@@ -196,6 +216,7 @@ function calculateVoucherPrice(
|
|||||||
local: {
|
local: {
|
||||||
currency: CurrencyEnum.Voucher,
|
currency: CurrencyEnum.Voucher,
|
||||||
price: 0,
|
price: 0,
|
||||||
|
regularPrice: undefined,
|
||||||
},
|
},
|
||||||
requested: undefined,
|
requested: undefined,
|
||||||
}
|
}
|
||||||
|
|||||||
78
packages/booking-flow/lib/utils/calculateRegularPrice.ts
Normal file
78
packages/booking-flow/lib/utils/calculateRegularPrice.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import type { Price } from "../types/price"
|
||||||
|
|
||||||
|
type RegularPrice = {
|
||||||
|
pricePerStay: number
|
||||||
|
regularPricePerStay?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to calculate regular/strikethrough price
|
||||||
|
export function calculateRegularPrice({
|
||||||
|
total,
|
||||||
|
useMemberRate,
|
||||||
|
regularMemberPrice,
|
||||||
|
regularPublicPrice,
|
||||||
|
additionalCost = 0,
|
||||||
|
}: {
|
||||||
|
total: Price
|
||||||
|
useMemberRate: boolean
|
||||||
|
regularMemberPrice: RegularPrice | undefined
|
||||||
|
regularPublicPrice: RegularPrice | undefined
|
||||||
|
additionalCost?: number
|
||||||
|
}) {
|
||||||
|
if (
|
||||||
|
!total ||
|
||||||
|
(!useMemberRate && !regularPublicPrice) ||
|
||||||
|
(useMemberRate && !regularMemberPrice)
|
||||||
|
) {
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
let basePrice = 0
|
||||||
|
// Legend:
|
||||||
|
// - total.local.price = Total Price = Black price, what the user pays
|
||||||
|
// - total.local.regularPrice = Regular Price = Strikethrough price (could potentially be none)
|
||||||
|
// - total.requested.price = Requested Price = EUR approx price
|
||||||
|
|
||||||
|
// We sometimes don't get all the required data to calculate the correct strikethrough total.
|
||||||
|
// Therefore we try these different approach to get a number that is close
|
||||||
|
// enough to the real number if all data would've been present.
|
||||||
|
|
||||||
|
if (useMemberRate && regularMemberPrice) {
|
||||||
|
if (regularPublicPrice) {
|
||||||
|
// #1 Member price uses public price as strikethrough
|
||||||
|
basePrice = regularPublicPrice.pricePerStay
|
||||||
|
} else if (regularMemberPrice.regularPricePerStay) {
|
||||||
|
// #2 Member price uses member regular price as strikethrough
|
||||||
|
basePrice = regularMemberPrice.regularPricePerStay
|
||||||
|
} else {
|
||||||
|
// #3 Member price uses member price as strikethrough
|
||||||
|
basePrice = regularMemberPrice.pricePerStay
|
||||||
|
}
|
||||||
|
} else if (regularPublicPrice) {
|
||||||
|
if (regularPublicPrice.regularPricePerStay) {
|
||||||
|
// #1 Public price uses public regular price as strikethrough
|
||||||
|
basePrice = regularPublicPrice.regularPricePerStay
|
||||||
|
} else {
|
||||||
|
// #2 Public price uses public price as strikethrough
|
||||||
|
basePrice = regularPublicPrice.pricePerStay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total.local.regularPrice = add(
|
||||||
|
total.local.regularPrice,
|
||||||
|
basePrice,
|
||||||
|
additionalCost
|
||||||
|
)
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
//copied from enter-details/helpers.ts
|
||||||
|
export function add(...nums: (number | string | undefined)[]) {
|
||||||
|
return nums.reduce((total: number, num) => {
|
||||||
|
if (typeof num === "undefined") {
|
||||||
|
num = 0
|
||||||
|
}
|
||||||
|
total = total + parseInt(`${num}`)
|
||||||
|
return total
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
@@ -62,6 +62,7 @@
|
|||||||
"./utils/isSameBooking": "./lib/utils/isSameBooking.ts",
|
"./utils/isSameBooking": "./lib/utils/isSameBooking.ts",
|
||||||
"./utils/url": "./lib/utils/url.ts",
|
"./utils/url": "./lib/utils/url.ts",
|
||||||
"./utils/SelectRate": "./lib/utils/SelectRate/index.tsx",
|
"./utils/SelectRate": "./lib/utils/SelectRate/index.tsx",
|
||||||
|
"./utils/calculateRegularPrice": "./lib/utils/calculateRegularPrice.ts",
|
||||||
"./utils/nuqs": "./lib/utils/nuqs.ts"
|
"./utils/nuqs": "./lib/utils/nuqs.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
Reference in New Issue
Block a user