Merged in fix/header-parallel-route (pull request #238)
Static pages sync Approved-by: Michael Zetterberg
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useParams } from "next/navigation"
|
||||||
|
|
||||||
|
import { baseUrls } from "@/constants/routes/baseUrls"
|
||||||
|
|
||||||
|
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
||||||
|
|
||||||
|
import { LangParams } from "@/types/params"
|
||||||
|
|
||||||
|
export default function Error() {
|
||||||
|
const params = useParams<LangParams>()
|
||||||
|
return <LanguageSwitcher urls={baseUrls} lang={params.lang} />
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
||||||
|
|
||||||
|
export default async function LanguageSwitcherRoute() {
|
||||||
|
const data = await serverClient().contentstack.languageSwitcher.get()
|
||||||
|
|
||||||
|
return <LanguageSwitcher urls={data.urls} lang={data.lang} />
|
||||||
|
}
|
||||||
21
app/[lang]/(live)/@header/[...paths]/layout.tsx
Normal file
21
app/[lang]/(live)/@header/[...paths]/layout.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { ReactNode } from "react"
|
||||||
|
|
||||||
|
import Header from "@/components/Current/Header"
|
||||||
|
|
||||||
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default function HeaderLayout({
|
||||||
|
params,
|
||||||
|
languageSwitcher,
|
||||||
|
children,
|
||||||
|
}: PageArgs<LangParams> & {
|
||||||
|
languageSwitcher: ReactNode
|
||||||
|
children: ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header lang={params.lang} languageSwitcher={languageSwitcher} />
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
3
app/[lang]/(live)/@header/[...paths]/page.tsx
Normal file
3
app/[lang]/(live)/@header/[...paths]/page.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function EmptyHeaderPage() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
5
app/[lang]/(live)/@header/error.tsx
Normal file
5
app/[lang]/(live)/@header/error.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
export default function Error() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
15
app/[lang]/(live)/@header/page.tsx
Normal file
15
app/[lang]/(live)/@header/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { baseUrls } from "@/constants/routes/baseUrls"
|
||||||
|
|
||||||
|
import Header from "@/components/Current/Header"
|
||||||
|
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
||||||
|
|
||||||
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function HeaderPage({ params }: PageArgs<LangParams>) {
|
||||||
|
return (
|
||||||
|
<Header
|
||||||
|
lang={params.lang}
|
||||||
|
languageSwitcher={<LanguageSwitcher urls={baseUrls} lang={params.lang} />}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { serverClient } from "@/lib/trpc/server"
|
|
||||||
|
|
||||||
import Desktop from "@/components/Current/Header/LanguageSwitcher/Desktop"
|
|
||||||
import Mobile from "@/components/Current/Header/LanguageSwitcher/Mobile"
|
|
||||||
|
|
||||||
export default async function LanguageSwitcher() {
|
|
||||||
const data = await serverClient().contentstack.languageSwitcher.get()
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Desktop currentLanguage={data.lang} urls={data.urls} />
|
|
||||||
<Mobile currentLanguage={data.lang} urls={data.urls} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export default async function DefaultLanguageSwitcher() {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ import TrpcProvider from "@/lib/trpc/Provider"
|
|||||||
|
|
||||||
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 VwoScript from "@/components/Current/VwoScript"
|
import VwoScript from "@/components/Current/VwoScript"
|
||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
import ServerIntlProvider from "@/i18n/Provider"
|
import ServerIntlProvider from "@/i18n/Provider"
|
||||||
@@ -24,10 +23,10 @@ export const metadata: Metadata = {
|
|||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
children,
|
children,
|
||||||
params,
|
params,
|
||||||
languageSwitcher,
|
header,
|
||||||
}: React.PropsWithChildren<
|
}: React.PropsWithChildren<
|
||||||
LayoutArgs<LangParams> & {
|
LayoutArgs<LangParams> & {
|
||||||
languageSwitcher: React.ReactNode
|
header: React.ReactNode
|
||||||
}
|
}
|
||||||
>) {
|
>) {
|
||||||
const { defaultLocale, locale, messages } = await getIntl()
|
const { defaultLocale, locale, messages } = await getIntl()
|
||||||
@@ -55,12 +54,9 @@ export default async function RootLayout({
|
|||||||
<body>
|
<body>
|
||||||
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
||||||
<TrpcProvider lang={params.lang}>
|
<TrpcProvider lang={params.lang}>
|
||||||
<Header
|
{header}
|
||||||
lang={params.lang}
|
|
||||||
languageSwitcher={languageSwitcher}
|
|
||||||
/>
|
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer lang={params.lang} />
|
||||||
</TrpcProvider>
|
</TrpcProvider>
|
||||||
</ServerIntlProvider>
|
</ServerIntlProvider>
|
||||||
<Script id="page-tracking">{`
|
<Script id="page-tracking">{`
|
||||||
|
|||||||
11
app/[lang]/(live)/middleware-error/404/page.tsx
Normal file
11
app/[lang]/(live)/middleware-error/404/page.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
|
import NotFound from "@/components/Current/NotFound"
|
||||||
|
|
||||||
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default function NotFoundPage({ params }: PageArgs<LangParams>) {
|
||||||
|
const lang = params.lang || Lang.en
|
||||||
|
|
||||||
|
return <NotFound lang={lang} />
|
||||||
|
}
|
||||||
@@ -1,14 +1,9 @@
|
|||||||
import { serverClient } from "@/lib/trpc/server"
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
import Desktop from "@/components/Current/Header/LanguageSwitcher/Desktop"
|
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
||||||
import Mobile from "@/components/Current/Header/LanguageSwitcher/Mobile"
|
|
||||||
|
|
||||||
export default async function LanguageSwitcher() {
|
export default async function LanguageSwitcherRoute() {
|
||||||
const data = await serverClient().contentstack.languageSwitcher.get()
|
const data = await serverClient().contentstack.languageSwitcher.get()
|
||||||
return (
|
|
||||||
<>
|
return <LanguageSwitcher urls={data.urls} lang={data.lang} />
|
||||||
<Desktop currentLanguage={data.lang} urls={data.urls} />
|
|
||||||
<Mobile currentLanguage={data.lang} urls={data.urls} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function DefaultLanguageSwitcher() {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
14
app/[lang]/(live-current)/@languageSwitcher/error.tsx
Normal file
14
app/[lang]/(live-current)/@languageSwitcher/error.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useParams } from "next/navigation"
|
||||||
|
|
||||||
|
import { baseUrls } from "@/constants/routes/baseUrls"
|
||||||
|
|
||||||
|
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
||||||
|
|
||||||
|
import { LangParams } from "@/types/params"
|
||||||
|
|
||||||
|
export default function Error() {
|
||||||
|
const params = useParams<LangParams>()
|
||||||
|
return <LanguageSwitcher urls={baseUrls} lang={params.lang} />
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ import { GetCurrentBlockPageTrackingData } from "@/lib/graphql/Query/CurrentBloc
|
|||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
|
|
||||||
import ContentPage from "@/components/Current/ContentPage"
|
import ContentPage from "@/components/Current/ContentPage"
|
||||||
import Header from "@/components/Current/Header"
|
|
||||||
import Tracking from "@/components/Current/Tracking"
|
import Tracking from "@/components/Current/Tracking"
|
||||||
|
|
||||||
import type { LangParams, PageArgs, UriParams } from "@/types/params"
|
import type { LangParams, PageArgs, UriParams } from "@/types/params"
|
||||||
@@ -27,9 +26,7 @@ export default async function CurrentContentPage({
|
|||||||
locale: params.lang,
|
locale: params.lang,
|
||||||
url: searchParams.uri,
|
url: searchParams.uri,
|
||||||
},
|
},
|
||||||
{
|
{ tags: [`${searchParams.uri}-${params.lang}`] }
|
||||||
next: { tags: [`${searchParams.uri}-${params.lang}`] },
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!response.data?.all_current_blocks_page?.total) {
|
if (!response.data?.all_current_blocks_page?.total) {
|
||||||
@@ -43,9 +40,7 @@ export default async function CurrentContentPage({
|
|||||||
const pageDataForTracking = await request<TrackingData>(
|
const pageDataForTracking = await request<TrackingData>(
|
||||||
GetCurrentBlockPageTrackingData,
|
GetCurrentBlockPageTrackingData,
|
||||||
{ uid: response.data.all_current_blocks_page.items[0].system.uid },
|
{ uid: response.data.all_current_blocks_page.items[0].system.uid },
|
||||||
{
|
{ tags: [`${searchParams.uri}-en`] }
|
||||||
next: { tags: [`${searchParams.uri}-en`] },
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const pageData = response.data.all_current_blocks_page.items[0]
|
const pageData = response.data.all_current_blocks_page.items[0]
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ import type { LangParams, LayoutArgs } from "@/types/params"
|
|||||||
export const fetchCache = "default-no-store"
|
export const fetchCache = "default-no-store"
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
description: "New web",
|
title: "Scandic Hotels",
|
||||||
title: "Scandic Hotels New Web",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
@@ -85,8 +84,8 @@ export default async function RootLayout({
|
|||||||
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
||||||
<Header lang={params.lang} languageSwitcher={languageSwitcher} />
|
<Header lang={params.lang} languageSwitcher={languageSwitcher} />
|
||||||
{children}
|
{children}
|
||||||
|
<Footer lang={params.lang} />
|
||||||
</ServerIntlProvider>
|
</ServerIntlProvider>
|
||||||
<Footer />
|
|
||||||
<Script id="page-tracking">{`
|
<Script id="page-tracking">{`
|
||||||
typeof _satellite !== "undefined" && _satellite.pageBottom();
|
typeof _satellite !== "undefined" && _satellite.pageBottom();
|
||||||
`}</Script>
|
`}</Script>
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { getIntl } from "@/i18n"
|
"use client"
|
||||||
|
|
||||||
export default async function NotFound() {
|
import { useParams } from "next/navigation"
|
||||||
const { formatMessage } = await getIntl()
|
|
||||||
return (
|
import NotFound from "@/components/Current/NotFound"
|
||||||
<main>
|
|
||||||
<h1>{formatMessage({ id: "Not found" })}</h1>
|
import { LangParams } from "@/types/params"
|
||||||
<p>{formatMessage({ id: "Could not find requested resource" })}</p>
|
|
||||||
</main>
|
export default function NotFoundPage() {
|
||||||
)
|
const { lang } = useParams<LangParams>()
|
||||||
|
|
||||||
|
return <NotFound lang={lang} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export default function RootLayout({
|
|||||||
<LangPopup lang={params.lang} />
|
<LangPopup lang={params.lang} />
|
||||||
<SkipToMainContent />
|
<SkipToMainContent />
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer lang={params.lang} />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import type { Metadata } from "next"
|
|
||||||
|
|
||||||
import type { LangParams, LayoutArgs } from "@/types/params"
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Test Site",
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function RootLayout({
|
|
||||||
children,
|
|
||||||
params,
|
|
||||||
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
|
|
||||||
return (
|
|
||||||
<html lang={params.lang}>
|
|
||||||
<body>{children}</body>
|
|
||||||
</html>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import type { Metadata } from "next"
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Hello World",
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function HelloWorldTestPage() {
|
|
||||||
return (
|
|
||||||
<main>
|
|
||||||
<header>
|
|
||||||
<h1>Hello World!</h1>
|
|
||||||
</header>
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -12,13 +12,13 @@ export default function Contact({ sections, system: { locale } }: ContactNode) {
|
|||||||
const visitingAddressMessage = getVisitingAddressMessage(locale)
|
const visitingAddressMessage = getVisitingAddressMessage(locale)
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
{sections.map((section) => {
|
{sections.map((section, idx) => {
|
||||||
switch (section.__typename) {
|
switch (section.__typename) {
|
||||||
case Section.ContactBlockSectionsExtraInfo:
|
case Section.ContactBlockSectionsExtraInfo:
|
||||||
return <p>{section.extra_info.text}</p>
|
return <p>{section.extra_info.text}</p>
|
||||||
case Section.ContactBlockSectionsMailingAddress:
|
case Section.ContactBlockSectionsMailingAddress:
|
||||||
return (
|
return (
|
||||||
<p>
|
<p key={`section-mail-${idx}`}>
|
||||||
{section.mailing_address.name}
|
{section.mailing_address.name}
|
||||||
<br />
|
<br />
|
||||||
{section.mailing_address.street}
|
{section.mailing_address.street}
|
||||||
@@ -30,7 +30,10 @@ export default function Contact({ sections, system: { locale } }: ContactNode) {
|
|||||||
)
|
)
|
||||||
case Section.ContactBlockSectionsPhone:
|
case Section.ContactBlockSectionsPhone:
|
||||||
return (
|
return (
|
||||||
<div className={styles.highlightBlock}>
|
<div
|
||||||
|
className={styles.highlightBlock}
|
||||||
|
key={`section-phone-${idx}`}
|
||||||
|
>
|
||||||
<h3>{section.phone.title}</h3>
|
<h3>{section.phone.title}</h3>
|
||||||
<div className={styles.phoneContainer}>
|
<div className={styles.phoneContainer}>
|
||||||
<svg
|
<svg
|
||||||
@@ -55,10 +58,14 @@ export default function Contact({ sections, system: { locale } }: ContactNode) {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
case Section.ContactBlockSectionsTitle:
|
case Section.ContactBlockSectionsTitle:
|
||||||
return <h2 className={styles.heading}>{section.title.text}</h2>
|
return (
|
||||||
|
<h2 className={styles.heading} key={`section-heading-${idx}`}>
|
||||||
|
{section.title.text}
|
||||||
|
</h2>
|
||||||
|
)
|
||||||
case Section.ContactBlockSectionsVisitingAddress:
|
case Section.ContactBlockSectionsVisitingAddress:
|
||||||
return (
|
return (
|
||||||
<p>
|
<p key={`section-visiting-address-${idx}`}>
|
||||||
{visitingAddressMessage}: {section.visiting_address.street}{" "}
|
{visitingAddressMessage}: {section.visiting_address.street}{" "}
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,15 +14,15 @@ export default function Blocks({ blocks }: BlocksProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.wrapper}>
|
<section className={styles.wrapper}>
|
||||||
{blocks.map((block) => {
|
{blocks.map((block, idx) => {
|
||||||
const type = block.__typename
|
const type = block.__typename
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BlocksTypenameEnum.CurrentBlocksPageBlocksList:
|
case BlocksTypenameEnum.CurrentBlocksPageBlocksList:
|
||||||
return <List key={block.__typename} {...block} />
|
return <List key={`${block.__typename}-${idx}`} {...block} />
|
||||||
case BlocksTypenameEnum.CurrentBlocksPageBlocksPuffs:
|
case BlocksTypenameEnum.CurrentBlocksPageBlocksPuffs:
|
||||||
return <Puffs key={block.__typename} {...block} />
|
return <Puffs key={`${block.__typename}-${idx}`} {...block} />
|
||||||
case BlocksTypenameEnum.CurrentBlocksPageBlocksText:
|
case BlocksTypenameEnum.CurrentBlocksPageBlocksText:
|
||||||
return <Text key={block.__typename} {...block} />
|
return <Text key={`${block.__typename}-${idx}`} {...block} />
|
||||||
default:
|
default:
|
||||||
console.log(`Unknown type: (${type})`)
|
console.log(`Unknown type: (${type})`)
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import Navigation from "./Navigation"
|
|||||||
|
|
||||||
import styles from "./footer.module.css"
|
import styles from "./footer.module.css"
|
||||||
|
|
||||||
export default async function Footer() {
|
import { LangParams } from "@/types/params"
|
||||||
const footerData = await serverClient().contentstack.base.footer()
|
|
||||||
|
export default async function Footer({ lang }: LangParams) {
|
||||||
|
const footerData = await serverClient().contentstack.base.footer({ lang })
|
||||||
return (
|
return (
|
||||||
<footer className={styles.container}>
|
<footer className={styles.container}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
sans-serif;
|
sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.languageSwitcher {
|
.toggle {
|
||||||
display: flex;
|
display: flex;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 3px 15px;
|
padding: 3px 15px;
|
||||||
|
|||||||
19
components/Current/Header/LanguageSwitcher/index.tsx
Normal file
19
components/Current/Header/LanguageSwitcher/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Desktop from "./Desktop"
|
||||||
|
import Mobile from "./Mobile"
|
||||||
|
|
||||||
|
import { LangParams } from "@/types/params"
|
||||||
|
import { LanguageSwitcherData } from "@/types/requests/languageSwitcher"
|
||||||
|
|
||||||
|
type LanguageSwitcherProps = LangParams & { urls: LanguageSwitcherData }
|
||||||
|
|
||||||
|
export default function LanguageSwitcher({
|
||||||
|
urls,
|
||||||
|
lang,
|
||||||
|
}: LanguageSwitcherProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Desktop currentLanguage={lang} urls={urls} />
|
||||||
|
<Mobile currentLanguage={lang} urls={urls} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -275,6 +275,7 @@
|
|||||||
.logo {
|
.logo {
|
||||||
width: 102.17px;
|
width: 102.17px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.listWrapper {
|
.listWrapper {
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ export default async function TopMenu({
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ul className={styles.list}>
|
<ul className={styles.list}>
|
||||||
<li className={styles.langSwitcher}>{languageSwitcher}</li>
|
{languageSwitcher ? (
|
||||||
|
<li className={styles.langSwitcher}>{languageSwitcher}</li>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{links.map(({ link }, i) => (
|
{links.map(({ link }, i) => (
|
||||||
<li key={link.href + i}>
|
<li key={link.href + i}>
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ export default async function Header({
|
|||||||
lang,
|
lang,
|
||||||
languageSwitcher,
|
languageSwitcher,
|
||||||
}: LangParams & { languageSwitcher: React.ReactNode }) {
|
}: LangParams & { languageSwitcher: React.ReactNode }) {
|
||||||
const data = await serverClient().contentstack.base.header()
|
const data = await serverClient().contentstack.base.header({
|
||||||
|
lang,
|
||||||
|
})
|
||||||
const session = await auth()
|
const session = await auth()
|
||||||
|
|
||||||
const homeHref = homeHrefs[env.NODE_ENV][lang]
|
const homeHref = homeHrefs[env.NODE_ENV][lang]
|
||||||
|
|||||||
131
components/Current/NotFound/Texts.ts
Normal file
131
components/Current/NotFound/Texts.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
|
type Texts = {
|
||||||
|
title: string
|
||||||
|
goToStartPage: {
|
||||||
|
question: string
|
||||||
|
link: string
|
||||||
|
linkText: string
|
||||||
|
}
|
||||||
|
goToDestinations: {
|
||||||
|
question: string
|
||||||
|
link: string
|
||||||
|
linkText: string
|
||||||
|
}
|
||||||
|
goToOffers: {
|
||||||
|
question: string
|
||||||
|
link: string
|
||||||
|
linkText: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const texts: Record<Lang, Texts> = {
|
||||||
|
en: {
|
||||||
|
title: "Sorry, page not found.",
|
||||||
|
goToStartPage: {
|
||||||
|
question: "Would you like to go back to the ",
|
||||||
|
link: "https://www.scandichotels.com/",
|
||||||
|
linkText: "startpage",
|
||||||
|
},
|
||||||
|
goToDestinations: {
|
||||||
|
question: "Or take a trip to our ",
|
||||||
|
link: "https://www.scandichotels.com/hotels",
|
||||||
|
linkText: "destinations",
|
||||||
|
},
|
||||||
|
goToOffers: {
|
||||||
|
question: " or latest ",
|
||||||
|
link: "https://www.scandichotels.com/weekend-packages-and-offers",
|
||||||
|
linkText: "offers",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sv: {
|
||||||
|
title: "Oj då, vi kunde inte hitta sidan du söker.",
|
||||||
|
goToStartPage: {
|
||||||
|
question: "Vill du gå tillbaka till ",
|
||||||
|
link: "https://www.scandichotels.se/",
|
||||||
|
linkText: "startsidan",
|
||||||
|
},
|
||||||
|
goToDestinations: {
|
||||||
|
question: "Eller resa till våra ",
|
||||||
|
link: "https://www.scandichotels.se/hotell",
|
||||||
|
linkText: "destinationer",
|
||||||
|
},
|
||||||
|
goToOffers: {
|
||||||
|
question: " eller se våra senaste ",
|
||||||
|
link: "https://www.scandichotels.se/erbjudanden-och-weekendpaket",
|
||||||
|
linkText: "erbjudanden",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
de: {
|
||||||
|
title: "Tut uns leid, Seite nicht gefunden.",
|
||||||
|
goToStartPage: {
|
||||||
|
question: "Möchten Sie zurück zur ",
|
||||||
|
link: "https://www.scandichotels.de/",
|
||||||
|
linkText: "Startseite",
|
||||||
|
},
|
||||||
|
goToDestinations: {
|
||||||
|
question: "Oder machen Sie einen Ausflug zu unseren ",
|
||||||
|
link: "https://www.scandichotels.de/hotelsuche",
|
||||||
|
linkText: "Reisezielen",
|
||||||
|
},
|
||||||
|
goToOffers: {
|
||||||
|
question: " und aktuellen ",
|
||||||
|
link: "https://www.scandichotels.de/angebote-arrangements",
|
||||||
|
linkText: "Angeboten",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fi: {
|
||||||
|
title: "TValitettavasti sivua ei löydy.",
|
||||||
|
goToStartPage: {
|
||||||
|
question: "Haluaisitko mennä takaisin ",
|
||||||
|
link: "https://www.scandichotels.fi/",
|
||||||
|
linkText: "etusivulle",
|
||||||
|
},
|
||||||
|
goToDestinations: {
|
||||||
|
question: "Voit myös tutustu ",
|
||||||
|
link: "https://www.scandichotels.fi/hotellit",
|
||||||
|
linkText: "kohteisiimme",
|
||||||
|
},
|
||||||
|
goToOffers: {
|
||||||
|
question: " tai ajankohtaisiin ",
|
||||||
|
link: "https://www.scandichotels.fi/tarjoukset",
|
||||||
|
linkText: "tarjouksiimme",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
no: {
|
||||||
|
title: "Oi da, vi fant ikke siden du lette etter...",
|
||||||
|
goToStartPage: {
|
||||||
|
question: "Vil du gå tilbake til ",
|
||||||
|
link: "https://www.scandichotels.no/",
|
||||||
|
linkText: "startsiden",
|
||||||
|
},
|
||||||
|
goToDestinations: {
|
||||||
|
question: "Eller ta en tur til våre ",
|
||||||
|
link: "https://www.scandichotels.no/hotell",
|
||||||
|
linkText: "destinasjoner",
|
||||||
|
},
|
||||||
|
goToOffers: {
|
||||||
|
question: " eller siste ",
|
||||||
|
link: "https://www.scandichotels.no/hotelltilbud",
|
||||||
|
linkText: "tilbud",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
da: {
|
||||||
|
title: "Hov - siden kan ikke findes!",
|
||||||
|
goToStartPage: {
|
||||||
|
question: "Vil du gå tilbage til ",
|
||||||
|
link: "https://www.scandichotels.dk/",
|
||||||
|
linkText: "startsiden",
|
||||||
|
},
|
||||||
|
goToDestinations: {
|
||||||
|
question: "Eller tag en tur til vores ",
|
||||||
|
link: "https://www.scandichotels.dk/hoteller",
|
||||||
|
linkText: "destinationer",
|
||||||
|
},
|
||||||
|
goToOffers: {
|
||||||
|
question: " eller seneste ",
|
||||||
|
link: "https://www.scandichotels.dk/tilbud-og-hotelpakker",
|
||||||
|
linkText: "tilbud",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
48
components/Current/NotFound/index.tsx
Normal file
48
components/Current/NotFound/index.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { texts } from "./Texts"
|
||||||
|
|
||||||
|
import styles from "./notFound.module.css"
|
||||||
|
|
||||||
|
import { LangParams } from "@/types/params"
|
||||||
|
|
||||||
|
export default function NotFound({ lang }: LangParams) {
|
||||||
|
const infoTexts = texts[lang]
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div className={styles.content}>
|
||||||
|
<h1 className={styles.header}>{infoTexts.title}</h1>
|
||||||
|
<div className={styles.pitch}>
|
||||||
|
<p className={styles.text}>
|
||||||
|
{infoTexts.goToStartPage.question}
|
||||||
|
<a
|
||||||
|
className={styles.link}
|
||||||
|
title={infoTexts.goToStartPage.linkText}
|
||||||
|
href={infoTexts.goToStartPage.link}
|
||||||
|
>
|
||||||
|
{infoTexts.goToStartPage.linkText}
|
||||||
|
</a>
|
||||||
|
?
|
||||||
|
</p>
|
||||||
|
<p className={styles.text}>
|
||||||
|
{infoTexts.goToDestinations.question}
|
||||||
|
<a
|
||||||
|
className={styles.link}
|
||||||
|
title={infoTexts.goToDestinations.linkText}
|
||||||
|
href={infoTexts.goToDestinations.link}
|
||||||
|
>
|
||||||
|
{infoTexts.goToDestinations.linkText}
|
||||||
|
</a>
|
||||||
|
{infoTexts.goToOffers.question}
|
||||||
|
<a
|
||||||
|
className={styles.link}
|
||||||
|
title={infoTexts.goToOffers.linkText}
|
||||||
|
href={infoTexts.goToOffers.link}
|
||||||
|
>
|
||||||
|
{infoTexts.goToOffers.linkText}
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
70
components/Current/NotFound/notFound.module.css
Normal file
70
components/Current/NotFound/notFound.module.css
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
.container {
|
||||||
|
margin-top: 0;
|
||||||
|
background: var(--Main-Grey-White);
|
||||||
|
position: relative;
|
||||||
|
border-top: 50px solid var(--Main-Grey-White);
|
||||||
|
background-clip: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: content-box;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 70px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
font-family:
|
||||||
|
brandon text,
|
||||||
|
Arial,
|
||||||
|
Helvetica,
|
||||||
|
sans-serif;
|
||||||
|
font-size: 32px;
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #483729;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pitch {
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-family:
|
||||||
|
Helvetica Neue,
|
||||||
|
Roboto,
|
||||||
|
Helvetica,
|
||||||
|
Arial,
|
||||||
|
sans-serif;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: normal;
|
||||||
|
text-transform: none;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: #00838e;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 740px) {
|
||||||
|
.content {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 950px) {
|
||||||
|
.header {
|
||||||
|
font-size: 46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,10 @@ export default function BenefitCard({
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.benefitComparison}>
|
<div className={styles.benefitComparison}>
|
||||||
{comparedValues.map((benefit, idx) => (
|
{comparedValues.map((benefit, idx) => (
|
||||||
<div key={idx} className={styles.comparisonItem}>
|
<div
|
||||||
|
key={`${benefit.valueDetails}-${idx}`}
|
||||||
|
className={styles.comparisonItem}
|
||||||
|
>
|
||||||
<BenefitValue benefit={benefit} />
|
<BenefitValue benefit={benefit} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ export default function BenefitList({ levels }: BenefitListProps) {
|
|||||||
<BenefitCard
|
<BenefitCard
|
||||||
title={benefit.name}
|
title={benefit.name}
|
||||||
description={benefit.description}
|
description={benefit.description}
|
||||||
comparedValues={levelBenefits.map((benefit) => {
|
comparedValues={levelBenefits.map((benefit, idx) => {
|
||||||
return {
|
return {
|
||||||
|
key: `${benefit.name}-${idx}`,
|
||||||
value: benefit.value,
|
value: benefit.value,
|
||||||
unlocked: benefit.unlocked,
|
unlocked: benefit.unlocked,
|
||||||
valueDetails: benefit.valueDetails,
|
valueDetails: benefit.valueDetails,
|
||||||
|
|||||||
@@ -14,10 +14,15 @@ export function Blocks({ lang, blocks }: BlocksProps & LangParams) {
|
|||||||
const firstItem = idx === 0
|
const firstItem = idx === 0
|
||||||
switch (block.__typename) {
|
switch (block.__typename) {
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid:
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid:
|
||||||
return <CardsGrid cards_grid={block.cards_grid} />
|
return (
|
||||||
|
<CardsGrid
|
||||||
|
key={`${block.cards_grid.title}-${idx}`}
|
||||||
|
cards_grid={block.cards_grid}
|
||||||
|
/>
|
||||||
|
)
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent:
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent:
|
||||||
return (
|
return (
|
||||||
<section>
|
<section key={`${block.__typename}-${idx}`}>
|
||||||
<JsonToHtml
|
<JsonToHtml
|
||||||
nodes={block.content.content.json.children}
|
nodes={block.content.content.json.children}
|
||||||
embeds={block.content.content.embedded_itemsConnection.edges}
|
embeds={block.content.content.embedded_itemsConnection.edges}
|
||||||
@@ -39,6 +44,7 @@ export function Blocks({ lang, blocks }: BlocksProps & LangParams) {
|
|||||||
<DynamicContentBlock
|
<DynamicContentBlock
|
||||||
dynamicContent={dynamicContent}
|
dynamicContent={dynamicContent}
|
||||||
firstItem={firstItem}
|
firstItem={firstItem}
|
||||||
|
key={`${block.dynamic_content.title}-${idx}`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
|
||||||
@@ -49,6 +55,7 @@ export function Blocks({ lang, blocks }: BlocksProps & LangParams) {
|
|||||||
return (
|
return (
|
||||||
<Shortcuts
|
<Shortcuts
|
||||||
firstItem={firstItem}
|
firstItem={firstItem}
|
||||||
|
key={`${block.shortcuts.title}-${idx}`}
|
||||||
shortcuts={shortcuts}
|
shortcuts={shortcuts}
|
||||||
title={block.shortcuts.title}
|
title={block.shortcuts.title}
|
||||||
subtitle={block.shortcuts.preamble}
|
subtitle={block.shortcuts.preamble}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function Blocks({ blocks }: BlocksProps) {
|
|||||||
switch (block.__typename) {
|
switch (block.__typename) {
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent:
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent:
|
||||||
return (
|
return (
|
||||||
<section>
|
<section key={`${block.__typename}-${idx}`}>
|
||||||
<JsonToHtml
|
<JsonToHtml
|
||||||
nodes={block.content.content.json.children}
|
nodes={block.content.content.json.children}
|
||||||
embeds={block.content.content.embedded_itemsConnection.edges}
|
embeds={block.content.content.embedded_itemsConnection.edges}
|
||||||
@@ -25,19 +25,26 @@ export function Blocks({ blocks }: BlocksProps) {
|
|||||||
<DynamicContentBlock
|
<DynamicContentBlock
|
||||||
dynamicContent={block.dynamic_content}
|
dynamicContent={block.dynamic_content}
|
||||||
firstItem={firstItem}
|
firstItem={firstItem}
|
||||||
|
key={`${block.dynamic_content.title}-${idx}`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
|
||||||
return (
|
return (
|
||||||
<Shortcuts
|
<Shortcuts
|
||||||
firstItem={firstItem}
|
firstItem={firstItem}
|
||||||
|
key={`${block.shortcuts.title}-${idx}`}
|
||||||
shortcuts={block.shortcuts.shortcuts}
|
shortcuts={block.shortcuts.shortcuts}
|
||||||
subtitle={block.shortcuts.preamble}
|
subtitle={block.shortcuts.preamble}
|
||||||
title={block.shortcuts.title}
|
title={block.shortcuts.title}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid:
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardsGrid:
|
||||||
return <CardsGrid cards_grid={block.cards_grid} />
|
return (
|
||||||
|
<CardsGrid
|
||||||
|
cards_grid={block.cards_grid}
|
||||||
|
key={`${block.cards_grid.title}-${idx}`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { login } from "@/constants/routes/handleAuth"
|
||||||
|
|
||||||
import { ScandicFriends } from "@/components/Levels"
|
import { ScandicFriends } from "@/components/Levels"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import Link from "@/components/TempDesignSystem/Link"
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
@@ -11,11 +13,12 @@ import Contact from "./Contact"
|
|||||||
import styles from "./joinLoyalty.module.css"
|
import styles from "./joinLoyalty.module.css"
|
||||||
|
|
||||||
import type { JoinLoyaltyContactProps } from "@/types/components/loyalty/sidebar"
|
import type { JoinLoyaltyContactProps } from "@/types/components/loyalty/sidebar"
|
||||||
|
import { LangParams } from "@/types/params"
|
||||||
|
|
||||||
export default async function JoinLoyaltyContact({
|
export default async function JoinLoyaltyContact({
|
||||||
block,
|
block,
|
||||||
lang,
|
lang,
|
||||||
}: JoinLoyaltyContactProps) {
|
}: JoinLoyaltyContactProps & LangParams) {
|
||||||
const { formatMessage } = await getIntl()
|
const { formatMessage } = await getIntl()
|
||||||
return (
|
return (
|
||||||
<section className={styles.container}>
|
<section className={styles.container}>
|
||||||
@@ -28,11 +31,9 @@ export default async function JoinLoyaltyContact({
|
|||||||
<Body textAlign="center">{block.preamble}</Body>
|
<Body textAlign="center">{block.preamble}</Body>
|
||||||
) : null}
|
) : null}
|
||||||
<Button asChild className={styles.link} intent="primary">
|
<Button asChild className={styles.link} intent="primary">
|
||||||
<Link href="#">
|
<Link href="#">{formatMessage({ id: "Join Scandic Friends" })}</Link>
|
||||||
{formatMessage({ id: "Join Scandic Friends" })}
|
|
||||||
</Link>
|
|
||||||
</Button>
|
</Button>
|
||||||
<Link href={`/${lang}/login`}>
|
<Link href={login[lang]}>
|
||||||
<Footnote textAlign="center" textTransform="bold">
|
<Footnote textAlign="center" textTransform="bold">
|
||||||
{formatMessage({ id: "Already a friend?" })} <br />
|
{formatMessage({ id: "Already a friend?" })} <br />
|
||||||
{formatMessage({ id: "Click here to log in" })}
|
{formatMessage({ id: "Click here to log in" })}
|
||||||
|
|||||||
@@ -6,15 +6,22 @@ import styles from "./sidebar.module.css"
|
|||||||
|
|
||||||
import { SidebarTypenameEnum } from "@/types/components/loyalty/enums"
|
import { SidebarTypenameEnum } from "@/types/components/loyalty/enums"
|
||||||
import { SidebarProps } from "@/types/components/loyalty/sidebar"
|
import { SidebarProps } from "@/types/components/loyalty/sidebar"
|
||||||
|
import { LangParams } from "@/types/params"
|
||||||
|
|
||||||
export default function SidebarLoyalty({ blocks, lang }: SidebarProps) {
|
export default function SidebarLoyalty({
|
||||||
|
blocks,
|
||||||
|
lang,
|
||||||
|
}: SidebarProps & LangParams) {
|
||||||
return (
|
return (
|
||||||
<aside className={styles.aside}>
|
<aside className={styles.aside}>
|
||||||
{blocks.map((block) => {
|
{blocks.map((block, idx) => {
|
||||||
switch (block.__typename) {
|
switch (block.__typename) {
|
||||||
case SidebarTypenameEnum.LoyaltyPageSidebarContent:
|
case SidebarTypenameEnum.LoyaltyPageSidebarContent:
|
||||||
return (
|
return (
|
||||||
<section className={styles.content}>
|
<section
|
||||||
|
className={styles.content}
|
||||||
|
key={`${block.__typename}-${idx}`}
|
||||||
|
>
|
||||||
<JsonToHtml
|
<JsonToHtml
|
||||||
embeds={block.content.content.embedded_itemsConnection.edges}
|
embeds={block.content.content.embedded_itemsConnection.edges}
|
||||||
nodes={block.content.content.json.children}
|
nodes={block.content.content.json.children}
|
||||||
@@ -26,6 +33,7 @@ export default function SidebarLoyalty({ blocks, lang }: SidebarProps) {
|
|||||||
<JoinLoyaltyContact
|
<JoinLoyaltyContact
|
||||||
block={block.join_loyalty_contact}
|
block={block.join_loyalty_contact}
|
||||||
lang={lang}
|
lang={lang}
|
||||||
|
key={`${block.join_loyalty_contact.title}-${idx}`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function DynamicComponent({ component, props }: AccountPageContentProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Content({ lang, content }: ContentProps) {
|
export default function Content({ lang, content }: ContentProps) {
|
||||||
return content.map((item) => {
|
return content.map((item, idx) => {
|
||||||
switch (item.__typename) {
|
switch (item.__typename) {
|
||||||
case ContentEntries.AccountPageContentDynamicContent:
|
case ContentEntries.AccountPageContentDynamicContent:
|
||||||
const link = item.dynamic_content.link.linkConnection.edges.length
|
const link = item.dynamic_content.link.linkConnection.edges.length
|
||||||
@@ -71,6 +71,7 @@ export default function Content({ lang, content }: ContentProps) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<DynamicComponent
|
<DynamicComponent
|
||||||
|
key={`${item.dynamic_content.title}-${idx}`}
|
||||||
component={item.dynamic_content.component}
|
component={item.dynamic_content.component}
|
||||||
props={componentProps}
|
props={componentProps}
|
||||||
/>
|
/>
|
||||||
@@ -78,6 +79,7 @@ export default function Content({ lang, content }: ContentProps) {
|
|||||||
case ContentEntries.AccountPageContentShortcuts:
|
case ContentEntries.AccountPageContentShortcuts:
|
||||||
return (
|
return (
|
||||||
<Shortcuts
|
<Shortcuts
|
||||||
|
key={`${item.shortcuts.title}-${idx}`}
|
||||||
shortcuts={item.shortcuts.shortcuts}
|
shortcuts={item.shortcuts.shortcuts}
|
||||||
subtitle={item.shortcuts.preamble}
|
subtitle={item.shortcuts.preamble}
|
||||||
title={item.shortcuts.title}
|
title={item.shortcuts.title}
|
||||||
@@ -85,7 +87,7 @@ export default function Content({ lang, content }: ContentProps) {
|
|||||||
)
|
)
|
||||||
case ContentEntries.AccountPageContentTextContent:
|
case ContentEntries.AccountPageContentTextContent:
|
||||||
return (
|
return (
|
||||||
<section>
|
<section key={`${item.__typename}-${idx}`}>
|
||||||
<JsonToHtml
|
<JsonToHtml
|
||||||
embeds={item.text_content.content.embedded_itemsConnection.edges}
|
embeds={item.text_content.content.embedded_itemsConnection.edges}
|
||||||
nodes={item.text_content.content.json.children}
|
nodes={item.text_content.content.json.children}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ function DynamicComponent({ component, props }: AccountPageContentProps) {
|
|||||||
export default function Content({ lang, content }: ContentProps) {
|
export default function Content({ lang, content }: ContentProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{content.map((item) => {
|
{content.map((item, idx) => {
|
||||||
switch (item.__typename) {
|
switch (item.__typename) {
|
||||||
case ContentEntries.AccountPageContentDynamicContent:
|
case ContentEntries.AccountPageContentDynamicContent:
|
||||||
const link = item.dynamic_content.link.linkConnection.edges.length
|
const link = item.dynamic_content.link.linkConnection.edges.length
|
||||||
@@ -70,6 +70,7 @@ export default function Content({ lang, content }: ContentProps) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<DynamicComponent
|
<DynamicComponent
|
||||||
|
key={`${item.dynamic_content.title}-${idx}`}
|
||||||
component={item.dynamic_content.component}
|
component={item.dynamic_content.component}
|
||||||
props={componentProps}
|
props={componentProps}
|
||||||
/>
|
/>
|
||||||
@@ -83,6 +84,7 @@ export default function Content({ lang, content }: ContentProps) {
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<Shortcuts
|
<Shortcuts
|
||||||
|
key={`${item.shortcuts.title}-${idx}`}
|
||||||
shortcuts={shortcuts}
|
shortcuts={shortcuts}
|
||||||
subtitle={item.shortcuts.preamble}
|
subtitle={item.shortcuts.preamble}
|
||||||
title={item.shortcuts.title}
|
title={item.shortcuts.title}
|
||||||
@@ -90,7 +92,7 @@ export default function Content({ lang, content }: ContentProps) {
|
|||||||
)
|
)
|
||||||
case ContentEntries.AccountPageContentTextContent:
|
case ContentEntries.AccountPageContentTextContent:
|
||||||
return (
|
return (
|
||||||
<section>
|
<section key={`${item.__typename}-${idx}`}>
|
||||||
<JsonToHtml
|
<JsonToHtml
|
||||||
embeds={
|
embeds={
|
||||||
item.text_content.content.embedded_itemsConnection.edges
|
item.text_content.content.embedded_itemsConnection.edges
|
||||||
|
|||||||
@@ -49,13 +49,10 @@
|
|||||||
line-height: var(--typography-Script-2-lineHeight);
|
line-height: var(--typography-Script-2-lineHeight);
|
||||||
letter-spacing: 0.48px;
|
letter-spacing: 0.48px;
|
||||||
padding: var(--Spacing-x1);
|
padding: var(--Spacing-x1);
|
||||||
|
margin: 0;
|
||||||
transform: rotate(-3deg);
|
transform: rotate(-3deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider {
|
|
||||||
border-bottom-color: var(--divider-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading {
|
.heading {
|
||||||
color: var(--font-color);
|
color: var(--font-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,7 @@ export default function Card({
|
|||||||
>
|
>
|
||||||
{scriptedTopTitle ? (
|
{scriptedTopTitle ? (
|
||||||
<section className={styles.scriptContainer}>
|
<section className={styles.scriptContainer}>
|
||||||
<Title className={styles.scriptedTitle} level="h3">
|
<h3 className={styles.scriptedTitle}>{scriptedTopTitle}</h3>
|
||||||
{scriptedTopTitle}
|
|
||||||
</Title>
|
|
||||||
<Divider className={styles.divider} />
|
|
||||||
</section>
|
</section>
|
||||||
) : null}
|
) : null}
|
||||||
<Title as="h5" className={styles.heading} level="h3">
|
<Title as="h5" className={styles.heading} level="h3">
|
||||||
|
|||||||
10
constants/routes/baseUrls.ts
Normal file
10
constants/routes/baseUrls.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { LanguageSwitcherData } from "@/types/requests/languageSwitcher"
|
||||||
|
|
||||||
|
export const baseUrls: LanguageSwitcherData = {
|
||||||
|
da: { url: "/da/" },
|
||||||
|
de: { url: "/de/" },
|
||||||
|
en: { url: "/en/" },
|
||||||
|
fi: { url: "/fi/" },
|
||||||
|
no: { url: "/no/" },
|
||||||
|
sv: { url: "/sv/" },
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
describe("Hello World", () => {
|
|
||||||
it("should have an h1 tag", () => {
|
|
||||||
cy.visit("/en/test")
|
|
||||||
cy.get("h1").contains("Hello World")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
32
cypress/e2e/static-pages/about.cy.ts
Normal file
32
cypress/e2e/static-pages/about.cy.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
describe("About page", () => {
|
||||||
|
it("should load in Danish", () => {
|
||||||
|
cy.visit("/da/kundeservice/sporgsmal-og-svar/om-scandics-website")
|
||||||
|
cy.get("h1").contains("SCANDICS WEBSITE – SPØRGSMÅL OG SVAR")
|
||||||
|
})
|
||||||
|
it("should load in German", () => {
|
||||||
|
cy.visit(
|
||||||
|
"/de/kundenbetreuung/haufig-gestellte-fragen/nutzung-der-internetseite"
|
||||||
|
)
|
||||||
|
cy.get("h1").contains("SCANDICS WEBSEITE – FRAGEN UND ANTWORTEN")
|
||||||
|
})
|
||||||
|
it("should load in English", () => {
|
||||||
|
cy.visit(
|
||||||
|
"/en/customer-service/frequently-asked-questions/using-the-website"
|
||||||
|
)
|
||||||
|
cy.get("h1").contains("SCANDIC WEBSITE – QUESTIONS AND ANSWERS")
|
||||||
|
})
|
||||||
|
it("should load in Finnish", () => {
|
||||||
|
cy.visit(
|
||||||
|
"/fi/asiakaspalvelu/usein-kysytyt-kysymykset/tietoja-internetsivuista"
|
||||||
|
)
|
||||||
|
cy.get("h1").contains("SCANDIC-VERKKOSIVUT – KYSYMYKSIÄ JA VASTAUKSIA")
|
||||||
|
})
|
||||||
|
it("should load in Norwegian", () => {
|
||||||
|
cy.visit("/no/kundeservice/sporsmal-og-svar/bruk-av-nettsiden")
|
||||||
|
cy.get("h1").contains("SCANDIC-NETTSTEDET – SPØRSMÅL OG SVAR")
|
||||||
|
})
|
||||||
|
it("should load in Swedish", () => {
|
||||||
|
cy.visit("/sv/kundservice/fragor-och-svar/om-scandics-webbplats")
|
||||||
|
cy.get("h1").contains("SCANDICS WEBBPLATS - FRÅGOR OCH SVAR")
|
||||||
|
})
|
||||||
|
})
|
||||||
26
cypress/e2e/static-pages/sponsoring.cy.ts
Normal file
26
cypress/e2e/static-pages/sponsoring.cy.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
describe("Sponsoring page", () => {
|
||||||
|
it("should load in Danish", () => {
|
||||||
|
cy.visit("/da/sponsorering")
|
||||||
|
cy.get("h1").contains("SCANDICS SYN PÅ SPONSORATER")
|
||||||
|
})
|
||||||
|
it("should load in German", () => {
|
||||||
|
cy.visit("/de/sponsoring")
|
||||||
|
cy.get("h1").contains("SPONSORING BEI SCANDIC")
|
||||||
|
})
|
||||||
|
it("should load in English", () => {
|
||||||
|
cy.visit("/en/sponsoring")
|
||||||
|
cy.get("h1").contains("SCANDIC'S TAKE ON SPONSORSHIP")
|
||||||
|
})
|
||||||
|
it("should load in Finnish", () => {
|
||||||
|
cy.visit("/fi/sponsorointi")
|
||||||
|
cy.get("h1").contains("SCANDIC JA SPONSOROINTI")
|
||||||
|
})
|
||||||
|
it("should load in Norwegian", () => {
|
||||||
|
cy.visit("/no/vi-sponser")
|
||||||
|
cy.get("h1").contains("SCANDICS SYN PÅ SPONSING")
|
||||||
|
})
|
||||||
|
it("should load in Swedish", () => {
|
||||||
|
cy.visit("/sv/vi-sponsrar")
|
||||||
|
cy.get("h1").contains("SÅ SER SCANDIC PÅ SPONSRING")
|
||||||
|
})
|
||||||
|
})
|
||||||
26
cypress/e2e/static-pages/wifi.cy.ts
Normal file
26
cypress/e2e/static-pages/wifi.cy.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
describe("Wifi page", () => {
|
||||||
|
it("should load in Danish", () => {
|
||||||
|
cy.visit("/da/oplev-scandic/wifi")
|
||||||
|
cy.get("h1").contains("FRI WIFI ER EN SELVFØLGE")
|
||||||
|
})
|
||||||
|
it("should load in German", () => {
|
||||||
|
cy.visit("/de/scandic-entdecken/wlan")
|
||||||
|
cy.get("h1").contains("GRATIS WLAN IST EINE SELBSTVERSTÄNDLICHKEIT")
|
||||||
|
})
|
||||||
|
it("should load in English", () => {
|
||||||
|
cy.visit("/en/explore-scandic/wifi")
|
||||||
|
cy.get("h1").contains("FREE WI-FI GOES WITHOUT SAYING")
|
||||||
|
})
|
||||||
|
it("should load in Finnish", () => {
|
||||||
|
cy.visit("/fi/koe-scandic/maksuton-internetyhteys")
|
||||||
|
cy.get("h1").contains("MAKSUTON WI-FI")
|
||||||
|
})
|
||||||
|
it("should load in Norwegian", () => {
|
||||||
|
cy.visit("/no/utforsk-scandic/wifi")
|
||||||
|
cy.get("h1").contains("SELVFØLGELIG HAR VI GRATIS WIFI")
|
||||||
|
})
|
||||||
|
it("should load in Swedish", () => {
|
||||||
|
cy.visit("/sv/utforska-scandic/wi-fi")
|
||||||
|
cy.get("h1").contains("FRITT WIFI, SÅ KLART")
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -18,3 +18,13 @@ import "./commands"
|
|||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
// Alternatively you can use CommonJS syntax:
|
||||||
// require('./commands')
|
// require('./commands')
|
||||||
|
Cypress.on("uncaught:exception", (err) => {
|
||||||
|
// https://github.com/cypress-io/cypress/issues/27204
|
||||||
|
// Cypress and React Hydrating the document don't get along
|
||||||
|
// for some unknown reason. Hopefully, we figure out why eventually
|
||||||
|
// so we can remove this.
|
||||||
|
// Maybe React 19 (that has changes to hydration logic) might solve this.
|
||||||
|
if (/hydration|hydrating/i.test(err.message)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,23 +1,35 @@
|
|||||||
query GetDaDeEnUrlsCurrentBlocksPage($uid: String!) {
|
query GetDaDeEnUrlsCurrentBlocksPage($uid: String!) {
|
||||||
de: current_blocks_page(uid: $uid, locale: "de") {
|
de: all_current_blocks_page(where: { uid: $uid }, locale: "de") {
|
||||||
url: original_url
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
en: current_blocks_page(uid: $uid, locale: "en") {
|
en: all_current_blocks_page(where: { uid: $uid }, locale: "en") {
|
||||||
url: original_url
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
da: current_blocks_page(uid: $uid, locale: "da") {
|
da: all_current_blocks_page(where: { uid: $uid }, locale: "da") {
|
||||||
url: original_url
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query GetFiNoSvUrlsCurrentBlocksPage($uid: String!) {
|
query GetFiNoSvUrlsCurrentBlocksPage($uid: String!) {
|
||||||
fi: current_blocks_page(uid: $uid, locale: "fi") {
|
fi: all_current_blocks_page(where: { uid: $uid }, locale: "fi") {
|
||||||
url: original_url
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
no: current_blocks_page(uid: $uid, locale: "no") {
|
no: all_current_blocks_page(where: { uid: $uid }, locale: "no") {
|
||||||
url: original_url
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sv: current_blocks_page(uid: $uid, locale: "sv") {
|
sv: all_current_blocks_page(where: { uid: $uid }, locale: "sv") {
|
||||||
url: original_url
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
74
lib/graphql/_request.ts
Normal file
74
lib/graphql/_request.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import "server-only"
|
||||||
|
|
||||||
|
import { GraphQLClient } from "graphql-request"
|
||||||
|
|
||||||
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
|
import type { DocumentNode } from "graphql"
|
||||||
|
|
||||||
|
import type { Data } from "@/types/request"
|
||||||
|
|
||||||
|
export async function request<T>(
|
||||||
|
client: GraphQLClient,
|
||||||
|
query: string | DocumentNode,
|
||||||
|
variables?: {},
|
||||||
|
next?: NextFetchRequestConfig
|
||||||
|
): Promise<Data<T>> {
|
||||||
|
try {
|
||||||
|
if (next) {
|
||||||
|
client.requestConfig.next = next
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env.PRINT_QUERY) {
|
||||||
|
const print = (await import("graphql/language/printer")).print
|
||||||
|
const rawResponse = await client.rawRequest<T>(
|
||||||
|
print(query as DocumentNode),
|
||||||
|
variables,
|
||||||
|
{
|
||||||
|
access_token: env.CMS_ACCESS_TOKEN,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Send to Monitoring (Logging and Metrics)
|
||||||
|
*/
|
||||||
|
console.log({
|
||||||
|
complexityLimit: rawResponse.headers.get("x-query-complexity"),
|
||||||
|
})
|
||||||
|
console.log({
|
||||||
|
referenceDepth: rawResponse.headers.get("x-reference-depth"),
|
||||||
|
})
|
||||||
|
console.log({ resolverCost: rawResponse.headers.get("x-resolver-cost") })
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: rawResponse.data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const print = (await import("graphql/language/printer")).print
|
||||||
|
const nr = Math.random()
|
||||||
|
console.log(`START REQUEST ${nr}`)
|
||||||
|
console.time(`OUTGOING REQUEST ${nr}`)
|
||||||
|
console.log(`Sending reqeust to ${env.CMS_URL}`)
|
||||||
|
console.log(`Query:`, print(query as DocumentNode))
|
||||||
|
console.log(`Variables:`, variables)
|
||||||
|
|
||||||
|
const response = await client.request<T>({
|
||||||
|
document: query,
|
||||||
|
requestHeaders: {
|
||||||
|
access_token: env.CMS_ACCESS_TOKEN,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
variables,
|
||||||
|
})
|
||||||
|
|
||||||
|
console.timeEnd(`OUTGOING REQUEST ${nr}`)
|
||||||
|
console.log({ response })
|
||||||
|
|
||||||
|
return { data: response }
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
throw new Error("Something went wrong")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,9 +12,7 @@ export async function batchRequest<T>(
|
|||||||
try {
|
try {
|
||||||
const response = await Promise.allSettled(
|
const response = await Promise.allSettled(
|
||||||
queries.map((query) =>
|
queries.map((query) =>
|
||||||
request<T>(query.document, query.variables, {
|
request<T>(query.document, query.variables, { tags: query.tags })
|
||||||
next: { tags: query.tags },
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
20
lib/graphql/edgeRequest.ts
Normal file
20
lib/graphql/edgeRequest.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { DocumentNode } from "graphql"
|
||||||
|
import { GraphQLClient } from "graphql-request"
|
||||||
|
|
||||||
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
|
import { request as _request } from "./_request"
|
||||||
|
|
||||||
|
import { Data } from "@/types/request"
|
||||||
|
|
||||||
|
const client = new GraphQLClient(env.CMS_URL, {
|
||||||
|
fetch: fetch,
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function edgeRequest<T>(
|
||||||
|
query: string | DocumentNode,
|
||||||
|
variables?: {},
|
||||||
|
next?: NextFetchRequestConfig
|
||||||
|
): Promise<Data<T>> {
|
||||||
|
return _request(client, query, variables, next)
|
||||||
|
}
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
import "server-only"
|
import { DocumentNode } from "graphql"
|
||||||
|
|
||||||
import { GraphQLClient } from "graphql-request"
|
import { GraphQLClient } from "graphql-request"
|
||||||
import { cache } from "react"
|
import { cache } from "react"
|
||||||
|
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
import type { DocumentNode } from "graphql"
|
import { request as _request } from "./_request"
|
||||||
|
|
||||||
import type { Data } from "@/types/request"
|
import { Data } from "@/types/request"
|
||||||
|
|
||||||
const client = new GraphQLClient(env.CMS_URL, {
|
const client = new GraphQLClient(env.CMS_URL, {
|
||||||
cache: "force-cache",
|
|
||||||
fetch: cache(async function (
|
fetch: cache(async function (
|
||||||
url: URL | RequestInfo,
|
url: URL | RequestInfo,
|
||||||
params: RequestInit | undefined
|
params: RequestInit | undefined
|
||||||
@@ -22,50 +20,7 @@ const client = new GraphQLClient(env.CMS_URL, {
|
|||||||
export async function request<T>(
|
export async function request<T>(
|
||||||
query: string | DocumentNode,
|
query: string | DocumentNode,
|
||||||
variables?: {},
|
variables?: {},
|
||||||
options?: Pick<RequestInit, "cache" | "next">
|
next?: NextFetchRequestConfig
|
||||||
): Promise<Data<T>> {
|
): Promise<Data<T>> {
|
||||||
if (options?.cache) {
|
return _request(client, query, variables, next)
|
||||||
client.requestConfig.cache = options.cache
|
|
||||||
}
|
|
||||||
if (options?.next) {
|
|
||||||
client.requestConfig.next = options.next
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.PRINT_QUERY) {
|
|
||||||
const { print } = await import("graphql")
|
|
||||||
const rawResponse = await client.rawRequest<T>(
|
|
||||||
print(query as DocumentNode),
|
|
||||||
variables,
|
|
||||||
{
|
|
||||||
access_token: env.CMS_ACCESS_TOKEN,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Send to Monitoring (Logging and Metrics)
|
|
||||||
*/
|
|
||||||
console.log({
|
|
||||||
complexityLimit: rawResponse.headers.get("x-query-complexity"),
|
|
||||||
})
|
|
||||||
console.log({
|
|
||||||
referenceDepth: rawResponse.headers.get("x-reference-depth"),
|
|
||||||
})
|
|
||||||
console.log({ resolverCost: rawResponse.headers.get("x-resolver-cost") })
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: rawResponse.data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await client.request<T>({
|
|
||||||
document: query,
|
|
||||||
requestHeaders: {
|
|
||||||
access_token: env.CMS_ACCESS_TOKEN,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
variables,
|
|
||||||
})
|
|
||||||
|
|
||||||
return { data: response }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { NextMiddleware, NextResponse } from "next/server"
|
import { NextMiddleware, NextResponse } from "next/server"
|
||||||
|
|
||||||
import { findLang } from "./constants/languages"
|
import { findLang, Lang } from "./constants/languages"
|
||||||
import * as authRequired from "./middlewares/authRequired"
|
import * as authRequired from "./middlewares/authRequired"
|
||||||
import * as cmsContent from "./middlewares/cmsContent"
|
import * as cmsContent from "./middlewares/cmsContent"
|
||||||
import * as currentWebLogin from "./middlewares/currentWebLogin"
|
import * as currentWebLogin from "./middlewares/currentWebLogin"
|
||||||
@@ -16,8 +16,10 @@ export const middleware: NextMiddleware = async (request, event) => {
|
|||||||
// Without it we shortcircuit early.
|
// Without it we shortcircuit early.
|
||||||
// We use middleware-error route because notFound() requires a root layout
|
// We use middleware-error route because notFound() requires a root layout
|
||||||
// which we do not want. We can move to that once all Current stuff is gone.
|
// which we do not want. We can move to that once all Current stuff is gone.
|
||||||
|
|
||||||
|
// Default to English if no lang is found.
|
||||||
return NextResponse.rewrite(
|
return NextResponse.rewrite(
|
||||||
new URL(`/${lang}/middleware-error/404`, request.nextUrl),
|
new URL(`/${Lang.en}/middleware-error/404`, request.nextUrl),
|
||||||
{
|
{
|
||||||
status: 404,
|
status: 404,
|
||||||
statusText: "Not found",
|
statusText: "Not found",
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ package = "netlify-plugin-cypress"
|
|||||||
[plugins.inputs]
|
[plugins.inputs]
|
||||||
configFile = "cypress.config.ts"
|
configFile = "cypress.config.ts"
|
||||||
[plugins.inputs.postBuild]
|
[plugins.inputs.postBuild]
|
||||||
enable = true
|
enable = true
|
||||||
start = "npm start"
|
start = "npm start"
|
||||||
wait-on = "http://127.0.0.1:3000/en/test"
|
wait-on = "http://127.0.0.1:3000/en/sponsoring"
|
||||||
wait-on-timeout = "30" # seconds
|
wait-on-timeout = "30" # seconds
|
||||||
|
|
||||||
[build.environment]
|
[build.environment]
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
"start": "node .next/standalone/server.js",
|
"start": "node .next/standalone/server.js",
|
||||||
"test:component": "cypress open --component",
|
"test:component": "cypress open --component",
|
||||||
"test:component:headless": "cypress run --component",
|
"test:component:headless": "cypress run --component",
|
||||||
"test:e2e": "start-server-and-test test:setup http://127.0.0.1:3000/en/test \"cypress open --e2e\"",
|
"test:e2e": "start-server-and-test test:setup http://127.0.0.1:3000/en/sponsoring \"cypress open --e2e\"",
|
||||||
"test:e2e:headless": "start-server-and-test test:setup http://127.0.0.1:3000/en/test \"cypress run --e2e\"",
|
"test:e2e:headless": "start-server-and-test test:setup http://127.0.0.1:3000/en/sponsoring \"cypress run --e2e\"",
|
||||||
"test:setup": "npm run build && npm run start",
|
"test:setup": "npm run build && npm run start",
|
||||||
"preinstall": "export $(cat .env.local | grep -v '^#' | xargs)",
|
"preinstall": "export $(cat .env.local | grep -v '^#' | xargs)",
|
||||||
"update-dotenv": "node update-dotenv.mjs"
|
"update-dotenv": "node update-dotenv.mjs"
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -37,9 +37,7 @@ export const accountPageQueryRouter = router({
|
|||||||
uid,
|
uid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
next: {
|
tags: [generateRefsResponseTag(lang, uid)],
|
||||||
tags: [generateRefsResponseTag(lang, uid)],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -71,7 +69,7 @@ export const accountPageQueryRouter = router({
|
|||||||
locale: lang,
|
locale: lang,
|
||||||
uid,
|
uid,
|
||||||
},
|
},
|
||||||
{ next: { tags } }
|
{ tags }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
|
|||||||
5
server/routers/contentstack/base/input.ts
Normal file
5
server/routers/contentstack/base/input.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
|
export const langInput = z.object({ lang: z.nativeEnum(Lang) })
|
||||||
@@ -9,10 +9,15 @@ import {
|
|||||||
} from "@/lib/graphql/Query/CurrentHeader.graphql"
|
} from "@/lib/graphql/Query/CurrentHeader.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { internalServerError, notFound } from "@/server/errors/trpc"
|
import { internalServerError, notFound } from "@/server/errors/trpc"
|
||||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
import {
|
||||||
|
contentstackBaseProcedure,
|
||||||
|
publicProcedure,
|
||||||
|
router,
|
||||||
|
} from "@/server/trpc"
|
||||||
|
|
||||||
import { generateTag } from "@/utils/generateTag"
|
import { generateTag } from "@/utils/generateTag"
|
||||||
|
|
||||||
|
import { langInput } from "./input"
|
||||||
import {
|
import {
|
||||||
type ContactConfigData,
|
type ContactConfigData,
|
||||||
FooterDataRaw,
|
FooterDataRaw,
|
||||||
@@ -47,23 +52,21 @@ export const baseQueryRouter = router({
|
|||||||
|
|
||||||
return validatedContactConfigConfig.data.all_contact_config.items[0]
|
return validatedContactConfigConfig.data.all_contact_config.items[0]
|
||||||
}),
|
}),
|
||||||
header: contentstackBaseProcedure.query(async ({ ctx }) => {
|
header: publicProcedure.input(langInput).query(async ({ input }) => {
|
||||||
const responseRef = await request<HeaderRefDataRaw>(GetCurrentHeaderRef, {
|
const responseRef = await request<HeaderRefDataRaw>(GetCurrentHeaderRef, {
|
||||||
locale: ctx.lang,
|
locale: input.lang,
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = await request<HeaderDataRaw>(
|
const response = await request<HeaderDataRaw>(
|
||||||
GetCurrentHeader,
|
GetCurrentHeader,
|
||||||
{ locale: ctx.lang },
|
{ locale: input.lang },
|
||||||
{
|
{
|
||||||
next: {
|
tags: [
|
||||||
tags: [
|
generateTag(
|
||||||
generateTag(
|
input.lang,
|
||||||
ctx.lang,
|
responseRef.data.all_current_header.items[0].system.uid
|
||||||
responseRef.data.all_current_header.items[0].system.uid
|
),
|
||||||
),
|
],
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -88,25 +91,23 @@ export const baseQueryRouter = router({
|
|||||||
logo,
|
logo,
|
||||||
} as HeaderData
|
} as HeaderData
|
||||||
}),
|
}),
|
||||||
footer: contentstackBaseProcedure.query(async ({ ctx }) => {
|
footer: publicProcedure.input(langInput).query(async ({ input }) => {
|
||||||
const responseRef = await request<FooterRefDataRaw>(GetCurrentFooterRef, {
|
const responseRef = await request<FooterRefDataRaw>(GetCurrentFooterRef, {
|
||||||
locale: ctx.lang,
|
locale: input.lang,
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = await request<FooterDataRaw>(
|
const response = await request<FooterDataRaw>(
|
||||||
GetCurrentFooter,
|
GetCurrentFooter,
|
||||||
{
|
{
|
||||||
locale: ctx.lang,
|
locale: input.lang,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
next: {
|
tags: [
|
||||||
tags: [
|
generateTag(
|
||||||
generateTag(
|
input.lang,
|
||||||
ctx.lang,
|
responseRef.data.all_current_footer.items[0].system.uid
|
||||||
responseRef.data.all_current_footer.items[0].system.uid
|
),
|
||||||
),
|
],
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -68,9 +68,7 @@ export type Variables = {
|
|||||||
|
|
||||||
export async function getRefsResponse<T>(query: string, variables: Variables) {
|
export async function getRefsResponse<T>(query: string, variables: Variables) {
|
||||||
const refsResponse = await request<T>(query, variables, {
|
const refsResponse = await request<T>(query, variables, {
|
||||||
next: {
|
tags: [generateRefsResponseTag(variables.locale, variables.url, affix)],
|
||||||
tags: [generateRefsResponseTag(variables.locale, variables.url, affix)],
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
if (!refsResponse.data) {
|
if (!refsResponse.data) {
|
||||||
throw notFound(refsResponse)
|
throw notFound(refsResponse)
|
||||||
@@ -91,7 +89,7 @@ export async function getResponse<T>(
|
|||||||
variables: Variables,
|
variables: Variables,
|
||||||
tags: string[]
|
tags: string[]
|
||||||
) {
|
) {
|
||||||
const response = await request<T>(query, variables, { next: { tags } })
|
const response = await request<T>(query, variables, { tags })
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
throw notFound(response)
|
throw notFound(response)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Lang } from "@/constants/languages"
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { baseUrls } from "@/constants/routes/baseUrls"
|
||||||
import { batchRequest } from "@/lib/graphql/batchRequest"
|
import { batchRequest } from "@/lib/graphql/batchRequest"
|
||||||
import {
|
import {
|
||||||
GetDaDeEnUrlsAccountPage,
|
GetDaDeEnUrlsAccountPage,
|
||||||
@@ -13,7 +14,7 @@ import {
|
|||||||
GetFiNoSvUrlsLoyaltyPage,
|
GetFiNoSvUrlsLoyaltyPage,
|
||||||
} from "@/lib/graphql/Query/LoyaltyPage.graphql"
|
} from "@/lib/graphql/Query/LoyaltyPage.graphql"
|
||||||
import { internalServerError } from "@/server/errors/trpc"
|
import { internalServerError } from "@/server/errors/trpc"
|
||||||
import { contentstackExtendedProcedureUID, router } from "@/server/trpc"
|
import { publicProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { generateTag } from "@/utils/generateTag"
|
import { generateTag } from "@/utils/generateTag"
|
||||||
|
|
||||||
@@ -91,12 +92,14 @@ async function getLanguageSwitcher(options: LanguageSwitcherVariables) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const languageSwitcherQueryRouter = router({
|
export const languageSwitcherQueryRouter = router({
|
||||||
get: contentstackExtendedProcedureUID.query(async ({ ctx }) => {
|
get: publicProcedure.query(async ({ ctx }) => {
|
||||||
|
if (!ctx.uid || !ctx.lang) {
|
||||||
|
return { lang: ctx.lang, urls: baseUrls }
|
||||||
|
}
|
||||||
const res = await getLanguageSwitcher({
|
const res = await getLanguageSwitcher({
|
||||||
contentType: ctx.contentType!,
|
contentType: ctx.contentType!,
|
||||||
uid: ctx.uid,
|
uid: ctx.uid,
|
||||||
})
|
})
|
||||||
|
|
||||||
const urls = Object.keys(res.data).reduce<LanguageSwitcherData>(
|
const urls = Object.keys(res.data).reduce<LanguageSwitcherData>(
|
||||||
(acc, key) => {
|
(acc, key) => {
|
||||||
const item = res.data[key as Lang]?.items[0]
|
const item = res.data[key as Lang]?.items[0]
|
||||||
|
|||||||
@@ -54,9 +54,7 @@ export const loyaltyPageQueryRouter = router({
|
|||||||
uid,
|
uid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
next: {
|
tags: [generateRefsResponseTag(lang, uid)],
|
||||||
tags: [generateRefsResponseTag(lang, uid)],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,7 +88,7 @@ export const loyaltyPageQueryRouter = router({
|
|||||||
locale: lang,
|
locale: lang,
|
||||||
uid,
|
uid,
|
||||||
},
|
},
|
||||||
{ next: { tags } }
|
{ tags }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
|
|||||||
@@ -60,9 +60,7 @@ export const navigationQueryRouter = router({
|
|||||||
GetNavigationMyPagesRefs,
|
GetNavigationMyPagesRefs,
|
||||||
{ locale: lang },
|
{ locale: lang },
|
||||||
{
|
{
|
||||||
next: {
|
tags: [generateRefsResponseTag(lang, "navigation_my_pages")],
|
||||||
tags: [generateRefsResponseTag(lang, "navigation_my_pages")],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,7 +88,7 @@ export const navigationQueryRouter = router({
|
|||||||
const response = await request<GetNavigationMyPagesData>(
|
const response = await request<GetNavigationMyPagesData>(
|
||||||
GetNavigationMyPages,
|
GetNavigationMyPages,
|
||||||
{ locale: lang },
|
{ locale: lang },
|
||||||
{ next: { tags } }
|
{ tags }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ import { initTRPC } from "@trpc/server"
|
|||||||
|
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
import {
|
import { badRequestError, sessionExpiredError } from "./errors/trpc"
|
||||||
badRequestError,
|
|
||||||
sessionExpiredError,
|
|
||||||
unauthorizedError,
|
|
||||||
} from "./errors/trpc"
|
|
||||||
import { transformer } from "./transformer"
|
import { transformer } from "./transformer"
|
||||||
|
|
||||||
import type { Meta } from "@/types/trpc/meta"
|
import type { Meta } from "@/types/trpc/meta"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export type MainMenuProps = {
|
|||||||
links: CurrentHeaderLink[]
|
links: CurrentHeaderLink[]
|
||||||
logo: Image
|
logo: Image
|
||||||
topMenuMobileLinks: TopMenuHeaderLink[]
|
topMenuMobileLinks: TopMenuHeaderLink[]
|
||||||
languageSwitcher: React.ReactNode
|
languageSwitcher: React.ReactNode | null
|
||||||
bookingHref: string
|
bookingHref: string
|
||||||
isLoggedIn: boolean
|
isLoggedIn: boolean
|
||||||
lang: Lang
|
lang: Lang
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ export type TopMenuProps = {
|
|||||||
frontpageLinkText: string
|
frontpageLinkText: string
|
||||||
homeHref: string
|
homeHref: string
|
||||||
links: TopMenuHeaderLink[]
|
links: TopMenuHeaderLink[]
|
||||||
languageSwitcher: React.ReactNode
|
languageSwitcher: React.ReactNode | null
|
||||||
lang: Lang
|
lang: Lang
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export enum Section {
|
|||||||
type ExtraInfo = Typename<
|
type ExtraInfo = Typename<
|
||||||
{
|
{
|
||||||
extra_info: {
|
extra_info: {
|
||||||
text: string
|
text: string[]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Section.ContactBlockSectionsExtraInfo
|
Section.ContactBlockSectionsExtraInfo
|
||||||
|
|||||||
@@ -1,31 +1,23 @@
|
|||||||
import { DocumentNode, print } from "graphql"
|
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
import { Lang } from "@/constants/languages"
|
||||||
import { env } from "@/env/server"
|
import { edgeRequest } from "@/lib/graphql/edgeRequest"
|
||||||
import { ResolveEntryByUrl } from "@/lib/graphql/Query/ResolveEntry.graphql"
|
import { ResolveEntryByUrl } from "@/lib/graphql/Query/ResolveEntry.graphql"
|
||||||
import { internalServerError } from "@/server/errors/next"
|
import { internalServerError } from "@/server/errors/next"
|
||||||
|
|
||||||
import { validateEntryResolveSchema } from "@/types/requests/entry"
|
import { validateEntryResolveSchema } from "@/types/requests/entry"
|
||||||
|
|
||||||
export async function resolve(url: string, lang = Lang.en) {
|
export async function resolve(url: string, lang = Lang.en) {
|
||||||
const result = await fetch(env.CMS_URL, {
|
const response = await edgeRequest(
|
||||||
method: "POST",
|
ResolveEntryByUrl,
|
||||||
headers: {
|
{
|
||||||
access_token: env.CMS_ACCESS_TOKEN,
|
locale: lang,
|
||||||
"Content-Type": "application/json",
|
url,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
{
|
||||||
query: print(ResolveEntryByUrl as DocumentNode),
|
revalidate: 3600,
|
||||||
variables: {
|
}
|
||||||
locale: lang,
|
)
|
||||||
url,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
||||||
const { data } = await result.json()
|
const validatedData = validateEntryResolveSchema.safeParse(response.data)
|
||||||
|
|
||||||
const validatedData = validateEntryResolveSchema.safeParse(data)
|
|
||||||
|
|
||||||
if (!validatedData.success) {
|
if (!validatedData.success) {
|
||||||
throw internalServerError(validatedData.error)
|
throw internalServerError(validatedData.error)
|
||||||
|
|||||||
Reference in New Issue
Block a user