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}
</Title>
{redeemStep === "initial" && (
<Body textAlign="center">{reward.description}</Body>
)}
{reward.redeemLocation !== "Non-redeemable" ? (
<>
{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>
)}
{redeemStep === "redeemed" &&
isRestaurantOnSiteTierReward(reward) &&
membershipNumber && (
<MembershipNumberBadge membershipNumber={membershipNumber} />
)}
</div>
{redeemStep === "initial" && (
<footer className={styles.modalFooter}>
<Button
onClick={() => setRedeemStep("confirmation")}
intent="primary"
theme="base"
>
{intl.formatMessage({ id: "Redeem benefit" })}
</Button>
</footer>
)}
{reward.redeemLocation !== "Non-redeemable" ? (
<>
{redeemStep === "initial" && (
<footer className={styles.modalFooter}>
<Button
onClick={() => setRedeemStep("confirmation")}
intent="primary"
theme="base"
>
{intl.formatMessage({ id: "Redeem benefit" })}
</Button>
</footer>
)}
{redeemStep === "confirmation" && (
<footer className={styles.modalFooter}>
<Button
onClick={onRedeem}
disabled={isRedeeming}
intent="primary"
theme="base"
>
{intl.formatMessage({ id: "Yes, redeem" })}
</Button>
<Button
onClick={() => setRedeemStep("initial")}
intent="secondary"
theme="base"
>
{intl.formatMessage({ id: "Go back" })}
</Button>
</footer>
)}
{redeemStep === "confirmation" && (
<footer className={styles.modalFooter}>
<Button
onClick={onRedeem}
disabled={isRedeeming}
intent="primary"
theme="base"
>
{intl.formatMessage({ id: "Yes, redeem" })}
</Button>
<Button
onClick={() => setRedeemStep("initial")}
intent="secondary"
theme="base"
>
{intl.formatMessage({ id: "Go back" })}
</Button>
</footer>
)}
</>
) : null}
</>
)
}

View File

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

View File

@@ -255,6 +255,7 @@
"Hours": "Tider",
"How do you want to sleep?": "Hvordan vil du sove?",
"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!",
"I accept": "Jeg accepterer",
"I accept the terms and conditions": "Jeg accepterer vilkårene",

View File

@@ -256,6 +256,7 @@
"Hours": "Zeiten",
"How do you want to sleep?": "Wie möchtest du schlafen?",
"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!",
"I accept": "Ich akzeptiere",
"I accept the terms and conditions": "Ich akzeptiere die Geschäftsbedingungen",

View File

@@ -259,6 +259,7 @@
"Hours": "Hours",
"How do you want to sleep?": "How do you want to sleep?",
"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!",
"I accept": "I accept",
"I accept the terms and conditions": "I accept the terms and conditions",

View File

@@ -255,6 +255,7 @@
"Hours": "Ajat",
"How do you want to sleep?": "Kuinka haluat nukkua?",
"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!",
"I accept": "Hyväksyn",
"I accept the terms and conditions": "Hyväksyn käyttöehdot",

View File

@@ -254,6 +254,7 @@
"Hours": "Tider",
"How do you want to sleep?": "Hvordan vil du sove?",
"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!",
"I accept": "Jeg aksepterer",
"I accept the terms and conditions": "Jeg aksepterer vilkårene",

View File

@@ -254,6 +254,7 @@
"Hours": "Tider",
"How do you want to sleep?": "Hur vill du sova?",
"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!",
"I accept": "Jag accepterar",
"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.
const BenefitReward = z.object({
const BaseReward = z.object({
title: z.string().optional(),
id: z.string().optional(),
redeemLocation: z.string().optional(),
rewardId: z.string().optional(),
rewardType: z.string().optional(), // TODO: Should be "Tier" but can't because of backwards compatibility
rewardTierLevel: z.string().optional(),
redeemLocation: 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({
couponCode: z.string().optional(),
unwrapped: z.boolean().default(false),
@@ -199,19 +203,16 @@ const CouponData = z.object({
expiresAt: z.string().datetime({ offset: true }).optional(),
})
const CouponReward = z.object({
title: z.string().optional(),
id: z.string().optional(),
rewardId: z.string().optional(),
rewardType: z.enum(["Surprise", "Campaign", "Member-voucher"]),
redeemLocation: z.string().optional(),
operaRewardId: z.string().default(""),
status: z.string().optional(),
coupon: z
.array(CouponData)
.optional()
.transform((val) => val || []),
})
const CouponReward = BaseReward.merge(
z.object({
rewardType: z.enum(["Surprise", "Campaign", "Member-voucher"]),
operaRewardId: z.string().default(""),
coupon: z
.array(CouponData)
.optional()
.transform((val) => val || []),
})
)
/**
* Schema for the new /profile/v1/Reward endpoint.