import { z } from "zod" import { MembershipLevelEnum } from "@/constants/membershipLevels" import { linkRefsUnionSchema, linkUnionSchema, transformPageLink, } from "../schemas/pageLinks" import { systemSchema } from "../schemas/system" export { BenefitReward, CouponData, CouponReward, REDEEM_LOCATIONS, REWARD_TYPES, rewardRefsSchema, validateApiAllTiersSchema, validateCategorizedRewardsSchema, validateCmsRewardsSchema, } const validateCmsRewardsSchema = z .object({ data: z.object({ all_reward: z.object({ items: z.array( z.object({ taxonomies: z.array( z.object({ term_uid: z.string().optional().default(""), }) ), label: z.string().optional(), reward_id: z.string(), grouped_label: z.string().optional(), description: z.string().optional(), redeem_description: z .object({ json: z.any(), // JSON embedded_itemsConnection: z.object({ edges: z.array( z.object({ node: linkUnionSchema.transform((data) => { const link = transformPageLink(data) if (link) { return link } return data }), }) ), }), }) // This is primarily added in order to handle a transition // switching from string to RTE .nullable(), grouped_description: z.string().optional(), value: z.string().optional(), }) ), }), }), }) .transform((data) => data.data.all_reward.items) const rewardRefsSchema = z.object({ data: z.object({ all_reward: z.object({ items: z.array( z.object({ redeem_description: z .object({ embedded_itemsConnection: z.object({ edges: z.array( z.object({ node: linkRefsUnionSchema, }) ), }), }) // This is primarily added in order to handle a transition // switching from string to RTE .nullable(), system: systemSchema, }) ), }), }), }) const REDEEM_LOCATIONS = ["Non-redeemable", "On-site", "Online"] as const const REWARD_CATEGORIES = [ "Restaurants", "Bar", "Voucher", "Services and rooms", "Spa and gym", ] as const const BaseReward = z.object({ title: z.string().optional(), id: z.string(), categories: z .array(z.enum(REWARD_CATEGORIES).or(z.literal(""))) .optional() // we sometimes receive empty categories, this filters them out .transform((categories = []) => categories.filter( (c): c is (typeof REWARD_CATEGORIES)[number] => c !== "" ) ), rewardId: z.string(), redeemLocation: z.enum(REDEEM_LOCATIONS), status: z.enum(["active", "expired"]), }) const REWARD_TYPES = { Surprise: "Surprise", Campaign: "Campaign", MemberVoucher: "Member-voucher", Tier: "Tier", } as const const BenefitReward = BaseReward.merge( z.object({ rewardType: z.enum([REWARD_TYPES.Tier]), rewardTierLevel: z.string().optional(), }) ) const CouponData = z.object({ couponCode: z.string(), unwrapped: z.boolean().default(false), state: z.enum(["claimed", "redeemed", "viewed"]), expiresAt: z.string().datetime({ offset: true }).optional(), }) const CouponReward = BaseReward.merge( z.object({ rewardType: z.enum([ REWARD_TYPES.Surprise, REWARD_TYPES.Campaign, REWARD_TYPES.MemberVoucher, ]), operaRewardId: z.string().default(""), coupon: z .array(CouponData) .optional() .transform((val) => val || []), }) ) const validateCategorizedRewardsSchema = z.object({ benefits: z.array(BenefitReward), coupons: z.array(CouponReward), }) const TierKeyMapping = { tier1: MembershipLevelEnum.L1, tier2: MembershipLevelEnum.L2, tier3: MembershipLevelEnum.L3, tier4: MembershipLevelEnum.L4, tier5: MembershipLevelEnum.L5, tier6: MembershipLevelEnum.L6, tier7: MembershipLevelEnum.L7, } as const const TierKeys = Object.keys(TierKeyMapping) as [keyof typeof TierKeyMapping] const validateApiAllTiersSchema = z.record( z.enum(TierKeys).transform((data) => TierKeyMapping[data]), z.array(BenefitReward) )