diff --git a/app/[lang]/(live)/loading.tsx b/app/[lang]/(live)/(protected)/loading.tsx similarity index 100% rename from app/[lang]/(live)/loading.tsx rename to app/[lang]/(live)/(protected)/loading.tsx diff --git a/app/[lang]/(live)/(protected)/my-pages/[...path]/page.tsx b/app/[lang]/(live)/(protected)/my-pages/[...path]/page.tsx index ddec0b715..cef74e464 100644 --- a/app/[lang]/(live)/(protected)/my-pages/[...path]/page.tsx +++ b/app/[lang]/(live)/(protected)/my-pages/[...path]/page.tsx @@ -3,6 +3,8 @@ import { Suspense } from "react" import { serverClient } from "@/lib/trpc/server" import Blocks from "@/components/Blocks" +import SectionHeader from "@/components/Section/Header" +import Preamble from "@/components/TempDesignSystem/Text/Preamble" import Title from "@/components/TempDesignSystem/Text/Title" import TrackingSDK from "@/components/TrackingSDK" import { getIntl } from "@/i18n" @@ -27,13 +29,20 @@ export default async function MyPages({ } const { tracking, accountPage } = accountPageRes + const { heading, preamble, content } = accountPage return ( <>
- {accountPage.heading} - {accountPage.content?.length ? ( - + + + {content?.length ? ( + ) : (

{intl.formatMessage({ id: "No content published" })}

)} diff --git a/app/[lang]/(partner)/(sas)/(protected)/loading.tsx b/app/[lang]/(partner)/(sas)/(protected)/loading.tsx new file mode 100644 index 000000000..aaf3fb778 --- /dev/null +++ b/app/[lang]/(partner)/(sas)/(protected)/loading.tsx @@ -0,0 +1,11 @@ +import LoadingSpinner from "@/components/LoadingSpinner" + +import { SASModal } from "./sas-x-scandic/components/SASModal" + +export default function Loading() { + return ( + + + + ) +} diff --git a/components/Blocks/CardsGrid.tsx b/components/Blocks/CardsGrid.tsx index ca2349db7..2c962d063 100644 --- a/components/Blocks/CardsGrid.tsx +++ b/components/Blocks/CardsGrid.tsx @@ -35,7 +35,8 @@ export default function CardsGrid({ {cards_grid.cards.map((card, index) => { diff --git a/components/Blocks/DynamicContent/Overview/index.tsx b/components/Blocks/DynamicContent/Overview/index.tsx index bfc6e9692..481ad7694 100644 --- a/components/Blocks/DynamicContent/Overview/index.tsx +++ b/components/Blocks/DynamicContent/Overview/index.tsx @@ -26,7 +26,13 @@ export default async function Overview({ return ( - + diff --git a/components/Blocks/DynamicContent/Points/Overview/index.tsx b/components/Blocks/DynamicContent/Points/Overview/index.tsx index 9ff59ccc9..03db76599 100644 --- a/components/Blocks/DynamicContent/Points/Overview/index.tsx +++ b/components/Blocks/DynamicContent/Points/Overview/index.tsx @@ -26,7 +26,13 @@ export default async function PointsOverview({ return ( - + diff --git a/components/Blocks/DynamicContent/SAS/LinkedAccounts/Card/TierLevelCard.tsx b/components/Blocks/DynamicContent/SAS/LinkedAccounts/Card/TierLevelCard.tsx new file mode 100644 index 000000000..5de787145 --- /dev/null +++ b/components/Blocks/DynamicContent/SAS/LinkedAccounts/Card/TierLevelCard.tsx @@ -0,0 +1,207 @@ +import { cva, type VariantProps } from "class-variance-authority" +import Image from "next/image" + +import RocketLaunch from "@/components/Icons/RocketLaunch" +import SkeletonShimmer from "@/components/SkeletonShimmer" +import Caption from "@/components/TempDesignSystem/Text/Caption" +import Footnote from "@/components/TempDesignSystem/Text/Footnote" +import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" +import { getIntl } from "@/i18n" + +import styles from "./tierLevelCard.module.css" + +import type { ComponentProps, ReactNode } from "react" + +type BoostState = "boostedInOtherSystem" | "boostedInThisSystem" | "notBoosted" + +type BaseProps = { + points: number + tier: string + boostState: BoostState +} + +type BoostedInOther = BaseProps & { + boostState: "boostedInOtherSystem" + boostedTier: string + boostExpiration: Date +} + +type BoostedInThis = BaseProps & { + boostState: "boostedInThisSystem" +} + +type NotBoosted = BaseProps & { + boostState: "notBoosted" +} + +const variants = cva(styles.tierlevelcard, { + variants: { + bonusSystem: { + scandic: styles.scandic, + sas: styles.sas, + }, + }, +}) + +type Props = VariantProps & + (BoostedInOther | BoostedInThis | NotBoosted) +export async function TierLevelCard({ + points, + tier, + bonusSystem, + ...boosted +}: Props) { + const intl = await getIntl() + + const className = variants({ bonusSystem }) + + return ( +
+ {boosted.boostState === "boostedInOtherSystem" && ( +
+
+ + {boosted.boostedTier} + + + {intl.formatMessage({ id: "Level upgrade" })} + +
+ + {intl.formatMessage( + { + id: "Upgrade expires {upgradeExpires, date, short}", + }, + { upgradeExpires: boosted.boostExpiration } + )} + +
+ )} +
+
+
+ + + {tier} + + +
+ {bonusSystemSpecifics[bonusSystem!].logo} +
+
+ + {boosted.boostState === "boostedInThisSystem" && ( + + + + {intl.formatMessage({ + id: bonusSystemSpecifics[bonusSystem!].boostTextId, + })} + + + )} +
+ + + {intl.formatMessage( + { id: "{points, number} Bonus points" }, + { points } + )} + +
+
+ ) +} + +export function TierLevelCardSkeleton({ + bonusSystem, +}: { + bonusSystem?: NonNullable +}) { + const className = variants({ bonusSystem }) + + return ( +
+
+
+
+ + + + + {bonusSystem && ( +
+ {bonusSystemSpecifics[bonusSystem!].logo} +
+ )} + {!bonusSystem && } +
+
+ + + + +
+
+ ) +} + +type BonusSystemSpecifics = { + boostTextId: string + logo: ReactNode + rocketLaunchColor: ComponentProps["color"] + color: ComponentProps["color"] +} + +type BonusSystemMapping = { + [bonusSystem in NonNullable< + VariantProps["bonusSystem"] + >]: BonusSystemSpecifics +} + +const bonusSystemSpecifics: BonusSystemMapping = { + scandic: { + boostTextId: "Your Friends level has upgraded your Eurobonus level", + rocketLaunchColor: "red", + color: "burgundy", + logo: ( + Scandic logo + ), + }, + sas: { + boostTextId: "Your Eurobonus level has upgraded your friends level", + rocketLaunchColor: "blue", + color: "uiTextActive", + logo: ( + <> + SAS logo + + Eurobonus + + + ), + }, +} diff --git a/components/Blocks/DynamicContent/SAS/LinkedAccounts/Card/tierLevelCard.module.css b/components/Blocks/DynamicContent/SAS/LinkedAccounts/Card/tierLevelCard.module.css new file mode 100644 index 000000000..37449d8eb --- /dev/null +++ b/components/Blocks/DynamicContent/SAS/LinkedAccounts/Card/tierLevelCard.module.css @@ -0,0 +1,93 @@ +.tierlevelcard { + display: flex; + flex-direction: column; + + justify-content: space-between; + + border-radius: var(--Corner-radius-Medium); + background: white; + box-shadow: 0px 0px 8px 3px #0000001a; + + overflow: hidden; + + width: 100%; + min-height: 176px; + + @media screen and (min-width: 768px) { + max-width: 335px; + } + + &.scandic { + background: white; + color: var(--Main-Brand-Burgundy); + + .boostedInfo { + background: linear-gradient(86.64deg, #faf6f2 0%, #f4d5c8 100.91%); + border-radius: var(--Corner-radius-Medium); + } + } + + &.sas { + background: #f0f4ff; + color: var(--Main-Brand-DarkBlue); + + .boostedInfo { + background: linear-gradient(90deg, #f0f4ff 0%, #bdcdff 100%); + border-radius: var(--Corner-radius-Medium); + } + } +} + +.boostedInfo { + display: flex; + flex-direction: column; + gap: var(--Spacing-x-half); + box-shadow: 0px 0px 4px 2px #0000001a; + + flex: 0; + padding: var(--Spacing-x2) var(--Spacing-x-one-and-half); +} + +.boostedTier { + display: flex; + justify-content: space-between; + gap: var(--Spacing-x-one-and-half); +} + +.baseInfo { + flex: 1; + display: flex; + flex-direction: column; + gap: var(--Spacing-x-one-and-half); + + padding: var(--Spacing-x2) var(--Spacing-x-one-and-half); + + justify-content: space-between; +} + +.header { + display: flex; + flex-direction: column; + gap: var(--Spacing-x2); +} + +.tierInfo { + display: flex; + justify-content: space-between; + text-transform: uppercase; + gap: var(--Spacing-x1); +} + +.logoContainer { + display: flex; + justify-content: center; + align-items: center; + gap: var(--Spacing-x1); +} + +.footnote { + display: flex; + align-items: center; + justify-content: flex-start; + gap: var(--Spacing-x1); +} diff --git a/components/Blocks/DynamicContent/SAS/LinkedAccounts/LevelUpgradeButton.tsx b/components/Blocks/DynamicContent/SAS/LinkedAccounts/LevelUpgradeButton.tsx new file mode 100644 index 000000000..661d0c568 --- /dev/null +++ b/components/Blocks/DynamicContent/SAS/LinkedAccounts/LevelUpgradeButton.tsx @@ -0,0 +1,50 @@ +"use client" + +import { useIntl } from "react-intl" + +import { trpc } from "@/lib/trpc/client" + +import Refresh from "@/components/Icons/Refresh" +import { Loading } from "@/components/Loading" +import Button from "@/components/TempDesignSystem/Button" +import { toast } from "@/components/TempDesignSystem/Toasts" + +import styles from "./levelupgradebutton.module.css" + +export function LevelUpgradeButton() { + const intl = useIntl() + + const { mutate, isPending } = + trpc.partner.sas.performLevelUpgrade.useMutation({ + onSuccess() { + toast.success(intl.formatMessage({ id: "Level upgraded" })) + }, + onError() { + toast.error(intl.formatMessage({ id: "Failed to upgrade level" })) + }, + }) + + const handleClick = () => { + mutate() + } + + return ( + <> + + + ) +} diff --git a/components/Blocks/DynamicContent/SAS/LinkedAccounts/UnlinkSAS.tsx b/components/Blocks/DynamicContent/SAS/LinkedAccounts/UnlinkSAS.tsx new file mode 100644 index 000000000..c1d8a8b6a --- /dev/null +++ b/components/Blocks/DynamicContent/SAS/LinkedAccounts/UnlinkSAS.tsx @@ -0,0 +1,47 @@ +"use client" + +import { useRouter } from "next/navigation" +import { useIntl } from "react-intl" + +import { trpc } from "@/lib/trpc/client" + +import { Loading } from "@/components/Loading" +import Link from "@/components/TempDesignSystem/Link" +import { toast } from "@/components/TempDesignSystem/Toasts" + +export function UnlinkSAS() { + const intl = useIntl() + const router = useRouter() + + const { mutate, isPending } = trpc.partner.sas.unlinkAccount.useMutation({ + onSuccess() { + toast.success(intl.formatMessage({ id: "Account unlinked, reloading" })) + // TODO: reload page + router.push("/en/scandic-friends/my-pages") + }, + onError() { + toast.error(intl.formatMessage({ id: "Failed to unlink account" })) + }, + }) + + const handleClick = (event: React.MouseEvent) => { + event.preventDefault() + mutate() + } + + if (isPending) { + return + } + + return ( + + {intl.formatMessage({ id: "Unlink accounts" })} + + ) +} diff --git a/components/Blocks/DynamicContent/SAS/LinkedAccounts/index.tsx b/components/Blocks/DynamicContent/SAS/LinkedAccounts/index.tsx new file mode 100644 index 000000000..09054b527 --- /dev/null +++ b/components/Blocks/DynamicContent/SAS/LinkedAccounts/index.tsx @@ -0,0 +1,94 @@ +import { Suspense } from "react" +import { setTimeout } from "timers/promises" + +import { TIER_TO_FRIEND_MAP } from "@/constants/membershipLevels" +import { env } from "@/env/server" +import { getProfile } from "@/lib/trpc/memoizedRequests" + +import SectionContainer from "@/components/Section/Container" +import SectionHeader from "@/components/Section/Header" +import SectionLink from "@/components/Section/Link" + +import { TierLevelCard, TierLevelCardSkeleton } from "./Card/TierLevelCard" +import { LevelUpgradeButton } from "./LevelUpgradeButton" +import { UnlinkSAS } from "./UnlinkSAS" + +import styles from "./linkedAccounts.module.css" + +type Props = { + title?: string + link?: { href: string; text: string } + subtitle?: string +} + +export default async function SASLinkedAccount({ + title, + subtitle, + link, +}: Props) { + if (!env.SAS_ENABLED) { + return null + } + + return ( + <> + + + +
+ }> + + +
+
+
+ + +
+ + ) +} + +function TierLevelCardsSkeleton() { + return ( + <> + + + + ) +} + +async function TierLevelCards() { + console.log("[SAS] Fetching tier level cards") + await setTimeout(2_000) + console.log("[SAS] AFTER Fetching tier level cards") + + const user = await getProfile() + if (!user || "error" in user) { + return null + } + + const sasPoints = 250_000 + const sfPoints = user.membership?.currentPoints || 0 + const sfLevelName = + TIER_TO_FRIEND_MAP[user.membership?.membershipLevel ?? "L1"] + + return ( + <> + + + + ) +} diff --git a/components/Blocks/DynamicContent/SAS/LinkedAccounts/levelupgradebutton.module.css b/components/Blocks/DynamicContent/SAS/LinkedAccounts/levelupgradebutton.module.css new file mode 100644 index 000000000..d6db44998 --- /dev/null +++ b/components/Blocks/DynamicContent/SAS/LinkedAccounts/levelupgradebutton.module.css @@ -0,0 +1,20 @@ +.button { + position: relative; + display: flex; + justify-content: center; + align-items: center; + + & .textContainer { + display: flex; + justify-content: center; + align-items: center; + gap: var(--Spacing-x1); + } +} + +.loading { + position: absolute; + &.hidden { + opacity: 0; + } +} diff --git a/components/Blocks/DynamicContent/SAS/LinkedAccounts/linkedAccounts.module.css b/components/Blocks/DynamicContent/SAS/LinkedAccounts/linkedAccounts.module.css new file mode 100644 index 000000000..5ed1930ba --- /dev/null +++ b/components/Blocks/DynamicContent/SAS/LinkedAccounts/linkedAccounts.module.css @@ -0,0 +1,31 @@ +.divider { + margin-top: var(--Spacing-x2); +} + +@media screen and (min-width: 768px) { + .divider { + display: none; + } +} + +.cardsContainer { + display: flex; + flex-direction: column; + gap: var(--Spacing-x3); + justify-content: flex-start; + + @media screen and (min-width: 768px) { + flex-direction: row; + } +} + +.mutationSection { + display: flex; + flex-direction: column; + gap: var(--Spacing-x2); + align-items: center; + + @media screen and (min-width: 768px) { + flex-direction: row; + } +} diff --git a/components/Blocks/DynamicContent/SectionWrapper/index.tsx b/components/Blocks/DynamicContent/SectionWrapper/index.tsx index 6c95d16df..c27e9ab26 100644 --- a/components/Blocks/DynamicContent/SectionWrapper/index.tsx +++ b/components/Blocks/DynamicContent/SectionWrapper/index.tsx @@ -21,7 +21,8 @@ export default function SectionWrapper({ link={dynamic_content.link} preamble={dynamic_content.subtitle} title={dynamic_content.title} - topTitle={firstItem} + headingAs={firstItem ? "h3" : "h4"} + headingLevel={firstItem ? "h1" : "h2"} /> ) : null} {children} diff --git a/components/Blocks/DynamicContent/index.tsx b/components/Blocks/DynamicContent/index.tsx index 44dd15859..fb6f27f8a 100644 --- a/components/Blocks/DynamicContent/index.tsx +++ b/components/Blocks/DynamicContent/index.tsx @@ -11,6 +11,7 @@ import ExpiringPoints from "@/components/Blocks/DynamicContent/Points/ExpiringPo import PointsOverview from "@/components/Blocks/DynamicContent/Points/Overview" import CurrentRewardsBlock from "@/components/Blocks/DynamicContent/Rewards/CurrentRewards" import NextLevelRewardsBlock from "@/components/Blocks/DynamicContent/Rewards/NextLevel" +import SASLinkedAccount from "@/components/Blocks/DynamicContent/SAS/LinkedAccounts" import SASTierComparisonBlock from "@/components/Blocks/DynamicContent/SASTierComparison" import SignupFormWrapper from "@/components/Blocks/DynamicContent/SignupFormWrapper" import SignUpVerification from "@/components/Blocks/DynamicContent/SignUpVerification" @@ -75,6 +76,8 @@ function DynamicContentBlocks(props: DynamicContentProps) { return case DynamicContentEnum.Blocks.components.upcoming_stays: return + case DynamicContentEnum.Blocks.components.sas_linked_account: + return case DynamicContentEnum.Blocks.components.sas_tier_comparison: return ( - +
{columns.map(({ id, column }) => ( + + + ) +} diff --git a/components/Icons/RocketLaunch.tsx b/components/Icons/RocketLaunch.tsx new file mode 100644 index 000000000..e63fbb1ff --- /dev/null +++ b/components/Icons/RocketLaunch.tsx @@ -0,0 +1,27 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function RocketLaunch({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + ) +} diff --git a/components/Icons/icon.module.css b/components/Icons/icon.module.css index bf9612fca..ae5bd0875 100644 --- a/components/Icons/icon.module.css +++ b/components/Icons/icon.module.css @@ -96,3 +96,8 @@ .disabled * { fill: var(--Base-Text-Disabled); } + +.currentColor, +.currentColor * { + fill: currentColor; +} diff --git a/components/Icons/variants.ts b/components/Icons/variants.ts index 5c6bce695..97a55ea5e 100644 --- a/components/Icons/variants.ts +++ b/components/Icons/variants.ts @@ -24,6 +24,7 @@ const config = { uiTextMediumContrast: styles.uiTextMediumContrast, uiTextPlaceholder: styles.uiTextPlaceholder, disabled: styles.disabled, + currentColor: styles.currentColor, }, }, defaultVariants: { diff --git a/components/Loading/index.tsx b/components/Loading/index.tsx new file mode 100644 index 000000000..fbf6c35e4 --- /dev/null +++ b/components/Loading/index.tsx @@ -0,0 +1,46 @@ +import styles from "./loading.module.css" + +type Size = "small" | "medium" | "large" +type Color = "white" | "burgundy" +type Props = { + size?: Size + color?: Color + className?: string +} + +export function Loading({ + size = "medium", + color = "burgundy", + className, +}: Props) { + return ( + + + + + + + + + + + ) +} + +const colors: { [color in Color]: string } = { + white: "#fff", + burgundy: "var(--Scandic-Brand-Burgundy)", +} + +const sizes: { [size in Size]: number } = { + small: 16, + medium: 20, + large: 24, +} diff --git a/components/Loading/loading.module.css b/components/Loading/loading.module.css new file mode 100644 index 000000000..2b6e93081 --- /dev/null +++ b/components/Loading/loading.module.css @@ -0,0 +1,43 @@ +.loading { + --animationDuration: 0.8s; + --delayPerItem: calc(var(--animationDuration) / 8); + + & circle { + animation: fadeInOut var(--animationDuration) infinite linear both; + transform-origin: center; + + &:nth-child(2) { + animation-delay: calc(var(--delayPerItem) * -7); + } + &:nth-child(3) { + animation-delay: calc(var(--delayPerItem) * -6); + } + &:nth-child(4) { + animation-delay: calc(var(--delayPerItem) * -5); + } + &:nth-child(5) { + animation-delay: calc(var(--delayPerItem) * -4); + } + &:nth-child(6) { + animation-delay: calc(var(--delayPerItem) * -3); + } + &:nth-child(7) { + animation-delay: calc(var(--delayPerItem) * -2); + } + &:nth-child(8) { + animation-delay: calc(var(--delayPerItem) * -1); + } + } +} + +@keyframes fadeInOut { + 0%, + 20%, + 80%, + 100% { + opacity: 0.3; + } + 50% { + opacity: 1; + } +} diff --git a/components/Section/Header/index.tsx b/components/Section/Header/index.tsx index 3bc5b6f01..67b2ff1d5 100644 --- a/components/Section/Header/index.tsx +++ b/components/Section/Header/index.tsx @@ -5,14 +5,27 @@ import SectionLink from "../Link" import styles from "./header.module.css" -import type { HeaderProps } from "@/types/components/myPages/header" +import type { HeadingProps } from "@/components/TempDesignSystem/Text/Title/title" + +export type HeaderProps = { + link?: { + href: string + text: string + } + preamble?: string | null + textTransform?: HeadingProps["textTransform"] + title?: string | null + headingLevel?: HeadingProps["level"] + headingAs?: HeadingProps["as"] +} export default function SectionHeader({ link, preamble, title, - topTitle = false, textTransform, + headingAs = "h4", + headingLevel = "h2", }: HeaderProps) { if (!title && !preamble && !link) { return null @@ -21,9 +34,9 @@ export default function SectionHeader({ return (
{title} diff --git a/i18n/dictionaries/da.json b/i18n/dictionaries/da.json index f0cf470da..1d479cffb 100644 --- a/i18n/dictionaries/da.json +++ b/i18n/dictionaries/da.json @@ -14,6 +14,7 @@ "Accessibility": "Tilgængelighed", "Accessibility at {hotel}": "Tilgængelighed på {hotel}", "Accessible room": "Tilgængelighedsrum", + "Account unlinked, reloading": "Account unlinked, reloading", "Accounts are already linked": "Accounts are already linked", "Active": "Aktiv", "Activities": "Aktiviteter", @@ -94,6 +95,7 @@ "Cancel": "Afbestille", "Cancellation policy": "Cancellation policy", "Change room": "Skift værelse", + "Check for level upgrade": "Check for level upgrade", "Check in": "Check ind", "Check in from: {checkInTime}": "Indtjekning fra: {checkInTime}", "Check out": "Check ud", @@ -184,6 +186,8 @@ "Extras to your booking": "Tillæg til din booking", "FAQ": "Ofte stillede spørgsmål", "Failed to delete credit card, please try again later.": "Kunne ikke slette kreditkort. Prøv venligst igen senere.", + "Failed to unlink account": "Failed to unlink account", + "Failed to upgrade level": "Failed to upgrade level", "Failed to verify membership": "Medlemskab ikke verificeret", "Fair": "Messe", "Family friendly": "Familievenlig", @@ -276,6 +280,7 @@ "Level 6": "Niveau 6", "Level 7": "Niveau 7", "Level up to unlock": "Stig i niveau for at låse op", + "Level upgraded": "Level upgraded", "Level {level}": "Niveau {level}", "Link my accounts": "Link my accounts", "Link your accounts": "Link your accounts", @@ -550,6 +555,8 @@ "Tuesday": "Tirsdag", "Type of bed": "Sengtype", "Type of room": "Værelsestype", + "Unlink accounts": "Unlink accounts", + "Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}", "Use Bonus Cheque": "Brug Bonus Cheque", "Use code/voucher": "Brug kode/voucher", "User information": "Brugeroplysninger", @@ -609,6 +616,7 @@ "You must accept the terms and conditions": "You must accept the terms and conditions", "You'll find all your gifts in 'My benefits'": "Du finder alle dine gaver i 'Mine fordele'", "Your Challenges Conquer & Earn!": "Dine udfordringer Overvind og tjen!", + "Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level", "Your accounts are connected": "Your accounts are connected", "Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Din booking er bekræftet, men vi kunne ikke verificere dit medlemskab. Hvis du har booket med et medlemstilbud, skal du enten vise dit eksisterende medlemskab ved check-in, blive medlem eller betale prisdifferencen ved check-in. Tilmelding er foretrukket online før opholdet.", "Your card was successfully removed!": "Dit kort blev fjernet!", @@ -666,6 +674,7 @@ "{min} to {max} characters": "{min} til {max} tegn", "{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} tilgængelig", "{number} km to city center": "{number} km til centrum", + "{points, number} Bonus points": "{points, number} Bonus points", "{pointsAmount, number} points": "{pointsAmount, number} point", "{points} spendable points expiring by {date}": "{points} Brugbare point udløber den {date}", "{price} {currency}": "{price} {currency}", diff --git a/i18n/dictionaries/de.json b/i18n/dictionaries/de.json index f636e4265..4e9896b97 100644 --- a/i18n/dictionaries/de.json +++ b/i18n/dictionaries/de.json @@ -14,6 +14,7 @@ "Accessibility": "Zugänglichkeit", "Accessibility at {hotel}": "Barrierefreiheit im {hotel}", "Accessible room": "Barrierefreies Zimmer", + "Account unlinked, reloading": "Account unlinked, reloading", "Accounts are already linked": "Accounts are already linked", "Active": "Aktiv", "Activities": "Aktivitäten", @@ -95,6 +96,7 @@ "Cancel": "Stornieren", "Cancellation policy": "Cancellation policy", "Change room": "Zimmer ändern", + "Check for level upgrade": "Check for level upgrade", "Check in": "Einchecken", "Check in from: {checkInTime}": "Check-in ab: {checkInTime}", "Check out": "Auschecken", @@ -185,6 +187,8 @@ "Extras to your booking": "Extras zu Ihrer Buchung", "FAQ": "Häufig gestellte Fragen", "Failed to delete credit card, please try again later.": "Kreditkarte konnte nicht gelöscht werden. Bitte versuchen Sie es später noch einmal.", + "Failed to unlink account": "Failed to unlink account", + "Failed to upgrade level": "Failed to upgrade level", "Failed to verify membership": "Medlemskab nicht verifiziert", "Fair": "Messe", "Family friendly": "Familienfreundlich", @@ -277,6 +281,7 @@ "Level 6": "Level 6", "Level 7": "Level 7", "Level up to unlock": "Zum Freischalten aufsteigen", + "Level upgraded": "Level upgraded", "Level {level}": "Level {level}", "Link my accounts": "Link my accounts", "Link your accounts": "Link your accounts", @@ -550,6 +555,8 @@ "Tuesday": "Dienstag", "Type of bed": "Bettentyp", "Type of room": "Zimmerart", + "Unlink accounts": "Unlink accounts", + "Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}", "Use Bonus Cheque": "Bonusscheck nutzen", "Use code/voucher": "Code/Gutschein nutzen", "User information": "Nutzerinformation", @@ -609,6 +616,7 @@ "You must accept the terms and conditions": "You must accept the terms and conditions", "You'll find all your gifts in 'My benefits'": "Alle Ihre Geschenke finden Sie unter „Meine Vorteile“", "Your Challenges Conquer & Earn!": "Meistern Sie Ihre Herausforderungen und verdienen Sie Geld!", + "Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level", "Your accounts are connected": "Your accounts are connected", "Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Ihre Buchung ist bestätigt, aber wir konnten Ihr Mitglied nicht verifizieren. Wenn Sie mit einem Mitgliederrabatt gebucht haben, müssen Sie entweder Ihr vorhandenes Mitgliedschaftsnummer bei der Anreise präsentieren, ein Mitglied werden oder die Preisdifferenz bei der Anreise bezahlen. Die Anmeldung ist vorzugsweise online vor der Aufenthaltsdauer erfolgreich.", "Your card was successfully removed!": "Ihre Karte wurde erfolgreich entfernt!", @@ -666,6 +674,7 @@ "{min} to {max} characters": "{min} zu {max} figuren", "{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} verfügbar", "{number} km to city center": "{number} km zum Stadtzentrum", + "{points, number} Bonus points": "{points, number} Bonus points", "{pointsAmount, number} points": "{pointsAmount, number} punkte", "{points} spendable points expiring by {date}": "{points} Einlösbare punkte verfallen bis zum {date}", "{price} {currency}": "{price} {currency}", diff --git a/i18n/dictionaries/en.json b/i18n/dictionaries/en.json index 1f212895f..7dd036c61 100644 --- a/i18n/dictionaries/en.json +++ b/i18n/dictionaries/en.json @@ -15,6 +15,7 @@ "Accessibility": "Accessibility", "Accessibility at {hotel}": "Accessibility at {hotel}", "Accessible room": "Accessibility room", + "Account unlinked, reloading": "Account unlinked, reloading", "Accounts are already linked": "Accounts are already linked", "Active": "Active", "Activities": "Activities", @@ -97,6 +98,7 @@ "Cancellation policy": "Cancellation policy", "Change room": "Change room", "Changes can be made until {time}, {date} pending availability.": "Changes can be made until {time}, {date} pending availability.", + "Check for level upgrade": "Check for level upgrade", "Check in": "Check in", "Check in from: {checkInTime}": "Check in from: {checkInTime}", "Check out": "Check out", @@ -187,6 +189,8 @@ "Extras to your booking": "Extras to your booking", "FAQ": "FAQ", "Failed to delete credit card, please try again later.": "Failed to delete credit card, please try again later.", + "Failed to unlink account": "Failed to unlink account", + "Failed to upgrade level": "Failed to upgrade level", "Failed to verify membership": "Failed to verify membership", "Fair": "Fair", "Family friendly": "Family friendly", @@ -279,6 +283,7 @@ "Level 6": "Level 6", "Level 7": "Level 7", "Level up to unlock": "Level up to unlock", + "Level upgraded": "Level upgraded", "Level {level}": "Level {level}", "Link my accounts": "Link my accounts", "Link your accounts": "Link your accounts", @@ -554,6 +559,8 @@ "Tuesday": "Tuesday", "Type of bed": "Type of bed", "Type of room": "Type of room", + "Unlink accounts": "Unlink accounts", + "Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}", "Use Bonus Cheque": "Use Bonus Cheque", "Use code/voucher": "Use code/voucher", "User information": "User information", @@ -613,6 +620,7 @@ "You must accept the terms and conditions": "You must accept the terms and conditions", "You'll find all your gifts in 'My benefits'": "You'll find all your gifts in 'My benefits'", "Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!", + "Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level", "Your accounts are connected": "Your accounts are connected", "Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.", "Your card was successfully removed!": "Your card was successfully removed!", @@ -668,6 +676,7 @@ "{min} to {max} characters": "{min} to {max} characters", "{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} available", "{number} km to city center": "{number} km to city center", + "{points, number} Bonus points": "{points, number} Bonus points", "{pointsAmount, number} points": "{pointsAmount, number} points", "{points} spendable points expiring by {date}": "{points} spendable points expiring by {date}", "{price} {currency}": "{price} {currency}", diff --git a/i18n/dictionaries/fi.json b/i18n/dictionaries/fi.json index d7089b7c7..30259bede 100644 --- a/i18n/dictionaries/fi.json +++ b/i18n/dictionaries/fi.json @@ -14,6 +14,7 @@ "Accessibility": "Saavutettavuus", "Accessibility at {hotel}": "Esteettömyys {hotel}", "Accessible room": "Esteetön huone", + "Account unlinked, reloading": "Account unlinked, reloading", "Accounts are already linked": "Accounts are already linked", "Active": "Aktiivinen", "Activities": "Aktiviteetit", @@ -93,6 +94,7 @@ "Cancel": "Peruuttaa", "Cancellation policy": "Cancellation policy", "Change room": "Vaihda huonetta", + "Check for level upgrade": "Check for level upgrade", "Check in": "Sisäänkirjautuminen", "Check in from: {checkInTime}": "Sisäänkirjautuminen alkaen: {checkInTime}", "Check out": "Uloskirjautuminen", @@ -184,6 +186,8 @@ "Extras to your booking": "Varauksessa lisäpalveluita", "FAQ": "Usein kysytyt kysymykset", "Failed to delete credit card, please try again later.": "Luottokortin poistaminen epäonnistui, yritä myöhemmin uudelleen.", + "Failed to unlink account": "Failed to unlink account", + "Failed to upgrade level": "Failed to upgrade level", "Failed to verify membership": "Jäsenyys ei verifioitu", "Fair": "Messukeskus", "Family friendly": "Perheystävällinen", @@ -276,6 +280,7 @@ "Level 6": "Taso 6", "Level 7": "Taso 7", "Level up to unlock": "Nosta taso avataksesi lukituksen", + "Level upgraded": "Level upgraded", "Level {level}": "Taso {level}", "Link my accounts": "Link my accounts", "Link your accounts": "Link your accounts", @@ -550,6 +555,8 @@ "Tuesday": "Tiistai", "Type of bed": "Vuodetyyppi", "Type of room": "Huonetyyppi", + "Unlink accounts": "Unlink accounts", + "Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}", "Use Bonus Cheque": "Käytä bonussekkiä", "Use code/voucher": "Käytä koodia/voucheria", "User information": "Käyttäjän tiedot", @@ -609,6 +616,7 @@ "You must accept the terms and conditions": "You must accept the terms and conditions", "You'll find all your gifts in 'My benefits'": "You'll find all your gifts in 'My benefits'", "Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!", + "Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level", "Your accounts are connected": "Your accounts are connected", "Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Varauksesi on vahvistettu, mutta jäsenyytesi ei voitu vahvistaa. Jos olet bookeutunut jäsenyysalennoilla, sinun on joko esitettävä olemassa olevan jäsenyysnumero tarkistukseen, tulla jäseneksi tai maksamaan hinnan eron hotellissa. Jäsenyyden tilittäminen on suositeltavampaa tehdä verkkoon ennen majoittumista.", "Your card was successfully removed!": "Korttisi poistettiin onnistuneesti!", @@ -666,6 +674,7 @@ "{min} to {max} characters": "{min} to {max} hahmoja", "{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} saatavilla", "{number} km to city center": "{number} km Etäisyys kaupunkiin", + "{points, number} Bonus points": "{points, number} Bonus points", "{pointsAmount, number} points": "{pointsAmount, number} pistettä", "{points} spendable points expiring by {date}": "{points} pistettä vanhenee {date} mennessä", "{price} {currency}": "{price} {currency}", diff --git a/i18n/dictionaries/no.json b/i18n/dictionaries/no.json index a076c74d8..3ce8fb31b 100644 --- a/i18n/dictionaries/no.json +++ b/i18n/dictionaries/no.json @@ -14,6 +14,7 @@ "Accessibility": "Tilgjengelighet", "Accessibility at {hotel}": "Tilgjengelighet på {hotel}", "Accessible room": "Tilgjengelighetsrom", + "Account unlinked, reloading": "Account unlinked, reloading", "Accounts are already linked": "Accounts are already linked", "Active": "Aktiv", "Activities": "Aktiviteter", @@ -93,6 +94,7 @@ "Cancel": "Avbryt", "Cancellation policy": "Cancellation policy", "Change room": "Endre rom", + "Check for level upgrade": "Check for level upgrade", "Check in": "Sjekk inn", "Check in from: {checkInTime}": "Innsjekking fra: {checkInTime}", "Check out": "Sjekk ut", @@ -183,6 +185,8 @@ "Extras to your booking": "Tilvalg til bestillingen din", "FAQ": "Ofte stilte spørsmål", "Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.", + "Failed to unlink account": "Failed to unlink account", + "Failed to upgrade level": "Failed to upgrade level", "Failed to verify membership": "Medlemskap ikke verifisert", "Fair": "Messe", "Family friendly": "Familievennlig", @@ -275,6 +279,7 @@ "Level 6": "Nivå 6", "Level 7": "Nivå 7", "Level up to unlock": "Nivå opp for å låse opp", + "Level upgraded": "Level upgraded", "Level {level}": "Nivå {level}", "Link my accounts": "Link my accounts", "Link your accounts": "Link your accounts", @@ -548,6 +553,8 @@ "Tuesday": "Tirsdag", "Type of bed": "Sengtype", "Type of room": "Romtype", + "Unlink accounts": "Unlink accounts", + "Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}", "Use Bonus Cheque": "Bruk bonussjekk", "Use code/voucher": "Bruk kode/voucher", "User information": "Brukerinformasjon", @@ -607,6 +614,7 @@ "You must accept the terms and conditions": "You must accept the terms and conditions", "You'll find all your gifts in 'My benefits'": "Du finner alle gavene dine i 'Mine fordeler'", "Your Challenges Conquer & Earn!": "Dine utfordringer Erobre og tjen!", + "Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level", "Your accounts are connected": "Your accounts are connected", "Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Din bestilling er bekreftet, men vi kunne ikke verifisere medlemskapet ditt. Hvis du har booke ut med et medlemsrabatt, må du enten presentere eksisterende medlemsnummer ved check-in, bli medlem eller betale prisdifferansen ved hotellet. Registrering er foretrukket gjort online før oppholdet.", "Your card was successfully removed!": "Kortet ditt ble fjernet!", @@ -664,6 +672,7 @@ "{min} to {max} characters": "{min} til {max} tegn", "{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} tilgjengelig", "{number} km to city center": "{number} km til sentrum", + "{points, number} Bonus points": "{points, number} Bonus points", "{pointsAmount, number} points": "{pointsAmount, number} poeng", "{points} spendable points expiring by {date}": "{points} Brukbare poeng utløper innen {date}", "{price} {currency}": "{price} {currency}", diff --git a/i18n/dictionaries/sv.json b/i18n/dictionaries/sv.json index 6dab1ccf4..ba1787cc8 100644 --- a/i18n/dictionaries/sv.json +++ b/i18n/dictionaries/sv.json @@ -14,6 +14,7 @@ "Accessibility": "Tillgänglighet", "Accessibility at {hotel}": "Tillgänglighet på {hotel}", "Accessible room": "Tillgänglighetsrum", + "Account unlinked, reloading": "Account unlinked, reloading", "Accounts are already linked": "Accounts are already linked", "Active": "Aktiv", "Activities": "Aktiviteter", @@ -93,6 +94,7 @@ "Cancel": "Avbryt", "Cancellation policy": "Cancellation policy", "Change room": "Ändra rum", + "Check for level upgrade": "Check for level upgrade", "Check in": "Checka in", "Check in from: {checkInTime}": "Incheckning från: {checkInTime}", "Check out": "Checka ut", @@ -183,6 +185,8 @@ "Extras to your booking": "Extra tillval till din bokning", "FAQ": "FAQ", "Failed to delete credit card, please try again later.": "Det gick inte att ta bort kreditkortet, försök igen senare.", + "Failed to unlink account": "Failed to unlink account", + "Failed to upgrade level": "Failed to upgrade level", "Failed to verify membership": "Medlemskap inte verifierat", "Fair": "Mässa", "Family friendly": "Familjevänligt", @@ -275,6 +279,7 @@ "Level 6": "Nivå 6", "Level 7": "Nivå 7", "Level up to unlock": "Levla upp för att låsa upp", + "Level upgraded": "Level upgraded", "Level {level}": "Nivå {level}", "Link my accounts": "Link my accounts", "Link your accounts": "Link your accounts", @@ -548,6 +553,8 @@ "Tuesday": "Tisdag", "Type of bed": "Sängtyp", "Type of room": "Rumstyp", + "Unlink accounts": "Unlink accounts", + "Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}", "Use Bonus Cheque": "Använd bonuscheck", "Use code/voucher": "Använd kod/voucher", "User information": "Användarinformation", @@ -607,6 +614,7 @@ "You must accept the terms and conditions": "You must accept the terms and conditions", "You'll find all your gifts in 'My benefits'": "Du hittar alla dina gåvor i 'Mina förmåner'", "Your Challenges Conquer & Earn!": "Dina utmaningar Erövra och tjäna!", + "Your SAS level has upgraded your friends level": "Your SAS level has upgraded your friends level", "Your accounts are connected": "Your accounts are connected", "Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Din bokning är bekräftad, men vi kunde inte verifiera ditt medlemskap. Om du har bokat med ett medlemsrabatt måste du antingen presentera ditt befintliga medlemsnummer vid check-in, bli medlem eller betala prisdifferensen vid hotell. Registrering är föredragen gjord online före vistelsen.", "Your card was successfully removed!": "Ditt kort har tagits bort!", @@ -666,6 +674,7 @@ "{min} to {max} characters": "{min} till {max} tecken", "{numberOfRooms, plural, one {# room type} other {# room types}} available": "{numberOfRooms, plural, one {# room type} other {# room types}} tillgängliga", "{number} km to city center": "{number} km till centrum", + "{points, number} Bonus points": "{points, number} Bonus points", "{pointsAmount, number} points": "{pointsAmount, number} poäng", "{points} spendable points expiring by {date}": "{points} poäng förfaller {date}", "{price} {currency}": "{price} {currency}", diff --git a/lib/graphql/Query/AccountPage/AccountPage.graphql b/lib/graphql/Query/AccountPage/AccountPage.graphql index d08f474c1..68722151c 100644 --- a/lib/graphql/Query/AccountPage/AccountPage.graphql +++ b/lib/graphql/Query/AccountPage/AccountPage.graphql @@ -7,6 +7,7 @@ query GetAccountPage($locale: String!, $uid: String!) { account_page(locale: $locale, uid: $uid) { heading + preamble title url content { diff --git a/server/routers/contentstack/accountPage/output.ts b/server/routers/contentstack/accountPage/output.ts index 413025ff4..c645f2650 100644 --- a/server/routers/contentstack/accountPage/output.ts +++ b/server/routers/contentstack/accountPage/output.ts @@ -43,6 +43,7 @@ export const accountPageSchema = z.object({ account_page: z.object({ content: discriminatedUnionArray(blocksSchema.options), heading: z.string().nullable(), + preamble: z.string().nullable(), title: z.string(), url: z.string(), system: systemSchema.merge( diff --git a/server/routers/partners/sas/index.ts b/server/routers/partners/sas/index.ts index 702acd694..8b1e06267 100644 --- a/server/routers/partners/sas/index.ts +++ b/server/routers/partners/sas/index.ts @@ -3,5 +3,13 @@ import { router } from "@/server/trpc" import { requestOtp } from "./otp/request/requestOtp" import { verifyOtp } from "./otp/verify/verifyOtp" import { linkAccount } from "./linkAccount" +import { performLevelUpgrade } from "./performLevelUpgrade" +import { unlinkAccount } from "./unlinkAccount" -export const sasRouter = router({ verifyOtp, requestOtp, linkAccount }) +export const sasRouter = router({ + verifyOtp, + requestOtp, + linkAccount, + unlinkAccount, + performLevelUpgrade, +}) diff --git a/server/routers/partners/sas/linkAccount.ts b/server/routers/partners/sas/linkAccount.ts index 59308e53d..f838dac52 100644 --- a/server/routers/partners/sas/linkAccount.ts +++ b/server/routers/partners/sas/linkAccount.ts @@ -1,3 +1,4 @@ +import { setTimeout } from "timers/promises" import { z } from "zod" import { protectedProcedure } from "@/server/trpc" @@ -14,7 +15,7 @@ export const linkAccount = protectedProcedure const sasAuthToken = getSasToken() console.log("[SAS] link account") - await timeout(1000) + await setTimeout(1000) //TODO: Call actual API here console.log("[SAS] link account done") @@ -22,7 +23,3 @@ export const linkAccount = protectedProcedure linkingState: "linked", } }) - -function timeout(ms: number): Promise<void> { - return new Promise((resolve) => setTimeout(resolve, ms)) -} diff --git a/server/routers/partners/sas/performLevelUpgrade.ts b/server/routers/partners/sas/performLevelUpgrade.ts new file mode 100644 index 000000000..5280a5d46 --- /dev/null +++ b/server/routers/partners/sas/performLevelUpgrade.ts @@ -0,0 +1,20 @@ +import { TRPCError } from "@trpc/server" +import { setTimeout } from "timers/promises" +import { z } from "zod" + +import { protectedProcedure } from "@/server/trpc" + +const outputSchema = z.object({}) + +export const performLevelUpgrade = protectedProcedure + .output(outputSchema) + .mutation(async function ({ ctx, input }) { + console.log("[SAS] perform upgrade") + await setTimeout(1000) + //TODO: Call actual API here + + throw new TRPCError({ + message: "Unable to perform ugprade", + code: "BAD_REQUEST", + }) + }) diff --git a/server/routers/partners/sas/unlinkAccount.ts b/server/routers/partners/sas/unlinkAccount.ts new file mode 100644 index 000000000..6a4438734 --- /dev/null +++ b/server/routers/partners/sas/unlinkAccount.ts @@ -0,0 +1,26 @@ +import { TRPCError } from "@trpc/server" +import { setTimeout } from "timers/promises" +import { z } from "zod" + +import { protectedProcedure } from "@/server/trpc" + +const outputSchema = z.object({ + // unlinked: z.boolean(), +}) + +export const unlinkAccount = protectedProcedure + .output(outputSchema) + .mutation(async function ({ ctx, input }) { + console.log("[SAS] unlink account") + await setTimeout(1000) + //TODO: Call actual API here + + throw new TRPCError({ + message: "Unable to unlink account", + code: "BAD_REQUEST", + }) + + return { + unlinked: true, + } + }) diff --git a/types/enums/dynamicContent.ts b/types/enums/dynamicContent.ts index dc71a07e5..2497b4b68 100644 --- a/types/enums/dynamicContent.ts +++ b/types/enums/dynamicContent.ts @@ -16,6 +16,7 @@ export namespace DynamicContentEnum { sign_up_verification = "sign_up_verification", soonest_stays = "soonest_stays", upcoming_stays = "upcoming_stays", + sas_linked_account = "sas_linked_account", sas_tier_comparison = "sas_tier_comparison", } @@ -36,6 +37,7 @@ export namespace DynamicContentEnum { components.sign_up_verification, components.soonest_stays, components.upcoming_stays, + components.sas_linked_account, components.sas_tier_comparison, ] } diff --git a/types/trpc/routers/contentstack/accountPage.ts b/types/trpc/routers/contentstack/accountPage.ts index 703228d8f..81b3ed3f4 100644 --- a/types/trpc/routers/contentstack/accountPage.ts +++ b/types/trpc/routers/contentstack/accountPage.ts @@ -1,6 +1,6 @@ -import { z } from "zod" +import type { z } from "zod" -import { +import type { accountPageRefsSchema, accountPageSchema, blocksSchema,