feat: SW-2028 Fixed review comments

This commit is contained in:
Hrishikesh Vaipurkar
2025-03-31 16:35:52 +02:00
parent 4e9ee82efa
commit 96fd0b73e4
14 changed files with 214 additions and 195 deletions

View File

@@ -5,13 +5,13 @@ import { useFormContext } from "react-hook-form"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons" import { MaterialIcon } from "@scandic-hotels/design-system/Icons"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { REDEMPTION } from "@/constants/booking" import { REDEMPTION } from "@/constants/booking"
import Modal from "@/components/Modal" import Modal from "@/components/Modal"
import Button from "@/components/TempDesignSystem/Button" import Button from "@/components/TempDesignSystem/Button"
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox" import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption" import Caption from "@/components/TempDesignSystem/Text/Caption"
import { RemoveExtraRooms } from "../BookingCode" import { RemoveExtraRooms } from "../BookingCode"
@@ -97,19 +97,19 @@ export default function RewardNight() {
<MaterialIcon <MaterialIcon
icon="info" icon="info"
size={20} size={20}
color="Icon/Feedback/Information" color="Icon/Interactive/Placeholder"
className={styles.errorIcon} className={styles.errorIcon}
/> />
</Button> </Button>
} }
title={reward} title={reward}
> >
<Body <Typography
color="uiTextHighContrast" variant="Body/Paragraph/mdRegular"
className={styles.rewardNightTooltip} className={styles.rewardNightTooltip}
> >
{rewardNightTooltip} <span>{rewardNightTooltip}</span>
</Body> </Typography>
</Modal> </Modal>
</div> </div>
</Checkbox> </Checkbox>

View File

@@ -138,7 +138,8 @@ export default function PriceDetailsTable({
"corporateCheque" in room.roomRate "corporateCheque" in room.roomRate
? room.roomRate.corporateCheque ? room.roomRate.corporateCheque
: undefined : undefined
const redemptionPrice = "redemption" in room.roomRate ? room.roomRate.redemption : undefined const redemptionPrice =
"redemption" in room.roomRate ? room.roomRate.redemption : undefined
if (!price && !voucherPrice && !chequePrice && !redemptionPrice) { if (!price && !voucherPrice && !chequePrice && !redemptionPrice) {
return null return null
} }
@@ -283,25 +284,18 @@ export default function PriceDetailsTable({
})} })}
<TableSection> <TableSection>
<TableSectionHeader title={intl.formatMessage({ id: "Total" })} /> <TableSectionHeader title={intl.formatMessage({ id: "Total" })} />
{ {!noVatCurrencies.includes(totalPrice.local.currency) ? (
// @ts-expect-error Currency type is string instead of CurrencyEnum. Change also impacts packages <>
!noVatCurrencies.includes(totalPrice.local.currency) ? ( <Row
<> label={intl.formatMessage({ id: "Price excluding VAT" })}
<Row value={formatPrice(intl, priceExclVat, totalPrice.local.currency)}
label={intl.formatMessage({ id: "Price excluding VAT" })} />
value={formatPrice( <Row
intl, label={intl.formatMessage({ id: "VAT {vat}%" }, { vat })}
priceExclVat, value={formatPrice(intl, vatAmount, totalPrice.local.currency)}
totalPrice.local.currency />
)} </>
/> ) : null}
<Row
label={intl.formatMessage({ id: "VAT {vat}%" }, { vat })}
value={formatPrice(intl, vatAmount, totalPrice.local.currency)}
/>
</>
) : null
}
<tr className={styles.row}> <tr className={styles.row}>
<td> <td>
<Body textTransform="bold"> <Body textTransform="bold">

View File

@@ -262,8 +262,7 @@ export default function SummaryUI({
{formatPrice( {formatPrice(
intl, intl,
0, 0,
room.roomPrice.perStay.local.additionalPriceCurrency ?? room.roomPrice.perStay.local.currency
room.roomPrice.perStay.local.currency
)} )}
</Body> </Body>
</div> </div>

View File

@@ -73,7 +73,9 @@ function HotelCard({
availability.productType?.member?.rateType === RateTypeEnum.Regular availability.productType?.member?.rateType === RateTypeEnum.Regular
const price = availability.productType const price = availability.productType
const userHasEnoughPoints = price?.redemptions?.some((r) => r.hasEnoughPoints) const hasInsufficientPoints = !price?.redemptions?.some(
(r) => r.hasEnoughPoints
)
const notEnoughPointsLabel = intl.formatMessage({ id: "Not enough points" }) const notEnoughPointsLabel = intl.formatMessage({ id: "Not enough points" })
return ( return (
@@ -211,7 +213,7 @@ function HotelCard({
))} ))}
</div> </div>
) : null} ) : null}
{price?.redemptions?.length && !userHasEnoughPoints ? ( {price?.redemptions?.length && hasInsufficientPoints ? (
<Tooltip <Tooltip
arrow="left" arrow="left"
position="bottom" position="bottom"

View File

@@ -7,6 +7,7 @@ import styles from "./specification.module.css"
import type { SpecificationProps } from "@/types/components/hotelReservation/myStay/receipt" import type { SpecificationProps } from "@/types/components/hotelReservation/myStay/receipt"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { CurrencyEnum } from "@/types/enums/currency"
export default async function Specification({ export default async function Specification({
ancillaryPackages, ancillaryPackages,
@@ -19,10 +20,10 @@ export default async function Specification({
(p) => p.type === "Breakfast" (p) => p.type === "Breakfast"
) )
const breakfastTotalPriceInMoney = breakfastPackages const breakfastTotalPriceInMoney = breakfastPackages
.filter((p) => p.currency !== "Points") .filter((p) => p.currency !== CurrencyEnum.POINTS)
.reduce((acc, curr) => acc + curr.totalPrice, 0) .reduce((acc, curr) => acc + curr.totalPrice, 0)
const breakfastTotalPriceInPoints = breakfastPackages const breakfastTotalPriceInPoints = breakfastPackages
.filter((p) => p.currency === "Points") .filter((p) => p.currency === CurrencyEnum.POINTS)
.reduce((acc, curr) => acc + curr.totalPrice, 0) .reduce((acc, curr) => acc + curr.totalPrice, 0)
const breakfastCount = breakfastPackages.reduce( const breakfastCount = breakfastPackages.reduce(
@@ -138,7 +139,7 @@ export default async function Specification({
<dl className={styles.dl}> <dl className={styles.dl}>
<Typography variant="Body/Supporting text (caption)/smRegular"> <Typography variant="Body/Supporting text (caption)/smRegular">
<dt> <dt>
{ancillary.currency !== "Points" {ancillary.currency !== CurrencyEnum.POINTS
? intl.formatMessage({ id: "Price including VAT" }) ? intl.formatMessage({ id: "Price including VAT" })
: intl.formatMessage({ id: "Price" })} : intl.formatMessage({ id: "Price" })}
</dt> </dt>

View File

@@ -6,6 +6,7 @@ import { getIntl } from "@/i18n"
import styles from "./total.module.css" import styles from "./total.module.css"
import type { TotalProps } from "@/types/components/hotelReservation/myStay/receipt" import type { TotalProps } from "@/types/components/hotelReservation/myStay/receipt"
import { CurrencyEnum } from "@/types/enums/currency"
export default async function Total({ booking, currency }: TotalProps) { export default async function Total({ booking, currency }: TotalProps) {
const intl = await getIntl() const intl = await getIntl()
@@ -14,7 +15,7 @@ export default async function Total({ booking, currency }: TotalProps) {
const totalPriceInMoneyExclVat = booking.totalPriceExVat const totalPriceInMoneyExclVat = booking.totalPriceExVat
const totalVat = booking.vatAmount const totalVat = booking.vatAmount
const totalPriceInPoints = booking.ancillaries const totalPriceInPoints = booking.ancillaries
.filter((a) => a.currency === "Points") .filter((a) => a.currency === CurrencyEnum.POINTS)
.reduce((acc, curr) => acc + curr.totalPrice, 0) .reduce((acc, curr) => acc + curr.totalPrice, 0)
const moneyString = const moneyString =

View File

@@ -26,6 +26,8 @@ import Total from "./Total"
import styles from "./receipt.module.css" import styles from "./receipt.module.css"
import { CurrencyEnum } from "@/types/enums/currency"
export async function Receipt({ refId }: { refId: string }) { export async function Receipt({ refId }: { refId: string }) {
const value = decrypt(refId) const value = decrypt(refId)
if (!value) { if (!value) {
@@ -52,10 +54,12 @@ export async function Receipt({ refId }: { refId: string }) {
}) })
const currency = const currency =
booking.currencyCode !== "Points" booking.currencyCode !== CurrencyEnum.POINTS
? booking.currencyCode ? booking.currencyCode
: (booking.ancillaries.find((a) => a.currency !== "Points")?.currency ?? : (booking.ancillaries.find((a) => a.currency !== CurrencyEnum.POINTS)
booking.packages.find((p) => p.currency !== "Points")?.currency) ?.currency ??
booking.packages.find((p) => p.currency !== CurrencyEnum.POINTS)
?.currency)
return ( return (
<main className={styles.main}> <main className={styles.main}>

View File

@@ -83,7 +83,7 @@ export function calculateTotalPrice(
}, },
{ {
local: { local: {
currency: "", currency: CurrencyEnum.Unknown,
price: 0, price: 0,
regularPrice: undefined, regularPrice: undefined,
}, },
@@ -103,7 +103,7 @@ export function calculateRedemptionTotalPrice(
additionalPriceCurrency: redemption.localPrice.currency additionalPriceCurrency: redemption.localPrice.currency
? redemption.localPrice.currency ? redemption.localPrice.currency
: undefined, : undefined,
currency: "PTS", currency: CurrencyEnum.POINTS,
price: redemption.localPrice.pointsPerStay, price: redemption.localPrice.pointsPerStay,
}, },
} }

View File

@@ -9,6 +9,8 @@ import {
nullableStringValidator, nullableStringValidator,
} from "@/utils/zod/stringValidator" } from "@/utils/zod/stringValidator"
import { CurrencyEnum } from "@/types/enums/currency"
const guestSchema = z.object({ const guestSchema = z.object({
email: nullableStringEmailValidator, email: nullableStringEmailValidator,
firstName: nullableStringValidator, firstName: nullableStringValidator,
@@ -97,7 +99,7 @@ export const packageSchema = z
unitPrice: z.number(), unitPrice: z.number(),
totalPrice: z.number().nullish(), totalPrice: z.number().nullish(),
totalUnit: z.number().int().nullish(), totalUnit: z.number().int().nullish(),
currency: z.string().default(""), currency: z.nativeEnum(CurrencyEnum).default(CurrencyEnum.Unknown),
points: nullableIntValidator, points: nullableIntValidator,
}), }),
comment: z.string().nullish(), comment: z.string().nullish(),
@@ -218,7 +220,7 @@ export const bookingConfirmationSchema = z
computedReservationStatus: z.string().nullable().default(""), computedReservationStatus: z.string().nullable().default(""),
confirmationNumber: nullableStringValidator, confirmationNumber: nullableStringValidator,
createDateTime: z.date({ coerce: true }), createDateTime: z.date({ coerce: true }),
currencyCode: z.string(), currencyCode: z.nativeEnum(CurrencyEnum),
guest: guestSchema, guest: guestSchema,
linkedReservations: nullableArrayObjectValidator( linkedReservations: nullableArrayObjectValidator(
linkedReservationSchema linkedReservationSchema

View File

@@ -208,12 +208,12 @@ export const getHotel = cache(
} }
) )
export const getHotelsAvailabilityByCity = async ( async function getHotelsAvailabilityByCity(
input: HotelsAvailabilityInputSchema, input: HotelsAvailabilityInputSchema,
apiLang: string, apiLang: string,
token: string, // Either service token or user access token in case of redemption search token: string, // Either service token or user access token in case of redemption search
session?: Session session?: Session
) => { ) {
const { const {
cityId, cityId,
roomStayStartDate, roomStayStartDate,
@@ -223,131 +223,124 @@ export const getHotelsAvailabilityByCity = async (
bookingCode, bookingCode,
redemption, redemption,
} = input } = input
const cacheClient = await getCacheClient()
return await cacheClient.cacheOrGet(
`${cityId}:${roomStayStartDate}:${roomStayEndDate}:${adults}:${children}:${bookingCode}:${redemption ? "isRedemption" : ""}`,
async () => {
const params: Record<string, string | number> = {
roomStayStartDate,
roomStayEndDate,
adults,
...(children && { children }),
...(bookingCode && { bookingCode }),
...(redemption ? { isRedemption: "true" } : {}),
language: apiLang,
}
metrics.hotelsAvailability.counter.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
redemption,
})
console.info(
"api.hotels.hotelsAvailability start",
JSON.stringify({ query: { cityId, params } })
)
const apiResponse = await api.get(
api.endpoints.v1.Availability.city(cityId),
{
headers: {
Authorization: `Bearer ${token}`,
},
},
params
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
metrics.hotelsAvailability.fail.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
}),
})
console.error(
"api.hotels.hotelsAvailability error",
JSON.stringify({
query: { cityId, params },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
},
})
)
throw new Error("Failed to fetch hotels availability by city") const params: Record<string, string | number> = {
} roomStayStartDate,
roomStayEndDate,
const apiJson = await apiResponse.json() adults,
const validateAvailabilityData = ...(children && { children }),
hotelsAvailabilitySchema.safeParse(apiJson) ...(bookingCode && { bookingCode }),
if (!validateAvailabilityData.success) { ...(redemption ? { isRedemption: "true" } : {}),
metrics.hotelsAvailability.fail.add(1, { language: apiLang,
cityId, }
roomStayStartDate, metrics.hotelsAvailability.counter.add(1, {
roomStayEndDate, cityId,
adults, roomStayStartDate,
children, roomStayEndDate,
bookingCode, adults,
redemption, children,
error_type: "validation_error", bookingCode,
error: JSON.stringify(validateAvailabilityData.error), redemption,
}) })
console.error( console.info(
"api.hotels.hotelsAvailability validation error", "api.hotels.hotelsAvailability start",
JSON.stringify({ JSON.stringify({ query: { cityId, params } })
query: { cityId, params },
error: validateAvailabilityData.error,
})
)
throw badRequestError()
}
metrics.hotelsAvailability.success.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
redemption,
})
console.info(
"api.hotels.hotelsAvailability success",
JSON.stringify({
query: { cityId, params: params },
})
)
if (redemption && session) {
const verifiedUser = await getVerifiedUser({ session })
if (!verifiedUser?.error) {
const userPoints = verifiedUser?.data.membership?.currentPoints ?? 0
validateAvailabilityData.data.data.forEach((data) => {
data.attributes.productType?.redemptions?.forEach((r) => {
r.hasEnoughPoints = userPoints >= r.localPrice.pointsPerStay
})
})
}
}
return {
availability: validateAvailabilityData.data.data.flatMap(
(hotels) => hotels.attributes
),
}
},
env.CACHE_TIME_CITY_SEARCH
) )
const apiResponse = await api.get(
api.endpoints.v1.Availability.city(cityId),
{
headers: {
Authorization: `Bearer ${token}`,
},
},
params
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
metrics.hotelsAvailability.fail.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
}),
})
console.error(
"api.hotels.hotelsAvailability error",
JSON.stringify({
query: { cityId, params },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
},
})
)
throw new Error("Failed to fetch hotels availability by city")
}
const apiJson = await apiResponse.json()
const validateAvailabilityData = hotelsAvailabilitySchema.safeParse(apiJson)
if (!validateAvailabilityData.success) {
metrics.hotelsAvailability.fail.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
redemption,
error_type: "validation_error",
error: JSON.stringify(validateAvailabilityData.error),
})
console.error(
"api.hotels.hotelsAvailability validation error",
JSON.stringify({
query: { cityId, params },
error: validateAvailabilityData.error,
})
)
throw badRequestError()
}
metrics.hotelsAvailability.success.add(1, {
cityId,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
redemption,
})
console.info(
"api.hotels.hotelsAvailability success",
JSON.stringify({
query: { cityId, params: params },
})
)
if (redemption && session) {
const verifiedUser = await getVerifiedUser({ session })
if (!verifiedUser?.error) {
const userPoints = verifiedUser?.data.membership?.currentPoints ?? 0
validateAvailabilityData.data.data.forEach((data) => {
data.attributes.productType?.redemptions?.forEach((r) => {
r.hasEnoughPoints = userPoints >= r.localPrice.pointsPerStay
})
})
}
}
return {
availability: validateAvailabilityData.data.data.flatMap(
(hotels) => hotels.attributes
),
}
} }
export const getHotelsAvailabilityByHotelIds = async ( export const getHotelsAvailabilityByHotelIds = async (
@@ -492,7 +485,23 @@ export const hotelQueryRouter = router({
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
const { lang } = ctx const { lang } = ctx
const apiLang = toApiLang(lang) const apiLang = toApiLang(lang)
return getHotelsAvailabilityByCity(input, apiLang, ctx.serviceToken) const {
cityId,
roomStayStartDate,
roomStayEndDate,
adults,
children,
bookingCode,
} = input
const cacheClient = await getCacheClient()
return await cacheClient.cacheOrGet(
`${cityId}:${roomStayStartDate}:${roomStayEndDate}:${adults}:${children}:${bookingCode}`,
async () => {
return getHotelsAvailabilityByCity(input, apiLang, ctx.serviceToken)
},
env.CACHE_TIME_CITY_SEARCH
)
}), }),
hotelsByCityWithRedemption: protectedProcedure hotelsByCityWithRedemption: protectedProcedure
.input(hotelsAvailabilityInputSchema) .input(hotelsAvailabilityInputSchema)
@@ -503,7 +512,7 @@ export const hotelQueryRouter = router({
input, input,
apiLang, apiLang,
ctx.session.token.access_token, ctx.session.token.access_token,
ctx.session, ctx.session
) )
}), }),
hotelsByHotelIds: serviceProcedure hotelsByHotelIds: serviceProcedure
@@ -519,12 +528,16 @@ export const hotelQueryRouter = router({
.use(async ({ ctx, input, next }) => { .use(async ({ ctx, input, next }) => {
if (input.redemption) { if (input.redemption) {
if (ctx.session?.token.access_token) { if (ctx.session?.token.access_token) {
return next({ const verifiedUser = await getVerifiedUser({ session: ctx.session })
ctx: { if (!verifiedUser?.error) {
token: ctx.session.token.access_token, return next({
}, ctx: {
input, token: ctx.session.token.access_token,
}) userPoints: verifiedUser?.data.membership?.currentPoints ?? 0,
},
input,
})
}
} }
throw unauthorizedError() throw unauthorizedError()
} }
@@ -622,16 +635,15 @@ export const hotelQueryRouter = router({
)?.mustBeGuaranteed )?.mustBeGuaranteed
} }
if (redemption && ctx.session) { if (redemption) {
const verifiedUser = await getVerifiedUser({ session: ctx.session }) validateAvailabilityData.data.roomConfigurations.forEach(
if (!verifiedUser?.error) { (data) => {
const userPoints = verifiedUser?.data.membership?.currentPoints ?? 0 data.redemptions?.forEach((r) => {
validateAvailabilityData.data.roomConfigurations.forEach((data) => { r.redemption.hasEnoughPoints =
data.redemptions?.forEach(r => { ctx.userPoints >= r.redemption.localPrice.pointsPerStay
r.redemption.hasEnoughPoints = userPoints >= r.redemption.localPrice.pointsPerStay
}) })
}) }
} )
} }
return validateAvailabilityData.data return validateAvailabilityData.data

View File

@@ -4,18 +4,19 @@ import { imageSizesSchema } from "./image"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter" import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { BreakfastPackageEnum } from "@/types/enums/breakfast" import { BreakfastPackageEnum } from "@/types/enums/breakfast"
import { CurrencyEnum } from "@/types/enums/currency"
import { PackageTypeEnum } from "@/types/enums/packages" import { PackageTypeEnum } from "@/types/enums/packages"
// TODO: Remove optional and default when the API change has been deployed // TODO: Remove optional and default when the API change has been deployed
export const packagePriceSchema = z export const packagePriceSchema = z
.object({ .object({
currency: z.string().default("N/A"), currency: z.nativeEnum(CurrencyEnum).default(CurrencyEnum.Unknown),
price: z.number(), price: z.number(),
totalPrice: z.number(), totalPrice: z.number(),
}) })
.optional() .optional()
.default({ .default({
currency: "N/A", currency: CurrencyEnum.Unknown,
price: 0, price: 0,
totalPrice: 0, totalPrice: 0,
}) })

View File

@@ -5,6 +5,7 @@ import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDet
import type { RoomPrice } from "@/types/components/hotelReservation/enterDetails/details" import type { RoomPrice } from "@/types/components/hotelReservation/enterDetails/details"
import type { PriceType } from "@/types/components/hotelReservation/myStay/myStay" import type { PriceType } from "@/types/components/hotelReservation/myStay/myStay"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate" import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
import { CurrencyEnum } from "@/types/enums/currency"
import type { Packages } from "@/types/requests/packages" import type { Packages } from "@/types/requests/packages"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
@@ -73,7 +74,7 @@ export const useMyStayRoomDetailsStore = create<MyStayRoomDetailsState>(
bookingCode: null, bookingCode: null,
cheques: 0, cheques: 0,
vouchers: 0, vouchers: 0,
currencyCode: "", currencyCode: CurrencyEnum.Unknown,
guest: { guest: {
email: "", email: "",
firstName: "", firstName: "",
@@ -97,21 +98,21 @@ export const useMyStayRoomDetailsStore = create<MyStayRoomDetailsState>(
perNight: { perNight: {
requested: { requested: {
price: 0, price: 0,
currency: "", currency: CurrencyEnum.Unknown,
}, },
local: { local: {
price: 0, price: 0,
currency: "", currency: CurrencyEnum.Unknown,
}, },
}, },
perStay: { perStay: {
requested: { requested: {
price: 0, price: 0,
currency: "", currency: CurrencyEnum.Unknown,
}, },
local: { local: {
price: 0, price: 0,
currency: "", currency: CurrencyEnum.Unknown,
}, },
}, },
}, },

View File

@@ -1,9 +1,11 @@
import { create } from "zustand" import { create } from "zustand"
import { CurrencyEnum } from "@/types/enums/currency"
interface RoomPrice { interface RoomPrice {
id: string id: string
totalPrice: number totalPrice: number
currencyCode: string currencyCode: CurrencyEnum
isMainBooking?: boolean isMainBooking?: boolean
roomPoints: number roomPoints: number
} }
@@ -11,7 +13,7 @@ interface RoomPrice {
interface MyStayTotalPriceState { interface MyStayTotalPriceState {
rooms: RoomPrice[] rooms: RoomPrice[]
totalPrice: number | null totalPrice: number | null
currencyCode: string currencyCode: CurrencyEnum
totalPoints: number totalPoints: number
actions: { actions: {
// Add a single room price // Add a single room price
@@ -26,7 +28,7 @@ export const useMyStayTotalPriceStore = create<MyStayTotalPriceState>(
totalPoints: 0, totalPoints: 0,
totalCheques: 0, totalCheques: 0,
totalVouchers: 0, totalVouchers: 0,
currencyCode: "", currencyCode: CurrencyEnum.Unknown,
actions: { actions: {
addRoomPrice: (room) => { addRoomPrice: (room) => {
set((state) => { set((state) => {
@@ -44,7 +46,7 @@ export const useMyStayTotalPriceStore = create<MyStayTotalPriceState>(
// Get currency from main booking or first room // Get currency from main booking or first room
const mainRoom = newRooms.find((r) => r.isMainBooking) || newRooms[0] const mainRoom = newRooms.find((r) => r.isMainBooking) || newRooms[0]
const currencyCode = mainRoom?.currencyCode || "" const currencyCode = mainRoom?.currencyCode ?? CurrencyEnum.Unknown
// Calculate total (only same currency for now) // Calculate total (only same currency for now)
const total = newRooms.reduce((sum, r) => { const total = newRooms.reduce((sum, r) => {

View File

@@ -3,7 +3,7 @@ import { z } from "zod"
import { CurrencyEnum } from "@/types/enums/currency" import { CurrencyEnum } from "@/types/enums/currency"
interface TPrice { interface TPrice {
currency: string currency: CurrencyEnum
price: number price: number
regularPrice?: number regularPrice?: number
additionalPrice?: number additionalPrice?: number