refactor(LOY-62): implement code review feedback
This commit is contained in:
@@ -1,98 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { trpc } from "@/lib/trpc/client"
|
|
||||||
import { Reward } from "@/server/routers/contentstack/reward/output"
|
|
||||||
|
|
||||||
import Image from "@/components/Image"
|
|
||||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
|
||||||
import Grids from "@/components/TempDesignSystem/Grids"
|
|
||||||
import ShowMoreButton from "@/components/TempDesignSystem/ShowMoreButton"
|
|
||||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
|
||||||
import useLang from "@/hooks/useLang"
|
|
||||||
|
|
||||||
import ScriptedRewardText from "../ScriptedRewardText"
|
|
||||||
import Redeem from "./Redeem"
|
|
||||||
|
|
||||||
import styles from "./current.module.css"
|
|
||||||
|
|
||||||
import type { CurrentRewardsClientProps } from "@/types/components/myPages/myPage/accountPage"
|
|
||||||
|
|
||||||
export default function ClientCurrentRewards({
|
|
||||||
initialCurrentRewards,
|
|
||||||
showRedeem,
|
|
||||||
}: CurrentRewardsClientProps) {
|
|
||||||
const lang = useLang()
|
|
||||||
const { data, isFetching, fetchNextPage, hasNextPage, isLoading } =
|
|
||||||
trpc.contentstack.rewards.current.useInfiniteQuery(
|
|
||||||
{
|
|
||||||
limit: 3,
|
|
||||||
lang,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
getNextPageParam: (lastPage) => lastPage?.nextCursor,
|
|
||||||
initialData: {
|
|
||||||
pageParams: [undefined, 1],
|
|
||||||
pages: [initialCurrentRewards],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
function loadMoreData() {
|
|
||||||
if (hasNextPage) {
|
|
||||||
fetchNextPage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const filteredRewards = data?.pages.filter((page) => page?.rewards) ?? []
|
|
||||||
const rewards = filteredRewards
|
|
||||||
.flatMap((page) => page?.rewards)
|
|
||||||
.filter((reward): reward is Reward => !!reward)
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <LoadingSpinner />
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rewards.length) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Grids.Stackable>
|
|
||||||
{rewards.map((reward, idx) => (
|
|
||||||
<article className={styles.card} key={`${reward.reward_id}-${idx}`}>
|
|
||||||
<div className={styles.content}>
|
|
||||||
<Image
|
|
||||||
src="/_static/img/loyalty-award.png"
|
|
||||||
width={113}
|
|
||||||
height={125}
|
|
||||||
alt={reward.label || ""}
|
|
||||||
/>
|
|
||||||
<ScriptedRewardText
|
|
||||||
rewardType={reward.rewardType}
|
|
||||||
rewardTierLevel={reward.rewardTierLevel}
|
|
||||||
/>
|
|
||||||
<Title
|
|
||||||
as="h4"
|
|
||||||
level="h3"
|
|
||||||
textAlign="center"
|
|
||||||
textTransform="regular"
|
|
||||||
>
|
|
||||||
{reward.label}
|
|
||||||
</Title>
|
|
||||||
</div>
|
|
||||||
{showRedeem && (
|
|
||||||
<div className={styles.btnContainer}>
|
|
||||||
<Redeem reward={reward} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</article>
|
|
||||||
))}
|
|
||||||
</Grids.Stackable>
|
|
||||||
{hasNextPage &&
|
|
||||||
(isFetching ? (
|
|
||||||
<LoadingSpinner />
|
|
||||||
) : (
|
|
||||||
<ShowMoreButton loadMoreData={loadMoreData} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import { useRef, useState } from "react"
|
import { useRef, useState } from "react"
|
||||||
|
|
||||||
import { RewardIcon } from "@/components/Blocks/DynamicContent/Rewards/RewardIcon"
|
import { RewardIcon } from "@/components/Blocks/DynamicContent/Rewards/RewardIcon"
|
||||||
|
import ScriptedRewardText from "@/components/Blocks/DynamicContent/Rewards/ScriptedRewardText"
|
||||||
import Pagination from "@/components/MyPages/Pagination"
|
import Pagination from "@/components/MyPages/Pagination"
|
||||||
import Grids from "@/components/TempDesignSystem/Grids"
|
import Grids from "@/components/TempDesignSystem/Grids"
|
||||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||||
@@ -45,6 +46,10 @@ export default function ClientCurrentRewards({
|
|||||||
<article className={styles.card} key={`${reward.reward_id}-${idx}`}>
|
<article className={styles.card} key={`${reward.reward_id}-${idx}`}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<RewardIcon rewardId={reward.reward_id} />
|
<RewardIcon rewardId={reward.reward_id} />
|
||||||
|
<ScriptedRewardText
|
||||||
|
rewardType={reward.rewardType}
|
||||||
|
rewardTierLevel={reward.rewardTierLevel}
|
||||||
|
/>
|
||||||
<Title
|
<Title
|
||||||
as="h4"
|
as="h4"
|
||||||
level="h3"
|
level="h3"
|
||||||
|
|||||||
@@ -1,23 +1,13 @@
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import {
|
import { TIER_TO_FRIEND_MAP } from "@/constants/membershipLevels"
|
||||||
isMembershipLevel,
|
|
||||||
MembershipLevelEnum,
|
|
||||||
} from "@/constants/membershipLevels"
|
|
||||||
|
|
||||||
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
||||||
|
import { isMembershipLevel } from "@/utils/membershipLevels"
|
||||||
|
import { isCouponRewardType } from "@/utils/rewards"
|
||||||
|
|
||||||
import type { ScriptedRewardTextProps } from "@/types/components/myPages/myPage/accountPage"
|
import type { ScriptedRewardTextProps } from "@/types/components/myPages/myPage/accountPage"
|
||||||
|
import type { CouponRewardType } from "@/types/components/myPages/rewards"
|
||||||
const TIER_TO_FRIEND_MAP: Record<MembershipLevelEnum, string> = {
|
|
||||||
[MembershipLevelEnum.L1]: "New Friend",
|
|
||||||
[MembershipLevelEnum.L2]: "Good Friend",
|
|
||||||
[MembershipLevelEnum.L3]: "Close Friend",
|
|
||||||
[MembershipLevelEnum.L4]: "Dear Friend",
|
|
||||||
[MembershipLevelEnum.L5]: "Loyal Friend",
|
|
||||||
[MembershipLevelEnum.L6]: "True Friend",
|
|
||||||
[MembershipLevelEnum.L7]: "Best Friend",
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ScriptedRewardText({
|
export default function ScriptedRewardText({
|
||||||
rewardType,
|
rewardType,
|
||||||
@@ -25,31 +15,21 @@ export default function ScriptedRewardText({
|
|||||||
}: ScriptedRewardTextProps) {
|
}: ScriptedRewardTextProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
let label: string | null = null
|
const couponLabelMap: Record<CouponRewardType, string> = {
|
||||||
|
Campaign: intl.formatMessage({ id: "Campaign" }),
|
||||||
switch (rewardType) {
|
Surprise: intl.formatMessage({ id: "Surprise!" }),
|
||||||
case "Tier":
|
"Member-voucher": intl.formatMessage({ id: "Voucher" }),
|
||||||
if (rewardTierLevel && isMembershipLevel(rewardTierLevel)) {
|
|
||||||
label = TIER_TO_FRIEND_MAP[rewardTierLevel]
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case "Campaign":
|
|
||||||
label = intl.formatMessage({ id: "Campaign" })
|
|
||||||
break
|
|
||||||
|
|
||||||
case "Surprise":
|
|
||||||
label = intl.formatMessage({ id: "Surprise!" })
|
|
||||||
break
|
|
||||||
|
|
||||||
case "Member-voucher":
|
|
||||||
label = intl.formatMessage({ id: "Voucher" })
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
label = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const label =
|
||||||
|
rewardTierLevel && isMembershipLevel(rewardTierLevel)
|
||||||
|
? TIER_TO_FRIEND_MAP[rewardTierLevel]
|
||||||
|
: isCouponRewardType(rewardType)
|
||||||
|
? couponLabelMap[rewardType]
|
||||||
|
: null
|
||||||
|
|
||||||
|
if (!label) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BiroScript type="two" color="red" tilted="small">
|
<BiroScript type="two" color="red" tilted="small">
|
||||||
{label}
|
{label}
|
||||||
|
|||||||
@@ -18,10 +18,14 @@ export enum MembershipLevelEnum {
|
|||||||
L7 = "L7",
|
L7 = "L7",
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isMembershipLevel(value: string): value is MembershipLevelEnum {
|
export const TIER_TO_FRIEND_MAP: Record<MembershipLevelEnum, string> = {
|
||||||
return Object.values(MembershipLevelEnum).includes(
|
[MembershipLevelEnum.L1]: "New Friend",
|
||||||
value as MembershipLevelEnum
|
[MembershipLevelEnum.L2]: "Good Friend",
|
||||||
)
|
[MembershipLevelEnum.L3]: "Close Friend",
|
||||||
|
[MembershipLevelEnum.L4]: "Dear Friend",
|
||||||
|
[MembershipLevelEnum.L5]: "Loyal Friend",
|
||||||
|
[MembershipLevelEnum.L6]: "True Friend",
|
||||||
|
[MembershipLevelEnum.L7]: "Best Friend",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MembershipLevel = keyof typeof MembershipLevelEnum
|
export type MembershipLevel = keyof typeof MembershipLevelEnum
|
||||||
|
|||||||
@@ -37,3 +37,9 @@ export const RESTAURANT_REWARD_IDS = [
|
|||||||
REWARD_IDS.FreeKidsDrink,
|
REWARD_IDS.FreeKidsDrink,
|
||||||
REWARD_IDS.FreeBreakfast,
|
REWARD_IDS.FreeBreakfast,
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
|
export const COUPON_REWARD_TYPES = [
|
||||||
|
"Surprise",
|
||||||
|
"Campaign",
|
||||||
|
"Member-voucher",
|
||||||
|
] as const
|
||||||
|
|||||||
@@ -239,5 +239,3 @@ export const validateApiAllTiersSchema = z.record(
|
|||||||
)
|
)
|
||||||
|
|
||||||
export type RedeemLocation = "Non-redeemable" | "On-site" | "Online"
|
export type RedeemLocation = "Non-redeemable" | "On-site" | "Online"
|
||||||
|
|
||||||
export type RewardType = "Tier" | "Member-voucher" | "Surprise" | "Campaign"
|
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ export const rewardQueryRouter = router({
|
|||||||
...reward,
|
...reward,
|
||||||
id: surprise.id,
|
id: surprise.id,
|
||||||
rewardType: surprise.rewardType,
|
rewardType: surprise.rewardType,
|
||||||
|
rewardTierLevel: undefined,
|
||||||
redeemLocation: surprise.redeemLocation,
|
redeemLocation: surprise.redeemLocation,
|
||||||
coupons: "coupon" in surprise ? surprise.coupon || [] : [],
|
coupons: "coupon" in surprise ? surprise.coupon || [] : [],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import type { IconProps } from "@/types/components/icon"
|
import type { IconProps } from "@/types/components/icon"
|
||||||
import type { RESTAURANT_REWARD_IDS, REWARD_IDS } from "@/constants/rewards"
|
import type {
|
||||||
|
COUPON_REWARD_TYPES,
|
||||||
|
RESTAURANT_REWARD_IDS,
|
||||||
|
REWARD_IDS,
|
||||||
|
} from "@/constants/rewards"
|
||||||
|
|
||||||
export interface RewardIconProps extends IconProps {
|
export interface RewardIconProps extends IconProps {
|
||||||
rewardId: string
|
rewardId: string
|
||||||
@@ -9,3 +13,5 @@ export interface RewardIconProps extends IconProps {
|
|||||||
export type RewardId = (typeof REWARD_IDS)[keyof typeof REWARD_IDS]
|
export type RewardId = (typeof REWARD_IDS)[keyof typeof REWARD_IDS]
|
||||||
|
|
||||||
export type RestaurantRewardId = (typeof RESTAURANT_REWARD_IDS)[number]
|
export type RestaurantRewardId = (typeof RESTAURANT_REWARD_IDS)[number]
|
||||||
|
|
||||||
|
export type CouponRewardType = (typeof COUPON_REWARD_TYPES)[number]
|
||||||
|
|||||||
5
utils/membershipLevels.ts
Normal file
5
utils/membershipLevels.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { MembershipLevelEnum } from "@/constants/membershipLevels"
|
||||||
|
|
||||||
|
export function isMembershipLevel(value: string): value is MembershipLevelEnum {
|
||||||
|
return Object.values(MembershipLevelEnum).some((level) => level === value)
|
||||||
|
}
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
import { RESTAURANT_REWARD_IDS, REWARD_IDS } from "@/constants/rewards"
|
import {
|
||||||
|
COUPON_REWARD_TYPES,
|
||||||
|
RESTAURANT_REWARD_IDS,
|
||||||
|
REWARD_IDS,
|
||||||
|
} from "@/constants/rewards"
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
CouponRewardType,
|
||||||
RestaurantRewardId,
|
RestaurantRewardId,
|
||||||
RewardId,
|
RewardId,
|
||||||
} from "@/types/components/myPages/rewards"
|
} from "@/types/components/myPages/rewards"
|
||||||
@@ -40,3 +45,9 @@ export function isRestaurantOnSiteTierReward(
|
|||||||
): boolean {
|
): boolean {
|
||||||
return isOnSiteTierReward(reward) && isRestaurantReward(reward.reward_id)
|
return isOnSiteTierReward(reward) && isRestaurantReward(reward.reward_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isCouponRewardType(
|
||||||
|
type: RewardWithRedeem["rewardType"]
|
||||||
|
): type is CouponRewardType {
|
||||||
|
return COUPON_REWARD_TYPES.some((t) => t === type)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user