refactor(LOY-62): implement code review feedback

This commit is contained in:
Chuma McPhoy
2025-01-13 13:45:13 +01:00
parent e0d8315565
commit da31539610
10 changed files with 61 additions and 143 deletions

View File

@@ -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>
)
}

View File

@@ -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"

View File

@@ -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}

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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 || [] : [],
} }

View File

@@ -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]

View 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)
}

View File

@@ -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)
}