feat(LOY-61): add confirmation box to close when redeemed a reward

This commit is contained in:
Christian Andolf
2025-02-12 14:22:13 +01:00
parent 962836606e
commit b656023bac
15 changed files with 189 additions and 39 deletions

View File

@@ -0,0 +1,48 @@
import { useIntl } from "react-intl"
import Button from "@/components/TempDesignSystem/Button"
import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title"
import useRedeemFlow from "./useRedeemFlow"
import styles from "./redeem.module.css"
export function ConfirmClose({ close }: { close: VoidFunction }) {
const intl = useIntl()
const { setRedeemStep } = useRedeemFlow()
return (
<>
<div className={styles.modalContent}>
<Title level="h3" textAlign="center" textTransform="regular">
{intl.formatMessage({
id: "If you close this your benefit will be removed",
})}
</Title>
<Body>
{intl.formatMessage({
id: "Have you showed this benefit to the hotel staff?",
})}
</Body>
<Body>
{intl.formatMessage({
id: "If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.",
})}
</Body>
</div>
<footer className={styles.modalFooter}>
<Button
onClick={() => setRedeemStep("redeemed")}
intent="primary"
theme="base"
>
{intl.formatMessage({ id: "No, go back" })}
</Button>
<Button onClick={close} intent="secondary" theme="base">
{intl.formatMessage({ id: "Yes, close and remove benefit" })}
</Button>
</footer>
</>
)
}

View File

@@ -10,17 +10,16 @@ import Title from "@/components/TempDesignSystem/Text/Title"
import { toast } from "@/components/TempDesignSystem/Toasts"
import { RewardIcon } from "../../RewardIcon"
import useRedeemFlow from "../useRedeemFlow"
import styles from "../redeem.module.css"
import type { RewardWithRedeem } from "@/server/routers/contentstack/reward/output"
export default function Campaign({ reward }: { reward: RewardWithRedeem }) {
export default function Campaign() {
const { reward } = useRedeemFlow()
const intl = useIntl()
function handleCopy() {
navigator.clipboard.writeText(reward.operaRewardId)
toast.success(intl.formatMessage({ id: "Copied to clipboard" }))
if (!reward) {
return null
}
return (
@@ -42,7 +41,10 @@ export default function Campaign({ reward }: { reward: RewardWithRedeem }) {
</div>
<footer className={styles.modalFooter}>
<Button
onClick={handleCopy}
onClick={() => {
navigator.clipboard.writeText(reward.operaRewardId)
toast.success(intl.formatMessage({ id: "Copied to clipboard" }))
}}
type="button"
variant="icon"
size="small"

View File

@@ -15,19 +15,20 @@ import useRedeemFlow from "../useRedeemFlow"
import styles from "../redeem.module.css"
import type { RewardWithRedeem } from "@/server/routers/contentstack/reward/output"
export default function Tier({
reward,
membershipNumber,
}: {
reward: RewardWithRedeem
membershipNumber: string
}) {
const { onRedeem, redeemStep, setRedeemStep, isRedeeming } =
useRedeemFlow(reward)
const { reward, onRedeem, redeemStep, setRedeemStep, isRedeeming } =
useRedeemFlow()
const intl = useIntl()
if (!reward) {
return null
}
return (
<>
<div className={styles.modalContent}>

View File

@@ -2,14 +2,20 @@
import { useIntl } from "react-intl"
import { dt } from "@/lib/dt"
import Countdown from "@/components/Countdown"
import { CheckCircleIcon } from "@/components/Icons"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import useRedeemFlow from "./useRedeemFlow"
import styles from "./redeem.module.css"
export default function TimedRedeemedBadge() {
const intl = useIntl()
const { timeRemaining, setTimeRemaining } = useRedeemFlow()
const duration = dt.duration(timeRemaining)
return (
<>
@@ -21,7 +27,11 @@ export default function TimedRedeemedBadge() {
})}
</Caption>
</div>
<Countdown />
<Countdown
minutes={duration.minutes()}
seconds={duration.seconds()}
onChange={(newTime) => setTimeRemaining(newTime)}
/>
</>
)
}

View File

@@ -18,6 +18,7 @@ import useLang from "@/hooks/useLang"
import Campaign from "./Flows/Campaign"
import Tier from "./Flows/Tier"
import { ConfirmClose } from "./ConfirmClose"
import { RedeemContext } from "./useRedeemFlow"
import styles from "./redeem.module.css"
@@ -32,12 +33,15 @@ import type { RewardWithRedeem } from "@/server/routers/contentstack/reward/outp
const MotionOverlay = motion(ModalOverlay)
const MotionModal = motion(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) =>
@@ -51,7 +55,16 @@ export default function Redeem({ reward, membershipNumber }: RedeemProps) {
}
return (
<RedeemContext.Provider value={{ redeemStep, setRedeemStep }}>
<RedeemContext.Provider
value={{
reward,
redeemStep,
setRedeemStep,
defaultTimeRemaining: thirtyMinutesInMs,
timeRemaining,
setTimeRemaining,
}}
>
<DialogTrigger
onOpenChange={(isOpen) => setAnimation(isOpen ? "visible" : "hidden")}
>
@@ -75,24 +88,39 @@ export default function Redeem({ reward, membershipNumber }: RedeemProps) {
animate={animation}
>
<Dialog className={styles.dialog} aria-label={reward.label}>
{({ close }) => (
<>
<header className={styles.modalHeader}>
<button
onClick={() => {
utils.contentstack.rewards.current.invalidate({ lang })
close()
}}
type="button"
className={styles.modalClose}
>
<CloseLargeIcon />
</button>
</header>
{({ close }) => {
function closeModal() {
utils.contentstack.rewards.current.invalidate({
lang,
})
close()
}
return (
<>
<header className={styles.modalHeader}>
<button
onClick={() => {
if (redeemStep === "redeemed") {
setRedeemStep("confirm-close")
} else {
closeModal()
}
}}
type="button"
className={styles.modalClose}
>
<CloseLargeIcon />
</button>
</header>
{getRedeemFlow(reward, membershipNumber || "")}
</>
)}
{redeemStep === "confirm-close" ? (
<ConfirmClose close={closeModal} />
) : (
getRedeemFlow(reward, membershipNumber || "")
)}
</>
)
}}
</Dialog>
</MotionModal>
</MotionOverlay>
@@ -130,10 +158,10 @@ const variants = {
function getRedeemFlow(reward: RewardWithRedeem, membershipNumber: string) {
switch (reward.rewardType) {
case "Campaign":
return <Campaign reward={reward} />
return <Campaign />
case "Surprise":
case "Tier":
return <Tier reward={reward} membershipNumber={membershipNumber} />
return <Tier membershipNumber={membershipNumber} />
default:
console.warn("Unsupported reward type for redeem:", reward.rewardType)
return null

View File

@@ -1,6 +1,6 @@
"use client"
import { createContext, useCallback, useContext } from "react"
import { createContext, useCallback, useContext, useEffect } from "react"
import { trpc } from "@/lib/trpc/client"
@@ -10,12 +10,23 @@ import type { RedeemFlowContext } from "@/types/components/myPages/myPage/accoun
import type { RewardWithRedeem } from "@/server/routers/contentstack/reward/output"
export const RedeemContext = createContext<RedeemFlowContext>({
reward: null,
redeemStep: "initial",
setRedeemStep: () => undefined,
defaultTimeRemaining: 0,
timeRemaining: 0,
setTimeRemaining: () => undefined,
})
export default function useRedeemFlow(reward: RewardWithRedeem) {
const { redeemStep, setRedeemStep } = useContext(RedeemContext)
export default function useRedeemFlow() {
const {
reward,
redeemStep,
setRedeemStep,
defaultTimeRemaining,
timeRemaining,
setTimeRemaining,
} = useContext(RedeemContext)
const lang = useLang()
const update = trpc.contentstack.rewards.redeem.useMutation<{
@@ -38,10 +49,19 @@ export default function useRedeemFlow(reward: RewardWithRedeem) {
}
}, [reward, update, setRedeemStep])
useEffect(() => {
if (redeemStep === "initial") {
setTimeRemaining(defaultTimeRemaining)
}
}, [redeemStep, setTimeRemaining, defaultTimeRemaining])
return {
reward,
onRedeem,
redeemStep,
setRedeemStep,
isRedeeming: update.isPending,
timeRemaining,
setTimeRemaining,
}
}

View File

@@ -12,6 +12,7 @@ import type { CountdownProps } from "@/types/components/countdown"
export default function Countdown({
minutes = 30,
seconds = 0,
onChange = () => undefined,
}: CountdownProps) {
const [time, setTime] = useState(dt.duration({ minutes, seconds }))
const timeSeconds = time.asSeconds()
@@ -20,6 +21,7 @@ export default function Countdown({
() => {
setTime((currentTime) => {
const newTime = currentTime.asMilliseconds() - 1000
onChange(newTime)
return dt.duration(newTime)
})
},

View File

@@ -242,6 +242,7 @@
"Guests & Rooms": "Gæster & værelser",
"Gym": "Fitnesscenter",
"Half circle": "Half circle",
"Have you showed this benefit to the hotel staff?": "Har du vist denne fordel for hotellets personale?",
"Hi {firstName}!": "Hei {firstName}!",
"High floor": "Højt niveau",
"Highest level": "Højeste niveau",
@@ -263,7 +264,9 @@
"I accept": "Jeg accepterer",
"I accept the terms and conditions": "Jeg accepterer vilkårene",
"I would like to get my booking confirmation via sms": "Jeg vil gerne få min booking bekræftelse via SMS",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "Hvis ikke, så gå tilbage og gør det, før du lukker dette. Når du lukker dette, vil din fordel blive ugyldig og fjernet fra Mine fordele.",
"If you are not redirected automatically, please <loginLink>click here</loginLink>.": "If you are not redirected automatically, please <loginLink>click here</loginLink>.",
"If you close this your benefit will be removed": "Hvis du lukker dette, vil din fordel blive fjernet",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "Hvis du bestiller et kampagnetilbud eller en firmaaftalepris, skal du bruge en bookingkode. Brug ikke specialtegn som (.) (,) (-) (:).Gem din bookingkode til næste gang du besøger siden ved at klikke “Husk bookingkode”. Vælg ikke dette, hvis du bruger en offentlig computer for at undgå uautoriseret anvendelse af din bookingkode.",
"In adults bed": "i de voksnes seng",
"In crib": "i tremmeseng",
@@ -382,6 +385,7 @@
"No transactions available": "Ingen tilgængelige transaktioner",
"No windows": "No windows",
"No windows but excellent lighting": "No windows but excellent lighting",
"No, go back": "Nej, gå tilbage",
"No, keep card": "Nej, behold kortet",
"Non refundable": "Ikke-refunderbart",
"Non-refundable": "Ikke-refunderbart",
@@ -637,6 +641,7 @@
"Windows with natural daylight": "Vinduer med naturligt dagslys",
"Year": "År",
"Yes": "Ja",
"Yes, close and remove benefit": "Ja, luk og fjern fordel",
"Yes, discard changes": "Ja, kasser ændringer",
"Yes, redeem": "Yes, redeem",
"Yes, remove my card": "Ja, fjern mit kort",

View File

@@ -243,6 +243,7 @@
"Guests & Rooms": "Gäste & Zimmer",
"Gym": "Fitnessstudio",
"Half circle": "Half circle",
"Have you showed this benefit to the hotel staff?": "Haben Sie dem Hotelpersonal diesen Vorteil gezeigt?",
"Hi {firstName}!": "Hallo {firstName}!",
"High floor": "Hohes Level",
"Highest level": "Höchstes Level",
@@ -264,7 +265,9 @@
"I accept": "Ich akzeptiere",
"I accept the terms and conditions": "Ich akzeptiere die Geschäftsbedingungen",
"I would like to get my booking confirmation via sms": "Ich möchte meine Buchungsbestätigung per SMS erhalten",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "Wenn nicht, gehen Sie bitte zurück und tun Sie dies, bevor Sie dies schließen. Sobald Sie dies schließen, verfällt Ihr Vorteil und wird aus „Meine Vorteile“ entfernt.",
"If you are not redirected automatically, please <loginLink>click here</loginLink>.": "If you are not redirected automatically, please <loginLink>click here</loginLink>.",
"If you close this your benefit will be removed": "Wenn Sie dies schließen, wird Ihr Vorteil entfernt",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "Um ein Aktionsangebot oder einen Unternehmenstarif in Anspruch zu nehmen, benötigen Sie einen speziellen Buchungscode. Verwenden Sie keine Sonderzeichen wie (.) (,) (-) (:).Wählen Sie das Kästchen aus, um beim nächsten Besuch der Seite Ihren Buchungscode zu speichern. “Buchungscode speichern”. Deaktivieren Sie das Feld, wenn Sie einen öffentlich zugänglichen Computer verwenden, um unautorisierte Zugriffe auf Ihren Buchungscode zu verhindern.",
"In adults bed": "Im Bett der Eltern",
"In crib": "im Kinderbett",
@@ -383,6 +386,7 @@
"No transactions available": "Keine Transaktionen verfügbar",
"No windows": "No windows",
"No windows but excellent lighting": "No windows but excellent lighting",
"No, go back": "Nein, geh zurück",
"No, keep card": "Nein, Karte behalten",
"Non refundable": "Nicht erstattungsfähig",
"Non-refundable": "Nicht erstattungsfähig",
@@ -637,6 +641,7 @@
"Windows with natural daylight": "Fenster mit natürlichem Tageslicht",
"Year": "Jahr",
"Yes": "Ja",
"Yes, close and remove benefit": "Ja, Vorteil schließen und entfernen",
"Yes, discard changes": "Ja, Änderungen verwerfen",
"Yes, redeem": "Yes, redeem",
"Yes, remove my card": "Ja, meine Karte entfernen",

View File

@@ -246,6 +246,7 @@
"Guests & Rooms": "Guests & Rooms",
"Gym": "Gym",
"Half circle": "Half circle",
"Have you showed this benefit to the hotel staff?": "Have you showed this benefit to the hotel staff?",
"Hi {firstName}!": "Hi {firstName}!",
"High floor": "High floor",
"Highest level": "Highest level",
@@ -268,7 +269,9 @@
"I accept": "I accept",
"I accept the terms and conditions": "I accept the terms and conditions",
"I would like to get my booking confirmation via sms": "I would like to get my booking confirmation via sms",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.",
"If you are not redirected automatically, please <loginLink>click here</loginLink>.": "If you are not redirected automatically, please <loginLink>click here</loginLink>.",
"If you close this your benefit will be removed": "If you close this your benefit will be removed",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.",
"In adults bed": "In adults bed",
"In crib": "In crib",
@@ -388,6 +391,7 @@
"No transactions available": "No transactions available",
"No windows": "No windows",
"No windows but excellent lighting": "No windows but excellent lighting",
"No, go back": "No, go back",
"No, keep card": "No, keep card",
"Non refundable": "Non refundable",
"Non-refundable": "Non-refundable",
@@ -643,6 +647,7 @@
"Windows with natural daylight": "Windows with natural daylight",
"Year": "Year",
"Yes": "Yes",
"Yes, close and remove benefit": "Yes, close and remove benefit",
"Yes, discard changes": "Yes, discard changes",
"Yes, redeem": "Yes, redeem",
"Yes, remove my card": "Yes, remove my card",

View File

@@ -242,6 +242,7 @@
"Guests & Rooms": "Vieraat & Huoneet",
"Gym": "Kuntosali",
"Half circle": "Half circle",
"Have you showed this benefit to the hotel staff?": "Oletko näyttänyt tämän edun hotellin henkilökunnalle?",
"Hi {firstName}!": "Hi {firstName}!",
"High floor": "Korkea taso",
"Highest level": "Korkein taso",
@@ -263,7 +264,9 @@
"I accept": "Hyväksyn",
"I accept the terms and conditions": "Hyväksyn käyttöehdot",
"I would like to get my booking confirmation via sms": "Haluan saada varauksen vahvistuksen SMS-viestillä",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "Jos ei, palaa takaisin ja tee se ennen kuin suljet tämän. Kun suljet tämän, etusi mitätöidään ja poistetaan Omista eduista.",
"If you are not redirected automatically, please <loginLink>click here</loginLink>.": "If you are not redirected automatically, please <loginLink>click here</loginLink>.",
"If you close this your benefit will be removed": "Jos suljet tämän, etusi poistetaan",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "Jos varaat tarjoushinnalla tai yrityksen sopimushinnalla, tarvitset varauskoodin. Älä käytä erikoismerkkejä, kuten (.) (,) (-) (:).Jos haluat tallentaa varauskoodisi seuraavaa varausta varten, rastita tämä ruutu. Älä rastita ruutua, mikäli käytät yleistä tietokonetta ja haluat välttää varauskoodin väärinkäyttöä.",
"In adults bed": "Aikuisten vuoteessa",
"In crib": "Pinnasängyssä",
@@ -382,6 +385,7 @@
"No transactions available": "Ei tapahtumia saatavilla",
"No windows": "No windows",
"No windows but excellent lighting": "No windows but excellent lighting",
"No, go back": "Ei, mene takaisin",
"No, keep card": "Ei, pidä kortti",
"Non refundable": "Ei palautettavissa",
"Non-refundable": "Ei palautettavissa",
@@ -637,6 +641,7 @@
"Windows with natural daylight": "Ikkunat luonnonvalolla",
"Year": "Vuosi",
"Yes": "Kyllä",
"Yes, close and remove benefit": "Kyllä, sulje ja poista etu",
"Yes, discard changes": "Kyllä, hylkää muutokset",
"Yes, redeem": "Yes, redeem",
"Yes, remove my card": "Kyllä, poista korttini",

View File

@@ -241,6 +241,7 @@
"Guests & Rooms": "Gjester & rom",
"Gym": "Treningsstudio",
"Half circle": "Half circle",
"Have you showed this benefit to the hotel staff?": "Har du vist denne fordelen til hotellpersonalet?",
"Hi {firstName}!": "Hei {firstName}!",
"High floor": "Høy nivå",
"Highest level": "Høyeste nivå",
@@ -262,7 +263,9 @@
"I accept": "Jeg aksepterer",
"I accept the terms and conditions": "Jeg aksepterer vilkårene",
"I would like to get my booking confirmation via sms": "Jeg vil gjerne motta bekreftelsen av bestillingen min via sms",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "Hvis ikke, gå tilbake og gjør det før du lukker dette. Når du lukker dette, vil fordelen din bli ugyldig og fjernet fra Mine fordeler.",
"If you are not redirected automatically, please <loginLink>click here</loginLink>.": "If you are not redirected automatically, please <loginLink>click here</loginLink>.",
"If you close this your benefit will be removed": "Hvis du lukker dette, vil fordelen din bli fjernet",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "Om du bestiller et tilbud eller en avtalepris kreves en spesiell bookingkode. Ikke benytt spesialtegn som (.) (,) (-) (:). Lagre din bookingkode til neste gang du besøker siden ved å klikke på \"Husk\". Klikk ikke på \"Husk\" dersom du bruker en offentlig datamaskin, da uvedkommede kan få tilgang til din bookingkode.",
"In adults bed": "i voksnes seng",
"In crib": "i sprinkelseng",
@@ -381,6 +384,7 @@
"No transactions available": "Ingen transaksjoner tilgjengelig",
"No windows": "No windows",
"No windows but excellent lighting": "No windows but excellent lighting",
"No, go back": "Nei, gå tilbake",
"No, keep card": "Nei, behold kortet",
"Non refundable": "Ikke-refunderbart",
"Non-refundable": "Ikke-refunderbart",
@@ -635,6 +639,7 @@
"Windows with natural daylight": "Vinduer med naturlig dagslys",
"Year": "År",
"Yes": "Ja",
"Yes, close and remove benefit": "Ja, lukk og fjern fordel",
"Yes, discard changes": "Ja, forkast endringer",
"Yes, redeem": "Yes, redeem",
"Yes, remove my card": "Ja, fjern kortet mitt",

View File

@@ -241,6 +241,7 @@
"Guests & Rooms": "Gäster & rum",
"Gym": "Gym",
"Half circle": "Half circle",
"Have you showed this benefit to the hotel staff?": "Har du visat denna fördel för hotellpersonalen?",
"Hi {firstName}!": "Hej {firstName}!",
"High floor": "Högt upp",
"Highest level": "Högsta nivå",
@@ -262,7 +263,9 @@
"I accept": "Jag accepterar",
"I accept the terms and conditions": "Jag accepterar villkoren",
"I would like to get my booking confirmation via sms": "Jag vill få min bokningsbekräftelse via sms",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "Om inte, gå tillbaka och gör det innan du stänger detta. När du stänger detta kommer din förmån att ogiltigförklaras och tas bort från Mina förmåner.",
"If you are not redirected automatically, please <loginLink>click here</loginLink>.": "If you are not redirected automatically, please <loginLink>click here</loginLink>.",
"If you close this your benefit will be removed": "Om du stänger detta kommer din förmån att tas bort",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "Om du bokar ett erbjudande eller avtalspris krävs en särskild bokningskod. Använd inga specialtecken som (.) (,) (-) (:). För bokning med koden VOF ber vi dig ringa oss 08-517 517 00.Spara din bokningskod till nästa gång du besöker sidan, genom att klicka i rutan \"Kom ihåg\". Klicka inte i rutan om du använder en allmän dator, då kan obehöriga komma åt din bokningskod.",
"In adults bed": "I vuxens säng",
"In crib": "I spjälsäng",
@@ -381,6 +384,7 @@
"No transactions available": "Inga transaktioner tillgängliga",
"No windows": "Inga fönster",
"No windows but excellent lighting": "Inga fönster men utmärkt belysning",
"No, go back": "Nej, gå tillbaka",
"No, keep card": "Nej, behåll kortet",
"Non refundable": "Ej återbetalningsbar",
"Non-refundable": "Ej återbetalningsbar",
@@ -635,6 +639,7 @@
"Windows with natural daylight": "Fönster med naturligt dagsljus",
"Year": "År",
"Yes": "Ja",
"Yes, close and remove benefit": "Ja, stäng och ta bort förmån",
"Yes, discard changes": "Ja, ignorera ändringar",
"Yes, redeem": "Yes, redeem",
"Yes, remove my card": "Ja, ta bort mitt kort",

View File

@@ -1,4 +1,5 @@
export interface CountdownProps {
minutes?: number
seconds?: number
onChange?: (time: number) => void
}

View File

@@ -1,4 +1,4 @@
import type { Dispatch, ReactNode, SetStateAction } from "react"
import type { Dispatch, SetStateAction } from "react"
import type { z } from "zod"
import type { DynamicContent } from "@/types/trpc/routers/contentstack/blocks"
@@ -42,9 +42,17 @@ export interface ScriptedRewardTextProps {
export type RedeemModalState = "unmounted" | "hidden" | "visible"
export type RedeemStep = "initial" | "confirmation" | "redeemed"
export type RedeemStep =
| "initial"
| "confirmation"
| "redeemed"
| "confirm-close"
export type RedeemFlowContext = {
reward: RewardWithRedeem | null
redeemStep: RedeemStep
setRedeemStep: Dispatch<SetStateAction<RedeemStep>>
defaultTimeRemaining: number
timeRemaining: number
setTimeRemaining: Dispatch<SetStateAction<number>>
}