Merged in feat/SW-1589-implement-booking-code-enter (pull request #1368)

Feat/SW-1589 implement booking code enter

* feat: SW-1589 Booking code rate implementation

* feat: SW-1589 Optimized price display

* feat: SW-1589 Display standard price

* feat: SW-1589 Fixed rate title issue


Approved-by: Niclas Edenvin
This commit is contained in:
Hrishikesh Vaipurkar
2025-03-05 09:32:32 +00:00
parent 43d3713f59
commit 39b6774269
17 changed files with 139 additions and 11 deletions

View File

@@ -90,6 +90,7 @@ export default async function DetailsPage({
cancellationText: roomAvailability.cancellationText,
mustBeGuaranteed: roomAvailability.mustBeGuaranteed,
packages,
rateTitle: roomAvailability.rateTitle,
rateDetails: roomAvailability.rateDetails ?? [],
roomType: roomAvailability.selectedRoom.roomType,
roomTypeCode: roomAvailability.selectedRoom.roomTypeCode,

View File

@@ -11,6 +11,7 @@ import {
ArrowRightIcon,
CheckIcon,
ChevronDownSmallIcon,
PriceTagIcon,
} from "@/components/Icons"
import Modal from "@/components/Modal"
import Button from "@/components/TempDesignSystem/Button"
@@ -175,7 +176,10 @@ export default function SummaryUI({
</Caption>
</Button>
}
title={room.cancellationText}
title={
room.rateTitle ? room.rateTitle : room.cancellationText
}
subtitle={room.rateTitle ? room.cancellationText : undefined}
>
<div className={styles.terms}>
{room.rateDetails?.map((info) => (
@@ -366,6 +370,7 @@ export default function SummaryUI({
}))}
totalPrice={totalPrice}
vat={vat}
bookingCode={booking.bookingCode}
/>
</div>
<div>
@@ -376,6 +381,15 @@ export default function SummaryUI({
totalPrice.local.currency
)}
</Body>
{totalPrice.local.regularPrice && (
<Caption color="uiTextMediumContrast" striked={true}>
{formatPrice(
intl,
totalPrice.local.regularPrice,
totalPrice.local.currency
)}
</Caption>
)}
{totalPrice.requested && (
<Caption color="uiTextMediumContrast">
{intl.formatMessage(
@@ -392,6 +406,12 @@ export default function SummaryUI({
)}
</div>
</div>
{booking.bookingCode && (
<div>
<PriceTagIcon />
{booking.bookingCode}
</div>
)}
<Divider className={styles.bottomDivider} color="primaryLightSubtle" />
</div>
{showSignupPromo && memberPrice && !isMember ? (

View File

@@ -5,6 +5,7 @@ import { useIntl } from "react-intl"
import { dt } from "@/lib/dt"
import { PriceTagIcon } from "@/components/Icons"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import useLang from "@/hooks/useLang"
@@ -75,6 +76,7 @@ interface PriceDetailsTableProps {
}[]
totalPrice: Price
vat: number
bookingCode?: string
}
export default function PriceDetailsTable({
@@ -83,6 +85,7 @@ export default function PriceDetailsTable({
rooms,
totalPrice,
vat,
bookingCode,
}: PriceDetailsTableProps) {
const intl = useIntl()
const lang = useLang()
@@ -229,6 +232,29 @@ export default function PriceDetailsTable({
</Body>
</td>
</tr>
{totalPrice.local.regularPrice && (
<tr className={styles.row}>
<td></td>
<td className={styles.price}>
<Caption color="uiTextMediumContrast" striked={true}>
{formatPrice(
intl,
totalPrice.local.regularPrice,
totalPrice.local.currency
)}
</Caption>
</td>
</tr>
)}
{bookingCode && totalPrice.local.regularPrice && (
<tr className={styles.row}>
<td>
<PriceTagIcon />
{bookingCode}
</td>
<td></td>
</tr>
)}
</TableSection>
</table>
)

View File

@@ -29,6 +29,7 @@ interface PriceDetailsModalProps {
}[]
totalPrice: Price
vat: number
bookingCode?: string
}
export default function PriceDetailsModal({
@@ -37,6 +38,7 @@ export default function PriceDetailsModal({
rooms,
totalPrice,
vat,
bookingCode,
}: PriceDetailsModalProps) {
const intl = useIntl()
@@ -58,6 +60,7 @@ export default function PriceDetailsModal({
rooms={rooms}
totalPrice={totalPrice}
vat={vat}
bookingCode={bookingCode}
/>
</Modal>
)

View File

@@ -255,6 +255,7 @@ export default function Summary({
}))}
totalPrice={totalPrice}
vat={vat}
bookingCode={booking.bookingCode}
/>
</div>
<div>
@@ -269,6 +270,15 @@ export default function Summary({
totalPrice.local.currency
)}
</Body>
{booking.bookingCode && totalPrice.local.regularPrice && (
<Caption color="uiTextMediumContrast" striked={true}>
{formatPrice(
intl,
totalPrice.local.regularPrice,
totalPrice.local.currency
)}
</Caption>
)}
{totalPrice.requested && (
<Caption color="uiTextMediumContrast">
{intl.formatMessage(

View File

@@ -53,6 +53,7 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
const checkInDate = new Date(roomsAvailability.checkInDate)
const checkOutDate = new Date(roomsAvailability.checkOutDate)
const nights = dt(checkOutDate).diff(dt(checkInDate), "days")
const bookingCode = params.get("bookingCode")
const totalNights = intl.formatMessage(
{ id: "{totalNights, plural, one {# night} other {# nights}}" },
@@ -224,6 +225,19 @@ export default function RateSummary({ isUserLoggedIn }: RateSummaryProps) {
totalPriceToShow.local.currency
)}
</Subtitle>
{bookingCode && totalPriceToShow.local.regularPrice && (
<Caption
textAlign="right"
color="uiTextMediumContrast"
striked={true}
>
{formatPrice(
intl,
totalPriceToShow.local.regularPrice,
totalPriceToShow.local.currency
)}
</Caption>
)}
{totalPriceToShow.requested ? (
<Body color="uiTextMediumContrast">
{intl.formatMessage(

View File

@@ -34,11 +34,17 @@ export const calculateTotalPrice = (
petRoomPrice = Number(petRoomPackage.localPrice.totalPrice)
}
const regularPrice = rate.localPrice.regularPricePerStay
? (total.local.regularPrice || 0) +
(rate.localPrice.regularPricePerStay || 0)
: undefined
return {
local: {
currency: rate.localPrice.currency,
price:
total.local.price + rate.localPrice.pricePerStay + petRoomPrice,
regularPrice,
},
requested: rate.requestedPrice
? {
@@ -56,6 +62,7 @@ export const calculateTotalPrice = (
currency: (selectedRateSummary[0].public?.localPrice.currency ||
selectedRateSummary[0].member?.localPrice.currency)!,
price: 0,
regularPrice: undefined,
},
requested: undefined,
}

View File

@@ -47,6 +47,10 @@ p.caption {
text-transform: uppercase;
}
.striked {
text-decoration: line-through;
}
.baseTextAccent {
color: var(--Base-Text-Accent);
}
@@ -111,6 +115,10 @@ p.caption {
text-align: left;
}
.right {
text-align: right;
}
.green {
color: var(--UI-Semantic-Success);
}

View File

@@ -12,6 +12,7 @@ export default function Caption({
textAlign,
textTransform,
uppercase,
striked,
type,
...props
}: CaptionProps) {
@@ -21,6 +22,7 @@ export default function Caption({
className,
textTransform,
uppercase,
striked,
type,
})
: captionVariants({
@@ -29,6 +31,7 @@ export default function Caption({
textTransform,
textAlign,
uppercase,
striked,
type,
})
return <Comp className={classNames} {...props} />

View File

@@ -34,10 +34,14 @@ const config = {
textAlign: {
center: styles.center,
left: styles.left,
right: styles.right,
},
uppercase: {
true: styles.uppercase,
},
striked: {
true: styles.striked,
},
},
defaultVariants: {
color: "black",
@@ -61,6 +65,9 @@ const fontOnlyConfig = {
uppercase: {
true: styles.uppercase,
},
striked: {
true: styles.striked,
},
},
defaultVariants: {
type: "regular",

View File

@@ -40,6 +40,7 @@ export default function EnterDetailsProvider({
breakfastIncluded: !!room.breakfastIncluded,
cancellationText: room.cancellationText,
rateDetails: room.rateDetails,
rateTitle: room.rateTitle,
roomFeatures: room.packages,
roomRate: room.roomRate,
roomType: room.roomType,

View File

@@ -65,6 +65,7 @@ import type { BedTypeSelection } from "@/types/components/hotelReservation/enter
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
import { HotelTypeEnum } from "@/types/enums/hotelType"
import { RateTypeEnum } from "@/types/enums/rateType"
import type { RequestOptionsWithOutBody } from "@/types/fetch"
import type { HotelDataWithUrl } from "@/types/hotel"
import type {
@@ -511,8 +512,8 @@ export const hotelQueryRouter = router({
adults: adultCount,
...(childArray &&
childArray.length > 0 && {
children: generateChildrenString(childArray),
}),
children: generateChildrenString(childArray),
}),
...(bookingCode && { bookingCode }),
language: apiLang,
}
@@ -754,9 +755,9 @@ export const hotelQueryRouter = router({
type: matchingRoom.mainBed.type,
extraBed: matchingRoom.fixedExtraBed
? {
type: matchingRoom.fixedExtraBed.type,
description: matchingRoom.fixedExtraBed.description,
}
type: matchingRoom.fixedExtraBed.type,
description: matchingRoom.fixedExtraBed.description,
}
: undefined,
}
}
@@ -784,6 +785,11 @@ export const hotelQueryRouter = router({
cancellationText: rateDefinition?.cancellationText ?? "",
mustBeGuaranteed: !!rateDefinition?.mustBeGuaranteed,
breakfastIncluded: !!rateDefinition?.breakfastIncluded,
// Send rate Title when it is a booking code rate
rateTitle:
rateDefinition?.rateType !== RateTypeEnum.Regular
? rateDefinition?.title
: undefined,
memberRate: rates?.member,
publicRate: rates?.public,
bedTypes,
@@ -1114,9 +1120,9 @@ export const hotelQueryRouter = router({
return hotelData
? {
...hotelData,
url: hotelPage?.url ?? null,
}
...hotelData,
url: hotelPage?.url ?? null,
}
: null
})
)

View File

@@ -114,6 +114,7 @@ export function getRoomPrice(roomRate: RoomRate, isMember: boolean) {
local: {
currency: roomRate.publicRate.localPrice.currency,
price: roomRate.publicRate.localPrice.pricePerNight,
regularPrice: roomRate.publicRate.localPrice.regularPricePerNight,
},
},
perStay: {
@@ -124,6 +125,7 @@ export function getRoomPrice(roomRate: RoomRate, isMember: boolean) {
local: {
currency: roomRate.publicRate.localPrice.currency,
price: roomRate.publicRate.localPrice.pricePerStay,
regularPrice: roomRate.publicRate.localPrice.regularPricePerStay,
},
},
}
@@ -136,7 +138,7 @@ export function getRoomPrice(roomRate: RoomRate, isMember: boolean) {
type TotalPrice = {
requested: { currency: string; price: number } | undefined
local: { currency: string; price: number }
local: { currency: string; price: number; regularPrice?: number }
}
export function getTotalPrice(roomRates: RoomRate[], isMember: boolean) {
@@ -165,6 +167,9 @@ export function getTotalPrice(roomRates: RoomRate[], isMember: boolean) {
local: {
currency: rate.localPrice.currency,
price: add(total.local.price ?? 0, rate.localPrice.pricePerStay),
regularPrice: rate.localPrice.regularPricePerStay
? add(total.local.regularPrice, rate.localPrice.regularPricePerStay)
: total.local.regularPrice,
},
}
},
@@ -232,6 +237,12 @@ export function calcTotalPrice(
breakfastLocalPrice * room.adults * nights,
roomFeaturesTotal
),
regularPrice: add(
acc.local.regularPrice,
roomPrice.perStay.local.regularPrice,
breakfastLocalPrice * room.adults * nights,
roomFeaturesTotal
),
},
}

View File

@@ -258,6 +258,7 @@ export function createDetailsStore(
}
const stateTotalLocalPrice = state.totalPrice.local.price
const stateTotalLocalRegularPrice = state.totalPrice.local.regularPrice
const addToTotalPrice =
(currentRoom.room.breakfast === undefined ||
@@ -290,7 +291,10 @@ export function createDetailsStore(
local: {
currency: breakfast.localPrice.currency,
price: stateTotalLocalPrice + breakfastTotalPrice,
},
regularPrice: stateTotalLocalRegularPrice
? stateTotalLocalRegularPrice + breakfastTotalPrice
: undefined,
},
}
}
@@ -322,6 +326,9 @@ export function createDetailsStore(
if (localPrice < 0) {
localPrice = 0
}
let regularPrice = stateTotalLocalRegularPrice
? stateTotalLocalRegularPrice - currentBreakfastTotalPrice
: undefined
state.totalPrice = {
requested: state.totalPrice.requested && {
@@ -331,6 +338,7 @@ export function createDetailsStore(
local: {
currency,
price: localPrice,
regularPrice,
},
}
}

View File

@@ -1,6 +1,7 @@
interface TPrice {
currency: string
price: number
regularPrice?: number
}
export interface Price {

View File

@@ -9,6 +9,7 @@ export interface Room {
mustBeGuaranteed?: boolean
packages: Packages | null
rateDetails: string[]
rateTitle?: string
roomRate: RoomRate
roomType: string
roomTypeCode: string

View File

@@ -27,6 +27,7 @@ export interface InitialRoomData {
breakfastIncluded: boolean
cancellationText: string
rateDetails: string[] | undefined
rateTitle?: string
roomFeatures: Packages | null
roomRate: RoomRate
roomType: string