Merge develop
This commit is contained in:
@@ -48,3 +48,5 @@ GOOGLE_STATIC_MAP_KEY=""
|
|||||||
GOOGLE_STATIC_MAP_SIGNATURE_SECRET=""
|
GOOGLE_STATIC_MAP_SIGNATURE_SECRET=""
|
||||||
GOOGLE_STATIC_MAP_ID=""
|
GOOGLE_STATIC_MAP_ID=""
|
||||||
GOOGLE_DYNAMIC_MAP_ID=""
|
GOOGLE_DYNAMIC_MAP_ID=""
|
||||||
|
|
||||||
|
HIDE_FOR_NEXT_RELEASE="true"
|
||||||
|
|||||||
@@ -41,3 +41,4 @@ GOOGLE_STATIC_MAP_KEY="test"
|
|||||||
GOOGLE_STATIC_MAP_SIGNATURE_SECRET="test"
|
GOOGLE_STATIC_MAP_SIGNATURE_SECRET="test"
|
||||||
GOOGLE_STATIC_MAP_ID="test"
|
GOOGLE_STATIC_MAP_ID="test"
|
||||||
GOOGLE_DYNAMIC_MAP_ID="test"
|
GOOGLE_DYNAMIC_MAP_ID="test"
|
||||||
|
HIDE_FOR_NEXT_RELEASE="true"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { notFound } from "next/navigation"
|
import { notFound } from "next/navigation"
|
||||||
|
|
||||||
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
import ContentPage from "@/components/ContentType/ContentPage"
|
import ContentPage from "@/components/ContentType/ContentPage"
|
||||||
import HotelPage from "@/components/ContentType/HotelPage"
|
import HotelPage from "@/components/ContentType/HotelPage"
|
||||||
import LoyaltyPage from "@/components/ContentType/LoyaltyPage"
|
import LoyaltyPage from "@/components/ContentType/LoyaltyPage"
|
||||||
@@ -21,10 +23,16 @@ export default async function ContentTypePage({
|
|||||||
|
|
||||||
switch (params.contentType) {
|
switch (params.contentType) {
|
||||||
case "content-page":
|
case "content-page":
|
||||||
|
if (env.HIDE_FOR_NEXT_RELEASE) {
|
||||||
|
return notFound()
|
||||||
|
}
|
||||||
return <ContentPage />
|
return <ContentPage />
|
||||||
case "loyalty-page":
|
case "loyalty-page":
|
||||||
return <LoyaltyPage />
|
return <LoyaltyPage />
|
||||||
case "hotel-page":
|
case "hotel-page":
|
||||||
|
if (env.HIDE_FOR_NEXT_RELEASE) {
|
||||||
|
return notFound()
|
||||||
|
}
|
||||||
return <HotelPage />
|
return <HotelPage />
|
||||||
default:
|
default:
|
||||||
const type: never = params.contentType
|
const type: never = params.contentType
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import { notFound } from "next/navigation"
|
||||||
|
|
||||||
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
import styles from "./layout.module.css"
|
import styles from "./layout.module.css"
|
||||||
|
|
||||||
import { LangParams, LayoutArgs } from "@/types/params"
|
import { LangParams, LayoutArgs } from "@/types/params"
|
||||||
@@ -5,5 +9,8 @@ import { LangParams, LayoutArgs } from "@/types/params"
|
|||||||
export default function HotelReservationLayout({
|
export default function HotelReservationLayout({
|
||||||
children,
|
children,
|
||||||
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
|
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
|
||||||
|
if (env.HIDE_FOR_NEXT_RELEASE) {
|
||||||
|
return notFound()
|
||||||
|
}
|
||||||
return <div className={styles.layout}>{children}</div>
|
return <div className={styles.layout}>{children}</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
|
import CurrentHeader from "@/components/Current/Header"
|
||||||
import Header from "@/components/Header"
|
import Header from "@/components/Header"
|
||||||
import { setLang } from "@/i18n/serverContext"
|
import { setLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
@@ -6,5 +9,9 @@ import { LangParams, PageArgs } from "@/types/params"
|
|||||||
export default function HeaderPage({ params }: PageArgs<LangParams>) {
|
export default function HeaderPage({ params }: PageArgs<LangParams>) {
|
||||||
setLang(params.lang)
|
setLang(params.lang)
|
||||||
|
|
||||||
|
if (env.HIDE_FOR_NEXT_RELEASE) {
|
||||||
|
return <CurrentHeader />
|
||||||
|
}
|
||||||
|
|
||||||
return <Header />
|
return <Header />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import "@scandic-hotels/design-system/style.css"
|
|||||||
import Script from "next/script"
|
import Script from "next/script"
|
||||||
import { Suspense } from "react"
|
import { Suspense } from "react"
|
||||||
|
|
||||||
|
import { env } from "@/env/server"
|
||||||
import TrpcProvider from "@/lib/trpc/Provider"
|
import TrpcProvider from "@/lib/trpc/Provider"
|
||||||
|
|
||||||
import TokenRefresher from "@/components/Auth/TokenRefresher"
|
import TokenRefresher from "@/components/Auth/TokenRefresher"
|
||||||
import AdobeSDKScript from "@/components/Current/AdobeSDKScript"
|
import AdobeSDKScript from "@/components/Current/AdobeSDKScript"
|
||||||
|
import CurrentFooter from "@/components/Current/Footer"
|
||||||
import VwoScript from "@/components/Current/VwoScript"
|
import VwoScript from "@/components/Current/VwoScript"
|
||||||
import Footer from "@/components/Footer"
|
import Footer from "@/components/Footer"
|
||||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||||
@@ -56,11 +58,11 @@ export default async function RootLayout({
|
|||||||
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
||||||
<TrpcProvider>
|
<TrpcProvider>
|
||||||
{header}
|
{header}
|
||||||
{bookingwidget}
|
{!env.HIDE_FOR_NEXT_RELEASE && <>{bookingwidget}</>}
|
||||||
{children}
|
{children}
|
||||||
<ToastHandler />
|
<ToastHandler />
|
||||||
<Suspense fallback={<LoadingSpinner />}>
|
<Suspense fallback={<LoadingSpinner />}>
|
||||||
<Footer />
|
{env.HIDE_FOR_NEXT_RELEASE ? <CurrentFooter /> : <Footer />}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<TokenRefresher />
|
<TokenRefresher />
|
||||||
</TrpcProvider>
|
</TrpcProvider>
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import Header from "@/components/Current/Header"
|
||||||
|
import { setLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function HeaderPage({ params }: PageArgs<LangParams>) {
|
||||||
|
setLang(params.lang)
|
||||||
|
|
||||||
|
return <Header />
|
||||||
|
}
|
||||||
5
app/[lang]/(live-current)/@header/error.tsx
Normal file
5
app/[lang]/(live-current)/@header/error.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
export default function Error() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { serverClient } from "@/lib/trpc/server"
|
|
||||||
|
|
||||||
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
|
||||||
import { setLang } from "@/i18n/serverContext"
|
|
||||||
|
|
||||||
import { LangParams, PageArgs } from "@/types/params"
|
|
||||||
|
|
||||||
export default async function LanguageSwitcherRoute({
|
|
||||||
params,
|
|
||||||
}: PageArgs<LangParams>) {
|
|
||||||
setLang(params.lang)
|
|
||||||
|
|
||||||
const data = await serverClient().contentstack.languageSwitcher.get()
|
|
||||||
if (!data) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return <LanguageSwitcher urls={data.urls} />
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { baseUrls } from "@/constants/routes/baseUrls"
|
|
||||||
|
|
||||||
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
|
||||||
|
|
||||||
export default function Error() {
|
|
||||||
return <LanguageSwitcher urls={baseUrls} />
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { serverClient } from "@/lib/trpc/server"
|
|
||||||
|
|
||||||
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
|
|
||||||
import { setLang } from "@/i18n/serverContext"
|
|
||||||
|
|
||||||
import { LangParams, PageArgs } from "@/types/params"
|
|
||||||
|
|
||||||
export default async function MyPagesMobileDropdownPage({
|
|
||||||
params,
|
|
||||||
}: PageArgs<LangParams>) {
|
|
||||||
setLang(params.lang)
|
|
||||||
|
|
||||||
const navigation = await serverClient().contentstack.myPages.navigation.get()
|
|
||||||
if (!navigation) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return <MyPagesMobileDropdown navigation={navigation} />
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
|
|
||||||
|
|
||||||
export default function Error() {
|
|
||||||
return <MyPagesMobileDropdown navigation={null} />
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ import TokenRefresher from "@/components/Auth/TokenRefresher"
|
|||||||
import BookingWidget from "@/components/BookingWidget"
|
import BookingWidget from "@/components/BookingWidget"
|
||||||
import AdobeScript from "@/components/Current/AdobeScript"
|
import AdobeScript from "@/components/Current/AdobeScript"
|
||||||
import Footer from "@/components/Current/Footer"
|
import Footer from "@/components/Current/Footer"
|
||||||
import Header from "@/components/Current/Header"
|
|
||||||
import LangPopup from "@/components/Current/LangPopup"
|
import LangPopup from "@/components/Current/LangPopup"
|
||||||
import SkipToMainContent from "@/components/SkipToMainContent"
|
import SkipToMainContent from "@/components/SkipToMainContent"
|
||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
@@ -27,12 +26,9 @@ export const metadata: Metadata = {
|
|||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
children,
|
children,
|
||||||
params,
|
params,
|
||||||
languageSwitcher,
|
header,
|
||||||
myPagesMobileDropdown,
|
|
||||||
}: React.PropsWithChildren<
|
}: React.PropsWithChildren<
|
||||||
LayoutArgs<LangParams> & { languageSwitcher: React.ReactNode } & {
|
LayoutArgs<LangParams> & { header: React.ReactNode }
|
||||||
myPagesMobileDropdown: React.ReactNode
|
|
||||||
}
|
|
||||||
>) {
|
>) {
|
||||||
setLang(params.lang)
|
setLang(params.lang)
|
||||||
const { defaultLocale, locale, messages } = await getIntl()
|
const { defaultLocale, locale, messages } = await getIntl()
|
||||||
@@ -68,10 +64,7 @@ export default async function RootLayout({
|
|||||||
<LangPopup />
|
<LangPopup />
|
||||||
<SkipToMainContent />
|
<SkipToMainContent />
|
||||||
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
||||||
<Header
|
{header}
|
||||||
myPagesMobileDropdown={myPagesMobileDropdown}
|
|
||||||
languageSwitcher={languageSwitcher}
|
|
||||||
/>
|
|
||||||
<BookingWidget />
|
<BookingWidget />
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ html,
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@@ -130,6 +131,16 @@ body {
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.overflow-hidden {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
body.overflow-hidden {
|
||||||
|
overflow: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
padding-inline-start: 0;
|
padding-inline-start: 0;
|
||||||
margin-block-start: 0;
|
margin-block-start: 0;
|
||||||
|
|||||||
29
components/Content/Blocks/TextCols/index.tsx
Normal file
29
components/Content/Blocks/TextCols/index.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import JsonToHtml from "@/components/JsonToHtml"
|
||||||
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
|
|
||||||
|
import { renderOptions } from "./renderOptions"
|
||||||
|
|
||||||
|
import styles from "./textcols.module.css"
|
||||||
|
|
||||||
|
import type { TextColsProps } from "@/types/components/content/blocks"
|
||||||
|
|
||||||
|
export default function TextCols({ textCols }: TextColsProps) {
|
||||||
|
return (
|
||||||
|
<div className={styles.columns}>
|
||||||
|
{textCols.columns.map((col) => {
|
||||||
|
return (
|
||||||
|
<section key={col.title} className={styles.column}>
|
||||||
|
<Subtitle>{col.title}</Subtitle>
|
||||||
|
<div className={styles.text}>
|
||||||
|
<JsonToHtml
|
||||||
|
nodes={col.text.json.children}
|
||||||
|
embeds={col.text.embedded_itemsConnection.edges}
|
||||||
|
renderOptions={renderOptions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
70
components/Content/Blocks/TextCols/renderOptions.tsx
Normal file
70
components/Content/Blocks/TextCols/renderOptions.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
|
|
||||||
|
import styles from "./textcols.module.css"
|
||||||
|
|
||||||
|
import type { EmbedByUid } from "@/types/components/jsontohtml"
|
||||||
|
import { RTEItemTypeEnum, RTETypeEnum } from "@/types/rte/enums"
|
||||||
|
import type {
|
||||||
|
RTEDefaultNode,
|
||||||
|
RTENext,
|
||||||
|
RTENode,
|
||||||
|
RTERegularNode,
|
||||||
|
} from "@/types/rte/node"
|
||||||
|
import type { RenderOptions } from "@/types/rte/option"
|
||||||
|
|
||||||
|
export const renderOptions: RenderOptions = {
|
||||||
|
[RTETypeEnum.p]: (
|
||||||
|
node: RTEDefaultNode,
|
||||||
|
embeds: EmbedByUid,
|
||||||
|
next: RTENext,
|
||||||
|
fullRenderOptions: RenderOptions
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<p key={node.uid} className={styles.p}>
|
||||||
|
{next(node.children, embeds, fullRenderOptions)}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[RTETypeEnum.a]: (
|
||||||
|
node: RTERegularNode,
|
||||||
|
embeds: EmbedByUid,
|
||||||
|
next: RTENext,
|
||||||
|
fullRenderOptions: RenderOptions
|
||||||
|
) => {
|
||||||
|
if (node.attrs.url) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={node.attrs.url}
|
||||||
|
target={node.attrs.target ?? "_blank"}
|
||||||
|
key={node.uid}
|
||||||
|
className={styles.a}
|
||||||
|
>
|
||||||
|
{next(node.children, embeds, fullRenderOptions)}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
[RTETypeEnum.reference]: (
|
||||||
|
node: RTENode,
|
||||||
|
embeds: EmbedByUid,
|
||||||
|
next: RTENext,
|
||||||
|
fullRenderOptions: RenderOptions
|
||||||
|
) => {
|
||||||
|
if ("attrs" in node) {
|
||||||
|
const type = node.attrs.type
|
||||||
|
if (type !== RTEItemTypeEnum.asset) {
|
||||||
|
const href = node.attrs?.locale
|
||||||
|
? `/${node.attrs.locale}${node.attrs.href}`
|
||||||
|
: node.attrs.href
|
||||||
|
return (
|
||||||
|
<Link href={href} key={node.uid} className={styles.a}>
|
||||||
|
{next(node.children, embeds, fullRenderOptions)}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
40
components/Content/Blocks/TextCols/textcols.module.css
Normal file
40
components/Content/Blocks/TextCols/textcols.module.css
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
.columns {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Spacing-x3);
|
||||||
|
padding: var(--Spacing-x3) var(--Spacing-x4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
padding-bottom: var(--Spacing-x2);
|
||||||
|
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||||
|
gap: var(--Spacing-x1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p {
|
||||||
|
color: var(--UI-Text-High-contrast);
|
||||||
|
line-height: var(--Spacing-x3);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.a {
|
||||||
|
color: var(--Base-Text-High-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text > section {
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.columns {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
flex: 0 0 calc(50% - var(--Spacing-x3));
|
||||||
|
max-width: calc(50% - var(--Spacing-x3));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import JsonToHtml from "@/components/JsonToHtml"
|
|||||||
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
|
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
|
||||||
|
|
||||||
import CardsGrid from "./CardsGrid"
|
import CardsGrid from "./CardsGrid"
|
||||||
|
import TextCols from "./TextCols"
|
||||||
|
|
||||||
import type { BlocksProps } from "@/types/components/content/blocks"
|
import type { BlocksProps } from "@/types/components/content/blocks"
|
||||||
import { ContentBlocksTypenameEnum } from "@/types/components/content/enums"
|
import { ContentBlocksTypenameEnum } from "@/types/components/content/enums"
|
||||||
@@ -38,6 +39,8 @@ export function Blocks({ blocks }: BlocksProps) {
|
|||||||
firstItem={firstItem}
|
firstItem={firstItem}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
case ContentBlocksTypenameEnum.ContentPageBlocksTextCols:
|
||||||
|
return <TextCols textCols={block.text_cols} />
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,25 +4,25 @@ import { serverClient } from "@/lib/trpc/server"
|
|||||||
|
|
||||||
import { getLang } from "@/i18n/serverContext"
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
import LanguageSwitcher from "./LanguageSwitcher"
|
||||||
import { MainMenu } from "./MainMenu"
|
import { MainMenu } from "./MainMenu"
|
||||||
|
import MyPagesMobileDropdown from "./MyPagesMobileDropdown"
|
||||||
import OfflineBanner from "./OfflineBanner"
|
import OfflineBanner from "./OfflineBanner"
|
||||||
import TopMenu from "./TopMenu"
|
import TopMenu from "./TopMenu"
|
||||||
|
|
||||||
import styles from "./header.module.css"
|
import styles from "./header.module.css"
|
||||||
|
|
||||||
export default async function Header({
|
export default async function Header() {
|
||||||
languageSwitcher,
|
const [data, user, languages, navigation] = await Promise.all([
|
||||||
myPagesMobileDropdown,
|
serverClient().contentstack.base.currentHeader({
|
||||||
}: {
|
lang: getLang(),
|
||||||
languageSwitcher: React.ReactNode
|
}),
|
||||||
myPagesMobileDropdown: React.ReactNode
|
serverClient().user.name(),
|
||||||
}) {
|
serverClient().contentstack.languageSwitcher.get(),
|
||||||
const data = await serverClient().contentstack.base.currentHeader({
|
serverClient().contentstack.myPages.navigation.get(),
|
||||||
lang: getLang(),
|
])
|
||||||
})
|
|
||||||
const user = await serverClient().user.name()
|
|
||||||
|
|
||||||
if (!data) {
|
if (!navigation || !languages || !data) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export default async function Header({
|
|||||||
frontpageLinkText={frontpage_link_text}
|
frontpageLinkText={frontpage_link_text}
|
||||||
homeHref={homeHref}
|
homeHref={homeHref}
|
||||||
links={top_menu.links}
|
links={top_menu.links}
|
||||||
languageSwitcher={languageSwitcher}
|
languageSwitcher={<LanguageSwitcher urls={languages.urls} />}
|
||||||
/>
|
/>
|
||||||
<MainMenu
|
<MainMenu
|
||||||
frontpageLinkText={frontpage_link_text}
|
frontpageLinkText={frontpage_link_text}
|
||||||
@@ -48,8 +48,10 @@ export default async function Header({
|
|||||||
links={menu.links}
|
links={menu.links}
|
||||||
logo={logo}
|
logo={logo}
|
||||||
topMenuMobileLinks={topMenuMobileLinks}
|
topMenuMobileLinks={topMenuMobileLinks}
|
||||||
languageSwitcher={languageSwitcher}
|
languageSwitcher={<LanguageSwitcher urls={languages.urls} />}
|
||||||
myPagesMobileDropdown={myPagesMobileDropdown}
|
myPagesMobileDropdown={
|
||||||
|
<MyPagesMobileDropdown navigation={navigation} />
|
||||||
|
}
|
||||||
bookingHref={homeHref}
|
bookingHref={homeHref}
|
||||||
user={user}
|
user={user}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useEffect } from "react"
|
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { languages } from "@/constants/languages"
|
import { languages } from "@/constants/languages"
|
||||||
@@ -28,12 +27,16 @@ export default function LanguageSwitcher({
|
|||||||
}: LanguageSwitcherProps) {
|
}: LanguageSwitcherProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const currentLanguage = useLang()
|
const currentLanguage = useLang()
|
||||||
const {
|
const toggleDropdown = useDropdownStore((state) => state.toggleDropdown)
|
||||||
toggleDropdown,
|
const isFooterLanguageSwitcherOpen = useDropdownStore(
|
||||||
isFooterLanguageSwitcherOpen,
|
(state) => state.isFooterLanguageSwitcherOpen
|
||||||
isHeaderLanguageSwitcherOpen,
|
)
|
||||||
isHeaderLanguageSwitcherMobileOpen,
|
const isHeaderLanguageSwitcherOpen = useDropdownStore(
|
||||||
} = useDropdownStore()
|
(state) => state.isHeaderLanguageSwitcherOpen
|
||||||
|
)
|
||||||
|
const isHeaderLanguageSwitcherMobileOpen = useDropdownStore(
|
||||||
|
(state) => state.isHeaderLanguageSwitcherMobileOpen
|
||||||
|
)
|
||||||
|
|
||||||
const isFooter = type === LanguageSwitcherTypesEnum.Footer
|
const isFooter = type === LanguageSwitcherTypesEnum.Footer
|
||||||
const isHeader = !isFooter
|
const isHeader = !isFooter
|
||||||
@@ -58,17 +61,14 @@ export default function LanguageSwitcher({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
function handleClick() {
|
||||||
if (isFooter && isFooterLanguageSwitcherOpen) {
|
const scrollPosition = window.scrollY
|
||||||
document.body.style.overflow = "hidden"
|
toggleDropdown(dropdownType)
|
||||||
} else {
|
|
||||||
document.body.style.overflow = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
requestAnimationFrame(() => {
|
||||||
document.body.style.overflow = ""
|
window.scrollTo(0, scrollPosition)
|
||||||
}
|
})
|
||||||
}, [isFooter, isFooterLanguageSwitcherOpen])
|
}
|
||||||
|
|
||||||
const classNames = languageSwitcherVariants({ color, position })
|
const classNames = languageSwitcherVariants({ color, position })
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ export default function LanguageSwitcher({
|
|||||||
? "Close language menu"
|
? "Close language menu"
|
||||||
: "Open language menu",
|
: "Open language menu",
|
||||||
})}
|
})}
|
||||||
onClick={() => toggleDropdown(dropdownType)}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<GlobeIcon width={20} height={20} color={color} />
|
<GlobeIcon width={20} height={20} color={color} />
|
||||||
<span>{languages[currentLanguage]}</span>
|
<span>{languages[currentLanguage]}</span>
|
||||||
|
|||||||
7
env/server.ts
vendored
7
env/server.ts
vendored
@@ -65,6 +65,12 @@ export const env = createEnv({
|
|||||||
GOOGLE_STATIC_MAP_SIGNATURE_SECRET: z.string(),
|
GOOGLE_STATIC_MAP_SIGNATURE_SECRET: z.string(),
|
||||||
GOOGLE_DYNAMIC_MAP_ID: z.string(),
|
GOOGLE_DYNAMIC_MAP_ID: z.string(),
|
||||||
GOOGLE_STATIC_MAP_ID: z.string(),
|
GOOGLE_STATIC_MAP_ID: z.string(),
|
||||||
|
HIDE_FOR_NEXT_RELEASE: z
|
||||||
|
.string()
|
||||||
|
// only allow "true" or "false"
|
||||||
|
.refine((s) => s === "true" || s === "false")
|
||||||
|
// transform to boolean
|
||||||
|
.transform((s) => s === "true"),
|
||||||
},
|
},
|
||||||
emptyStringAsUndefined: true,
|
emptyStringAsUndefined: true,
|
||||||
runtimeEnv: {
|
runtimeEnv: {
|
||||||
@@ -117,5 +123,6 @@ export const env = createEnv({
|
|||||||
process.env.GOOGLE_STATIC_MAP_SIGNATURE_SECRET,
|
process.env.GOOGLE_STATIC_MAP_SIGNATURE_SECRET,
|
||||||
GOOGLE_STATIC_MAP_ID: process.env.GOOGLE_STATIC_MAP_ID,
|
GOOGLE_STATIC_MAP_ID: process.env.GOOGLE_STATIC_MAP_ID,
|
||||||
GOOGLE_DYNAMIC_MAP_ID: process.env.GOOGLE_DYNAMIC_MAP_ID,
|
GOOGLE_DYNAMIC_MAP_ID: process.env.GOOGLE_DYNAMIC_MAP_ID,
|
||||||
|
HIDE_FOR_NEXT_RELEASE: process.env.HIDE_FOR_NEXT_RELEASE,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -98,6 +98,28 @@ query GetContentPage($locale: String!, $uid: String!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
... on ContentPageBlocksTextCols {
|
||||||
|
__typename
|
||||||
|
text_cols {
|
||||||
|
columns {
|
||||||
|
title
|
||||||
|
text {
|
||||||
|
json
|
||||||
|
embedded_itemsConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
__typename
|
||||||
|
...LoyaltyPageLink
|
||||||
|
...ContentPageLink
|
||||||
|
...HotelPageLink
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
title
|
title
|
||||||
header {
|
header {
|
||||||
|
|||||||
@@ -12,12 +12,8 @@ import {
|
|||||||
SidebarDynamicComponentEnum,
|
SidebarDynamicComponentEnum,
|
||||||
SidebarTypenameEnum,
|
SidebarTypenameEnum,
|
||||||
} from "@/types/components/content/enums"
|
} from "@/types/components/content/enums"
|
||||||
import { ImageVaultAsset } from "@/types/components/imageVault"
|
|
||||||
import { Embeds } from "@/types/requests/embeds"
|
|
||||||
import { PageLinkEnum } from "@/types/requests/pageLinks"
|
import { PageLinkEnum } from "@/types/requests/pageLinks"
|
||||||
import { RTEEmbedsEnum } from "@/types/requests/rte"
|
import { RTEEmbedsEnum } from "@/types/requests/rte"
|
||||||
import { EdgesWithTotalCount } from "@/types/requests/utils/edges"
|
|
||||||
import { RTEDocument } from "@/types/rte/node"
|
|
||||||
|
|
||||||
// Block schemas
|
// Block schemas
|
||||||
export const contentPageBlockTextContent = z.object({
|
export const contentPageBlockTextContent = z.object({
|
||||||
@@ -135,11 +131,29 @@ export const contentPageCards = z.object({
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const contentPageTextCols = z.object({
|
||||||
|
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksTextCols),
|
||||||
|
text_cols: z.object({
|
||||||
|
columns: z.array(
|
||||||
|
z.object({
|
||||||
|
title: z.string(),
|
||||||
|
text: z.object({
|
||||||
|
json: z.any(),
|
||||||
|
embedded_itemsConnection: z.object({
|
||||||
|
edges: z.array(z.any()),
|
||||||
|
totalCount: z.number(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
})
|
||||||
const contentPageBlockItem = z.discriminatedUnion("__typename", [
|
const contentPageBlockItem = z.discriminatedUnion("__typename", [
|
||||||
contentPageBlockTextContent,
|
contentPageBlockTextContent,
|
||||||
contentPageCards,
|
contentPageCards,
|
||||||
contentPageDynamicContent,
|
contentPageDynamicContent,
|
||||||
contentPageShortcuts,
|
contentPageShortcuts,
|
||||||
|
contentPageTextCols,
|
||||||
])
|
])
|
||||||
|
|
||||||
export const contentPageSidebarTextContent = z.object({
|
export const contentPageSidebarTextContent = z.object({
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import { ZodError } from "zod"
|
|||||||
|
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
import { type Context, createContext } from "./context"
|
|
||||||
import {
|
import {
|
||||||
badRequestError,
|
badRequestError,
|
||||||
internalServerError,
|
internalServerError,
|
||||||
sessionExpiredError,
|
sessionExpiredError,
|
||||||
unauthorizedError,
|
unauthorizedError,
|
||||||
} from "./errors/trpc"
|
} from "./errors/trpc"
|
||||||
|
import { type Context, createContext } from "./context"
|
||||||
import { fetchServiceToken } from "./tokenManager"
|
import { fetchServiceToken } from "./tokenManager"
|
||||||
import { transformer } from "./transformer"
|
import { transformer } from "./transformer"
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,11 @@ const useDropdownStore = create<DropdownState>((set, get) => ({
|
|||||||
state.isMyPagesMenuOpen = false
|
state.isMyPagesMenuOpen = false
|
||||||
state.isHeaderLanguageSwitcherOpen = false
|
state.isHeaderLanguageSwitcherOpen = false
|
||||||
state.isHeaderLanguageSwitcherMobileOpen = false
|
state.isHeaderLanguageSwitcherMobileOpen = false
|
||||||
|
if (state.isFooterLanguageSwitcherOpen) {
|
||||||
|
document.body.classList.add("overflow-hidden")
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove("overflow-hidden")
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Block,
|
Block,
|
||||||
CardsGrid,
|
CardsGrid,
|
||||||
DynamicContent,
|
DynamicContent,
|
||||||
|
TextCols,
|
||||||
} from "@/types/trpc/routers/contentstack/contentPage"
|
} from "@/types/trpc/routers/contentstack/contentPage"
|
||||||
|
|
||||||
export type BlocksProps = {
|
export type BlocksProps = {
|
||||||
@@ -17,6 +18,10 @@ export type CardsGridProps = Pick<CardsGrid, "cards_grid"> & {
|
|||||||
firstItem?: boolean
|
firstItem?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TextColsProps = {
|
||||||
|
textCols: TextCols["text_cols"]
|
||||||
|
}
|
||||||
|
|
||||||
export type DynamicContentProps = {
|
export type DynamicContentProps = {
|
||||||
dynamicContent: DynamicContent["dynamic_content"]
|
dynamicContent: DynamicContent["dynamic_content"]
|
||||||
firstItem: boolean
|
firstItem: boolean
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export enum ContentBlocksTypenameEnum {
|
|||||||
ContentPageBlocksShortcuts = "ContentPageBlocksShortcuts",
|
ContentPageBlocksShortcuts = "ContentPageBlocksShortcuts",
|
||||||
ContentPageBlocksCardsGrid = "ContentPageBlocksCardsGrid",
|
ContentPageBlocksCardsGrid = "ContentPageBlocksCardsGrid",
|
||||||
ContentPageBlocksDynamicContent = "ContentPageBlocksDynamicContent",
|
ContentPageBlocksDynamicContent = "ContentPageBlocksDynamicContent",
|
||||||
|
ContentPageBlocksTextCols = "ContentPageBlocksTextCols",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CardsGridEnum {
|
export enum CardsGridEnum {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
contentPageShortcuts,
|
contentPageShortcuts,
|
||||||
contentPageSidebarDynamicContent,
|
contentPageSidebarDynamicContent,
|
||||||
contentPageSidebarTextContent,
|
contentPageSidebarTextContent,
|
||||||
|
contentPageTextCols,
|
||||||
loyaltyCardBlock,
|
loyaltyCardBlock,
|
||||||
validateContentPageRefsSchema,
|
validateContentPageRefsSchema,
|
||||||
validateContentPageSchema,
|
validateContentPageSchema,
|
||||||
@@ -81,4 +82,22 @@ export type CardsGrid = Omit<CardsGridRaw, "cards"> & {
|
|||||||
}
|
}
|
||||||
export type CardsRaw = CardsGrid["cards_grid"]["cards"][number]
|
export type CardsRaw = CardsGrid["cards_grid"]["cards"][number]
|
||||||
|
|
||||||
export type Block = RteBlockContent | Shortcuts | CardsGrid | DynamicContent
|
type TextColsRaw = z.infer<typeof contentPageTextCols>
|
||||||
|
export interface TextCols extends TextColsRaw {
|
||||||
|
textCols: {
|
||||||
|
columns: {
|
||||||
|
title: string
|
||||||
|
text: {
|
||||||
|
json: RTEDocument
|
||||||
|
embedded_itemsConnection: EdgesWithTotalCount<Embeds>
|
||||||
|
}
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Block =
|
||||||
|
| RteBlockContent
|
||||||
|
| Shortcuts
|
||||||
|
| CardsGrid
|
||||||
|
| DynamicContent
|
||||||
|
| TextCols
|
||||||
|
|||||||
Reference in New Issue
Block a user