feat: move room charge to top in price details modal
This commit is contained in:
committed by
Michael Zetterberg
parent
194a401a56
commit
a99e434d84
@@ -53,13 +53,62 @@ export default function PriceDetails() {
|
||||
total.local.price = total.local.price + room.totalPrice
|
||||
}
|
||||
|
||||
if (
|
||||
(room.cheques || room.roomPoints || room.vouchers) &&
|
||||
room.roomPrice
|
||||
) {
|
||||
// Corporate Cheque
|
||||
if (room.cheques) {
|
||||
if (room.roomPrice) {
|
||||
total.local.additionalPrice =
|
||||
(total.local.additionalPrice || 0) + room.totalPrice
|
||||
total.local.additionalPriceCurrency = currency
|
||||
} else {
|
||||
const pkgsSum = room.packages.reduce(
|
||||
(total, pkg) => total + pkg.totalPrice,
|
||||
0
|
||||
)
|
||||
total.local.additionalPrice =
|
||||
(total.local.additionalPrice || 0) + pkgsSum
|
||||
const pkgsCurrency = room.packages.find(
|
||||
(pkg) => pkg.currency
|
||||
)?.currency
|
||||
if (!total.local.additionalPriceCurrency) {
|
||||
total.local.additionalPriceCurrency = pkgsCurrency ?? currency
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Redemption
|
||||
if (room.roomPoints) {
|
||||
if (room.roomPrice) {
|
||||
total.local.additionalPrice =
|
||||
(total.local.additionalPrice || 0) + room.totalPrice
|
||||
total.local.additionalPriceCurrency = currency
|
||||
} else {
|
||||
const pkgsSum = room.packages.reduce(
|
||||
(total, pkg) => total + pkg.totalPrice,
|
||||
0
|
||||
)
|
||||
total.local.additionalPrice =
|
||||
(total.local.additionalPrice || 0) + pkgsSum
|
||||
const pkgsCurrency = room.packages.find(
|
||||
(pkg) => pkg.currency
|
||||
)?.currency
|
||||
if (!total.local.additionalPriceCurrency) {
|
||||
total.local.additionalPriceCurrency = pkgsCurrency ?? currency
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Voucher
|
||||
if (room.vouchers && room.packages) {
|
||||
const pkgsSum = room.packages.reduce(
|
||||
(total, pkg) => total + pkg.totalPrice,
|
||||
0
|
||||
)
|
||||
total.local.additionalPrice =
|
||||
(total.local.additionalPrice || 0) + room.totalPrice
|
||||
total.local.additionalPriceCurrency = currency
|
||||
(total.local.additionalPrice || 0) + pkgsSum
|
||||
const pkgsCurrency = room.packages.find((pkg) => pkg.currency)?.currency
|
||||
if (!total.local.additionalPriceCurrency) {
|
||||
total.local.additionalPriceCurrency = pkgsCurrency ?? currency
|
||||
}
|
||||
}
|
||||
|
||||
return total
|
||||
|
||||
@@ -17,20 +17,16 @@ export function mapToPrice(rooms: (Room | null)[], nights: number) {
|
||||
if (room.cheques) {
|
||||
price = {
|
||||
corporateCheque: {
|
||||
additionalPricePerStay: room.totalPrice
|
||||
? room.totalPrice
|
||||
: undefined,
|
||||
currency: room.totalPrice ? room.currencyCode : undefined,
|
||||
additionalPricePerStay: room.roomPrice ? room.roomPrice : undefined,
|
||||
currency: room.roomPrice ? room.currencyCode : undefined,
|
||||
numberOfCheques: room.cheques,
|
||||
},
|
||||
}
|
||||
} else if (room.roomPoints) {
|
||||
price = {
|
||||
redemption: {
|
||||
additionalPricePerStay: room.totalPrice
|
||||
? room.totalPrice
|
||||
: undefined,
|
||||
currency: room.totalPrice ? room.currencyCode : undefined,
|
||||
additionalPricePerStay: room.roomPrice ? room.roomPrice : undefined,
|
||||
currency: room.roomPrice ? room.currencyCode : undefined,
|
||||
pointsPerNight: room.roomPoints / nights,
|
||||
pointsPerStay: room.roomPoints,
|
||||
},
|
||||
@@ -46,7 +42,7 @@ export function mapToPrice(rooms: (Room | null)[], nights: number) {
|
||||
regular: {
|
||||
currency: room.currencyCode,
|
||||
pricePerNight: room.roomPrice / nights,
|
||||
pricePerStay: room.totalPrice,
|
||||
pricePerStay: room.roomPrice,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function mapRoomState(
|
||||
intl,
|
||||
booking.roomPoints,
|
||||
CurrencyEnum.POINTS,
|
||||
booking.totalPrice,
|
||||
booking.roomPrice,
|
||||
booking.currencyCode
|
||||
)
|
||||
} else if (booking.cheques) {
|
||||
@@ -37,7 +37,7 @@ export function mapRoomState(
|
||||
intl,
|
||||
booking.cheques,
|
||||
CurrencyEnum.CC,
|
||||
booking.totalPrice,
|
||||
booking.roomPrice,
|
||||
booking.currencyCode
|
||||
)
|
||||
} else if (booking.vouchers) {
|
||||
|
||||
@@ -9,14 +9,16 @@ import SummaryUI from "./UI"
|
||||
import type { SummaryProps } from "@/types/components/hotelReservation/summary"
|
||||
|
||||
export default function DesktopSummary({ isMember }: SummaryProps) {
|
||||
const {
|
||||
booking,
|
||||
actions: { toggleSummaryOpen },
|
||||
totalPrice,
|
||||
vat,
|
||||
} = useEnterDetailsStore((state) => state)
|
||||
const toggleSummaryOpen = useEnterDetailsStore(
|
||||
(state) => state.actions.toggleSummaryOpen
|
||||
)
|
||||
|
||||
const rooms = useEnterDetailsStore((state) => state.rooms)
|
||||
const { booking, rooms, totalPrice, vat } = useEnterDetailsStore((state) => ({
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
totalPrice: state.totalPrice,
|
||||
vat: state.vat,
|
||||
}))
|
||||
|
||||
return (
|
||||
<SidePanel variant="summary">
|
||||
|
||||
@@ -12,14 +12,16 @@ import styles from "./mobile.module.css"
|
||||
import type { SummaryProps } from "@/types/components/hotelReservation/summary"
|
||||
|
||||
export default function MobileSummary({ isMember }: SummaryProps) {
|
||||
const {
|
||||
booking,
|
||||
actions: { toggleSummaryOpen },
|
||||
totalPrice,
|
||||
vat,
|
||||
} = useEnterDetailsStore((state) => state)
|
||||
const toggleSummaryOpen = useEnterDetailsStore(
|
||||
(state) => state.actions.toggleSummaryOpen
|
||||
)
|
||||
|
||||
const rooms = useEnterDetailsStore((state) => state.rooms)
|
||||
const { booking, rooms, totalPrice, vat } = useEnterDetailsStore((state) => ({
|
||||
booking: state.booking,
|
||||
rooms: state.rooms,
|
||||
totalPrice: state.totalPrice,
|
||||
vat: state.vat,
|
||||
}))
|
||||
|
||||
const showPromo =
|
||||
!isMember &&
|
||||
|
||||
@@ -94,7 +94,7 @@ export default function SummaryUI({
|
||||
? totalPrice.requested.currency === totalPrice.local.currency
|
||||
: false
|
||||
|
||||
const priceDetailsRooms = mapToPrice(rooms, isMember, nights)
|
||||
const priceDetailsRooms = mapToPrice(rooms, isMember)
|
||||
const isAllCampaignRate = rooms.every(
|
||||
(room) => room.room.roomRate.rateDefinition.isCampaignRate
|
||||
)
|
||||
@@ -378,7 +378,12 @@ export default function SummaryUI({
|
||||
<div className={styles.entry}>
|
||||
<Body color="uiTextHighContrast">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Breakfast included",
|
||||
defaultMessage: "Breakfast buffet",
|
||||
})}
|
||||
</Body>
|
||||
<Body color="red">
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Included",
|
||||
})}
|
||||
</Body>
|
||||
</div>
|
||||
|
||||
@@ -2,11 +2,7 @@ import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
|
||||
import type { RoomState } from "@/types/stores/enter-details"
|
||||
|
||||
export function mapToPrice(
|
||||
rooms: RoomState[],
|
||||
isMember: boolean,
|
||||
nights: number
|
||||
) {
|
||||
export function mapToPrice(rooms: RoomState[], isMember: boolean) {
|
||||
return rooms
|
||||
.filter((room) => room && room.room.roomRate)
|
||||
.map(({ room }, idx) => {
|
||||
@@ -26,8 +22,8 @@ export function mapToPrice(
|
||||
corporateCheque: {
|
||||
...room.roomRate.corporateCheque.localPrice,
|
||||
additionalPricePerStay:
|
||||
(room.roomRate.corporateCheque.localPrice
|
||||
.additionalPricePerStay || 0) + pkgsSum.price,
|
||||
room.roomRate.corporateCheque.localPrice
|
||||
.additionalPricePerStay || 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -53,8 +49,8 @@ export function mapToPrice(
|
||||
redemption: {
|
||||
...room.roomRate.redemption.localPrice,
|
||||
additionalPricePerStay:
|
||||
(room.roomRate.redemption.localPrice.additionalPricePerStay ||
|
||||
0) + pkgsSum.price,
|
||||
room.roomRate.redemption.localPrice.additionalPricePerStay ||
|
||||
0,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -88,12 +84,8 @@ export function mapToPrice(
|
||||
price: {
|
||||
regular: {
|
||||
...room.roomRate.member.localPrice,
|
||||
pricePerNight:
|
||||
room.roomRate.member.localPrice.pricePerNight +
|
||||
pkgsSum.price / nights,
|
||||
pricePerStay:
|
||||
room.roomRate.member.localPrice.pricePerStay +
|
||||
pkgsSum.price,
|
||||
pricePerNight: room.roomRate.member.localPrice.pricePerNight,
|
||||
pricePerStay: room.roomRate.member.localPrice.pricePerStay,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -116,11 +108,8 @@ export function mapToPrice(
|
||||
price: {
|
||||
regular: {
|
||||
...room.roomRate.public.localPrice,
|
||||
pricePerNight:
|
||||
room.roomRate.public.localPrice.pricePerNight +
|
||||
pkgsSum.price / nights,
|
||||
pricePerStay:
|
||||
room.roomRate.public.localPrice.pricePerStay + pkgsSum.price,
|
||||
pricePerNight: room.roomRate.public.localPrice.pricePerNight,
|
||||
pricePerStay: room.roomRate.public.localPrice.pricePerStay,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { dt } from "@/lib/dt"
|
||||
|
||||
import { sumPackages } from "../../utils"
|
||||
|
||||
import { PriceTypeEnum } from "@/types/components/hotelReservation/myStay/myStay"
|
||||
import type { Price } from "@/types/components/hotelReservation/price"
|
||||
import { CurrencyEnum } from "@/types/enums/currency"
|
||||
@@ -10,7 +12,7 @@ export function mapToPrice(room: Room) {
|
||||
case PriceTypeEnum.cheque:
|
||||
return {
|
||||
corporateCheque: {
|
||||
additionalPricePerStay: room.totalPrice,
|
||||
additionalPricePerStay: room.roomPrice.perStay.local.price,
|
||||
currency: room.roomPrice.perStay.local.currency,
|
||||
numberOfCheques: room.cheques,
|
||||
},
|
||||
@@ -20,7 +22,7 @@ export function mapToPrice(room: Room) {
|
||||
regular: {
|
||||
currency: room.currencyCode,
|
||||
pricePerNight: room.roomPrice.perNight.local.price,
|
||||
pricePerStay: room.totalPrice,
|
||||
pricePerStay: room.roomPrice.perStay.local.price,
|
||||
},
|
||||
}
|
||||
case PriceTypeEnum.points:
|
||||
@@ -29,7 +31,7 @@ export function mapToPrice(room: Room) {
|
||||
.diff(dt(room.checkInDate).startOf("day"), "days")
|
||||
return {
|
||||
redemption: {
|
||||
additionalPricePerStay: room.totalPrice,
|
||||
additionalPricePerStay: room.roomPrice.perStay.local.price,
|
||||
currency: room.currencyCode,
|
||||
pointsPerNight: room.roomPoints / nights,
|
||||
pointsPerStay: room.roomPoints,
|
||||
@@ -80,7 +82,6 @@ export function calculateTotalPrice(rooms: Room[], currency: CurrencyEnum) {
|
||||
switch (room.priceType) {
|
||||
case PriceTypeEnum.cheque:
|
||||
case PriceTypeEnum.points:
|
||||
case PriceTypeEnum.voucher:
|
||||
{
|
||||
if (room.totalPrice) {
|
||||
total.local.additionalPrice =
|
||||
@@ -88,8 +89,19 @@ export function calculateTotalPrice(rooms: Room[], currency: CurrencyEnum) {
|
||||
}
|
||||
|
||||
if (!total.local.additionalPriceCurrency) {
|
||||
if (room.currencyCode) {
|
||||
total.local.additionalPriceCurrency = room.currencyCode
|
||||
total.local.additionalPriceCurrency = currency
|
||||
}
|
||||
}
|
||||
break
|
||||
case PriceTypeEnum.voucher:
|
||||
{
|
||||
if (room.packages) {
|
||||
const pkgsSum = sumPackages(room.packages)
|
||||
total.local.additionalPrice =
|
||||
(total.local.additionalPrice || 0) + pkgsSum.price
|
||||
|
||||
if (pkgsSum.currency) {
|
||||
total.local.additionalPriceCurrency = pkgsSum.currency
|
||||
} else {
|
||||
total.local.additionalPriceCurrency = currency
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ import { useIntl } from "react-intl"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
@@ -12,15 +10,16 @@ import { CurrencyEnum } from "@/types/enums/currency"
|
||||
|
||||
export default function Cheques({
|
||||
cheques,
|
||||
currencyCode,
|
||||
isCancelled,
|
||||
price,
|
||||
}: {
|
||||
cheques: number
|
||||
currencyCode: CurrencyEnum
|
||||
isCancelled: boolean
|
||||
price: number
|
||||
}) {
|
||||
const intl = useIntl()
|
||||
const currency = useMyStayStore((state) => state.bookedRoom.currencyCode)
|
||||
|
||||
if (!cheques) {
|
||||
return <SkeletonShimmer width="100px" />
|
||||
@@ -31,7 +30,7 @@ export default function Cheques({
|
||||
cheques,
|
||||
CurrencyEnum.CC,
|
||||
price,
|
||||
currency
|
||||
currencyCode
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,24 +3,23 @@ import { useIntl } from "react-intl"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import { CurrencyEnum } from "@/types/enums/currency"
|
||||
|
||||
export default function Vouchers({
|
||||
currencyCode,
|
||||
isCancelled,
|
||||
price,
|
||||
vouchers,
|
||||
}: {
|
||||
currencyCode: CurrencyEnum
|
||||
isCancelled: boolean
|
||||
price?: number
|
||||
vouchers: number
|
||||
}) {
|
||||
const intl = useIntl()
|
||||
const currency = useMyStayStore((state) => state.bookedRoom.currencyCode)
|
||||
|
||||
if (!vouchers) {
|
||||
return <SkeletonShimmer width="100px" />
|
||||
@@ -31,7 +30,7 @@ export default function Vouchers({
|
||||
vouchers,
|
||||
CurrencyEnum.Voucher,
|
||||
price,
|
||||
currency
|
||||
currencyCode
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@@ -39,6 +39,7 @@ export default function PriceType({
|
||||
return (
|
||||
<Cheques
|
||||
cheques={cheques}
|
||||
currencyCode={currencyCode}
|
||||
isCancelled={isCancelled}
|
||||
price={totalPrice}
|
||||
/>
|
||||
@@ -63,6 +64,7 @@ export default function PriceType({
|
||||
case PriceTypeEnum.voucher:
|
||||
return (
|
||||
<Vouchers
|
||||
currencyCode={currencyCode}
|
||||
isCancelled={isCancelled}
|
||||
price={totalPrice}
|
||||
vouchers={vouchers}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
|
||||
import PriceType from "@/components/HotelReservation/MyStay/PriceType"
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
|
||||
import styles from "./details.module.css"
|
||||
|
||||
@@ -17,6 +18,7 @@ export default function PriceDetails() {
|
||||
formattedTotalPrice: state.totalPrice,
|
||||
isCancelled: state.bookedRoom.isCancelled,
|
||||
currencyCode: state.bookedRoom.currencyCode,
|
||||
packages: state.bookedRoom.packages,
|
||||
priceType: state.bookedRoom.priceType,
|
||||
rateDefinition: state.bookedRoom.rateDefinition,
|
||||
roomPoints: state.bookedRoom.roomPoints,
|
||||
@@ -24,6 +26,14 @@ export default function PriceDetails() {
|
||||
vouchers: state.bookedRoom.vouchers,
|
||||
}))
|
||||
|
||||
let totalPrice = pricing.totalPrice
|
||||
// API returns negative values for totalPrice
|
||||
// on voucher bookings (╯°□°)╯︵ ┻━┻
|
||||
if (pricing.vouchers && totalPrice < 0) {
|
||||
const pkgsSum = sumPackages(pricing.packages)
|
||||
totalPrice = pkgsSum.price
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.priceDetails}>
|
||||
<div className={styles.price}>
|
||||
@@ -34,7 +44,7 @@ export default function PriceDetails() {
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<PriceType {...pricing} />
|
||||
<PriceType {...pricing} totalPrice={totalPrice} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
"use client"
|
||||
import { useMyStayStore } from "@/stores/my-stay"
|
||||
|
||||
import { sumPackages } from "../../utils"
|
||||
import PriceType from "../PriceType"
|
||||
|
||||
import type { PriceType as _PriceType } from "@/types/components/hotelReservation/myStay/myStay"
|
||||
|
||||
export default function TotalPrice() {
|
||||
const { bookedRoom, formattedTotalPrice } = useMyStayStore((state) => ({
|
||||
bookedRoom: state.bookedRoom,
|
||||
formattedTotalPrice: state.totalPrice,
|
||||
}))
|
||||
const { bookedRoom, formattedTotalPrice, rooms } = useMyStayStore(
|
||||
(state) => ({
|
||||
bookedRoom: state.bookedRoom,
|
||||
formattedTotalPrice: state.totalPrice,
|
||||
rooms: state.rooms,
|
||||
})
|
||||
)
|
||||
|
||||
const totalCheques = rooms.reduce((total, room) => total + room.cheques, 0)
|
||||
const totalPoints = rooms.reduce((total, room) => total + room.roomPoints, 0)
|
||||
|
||||
let totalPrice = rooms.reduce((total, room) => total + room.totalPrice, 0)
|
||||
if (rooms.some((room) => room.vouchers)) {
|
||||
const pkgsSum = sumPackages(rooms.flatMap((r) => r.packages || []))
|
||||
totalPrice = pkgsSum.price
|
||||
}
|
||||
|
||||
return (
|
||||
<PriceType
|
||||
cheques={bookedRoom.cheques}
|
||||
cheques={totalCheques}
|
||||
formattedTotalPrice={formattedTotalPrice}
|
||||
isCancelled={bookedRoom.isCancelled}
|
||||
currencyCode={bookedRoom.currencyCode}
|
||||
priceType={bookedRoom.priceType}
|
||||
rateDefinition={bookedRoom.rateDefinition}
|
||||
roomPoints={bookedRoom.roomPoints}
|
||||
totalPrice={bookedRoom.totalPrice}
|
||||
roomPoints={totalPoints}
|
||||
totalPrice={totalPrice}
|
||||
vouchers={bookedRoom.vouchers}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -21,7 +21,6 @@ export interface CorporateChequePriceType {
|
||||
|
||||
interface CorporateChequePriceProps extends SharedPriceRowProps {
|
||||
currency: string
|
||||
nights: number
|
||||
price: CorporateChequePriceType["corporateCheque"]
|
||||
}
|
||||
|
||||
@@ -57,9 +56,6 @@ export default function CorporateChequePrice({
|
||||
|
||||
return (
|
||||
<>
|
||||
<RegularRow label={averagePriceTitle} value={averagePricePerNight} />
|
||||
<BedTypeRow bedType={bedType} currency={currency} />
|
||||
<PackagesRow packages={packages} />
|
||||
<BoldRow
|
||||
label={intl.formatMessage({ defaultMessage: "Room charge" })}
|
||||
value={formatPrice(
|
||||
@@ -70,6 +66,11 @@ export default function CorporateChequePrice({
|
||||
additionalCurrency
|
||||
)}
|
||||
/>
|
||||
{nights > 1 ? (
|
||||
<RegularRow label={averagePriceTitle} value={averagePricePerNight} />
|
||||
) : null}
|
||||
<BedTypeRow bedType={bedType} currency={currency} />
|
||||
<PackagesRow packages={packages} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,9 +57,6 @@ export default function RedemptionPrice({
|
||||
|
||||
return (
|
||||
<>
|
||||
<RegularRow label={averagePriceTitle} value={averagePricePerNight} />
|
||||
<BedTypeRow bedType={bedType} currency={currency} />
|
||||
<PackagesRow packages={packages} />
|
||||
<BoldRow
|
||||
label={intl.formatMessage({ defaultMessage: "Room charge" })}
|
||||
value={formatPrice(
|
||||
@@ -70,6 +67,11 @@ export default function RedemptionPrice({
|
||||
additionalCurrency
|
||||
)}
|
||||
/>
|
||||
{nights > 1 ? (
|
||||
<RegularRow label={averagePriceTitle} value={averagePricePerNight} />
|
||||
) : null}
|
||||
<BedTypeRow bedType={bedType} currency={currency} />
|
||||
<PackagesRow packages={packages} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ interface RegularPriceProps extends SharedPriceRowProps {
|
||||
|
||||
export default function RegularPrice({
|
||||
bedType,
|
||||
nights,
|
||||
packages,
|
||||
price,
|
||||
}: RegularPriceProps) {
|
||||
@@ -48,13 +49,15 @@ export default function RegularPrice({
|
||||
|
||||
return (
|
||||
<>
|
||||
<RegularRow label={averagePriceTitle} value={avgeragePricePerNight} />
|
||||
<BedTypeRow bedType={bedType} currency={price.currency} />
|
||||
<PackagesRow packages={packages} />
|
||||
<BoldRow
|
||||
label={intl.formatMessage({ defaultMessage: "Room charge" })}
|
||||
value={roomCharge}
|
||||
/>
|
||||
{nights > 1 ? (
|
||||
<RegularRow label={averagePriceTitle} value={avgeragePricePerNight} />
|
||||
) : null}
|
||||
<BedTypeRow bedType={bedType} currency={price.currency} />
|
||||
<PackagesRow packages={packages} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import BoldRow from "../Bold"
|
||||
@@ -40,37 +39,20 @@ export default function VoucherPrice({
|
||||
const averagePriceTitle = intl.formatMessage({
|
||||
defaultMessage: "Average price per night",
|
||||
})
|
||||
const pkgsSum = sumPackages(packages)
|
||||
|
||||
let additionalPricePerStay
|
||||
if (pkgsSum.price) {
|
||||
additionalPricePerStay = pkgsSum.price
|
||||
}
|
||||
|
||||
const averageAdditionalPricePerNight = additionalPricePerStay
|
||||
? Math.ceil(additionalPricePerStay / nights)
|
||||
: null
|
||||
|
||||
let averagePricePerNight = `${price.numberOfVouchers} ${CurrencyEnum.Voucher}`
|
||||
if (averageAdditionalPricePerNight) {
|
||||
averagePricePerNight = `${averagePricePerNight} + ${averageAdditionalPricePerNight} ${pkgsSum.currency ?? currency}`
|
||||
}
|
||||
const averagePricePerNight = `${price.numberOfVouchers / nights} ${CurrencyEnum.Voucher}`
|
||||
|
||||
return (
|
||||
<>
|
||||
<RegularRow label={averagePriceTitle} value={averagePricePerNight} />
|
||||
<BedTypeRow bedType={bedType} currency={currency} />
|
||||
<PackagesRow packages={packages} />
|
||||
<BoldRow
|
||||
label={intl.formatMessage({ defaultMessage: "Room charge" })}
|
||||
value={formatPrice(
|
||||
intl,
|
||||
price.numberOfVouchers,
|
||||
CurrencyEnum.Voucher,
|
||||
additionalPricePerStay,
|
||||
pkgsSum.currency ?? currency
|
||||
)}
|
||||
value={formatPrice(intl, price.numberOfVouchers, CurrencyEnum.Voucher)}
|
||||
/>
|
||||
{nights > 1 ? (
|
||||
<RegularRow label={averagePriceTitle} value={averagePricePerNight} />
|
||||
) : null}
|
||||
<BedTypeRow bedType={bedType} currency={currency} />
|
||||
<PackagesRow packages={packages} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@ import type { Packages } from "@/types/requests/packages"
|
||||
|
||||
export interface SharedPriceRowProps {
|
||||
bedType: BedTypeSchema | undefined
|
||||
nights: number
|
||||
packages: Packages | null
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ const noVatCurrencies = [
|
||||
CurrencyEnum.CC,
|
||||
CurrencyEnum.POINTS,
|
||||
CurrencyEnum.Voucher,
|
||||
CurrencyEnum.Unknown,
|
||||
]
|
||||
|
||||
export default function VatRow({ totalPrice, vat }: VatProps) {
|
||||
|
||||
@@ -72,15 +72,15 @@ export default function PriceDetailsTable({
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
|
||||
const diff = dt(toDate).diff(fromDate, "days")
|
||||
const nights = intl.formatMessage(
|
||||
const nights = dt(toDate).diff(fromDate, "days")
|
||||
const nightsMsg = intl.formatMessage(
|
||||
{ defaultMessage: "{totalNights, plural, one {# night} other {# nights}}" },
|
||||
{ totalNights: diff }
|
||||
{ totalNights: nights }
|
||||
)
|
||||
|
||||
const arrival = dt(fromDate).locale(lang).format("ddd, D MMM")
|
||||
const departue = dt(toDate).locale(lang).format("ddd, D MMM")
|
||||
const duration = ` ${arrival} - ${departue} (${nights})`
|
||||
const duration = ` ${arrival} - ${departue} (${nightsMsg})`
|
||||
|
||||
const allRoomsPackages: Package[] = rooms
|
||||
.flatMap((r) => r.packages)
|
||||
@@ -153,26 +153,27 @@ export default function PriceDetailsTable({
|
||||
<RegularPrice
|
||||
bedType={room.bedType}
|
||||
packages={room.packages}
|
||||
nights={nights}
|
||||
price={price}
|
||||
/>
|
||||
<CorporateChequePrice
|
||||
bedType={room.bedType}
|
||||
currency={currency}
|
||||
nights={diff}
|
||||
nights={nights}
|
||||
packages={room.packages}
|
||||
price={chequePrice}
|
||||
/>
|
||||
<RedemptionPrice
|
||||
bedType={room.bedType}
|
||||
currency={currency}
|
||||
nights={diff}
|
||||
nights={nights}
|
||||
packages={room.packages}
|
||||
price={redemptionPrice}
|
||||
/>
|
||||
<VoucherPrice
|
||||
bedType={room.bedType}
|
||||
currency={currency}
|
||||
nights={diff}
|
||||
nights={nights}
|
||||
packages={room.packages}
|
||||
price={voucherPrice}
|
||||
/>
|
||||
@@ -184,7 +185,7 @@ export default function PriceDetailsTable({
|
||||
breakfastIncluded={room.breakfastIncluded}
|
||||
childrenInRoom={room.childrenInRoom}
|
||||
currency={currency}
|
||||
nights={diff}
|
||||
nights={nights}
|
||||
/>
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
@@ -128,6 +128,13 @@ export function calculateVoucherPrice(selectedRateSummary: Rate[]) {
|
||||
|
||||
total.local.price = total.local.price + rate.numberOfVouchers
|
||||
|
||||
const pkgsSum = sumPackages(room.packages)
|
||||
if (pkgsSum.price && pkgsSum.currency) {
|
||||
total.local.additionalPrice =
|
||||
(total.local.additionalPrice || 0) + pkgsSum.price
|
||||
total.local.additionalPriceCurrency = pkgsSum.currency
|
||||
}
|
||||
|
||||
return total
|
||||
},
|
||||
{
|
||||
|
||||
@@ -80,6 +80,9 @@ export default function SelectedRoomPanel() {
|
||||
return null
|
||||
}
|
||||
|
||||
const selectedPackagesCurrency = selectedPackages.find(
|
||||
(pkg) => pkg.localPrice.currency
|
||||
)
|
||||
const selectedPackagesPrice = selectedPackages.reduce(
|
||||
(total, pkg) => total + pkg.localPrice.totalPrice,
|
||||
0
|
||||
@@ -104,11 +107,17 @@ export default function SelectedRoomPanel() {
|
||||
} else if ("corporateCheque" in selectedRate.product) {
|
||||
const { localPrice } = selectedRate.product.corporateCheque
|
||||
selectedProduct = `${localPrice.numberOfCheques} ${CurrencyEnum.CC}`
|
||||
if (localPrice.additionalPricePerStay && localPrice.currency) {
|
||||
selectedProduct = `${selectedProduct} + ${localPrice.additionalPricePerStay} ${localPrice.currency}`
|
||||
if (
|
||||
(localPrice.additionalPricePerStay || selectedPackagesPrice) &&
|
||||
localPrice.currency
|
||||
) {
|
||||
selectedProduct = `${selectedProduct} + ${localPrice.additionalPricePerStay + selectedPackagesPrice} ${localPrice.currency}`
|
||||
}
|
||||
} else if ("voucher" in selectedRate.product) {
|
||||
selectedProduct = `${selectedRate.product.voucher.numberOfVouchers} ${CurrencyEnum.Voucher}`
|
||||
if (selectedPackagesPrice && selectedPackagesCurrency) {
|
||||
selectedProduct = `${selectedProduct} + ${selectedPackagesPrice} ${selectedPackagesCurrency}`
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedProduct) {
|
||||
|
||||
@@ -115,6 +115,16 @@ export default function Code({
|
||||
if ("voucher" in product) {
|
||||
const { numberOfVouchers, rateCode } = product.voucher
|
||||
const isSelected = isSelectedVoucher(product, selectedRate, roomTypeCode)
|
||||
|
||||
const voucherMsg = intl
|
||||
.formatMessage({
|
||||
defaultMessage: "Voucher",
|
||||
})
|
||||
.toUpperCase()
|
||||
let price = `${numberOfVouchers} ${voucherMsg}`
|
||||
if (pkgsSum.price) {
|
||||
price = `${price} + ${pkgsSum.price}`
|
||||
}
|
||||
return (
|
||||
<CodeRateCard
|
||||
key={product.rate}
|
||||
@@ -125,12 +135,8 @@ export default function Code({
|
||||
paymentTerm={rateTitles[product.rate].paymentTerm}
|
||||
rate={{
|
||||
label: product.rateDefinition?.title,
|
||||
price: numberOfVouchers.toString(),
|
||||
unit: intl
|
||||
.formatMessage({
|
||||
defaultMessage: "Voucher",
|
||||
})
|
||||
.toUpperCase(),
|
||||
price,
|
||||
unit: pkgsSum.currency ?? "",
|
||||
}}
|
||||
rateTitle={rateTitles[product.rate].title}
|
||||
rateTermDetails={rateTermDetails}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { dt } from "@/lib/dt"
|
||||
import GuestDetails from "@/components/HotelReservation/MyStay/GuestDetails"
|
||||
import PriceType from "@/components/HotelReservation/MyStay/PriceType"
|
||||
import { hasModifiableRate } from "@/components/HotelReservation/MyStay/utils"
|
||||
import { sumPackages } from "@/components/HotelReservation/utils"
|
||||
import ImageGallery from "@/components/ImageGallery"
|
||||
import Accordion from "@/components/TempDesignSystem/Accordion"
|
||||
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
|
||||
@@ -103,6 +104,14 @@ export default function BookedRoomSidePeek({
|
||||
vouchers,
|
||||
} = room
|
||||
|
||||
let totalRoomPrice = totalPrice
|
||||
// API returns negative values for totalPrice
|
||||
// on voucher bookings (╯°□°)╯︵ ┻━┻
|
||||
if (vouchers && totalRoomPrice < 0) {
|
||||
const pkgsSum = sumPackages(packages)
|
||||
totalRoomPrice = pkgsSum.price
|
||||
}
|
||||
|
||||
const fromDate = dt(checkInDate).locale(lang)
|
||||
|
||||
const galleryImages = hotelRoom
|
||||
@@ -382,7 +391,7 @@ export default function BookedRoomSidePeek({
|
||||
currencyCode={currencyCode}
|
||||
rateDefinition={rateDefinition}
|
||||
roomPoints={roomPoints}
|
||||
totalPrice={totalPrice}
|
||||
totalPrice={totalRoomPrice}
|
||||
vouchers={vouchers}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -61,12 +61,29 @@ export default function BookingConfirmationProvider({
|
||||
currencyCode
|
||||
)
|
||||
} else if (totalBookingVouchers) {
|
||||
isVatCurrency = false
|
||||
formattedTotalCost = formatPrice(
|
||||
intl,
|
||||
totalBookingVouchers,
|
||||
CurrencyEnum.Voucher
|
||||
)
|
||||
const room = rooms?.[0]
|
||||
if (room?.packages) {
|
||||
const pkgsSum = room.packages.reduce(
|
||||
(total, pkg) => total + pkg.totalPrice,
|
||||
0
|
||||
)
|
||||
const currency = room.packages.find((pkg) => pkg.currency)?.currency
|
||||
isVatCurrency = false
|
||||
formattedTotalCost = formatPrice(
|
||||
intl,
|
||||
totalBookingVouchers,
|
||||
CurrencyEnum.Voucher,
|
||||
pkgsSum,
|
||||
currency
|
||||
)
|
||||
} else {
|
||||
isVatCurrency = false
|
||||
formattedTotalCost = formatPrice(
|
||||
intl,
|
||||
totalBookingVouchers,
|
||||
CurrencyEnum.Voucher
|
||||
)
|
||||
}
|
||||
}
|
||||
const initialData = {
|
||||
bookingCode,
|
||||
|
||||
@@ -12,6 +12,7 @@ import type { Price } from "@/types/components/hotelReservation/price"
|
||||
import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
import { CurrencyEnum } from "@/types/enums/currency"
|
||||
import { StepEnum } from "@/types/enums/step"
|
||||
import type { Package } from "@/types/requests/packages"
|
||||
import type { PersistedState, RoomState } from "@/types/stores/enter-details"
|
||||
import type { SafeUser } from "@/types/user"
|
||||
|
||||
@@ -368,15 +369,22 @@ export function getTotalPrice(roomRates: RoomRate[], isMember: boolean) {
|
||||
return totalPrice
|
||||
}
|
||||
|
||||
export function calculateVoucherPrice(roomRates: RoomRate[]) {
|
||||
export function calculateVoucherPrice(
|
||||
roomRates: RoomRate[],
|
||||
packages: Package[]
|
||||
) {
|
||||
return roomRates.reduce<Price>(
|
||||
(total, room) => {
|
||||
if (!("voucher" in room)) {
|
||||
return total
|
||||
}
|
||||
|
||||
const pkgsSum = sumPackages(packages)
|
||||
|
||||
return {
|
||||
local: {
|
||||
additionalPrice: pkgsSum.price,
|
||||
additionalPriceCurrency: pkgsSum.currency,
|
||||
currency: total.local.currency,
|
||||
price: total.local.price + room.voucher.numberOfVouchers,
|
||||
},
|
||||
|
||||
@@ -85,7 +85,8 @@ export function createDetailsStore(
|
||||
roomOneRoomRate.redemption.localPrice.additionalPricePerStay
|
||||
}
|
||||
} else if (isVoucher) {
|
||||
initialTotalPrice = calculateVoucherPrice(initialRoomRates)
|
||||
const pkgs = initialState.rooms.flatMap((room) => room.roomFeatures || [])
|
||||
initialTotalPrice = calculateVoucherPrice(initialRoomRates, pkgs)
|
||||
} else if (isCorpChq) {
|
||||
initialTotalPrice = calculateCorporateChequePrice(initialRoomRates)
|
||||
} else {
|
||||
|
||||
@@ -26,7 +26,9 @@ export function calculateTotalPrice(
|
||||
if (room.totalPoints) {
|
||||
total.points = total.points + room.totalPoints
|
||||
}
|
||||
if (room.totalPrice) {
|
||||
// room.totalPrice is a negative value when
|
||||
// its a vouchers booking (╯°□°)╯︵ ┻━┻
|
||||
if (room.totalPrice && !room.vouchers) {
|
||||
total.cash = total.cash + room.totalPrice
|
||||
}
|
||||
return total
|
||||
|
||||
Reference in New Issue
Block a user