Merged in feat/SW-1261 (pull request #1263)

feat: only show member price when logged in

* feat: only show member price when logged in


Approved-by: Michael Zetterberg
This commit is contained in:
Simon.Emanuelsson
2025-02-07 08:51:50 +00:00
parent c0f5c0278b
commit c204532acc
27 changed files with 479 additions and 238 deletions

View File

@@ -25,6 +25,10 @@ import type {
Restaurant,
Room,
} from "@/types/hotel"
import type {
Product,
RateDefinition,
} from "@/types/trpc/routers/hotel/roomAvailability"
// NOTE: Find schema at: https://aks-test.scandichotels.com/hotel/swagger/v1/index.html
export const hotelSchema = z
@@ -91,6 +95,30 @@ export const hotelsAvailabilitySchema = z.object({
),
})
function everyRateHasBreakfastIncluded(
product: Product,
rateDefinitions: RateDefinition[],
userType: "member" | "public"
) {
const rateDefinition = rateDefinitions.find(
(rd) => rd.rateCode === product.productType[userType]?.rateCode
)
if (!rateDefinition) {
return false
}
return rateDefinition.breakfastIncluded
}
/**
* This is used for custom sorting further down
* to guarantee correct order of rates
*/
const cancellationRules = {
CancellableBefore6PM: 2,
Changeable: 1,
NotCancellable: 0,
} as const
export const roomsAvailabilitySchema = z
.object({
data: z.object({
@@ -107,7 +135,52 @@ export const roomsAvailabilitySchema = z
type: z.string().optional(),
}),
})
.transform((o) => o.data.attributes)
.transform((o) => {
const cancellationRuleLookup = o.data.attributes.rateDefinitions.reduce(
(acc, val) => {
// @ts-expect-error - index of cancellationRule TS
acc[val.rateCode] = cancellationRules[val.cancellationRule]
return acc
},
{}
)
o.data.attributes.roomConfigurations =
o.data.attributes.roomConfigurations.map((room) => {
if (room.products.length) {
room.breakfastIncludedInAllRatesMember = room.products.every(
(product) =>
everyRateHasBreakfastIncluded(
product,
o.data.attributes.rateDefinitions,
"member"
)
)
room.breakfastIncludedInAllRatesPublic = room.products.every(
(product) =>
everyRateHasBreakfastIncluded(
product,
o.data.attributes.rateDefinitions,
"public"
)
)
}
// CancellationRule is the same for public and member per product
// Sorting to guarantee order based on rate
room.products = room.products.sort(
(a, b) =>
// @ts-expect-error - index
cancellationRuleLookup[a.productType.public.rateCode] -
// @ts-expect-error - index
cancellationRuleLookup[b.productType.public.rateCode]
)
return room
})
return o.data.attributes
})
export const ratesSchema = z.array(rateSchema)

View File

@@ -1,25 +1,81 @@
import deepmerge from "deepmerge"
import { z } from "zod"
import { productSchema } from "./product"
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
export const roomConfigurationSchema = z.object({
features: z
.array(
z.object({
inventory: z.number(),
code: z.enum([
RoomPackageCodeEnum.PET_ROOM,
RoomPackageCodeEnum.ALLERGY_ROOM,
RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
]),
})
)
.default([]),
products: z.array(productSchema).default([]),
roomsLeft: z.number(),
roomType: z.string(),
roomTypeCode: z.string(),
status: z.string(),
})
export const roomConfigurationSchema = z
.object({
breakfastIncludedInAllRatesMember: z.boolean().default(false),
breakfastIncludedInAllRatesPublic: z.boolean().default(false),
features: z
.array(
z.object({
inventory: z.number(),
code: z.enum([
RoomPackageCodeEnum.PET_ROOM,
RoomPackageCodeEnum.ALLERGY_ROOM,
RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
]),
})
)
.default([]),
products: z.array(productSchema).default([]),
roomsLeft: z.number(),
roomType: z.string(),
roomTypeCode: z.string(),
status: z.string(),
})
.transform((data) => {
if (data.products.length) {
const someProductsMissAtLeastOneRateCode = data.products.some(
({ productType }) =>
!productType.public.rateCode || !productType.member?.rateCode
)
if (someProductsMissAtLeastOneRateCode) {
data.products = data.products.map((product) => {
if (
product.productType.public.rateCode &&
product.productType.member?.rateCode
) {
return product
}
/**
* Reset both rateCodes if one is missing to show `No prices available` for the same reason as
* mentioned above.
*
* TODO: (Maybe) notify somewhere that this happened
*/
return deepmerge(product, {
productType: {
member: {
rateCode: "",
},
public: {
rateCode: "",
},
},
})
})
}
/**
* When all products miss at least one rateCode (member or public), we change the status to NotAvailable
* since we cannot as of now (31 january) guarantee the flow with missing rateCodes.
*
* TODO: (Maybe) notify somewhere that this happened
*/
const allProductsMissAtLeastOneRateCode = data.products.every(
({ productType }) =>
!productType.public.rateCode || !productType.member?.rateCode
)
if (allProductsMissAtLeastOneRateCode) {
data.status = AvailabilityEnum.NotAvailable
}
}
return data
})