Merged in feat/LOY-134-add-informative-reward-modal (pull request #1307)

feat(LOY-134): display informative rewards and modal

Approved-by: Chuma Mcphoy (We Ahead)
This commit is contained in:
Christian Andolf
2025-02-13 10:13:35 +00:00
9 changed files with 78 additions and 59 deletions

View File

@@ -45,52 +45,62 @@ export default function Tier({
{reward.label} {reward.label}
</Title> </Title>
{redeemStep === "initial" && ( {reward.redeemLocation !== "Non-redeemable" ? (
<Body textAlign="center">{reward.description}</Body> <>
)} {redeemStep === "initial" && (
<Body textAlign="center">{reward.description}</Body>
)}
{redeemStep === "confirmation" && ( {redeemStep === "confirmation" && (
<Body textAlign="center">{reward.redeem_description}</Body>
)}
{redeemStep === "redeemed" &&
isRestaurantOnSiteTierReward(reward) &&
membershipNumber && (
<MembershipNumberBadge membershipNumber={membershipNumber} />
)}
</>
) : (
<Body textAlign="center">{reward.redeem_description}</Body> <Body textAlign="center">{reward.redeem_description}</Body>
)} )}
{redeemStep === "redeemed" &&
isRestaurantOnSiteTierReward(reward) &&
membershipNumber && (
<MembershipNumberBadge membershipNumber={membershipNumber} />
)}
</div> </div>
{redeemStep === "initial" && ( {reward.redeemLocation !== "Non-redeemable" ? (
<footer className={styles.modalFooter}> <>
<Button {redeemStep === "initial" && (
onClick={() => setRedeemStep("confirmation")} <footer className={styles.modalFooter}>
intent="primary" <Button
theme="base" onClick={() => setRedeemStep("confirmation")}
> intent="primary"
{intl.formatMessage({ id: "Redeem benefit" })} theme="base"
</Button> >
</footer> {intl.formatMessage({ id: "Redeem benefit" })}
)} </Button>
</footer>
)}
{redeemStep === "confirmation" && ( {redeemStep === "confirmation" && (
<footer className={styles.modalFooter}> <footer className={styles.modalFooter}>
<Button <Button
onClick={onRedeem} onClick={onRedeem}
disabled={isRedeeming} disabled={isRedeeming}
intent="primary" intent="primary"
theme="base" theme="base"
> >
{intl.formatMessage({ id: "Yes, redeem" })} {intl.formatMessage({ id: "Yes, redeem" })}
</Button> </Button>
<Button <Button
onClick={() => setRedeemStep("initial")} onClick={() => setRedeemStep("initial")}
intent="secondary" intent="secondary"
theme="base" theme="base"
> >
{intl.formatMessage({ id: "Go back" })} {intl.formatMessage({ id: "Go back" })}
</Button> </Button>
</footer> </footer>
)} )}
</>
) : null}
</> </>
) )
} }

View File

@@ -51,7 +51,9 @@ export default function Redeem({ reward, membershipNumber }: RedeemProps) {
onOpenChange={(isOpen) => setAnimation(isOpen ? "visible" : "hidden")} onOpenChange={(isOpen) => setAnimation(isOpen ? "visible" : "hidden")}
> >
<Button intent="primary" fullWidth> <Button intent="primary" fullWidth>
{intl.formatMessage({ id: "Open" })} {reward.redeemLocation === "Non-redeemable"
? intl.formatMessage({ id: "How to use" })
: intl.formatMessage({ id: "Open" })}
</Button> </Button>
<MotionOverlay <MotionOverlay
className={styles.overlay} className={styles.overlay}

View File

@@ -255,6 +255,7 @@
"Hours": "Tider", "Hours": "Tider",
"How do you want to sleep?": "Hvordan vil du sove?", "How do you want to sleep?": "Hvordan vil du sove?",
"How it works": "Hvordan det virker", "How it works": "Hvordan det virker",
"How to use": "Sådan bruges",
"Hurry up and use them before they expire!": "Skynd dig og brug dem, før de udløber!", "Hurry up and use them before they expire!": "Skynd dig og brug dem, før de udløber!",
"I accept": "Jeg accepterer", "I accept": "Jeg accepterer",
"I accept the terms and conditions": "Jeg accepterer vilkårene", "I accept the terms and conditions": "Jeg accepterer vilkårene",

View File

@@ -256,6 +256,7 @@
"Hours": "Zeiten", "Hours": "Zeiten",
"How do you want to sleep?": "Wie möchtest du schlafen?", "How do you want to sleep?": "Wie möchtest du schlafen?",
"How it works": "Wie es funktioniert", "How it works": "Wie es funktioniert",
"How to use": "Wie zu verwenden",
"Hurry up and use them before they expire!": "Beeilen Sie sich und nutzen Sie sie, bevor sie ablaufen!", "Hurry up and use them before they expire!": "Beeilen Sie sich und nutzen Sie sie, bevor sie ablaufen!",
"I accept": "Ich akzeptiere", "I accept": "Ich akzeptiere",
"I accept the terms and conditions": "Ich akzeptiere die Geschäftsbedingungen", "I accept the terms and conditions": "Ich akzeptiere die Geschäftsbedingungen",

View File

@@ -259,6 +259,7 @@
"Hours": "Hours", "Hours": "Hours",
"How do you want to sleep?": "How do you want to sleep?", "How do you want to sleep?": "How do you want to sleep?",
"How it works": "How it works", "How it works": "How it works",
"How to use": "How to use",
"Hurry up and use them before they expire!": "Hurry up and use them before they expire!", "Hurry up and use them before they expire!": "Hurry up and use them before they expire!",
"I accept": "I accept", "I accept": "I accept",
"I accept the terms and conditions": "I accept the terms and conditions", "I accept the terms and conditions": "I accept the terms and conditions",

View File

@@ -255,6 +255,7 @@
"Hours": "Ajat", "Hours": "Ajat",
"How do you want to sleep?": "Kuinka haluat nukkua?", "How do you want to sleep?": "Kuinka haluat nukkua?",
"How it works": "Kuinka se toimii", "How it works": "Kuinka se toimii",
"How to use": "Kuinka käyttää",
"Hurry up and use them before they expire!": "Ole nopea ja käytä ne ennen kuin ne vanhenevat!", "Hurry up and use them before they expire!": "Ole nopea ja käytä ne ennen kuin ne vanhenevat!",
"I accept": "Hyväksyn", "I accept": "Hyväksyn",
"I accept the terms and conditions": "Hyväksyn käyttöehdot", "I accept the terms and conditions": "Hyväksyn käyttöehdot",

View File

@@ -254,6 +254,7 @@
"Hours": "Tider", "Hours": "Tider",
"How do you want to sleep?": "Hvordan vil du sove?", "How do you want to sleep?": "Hvordan vil du sove?",
"How it works": "Hvordan det fungerer", "How it works": "Hvordan det fungerer",
"How to use": "Hvordan bruke",
"Hurry up and use them before they expire!": "Skynd deg og bruk dem før de utløper!", "Hurry up and use them before they expire!": "Skynd deg og bruk dem før de utløper!",
"I accept": "Jeg aksepterer", "I accept": "Jeg aksepterer",
"I accept the terms and conditions": "Jeg aksepterer vilkårene", "I accept the terms and conditions": "Jeg aksepterer vilkårene",

View File

@@ -254,6 +254,7 @@
"Hours": "Tider", "Hours": "Tider",
"How do you want to sleep?": "Hur vill du sova?", "How do you want to sleep?": "Hur vill du sova?",
"How it works": "Hur det fungerar", "How it works": "Hur det fungerar",
"How to use": "Hur man använder",
"Hurry up and use them before they expire!": "Skynda dig och använd dem innan de går ut!", "Hurry up and use them before they expire!": "Skynda dig och använd dem innan de går ut!",
"I accept": "Jag accepterar", "I accept": "Jag accepterar",
"I accept the terms and conditions": "Jag accepterar villkoren", "I accept the terms and conditions": "Jag accepterar villkoren",

View File

@@ -181,17 +181,21 @@ export type RewardWithRedeem = CMSRewardWithRedeem & {
} }
// New endpoint related types and schemas. // New endpoint related types and schemas.
const BaseReward = z.object({
const BenefitReward = z.object({
title: z.string().optional(), title: z.string().optional(),
id: z.string().optional(), id: z.string().optional(),
redeemLocation: z.string().optional(),
rewardId: z.string().optional(), rewardId: z.string().optional(),
rewardType: z.string().optional(), // TODO: Should be "Tier" but can't because of backwards compatibility redeemLocation: z.string().optional(),
rewardTierLevel: z.string().optional(),
status: z.string().optional(), status: z.string().optional(),
}) })
const BenefitReward = BaseReward.merge(
z.object({
rewardType: z.string().optional(), // TODO: Should be "Tier" but can't because of backwards compatibility
rewardTierLevel: z.string().optional(),
})
)
const CouponData = z.object({ const CouponData = z.object({
couponCode: z.string().optional(), couponCode: z.string().optional(),
unwrapped: z.boolean().default(false), unwrapped: z.boolean().default(false),
@@ -199,19 +203,16 @@ const CouponData = z.object({
expiresAt: z.string().datetime({ offset: true }).optional(), expiresAt: z.string().datetime({ offset: true }).optional(),
}) })
const CouponReward = z.object({ const CouponReward = BaseReward.merge(
title: z.string().optional(), z.object({
id: z.string().optional(), rewardType: z.enum(["Surprise", "Campaign", "Member-voucher"]),
rewardId: z.string().optional(), operaRewardId: z.string().default(""),
rewardType: z.enum(["Surprise", "Campaign", "Member-voucher"]), coupon: z
redeemLocation: z.string().optional(), .array(CouponData)
operaRewardId: z.string().default(""), .optional()
status: z.string().optional(), .transform((val) => val || []),
coupon: z })
.array(CouponData) )
.optional()
.transform((val) => val || []),
})
/** /**
* Schema for the new /profile/v1/Reward endpoint. * Schema for the new /profile/v1/Reward endpoint.