feat(LOY-64): add ActiveRedeemedBadge UI for on site tier rewards
This commit is contained in:
@@ -17,6 +17,7 @@ export default function ClientCurrentRewards({
|
||||
rewards,
|
||||
pageSize,
|
||||
showRedeem,
|
||||
memberId,
|
||||
}: CurrentRewardsClientProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
@@ -55,7 +56,7 @@ export default function ClientCurrentRewards({
|
||||
</div>
|
||||
{showRedeem && "redeem_description" in reward && (
|
||||
<div className={styles.btnContainer}>
|
||||
<Redeem reward={reward} />
|
||||
<Redeem reward={reward} memberId={memberId} />
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { motion } from "framer-motion"
|
||||
import { AnimatePresence, motion } from "framer-motion"
|
||||
import { useState } from "react"
|
||||
import {
|
||||
Dialog,
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
import { Reward } from "@/server/routers/contentstack/reward/output"
|
||||
|
||||
import Countdown from "@/components/Countdown"
|
||||
import { CheckCircleIcon, CloseLargeIcon } from "@/components/Icons"
|
||||
@@ -18,6 +19,7 @@ import Button from "@/components/TempDesignSystem/Button"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { isRestaurantOnSiteTierReward } from "@/utils/rewards"
|
||||
|
||||
import { RewardIcon } from "../RewardIcon"
|
||||
|
||||
@@ -32,7 +34,7 @@ import type {
|
||||
const MotionOverlay = motion(ModalOverlay)
|
||||
const MotionModal = motion(Modal)
|
||||
|
||||
export default function Redeem({ reward }: RedeemProps) {
|
||||
export default function Redeem({ reward, memberId }: RedeemProps) {
|
||||
const [animation, setAnimation] = useState<RedeemModalState>("unmounted")
|
||||
const intl = useIntl()
|
||||
const update = trpc.contentstack.rewards.redeem.useMutation()
|
||||
@@ -100,17 +102,7 @@ export default function Redeem({ reward }: RedeemProps) {
|
||||
</header>
|
||||
<div className={styles.modalContent}>
|
||||
{redeemStep === "redeemed" && (
|
||||
<div className={styles.badge}>
|
||||
<div className={styles.redeemed}>
|
||||
<CheckCircleIcon color="uiSemanticSuccess" />
|
||||
<Caption>
|
||||
{intl.formatMessage({
|
||||
id: "Redeemed & valid through:",
|
||||
})}
|
||||
</Caption>
|
||||
</div>
|
||||
<Countdown />
|
||||
</div>
|
||||
<ConfirmationBadge reward={reward} />
|
||||
)}
|
||||
<RewardIcon rewardId={reward.reward_id} />
|
||||
<Title level="h3" textAlign="center" textTransform="regular">
|
||||
@@ -127,6 +119,18 @@ export default function Redeem({ reward }: RedeemProps) {
|
||||
{reward.redeem_description}
|
||||
</Body>
|
||||
)}
|
||||
{redeemStep === "redeemed" &&
|
||||
isRestaurantOnSiteTierReward(reward) && (
|
||||
<div className={styles.memberIdBadge}>
|
||||
<Caption
|
||||
textTransform="uppercase"
|
||||
textAlign="center"
|
||||
color="uiTextHighContrast"
|
||||
>
|
||||
{intl.formatMessage({ id: "Member ID:" })} {memberId}
|
||||
</Caption>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{redeemStep === "initial" && (
|
||||
<footer className={styles.modalFooter}>
|
||||
@@ -189,3 +193,55 @@ const variants = {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
function ConfirmationBadge({ reward }: { reward: Reward }) {
|
||||
return (
|
||||
<div className={styles.badge}>
|
||||
{isRestaurantOnSiteTierReward(reward) ? (
|
||||
<ActiveRedeemedBadge />
|
||||
) : (
|
||||
<TimedRedeemedBadge />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ActiveRedeemedBadge() {
|
||||
const intl = useIntl()
|
||||
|
||||
return (
|
||||
<div className={styles.redeemed}>
|
||||
<motion.div
|
||||
animate={{
|
||||
opacity: [1, 0.4, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
>
|
||||
<CheckCircleIcon color="uiSemanticSuccess" />
|
||||
</motion.div>
|
||||
<Caption>{intl.formatMessage({ id: "Active" })}</Caption>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function TimedRedeemedBadge() {
|
||||
const intl = useIntl()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.redeemed}>
|
||||
<CheckCircleIcon color="uiSemanticSuccess" />
|
||||
<Caption>
|
||||
{intl.formatMessage({
|
||||
id: "Redeemed & valid through:",
|
||||
})}
|
||||
</Caption>
|
||||
</div>
|
||||
<Countdown />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.redeemed {
|
||||
@@ -129,4 +130,17 @@
|
||||
height: var(--button-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.active {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x-half);
|
||||
color: var(--UI-Semantic-Success);
|
||||
}
|
||||
|
||||
.memberIdBadge {
|
||||
border-radius: var(--Small);
|
||||
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
||||
background: var(--Base-Surface-Secondary-light-Normal);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { env } from "@/env/server"
|
||||
import { getMembershipLevel } from "@/lib/trpc/memoizedRequests"
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
@@ -14,7 +15,10 @@ export default async function CurrentRewardsBlock({
|
||||
subtitle,
|
||||
link,
|
||||
}: AccountPageComponentProps) {
|
||||
const rewardsResponse = await serverClient().contentstack.rewards.current()
|
||||
const [rewardsResponse, membershipLevel] = await Promise.all([
|
||||
serverClient().contentstack.rewards.current(),
|
||||
getMembershipLevel(),
|
||||
])
|
||||
|
||||
if (!rewardsResponse?.rewards.length) {
|
||||
return null
|
||||
@@ -27,6 +31,7 @@ export default async function CurrentRewardsBlock({
|
||||
rewards={rewardsResponse.rewards}
|
||||
pageSize={6}
|
||||
showRedeem={env.USE_NEW_REWARDS_ENDPOINT && env.USE_NEW_REWARD_MODEL}
|
||||
memberId={membershipLevel?.membershipNumber}
|
||||
/>
|
||||
<SectionLink link={link} variant="mobile" />
|
||||
</SectionContainer>
|
||||
|
||||
Reference in New Issue
Block a user