feat: add packages info to summary

This commit is contained in:
Christel Westerberg
2024-11-12 10:06:13 +01:00
parent dbb5367df2
commit dc3516f4e1
16 changed files with 116 additions and 105 deletions

View File

@@ -46,14 +46,17 @@ export default async function SummaryPage({
packageCodes, packageCodes,
}) })
const user = await getProfileSafely() const user = await getProfileSafely()
const packages = await getPackages({
hotelId: hotel, const packages = packageCodes
startDate: fromDate, ? await getPackages({
endDate: toDate, hotelId: hotel,
adults, startDate: fromDate,
children: children?.length, endDate: toDate,
packageCodes, adults,
}) children: children?.length,
packageCodes,
})
: null
if (!availability || !availability.selectedRoom) { if (!availability || !availability.selectedRoom) {
console.error("No hotel or availability data", availability) console.error("No hotel or availability data", availability)
@@ -84,7 +87,6 @@ export default async function SummaryPage({
}, },
} }
console.log({ packages })
return ( return (
<> <>
<div className={styles.mobileSummary}> <div className={styles.mobileSummary}>
@@ -99,6 +101,7 @@ export default async function SummaryPage({
adults, adults,
children, children,
cancellationText: availability.cancellationText, cancellationText: availability.cancellationText,
packages,
}} }}
/> />
</div> </div>
@@ -116,6 +119,7 @@ export default async function SummaryPage({
adults, adults,
children, children,
cancellationText: availability.cancellationText, cancellationText: availability.cancellationText,
packages,
}} }}
/> />
</div> </div>

View File

@@ -46,7 +46,13 @@ export default async function StepPage({
toDate, toDate,
} = getQueryParamsForEnterDetails(selectRoomParams) } = getQueryParamsForEnterDetails(selectRoomParams)
const { adults, children, roomTypeCode, rateCode, packages } = rooms[0] // TODO: Handle multiple rooms const {
adults,
children,
roomTypeCode,
rateCode,
packages: packageCodes,
} = rooms[0] // TODO: Handle multiple rooms
const childrenAsString = children && generateChildrenString(children) const childrenAsString = children && generateChildrenString(children)
@@ -60,7 +66,7 @@ export default async function StepPage({
roomStayEndDate: toDate, roomStayEndDate: toDate,
rateCode, rateCode,
roomTypeCode, roomTypeCode,
packageCodes: packages, packageCodes,
}) })
const roomAvailability = await getSelectedRoomAvailability({ const roomAvailability = await getSelectedRoomAvailability({
@@ -71,7 +77,7 @@ export default async function StepPage({
roomStayEndDate: toDate, roomStayEndDate: toDate,
rateCode, rateCode,
roomTypeCode, roomTypeCode,
packageCodes: packages, packageCodes,
}) })
const hotelData = await getHotelData({ const hotelData = await getHotelData({
hotelId, hotelId,

View File

@@ -29,7 +29,7 @@ export default function Counter({
> >
<MinusIcon color="burgundy" /> <MinusIcon color="burgundy" />
</Button> </Button>
<Body color="textHighContrast" textAlign="center"> <Body color="baseTextHighContrast" textAlign="center">
{count} {count}
</Body> </Body>
<Button <Button

View File

@@ -48,7 +48,7 @@ export default function MyPagesMenu({
onClick={() => toggleDropdown(DropdownTypeEnum.MyPagesMenu)} onClick={() => toggleDropdown(DropdownTypeEnum.MyPagesMenu)}
> >
<Avatar initials={getInitials(user.firstName, user.lastName)} /> <Avatar initials={getInitials(user.firstName, user.lastName)} />
<Body textTransform="bold" color="textHighContrast" asChild> <Body textTransform="bold" color="baseTextHighContrast" asChild>
<span> <span>
{intl.formatMessage({ id: "Hi" })} {user.firstName}! {intl.formatMessage({ id: "Hi" })} {user.firstName}!
</span> </span>

View File

@@ -1,7 +1,6 @@
"use client" "use client"
import { zodResolver } from "@hookform/resolvers/zod" import { zodResolver } from "@hookform/resolvers/zod"
import { isValidPhoneNumber, parsePhoneNumber } from "libphonenumber-js"
import { useRouter, useSearchParams } from "next/navigation" import { useRouter, useSearchParams } from "next/navigation"
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
import { Label as AriaLabel } from "react-aria-components" import { Label as AriaLabel } from "react-aria-components"
@@ -77,6 +76,7 @@ export default function Payment({
countryCode, countryCode,
breakfast, breakfast,
bedType, bedType,
membershipNo,
} = userData } = userData
const { toDate, fromDate, rooms: rooms, hotel } = roomData const { toDate, fromDate, rooms: rooms, hotel } = roomData
@@ -147,17 +147,6 @@ export default function Payment({
(card) => card.id === data.paymentMethod (card) => card.id === data.paymentMethod
) )
let phone: string
let phoneCountryCodePrefix: string | null = null
if (isValidPhoneNumber(phoneNumber)) {
const parsedPhone = parsePhoneNumber(phoneNumber)
phone = parsedPhone.nationalNumber
phoneCountryCodePrefix = parsedPhone.countryCallingCode
} else {
phone = phoneNumber
}
initiateBooking.mutate({ initiateBooking.mutate({
hotelId: hotel, hotelId: hotel,
checkInDate: fromDate, checkInDate: fromDate,
@@ -175,9 +164,9 @@ export default function Payment({
firstName, firstName,
lastName, lastName,
email, email,
phoneCountryCodePrefix, phoneNumber,
phoneNumber: phone,
countryCode, countryCode,
membershipNumber: membershipNo,
}, },
packages: { packages: {
breakfast: breakfast !== BreakfastPackageEnum.NO_BREAKFAST, breakfast: breakfast !== BreakfastPackageEnum.NO_BREAKFAST,
@@ -186,7 +175,8 @@ export default function Payment({
petFriendly: petFriendly:
room.packages?.includes(RoomPackageCodeEnum.PET_ROOM) ?? false, room.packages?.includes(RoomPackageCodeEnum.PET_ROOM) ?? false,
accessibility: accessibility:
room.packages?.includes(RoomPackageCodeEnum.ALLERGY_ROOM) ?? false, room.packages?.includes(RoomPackageCodeEnum.ACCESSIBILITY_ROOM) ??
false,
}, },
smsConfirmationRequested: data.smsConfirmation, smsConfirmationRequested: data.smsConfirmation,
roomPrice, roomPrice,

View File

@@ -35,10 +35,6 @@ function storeSelector(state: EnterDetailsState) {
} }
} }
function parsePrice(price: string | undefined) {
return price ? parseInt(price) : 0
}
export default function Summary({ export default function Summary({
showMemberPrice, showMemberPrice,
room, room,
@@ -74,53 +70,54 @@ export default function Summary({
color = "red" color = "red"
} }
const additionalPackageCost = room.packages?.reduce(
(acc, curr) => {
acc.local = acc.local + parseInt(curr.localPrice.totalPrice)
acc.euro = acc.euro + parseInt(curr.requestedPrice.totalPrice)
return acc
},
{ local: 0, euro: 0 }
) || { local: 0, euro: 0 }
const roomsPriceLocal = room.localPrice.price + additionalPackageCost.local
const roomsPriceEuro = room.euroPrice.price + additionalPackageCost.euro
useEffect(() => { useEffect(() => {
setChosenBed(bedType) setChosenBed(bedType)
setChosenBreakfast(breakfast)
if (breakfast) { if (breakfast && breakfast !== BreakfastPackageEnum.NO_BREAKFAST) {
setChosenBreakfast(breakfast) setTotalPrice({
if (breakfast === BreakfastPackageEnum.NO_BREAKFAST) { local: {
setTotalPrice({ price: roomsPriceLocal + parseInt(breakfast.localPrice.totalPrice),
local: { currency: room.localPrice.currency,
price: parsePrice(room.localPrice.price), },
currency: room.localPrice.currency, euro: {
}, price: roomsPriceEuro + parseInt(breakfast.requestedPrice.totalPrice),
euro: { currency: room.euroPrice.currency,
price: parsePrice(room.euroPrice.price), },
currency: room.euroPrice.currency, })
}, } else {
}) setTotalPrice({
} else { local: {
setTotalPrice({ price: roomsPriceLocal,
local: { currency: room.localPrice.currency,
price: },
parsePrice(room.localPrice.price) + euro: {
parsePrice(breakfast.localPrice.totalPrice), price: roomsPriceEuro,
currency: room.localPrice.currency, currency: room.euroPrice.currency,
}, },
euro: { })
price:
parsePrice(room.euroPrice.price) +
parsePrice(breakfast.requestedPrice.totalPrice),
currency: room.euroPrice.currency,
},
})
}
} }
}, [bedType, breakfast, room.localPrice, room.euroPrice, setTotalPrice]) }, [
bedType,
useEffect(() => { breakfast,
setTotalPrice({ roomsPriceLocal,
local: { room.localPrice.currency,
price: parsePrice(room.localPrice.price), room.euroPrice.currency,
currency: room.localPrice.currency, roomsPriceEuro,
}, setTotalPrice,
euro: { ])
price: parsePrice(room.euroPrice.price),
currency: room.euroPrice.currency,
},
})
}, [room.localPrice, room.euroPrice, setTotalPrice])
return ( return (
<section className={styles.summary}> <section className={styles.summary}>
@@ -146,14 +143,12 @@ export default function Summary({
<div className={styles.addOns}> <div className={styles.addOns}>
<div> <div>
<div className={styles.entry}> <div className={styles.entry}>
<Body color="textHighContrast">{room.roomType}</Body> <Body color="uiTextHighContrast">{room.roomType}</Body>
<Caption color={color}> <Caption color={color}>
{intl.formatMessage( {intl.formatMessage(
{ id: "{amount} {currency}" }, { id: "{amount} {currency}" },
{ {
amount: intl.formatNumber( amount: intl.formatNumber(room.localPrice.price),
parseInt(room.localPrice.price ?? "0")
),
currency: room.localPrice.currency, currency: room.localPrice.currency,
} }
)} )}
@@ -180,17 +175,37 @@ export default function Summary({
{intl.formatMessage({ id: "Rate details" })} {intl.formatMessage({ id: "Rate details" })}
</Link> </Link>
</div> </div>
{room.packages
? room.packages.map((roomPackage) => (
<div className={styles.entry} key={roomPackage.code}>
<div>
<Body color="uiTextHighContrast">
{roomPackage.description}
</Body>
</div>
<Caption color="uiTextHighContrast">
{intl.formatMessage(
{ id: "{amount} {currency}" },
{
amount: roomPackage.localPrice.price,
currency: roomPackage.localPrice.currency,
}
)}
</Caption>
</div>
))
: null}
{chosenBed ? ( {chosenBed ? (
<div className={styles.entry}> <div className={styles.entry}>
<div> <div>
<Body color="textHighContrast">{chosenBed.description}</Body> <Body color="uiTextHighContrast">{chosenBed.description}</Body>
<Caption color="uiTextMediumContrast"> <Caption color="uiTextMediumContrast">
{intl.formatMessage({ id: "Based on availability" })} {intl.formatMessage({ id: "Based on availability" })}
</Caption> </Caption>
</div> </div>
<Caption color="uiTextMediumContrast"> <Caption color="uiTextHighContrast">
{intl.formatMessage( {intl.formatMessage(
{ id: "{amount} {currency}" }, { id: "{amount} {currency}" },
{ amount: "0", currency: room.localPrice.currency } { amount: "0", currency: room.localPrice.currency }
@@ -202,10 +217,10 @@ export default function Summary({
{chosenBreakfast ? ( {chosenBreakfast ? (
chosenBreakfast === BreakfastPackageEnum.NO_BREAKFAST ? ( chosenBreakfast === BreakfastPackageEnum.NO_BREAKFAST ? (
<div className={styles.entry}> <div className={styles.entry}>
<Body color="textHighContrast"> <Body color="uiTextHighContrast">
{intl.formatMessage({ id: "No breakfast" })} {intl.formatMessage({ id: "No breakfast" })}
</Body> </Body>
<Caption color="uiTextMediumContrast"> <Caption color="uiTextHighContrast">
{intl.formatMessage( {intl.formatMessage(
{ id: "{amount} {currency}" }, { id: "{amount} {currency}" },
{ amount: "0", currency: room.localPrice.currency } { amount: "0", currency: room.localPrice.currency }
@@ -214,10 +229,10 @@ export default function Summary({
</div> </div>
) : ( ) : (
<div className={styles.entry}> <div className={styles.entry}>
<Body color="textHighContrast"> <Body color="uiTextHighContrast">
{intl.formatMessage({ id: "Breakfast buffet" })} {intl.formatMessage({ id: "Breakfast buffet" })}
</Body> </Body>
<Caption color="uiTextMediumContrast"> <Caption color="uiTextHighContrast">
{intl.formatMessage( {intl.formatMessage(
{ id: "{amount} {currency}" }, { id: "{amount} {currency}" },
{ {

View File

@@ -41,7 +41,7 @@ export default function HotelSelectionHeader({
<Divider variant="vertical" color="subtle" /> <Divider variant="vertical" color="subtle" />
</div> </div>
<div className={styles.descriptionContainer}> <div className={styles.descriptionContainer}>
<Body color="textHighContrast"> <Body color="baseTextHighContrast">
{hotel.hotelContent.texts.descriptions.short} {hotel.hotelContent.texts.descriptions.short}
</Body> </Body>
</div> </div>

View File

@@ -73,7 +73,7 @@ export default function HotelListingMapContent({
</span> </span>
<Body <Body
asChild asChild
color={isActiveOrHovered ? "white" : "textHighContrast"} color={isActiveOrHovered ? "white" : "baseTextHighContrast"}
> >
<span> <span>
{pin.memberPrice} {pin.currency} {pin.memberPrice} {pin.currency}

View File

@@ -80,7 +80,7 @@
color: var(--Base-Text-Medium-contrast); color: var(--Base-Text-Medium-contrast);
} }
.textHighContrast { .baseTextHighContrast {
color: var(--Base-Text-High-contrast); color: var(--Base-Text-High-contrast);
} }

View File

@@ -13,7 +13,7 @@ const config = {
red: styles.red, red: styles.red,
textMediumContrast: styles.textMediumContrast, textMediumContrast: styles.textMediumContrast,
baseTextMediumContrast: styles.baseTextMediumContrast, baseTextMediumContrast: styles.baseTextMediumContrast,
textHighContrast: styles.textHighContrast, baseTextHighContrast: styles.baseTextHighContrast,
white: styles.white, white: styles.white,
peach50: styles.peach50, peach50: styles.peach50,
uiTextHighContrast: styles.uiTextHighContrast, uiTextHighContrast: styles.uiTextHighContrast,

View File

@@ -14,13 +14,11 @@ const roomsSchema = z.array(
) )
.default([]), .default([]),
rateCode: z.string(), rateCode: z.string(),
roomTypeCode: z.string(), roomTypeCode: z.coerce.string(),
guest: z.object({ guest: z.object({
title: z.string(),
firstName: z.string(), firstName: z.string(),
lastName: z.string(), lastName: z.string(),
email: z.string().email(), email: z.string().email(),
phoneCountryCodePrefix: z.string().nullable(),
phoneNumber: z.string(), phoneNumber: z.string(),
countryCode: z.string(), countryCode: z.string(),
membershipNumber: z.string().optional(), membershipNumber: z.string().optional(),

View File

@@ -541,8 +541,8 @@ export type HotelsAvailabilityPrices =
HotelsAvailability["data"][number]["attributes"]["bestPricePerNight"] HotelsAvailability["data"][number]["attributes"]["bestPricePerNight"]
export const priceSchema = z.object({ export const priceSchema = z.object({
pricePerNight: z.string(), pricePerNight: z.coerce.number(),
pricePerStay: z.string(), pricePerStay: z.coerce.number(),
currency: z.string(), currency: z.string(),
}) })

View File

@@ -724,8 +724,6 @@ export const hotelQueryRouter = router({
ctx.serviceToken ctx.serviceToken
) )
console.log({ packageCodes })
const availableRooms = const availableRooms =
validateAvailabilityData.data.roomConfigurations.filter((room) => { validateAvailabilityData.data.roomConfigurations.filter((room) => {
if (packageCodes) { if (packageCodes) {
@@ -740,7 +738,6 @@ export const hotelQueryRouter = router({
return room.status === "Available" return room.status === "Available"
}) })
console.log("hrteij", JSON.stringify(availableRooms, null, 4))
const selectedRoom = availableRooms.find( const selectedRoom = availableRooms.find(
(room) => room.roomTypeCode === roomTypeCode (room) => room.roomTypeCode === roomTypeCode
) )
@@ -748,11 +745,6 @@ export const hotelQueryRouter = router({
const availableRoomsInCategory = availableRooms.filter( const availableRoomsInCategory = availableRooms.filter(
(room) => room.roomType === selectedRoom?.roomType (room) => room.roomType === selectedRoom?.roomType
) )
console.log(
"availableRoomsInCategory",
JSON.stringify(availableRoomsInCategory, null, 4)
)
if (!selectedRoom) { if (!selectedRoom) {
console.error("No matching room found") console.error("No matching room found")
return null return null

View File

@@ -1,6 +1,8 @@
import { RoomPackageCodeEnum } from "../selectRate/roomFilter" import { RoomPackageCodeEnum } from "../selectRate/roomFilter"
import { Child } from "../selectRate/selectRate" import { Child } from "../selectRate/selectRate"
import { Packages } from "@/types/requests/packages"
interface Room { interface Room {
adults: number adults: number
roomTypeCode: string roomTypeCode: string
@@ -16,7 +18,7 @@ export interface BookingData {
} }
type Price = { type Price = {
price: string price: number
currency: string currency: string
} }
@@ -27,4 +29,5 @@ export type RoomsData = {
adults: number adults: number
children?: Child[] children?: Child[]
cancellationText: string cancellationText: string
packages: Packages | null
} }

View File

@@ -28,7 +28,7 @@ export interface BreakfastSelectionProps extends SectionProps {
export interface DetailsProps extends SectionProps {} export interface DetailsProps extends SectionProps {}
export interface PaymentProps { export interface PaymentProps {
roomPrice: string roomPrice: number
otherPaymentOptions: string[] otherPaymentOptions: string[]
savedCreditCards: CreditCard[] | null savedCreditCards: CreditCard[] | null
mustBeGuaranteed: boolean mustBeGuaranteed: boolean

View File

@@ -4,9 +4,12 @@ import {
getBreakfastPackageInputSchema, getBreakfastPackageInputSchema,
getRoomPackagesInputSchema, getRoomPackagesInputSchema,
} from "@/server/routers/hotels/input" } from "@/server/routers/hotels/input"
import { getRoomPackagesSchema } from "@/server/routers/hotels/output"
export interface BreackfastPackagesInput export interface BreackfastPackagesInput
extends z.input<typeof getBreakfastPackageInputSchema> {} extends z.input<typeof getBreakfastPackageInputSchema> {}
export interface PackagesInput export interface PackagesInput
extends z.input<typeof getRoomPackagesInputSchema> {} extends z.input<typeof getRoomPackagesInputSchema> {}
export interface Packages extends z.output<typeof getRoomPackagesSchema> {}