diff --git a/components/HotelReservation/EnterDetails/Breakfast/index.tsx b/components/HotelReservation/EnterDetails/Breakfast/index.tsx index e8868edf7..00d5ab4cc 100644 --- a/components/HotelReservation/EnterDetails/Breakfast/index.tsx +++ b/components/HotelReservation/EnterDetails/Breakfast/index.tsx @@ -76,21 +76,21 @@ export default function Breakfast({ packages }: BreakfastProps) { subtitle={ pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST ? intl.formatMessage( - { id: "breakfast.price.free" }, - { - amount: pkg.originalPrice, - currency: pkg.currency, - free: (str) => {str}, - strikethrough: (str) => {str}, - } - ) + { id: "breakfast.price.free" }, + { + amount: pkg.localPrice.price, + currency: pkg.localPrice.currency, + free: (str) => {str}, + strikethrough: (str) => {str}, + } + ) : intl.formatMessage( - { id: "breakfast.price" }, - { - amount: pkg.packagePrice, - currency: pkg.currency, - } - ) + { id: "breakfast.price" }, + { + amount: pkg.localPrice.price, + currency: pkg.localPrice.currency, + } + ) } text={intl.formatMessage({ id: "All our breakfast buffets offer gluten free, vegan, and allergy-friendly options.", diff --git a/components/HotelReservation/EnterDetails/Summary/index.tsx b/components/HotelReservation/EnterDetails/Summary/index.tsx index 84c6280af..ca2891912 100644 --- a/components/HotelReservation/EnterDetails/Summary/index.tsx +++ b/components/HotelReservation/EnterDetails/Summary/index.tsx @@ -150,8 +150,8 @@ export default function Summary({ {intl.formatMessage( { id: "{amount} {currency}" }, { - amount: chosenBreakfast.totalPrice, - currency: chosenBreakfast.currency, + amount: chosenBreakfast.localPrice.price, + currency: chosenBreakfast.localPrice.currency, } )} diff --git a/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/PriceList/index.tsx b/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/PriceList/index.tsx index dc7ca20fc..c3791d57f 100644 --- a/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/PriceList/index.tsx +++ b/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/PriceList/index.tsx @@ -1,9 +1,14 @@ +import { useSearchParams } from "next/navigation" import { useIntl } from "react-intl" +import { dt } from "@/lib/dt" + import Body from "@/components/TempDesignSystem/Text/Body" import Caption from "@/components/TempDesignSystem/Text/Caption" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" +import { calculatePricesPerNight } from "./utils" + import styles from "./priceList.module.css" import { PriceListProps } from "@/types/components/hotelReservation/selectRate/flexibilityOption" @@ -11,6 +16,7 @@ import { PriceListProps } from "@/types/components/hotelReservation/selectRate/f export default function PriceList({ publicPrice = {}, memberPrice = {}, + petRoomPackage, }: PriceListProps) { const intl = useIntl() @@ -19,7 +25,34 @@ export default function PriceList({ const { localPrice: memberLocalPrice, requestedPrice: memberRequestedPrice } = memberPrice + const petRoomLocalPrice = petRoomPackage?.localPrice + const petRoomRequestedPrice = petRoomPackage?.requestedPrice + const showRequestedPrice = publicRequestedPrice && memberRequestedPrice + const searchParams = useSearchParams() + const fromDate = searchParams.get("fromDate") + const toDate = searchParams.get("toDate") + + let nights = 1 + + if (fromDate && toDate) { + nights = dt(toDate).diff(dt(fromDate), "days") + } + + const { + totalPublicLocalPricePerNight, + totalMemberLocalPricePerNight, + totalPublicRequestedPricePerNight, + totalMemberRequestedPricePerNight, + } = calculatePricesPerNight({ + publicLocalPrice, + memberLocalPrice, + publicRequestedPrice, + memberRequestedPrice, + petRoomLocalPrice, + petRoomRequestedPrice, + nights, + }) return (
@@ -27,7 +60,9 @@ export default function PriceList({
{intl.formatMessage({ id: "Standard price" })} @@ -36,7 +71,7 @@ export default function PriceList({ {publicLocalPrice ? (
- {publicLocalPrice.pricePerNight} + {totalPublicLocalPricePerNight} {publicLocalPrice.currency} @@ -63,7 +98,7 @@ export default function PriceList({ {memberLocalPrice ? (
- {memberLocalPrice.pricePerNight} + {totalMemberLocalPricePerNight} {memberLocalPrice.currency} @@ -91,8 +126,8 @@ export default function PriceList({
{showRequestedPrice ? ( - {publicRequestedPrice.pricePerNight}/ - {memberRequestedPrice.pricePerNight}{" "} + {totalPublicRequestedPricePerNight}/ + {totalMemberRequestedPricePerNight}{" "} {publicRequestedPrice.currency} ) : ( diff --git a/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/PriceList/utils.ts b/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/PriceList/utils.ts new file mode 100644 index 000000000..4043aa652 --- /dev/null +++ b/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/PriceList/utils.ts @@ -0,0 +1,46 @@ +import type { CalculatePricesPerNightProps } from "@/types/components/hotelReservation/selectRate/roomCard" + +export function calculatePricesPerNight({ + publicLocalPrice, + memberLocalPrice, + publicRequestedPrice, + memberRequestedPrice, + petRoomLocalPrice, + petRoomRequestedPrice, + nights, +}: CalculatePricesPerNightProps) { + const totalPublicLocalPricePerNight = publicLocalPrice + ? petRoomLocalPrice + ? Number(publicLocalPrice.pricePerNight) + + Number(petRoomLocalPrice.price) / nights + : Number(publicLocalPrice.pricePerNight) + : undefined + + const totalMemberLocalPricePerNight = memberLocalPrice + ? petRoomLocalPrice + ? Number(memberLocalPrice.pricePerNight) + + Number(petRoomLocalPrice.price) / nights + : Number(memberLocalPrice.pricePerNight) + : undefined + + const totalPublicRequestedPricePerNight = publicRequestedPrice + ? petRoomRequestedPrice + ? Number(publicRequestedPrice.pricePerNight) + + Number(petRoomRequestedPrice.price) / nights + : Number(publicRequestedPrice.pricePerNight) + : undefined + + const totalMemberRequestedPricePerNight = memberRequestedPrice + ? petRoomRequestedPrice + ? Number(memberRequestedPrice.pricePerNight) + + Number(petRoomRequestedPrice.price) / nights + : Number(memberRequestedPrice.pricePerNight) + : undefined + + return { + totalPublicLocalPricePerNight, + totalMemberLocalPricePerNight, + totalPublicRequestedPricePerNight, + totalMemberRequestedPricePerNight, + } +} diff --git a/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/index.tsx b/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/index.tsx index a0a92bb66..f1781d978 100644 --- a/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/index.tsx +++ b/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption/index.tsx @@ -20,6 +20,7 @@ export default function FlexibilityOption({ roomType, roomTypeCode, features, + petRoomPackage, handleSelectRate, }: FlexibilityOptionProps) { const [rootDiv, setRootDiv] = useState(undefined) @@ -113,7 +114,11 @@ export default function FlexibilityOption({ {name} ({paymentTerm}) - + pkg.code === RoomPackageCodeEnum.PET_ROOM ) - const petRoomPrice = petRoomPackage?.calculatedPrice ?? null - const petRoomCurrency = petRoomPackage?.currency ?? null + const petRoomPrice = petRoomPackage?.localPrice.totalPrice ?? null + const petRoomCurrency = petRoomPackage?.localPrice.currency ?? null const checkInDate = new Date(roomsAvailability.checkInDate) const checkOutDate = new Date(roomsAvailability.checkOutDate) - const nights = differenceInCalendarDays(checkOutDate, checkInDate) + const nights = dt(checkOutDate).diff(dt(checkInDate), "days") return (
diff --git a/components/HotelReservation/SelectRate/RoomSelection/RoomCard/index.tsx b/components/HotelReservation/SelectRate/RoomSelection/RoomCard/index.tsx index 4eae4293c..530af45da 100644 --- a/components/HotelReservation/SelectRate/RoomSelection/RoomCard/index.tsx +++ b/components/HotelReservation/SelectRate/RoomSelection/RoomCard/index.tsx @@ -18,12 +18,14 @@ import { getIconForFeatureCode } from "../../utils" import styles from "./roomCard.module.css" import type { RoomCardProps } from "@/types/components/hotelReservation/selectRate/roomCard" +import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" export default function RoomCard({ rateDefinitions, roomConfiguration, roomCategories, selectedPackages, + packages, handleSelectRate, }: RoomCardProps) { const intl = useIntl() @@ -55,6 +57,10 @@ export default function RoomCard({ ?.generalTerms } + const petRoomPackage = packages.find( + (pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM + ) + const selectedRoom = roomCategories.find( (room) => room.name === roomConfiguration.roomType ) @@ -118,6 +124,7 @@ export default function RoomCard({ roomType={roomConfiguration.roomType} roomTypeCode={roomConfiguration.roomTypeCode} features={roomConfiguration.features} + petRoomPackage={petRoomPackage} /> ))}
diff --git a/components/HotelReservation/SelectRate/RoomSelection/index.tsx b/components/HotelReservation/SelectRate/RoomSelection/index.tsx index 38745e241..112f69f76 100644 --- a/components/HotelReservation/SelectRate/RoomSelection/index.tsx +++ b/components/HotelReservation/SelectRate/RoomSelection/index.tsx @@ -72,6 +72,7 @@ export default function RoomSelection({ roomCategories={roomCategories} handleSelectRate={setRateSummary} selectedPackages={selectedPackages} + packages={packages} /> ))} diff --git a/server/routers/hotels/output.ts b/server/routers/hotels/output.ts index 1b49b5fbf..957ed20ad 100644 --- a/server/routers/hotels/output.ts +++ b/server/routers/hotels/output.ts @@ -488,7 +488,7 @@ export type HotelsAvailability = z.infer export type HotelsAvailabilityPrices = HotelsAvailability["data"][number]["attributes"]["bestPricePerNight"] -const priceSchema = z.object({ +export const priceSchema = z.object({ pricePerNight: z.string(), pricePerStay: z.string(), currency: z.string(), @@ -774,17 +774,27 @@ export const apiLocationsSchema = z.object({ ), }) +const breakfastPackagePriceSchema = z + .object({ + currency: z.nativeEnum(CurrencyEnum), + price: z.string(), + totalPrice: z.string(), + }) + .default({ + currency: CurrencyEnum.SEK, + price: "0", + totalPrice: "0", + }) // TODO: Remove optional and default when the API change has been deployed + export const breakfastPackageSchema = z.object({ code: z.string(), - currency: z.nativeEnum(CurrencyEnum), description: z.string(), - originalPrice: z.number().default(0), - packagePrice: z.number(), + localPrice: breakfastPackagePriceSchema, + requestedPrice: breakfastPackagePriceSchema, packageType: z.enum([ PackageTypeEnum.BreakfastAdult, PackageTypeEnum.BreakfastChildren, ]), - totalPrice: z.number(), }) export const breakfastPackagesSchema = z diff --git a/server/routers/hotels/query.ts b/server/routers/hotels/query.ts index 627ad1f8d..a9b382bd0 100644 --- a/server/routers/hotels/query.ts +++ b/server/routers/hotels/query.ts @@ -913,11 +913,16 @@ export const hotelQueryRouter = router({ const { hotelId, startDate, endDate, adults, children, packageCodes } = input + const { lang } = ctx + + const apiLang = toApiLang(lang) + const searchParams = new URLSearchParams({ startDate, endDate, adults: adults.toString(), children: children.toString(), + language: apiLang, }) packageCodes.forEach((code) => { @@ -993,11 +998,17 @@ export const hotelQueryRouter = router({ breakfast: safeProtectedServiceProcedure .input(getBreakfastPackageInputSchema) .query(async function ({ ctx, input }) { + const { lang } = ctx + + const apiLang = toApiLang(lang) + const params = { Adults: input.adults, EndDate: dt(input.toDate).format("YYYY-MM-DD"), StartDate: dt(input.fromDate).format("YYYY-MM-DD"), + language: apiLang, } + const metricsData = { ...params, hotelId: input.hotelId } breakfastPackagesCounter.add(1, metricsData) console.info( @@ -1084,10 +1095,13 @@ export const hotelQueryRouter = router({ const freeBreakfastPackage = breakfastPackages.data.find( (pkg) => pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST ) - if (freeBreakfastPackage) { - if (originalBreakfastPackage) { - freeBreakfastPackage.originalPrice = - originalBreakfastPackage.packagePrice + if (freeBreakfastPackage && freeBreakfastPackage.localPrice) { + if ( + originalBreakfastPackage && + originalBreakfastPackage.localPrice + ) { + freeBreakfastPackage.localPrice.price = + originalBreakfastPackage.localPrice.price } return [freeBreakfastPackage] } diff --git a/server/routers/hotels/schemas/packages.ts b/server/routers/hotels/schemas/packages.ts index 2f12f7255..738da80ce 100644 --- a/server/routers/hotels/schemas/packages.ts +++ b/server/routers/hotels/schemas/packages.ts @@ -1,6 +1,7 @@ import { z } from "zod" import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" +import { CurrencyEnum } from "@/types/enums/currency" export const getRoomPackagesInputSchema = z.object({ hotelId: z.string(), @@ -11,33 +12,40 @@ export const getRoomPackagesInputSchema = z.object({ packageCodes: z.array(z.string()).optional().default([]), }) -const packagesSchema = z.array( - z.object({ - code: z.enum([ - RoomPackageCodeEnum.PET_ROOM, - RoomPackageCodeEnum.ALLERGY_ROOM, - RoomPackageCodeEnum.ACCESSIBILITY_ROOM, - ]), - itemCode: z.string(), - description: z.string(), - currency: z.string(), - calculatedPrice: z.number(), - inventories: z.array( - z.object({ - date: z.string(), - total: z.number(), - available: z.number(), - }) - ), +export const packagePriceSchema = z + .object({ + currency: z.nativeEnum(CurrencyEnum), + price: z.string(), + totalPrice: z.string(), }) -) + .optional() + .default({ + currency: CurrencyEnum.SEK, + price: "0", + totalPrice: "0", + }) // TODO: Remove optional and default when the API change has been deployed + +export const packagesSchema = z.object({ + code: z.nativeEnum(RoomPackageCodeEnum), + itemCode: z.string(), + description: z.string(), + localPrice: packagePriceSchema, + requestedPrice: packagePriceSchema, + inventories: z.array( + z.object({ + date: z.string(), + total: z.number(), + available: z.number(), + }) + ), +}) export const getRoomPackagesSchema = z .object({ data: z.object({ attributes: z.object({ hotelId: z.number(), - packages: packagesSchema, + packages: z.array(packagesSchema), }), relationships: z .object({ diff --git a/types/components/hotelReservation/selectRate/flexibilityOption.ts b/types/components/hotelReservation/selectRate/flexibilityOption.ts index 1a432dc32..811afc139 100644 --- a/types/components/hotelReservation/selectRate/flexibilityOption.ts +++ b/types/components/hotelReservation/selectRate/flexibilityOption.ts @@ -1,14 +1,17 @@ import { z } from "zod" import { + priceSchema, Product, productTypePriceSchema, RoomConfiguration, } from "@/server/routers/hotels/output" +import { RoomPackage } from "./roomFilter" import { Rate } from "./selectRate" type ProductPrice = z.output +export type RoomPriceSchema = z.output export type FlexibilityOptionProps = { product: Product | undefined @@ -19,10 +22,12 @@ export type FlexibilityOptionProps = { roomType: RoomConfiguration["roomType"] roomTypeCode: RoomConfiguration["roomTypeCode"] features: RoomConfiguration["features"] + petRoomPackage: RoomPackage | undefined handleSelectRate: (rate: Rate) => void } export interface PriceListProps { publicPrice?: ProductPrice | Record memberPrice?: ProductPrice | Record + petRoomPackage?: RoomPackage | undefined } diff --git a/types/components/hotelReservation/selectRate/roomCard.ts b/types/components/hotelReservation/selectRate/roomCard.ts index 5af0c4bb1..b47a8c5bb 100644 --- a/types/components/hotelReservation/selectRate/roomCard.ts +++ b/types/components/hotelReservation/selectRate/roomCard.ts @@ -1,17 +1,34 @@ +import { z } from "zod" + import { RateDefinition, RoomConfiguration, } from "@/server/routers/hotels/output" +import { packagePriceSchema } from "@/server/routers/hotels/schemas/packages" +import { RoomPriceSchema } from "./flexibilityOption" import { Rate } from "./selectRate" import type { RoomData } from "@/types/hotel" -import type { RoomPackageCodes } from "./roomFilter" +import type { RoomPackageCodes, RoomPackageData } from "./roomFilter" export type RoomCardProps = { roomConfiguration: RoomConfiguration rateDefinitions: RateDefinition[] roomCategories: RoomData[] selectedPackages: RoomPackageCodes[] + packages: RoomPackageData handleSelectRate: (rate: Rate) => void } + +type RoomPackagePriceSchema = z.output + +export type CalculatePricesPerNightProps = { + publicLocalPrice: RoomPriceSchema + memberLocalPrice: RoomPriceSchema + publicRequestedPrice?: RoomPriceSchema + memberRequestedPrice?: RoomPriceSchema + petRoomLocalPrice?: RoomPackagePriceSchema + petRoomRequestedPrice?: RoomPackagePriceSchema + nights: number +} diff --git a/types/components/hotelReservation/selectRate/roomFilter.ts b/types/components/hotelReservation/selectRate/roomFilter.ts index d42669295..8250e7f32 100644 --- a/types/components/hotelReservation/selectRate/roomFilter.ts +++ b/types/components/hotelReservation/selectRate/roomFilter.ts @@ -1,6 +1,9 @@ import { z } from "zod" -import { getRoomPackagesSchema } from "@/server/routers/hotels/schemas/packages" +import { + getRoomPackagesSchema, + packagesSchema, +} from "@/server/routers/hotels/schemas/packages" export enum RoomPackageCodeEnum { PET_ROOM = "PETR", @@ -17,3 +20,5 @@ export interface RoomPackageData extends z.output {} export type RoomPackageCodes = RoomPackageData[number]["code"] + +export type RoomPackage = z.output