fix(LOY-39): refetch rewards when redeemed
update expiration date text possible to redeem rewards with coupon code
This commit is contained in:
@@ -2,27 +2,52 @@
|
|||||||
|
|
||||||
import { useRef, useState } from "react"
|
import { useRef, useState } from "react"
|
||||||
|
|
||||||
|
import { trpc } from "@/lib/trpc/client"
|
||||||
|
|
||||||
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 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"
|
||||||
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
import Redeem from "../Redeem"
|
import Redeem from "../Redeem"
|
||||||
|
|
||||||
import styles from "./current.module.css"
|
import styles from "./current.module.css"
|
||||||
|
|
||||||
import type { CurrentRewardsClientProps } from "@/types/components/myPages/myPage/accountPage"
|
import type { CurrentRewardsClientProps } from "@/types/components/myPages/myPage/accountPage"
|
||||||
|
import type {
|
||||||
|
Reward,
|
||||||
|
RewardWithRedeem,
|
||||||
|
} from "@/server/routers/contentstack/reward/output"
|
||||||
|
|
||||||
export default function ClientCurrentRewards({
|
export default function ClientCurrentRewards({
|
||||||
rewards,
|
rewards: initialData,
|
||||||
pageSize,
|
pageSize,
|
||||||
showRedeem,
|
showRedeem,
|
||||||
membershipNumber,
|
membershipNumber,
|
||||||
}: CurrentRewardsClientProps) {
|
}: CurrentRewardsClientProps) {
|
||||||
|
const lang = useLang()
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
const [currentPage, setCurrentPage] = useState(1)
|
const [currentPage, setCurrentPage] = useState(1)
|
||||||
|
|
||||||
|
const { data } = trpc.contentstack.rewards.current.useQuery<{
|
||||||
|
rewards: (Reward | RewardWithRedeem)[]
|
||||||
|
}>(
|
||||||
|
{
|
||||||
|
lang,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
initialData: { rewards: initialData },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const rewards = data.rewards
|
||||||
|
|
||||||
const totalPages = Math.ceil(rewards.length / pageSize)
|
const totalPages = Math.ceil(rewards.length / pageSize)
|
||||||
const startIndex = (currentPage - 1) * pageSize
|
const startIndex = (currentPage - 1) * pageSize
|
||||||
const endIndex = startIndex + pageSize
|
const endIndex = startIndex + pageSize
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { createContext, useCallback, useContext, useState } from "react"
|
import { createContext, useCallback, useContext } from "react"
|
||||||
|
|
||||||
import { trpc } from "@/lib/trpc/client"
|
import { trpc } from "@/lib/trpc/client"
|
||||||
|
|
||||||
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
import type { RedeemFlowContext } from "@/types/components/myPages/myPage/accountPage"
|
import type { RedeemFlowContext } from "@/types/components/myPages/myPage/accountPage"
|
||||||
import type { RewardWithRedeem } from "@/server/routers/contentstack/reward/output"
|
import type { RewardWithRedeem } from "@/server/routers/contentstack/reward/output"
|
||||||
|
|
||||||
@@ -14,18 +16,22 @@ export const RedeemContext = createContext<RedeemFlowContext>({
|
|||||||
|
|
||||||
export default function useRedeemFlow(reward: RewardWithRedeem) {
|
export default function useRedeemFlow(reward: RewardWithRedeem) {
|
||||||
const { redeemStep, setRedeemStep } = useContext(RedeemContext)
|
const { redeemStep, setRedeemStep } = useContext(RedeemContext)
|
||||||
|
const lang = useLang()
|
||||||
|
|
||||||
const update = trpc.contentstack.rewards.redeem.useMutation<{
|
const update = trpc.contentstack.rewards.redeem.useMutation<{
|
||||||
rewards: RewardWithRedeem[]
|
rewards: RewardWithRedeem[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const utils = trpc.useUtils()
|
||||||
|
|
||||||
const onRedeem = useCallback(() => {
|
const onRedeem = useCallback(() => {
|
||||||
if (reward?.id) {
|
if (reward?.id) {
|
||||||
update.mutate(
|
update.mutate(
|
||||||
{ rewardId: reward.id },
|
{ rewardId: reward.id, couponCode: reward.couponCode },
|
||||||
{
|
{
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
setRedeemStep("redeemed")
|
setRedeemStep("redeemed")
|
||||||
|
utils.contentstack.rewards.current.invalidate({ lang })
|
||||||
},
|
},
|
||||||
onError(error) {
|
onError(error) {
|
||||||
console.error("Failed to redeem", error)
|
console.error("Failed to redeem", error)
|
||||||
@@ -33,7 +39,7 @@ export default function useRedeemFlow(reward: RewardWithRedeem) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [reward, update, setRedeemStep])
|
}, [reward, update, setRedeemStep, utils.contentstack.rewards, lang])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onRedeem,
|
onRedeem,
|
||||||
|
|||||||
@@ -36,8 +36,11 @@ export default function SurprisesNotification({
|
|||||||
const [open, setOpen] = useState(true)
|
const [open, setOpen] = useState(true)
|
||||||
const [[selectedSurprise, direction], setSelectedSurprise] = useState([0, 0])
|
const [[selectedSurprise, direction], setSelectedSurprise] = useState([0, 0])
|
||||||
const [showSurprises, setShowSurprises] = useState(false)
|
const [showSurprises, setShowSurprises] = useState(false)
|
||||||
|
const utils = trpc.useUtils()
|
||||||
const unwrap = trpc.contentstack.rewards.unwrap.useMutation({
|
const unwrap = trpc.contentstack.rewards.unwrap.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
utils.contentstack.rewards.current.invalidate({ lang })
|
||||||
|
|
||||||
if (pathname.indexOf(benefits[lang]) !== 0) {
|
if (pathname.indexOf(benefits[lang]) !== 0) {
|
||||||
toast.success(
|
toast.success(
|
||||||
<>
|
<>
|
||||||
@@ -57,6 +60,11 @@ export default function SurprisesNotification({
|
|||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("Failed to unwrap surprise", error)
|
console.error("Failed to unwrap surprise", error)
|
||||||
|
toast.error(
|
||||||
|
<>
|
||||||
|
{intl.formatMessage({ id: "An error occurred. Please try again." })}
|
||||||
|
</>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -77,7 +85,7 @@ export default function SurprisesNotification({
|
|||||||
async function viewRewards() {
|
async function viewRewards() {
|
||||||
const updates = surprises
|
const updates = surprises
|
||||||
.map((surprise) => {
|
.map((surprise) => {
|
||||||
const coupons = surprise.coupons
|
const coupons = surprise.coupon
|
||||||
?.map((coupon) => {
|
?.map((coupon) => {
|
||||||
if (coupon?.couponCode) {
|
if (coupon?.couponCode) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function Slide({ surprise, membershipNumber }: SlideProps) {
|
|||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
const earliestExpirationDate = surprise.coupons?.reduce(
|
const earliestExpirationDate = surprise.coupon?.reduce(
|
||||||
(earliestDate, coupon) => {
|
(earliestDate, coupon) => {
|
||||||
const expiresAt = dt(coupon.expiresAt)
|
const expiresAt = dt(coupon.expiresAt)
|
||||||
return earliestDate.isBefore(expiresAt) ? earliestDate : expiresAt
|
return earliestDate.isBefore(expiresAt) ? earliestDate : expiresAt
|
||||||
@@ -30,7 +30,7 @@ export default function Slide({ surprise, membershipNumber }: SlideProps) {
|
|||||||
<div className={styles.badge}>
|
<div className={styles.badge}>
|
||||||
<Caption>
|
<Caption>
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
{ id: "Expires at the earliest {expirationDate}" },
|
{ id: "Valid through {expirationDate}" },
|
||||||
{
|
{
|
||||||
expirationDate: dt(earliestExpirationDate)
|
expirationDate: dt(earliestExpirationDate)
|
||||||
.locale(lang)
|
.locale(lang)
|
||||||
|
|||||||
@@ -150,7 +150,6 @@
|
|||||||
"Enter your details": "Indtast dine oplysninger",
|
"Enter your details": "Indtast dine oplysninger",
|
||||||
"Events that make an impression": "Events that make an impression",
|
"Events that make an impression": "Events that make an impression",
|
||||||
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
||||||
"Expires at the earliest {expirationDate}": "Udløber tidligst {expirationDate}",
|
|
||||||
"Explore all levels and benefits": "Udforsk alle niveauer og fordele",
|
"Explore all levels and benefits": "Udforsk alle niveauer og fordele",
|
||||||
"Explore nearby": "Udforsk i nærheden",
|
"Explore nearby": "Udforsk i nærheden",
|
||||||
"Extra bed (child) × {count}": "Ekstra seng (barn) × {count}",
|
"Extra bed (child) × {count}": "Ekstra seng (barn) × {count}",
|
||||||
@@ -467,6 +466,7 @@
|
|||||||
"Use code/voucher": "Brug kode/voucher",
|
"Use code/voucher": "Brug kode/voucher",
|
||||||
"User information": "Brugeroplysninger",
|
"User information": "Brugeroplysninger",
|
||||||
"VAT {vat}%": "Moms {vat}%",
|
"VAT {vat}%": "Moms {vat}%",
|
||||||
|
"Valid through {expirationDate}": "Gyldig til og med {expirationDate}",
|
||||||
"View as list": "Vis som liste",
|
"View as list": "Vis som liste",
|
||||||
"View as map": "Vis som kort",
|
"View as map": "Vis som kort",
|
||||||
"View your booking": "Se din booking",
|
"View your booking": "Se din booking",
|
||||||
|
|||||||
@@ -149,7 +149,6 @@
|
|||||||
"Enter your details": "Geben Sie Ihre Daten ein",
|
"Enter your details": "Geben Sie Ihre Daten ein",
|
||||||
"Events that make an impression": "Events that make an impression",
|
"Events that make an impression": "Events that make an impression",
|
||||||
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
||||||
"Expires at the earliest {expirationDate}": "Läuft frühestens am {expirationDate} ab",
|
|
||||||
"Explore all levels and benefits": "Entdecken Sie alle Levels und Vorteile",
|
"Explore all levels and benefits": "Entdecken Sie alle Levels und Vorteile",
|
||||||
"Explore nearby": "Erkunden Sie die Umgebung",
|
"Explore nearby": "Erkunden Sie die Umgebung",
|
||||||
"Extra bed (child) × {count}": "Ekstra seng (Kind) × {count}",
|
"Extra bed (child) × {count}": "Ekstra seng (Kind) × {count}",
|
||||||
@@ -465,6 +464,7 @@
|
|||||||
"Use code/voucher": "Code/Gutschein nutzen",
|
"Use code/voucher": "Code/Gutschein nutzen",
|
||||||
"User information": "Nutzerinformation",
|
"User information": "Nutzerinformation",
|
||||||
"VAT {vat}%": "MwSt. {vat}%",
|
"VAT {vat}%": "MwSt. {vat}%",
|
||||||
|
"Valid through {expirationDate}": "Gültig bis {expirationDate}",
|
||||||
"View as list": "Als Liste anzeigen",
|
"View as list": "Als Liste anzeigen",
|
||||||
"View as map": "Als Karte anzeigen",
|
"View as map": "Als Karte anzeigen",
|
||||||
"View your booking": "Ihre Buchung ansehen",
|
"View your booking": "Ihre Buchung ansehen",
|
||||||
|
|||||||
@@ -161,7 +161,6 @@
|
|||||||
"Enter your details": "Enter your details",
|
"Enter your details": "Enter your details",
|
||||||
"Events that make an impression": "Events that make an impression",
|
"Events that make an impression": "Events that make an impression",
|
||||||
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
||||||
"Expires at the earliest {expirationDate}": "Expires at the earliest {expirationDate}",
|
|
||||||
"Explore all levels and benefits": "Explore all levels and benefits",
|
"Explore all levels and benefits": "Explore all levels and benefits",
|
||||||
"Explore nearby": "Explore nearby",
|
"Explore nearby": "Explore nearby",
|
||||||
"Extra bed (child) × {count}": "Extra bed (child) × {count}",
|
"Extra bed (child) × {count}": "Extra bed (child) × {count}",
|
||||||
@@ -508,6 +507,7 @@
|
|||||||
"VAT": "VAT",
|
"VAT": "VAT",
|
||||||
"VAT amount": "VAT amount",
|
"VAT amount": "VAT amount",
|
||||||
"VAT {vat}%": "VAT {vat}%",
|
"VAT {vat}%": "VAT {vat}%",
|
||||||
|
"Valid through {expirationDate}": "Valid through {expirationDate}",
|
||||||
"View and buy add-ons": "View and buy add-ons",
|
"View and buy add-ons": "View and buy add-ons",
|
||||||
"View as list": "View as list",
|
"View as list": "View as list",
|
||||||
"View as map": "View as map",
|
"View as map": "View as map",
|
||||||
|
|||||||
@@ -150,7 +150,6 @@
|
|||||||
"Enter your details": "Anna tietosi",
|
"Enter your details": "Anna tietosi",
|
||||||
"Events that make an impression": "Events that make an impression",
|
"Events that make an impression": "Events that make an impression",
|
||||||
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
||||||
"Expires at the earliest {expirationDate}": "Päättyy aikaisintaan {expirationDate}",
|
|
||||||
"Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin",
|
"Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin",
|
||||||
"Explore nearby": "Tutustu lähialueeseen",
|
"Explore nearby": "Tutustu lähialueeseen",
|
||||||
"Extra bed (child) × {count}": "Lisävuode (lasta) × {count}",
|
"Extra bed (child) × {count}": "Lisävuode (lasta) × {count}",
|
||||||
@@ -466,6 +465,7 @@
|
|||||||
"Use code/voucher": "Käytä koodia/voucheria",
|
"Use code/voucher": "Käytä koodia/voucheria",
|
||||||
"User information": "Käyttäjän tiedot",
|
"User information": "Käyttäjän tiedot",
|
||||||
"VAT {vat}%": "ALV {vat}%",
|
"VAT {vat}%": "ALV {vat}%",
|
||||||
|
"Valid through {expirationDate}": "Voimassa {expirationDate} asti",
|
||||||
"View as list": "Näytä listana",
|
"View as list": "Näytä listana",
|
||||||
"View as map": "Näytä kartalla",
|
"View as map": "Näytä kartalla",
|
||||||
"View your booking": "Näytä varauksesi",
|
"View your booking": "Näytä varauksesi",
|
||||||
|
|||||||
@@ -149,7 +149,6 @@
|
|||||||
"Enter your details": "Skriv inn detaljene dine",
|
"Enter your details": "Skriv inn detaljene dine",
|
||||||
"Events that make an impression": "Events that make an impression",
|
"Events that make an impression": "Events that make an impression",
|
||||||
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
||||||
"Expires at the earliest {expirationDate}": "Utløper tidligst {expirationDate}",
|
|
||||||
"Explore all levels and benefits": "Utforsk alle nivåer og fordeler",
|
"Explore all levels and benefits": "Utforsk alle nivåer og fordeler",
|
||||||
"Explore nearby": "Utforsk i nærheten",
|
"Explore nearby": "Utforsk i nærheten",
|
||||||
"Extra bed (child) × {count}": "Ekstra seng (barn) × {count}",
|
"Extra bed (child) × {count}": "Ekstra seng (barn) × {count}",
|
||||||
@@ -465,6 +464,7 @@
|
|||||||
"Use code/voucher": "Bruk kode/voucher",
|
"Use code/voucher": "Bruk kode/voucher",
|
||||||
"User information": "Brukerinformasjon",
|
"User information": "Brukerinformasjon",
|
||||||
"VAT {vat}%": "mva {vat}%",
|
"VAT {vat}%": "mva {vat}%",
|
||||||
|
"Valid through {expirationDate}": "Gyldig til og med {expirationDate}",
|
||||||
"View as list": "Vis som liste",
|
"View as list": "Vis som liste",
|
||||||
"View as map": "Vis som kart",
|
"View as map": "Vis som kart",
|
||||||
"View your booking": "Se din bestilling",
|
"View your booking": "Se din bestilling",
|
||||||
|
|||||||
@@ -149,7 +149,6 @@
|
|||||||
"Enter your details": "Ange dina uppgifter",
|
"Enter your details": "Ange dina uppgifter",
|
||||||
"Events that make an impression": "Events that make an impression",
|
"Events that make an impression": "Events that make an impression",
|
||||||
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
"Expiration Date: {expirationDate}": "Expiration Date: {expirationDate}",
|
||||||
"Expires at the earliest {expirationDate}": "Löper ut tidigast {expirationDate}",
|
|
||||||
"Explore all levels and benefits": "Utforska alla nivåer och fördelar",
|
"Explore all levels and benefits": "Utforska alla nivåer och fördelar",
|
||||||
"Explore nearby": "Utforska i närheten",
|
"Explore nearby": "Utforska i närheten",
|
||||||
"Extra bed (child) × {count}": "Extra säng (barn) × {count}",
|
"Extra bed (child) × {count}": "Extra säng (barn) × {count}",
|
||||||
@@ -465,6 +464,7 @@
|
|||||||
"Use code/voucher": "Använd kod/voucher",
|
"Use code/voucher": "Använd kod/voucher",
|
||||||
"User information": "Användarinformation",
|
"User information": "Användarinformation",
|
||||||
"VAT {vat}%": "Moms {vat}%",
|
"VAT {vat}%": "Moms {vat}%",
|
||||||
|
"Valid through {expirationDate}": "Gäller till och med {expirationDate}",
|
||||||
"View as list": "Visa som lista",
|
"View as list": "Visa som lista",
|
||||||
"View as map": "Visa som karta",
|
"View as map": "Visa som karta",
|
||||||
"View your booking": "Visa din bokning",
|
"View your booking": "Visa din bokning",
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ export type Reward = CMSReward & {
|
|||||||
redeemLocation: string | undefined
|
redeemLocation: string | undefined
|
||||||
rewardTierLevel: string | undefined
|
rewardTierLevel: string | undefined
|
||||||
operaRewardId: string
|
operaRewardId: string
|
||||||
|
couponCode: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RewardWithRedeem = CMSRewardWithRedeem & {
|
export type RewardWithRedeem = CMSRewardWithRedeem & {
|
||||||
@@ -176,6 +177,7 @@ export type RewardWithRedeem = CMSRewardWithRedeem & {
|
|||||||
redeemLocation: string | undefined
|
redeemLocation: string | undefined
|
||||||
rewardTierLevel: string | undefined
|
rewardTierLevel: string | undefined
|
||||||
operaRewardId: string
|
operaRewardId: string
|
||||||
|
couponCode: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
// New endpoint related types and schemas.
|
// New endpoint related types and schemas.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
import * as api from "@/lib/api"
|
import * as api from "@/lib/api"
|
||||||
|
import { dt } from "@/lib/dt"
|
||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
import {
|
import {
|
||||||
contentStackBaseWithProtectedProcedure,
|
contentStackBaseWithProtectedProcedure,
|
||||||
@@ -8,6 +9,7 @@ import {
|
|||||||
router,
|
router,
|
||||||
} from "@/server/trpc"
|
} from "@/server/trpc"
|
||||||
|
|
||||||
|
import { langInput } from "../base/input"
|
||||||
import { getAllLoyaltyLevels, getLoyaltyLevel } from "../loyaltyLevel/query"
|
import { getAllLoyaltyLevels, getLoyaltyLevel } from "../loyaltyLevel/query"
|
||||||
import {
|
import {
|
||||||
rewardsAllInput,
|
rewardsAllInput,
|
||||||
@@ -160,118 +162,139 @@ export const rewardQueryRouter = router({
|
|||||||
getByLevelRewardSuccessCounter.add(1)
|
getByLevelRewardSuccessCounter.add(1)
|
||||||
return { level: loyaltyLevelsConfig, rewards: levelsWithRewards }
|
return { level: loyaltyLevelsConfig, rewards: levelsWithRewards }
|
||||||
}),
|
}),
|
||||||
current: contentStackBaseWithProtectedProcedure.query(async function ({
|
current: contentStackBaseWithProtectedProcedure
|
||||||
ctx,
|
.input(langInput.optional()) // lang is required for client, but not for server
|
||||||
}) {
|
.query(async function ({ ctx }) {
|
||||||
getCurrentRewardCounter.add(1)
|
getCurrentRewardCounter.add(1)
|
||||||
|
|
||||||
const isNewEndpoint = env.USE_NEW_REWARDS_ENDPOINT
|
const isNewEndpoint = env.USE_NEW_REWARDS_ENDPOINT
|
||||||
const endpoint = isNewEndpoint
|
const endpoint = isNewEndpoint
|
||||||
? api.endpoints.v1.Profile.Reward.reward
|
? api.endpoints.v1.Profile.Reward.reward
|
||||||
: api.endpoints.v1.Profile.reward
|
: api.endpoints.v1.Profile.reward
|
||||||
|
|
||||||
const apiResponse = await api.get(endpoint, {
|
const apiResponse = await api.get(endpoint, {
|
||||||
cache: undefined, // override defaultOptions
|
cache: undefined, // override defaultOptions
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
},
|
},
|
||||||
next: { revalidate: ONE_HOUR },
|
next: { revalidate: ONE_HOUR },
|
||||||
})
|
|
||||||
|
|
||||||
if (!apiResponse.ok) {
|
|
||||||
const text = await apiResponse.text()
|
|
||||||
getCurrentRewardFailCounter.add(1, {
|
|
||||||
error_type: "http_error",
|
|
||||||
error: JSON.stringify({
|
|
||||||
status: apiResponse.status,
|
|
||||||
statusText: apiResponse.statusText,
|
|
||||||
text,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
console.error(
|
|
||||||
"api.reward error ",
|
if (!apiResponse.ok) {
|
||||||
JSON.stringify({
|
const text = await apiResponse.text()
|
||||||
error: {
|
getCurrentRewardFailCounter.add(1, {
|
||||||
|
error_type: "http_error",
|
||||||
|
error: JSON.stringify({
|
||||||
status: apiResponse.status,
|
status: apiResponse.status,
|
||||||
statusText: apiResponse.statusText,
|
statusText: apiResponse.statusText,
|
||||||
text,
|
text,
|
||||||
},
|
}),
|
||||||
})
|
})
|
||||||
)
|
console.error(
|
||||||
return null
|
"api.reward error ",
|
||||||
}
|
JSON.stringify({
|
||||||
|
error: {
|
||||||
const data = await apiResponse.json()
|
status: apiResponse.status,
|
||||||
|
statusText: apiResponse.statusText,
|
||||||
const validatedApiRewards = isNewEndpoint
|
text,
|
||||||
? validateCategorizedRewardsSchema.safeParse(data)
|
},
|
||||||
: validateApiRewardSchema.safeParse(data)
|
})
|
||||||
|
|
||||||
if (!validatedApiRewards.success) {
|
|
||||||
getCurrentRewardFailCounter.add(1, {
|
|
||||||
locale: ctx.lang,
|
|
||||||
error_type: "validation_error",
|
|
||||||
error: JSON.stringify(validatedApiRewards.error),
|
|
||||||
})
|
|
||||||
console.error(validatedApiRewards.error)
|
|
||||||
console.error(
|
|
||||||
"contentstack.rewards validation error",
|
|
||||||
JSON.stringify({
|
|
||||||
query: { locale: ctx.lang },
|
|
||||||
error: validatedApiRewards.error,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const rewardIds = validatedApiRewards.data
|
|
||||||
.map((reward) => reward?.rewardId)
|
|
||||||
.filter((rewardId): rewardId is string => !!rewardId)
|
|
||||||
.sort()
|
|
||||||
|
|
||||||
const cmsRewards = await getCmsRewards(ctx.lang, rewardIds)
|
|
||||||
|
|
||||||
if (!cmsRewards) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrappedSurprisesIds = validatedApiRewards.data
|
|
||||||
.filter(
|
|
||||||
(reward) =>
|
|
||||||
reward.type === "coupon" &&
|
|
||||||
reward.rewardType === "Surprise" &&
|
|
||||||
"coupon" in reward &&
|
|
||||||
reward.coupon?.some(({ unwrapped }) => !unwrapped)
|
|
||||||
)
|
|
||||||
.map(({ rewardId }) => rewardId)
|
|
||||||
|
|
||||||
const rewards = cmsRewards
|
|
||||||
.filter((cmsReward) => !wrappedSurprisesIds.includes(cmsReward.reward_id))
|
|
||||||
.map((cmsReward) => {
|
|
||||||
const apiReward = validatedApiRewards.data.find(
|
|
||||||
({ rewardId }) => rewardId === cmsReward.reward_id
|
|
||||||
)
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
const data = await apiResponse.json()
|
||||||
...cmsReward,
|
|
||||||
id: apiReward?.id,
|
|
||||||
rewardType: apiReward?.rewardType,
|
|
||||||
redeemLocation: apiReward?.redeemLocation,
|
|
||||||
rewardTierLevel:
|
|
||||||
apiReward && "rewardTierLevel" in apiReward
|
|
||||||
? apiReward.rewardTierLevel
|
|
||||||
: undefined,
|
|
||||||
operaRewardId:
|
|
||||||
apiReward && "operaRewardId" in apiReward
|
|
||||||
? apiReward.operaRewardId
|
|
||||||
: "",
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
getCurrentRewardSuccessCounter.add(1)
|
const validatedApiRewards = isNewEndpoint
|
||||||
|
? validateCategorizedRewardsSchema.safeParse(data)
|
||||||
|
: validateApiRewardSchema.safeParse(data)
|
||||||
|
|
||||||
return { rewards }
|
if (!validatedApiRewards.success) {
|
||||||
}),
|
getCurrentRewardFailCounter.add(1, {
|
||||||
|
locale: ctx.lang,
|
||||||
|
error_type: "validation_error",
|
||||||
|
error: JSON.stringify(validatedApiRewards.error),
|
||||||
|
})
|
||||||
|
console.error(validatedApiRewards.error)
|
||||||
|
console.error(
|
||||||
|
"contentstack.rewards validation error",
|
||||||
|
JSON.stringify({
|
||||||
|
query: { locale: ctx.lang },
|
||||||
|
error: validatedApiRewards.error,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const rewardIds = validatedApiRewards.data
|
||||||
|
.map((reward) => reward?.rewardId)
|
||||||
|
.filter((rewardId): rewardId is string => !!rewardId)
|
||||||
|
.sort()
|
||||||
|
|
||||||
|
const cmsRewards = await getCmsRewards(ctx.lang, rewardIds)
|
||||||
|
|
||||||
|
if (!cmsRewards) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrappedSurprisesIds = validatedApiRewards.data
|
||||||
|
.filter(
|
||||||
|
(reward) =>
|
||||||
|
reward.type === "coupon" &&
|
||||||
|
reward.rewardType === "Surprise" &&
|
||||||
|
"coupon" in reward &&
|
||||||
|
reward.coupon?.some(({ unwrapped }) => !unwrapped)
|
||||||
|
)
|
||||||
|
.map(({ rewardId }) => rewardId)
|
||||||
|
|
||||||
|
const rewards = cmsRewards
|
||||||
|
.filter(
|
||||||
|
(cmsReward) => !wrappedSurprisesIds.includes(cmsReward.reward_id)
|
||||||
|
)
|
||||||
|
.map((cmsReward) => {
|
||||||
|
const apiReward = validatedApiRewards.data.find(
|
||||||
|
({ rewardId }) => rewardId === cmsReward.reward_id
|
||||||
|
)
|
||||||
|
|
||||||
|
const redeemableCoupons =
|
||||||
|
(apiReward &&
|
||||||
|
"coupon" in apiReward &&
|
||||||
|
apiReward.coupon?.filter(
|
||||||
|
(coupon) => coupon.state !== "redeemed" && coupon.unwrapped
|
||||||
|
)) ||
|
||||||
|
[]
|
||||||
|
|
||||||
|
const firstRedeemableCouponToExpire = redeemableCoupons.reduce(
|
||||||
|
(earliest, coupon) => {
|
||||||
|
if (dt(coupon.expiresAt).isBefore(dt(earliest.expiresAt))) {
|
||||||
|
return coupon
|
||||||
|
}
|
||||||
|
return earliest
|
||||||
|
},
|
||||||
|
redeemableCoupons[0]
|
||||||
|
)?.couponCode
|
||||||
|
|
||||||
|
return {
|
||||||
|
...cmsReward,
|
||||||
|
id: apiReward?.id,
|
||||||
|
rewardType: apiReward?.rewardType,
|
||||||
|
redeemLocation: apiReward?.redeemLocation,
|
||||||
|
rewardTierLevel:
|
||||||
|
apiReward && "rewardTierLevel" in apiReward
|
||||||
|
? apiReward.rewardTierLevel
|
||||||
|
: undefined,
|
||||||
|
operaRewardId:
|
||||||
|
apiReward && "operaRewardId" in apiReward
|
||||||
|
? apiReward.operaRewardId
|
||||||
|
: "",
|
||||||
|
couponCode: firstRedeemableCouponToExpire,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
getCurrentRewardSuccessCounter.add(1)
|
||||||
|
|
||||||
|
return { rewards }
|
||||||
|
}),
|
||||||
surprises: contentStackBaseWithProtectedProcedure.query(async ({ ctx }) => {
|
surprises: contentStackBaseWithProtectedProcedure.query(async ({ ctx }) => {
|
||||||
getCurrentRewardCounter.add(1)
|
getCurrentRewardCounter.add(1)
|
||||||
|
|
||||||
@@ -380,7 +403,7 @@ export const rewardQueryRouter = router({
|
|||||||
rewardType: surprise.rewardType,
|
rewardType: surprise.rewardType,
|
||||||
rewardTierLevel: undefined,
|
rewardTierLevel: undefined,
|
||||||
redeemLocation: surprise.redeemLocation,
|
redeemLocation: surprise.redeemLocation,
|
||||||
coupons: "coupon" in surprise ? surprise.coupon || [] : [],
|
coupon: "coupon" in surprise ? surprise.coupon || [] : [],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.flatMap((surprises) => (surprises ? [surprises] : []))
|
.flatMap((surprises) => (surprises ? [surprises] : []))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Reward } from "@/server/routers/contentstack/reward/output"
|
import type { Reward } from "@/server/routers/contentstack/reward/output"
|
||||||
|
|
||||||
export interface Surprise extends Omit<Reward, "operaRewardId"> {
|
export interface Surprise extends Omit<Reward, "operaRewardId" | "couponCode"> {
|
||||||
coupons: { couponCode?: string; expiresAt?: string }[]
|
coupon: { couponCode?: string | undefined; expiresAt?: string }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SurprisesProps {
|
export interface SurprisesProps {
|
||||||
|
|||||||
Reference in New Issue
Block a user