import { z } from "zod" import { MembershipLevelEnum } from "@/constants/membershipLevels" import { COUPON_REWARD_TYPES, REWARD_CATEGORIES } from "@/constants/rewards" import { linkRefsUnionSchema, linkUnionSchema, transformPageLink, } from "../schemas/pageLinks" import { systemSchema } from "../schemas/system" export { type ApiReward, type CMSReward, type CMSRewardsResponse, type CMSRewardsWithRedeemResponse, type CMSRewardWithRedeem, type Coupon, type GetRewardWithRedeemRefsSchema, type RedeemableCoupon, type RedeemLocation, rewardWithRedeemRefsSchema, type Surprise, type SurpriseReward, validateApiAllTiersSchema, validateCategorizedRewardsSchema, validateCmsRewardsSchema, validateCmsRewardsWithRedeemSchema, } enum TierKey { tier1 = MembershipLevelEnum.L1, tier2 = MembershipLevelEnum.L2, tier3 = MembershipLevelEnum.L3, tier4 = MembershipLevelEnum.L4, tier5 = MembershipLevelEnum.L5, tier6 = MembershipLevelEnum.L6, tier7 = MembershipLevelEnum.L7, } type Key = keyof typeof TierKey /* * TODO: Remove this once we start using the new CMS model with redeem entirely */ 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(), grouped_description: z.string().optional(), value: z.string().optional(), }) ), }), }), }) .transform((data) => data.data.all_reward.items) type CMSRewardsResponse = z.input type CMSReward = z.output[number] const validateCmsRewardsWithRedeemSchema = 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 }), }) ), }), }), grouped_description: z.string().optional(), value: z.string().optional(), }) ), }), }), }) .transform((data) => data.data.all_reward.items) type CMSRewardsWithRedeemResponse = z.input< typeof validateCmsRewardsWithRedeemSchema > type CMSRewardWithRedeem = z.output< typeof validateCmsRewardsWithRedeemSchema >[number] const rewardWithRedeemRefsSchema = 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, }) ), }), }), system: systemSchema, }) ), }), }), }) type GetRewardWithRedeemRefsSchema = z.input const REDEEM_LOCATIONS = ["Non-redeemable", "On-site", "Online"] as const type RedeemLocation = (typeof REDEEM_LOCATIONS)[number] const BaseReward = z.object({ title: z.string().optional(), id: z.string(), rewardId: z.string(), redeemLocation: z.enum(REDEEM_LOCATIONS), status: z.enum(["active", "expired"]), }) const BenefitReward = BaseReward.merge( z.object({ rewardType: z.enum(["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(), }) type Coupon = z.output type RedeemableCoupon = Coupon & { state: Exclude } const CouponReward = BaseReward.merge( z.object({ rewardType: z.enum(COUPON_REWARD_TYPES), operaRewardId: z.string().default(""), categories: z.array(z.enum(REWARD_CATEGORIES)).optional(), coupon: z .array(CouponData) .optional() .transform((val) => val || []), }) ) type SurpriseReward = z.output & { rewardType: "Surprise" } interface Surprise extends CMSReward { data: SurpriseReward } const validateCategorizedRewardsSchema = z .object({ benefits: z.array(BenefitReward), coupons: z.array(CouponReward), }) .transform((data) => [...data.benefits, ...data.coupons]) type ApiReward = z.output[number] const validateApiAllTiersSchema = z.record( z.nativeEnum(TierKey).transform((data) => { return TierKey[data as unknown as Key] }), z.array(BenefitReward) )