Feature/wrap logging * feat: change all logging to go through our own logger function so that we can control log levels * move packages/trpc to using our own logger * merge Approved-by: Linus Flood
185 lines
5.3 KiB
TypeScript
185 lines
5.3 KiB
TypeScript
"use client"
|
|
|
|
import { motion } from "motion/react"
|
|
import { useState } from "react"
|
|
import {
|
|
Dialog,
|
|
DialogTrigger,
|
|
Modal,
|
|
ModalOverlay,
|
|
} from "react-aria-components"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { logger } from "@scandic-hotels/common/logger"
|
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
|
import { trpc } from "@scandic-hotels/trpc/client"
|
|
|
|
import Button from "@/components/TempDesignSystem/Button"
|
|
import useLang from "@/hooks/useLang"
|
|
import { isRestaurantOnSiteTierReward } from "@/utils/rewards"
|
|
|
|
import RedeemCampaign from "./Flows/Campaign"
|
|
import RedeemTier from "./Flows/Tier"
|
|
import { ConfirmClose } from "./ConfirmClose"
|
|
import { RedeemContext } from "./useRedeemFlow"
|
|
|
|
import styles from "./redeem.module.css"
|
|
|
|
import type { Reward } from "@scandic-hotels/trpc/types/rewards"
|
|
|
|
import type {
|
|
RedeemModalState,
|
|
RedeemProps,
|
|
RedeemStep,
|
|
} from "@/types/components/myPages/myPage/accountPage"
|
|
|
|
const MotionOverlay = motion.create(ModalOverlay)
|
|
const MotionModal = motion.create(Modal)
|
|
|
|
const thirtyMinutesInMs = 1000 * 60 * 30
|
|
|
|
export default function Redeem({ reward, membershipNumber }: RedeemProps) {
|
|
const [animation, setAnimation] = useState<RedeemModalState>("unmounted")
|
|
const intl = useIntl()
|
|
const lang = useLang()
|
|
const utils = trpc.useUtils()
|
|
const [redeemStep, setRedeemStep] = useState<RedeemStep>("initial")
|
|
const [timeRemaining, setTimeRemaining] = useState(thirtyMinutesInMs)
|
|
|
|
function modalStateHandler(newAnimationState: RedeemModalState) {
|
|
setAnimation((currentAnimationState) =>
|
|
newAnimationState === "hidden" && currentAnimationState === "hidden"
|
|
? "unmounted"
|
|
: currentAnimationState
|
|
)
|
|
if (newAnimationState === "unmounted") {
|
|
setRedeemStep("initial")
|
|
}
|
|
}
|
|
|
|
return (
|
|
<RedeemContext.Provider
|
|
value={{
|
|
redeemStep,
|
|
setRedeemStep,
|
|
defaultTimeRemaining: thirtyMinutesInMs,
|
|
timeRemaining,
|
|
setTimeRemaining,
|
|
}}
|
|
>
|
|
<DialogTrigger
|
|
onOpenChange={(isOpen) => setAnimation(isOpen ? "visible" : "hidden")}
|
|
>
|
|
<Button intent="primary" fullWidth>
|
|
{reward.redeemLocation === "Non-redeemable"
|
|
? intl.formatMessage({
|
|
defaultMessage: "How to use",
|
|
})
|
|
: intl.formatMessage({
|
|
defaultMessage: "Open",
|
|
})}
|
|
</Button>
|
|
<MotionOverlay
|
|
className={styles.overlay}
|
|
isExiting={animation === "hidden"}
|
|
onAnimationComplete={modalStateHandler}
|
|
variants={variants.fade}
|
|
initial="hidden"
|
|
animate={animation}
|
|
>
|
|
<MotionModal
|
|
className={styles.modal}
|
|
variants={variants.slideInOut}
|
|
initial="hidden"
|
|
animate={animation}
|
|
>
|
|
<Dialog className={styles.dialog} aria-label={reward.label}>
|
|
{({ close }) => {
|
|
function closeModal() {
|
|
if (
|
|
redeemStep === "redeemed" ||
|
|
redeemStep === "confirm-close"
|
|
) {
|
|
utils.contentstack.rewards.current.invalidate({
|
|
lang,
|
|
})
|
|
}
|
|
close()
|
|
}
|
|
return (
|
|
<>
|
|
<header className={styles.modalHeader}>
|
|
<button
|
|
onClick={() => {
|
|
if (
|
|
redeemStep === "redeemed" &&
|
|
!isRestaurantOnSiteTierReward(reward)
|
|
) {
|
|
setRedeemStep("confirm-close")
|
|
} else {
|
|
closeModal()
|
|
}
|
|
}}
|
|
type="button"
|
|
className={styles.modalClose}
|
|
>
|
|
<MaterialIcon icon="close" />
|
|
</button>
|
|
</header>
|
|
|
|
{redeemStep === "confirm-close" ? (
|
|
<ConfirmClose close={closeModal} />
|
|
) : (
|
|
getRedeemFlow(reward, membershipNumber || "")
|
|
)}
|
|
</>
|
|
)
|
|
}}
|
|
</Dialog>
|
|
</MotionModal>
|
|
</MotionOverlay>
|
|
</DialogTrigger>
|
|
</RedeemContext.Provider>
|
|
)
|
|
}
|
|
|
|
const variants = {
|
|
fade: {
|
|
hidden: {
|
|
opacity: 0,
|
|
transition: { duration: 0.4, ease: "easeInOut" },
|
|
},
|
|
visible: {
|
|
opacity: 1,
|
|
transition: { duration: 0.4, ease: "easeInOut" },
|
|
},
|
|
},
|
|
|
|
slideInOut: {
|
|
hidden: {
|
|
opacity: 0,
|
|
y: 32,
|
|
transition: { duration: 0.4, ease: "easeInOut" },
|
|
},
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
transition: { duration: 0.4, ease: "easeInOut" },
|
|
},
|
|
},
|
|
}
|
|
|
|
function getRedeemFlow(reward: Reward, membershipNumber: string) {
|
|
const { rewardType } = reward
|
|
switch (rewardType) {
|
|
case "Campaign":
|
|
return <RedeemCampaign reward={reward} />
|
|
case "Surprise":
|
|
case "Tier":
|
|
return <RedeemTier reward={reward} membershipNumber={membershipNumber} />
|
|
default:
|
|
logger.warn("Unsupported reward type for redeem:", rewardType)
|
|
return null
|
|
}
|
|
}
|