feat(SW-1717): rewrite select-rate to show all variants of rate-cards
This commit is contained in:
committed by
Michael Zetterberg
parent
adde77eaa9
commit
ebaea78fb3
@@ -1,7 +1,7 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import {
|
||||
productTypeChequeSchema,
|
||||
productTypeCorporateChequeSchema,
|
||||
productTypePointsSchema,
|
||||
productTypePriceSchema,
|
||||
productTypeVoucherSchema,
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
|
||||
export const productTypeSchema = z
|
||||
.object({
|
||||
bonusCheque: productTypeChequeSchema.optional(),
|
||||
bonusCheque: productTypeCorporateChequeSchema.optional(),
|
||||
public: productTypePriceSchema.optional(),
|
||||
member: productTypePriceSchema.optional(),
|
||||
redemptions: z.array(productTypePointsSchema).optional(),
|
||||
|
||||
@@ -1,63 +1,76 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { nullableNumberValidator } from "@/utils/zod/numberValidator"
|
||||
import { nullableStringValidator } from "@/utils/zod/stringValidator"
|
||||
|
||||
import { CurrencyEnum } from "@/types/enums/currency"
|
||||
import { RateTypeEnum } from "@/types/enums/rateType"
|
||||
|
||||
export const corporateChequeSchema = z
|
||||
.object({
|
||||
additionalPricePerStay: nullableNumberValidator,
|
||||
currency: z.nativeEnum(CurrencyEnum).nullish(),
|
||||
numberOfBonusCheques: nullableNumberValidator,
|
||||
})
|
||||
.transform((data) => ({
|
||||
additionalPricePerStay: data.additionalPricePerStay,
|
||||
currency: data.currency,
|
||||
numberOfCheques: data.numberOfBonusCheques,
|
||||
}))
|
||||
|
||||
export const redemptionSchema = z.object({
|
||||
additionalPricePerStay: nullableNumberValidator,
|
||||
currency: z.nativeEnum(CurrencyEnum).nullish(),
|
||||
pointsPerNight: nullableNumberValidator,
|
||||
pointsPerStay: nullableNumberValidator,
|
||||
})
|
||||
|
||||
export const priceSchema = z.object({
|
||||
currency: z.nativeEnum(CurrencyEnum),
|
||||
pricePerNight: z.coerce.number(),
|
||||
pricePerStay: z.coerce.number(),
|
||||
regularPricePerNight: z.coerce.number().optional(),
|
||||
regularPricePerStay: z.coerce.number().optional(),
|
||||
})
|
||||
|
||||
export const pointsSchema = z
|
||||
.object({
|
||||
currency: z.nativeEnum(CurrencyEnum).optional(),
|
||||
pricePerStay: z.coerce.number().optional(),
|
||||
pointsPerStay: z.coerce.number(),
|
||||
additionalPricePerStay: z.coerce.number().optional(),
|
||||
additionalPriceCurrency: z.nativeEnum(CurrencyEnum).optional(),
|
||||
})
|
||||
.transform((data) => ({
|
||||
...data,
|
||||
additionalPriceCurrency: data.currency,
|
||||
currency: CurrencyEnum.POINTS,
|
||||
pricePerStay: data.pointsPerStay,
|
||||
price: data.pointsPerStay,
|
||||
additionalPrice: data.additionalPricePerStay,
|
||||
}))
|
||||
|
||||
export const voucherSchema = z.object({
|
||||
currency: z.nativeEnum(CurrencyEnum),
|
||||
pricePerStay: z.number(),
|
||||
})
|
||||
|
||||
export const chequeSchema = z.object({
|
||||
additionalPricePerStay: z.number().optional(),
|
||||
currency: z.nativeEnum(CurrencyEnum).optional(),
|
||||
numberOfBonusCheques: z.coerce.number(),
|
||||
omnibusPricePerNight: nullableNumberValidator,
|
||||
pricePerNight: nullableNumberValidator,
|
||||
pricePerStay: nullableNumberValidator,
|
||||
regularPricePerNight: nullableNumberValidator,
|
||||
regularPricePerStay: nullableNumberValidator,
|
||||
})
|
||||
|
||||
const partialPriceSchema = z.object({
|
||||
rateCode: z.string(),
|
||||
rateType: z.string().optional(),
|
||||
rateCode: nullableStringValidator,
|
||||
rateType: z.nativeEnum(RateTypeEnum).catch((err) => {
|
||||
const issue = err.error.issues[0]
|
||||
// This is necessary to handle cases were a
|
||||
// new `rateType` is added in the API that has
|
||||
// not yet been handled in web
|
||||
if (issue.code === "invalid_enum_value") {
|
||||
return issue.received.toString() as RateTypeEnum
|
||||
}
|
||||
return RateTypeEnum.Regular
|
||||
}),
|
||||
})
|
||||
|
||||
export const productTypePriceSchema = partialPriceSchema.extend({
|
||||
localPrice: priceSchema,
|
||||
requestedPrice: priceSchema.optional(),
|
||||
})
|
||||
export const productTypeCorporateChequeSchema = z
|
||||
.object({
|
||||
localPrice: corporateChequeSchema,
|
||||
requestedPrice: corporateChequeSchema.nullish(),
|
||||
})
|
||||
.merge(partialPriceSchema)
|
||||
|
||||
export const productTypePointsSchema = partialPriceSchema.extend({
|
||||
localPrice: pointsSchema,
|
||||
requestedPrice: pointsSchema.optional(),
|
||||
})
|
||||
export const productTypePriceSchema = z
|
||||
.object({
|
||||
localPrice: priceSchema,
|
||||
requestedPrice: priceSchema.nullish(),
|
||||
})
|
||||
.merge(partialPriceSchema)
|
||||
|
||||
export const productTypeVoucherSchema = partialPriceSchema.extend({
|
||||
numberOfVouchers: z.coerce.number(),
|
||||
})
|
||||
export const productTypePointsSchema = z
|
||||
.object({
|
||||
localPrice: redemptionSchema,
|
||||
requestedPrice: redemptionSchema.nullish(),
|
||||
})
|
||||
.merge(partialPriceSchema)
|
||||
|
||||
export const productTypeChequeSchema = partialPriceSchema.extend({
|
||||
localPrice: chequeSchema,
|
||||
requestedPrice: chequeSchema.optional(),
|
||||
})
|
||||
export const productTypeVoucherSchema = z
|
||||
.object({
|
||||
numberOfVouchers: nullableNumberValidator,
|
||||
})
|
||||
.merge(partialPriceSchema)
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { productSchema } from "./product"
|
||||
import {
|
||||
corporateChequeProduct,
|
||||
priceProduct,
|
||||
productSchema,
|
||||
redemptionProduct,
|
||||
voucherProduct,
|
||||
} from "./product"
|
||||
|
||||
import { AvailabilityEnum } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
import {
|
||||
AvailabilityEnum,
|
||||
} from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||
import {
|
||||
RoomPackageCodeEnum,
|
||||
} from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
|
||||
export const roomConfigurationSchema = z
|
||||
.object({
|
||||
breakfastIncludedInAllRatesMember: z.boolean().default(false),
|
||||
breakfastIncludedInAllRatesPublic: z.boolean().default(false),
|
||||
breakfastIncludedInAllRates: z.boolean().default(false),
|
||||
features: z
|
||||
.array(
|
||||
z.object({
|
||||
@@ -25,36 +35,36 @@ export const roomConfigurationSchema = z
|
||||
roomsLeft: z.number(),
|
||||
roomType: z.string(),
|
||||
roomTypeCode: z.string(),
|
||||
status: z.string(),
|
||||
})
|
||||
.transform((data) => {
|
||||
if (data.products.length) {
|
||||
if (data.products[0].redemptions) {
|
||||
// No need of rate check in reward night scenario
|
||||
return { ...data }
|
||||
} else {
|
||||
const isVoucher = data.products.some((product) => product.voucher)
|
||||
const isCorpChq = data.products.some((product) => product.bonusCheque)
|
||||
if (isVoucher || isCorpChq) {
|
||||
return {
|
||||
...data,
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Just guaranteeing that if all products all miss
|
||||
* both public and member rateCode that status is
|
||||
* set to `NotAvailable`
|
||||
*/
|
||||
const allProductsMissBothRateCodes = data.products.every(
|
||||
(product) => !product.public?.rateCode && !product.member?.rateCode
|
||||
)
|
||||
if (allProductsMissBothRateCodes) {
|
||||
return {
|
||||
...data,
|
||||
status: AvailabilityEnum.NotAvailable,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return data
|
||||
status: z
|
||||
.nativeEnum(AvailabilityEnum)
|
||||
.nullish()
|
||||
.default(AvailabilityEnum.NotAvailable),
|
||||
|
||||
// Red
|
||||
campaign: z
|
||||
.array(priceProduct)
|
||||
.nullish()
|
||||
.transform(val => val ? val.filter(Boolean) : []),
|
||||
// Blue
|
||||
code: z
|
||||
.array(
|
||||
z
|
||||
.union([
|
||||
corporateChequeProduct,
|
||||
priceProduct,
|
||||
voucherProduct,
|
||||
])
|
||||
)
|
||||
.nullish()
|
||||
.transform(val => val ? val.filter(Boolean) : []),
|
||||
// Beige
|
||||
regular: z
|
||||
.array(priceProduct)
|
||||
.nullish()
|
||||
.transform(val => val ? val.filter(Boolean) : []),
|
||||
// Burgundy
|
||||
redemptions: z
|
||||
.array(redemptionProduct)
|
||||
.nullish()
|
||||
.transform(val => val ? val.filter(Boolean) : []),
|
||||
})
|
||||
|
||||
@@ -1,28 +1,161 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import {
|
||||
productTypeCorporateChequeSchema,
|
||||
productTypePointsSchema,
|
||||
productTypeChequeSchema,
|
||||
productTypePriceSchema,
|
||||
productTypeVoucherSchema,
|
||||
} from "../productTypePrice"
|
||||
import { rateDefinitionSchema } from "./rateDefinition"
|
||||
|
||||
export const productSchema = z
|
||||
const baseProductSchema = z.object({
|
||||
// Is breakfast included on product
|
||||
breakfastIncluded: z.boolean().default(false),
|
||||
// Used to set the rate that we use to chose titles etc.
|
||||
rate: z.enum(["change", "flex", "save"]).default("save"),
|
||||
rateDefinition: rateDefinitionSchema.nullish().transform((val) =>
|
||||
val
|
||||
? val
|
||||
: {
|
||||
breakfastIncluded: false,
|
||||
cancellationRule: "",
|
||||
cancellationText: "",
|
||||
displayPriceRed: false,
|
||||
isCampaignRate: false,
|
||||
isMemberRate: false,
|
||||
isPackageRate: false,
|
||||
generalTerms: [],
|
||||
mustBeGuaranteed: false,
|
||||
rateCode: "",
|
||||
rateType: "",
|
||||
title: "",
|
||||
}
|
||||
),
|
||||
})
|
||||
|
||||
function mapBaseProduct(baseProduct: typeof baseProductSchema._type) {
|
||||
return {
|
||||
breakfastIncluded: baseProduct.breakfastIncluded,
|
||||
rate: baseProduct.rate,
|
||||
rateDefinition: baseProduct.rateDefinition,
|
||||
}
|
||||
}
|
||||
|
||||
const rawCorporateChequeProduct = z
|
||||
.object({
|
||||
// Is product flex rate
|
||||
isFlex: z.boolean().default(false),
|
||||
productType: z.object({
|
||||
bonusCheque: productTypeChequeSchema.optional(),
|
||||
member: productTypePriceSchema.optional(),
|
||||
public: productTypePriceSchema.optional(),
|
||||
redemptions: z.array(productTypePointsSchema).optional(),
|
||||
voucher: productTypeVoucherSchema.optional(),
|
||||
}),
|
||||
// Used to set the rate that we use to chose titles etc.
|
||||
rate: z.enum(["change", "flex", "save"]).default("save"),
|
||||
productType: z
|
||||
.object({
|
||||
bonusCheque: productTypeCorporateChequeSchema,
|
||||
})
|
||||
.transform((data) => ({
|
||||
corporateCheque: data.bonusCheque,
|
||||
})),
|
||||
})
|
||||
.transform((data) => ({
|
||||
.merge(baseProductSchema)
|
||||
|
||||
function transformCorporateCheque(
|
||||
data: z.output<typeof rawCorporateChequeProduct>
|
||||
) {
|
||||
return {
|
||||
...data.productType,
|
||||
isFlex: data.isFlex,
|
||||
rate: data.rate,
|
||||
}))
|
||||
...mapBaseProduct(data),
|
||||
}
|
||||
}
|
||||
|
||||
export const corporateChequeProduct = rawCorporateChequeProduct.transform(
|
||||
transformCorporateCheque
|
||||
)
|
||||
|
||||
const rawPriceProduct = z
|
||||
.object({
|
||||
productType: z.object({
|
||||
member: productTypePriceSchema.nullish().default(null),
|
||||
public: productTypePriceSchema.nullish().default(null),
|
||||
}),
|
||||
})
|
||||
.merge(baseProductSchema)
|
||||
|
||||
function transformPriceProduct(data: z.output<typeof rawPriceProduct>) {
|
||||
return {
|
||||
...data.productType,
|
||||
...mapBaseProduct(data),
|
||||
}
|
||||
}
|
||||
|
||||
export const priceProduct = rawPriceProduct.transform(transformPriceProduct)
|
||||
|
||||
export const redemptionProduct = z
|
||||
.object({
|
||||
redemption: productTypePointsSchema,
|
||||
})
|
||||
.merge(baseProductSchema)
|
||||
|
||||
const rawRedemptionsProduct = z.object({
|
||||
type: z.literal("REDEMPTION").optional().default("REDEMPTION"),
|
||||
productType: z.object({
|
||||
redemptions: z
|
||||
.array(productTypePointsSchema.merge(baseProductSchema))
|
||||
.transform((data) =>
|
||||
data.map(
|
||||
({ breakfastIncluded, rate, rateDefinition, ...redemption }) => ({
|
||||
breakfastIncluded,
|
||||
rate,
|
||||
rateDefinition,
|
||||
redemption,
|
||||
})
|
||||
)
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
export const redemptionsProduct = rawRedemptionsProduct.transform(
|
||||
(data) => data.productType.redemptions
|
||||
)
|
||||
|
||||
const rawVoucherProduct = z
|
||||
.object({
|
||||
type: z.literal("VOUCHER").optional().default("VOUCHER"),
|
||||
productType: z.object({
|
||||
voucher: productTypeVoucherSchema,
|
||||
}),
|
||||
})
|
||||
.merge(baseProductSchema)
|
||||
|
||||
function transformVoucherProduct(data: z.output<typeof rawVoucherProduct>) {
|
||||
return {
|
||||
...data.productType,
|
||||
...mapBaseProduct(data),
|
||||
}
|
||||
}
|
||||
|
||||
export const voucherProduct = rawVoucherProduct.transform(
|
||||
transformVoucherProduct
|
||||
)
|
||||
|
||||
export const productSchema = z.union([
|
||||
corporateChequeProduct,
|
||||
redemptionsProduct,
|
||||
voucherProduct,
|
||||
priceProduct,
|
||||
])
|
||||
// export const productSchema = z.discriminatedUnion(
|
||||
// "type",
|
||||
// [
|
||||
// rawCorporateChequeProduct,
|
||||
// rawPriceProduct,
|
||||
// rawRedemptionsProduct,
|
||||
// rawVoucherProduct,
|
||||
// ]
|
||||
// )
|
||||
// .transform(data => {
|
||||
// switch (data.type) {
|
||||
// case "CORPORATECHEQUE":
|
||||
// return transformCorporateCheque(data)
|
||||
// case "PRICEPRODUCT":
|
||||
// return transformPriceProduct(data)
|
||||
// case "REDEMPTION":
|
||||
// return data.productType.redemptions
|
||||
// case "VOUCHER":
|
||||
// return transformVoucherProduct(data)
|
||||
// }
|
||||
// })
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { nullableStringValidator } from "@/utils/zod/stringValidator"
|
||||
import { z } from "zod"
|
||||
|
||||
export const rateDefinitionSchema = z.object({
|
||||
breakfastIncluded: z.boolean(),
|
||||
cancellationRule: z.string(),
|
||||
cancellationText: z.string().optional(),
|
||||
cancellationText: nullableStringValidator,
|
||||
displayPriceRed: z.boolean().default(false),
|
||||
generalTerms: z.array(z.string()),
|
||||
isCampaignRate: z.boolean().default(false),
|
||||
isMemberRate: z.boolean().default(false),
|
||||
isPackageRate: z.boolean().default(false),
|
||||
mustBeGuaranteed: z.boolean(),
|
||||
rateCode: z.string(),
|
||||
rateType: z.string().optional(),
|
||||
rateType: nullableStringValidator,
|
||||
title: z.string(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user