fix(SW-696): add unwrap to surprises
add animations to sliding cards various minor fixes
This commit is contained in:
@@ -2,8 +2,8 @@ import { Suspense } from "react"
|
|||||||
|
|
||||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||||
import Sidebar from "@/components/MyPages/Sidebar"
|
import Sidebar from "@/components/MyPages/Sidebar"
|
||||||
|
import Surprises from "@/components/MyPages/Surprises"
|
||||||
|
|
||||||
// import Surprises from "@/components/MyPages/Surprises"
|
|
||||||
import styles from "./layout.module.css"
|
import styles from "./layout.module.css"
|
||||||
|
|
||||||
export default async function MyPagesLayout({
|
export default async function MyPagesLayout({
|
||||||
@@ -24,9 +24,7 @@ export default async function MyPagesLayout({
|
|||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* TODO: Waiting on new API stuff
|
<Surprises />
|
||||||
<Surprises />
|
|
||||||
*/}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
|
import { AnimatePresence, motion } from "framer-motion"
|
||||||
import { usePathname } from "next/navigation"
|
import { usePathname } from "next/navigation"
|
||||||
import React, { useState } from "react"
|
import React, { useState } from "react"
|
||||||
import { Dialog, Modal, ModalOverlay } from "react-aria-components"
|
import { Dialog, Modal, ModalOverlay } from "react-aria-components"
|
||||||
@@ -21,7 +22,29 @@ import useLang from "@/hooks/useLang"
|
|||||||
|
|
||||||
import styles from "./surprises.module.css"
|
import styles from "./surprises.module.css"
|
||||||
|
|
||||||
import type { SurprisesProps } from "@/types/components/blocks/surprises"
|
import type {
|
||||||
|
Surprise,
|
||||||
|
SurprisesProps,
|
||||||
|
} from "@/types/components/blocks/surprises"
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
enter: (direction: number) => {
|
||||||
|
return {
|
||||||
|
x: direction > 0 ? 1000 : -1000,
|
||||||
|
opacity: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
center: {
|
||||||
|
x: 0,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
exit: (direction: number) => {
|
||||||
|
return {
|
||||||
|
x: direction < 0 ? 1000 : -1000,
|
||||||
|
opacity: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export default function SurprisesNotification({
|
export default function SurprisesNotification({
|
||||||
surprises,
|
surprises,
|
||||||
@@ -30,9 +53,29 @@ export default function SurprisesNotification({
|
|||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const [open, setOpen] = useState(true)
|
const [open, setOpen] = useState(true)
|
||||||
const [selectedSurprise, setSelectedSurprise] = useState(0)
|
const [[selectedSurprise, direction], setSelectedSurprise] = useState([0, 0])
|
||||||
const [showSurprises, setShowSurprises] = useState(false)
|
const [showSurprises, setShowSurprises] = useState(false)
|
||||||
const update = trpc.contentstack.rewards.update.useMutation()
|
const unwrap = trpc.contentstack.rewards.unwrap.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
if (pathname.indexOf(benefits[lang]) !== 0) {
|
||||||
|
toast.success(
|
||||||
|
<>
|
||||||
|
{intl.formatMessage(
|
||||||
|
{ id: "Gift(s) added to your benefits" },
|
||||||
|
{ amount: surprises.length }
|
||||||
|
)}
|
||||||
|
<br />
|
||||||
|
<Link href={benefits[lang]} variant="underscored" color="burgundy">
|
||||||
|
{intl.formatMessage({ id: "Go to My Benefits" })}
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error("Error", error)
|
||||||
|
},
|
||||||
|
})
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
if (!surprises.length) {
|
if (!surprises.length) {
|
||||||
@@ -41,36 +84,48 @@ export default function SurprisesNotification({
|
|||||||
|
|
||||||
const surprise = surprises[selectedSurprise]
|
const surprise = surprises[selectedSurprise]
|
||||||
|
|
||||||
function showSurprise(n: number) {
|
function showSurprise(newDirection: number) {
|
||||||
setSelectedSurprise((surprise) => surprise + n)
|
setSelectedSurprise(([currentIndex]) => [
|
||||||
|
currentIndex + newDirection,
|
||||||
|
newDirection,
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewRewards() {
|
async function viewRewards() {
|
||||||
if (surprise.reward_id) {
|
const updates = surprises
|
||||||
update.mutate({ id: surprise.reward_id })
|
.map((surprise) => {
|
||||||
}
|
const coupons = surprise.coupons
|
||||||
}
|
?.map((coupon) => {
|
||||||
|
if (coupon?.couponCode) {
|
||||||
|
return {
|
||||||
|
rewardId: surprise.id,
|
||||||
|
couponCode: coupon.couponCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(
|
||||||
|
(coupon): coupon is { rewardId: string; couponCode: string } =>
|
||||||
|
!!coupon
|
||||||
|
)
|
||||||
|
|
||||||
function closeModal(close: VoidFunction) {
|
return coupons
|
||||||
viewRewards()
|
})
|
||||||
close()
|
.flat()
|
||||||
|
.filter(
|
||||||
if (pathname.indexOf(benefits[lang]) !== 0) {
|
(coupon): coupon is { rewardId: string; couponCode: string } => !!coupon
|
||||||
toast.success(
|
|
||||||
<>
|
|
||||||
{intl.formatMessage(
|
|
||||||
{ id: "Gift(s) added to your benefits" },
|
|
||||||
{ amount: surprises.length }
|
|
||||||
)}
|
|
||||||
<br />
|
|
||||||
<Link href={benefits[lang]} variant="underscored" color="burgundy">
|
|
||||||
{intl.formatMessage({ id: "Go to My Benefits" })}
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
unwrap.mutate(updates)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const earliestExpirationDate = surprise.coupons?.reduce(
|
||||||
|
(earliestDate, coupon) => {
|
||||||
|
const expiresAt = dt(coupon.expiresAt)
|
||||||
|
return earliestDate.isBefore(expiresAt) ? earliestDate : expiresAt
|
||||||
|
},
|
||||||
|
dt()
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalOverlay
|
<ModalOverlay
|
||||||
className={styles.overlay}
|
className={styles.overlay}
|
||||||
@@ -96,7 +151,10 @@ export default function SurprisesNotification({
|
|||||||
</Caption>
|
</Caption>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => closeModal(close)}
|
onClick={() => {
|
||||||
|
viewRewards()
|
||||||
|
close()
|
||||||
|
}}
|
||||||
type="button"
|
type="button"
|
||||||
className={styles.close}
|
className={styles.close}
|
||||||
>
|
>
|
||||||
@@ -105,23 +163,46 @@ export default function SurprisesNotification({
|
|||||||
</div>
|
</div>
|
||||||
{showSurprises ? (
|
{showSurprises ? (
|
||||||
<>
|
<>
|
||||||
<div className={styles.content}>
|
<AnimatePresence
|
||||||
<Surprise title={surprise.label}>
|
mode="popLayout"
|
||||||
<Body textAlign="center">{surprise.description}</Body>
|
initial={false}
|
||||||
<div className={styles.badge}>
|
custom={direction}
|
||||||
<Caption>
|
>
|
||||||
{intl.formatMessage({ id: "Valid through" })}{" "}
|
<motion.div
|
||||||
{dt(surprise.endsAt)
|
key={selectedSurprise}
|
||||||
.locale(lang)
|
custom={direction}
|
||||||
.format("DD MMM YYYY")}
|
variants={variants}
|
||||||
</Caption>
|
initial="enter"
|
||||||
<Caption>
|
animate="center"
|
||||||
{intl.formatMessage({ id: "Membership ID" })}{" "}
|
exit="exit"
|
||||||
{membershipNumber}
|
transition={{
|
||||||
</Caption>
|
x: { type: "ease", duration: 0.5 },
|
||||||
</div>
|
opacity: { duration: 0.2 },
|
||||||
</Surprise>
|
}}
|
||||||
</div>
|
>
|
||||||
|
<Surprise title={surprise.label}>
|
||||||
|
<Body textAlign="center">{surprise.description}</Body>
|
||||||
|
<div className={styles.badge}>
|
||||||
|
<Caption>
|
||||||
|
{intl.formatMessage(
|
||||||
|
{ id: "Expires at the earliest" },
|
||||||
|
{
|
||||||
|
date: dt(earliestExpirationDate)
|
||||||
|
.locale(lang)
|
||||||
|
.format("D MMM YYYY"),
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</Caption>
|
||||||
|
<Caption>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "Membership ID",
|
||||||
|
})}{" "}
|
||||||
|
{membershipNumber}
|
||||||
|
</Caption>
|
||||||
|
</div>
|
||||||
|
</Surprise>
|
||||||
|
</motion.div>
|
||||||
|
</AnimatePresence>
|
||||||
{surprises.length > 1 && (
|
{surprises.length > 1 && (
|
||||||
<>
|
<>
|
||||||
<nav className={styles.nav}>
|
<nav className={styles.nav}>
|
||||||
@@ -154,10 +235,10 @@ export default function SurprisesNotification({
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.content}>
|
<Surprise title={intl.formatMessage({ id: "Surprise!" })}>
|
||||||
{surprises.length > 1 ? (
|
<Body textAlign="center">
|
||||||
<Surprise title={intl.formatMessage({ id: "Surprise!" })}>
|
{surprises.length > 1 ? (
|
||||||
<Body textAlign="center">
|
<>
|
||||||
{intl.formatMessage<React.ReactNode>(
|
{intl.formatMessage<React.ReactNode>(
|
||||||
{
|
{
|
||||||
id: "You have <b>#</b> gifts waiting for you!",
|
id: "You have <b>#</b> gifts waiting for you!",
|
||||||
@@ -171,32 +252,22 @@ export default function SurprisesNotification({
|
|||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
id: "Hurry up and use them before they expire!",
|
id: "Hurry up and use them before they expire!",
|
||||||
})}
|
})}
|
||||||
</Body>
|
</>
|
||||||
<Caption>
|
) : (
|
||||||
{intl.formatMessage({
|
intl.formatMessage({
|
||||||
id: "You'll find all your gifts in 'My benefits'",
|
id: "We have a special gift waiting for you!",
|
||||||
})}
|
})
|
||||||
</Caption>
|
)}
|
||||||
</Surprise>
|
</Body>
|
||||||
) : (
|
<Caption>
|
||||||
<Surprise title={intl.formatMessage({ id: "Surprise!" })}>
|
{intl.formatMessage({
|
||||||
<Body textAlign="center">
|
id: "You'll find all your gifts in 'My benefits'",
|
||||||
{intl.formatMessage({
|
})}
|
||||||
id: "We have a special gift waiting for you!",
|
</Caption>
|
||||||
})}
|
|
||||||
</Body>
|
|
||||||
<Caption>
|
|
||||||
{intl.formatMessage({
|
|
||||||
id: "You'll find all your gifts in 'My benefits'",
|
|
||||||
})}
|
|
||||||
</Caption>
|
|
||||||
</Surprise>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
intent="primary"
|
intent="primary"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
viewRewards()
|
|
||||||
setShowSurprises(true)
|
setShowSurprises(true)
|
||||||
}}
|
}}
|
||||||
size="medium"
|
size="medium"
|
||||||
@@ -211,7 +282,7 @@ export default function SurprisesNotification({
|
|||||||
{ amount: surprises.length }
|
{ amount: surprises.length }
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</Surprise>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@@ -230,18 +301,20 @@ function Surprise({
|
|||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={styles.content}>
|
||||||
<Image
|
<Image
|
||||||
src="/_static/img/loyalty-award.png"
|
src="/_static/img/loyalty-award.png"
|
||||||
width={113}
|
width={113}
|
||||||
height={125}
|
height={125}
|
||||||
alt="Gift"
|
alt="Gift"
|
||||||
/>
|
/>
|
||||||
<Title textAlign="center" level="h4">
|
<header>
|
||||||
{title}
|
<Title textAlign="center" level="h4">
|
||||||
</Title>
|
{title}
|
||||||
|
</Title>
|
||||||
|
</header>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
|
import { env } from "@/env/server"
|
||||||
import { getProfile } from "@/lib/trpc/memoizedRequests"
|
import { getProfile } from "@/lib/trpc/memoizedRequests"
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
import SurprisesNotification from "./SurprisesNotification"
|
import SurprisesNotification from "./SurprisesNotification"
|
||||||
|
|
||||||
export default async function Surprises() {
|
export default async function Surprises() {
|
||||||
|
if (env.HIDE_FOR_NEXT_RELEASE) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const user = await getProfile()
|
const user = await getProfile()
|
||||||
|
|
||||||
if (!user || "error" in user) {
|
if (!user || "error" in user) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
||||||
&[data-entering] {
|
&[data-entering] {
|
||||||
animation: modal-fade 200ms;
|
animation: modal-fade 200ms ease-in;
|
||||||
}
|
}
|
||||||
&[data-exiting] {
|
&[data-exiting] {
|
||||||
animation: modal-fade 200ms reverse ease-in;
|
animation: modal-fade 200ms reverse ease-in;
|
||||||
@@ -40,6 +40,17 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
background-image: url("/_static/img/confetti.svg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center 40%;
|
||||||
|
content: "";
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
animation: modal-fade 200ms ease-in;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +76,10 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--Spacing-x2);
|
gap: var(--Spacing-x2);
|
||||||
padding-bottom: var(--Spacing-x2);
|
padding-bottom: var(--Spacing-x2);
|
||||||
|
|
||||||
|
/* to hide sliding cards */
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
@@ -90,8 +105,10 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
padding: 0 var(--Spacing-x3);
|
padding: 0 var(--Spacing-x3);
|
||||||
gap: var(--Spacing-x2);
|
gap: var(--Spacing-x2);
|
||||||
|
min-height: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
@@ -103,6 +120,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav button {
|
.nav button {
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
&:nth-child(1) {
|
&:nth-child(1) {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,7 @@
|
|||||||
"Enter destination or hotel": "Indtast destination eller hotel",
|
"Enter destination or hotel": "Indtast destination eller hotel",
|
||||||
"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",
|
||||||
|
"Expires at the earliest": "Udløber tidligst {date}",
|
||||||
"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",
|
||||||
"Extras to your booking": "Tillæg til din booking",
|
"Extras to your booking": "Tillæg til din booking",
|
||||||
@@ -380,7 +381,6 @@
|
|||||||
"Use bonus cheque": "Brug Bonus Cheque",
|
"Use bonus cheque": "Brug Bonus Cheque",
|
||||||
"Use code/voucher": "Brug kode/voucher",
|
"Use code/voucher": "Brug kode/voucher",
|
||||||
"User information": "Brugeroplysninger",
|
"User information": "Brugeroplysninger",
|
||||||
"Valid through": "Gyldig igennem",
|
|
||||||
"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",
|
||||||
|
|||||||
@@ -118,6 +118,7 @@
|
|||||||
"Enter destination or hotel": "Reiseziel oder Hotel eingeben",
|
"Enter destination or hotel": "Reiseziel oder Hotel eingeben",
|
||||||
"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",
|
||||||
|
"Expires at the earliest": "Läuft frühestens am {date} 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",
|
||||||
"Extras to your booking": "Extras zu Ihrer Buchung",
|
"Extras to your booking": "Extras zu Ihrer Buchung",
|
||||||
@@ -378,7 +379,6 @@
|
|||||||
"Use bonus cheque": "Bonusscheck nutzen",
|
"Use bonus cheque": "Bonusscheck nutzen",
|
||||||
"Use code/voucher": "Code/Gutschein nutzen",
|
"Use code/voucher": "Code/Gutschein nutzen",
|
||||||
"User information": "Nutzerinformation",
|
"User information": "Nutzerinformation",
|
||||||
"Valid through": "Gültig bis",
|
|
||||||
"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",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"Enter destination or hotel": "Enter destination or hotel",
|
"Enter destination or hotel": "Enter destination or hotel",
|
||||||
"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",
|
||||||
|
"Expires at the earliest": "Expires at the earliest {date}",
|
||||||
"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",
|
||||||
"Extras to your booking": "Extras to your booking",
|
"Extras to your booking": "Extras to your booking",
|
||||||
@@ -410,7 +411,6 @@
|
|||||||
"User information": "User information",
|
"User information": "User information",
|
||||||
"VAT": "VAT",
|
"VAT": "VAT",
|
||||||
"VAT amount": "VAT amount",
|
"VAT amount": "VAT amount",
|
||||||
"Valid through": "Valid through",
|
|
||||||
"View as list": "View as list",
|
"View as list": "View as list",
|
||||||
"View as map": "View as map",
|
"View as map": "View as map",
|
||||||
"View terms": "View terms",
|
"View terms": "View terms",
|
||||||
|
|||||||
@@ -118,6 +118,7 @@
|
|||||||
"Enter destination or hotel": "Anna kohde tai hotelli",
|
"Enter destination or hotel": "Anna kohde tai hotelli",
|
||||||
"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",
|
||||||
|
"Expires at the earliest": "Päättyy aikaisintaan {date}",
|
||||||
"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",
|
||||||
"Extras to your booking": "Varauksessa lisäpalveluita",
|
"Extras to your booking": "Varauksessa lisäpalveluita",
|
||||||
@@ -380,7 +381,6 @@
|
|||||||
"Use bonus cheque": "Käytä bonussekkiä",
|
"Use bonus cheque": "Käytä bonussekkiä",
|
||||||
"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",
|
||||||
"Valid through": "Voimassa läpi",
|
|
||||||
"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",
|
||||||
|
|||||||
@@ -117,6 +117,7 @@
|
|||||||
"Enter destination or hotel": "Skriv inn destinasjon eller hotell",
|
"Enter destination or hotel": "Skriv inn destinasjon eller hotell",
|
||||||
"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",
|
||||||
|
"Expires at the earliest": "Utløper tidligst {date}",
|
||||||
"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",
|
||||||
"Extras to your booking": "Tilvalg til bestillingen din",
|
"Extras to your booking": "Tilvalg til bestillingen din",
|
||||||
@@ -377,7 +378,6 @@
|
|||||||
"Use bonus cheque": "Bruk bonussjekk",
|
"Use bonus cheque": "Bruk bonussjekk",
|
||||||
"Use code/voucher": "Bruk kode/voucher",
|
"Use code/voucher": "Bruk kode/voucher",
|
||||||
"User information": "Brukerinformasjon",
|
"User information": "Brukerinformasjon",
|
||||||
"Valid through": "Gyldig gjennom",
|
|
||||||
"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",
|
||||||
|
|||||||
@@ -117,6 +117,7 @@
|
|||||||
"Enter destination or hotel": "Ange destination eller hotell",
|
"Enter destination or hotel": "Ange destination eller hotell",
|
||||||
"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",
|
||||||
|
"Expires at the earliest": "Löper ut tidigast {date}",
|
||||||
"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",
|
||||||
"Extras to your booking": "Extra tillval till din bokning",
|
"Extras to your booking": "Extra tillval till din bokning",
|
||||||
@@ -377,7 +378,6 @@
|
|||||||
"Use bonus cheque": "Använd bonuscheck",
|
"Use bonus cheque": "Använd bonuscheck",
|
||||||
"Use code/voucher": "Använd kod/voucher",
|
"Use code/voucher": "Använd kod/voucher",
|
||||||
"User information": "Användarinformation",
|
"User information": "Användarinformation",
|
||||||
"Valid through": "Giltig t.o.m.",
|
|
||||||
"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",
|
||||||
|
|||||||
1
public/_static/img/confetti.svg
Normal file
1
public/_static/img/confetti.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 14 KiB |
@@ -18,6 +18,9 @@ export const rewardsCurrentInput = z.object({
|
|||||||
lang: z.nativeEnum(Lang).optional(),
|
lang: z.nativeEnum(Lang).optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const rewardsUpdateInput = z.object({
|
export const rewardsUpdateInput = z.array(
|
||||||
id: z.string(),
|
z.object({
|
||||||
})
|
rewardId: z.string(),
|
||||||
|
couponCode: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { notFound } from "@/server/errors/trpc"
|
|||||||
import {
|
import {
|
||||||
contentStackBaseWithProtectedProcedure,
|
contentStackBaseWithProtectedProcedure,
|
||||||
contentStackBaseWithServiceProcedure,
|
contentStackBaseWithServiceProcedure,
|
||||||
|
protectedProcedure,
|
||||||
router,
|
router,
|
||||||
} from "@/server/trpc"
|
} from "@/server/trpc"
|
||||||
|
|
||||||
@@ -16,7 +17,6 @@ import {
|
|||||||
} from "./input"
|
} from "./input"
|
||||||
import {
|
import {
|
||||||
Reward,
|
Reward,
|
||||||
SurpriseReward,
|
|
||||||
validateApiRewardSchema,
|
validateApiRewardSchema,
|
||||||
validateCategorizedRewardsSchema,
|
validateCategorizedRewardsSchema,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
@@ -36,8 +36,6 @@ import {
|
|||||||
getUniqueRewardIds,
|
getUniqueRewardIds,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
|
|
||||||
import { Surprise } from "@/types/components/blocks/surprises"
|
|
||||||
|
|
||||||
const ONE_HOUR = 60 * 60
|
const ONE_HOUR = 60 * 60
|
||||||
|
|
||||||
export const rewardQueryRouter = router({
|
export const rewardQueryRouter = router({
|
||||||
@@ -327,42 +325,84 @@ export const rewardQueryRouter = router({
|
|||||||
|
|
||||||
getCurrentRewardSuccessCounter.add(1)
|
getCurrentRewardSuccessCounter.add(1)
|
||||||
|
|
||||||
const surprises =
|
const surprises = validatedApiRewards.data
|
||||||
validatedApiRewards.data
|
// TODO: Add predicates once legacy endpoints are removed
|
||||||
.filter(
|
.filter((reward) => {
|
||||||
(reward): reward is SurpriseReward =>
|
if (reward?.rewardType !== "Surprise") {
|
||||||
reward?.type === "coupon" && reward?.rewardType === "Surprise"
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("coupon" in reward)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const unwrappedCoupons =
|
||||||
|
reward.coupon?.filter((coupon) => !coupon.unwrapped) || []
|
||||||
|
if (unwrappedCoupons.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.map((surprise) => {
|
||||||
|
const reward = cmsRewards.find(
|
||||||
|
({ reward_id }) => surprise.rewardId === reward_id
|
||||||
)
|
)
|
||||||
.map((surprise) => {
|
|
||||||
const reward = cmsRewards.find(
|
|
||||||
({ reward_id }) => surprise.rewardId === reward_id
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!reward) {
|
if (!reward) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...reward,
|
...reward,
|
||||||
id: surprise.id,
|
id: surprise.id,
|
||||||
endsAt: surprise.endsAt,
|
coupons: "coupon" in surprise ? surprise.coupon || [] : [],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter((surprise): surprise is Surprise => !!surprise) ?? []
|
.flatMap((surprises) => (surprises ? [surprises] : []))
|
||||||
|
|
||||||
return surprises
|
return surprises
|
||||||
}),
|
}),
|
||||||
update: contentStackBaseWithProtectedProcedure
|
unwrap: protectedProcedure
|
||||||
.input(rewardsUpdateInput)
|
.input(rewardsUpdateInput)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const response = await Promise.resolve({ ok: true })
|
const promises = input.map(({ rewardId, couponCode }) => {
|
||||||
// const response = await api.post(api.endpoints.v1.rewards, {
|
return api.post(api.endpoints.v1.Profile.Reward.unwrap, {
|
||||||
// body: {
|
body: {
|
||||||
// ids: [input.id],
|
rewardId,
|
||||||
// },
|
couponCode,
|
||||||
// })
|
},
|
||||||
if (!response.ok) {
|
headers: {
|
||||||
return false
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const responses = await Promise.all(promises)
|
||||||
|
|
||||||
|
const errors = await Promise.all(
|
||||||
|
responses.map(async (apiResponse) => {
|
||||||
|
if (!apiResponse.ok) {
|
||||||
|
const text = await apiResponse.text()
|
||||||
|
console.error(
|
||||||
|
"contentstack.unwrap validation error",
|
||||||
|
JSON.stringify({
|
||||||
|
error: {
|
||||||
|
status: apiResponse.status,
|
||||||
|
statusText: apiResponse.statusText,
|
||||||
|
text,
|
||||||
|
},
|
||||||
|
query: {},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
if (errors.filter((ok) => !ok).length > 0) {
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import {
|
import { Reward } from "@/server/routers/contentstack/reward/output"
|
||||||
Reward,
|
|
||||||
SurpriseReward,
|
|
||||||
} from "@/server/routers/contentstack/reward/output"
|
|
||||||
|
|
||||||
export interface Surprise extends Reward {
|
export interface Surprise extends Reward {
|
||||||
endsAt: SurpriseReward["endsAt"]
|
coupons: { couponCode?: string; expiresAt?: string }[]
|
||||||
id: SurpriseReward["id"]
|
id?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SurprisesProps {
|
export interface SurprisesProps {
|
||||||
|
|||||||
Reference in New Issue
Block a user