Merged in feat/language-switcher-revalidation (pull request #42)

Feat/language switcher revalidation

Approved-by: Christel Westerberg
Approved-by: Arvid Norlin
This commit is contained in:
Simon.Emanuelsson
2024-02-22 16:13:53 +00:00
committed by Christel Westerberg
85 changed files with 1176 additions and 1801 deletions

View File

@@ -1,8 +1,8 @@
import AdobeScript from "@/components/Current/AdobeScript"
import Script from "next/script"
import type { Metadata } from "next"
import type { LangParams, LayoutArgs } from "@/types/params"
import AdobeScript from "../AdobeScript"
export const metadata: Metadata = {
description: "New web",

View File

@@ -4,7 +4,8 @@ import { request } from "@/lib/request"
import { GetCurrentBlockPage } from "@/lib/graphql/Query/CurrentBlockPage.graphql"
import ContentPage from "@/components/Current/ContentPage"
import Tracking from "../../Tracking"
import Header from "@/components/Current/Header"
import Tracking from "@/components/Current/Tracking"
import type { PageArgs, LangParams, UriParams } from "@/types/params"
import type { GetCurrentBlockPageData } from "@/types/requests/currentBlockPage"
@@ -23,6 +24,9 @@ export default async function CurrentContentPage({
{
locale: params.lang,
url: searchParams.uri,
},
{
tags: [`${searchParams.uri}-${params.lang}`]
}
)
@@ -43,11 +47,8 @@ export default async function CurrentContentPage({
return (
<>
<ContentPage
data={response.data}
uri={searchParams.uri}
lang={params.lang}
/>
<Header lang={params.lang} uid={pageData.system.uid} />
<ContentPage data={response.data} />
<Tracking pageData={trackingData} />
</>
)

View File

@@ -1,10 +1,10 @@
/* eslint-disable @next/next/no-css-tags */
import AdobeScript from "@/components/Current/AdobeScript"
import Footer from "@/components/Current/Footer"
import LangPopup from "@/components/Current/LangPopup"
import Script from "next/script"
import SkipToMainContent from "@/components/SkipToMainContent"
import AdobeScript from "../AdobeScript"
import type { Metadata } from "next"
import type { LangParams, LayoutArgs } from "@/types/params"
@@ -21,8 +21,8 @@ export default function RootLayout({
return (
<html lang={params.lang}>
<head>
<link rel="stylesheet" href="/Static/css/core.css"/>
<link rel="stylesheet" href="/Static/css/scandic.css"/>
<link rel="stylesheet" href="/Static/css/core.css" />
<link rel="stylesheet" href="/Static/css/scandic.css" />
<Script
data-cookieconsent="ignore"
src="/Static/dist/js/cookie-bot.js?1705409331007"

View File

@@ -1,6 +1,7 @@
import InitLivePreview from "@/components/Current/LivePreview"
import type { Metadata } from "next"
import type { LangParams, LayoutArgs } from "@/types/params"
import InitLivePreview from "@/components/Current/LivePreview"
export const metadata: Metadata = {
description: "New web",

View File

@@ -30,13 +30,7 @@ export default async function CurrentPreviewPage({
throw new Error("Not found")
}
return (
<ContentPage
data={response.data}
uri={searchParams.uri}
lang={params.lang}
/>
)
return <ContentPage data={response.data} />
} catch (error) {
// TODO: throw 500
console.error(error)

View File

@@ -0,0 +1,45 @@
import { env } from "@/env/server"
import { revalidateTag } from 'next/cache'
import type { NextRequest } from 'next/server'
export async function POST(request: NextRequest) {
try {
const secret = request.nextUrl.searchParams.get("secret") ?? ""
const tag = request.nextUrl.searchParams.get("tag") ?? ""
if (secret !== env.REVALIDATE_SECRET) {
return Response.json(
{
message: 'Invalid secret',
now: Date.now(),
revalidated: false,
},
{
status: 401,
}
)
}
if (!tag) {
return Response.json(
{
message: 'Missing tag param',
now: Date.now(),
revalidated: false,
},
{
status: 400,
}
)
}
revalidateTag(tag)
return Response.json({ revalidated: true, now: Date.now() })
} catch (error) {
console.info("Failed to revalidate tag")
console.error(error)
return Response.json({ revalidated: false, now: Date.now() }, { status: 500 })
}
}

View File

@@ -0,0 +1,42 @@
import { renderOptions } from "./renderOptions"
import Image from "@/components/Image"
import JsonToHtml from "@/components/JsonToHtml"
import styles from "./puff.module.css"
import type { PuffProps } from "@/types/components/current/asides/puff"
export default function Puff({
imageConnection,
link,
text,
title,
}: PuffProps) {
return (
<a className={styles.link} href={link.href}>
<article>
{imageConnection.edges.map((image) => (
<Image
alt={image.node.title}
className={styles.image}
height={image.node.dimension.height}
key={image.node.system.uid}
src={image.node.url}
width={image.node.dimension.width}
/>
))}
<section className={styles.content}>
<header>
<h3 className={styles.heading}>{title}</h3>
</header>
<JsonToHtml
embeds={[]}
nodes={text.json.children}
renderOptions={renderOptions}
/>
</section>
</article>
</a>
)
}

View File

@@ -1,74 +0,0 @@
import { renderOptions } from "./renderOptions"
import JsonToHtml from "@/components/JsonToHtml"
import Link from "next/link"
import styles from "./puff.module.css"
import type { PuffProps } from "@/types/components/current/asides/puff"
import Image from "@/components/Image"
export default function Puff({
imageConnection,
is_internal,
link,
pageConnection,
text,
title,
}: PuffProps) {
if (is_internal) {
const page = pageConnection.edges[0]
if (!page?.node?.url) {
return null
}
return (
<Link className={styles.link} href={page.node.url}>
<PuffContent
imageConnection={imageConnection}
text={text}
title={title}
/>
</Link>
)
}
return (
<a className={styles.link} href={link.href} target="_blank">
<PuffContent
imageConnection={imageConnection}
text={text}
title={title}
/>
</a>
)
}
function PuffContent({
imageConnection,
text,
title,
}: Pick<PuffProps, "imageConnection" | "text" | "title">) {
return (
<article>
{imageConnection.edges.map((image) => (
<Image
alt={image.node.title}
className={styles.image}
height={image.node.dimension.height}
key={image.node.system.uid}
src={image.node.url}
width={image.node.dimension.width}
/>
))}
<section className={styles.content}>
<header>
<h3 className={styles.heading}>{title}</h3>
</header>
<JsonToHtml
embeds={text.embedded_itemsConnection.edges}
nodes={text.json.children}
renderOptions={renderOptions}
/>
</section>
</article>
)
}

View File

@@ -1,16 +0,0 @@
import Puff from "./Puff"
import type { PuffsProps } from "@/types/components/current/asides/puffs"
export default function Puffs({ puffs }: PuffsProps) {
if (!puffs.length) {
return null
}
return (
<>
{puffs.map((puff) => (
<Puff key={puff.node.system.uid} {...puff.node} />
))}
</>
)
}

View File

@@ -1,4 +1,4 @@
import Puffs from "./Puffs"
import Puff from "./Puff"
import Contacts from "./Contacts"
import { AsideTypenameEnum } from "@/types/requests/utils/typename"
@@ -25,9 +25,9 @@ export default function Aside({ blocks }: AsideProps) {
)
case AsideTypenameEnum.CurrentBlocksPageAsidePuff:
return (
<Puffs
<Puff
key={`block-${idx}`}
puffs={block.puff.puffConnection.edges}
{...block.puff}
/>
)
default:

View File

@@ -1,4 +1,3 @@
import Link from "next/link"
import { cva } from "class-variance-authority"
import { BlockListItemsEnum } from "@/types/requests/blocks/list"
@@ -63,27 +62,6 @@ export default function ListItem({ listItem }: { listItem: ListItem }) {
)}
</li>
)
case BlockListItemsEnum.CurrentBlocksPageBlocksListBlockListItemsListItemInternalLink:
const link = listItem.list_item_internal_link.pageConnection.edges[0]
const linkUrlWithLocale = `/${link.node.system.locale}${link.node.url}`
return (
<li
key={link.node.system.uid}
className={listItemStyle({
type: listItem.list_item_internal_link.list_item_style,
})}
>
<Link href={linkUrlWithLocale} className={styles.link}>
{listItem.list_item_internal_link.link_text}
</Link>
{listItem.list_item_internal_link.subtitle && (
<span>
<br />
{listItem.list_item_internal_link.subtitle}
</span>
)}
</li>
)
default:
return null

View File

@@ -1,17 +1,18 @@
import type { ListProps } from "@/types/requests/blocks/list"
import ListItem from "./ListItem"
import styles from "./list.module.css"
import ListItem from "./ListItem"
import type { ListProps } from "@/types/requests/blocks/list"
export default function List({ list }: ListProps) {
return (
<div>
<>
{list.title ? <h2 className={styles.title}>{list.title}</h2> : null}
<ul className={styles.ul}>
{list.list_items.map((item, i) => (
<ListItem listItem={item} key={`list-item-${i}`} />
))}
</ul>
</div>
</>
)
}

View File

@@ -1,11 +1,16 @@
.title {
color: #483729;
font-family: BrandonText-Bold, Arial, Helvetica, sans-serif;
font-size: 1.375rem;
line-height: 1.1em;
text-transform: uppercase;
font-weight: 400;
color: #483729;
line-height: 1.1em;
margin-bottom: 1rem;
margin-top: 2rem;
text-transform: uppercase;
}
.title:first-child {
margin-top: 0;
}
.ul {
@@ -64,4 +69,4 @@
.title {
font-size: 1.625rem;
}
}
}

View File

@@ -1,8 +1,7 @@
.wrapper {
width: 100%;
position: relative;
z-index: 10;
padding-bottom: 50px;
background: #f3f2f1;
display: block;
}
padding-bottom: 50px;
width: 100%;
z-index: 10;
}

View File

@@ -1,6 +1,5 @@
import Aside from "@/components/Current/Aside"
import Blocks from "@/components/Current/Blocks"
import Header from "@/components/Current/Header"
import Hero from "@/components/Current/Hero"
import Preamble from "@/components/Current/Preamble"
import Section from "@/components/Current/Section"
@@ -10,15 +9,14 @@ import styles from "./contentPage.module.css"
import type { ContentPageProps } from "@/types/components/current/contentPage"
export default function ContentPage({ data, lang, uri }: ContentPageProps) {
export default function ContentPage({ data }: ContentPageProps) {
const page = data.all_current_blocks_page.items[0]
const images = page.hero?.imagesConnection
const breadcrumbs = page.breadcrumbs.parentsConnection
const parent = breadcrumbs.edges.at(-1)
const breadcrumbs = page.breadcrumbs.parents
const parent = breadcrumbs.at(-1)
return (
<>
<Header lang={lang} pathname={uri} />
{images?.totalCount ? <Hero images={images.edges} /> : null}
<main className={styles.wrapper} id="maincontent" role="main">
<input

View File

@@ -1,49 +1,24 @@
import {
FooterNavigationProps,
FooterNavigationItemProps,
} from "@/types/components/current/footer"
import Link from "next/link"
import type { FooterNavigationProps } from "@/types/components/current/footer"
function NavigationItem({ linkObject, lang }: FooterNavigationItemProps) {
if (linkObject.__typename === "FooterNavigationLinksInternalLink") {
const href = `/${lang}${linkObject.internal_link.pageConnection.edges[0].node.url}`
return (
<li>
<Link href={href}>{linkObject.internal_link.link_text}</Link>
</li>
)
}
return (
<li>
<a href={linkObject.external_link.link.href}>
{linkObject.external_link.link.title}
</a>
</li>
)
}
export default function Navigation({
linkGroups,
lang,
}: FooterNavigationProps) {
export default function Navigation({ linkGroups }: FooterNavigationProps) {
return (
<ul className="l-footer-sections global-footer__content__sections">
{linkGroups.map((group) => {
return (
<li className="global-footer-section" key={group.title}>
<div className="link-list">
<h3 className="link-list-header">{group.title}</h3>
<ul className="list-footer-pages">
{group.links.map((link, idx) => {
return (
<NavigationItem key={idx} linkObject={link} lang={lang} />
)
})}
</ul>
</div>
</li>
)
})}
{linkGroups.map((group) => (
<li className="global-footer-section" key={group.title}>
<div className="link-list">
<h3 className="link-list-header">{group.title}</h3>
<ul className="list-footer-pages">
{group.links.map(link => (
<li key={link.href}>
<a href={link.href}>
{link.title}
</a>
</li>
))}
</ul>
</div>
</li>
))}
</ul>
)
}

View File

@@ -9,9 +9,15 @@ import type { GetFooterData } from "@/types/requests/footer"
import type { LangParams } from "@/types/params"
export default async function Footer({ lang }: LangParams) {
const response = await request<GetFooterData>(GetFooter, {
locale: lang,
})
const response = await request<GetFooterData>(
GetFooter,
{
locale: lang,
},
{
tags: [`footer-${lang}`]
}
)
const footerData = response.data.all_footer.items[0]
return (
@@ -40,7 +46,7 @@ export default async function Footer({ lang }: LangParams) {
data-js-collapsible="global-footer"
className="collapsible-global-footer"
>
<Navigation lang={lang} linkGroups={footerData.navigation} />
<Navigation linkGroups={footerData.navigation} />
<div className="global-footer__content__bottom">
<p className="newFooterTitle">

View File

@@ -1,198 +0,0 @@
/* eslint-disable @next/next/no-img-element */
import {
currentAboutLinks,
currentSponsoringLinks,
currentWifiLinks,
} from "@/constants/current/links"
import Desktop from "./LanguageSwitcher/Desktop"
import Mobile from "./LanguageSwitcher/Mobile"
import type { LanguageSwitcherLink } from "@/types/components/current/languageSwitcher"
import type { HeaderProps } from "@/types/components/current/header"
const paths: Record<string, LanguageSwitcherLink[]> = {
"/kundeservice/sporgsmal-og-svar/om-scandics-website": currentAboutLinks,
"/oplev-scandic/wifi": currentWifiLinks,
"/sponsorering": currentSponsoringLinks,
}
const currentLanguage = "Dansk"
export default function DaHeader({ pathname }: HeaderProps) {
const links = paths?.[pathname] ?? null
return (
<header className="header" role="banner">
<div className="offline-banner hidden">
Du er offline. Noget indhold siden kan være forældet.
<button type="button" className="reload">
Opdatér siden
</button>
</div>
<div
className="l-section main-header navigation-bar"
data-js="main-nav-bar"
>
<div className="navigation-bar__top navigation-bar__top--ghostwhite-light">
<div className="l-section__inner">
<a
href="https://www.scandichotels.dk"
className="scandic-main-page-link"
>
Back to scandichotels.dk
</a>
<ul className="nav-secondary navbar-login">
<li className="nav-secondary__item hidden-xxsmall hidden-xsmall hidden-small">
{links ? (
<Desktop currentLanguage={currentLanguage} links={links} />
) : null}
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.dk/hotelreservation/hent-booking"
>
Vis/Afbestil booking
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.dk/job-hos-scandic/ledige-stillinger"
>
Arbejd med os
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.dk/forretningsrejse"
>
Forretningsrejse
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.dk/scandic-friends"
>
Om Scandic Friends
</a>
</li>
</ul>
</div>
</div>
<div className="navigation-bar__main">
<div
className="l-section__inner l-section__inner--small-no-padding"
itemScope
itemType="http://schema.org/Organization"
>
<meta itemProp="name" content="Scandic" />
<button
type="button"
className="navigation-bar__main__expander"
data-js="main-nav-toggler"
data-target="#main-menu"
aria-pressed="false"
>
<span className="icon-bars"></span>
<span className="hidden--accessible">Menu</span>
</button>
<a
id="scandic-logo"
className="navigation-bar__main__logo hidden-medium "
href="https://www.scandichotels.dk"
itemProp="url"
>
<span className="hidden--accessible">
Back to scandichotels.dk
</span>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
itemProp="logo"
/>
</a>
<nav>
<ul
id="main-menu"
className="nav-primary is-collapsed"
data-collapsable="main-menu"
>
<li
className="nav-primary__item nav-primary__item--primary hidden-large hidden-small hidden-xsmall hidden-xxsmall"
aria-hidden="true"
>
<a
className="navigation-bar__main__logo"
href="https://www.scandichotels.dk"
>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
/>
<span className="hidden--accessible">
Back to scandichotels.dk
</span>
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.dk/hoteller">
Hoteller & destinationer
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.dk/rejseguide">
Rejseguide
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.dk/mode-konference-event">
Møde, konference & event
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.dk/oplev-scandic">
Oplev Scandic
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.dk/tilbud-og-hotelpakker">
Tilbud
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.dk/scandic-friends">
Om Scandic Friends
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.dk/forretningsrejse">
Forretningsrejse
</a>
</li>
<li className="nav-primary__item hidden-medium hidden-large">
{links ? (
<Mobile currentLanguage={currentLanguage} links={links} />
) : null}
</li>
</ul>
</nav>
</div>
</div>
</div>
</header>
)
}

View File

@@ -1,209 +0,0 @@
import {
currentAboutLinks,
currentSponsoringLinks,
currentWifiLinks,
} from "@/constants/current/links"
import Desktop from "./LanguageSwitcher/Desktop"
import Mobile from "./LanguageSwitcher/Mobile"
import type { HeaderProps } from "@/types/components/current/header"
import type { LanguageSwitcherLink } from "@/types/components/current/languageSwitcher"
const paths: Record<string, LanguageSwitcherLink[]> = {
"/kundenbetreuung/haufig-gestellte-fragen/nutzung-der-internetseite":
currentAboutLinks,
"/scandic-entdecken/wlan": currentWifiLinks,
"/sponsoring": currentSponsoringLinks,
}
const currentLanguage = "Deutsch"
export default function DeHeader({ pathname }: HeaderProps) {
const links = paths?.[pathname] ?? null
return (
<header className="header" role="banner">
<div className="offline-banner hidden">
Sie sind offline. Manche Inhalte könnten nicht aktuell sein.
<button type="button" className="reload">
Neu laden
</button>
</div>
<div
className="l-section main-header navigation-bar"
data-js="main-nav-bar"
>
<div className="navigation-bar__top navigation-bar__top--ghostwhite-light">
<div className="l-section__inner">
<a
href="https://www.scandichotels.de"
className="scandic-main-page-link"
>
Back to scandichotels.de
</a>
<ul className="nav-secondary navbar-login">
<li className="nav-secondary__item hidden-xxsmall hidden-xsmall hidden-small">
{links ? (
<Desktop currentLanguage={currentLanguage} links={links} />
) : null}
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.de/hotelreservation/get-booking"
>
Buchung ansehen/ändern
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.de/karriere-bei-scandic-hotels"
>
Karriere bei Scandic
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.de/geschaftsreisen"
>
Firmenkunden
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.de/scandic-friends"
>
Über Scandic Friends
</a>
</li>
</ul>
</div>
</div>
<div className="navigation-bar__main">
<div
className="l-section__inner l-section__inner--small-no-padding"
itemScope={undefined}
itemType="http://schema.org/Organization"
>
<meta itemProp="name" content="Scandic" />
<button
type="button"
className="navigation-bar__main__expander"
data-js="main-nav-toggler"
data-target="#main-menu"
aria-pressed="false"
>
<span className="icon-bars"></span>
<span className="hidden--accessible">Menu</span>
</button>
<a
id="scandic-logo"
className="navigation-bar__main__logo hidden-medium "
href="https://www.scandichotels.de"
itemProp="url"
>
<span className="hidden--accessible">
Back to scandichotels.de
</span>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
itemProp="logo"
/>
</a>
<ul
id="navbar-header-design"
className="hidden hidden-medium hidden-large nav-primary__header"
>
<li className="nav-primary__scandicfriendslogo">
<img
src="/Static/img/icons/scandic-friends/icon-scandic-friends.svg"
width="35"
height="35"
/>
</li>
</ul>
<nav>
<ul
id="main-menu"
className="nav-primary is-collapsed"
data-collapsable="main-menu"
>
<li
className="nav-primary__item nav-primary__item--primary hidden-large hidden-small hidden-xsmall hidden-xxsmall"
aria-hidden="true"
>
<a
className="navigation-bar__main__logo"
href="https://www.scandichotels.de"
>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
/>
<span className="hidden--accessible">
Back to scandichotels.de
</span>
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.de/hotelsuche">
Hotels & Reiseziele
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.de/reisemagazin">
Reisemagazin
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.de/tagungen-und-events">
Tagungen & Events
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.de/scandic-entdecken">
Scandic entdecken
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.de/angebote-arrangements">
Angebote
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.de/scandic-friends">
Über Scandic Friends
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.de/geschaftsreisen">
Geschäftsreisen
</a>
</li>
<li className="nav-primary__item hidden-medium hidden-large">
{links ? (
<Mobile currentLanguage={currentLanguage} links={links} />
) : null}
</li>
</ul>
</nav>
</div>
</div>
</div>
</header>
)
}

View File

@@ -1,196 +0,0 @@
/* eslint-disable @next/next/no-img-element */
import {
currentAboutLinks,
currentSponsoringLinks,
currentWifiLinks,
} from "@/constants/current/links"
import Desktop from "./LanguageSwitcher/Desktop"
import Mobile from "./LanguageSwitcher/Mobile"
import type { LanguageSwitcherLink } from "@/types/components/current/languageSwitcher"
import type { HeaderProps } from "@/types/components/current/header"
const paths: Record<string, LanguageSwitcherLink[]> = {
"/customer-service/frequently-asked-questions/using-the-website":
currentAboutLinks,
"/explore-scandic/wifi": currentWifiLinks,
"/sponsoring": currentSponsoringLinks,
}
const currentLanguage = "English"
export default function EnHeader({ pathname }: HeaderProps) {
const links = paths?.[pathname] ?? null
return (
<header className="header" role="banner">
<div className="offline-banner hidden">
You are offline, some content may be out of date.
<button type="button" className="reload">
Reload
</button>
</div>
<div
className="l-section main-header navigation-bar"
data-js="main-nav-bar"
>
<div className="navigation-bar__top navigation-bar__top--ghostwhite-light">
<div className="l-section__inner">
<a
href="https://www.scandichotels.com"
className="scandic-main-page-link"
>
Back to scandichotels.com
</a>
<ul className="nav-secondary navbar-login">
<li className="nav-secondary__item hidden-xxsmall hidden-xsmall hidden-small">
{links ? (
<Desktop currentLanguage={currentLanguage} links={links} />
) : null}
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="/hotelreservation/get-booking"
>
View/Cancel booking
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a className="nav-secondary__item__link" href="/work-with-us">
Work with us
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="/corporate-travel"
>
Corporate Travel
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="/scandic-friends"
>
About Scandic Friends
</a>
</li>
</ul>
</div>
</div>
<div className="navigation-bar__main">
<div
className="l-section__inner l-section__inner--small-no-padding"
itemScope
itemType="http://schema.org/Organization"
>
<meta itemProp="name" content="Scandic" />
<button
type="button"
className="navigation-bar__main__expander"
data-js="main-nav-toggler"
data-target="#main-menu"
aria-pressed="false"
>
<span className="icon-bars"></span>
<span className="hidden--accessible">Menu</span>
</button>
<a
id="scandic-logo"
className="navigation-bar__main__logo hidden-medium "
href="https://www.scandichotels.com"
itemProp="url"
>
<span className="hidden--accessible">
Back to scandichotels.com
</span>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
itemProp="logo"
/>
</a>
<nav>
<ul
id="main-menu"
className="nav-primary is-collapsed"
data-collapsable="main-menu"
>
<li
className="nav-primary__item nav-primary__item--primary hidden-large hidden-small hidden-xsmall hidden-xxsmall"
aria-hidden="true"
>
<a
className="navigation-bar__main__logo"
href="https://www.scandichotels.com"
>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
/>
<span className="hidden--accessible">
Back to scandichotels.com
</span>
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.com/hotels">
Hotels &amp; Destinations
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.com/travel-guides">
Travel guides
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.com/conferences-meetings">
Conferences &amp; Meetings
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.com/explore-scandic">
Explore Scandic
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.com/weekend-packages-and-offers">
Offers
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.com/scandic-friends">
About Scandic Friends
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.com/corporate-travel">
Corporate Travel
</a>
</li>
<li className="nav-primary__item hidden-medium hidden-large">
{links ? (
<Mobile currentLanguage={currentLanguage} links={links} />
) : null}
</li>
</ul>
</nav>
</div>
</div>
</div>
</header>
)
}

View File

@@ -1,198 +0,0 @@
/* eslint-disable @next/next/no-img-element */
import {
currentAboutLinks,
currentSponsoringLinks,
currentWifiLinks,
} from "@/constants/current/links"
import Desktop from "./LanguageSwitcher/Desktop"
import Mobile from "./LanguageSwitcher/Mobile"
import type { LanguageSwitcherLink } from "@/types/components/current/languageSwitcher"
import type { HeaderProps } from "@/types/components/current/header"
const paths: Record<string, LanguageSwitcherLink[]> = {
"/asiakaspalvelu/usein-kysytyt-kysymykset/tietoja-internetsivuista":
currentAboutLinks,
"/koe-scandic/maksuton-internetyhteys": currentSponsoringLinks,
"/scandic-entdecken/wlan": currentWifiLinks,
}
const currentLanguage = "Suomi"
export default function FiHeader({ pathname }: HeaderProps) {
const links = paths?.[pathname] ?? null
return (
<header className="header" role="banner">
<div className="offline-banner hidden">
Ei yhteyttä verkkoon. Osa sisällöstä saattaa olla vanhentunut.
<button type="button" className="reload">
Lataa uudelleen
</button>
</div>
<div
className="l-section main-header navigation-bar"
data-js="main-nav-bar"
>
<div className="navigation-bar__top navigation-bar__top--ghostwhite-light">
<div className="l-section__inner">
<a
href="https://www.scandichotels.fi"
className="scandic-main-page-link"
>
Back to scandichotels.fi
</a>
<ul className="nav-secondary navbar-login">
<li className="nav-secondary__item hidden-xxsmall hidden-xsmall hidden-small">
{links ? (
<Desktop currentLanguage={currentLanguage} links={links} />
) : null}
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.fi/varaa-hotelli/hae-varaus"
>
Varaukseni
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.fi/tyo/avoimet-tyopaikat"
>
Työpaikat
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.fi/yrityksille"
>
Yrityksille
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.fi/scandic-friends"
>
Scandic Friends
</a>
</li>
</ul>
</div>
</div>
<div className="navigation-bar__main">
<div
className="l-section__inner l-section__inner--small-no-padding"
itemScope
itemType="http://schema.org/Organization"
>
<meta itemProp="name" content="Scandic" />
<button
type="button"
className="navigation-bar__main__expander"
data-js="main-nav-toggler"
data-target="#main-menu"
aria-pressed="false"
>
<span className="icon-bars"></span>
<span className="hidden--accessible">Menu</span>
</button>
<a
id="scandic-logo"
className="navigation-bar__main__logo hidden-medium "
href="https://www.scandichotels.fi"
itemProp="url"
>
<span className="hidden--accessible">
Back to scandichotels.fi
</span>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
itemProp="logo"
/>
</a>
<nav>
<ul
id="main-menu"
className="nav-primary is-collapsed"
data-collapsable="main-menu"
>
<li
className="nav-primary__item nav-primary__item--primary hidden-large hidden-small hidden-xsmall hidden-xxsmall"
aria-hidden="true"
>
<a
className="navigation-bar__main__logo"
href="https://www.scandichotels.fi"
>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
/>
<span className="hidden--accessible">
Back to scandichotels.fi
</span>
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.fi/hotellit">
Hotellit ja kohteet
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.fi/matkavinkit">
Matkavinkit
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.fi/kokoukset-ja-juhlatilat">
Kokoukset ja juhlatilat
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.fi/koe-scandic">
Koe Scandic
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.fi/tarjoukset">
Tarjoukset
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.fi/scandic-friends">
Scandic Friends
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.fi/yrityksille">
Yrityksille
</a>
</li>
<li className="nav-primary__item hidden-medium hidden-large">
{links ? (
<Mobile currentLanguage={currentLanguage} links={links} />
) : null}
</li>
</ul>
</nav>
</div>
</div>
</div>
</header>
)
}

View File

@@ -1,37 +0,0 @@
import type { LanguageSwitcherProps } from "@/types/components/current/languageSwitcher"
export default function Desktop({
currentLanguage,
links,
}: LanguageSwitcherProps) {
return (
<div className="dropdown-container navbar-language-selector ">
<button
className="navbar-language-selector__toggler"
data-js="dropdown-toggler"
aria-pressed="false"
>
<svg
focusable="false"
className="icon icon--xs icon--white"
viewBox="0 0 32 32"
>
<use xlinkHref="/Static/img/icons/sprites.svg#icon-globe"></use>
</svg>
{currentLanguage}
<span className="hidden--accessible">Choose language</span>
<span className="caret"></span>
</button>
<ul className="dropdown-menu">
{links.map((link) => (
<li
className={currentLanguage === link.title ? "active" : undefined}
key={link.href}
>
<a href={link.href}>{link.title}</a>
</li>
))}
</ul>
</div>
)
}

View File

@@ -0,0 +1,45 @@
.dropdown {
background-clip: padding-box;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, .15);
border-radius: 4px;
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
display: none;
float: left;
font-size: 1rem;
left: 0;
list-style: none;
margin: 2px 0 0;
min-width: 160px;
padding: 5px 0;
position: absolute;
text-align: left;
top: 100%;
z-index: 11;
}
.dropdown.isOpen {
display: block;
}
.link {
clear: both;
color: grey;
display: block;
font-weight: 400;
padding: 3px 20px;
white-space: nowrap;
}
.link:hover {
background-color: #f5f5f5;
color: #737373;
text-decoration: none;
}
.active>.link {
background-color: #00838e;
color: #fff;
outline: 0;
text-decoration: none;
}

View File

@@ -0,0 +1,83 @@
"use client"
import { useCallback, useEffect, useRef, useState } from "react"
import { languages } from "@/constants/languages"
import styles from "./desktop.module.css"
import type { LanguageSwitcherProps } from "@/types/components/current/languageSwitcher"
export default function Desktop({
currentLanguage,
urls,
}: LanguageSwitcherProps) {
const [isOpen, setIsOpen] = useState(false)
const divRef = useRef<HTMLDivElement>(null)
function toggleOpen() {
setIsOpen(prevIsOpen => !prevIsOpen)
}
const close = useCallback(() => {
setIsOpen(false)
}, [setIsOpen])
useEffect(() => {
function handleClickOutside(evt: Event) {
const target = evt.target as HTMLElement
if (divRef.current && target && !divRef.current.contains(target)) {
close()
}
}
if (divRef.current) {
document.addEventListener('click', handleClickOutside, false)
}
return () => {
document.removeEventListener('click', handleClickOutside, false)
}
}, [close])
return (
<div className="dropdown-container navbar-language-selector " ref={divRef}>
<button
aria-pressed="false"
className="navbar-language-selector__toggler"
data-js="dropdown-toggler"
onClick={toggleOpen}
>
<svg
focusable="false"
className="icon icon--xs icon--white"
viewBox="0 0 32 32"
>
<use xlinkHref="/Static/img/icons/sprites.svg#icon-globe"></use>
</svg>
{currentLanguage}
<span className="hidden--accessible">Choose language</span>
<span className="caret"></span>
</button>
<ul className={`${styles.dropdown} ${isOpen ? styles.isOpen : ""}`}>
<li className={currentLanguage === languages.en ? styles.active : undefined}>
<a className={styles.link} href={urls.en?.url}>{languages.en}</a>
</li>
<li className={currentLanguage === languages.sv ? styles.active : undefined}>
<a className={styles.link} href={urls.sv?.url}>{languages.sv}</a>
</li>
<li className={currentLanguage === languages.no ? styles.active : undefined}>
<a className={styles.link} href={urls.no?.url}>{languages.no}</a>
</li>
{/* When we have 6 languages in Contenstack, danish url should come from urls.da?.url */}
<li className={currentLanguage === languages.da ? styles.active : undefined}>
<a className={styles.link} href="https://www.scandichotels.dk/">{languages.da}</a>
</li>
<li className={currentLanguage === languages.fi ? styles.active : undefined}>
<a className={styles.link} href={urls.fi?.url}>{languages.fi}</a>
</li>
<li className={currentLanguage === languages.de ? styles.active : undefined}>
<a className={styles.link} href={urls.de?.url}>{languages.de}</a>
</li>
</ul>
</div>
)
}

View File

@@ -1,31 +0,0 @@
import type { LanguageSwitcherProps } from "@/types/components/current/languageSwitcher"
export default function Mobile({
currentLanguage,
links,
}: LanguageSwitcherProps) {
return (
<div className="navbar-language-selector">
<button
className="navbar-language-selector__toggler "
data-js="collapsible-toggler"
data-target="language-menu"
aria-pressed="false"
>
{currentLanguage}{" "}
<span className="navbar-language-selector__toggler__arrow"></span>
<span className="hidden--accessible">Choose language</span>
</button>
<ul className="is-collapsed" data-collapsable="language-menu">
{links.map((link) => (
<li
className={`navbar-language-selector__item ${currentLanguage === link.title ? "is-active" : ""}`}
key={link.href}
>
<a href={link.href}>{link.title}</a>
</li>
))}
</ul>
</div>
)
}

View File

@@ -0,0 +1,56 @@
"use client"
import { useState } from "react"
import { languages } from "@/constants/languages"
import styles from "./mobile.module.css"
import type { LanguageSwitcherProps } from "@/types/components/current/languageSwitcher"
export default function Mobile({
currentLanguage,
urls,
}: LanguageSwitcherProps) {
const [isOpen, setIsOpen] = useState(false)
function toggleOpen() {
setIsOpen(prevIsOpen => !prevIsOpen)
}
return (
<div className="navbar-language-selector">
<button
aria-pressed="false"
className="navbar-language-selector__toggler "
data-js="collapsible-toggler"
data-target="language-menu"
onClick={toggleOpen}
>
{currentLanguage}{" "}
<span className={`${styles.arrow} ${isOpen ? styles.open : ""}`}></span>
<span className="hidden--accessible">Choose language</span>
</button>
<ul className={`${styles.dropdown} ${isOpen ? styles.isOpen : ""}`} data-collapsable="language-menu">
<li className={`navbar-language-selector__item ${currentLanguage === languages.en ? "is-active" : ""}`}>
<a href={urls.en?.url}>{languages.en}</a>
</li>
<li className={`navbar-language-selector__item ${currentLanguage === languages.sv ? "is-active" : ""}`}>
<a href={urls.sv?.url}>{languages.sv}</a>
</li>
<li className={`navbar-language-selector__item ${currentLanguage === languages.no ? "is-active" : ""}`}>
<a href={urls.no?.url}>{languages.no}</a>
</li>
{/* When we have 6 languages in Contenstack, danish url should come from urls.da?.url */}
<li className={`navbar-language-selector__item ${currentLanguage === languages.da ? "is-active" : ""}`}>
<a href="https://www.scandichotels.dk/">{languages.da}</a>
</li>
<li className={`navbar-language-selector__item ${currentLanguage === languages.fi ? "is-active" : ""}`}>
<a href={urls.fi?.url}>{languages.fi}</a>
</li>
<li className={`navbar-language-selector__item ${currentLanguage === languages.de ? "is-active" : ""}`}>
<a href={urls.de?.url}>{languages.de}</a>
</li>
</ul>
</div>
)
}

View File

@@ -0,0 +1,31 @@
.dropdown {
display: none;
}
.dropdown.isOpen {
display: block;
}
.arrow {
background-image: url("/Static/img/icons/arrows/arrow-down-grey.png");
background-position: 50%;
background-repeat: no-repeat;
display: inline-block;
margin-left: 5px;
padding: 5px 10px;
}
.arrow.open {
background-image: url("/Static/img/icons/arrows/arrow-up-grey.png");
}
.link {
color: grey;
display: block;
text-transform: capitalize;
}
.link:hover {
color: #7f7369;
text-decoration: none;
}

View File

@@ -0,0 +1,107 @@
"use client"
import { useState } from "react"
import Image from "@/components/Image"
import Mobile from "../LanguageSwitcher/Mobile"
import styles from "./mainMenu.module.css"
import type { MainMenuProps } from "@/types/components/current/header/mainMenu"
export default function MainMenu({
currentLanguage,
frontpageLinkText,
homeHref,
links,
logo,
topMenuMobileLinks,
urls,
}: MainMenuProps) {
const [isOpen, setIsOpen] = useState(false)
function toogleIsOpen() {
setIsOpen(prevIsOpen => !prevIsOpen)
}
return (
<div className={styles.mainMenu}>
<div
className={styles.container}
itemScope
itemType="http://schema.org/Organization"
>
<meta itemProp="name" content="Scandic" />
<button
aria-pressed="false"
className={`${styles.expanderBtn} ${isOpen ? styles.expanded : ""}`}
data-js="main-nav-toggler"
data-target="#main-menu"
onClick={toogleIsOpen}
type="button"
>
<span className={styles.iconBars}></span>
<span className={styles.hiddenAccessible}>Menu</span>
</button>
<a
className={styles.logoLink}
href={homeHref}
id="scandic-logo"
itemProp="url"
>
<span className={styles.hiddenAccessible}>
{frontpageLinkText}
</span>
<Image
alt="Scandic Hotels logo"
className={styles.logo}
data-js="scandiclogoimg"
data-nosvgsrc="/Static/img/scandic-logotype.png"
itemProp="logo"
height={22}
src={logo.url}
width={logo.dimension.width}
/>
</a>
<nav>
<ul
className={`${styles.list} ${isOpen ? styles.isOpen : ""}`}
data-collapsable="main-menu"
id="main-menu"
>
{links.map(link => (
<li
className={styles.li}
key={link.href}
>
<a
className={styles.link}
href={link.href}
>
{link.title}
</a>
</li>
))}
<ul className={styles.mobileList}>
{topMenuMobileLinks.map(({ link }) => (
<li className={styles.mobileLi} key={link.href}>
<a className={styles.mobileLink} href={link.href}>
{link.title}
</a>
</li>
))}
</ul>
{urls ? (
<li className={styles.mobileLi}>
<Mobile currentLanguage={currentLanguage} urls={urls} />
</li>
) : null}
</ul>
</nav>
</div>
</div>
)
}

View File

@@ -0,0 +1,225 @@
.mainMenu {
background-color: #fff;
background-image: none;
box-shadow: 0 0 7px rgba(0, 0, 0, .75);
max-height: 100%;
overflow: visible;
position: fixed;
top: 0;
width: 100%;
z-index: 99999;
}
.container {
box-sizing: content-box;
display: grid;
/** Third column is Book button */
grid-template-columns: repeat(3, 1fr);
height: 100%;
margin: 0 auto;
max-width: 1200px;
padding: 0;
}
.expanderBtn {
background-color: transparent;
border: none;
cursor: pointer;
justify-self: flex-start;
left: 0;
padding: 0.75rem 0.5rem 1rem;
transition: .3s;
user-select: none;
}
.iconBars,
.iconBars::after,
.iconBars::before {
background: #757575;
border-radius: 0.1428571429rem;
display: inline-block;
height: 0.2857142857rem;
position: relative;
transition: .3s;
width: 2rem;
}
.iconBars::after,
.iconBars::before {
content: "";
left: 0;
position: absolute;
transform-origin: 0.1428571429rem center;
}
.iconBars::after {
top: -0.5rem;
}
.iconBars::before {
top: 0.5rem;
}
.expanded .iconBars {
background: transparent;
}
.expanded .iconBars::after,
.expanded .iconBars::before {
top: 0;
transform-origin: 50% 50%;
width: 2rem;
}
.expanded .iconBars::after {
transform: rotate(-45deg);
}
.expanded .iconBars::before {
transform: rotate(45deg);
}
.hiddenAccessible {
display: block;
height: 1px;
left: -100000em;
overflow: hidden;
position: absolute;
top: auto;
width: 1px;
}
.logoLink {
align-items: center;
display: flex;
justify-self: center;
}
.list {
background-color: #fff;
border-top: 1px solid #e3e0db;
display: none;
list-style: none;
overflow-y: visible;
padding-bottom: 20px;
}
.list.isOpen {
display: block;
left: 0;
position: absolute;
right: 0;
top: 100%;
}
.li {
border-bottom: none;
display: block;
line-height: 17px;
position: relative;
text-align: center;
}
.link {
color: #000;
display: block;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: .875rem;
font-weight: 700;
padding-bottom: 20px;
padding-top: 20px;
text-transform: uppercase;
}
.link:hover {
color: #7f7369;
text-decoration: none;
}
.mobileList {
padding-top: 6px;
}
.mobileLi {
display: block;
position: relative;
text-align: center;
}
.mobileLink {
color: #000;
display: block;
font-family: Helvetica;
font-size: .875rem;
padding: 5px 0;
}
@media screen and (max-width: 950px) {
.logoLink {
width: 5rem;
}
.li {
background-color: #f3f2f1;
}
}
@media screen and (min-width: 950px) {
.mainMenu {
background-color: hsla(0, 0%, 100%, .95);
position: relative;
z-index: unset;
}
.container {
gap: 30px;
grid-template-columns: minmax(100px, auto) 1fr;
padding: 0px 30px;
}
.expanderBtn {
display: none;
}
.logo {
max-width: none;
min-width: 98px;
width: auto;
}
.list {
border-top: none;
display: block;
padding-bottom: 0;
padding-top: 0;
position: static;
width: 100%;
}
.list.isOpen {
position: static;
}
.li {
display: table-cell;
float: none;
vertical-align: middle;
}
.link {
background-image: none;
font-family: Helvetica, Arial, sans-serif;
font-weight: 700;
line-height: 1.15;
padding: 30px 15px;
}
.mobileList {
display: none;
padding-top: 0px;
}
.mobileLi {
display: none;
}
}

View File

@@ -1,203 +0,0 @@
import {
currentAboutLinks,
currentSponsoringLinks,
currentWifiLinks,
} from "@/constants/current/links"
import Desktop from "./LanguageSwitcher/Desktop"
import Mobile from "./LanguageSwitcher/Mobile"
import type { LanguageSwitcherLink } from "@/types/components/current/languageSwitcher"
import type { HeaderProps } from "@/types/components/current/header"
const paths: Record<string, LanguageSwitcherLink[]> = {
"/kundeservice/sporsmal-og-svar/bruk-av-nettsiden": currentAboutLinks,
"/utforsk-scandic/wifi": currentWifiLinks,
"/vi-sponser": currentSponsoringLinks,
}
const currentLanguage = "Norsk"
export default function NoHeader({ pathname }: HeaderProps) {
const links = paths?.[pathname] ?? null
return (
<header className="header" role="banner">
<div className="offline-banner hidden">
Du er offline. Noe innhold kan være utdatert.
<button type="button" className="reload">
Last inn nytt
</button>
</div>
<div
className="l-section main-header navigation-bar"
data-js="main-nav-bar"
>
<div className="navigation-bar__top navigation-bar__top--ghostwhite-light">
<div className="l-section__inner">
<a
href="https://www.scandichotels.no"
className="scandic-main-page-link"
>
Tilbake til scandichotels.no
</a>
<ul className="nav-secondary navbar-login">
<li className="nav-secondary__item hidden-xxsmall hidden-xsmall hidden-small">
{links ? (
<Desktop currentLanguage={currentLanguage} links={links} />
) : null}
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.no/hotelreservation/get-booking"
>
Vis/Avbestill din booking
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.no/jobb-hos-oss/ledige-stillinger"
>
Jobb hos oss
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.no/for-bedrifter"
>
For bedrifter
</a>
</li>
<li className=" hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.no/scandic-friends"
>
Om Scandic Friends
</a>
</li>
</ul>
</div>
</div>
<div className="navigation-bar__main">
<div
className="l-section__inner l-section__inner--small-no-padding"
itemScope={undefined}
itemType="http://schema.org/Organization"
>
<meta itemProp="name" content="Scandic" />
<button
type="button"
className="navigation-bar__main__expander"
data-js="main-nav-toggler"
data-target="#main-menu"
aria-pressed="false"
>
<span className="icon-bars"></span>
<span className="hidden--accessible">Menu</span>
</button>
<a
id="scandic-logo"
className="navigation-bar__main__logo hidden-medium "
href="https://www.scandichotels.no"
itemProp="url"
>
<span className="hidden--accessible">
Tilbake til scandichotels.no
</span>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
itemProp="logo"
/>
</a>
<ul
id="navbar-header-design"
className="hidden hidden-medium hidden-large nav-primary__header"
>
<li className="nav-primary__scandicfriendslogo">
<img
src="/Static/img/icons/scandic-friends/icon-scandic-friends.svg"
width="35"
height="35"
/>
</li>
</ul>
<nav>
<ul
id="main-menu"
className="nav-primary is-collapsed"
data-collapsable="main-menu"
>
<li
className="nav-primary__item nav-primary__item--primary hidden-large hidden-small hidden-xsmall hidden-xxsmall"
aria-hidden="true"
>
<a
className="navigation-bar__main__logo"
href="https://www.scandichotels.no"
>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
/>
<span className="hidden--accessible">
Tilbake til scandichotels.no
</span>
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.no/hotell">
Hoteller og destinasjoner
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.no/reiseguider">
Reiseguider
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.no/moter-og-konferanser">
Møter og konferanser
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.no/utforsk-scandic">
Utforsk Scandic
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.no/hotelltilbud">Tilbud</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.no/scandic-friends">
Om Scandic Friends
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.no/for-bedrifter">
For bedrifter
</a>
</li>
<li className="nav-primary__item hidden-medium hidden-large">
{links ? (
<Mobile currentLanguage={currentLanguage} links={links} />
) : null}
</li>
</ul>
</nav>
</div>
</div>
</div>
</header>
)
}

View File

@@ -0,0 +1,23 @@
.banner {
align-items: center;
background: #606060;
color: #fff;
display: flex;
justify-content: space-between;
padding: 10px;
position: relative;
z-index: 10;
}
.hidden {
display: none;
}
.reloadBtn {
color: #fff;
background-color: #00838e;
border: 0;
border-radius: 18px;
outline: 0 none;
padding: 5px 15px;
}

View File

@@ -0,0 +1,10 @@
import styles from "./banner.module.css"
export default function OfflineBanner() {
return (
<div className={`${styles.banner} ${styles.hidden}`}>
You are offline, some content may be out of date.
<button className={styles.reloadBtn} type="button">Reload</button>
</div>
)
}

View File

@@ -1,198 +0,0 @@
/* eslint-disable @next/next/no-img-element */
import {
currentAboutLinks,
currentSponsoringLinks,
currentWifiLinks,
} from "@/constants/current/links"
import Desktop from "./LanguageSwitcher/Desktop"
import Mobile from "./LanguageSwitcher/Mobile"
import type { LanguageSwitcherLink } from "@/types/components/current/languageSwitcher"
import type { HeaderProps } from "@/types/components/current/header"
const paths: Record<string, LanguageSwitcherLink[]> = {
"/kundservice/fragor-och-svar/om-scandics-webbplats": currentAboutLinks,
"/utforska-scandic/wi-fi": currentWifiLinks,
"/vi-sponsrar": currentSponsoringLinks,
}
const currentLanguage = "Svenska"
export default function SvHeader({ pathname }: HeaderProps) {
const links = paths?.[pathname] ?? null
return (
<header className="header" role="banner">
<div className="offline-banner hidden">
Du är offline. Sidan kan visa gammalt innehåll.
<button type="button" className="reload">
Ladda om
</button>
</div>
<div
className="l-section main-header navigation-bar"
data-js="main-nav-bar"
>
<div className="navigation-bar__top navigation-bar__top--ghostwhite-light">
<div className="l-section__inner">
<a
href="https://www.scandichotels.se"
className="scandic-main-page-link"
>
Tillbaka till scandichotels.se
</a>
<ul className="nav-secondary navbar-login">
<li className="nav-secondary__item hidden-xxsmall hidden-xsmall hidden-small">
{links ? (
<Desktop currentLanguage={currentLanguage} links={links} />
) : null}
</li>
<li className="hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.se/hotelreservation/hitta-bokning"
>
Visa bokning / Avboka
</a>
</li>
<li className="hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.se/jobba-hos-oss/lediga-tjanster"
>
Jobba hos oss
</a>
</li>
<li className="hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.se/for-foretag"
>
Företag
</a>
</li>
<li className="hidden-xxsmall hidden-xsmall hidden-small nav-secondary__item">
<a
className="nav-secondary__item__link"
href="https://www.scandichotels.se/scandic-friends"
>
Om Scandic Friends
</a>
</li>
</ul>
</div>
</div>
<div className="navigation-bar__main">
<div
className="l-section__inner l-section__inner--small-no-padding"
itemScope
itemType="http://schema.org/Organization"
>
<meta itemProp="name" content="Scandic" />
<button
type="button"
className="navigation-bar__main__expander"
data-js="main-nav-toggler"
data-target="#main-menu"
aria-pressed="false"
>
<span className="icon-bars"></span>
<span className="hidden--accessible">Menu</span>
</button>
<a
id="scandic-logo"
className="navigation-bar__main__logo hidden-medium "
href="https://www.scandichotels.se"
itemProp="url"
>
<span className="hidden--accessible">
Tillbaka till scandichotels.se
</span>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
itemProp="logo"
/>
</a>
<nav>
<ul
id="main-menu"
className="nav-primary is-collapsed"
data-collapsable="main-menu"
>
<li
className="nav-primary__item nav-primary__item--primary hidden-large hidden-small hidden-xsmall hidden-xxsmall"
aria-hidden="true"
>
<a
className="navigation-bar__main__logo"
href="https://www.scandichotels.se"
>
<img
src="/Static/img/scandic-logotype.svg"
data-js="scandiclogoimg"
alt="Scandic Hotels logo"
height="22"
data-nosvgsrc="/Static/img/scandic-logotype.png"
/>
<span className="hidden--accessible">
Tillbaka till scandichotels.se
</span>
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.se/hotell">
Hotell & destinationer
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.se/reseguide">
Reseguider
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.se/konferens-mote">
Konferens & möten
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.se/utforska-scandic">
Utforska Scandic
</a>
</li>
<li className="nav-primary__item nav-primary__item--primary ">
<a href="https://www.scandichotels.se/erbjudanden-och-weekendpaket">
Erbjudanden
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.se/scandic-friends">
Om Scandic Friends
</a>
</li>
<li className="nav-primary__item nav-primary__item--secondary hidden-medium hidden-large ">
<a href="https://www.scandichotels.se/for-foretag">
För företag
</a>
</li>
<li className="nav-primary__item hidden-medium hidden-large">
{links ? (
<Mobile currentLanguage={currentLanguage} links={links} />
) : null}
</li>
</ul>
</nav>
</div>
</div>
</div>
</header>
)
}

View File

@@ -0,0 +1,39 @@
import Desktop from "../LanguageSwitcher/Desktop"
import styles from "./topMenu.module.css"
import type { TopMenuProps } from "@/types/components/current/header/topMenu"
export default function TopMenu({ currentLanguage, frontpageLinkText, homeHref, links, urls }: TopMenuProps) {
return (
<div className={styles.topMenu}>
<div className={styles.container}>
<a
className={styles.homeLink}
href={homeHref}
>
{frontpageLinkText}
</a>
<ul className={styles.list}>
{urls ? (
<li className="nav-secondary__item hidden-xxsmall hidden-xsmall hidden-small">
<Desktop currentLanguage={currentLanguage} urls={urls} />
</li>
) : null}
{links.map(({ link }) => (
<li key={link.href}>
<a
className={styles.link}
href={link.href}
>
{link.title}
</a>
</li>
))}
</ul>
</div>
</div>
)
}

View File

@@ -0,0 +1,51 @@
.topMenu {
background-color: #8d3a7c;
color: #fff;
display: none;
font-size: .8125rem;
position: relative;
z-index: 1;
}
.container {
box-sizing: content-box;
display: flex;
justify-content: flex-end;
margin: 0 auto;
max-width: 1200px;
padding: 0 10px;
}
.homeLink {
display: none;
}
.list {
display: flex;
list-style: none;
}
.link {
color: #fff;
display: inline-block;
padding: 3px 10px;
text-decoration: none;
}
@media screen and (min-width: 740px) {
.container {
padding: 0 30px;
}
}
@media screen and (min-width: 950px) {
.topMenu {
background-color: #3d3835;
display: block;
}
.link {
padding-top: 4px;
padding-bottom: 4px;
}
}

View File

@@ -0,0 +1,9 @@
.header {
display: grid;
}
@media screen and (max-width: 950px) {
.header {
height: 50px;
}
}

View File

@@ -1,30 +1,77 @@
import { langEnum } from "@/types/lang"
import { languages } from "@/constants/languages"
import { batchRequest } from "@/lib/batchRequest"
import { request } from "@/lib/request"
import { GetHeader } from "@/lib/graphql/Query/Header.graphql"
import { GetDaDeEnUrls, GetFiNoSvUrls } from "@/lib/graphql/Query/LanguageSwitcher.graphql"
import { homeHrefs } from "@/constants/homeHrefs"
import { env } from "@/env/server"
import Da from "./Da"
import De from "./De"
import En from "./En"
import Fi from "./Fi"
import No from "./No"
import Sv from "./Sv"
import MainMenu from "./MainMenu"
import OfflineBanner from "./OfflineBanner"
import TopMenu from "./TopMenu"
import styles from "./header.module.css"
import type { HeaderProps } from "@/types/components/current/header"
import { LangParams } from "@/types/params"
import type { HeaderQueryData } from "@/types/requests/header"
import type { HeaderProps } from "@/types/components/current/header"
import type { LanguageSwitcherQueryData } from "@/types/requests/languageSwitcher"
export default function Header({ lang, pathname }: LangParams & HeaderProps) {
switch (lang) {
case langEnum.sv:
return <Sv pathname={pathname} />
case langEnum.fi:
return <Fi pathname={pathname} />
case langEnum.en:
return <En pathname={pathname} />
case langEnum.da:
return <Da pathname={pathname} />
case langEnum.de:
return <De pathname={pathname} />
case langEnum.no:
return <No pathname={pathname} />
default:
export default async function Header({ lang, uid }: LangParams & HeaderProps) {
try {
const variables = {
locale: lang,
uid,
}
const { data } = await request<HeaderQueryData>(GetHeader, { locale: lang }, { tags: [`header-${lang}`] })
const { data: urls } = await batchRequest<LanguageSwitcherQueryData>([
{
document: GetDaDeEnUrls,
tags: [`DA-DE-EN-${uid}`],
variables,
},
{
document: GetFiNoSvUrls,
tags: [`FI-NO-SV-${uid}`],
variables,
},
])
if (!data.all_header.items.length) {
return null
}
const currentLanguage = languages[lang]
const homeHref = homeHrefs[env.NODE_ENV][lang]
const { frontpage_link_text, logoConnection, menu, top_menu } = data.all_header.items[0]
const logo = logoConnection.edges?.[0]?.node
const topMenuMobileLinks = top_menu.links.filter(link => link.show_on_mobile)
.sort((a, b) => a.sort_order_mobile < b.sort_order_mobile ? 1 : -1)
return (
<header className={styles.header} role="banner">
<OfflineBanner />
<TopMenu
currentLanguage={currentLanguage}
frontpageLinkText={frontpage_link_text}
homeHref={homeHref}
links={top_menu.links}
urls={urls}
/>
<MainMenu
currentLanguage={currentLanguage}
frontpageLinkText={frontpage_link_text}
homeHref={homeHref}
links={menu.links}
logo={logo}
topMenuMobileLinks={topMenuMobileLinks}
urls={urls}
/>
</header>
)
} catch (error) {
console.error(error)
return null
}
}

View File

@@ -1,5 +1,3 @@
import Link from "next/link"
import styles from "./breadcrumbs.module.css"
import type { BreadcrumbsProps } from "@/types/components/current/breadcrumbs"
@@ -14,20 +12,20 @@ export default function Breadcrumbs({
<ul className={styles.list}>
{parent ? (
<li className={styles.parent}>
<Link href={parent.node.url}>
{parent.node.breadcrumbs?.title ?? parent.node.title}
</Link>
<a href={parent.href}>
{parent.title}
</a>
</li>
) : null}
{breadcrumbs.edges.map((breadcrumb) => (
{breadcrumbs.map((breadcrumb) => (
<li
className={styles.li}
itemProp="breadcrumb"
key={breadcrumb.node.title}
key={breadcrumb.href}
>
<Link className={styles.link} href={breadcrumb.node.url}>
{breadcrumb.node.breadcrumbs?.title ?? breadcrumb.node.title}
</Link>
<a className={styles.link} href={breadcrumb.href}>
{breadcrumb.title}
</a>
</li>
))}
<li className={styles.currentPage}>

View File

@@ -1,5 +1,3 @@
import Link from "next/link"
import type { SubnavMobileProps } from "@/types/components/current/subnavMobile"
export default async function SubnavMobile({
@@ -13,16 +11,16 @@ export default async function SubnavMobile({
<ul className="breadcrumb-list hidden-small hidden-medium hidden-large">
{parent ? (
<li className="breadcrumb-list__parent hidden-medium hidden-large">
<Link href={parent.node.url}>
{parent.node.breadcrumbs?.title ?? parent.node.title}
</Link>
<a href={parent.href}>
{parent.title}
</a>
</li>
) : null}
{breadcrumbs.edges.map((breadcrumb) => (
<li className="breadcrumb-list__body" key={breadcrumb.node.url}>
<Link href={breadcrumb.node.url}>
{breadcrumb.node.breadcrumbs?.title ?? breadcrumb.node.title}
</Link>
{breadcrumbs.map((breadcrumb) => (
<li className="breadcrumb-list__body" key={breadcrumb.href}>
<a href={breadcrumb.href}>
{breadcrumb.title}
</a>
</li>
))}
<li className="breadcrumb-list__body">

View File

@@ -263,8 +263,9 @@ export const renderOptions: RenderOptions = {
}
} else {
const props = extractPossibleAttributes(node.attrs)
const href = node.attrs?.locale ? `/${node.attrs.locale}${node.attrs.href}` : node.attrs.href
return (
<Link {...props} href={node.attrs.href} key={node.uid}>
<Link {...props} href={href} key={node.uid}>
{next(node.children, embeds, fullRenderOptions)}
</Link>
)

View File

@@ -1,80 +0,0 @@
export const currentAboutLinks = [
{
href: "https://www.scandichotels.com/customer-service/frequently-asked-questions/using-the-website",
title: "English",
},
{
href: "https://www.scandichotels.se/kundservice/fragor-och-svar/om-scandics-webbplats",
title: "Svenska",
},
{
href: "https://www.scandichotels.no/kundeservice/sporsmal-og-svar/bruk-av-nettsiden",
title: "Norsk",
},
{
href: "https://www.scandichotels.dk/kundeservice/sporgsmal-og-svar/om-scandics-website",
title: "Dansk",
},
{
href: "https://www.scandichotels.fi/asiakaspalvelu/usein-kysytyt-kysymykset/tietoja-internetsivuista",
title: "Suomi",
},
{
href: "https://www.scandichotels.de/kundenbetreuung/haufig-gestellte-fragen/nutzung-der-internetseite",
title: "Deutsch",
},
]
export const currentSponsoringLinks = [
{
href: "https://www.scandichotels.com/sponsoring",
title: "English",
},
{
href: "https://www.scandichotels.se/vi-sponsrar",
title: "Svenska",
},
{
href: "https://www.scandichotels.no/vi-sponser",
title: "Norsk",
},
{
href: "https://www.scandichotels.dk/sponsorering",
title: "Dansk",
},
{
href: "https://www.scandichotels.fi/sponsorointi",
title: "Suomi",
},
{
href: "https://www.scandichotels.de/sponsoring",
title: "Deutsch",
},
]
export const currentWifiLinks = [
{
href: "https://www.scandichotels.com/explore-scandic/wifi",
title: "English",
},
{
href: "https://www.scandichotels.se/utforska-scandic/wi-fi",
title: "Svenska",
},
{
href: "https://www.scandichotels.no/utforsk-scandic/wifi",
title: "Norsk",
},
{
href: "https://www.scandichotels.dk/oplev-scandic/wifi",
title: "Dansk",
},
{
href: "https://www.scandichotels.fi/koe-scandic/maksuton-internetyhteys",
title: "Suomi",
},
{
href: "https://www.scandichotels.de/scandic-entdecken/wlan",
title: "Deutsch",
},
]

27
constants/homeHrefs.ts Normal file
View File

@@ -0,0 +1,27 @@
export const homeHrefs = {
development: {
da: "https://stage.scandichotels.dk",
de: "https://stage.scandichotels.de",
en: "https://stage.scandichotels.com",
fi: "https://stage.scandichotels.fi",
no: "https://stage.scandichotels.no",
sv: "https://stage.scandichotels.se",
},
production: {
da: "https://www.scandichotels.dk",
de: "https://www.scandichotels.de",
en: "https://www.scandichotels.com",
fi: "https://www.scandichotels.fi",
no: "https://www.scandichotels.no",
sv: "https://www.scandichotels.se",
},
test: {
da: "https://test2.scandichotels.dk",
de: "https://test2.scandichotels.de",
en: "https://test2.scandichotels.com",
fi: "https://test2.scandichotels.fi",
no: "https://test2.scandichotels.no",
sv: "https://test2.scandichotels.se",
},
}

8
constants/languages.ts Normal file
View File

@@ -0,0 +1,8 @@
export const languages = {
da: "Dansk",
de: "Deutsch",
en: "English",
fi: "Suomi",
no: "Norsk",
sv: "Svenska",
}

2
env/server.ts vendored
View File

@@ -11,6 +11,7 @@ export const env = createEnv({
CMS_PREVIEW_TOKEN: z.string(),
NODE_ENV: z.enum(["development", "test", "production"]),
PRINT_QUERY: z.boolean().default(false),
REVALIDATE_SECRET: z.string(),
},
emptyStringAsUndefined: true,
runtimeEnv: {
@@ -22,5 +23,6 @@ export const env = createEnv({
CMS_PREVIEW_TOKEN: process.env.CMS_PREVIEW_TOKEN,
NODE_ENV: process.env.NODE_ENV,
PRINT_QUERY: process.env.PRINT_QUERY,
REVALIDATE_SECRET: process.env.REVALIDATE_SECRET,
},
})

28
lib/batchRequest.ts Normal file
View File

@@ -0,0 +1,28 @@
import "server-only"
import { request } from "./request"
import type { Data } from "@/types/request"
import type { BatchRequestDocument } from "graphql-request"
export async function batchRequest<T>(queries: (BatchRequestDocument & NextFetchRequestConfig)[]): Promise<Data<T>> {
try {
const response = await Promise.allSettled(
queries.map(query => request<T>(query.document, query.variables, { tags: query.tags }))
)
let data = {} as T
const reasons = []
response.forEach(res => {
if (res.status === "fulfilled") {
data = Object.assign({}, data, res.value.data)
} else {
reasons.push(res.reason)
}
})
return { data }
} catch (error) {
console.error(error)
throw new Error("Something went wrong")
}
}

View File

@@ -2,13 +2,6 @@
fragment PuffAside on CurrentBlocksPageAsidePuff {
puff {
puffConnection {
totalCount
edges {
node {
...Puff
}
}
}
...Puff
}
}

View File

@@ -1,5 +1,3 @@
#import "../PageLinks.graphql"
fragment ListItem on CurrentBlocksPageBlocksListBlockListItemsListItem {
list_item {
list_item_style
@@ -19,30 +17,12 @@ fragment ListItemExternalLink on CurrentBlocksPageBlocksListBlockListItemsListIt
}
}
fragment ListItemInternalLink on CurrentBlocksPageBlocksListBlockListItemsListItemInternalLink {
list_item_internal_link {
link_text
list_item_style
subtitle
pageConnection {
totalCount
edges {
node {
__typename
...CurrentBlocksPageLink
}
}
}
}
}
fragment ListBlock on CurrentBlocksPageBlocksList {
list {
list_items {
__typename
...ListItem
...ListItemExternalLink
...ListItemInternalLink
}
title
}

View File

@@ -2,11 +2,9 @@
fragment PuffBlock on CurrentBlocksPageBlocksPuffs {
puffs {
# We have to manually add a limit since Contentstack handles its complexity calculation in a certain way
puffsConnection(limit: 9) {
totalCount
edges {
node {
puffs {
... on CurrentBlocksPageBlocksPuffsBlockPuffsPuff {
puff {
...Puff
}
}

View File

@@ -1,5 +1,4 @@
#import "../Image.graphql"
#import "../PageLinks.graphql"
fragment TextBlock on CurrentBlocksPageBlocksText {
text {
@@ -9,7 +8,6 @@ fragment TextBlock on CurrentBlocksPageBlocksText {
edges {
node {
__typename
...CurrentBlocksPageLink
...Image
}
}

View File

@@ -1,18 +1,9 @@
fragment Breadcrumbs on CurrentBlocksPage {
breadcrumbs {
title
parentsConnection {
edges {
node {
... on CurrentBlocksPage {
breadcrumbs {
title
}
title
url
}
}
}
parents {
href
title
}
title
}
}

View File

@@ -1,9 +1,10 @@
#import "../Image.graphql"
fragment Logo on Footer {
logoConnection {
edges {
node {
title
url
...Image
}
}
}

View File

@@ -1,33 +1,9 @@
#import "../PageLinks.graphql"
fragment Navigation on Footer {
navigation {
links {
... on FooterNavigationLinksInternalLink {
__typename
internal_link {
link_text
pageConnection {
edges {
node {
__typename
...CurrentBlocksPageLink
}
}
}
}
}
... on FooterNavigationLinksExternalLink {
__typename
external_link {
link {
href
title
}
}
}
href
title
}
title
}
}

View File

@@ -1,5 +1,4 @@
#import "./Image.graphql"
#import "./PageLinks.graphql"
fragment Hero on Hero {
imagesConnection {

View File

@@ -1,7 +1,7 @@
fragment CurrentBlocksPageLink on CurrentBlocksPage {
system {
uid
locale
uid
}
title
url

View File

@@ -1,5 +1,4 @@
#import "./Image.graphql"
#import "./PageLinks.graphql"
fragment Preamble on CurrentBlocksPage {
preamble {
@@ -9,7 +8,6 @@ fragment Preamble on CurrentBlocksPage {
edges {
node {
__typename
...CurrentBlocksPageLink
...Image
}
}

View File

@@ -1,5 +1,4 @@
#import "./Image.graphql"
#import "./PageLinks.graphql"
fragment Puff on Puff {
imageConnection {
@@ -9,33 +8,12 @@ fragment Puff on Puff {
}
}
}
is_internal
link {
href
title
}
pageConnection {
edges {
node {
__typename
...CurrentBlocksPageLink
}
}
}
system {
uid
}
text {
json
embedded_itemsConnection {
totalCount
edges {
node {
__typename
...Image
}
}
}
}
title
}

View File

@@ -27,13 +27,13 @@ query GetCurrentBlockPage($locale: String!, $url: String!) {
...Hero
}
...Preamble
title
url
system {
uid
created_at
uid
updated_at
}
title
url
}
total
}

View File

@@ -0,0 +1,32 @@
#import "../Fragments/Image.graphql"
query GetHeader($locale: String!) {
all_header(limit: 1, locale: $locale) {
items {
frontpage_link_text
logoConnection {
edges {
node {
...Image
}
}
}
menu {
links {
href
title
}
}
top_menu {
links {
link {
href
title
}
show_on_mobile
sort_order_mobile
}
}
}
}
}

View File

@@ -0,0 +1,21 @@
query GetDaDeEnUrls($uid: String!) {
de: current_blocks_page(uid: $uid, locale: "de") {
url: original_url
}
en: current_blocks_page(uid: $uid, locale: "en") {
url: original_url
}
}
query GetFiNoSvUrls($uid: String!) {
fi: current_blocks_page(uid: $uid, locale: "fi") {
url: original_url
}
no: current_blocks_page(uid: $uid, locale: "no") {
url: original_url
}
sv: current_blocks_page(uid: $uid, locale: "sv") {
url: original_url
}
}

View File

@@ -1,21 +1,31 @@
import "server-only"
import { request as graphqlRequest } from "graphql-request"
import { GraphQLClient } from "graphql-request"
import { env } from "@/env/server"
import { cache } from "react"
import type { Data } from "@/types/request"
import type { DocumentNode } from "graphql"
const client = new GraphQLClient(env.CMS_URL, {
fetch: cache(async function (url: URL | RequestInfo, params: RequestInit | undefined) {
return fetch(url, params)
}),
})
export async function request<T>(
query: string | DocumentNode,
variables?: {}
variables?: {},
next?: NextFetchRequestConfig
): Promise<Data<T>> {
try {
if (env.PRINT_QUERY) {
const graphqlRawRequest = (await import("graphql-request")).rawRequest
const print = (await import("graphql/language/printer")).print
const rawResponse = await graphqlRawRequest<T>(
env.CMS_URL,
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,
{
@@ -40,13 +50,12 @@ export async function request<T>(
}
}
const response = await graphqlRequest<T>({
const response = await client.request<T>({
document: query,
requestHeaders: {
access_token: env.CMS_ACCESS_TOKEN,
"Content-Type": "application/json",
},
url: env.CMS_URL,
variables,
})

View File

@@ -25,8 +25,14 @@ const nextConfig = {
loader: "graphql-tag/loader",
})
return config
return config;
},
}
logging: {
fetches: {
fullUrl: true,
},
},
};
export default nextConfig

View File

@@ -2,7 +2,7 @@
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="27 7.7 85 18.3" style="enable-background:new 27 7.7 85 18.3;" xml:space="preserve">
viewBox="27 7.7 85 19" style="enable-background:new 27 7.7 85 19;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -2,7 +2,7 @@
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 85 18.3" enable-background="new 0 0 85 18.3" xml:space="preserve">
viewBox="0 0 85 19" enable-background="new 0 0 85 19" xml:space="preserve">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#CD0921" d="M24.8,17.9c-1.2,0.3-2.4,0.4-3.6,0.4c-4.3,0-7.1-2-7.1-6.7
c0-4.5,2.9-6.4,7-6.4c1.2,0,2.4,0.1,3.6,0.3v2.2c-0.9-0.1-1.6-0.2-2.4-0.2c-3.3,0-4.9,1-4.9,4.3c0,3.1,1.5,4.2,4.9,4.2
c0.9,0,1.6-0.1,2.4-0.2V17.9z"/>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,8 +1,7 @@
import type { Breadcrumb } from "@/types/requests/currentBlockPage"
import type { Edges, Node } from "@/types/requests/utils/edges"
export type BreadcrumbsProps = {
breadcrumbs: Edges<Breadcrumb>
parent?: Node<Breadcrumb>
breadcrumbs: Breadcrumb[]
parent?: Breadcrumb
title: string
}

View File

@@ -1,8 +1,5 @@
import type { Lang } from "@/types/lang"
import type { GetCurrentBlockPageData } from "@/types/requests/currentBlockPage"
export type ContentPageProps = {
data: GetCurrentBlockPageData
uri: string
lang: Lang
}

View File

@@ -1,16 +1,5 @@
import { Lang } from "@/types/lang"
import {
ExternalLink,
InternalLink,
NavigationItem,
} from "@/types/requests/footer"
import type { NavigationItem } from "@/types/requests/footer"
export type FooterNavigationProps = {
linkGroups: NavigationItem[]
lang: Lang
}
export type FooterNavigationItemProps = {
linkObject: InternalLink | ExternalLink
lang: Lang
}

View File

@@ -1,3 +1,3 @@
export type HeaderProps = {
pathname: string
uid: string
}

View File

@@ -0,0 +1,13 @@
import type { HeaderLink, TopMenuHeaderLink } from "@/types/requests/header"
import type { Image } from "@/types/image"
import type { LanguageSwitcherQueryData } from "@/types/requests/languageSwitcher"
export type MainMenuProps = {
currentLanguage: string
frontpageLinkText: string
homeHref: string
links: HeaderLink[]
logo: Image
topMenuMobileLinks: TopMenuHeaderLink[]
urls: LanguageSwitcherQueryData
}

View File

@@ -0,0 +1,10 @@
import type { LanguageSwitcherQueryData } from "@/types/requests/languageSwitcher"
import type { TopMenuHeaderLink } from "@/types/requests/header"
export type TopMenuProps = {
currentLanguage: string
frontpageLinkText: string
homeHref: string
links: TopMenuHeaderLink[]
urls: LanguageSwitcherQueryData
}

View File

@@ -1,3 +1,5 @@
import type { LanguageSwitcherQueryData } from "@/types/requests/languageSwitcher"
export type LanguageSwitcherLink = {
href: string
title: string
@@ -5,5 +7,5 @@ export type LanguageSwitcherLink = {
export type LanguageSwitcherProps = {
currentLanguage: string
links: LanguageSwitcherLink[]
urls: LanguageSwitcherQueryData
}

View File

@@ -1,8 +1,7 @@
import type { Breadcrumb } from "@/types/requests/currentBlockPage"
import type { Edges, Node } from "@/types/requests/utils/edges"
export type SubnavMobileProps = {
breadcrumbs: Edges<Breadcrumb>
parent?: Node<Breadcrumb>
breadcrumbs: Breadcrumb[]
parent?: Breadcrumb
title: string
}

View File

@@ -1,8 +1,5 @@
import type { Puff } from "../puff"
import type { Edges } from "../utils/edges"
export type PuffAside = {
puff: {
puffConnection: Edges<Puff>
}
puff: Puff
}

View File

@@ -1,5 +1,3 @@
import type { Edges } from "../utils/edges"
import type { PageLink } from "../utils/pageLink"
import type { Typename } from "../utils/typename"
enum ListItemStyleEnum {
@@ -12,7 +10,6 @@ type ListItemStyle = keyof typeof ListItemStyleEnum
export enum BlockListItemsEnum {
CurrentBlocksPageBlocksListBlockListItemsListItem = "CurrentBlocksPageBlocksListBlockListItemsListItem",
CurrentBlocksPageBlocksListBlockListItemsListItemExternalLink = "CurrentBlocksPageBlocksListBlockListItemsListItemExternalLink",
CurrentBlocksPageBlocksListBlockListItemsListItemInternalLink = "CurrentBlocksPageBlocksListBlockListItemsListItemInternalLink",
}
type ExternalLinkListItem = Typename<
@@ -29,18 +26,6 @@ type ExternalLinkListItem = Typename<
BlockListItemsEnum.CurrentBlocksPageBlocksListBlockListItemsListItemExternalLink
>
type InternalLinkListItem = Typename<
{
list_item_internal_link: {
link_text?: string
list_item_style: ListItemStyle
subtitle?: string
pageConnection: Edges<PageLink>
}
},
BlockListItemsEnum.CurrentBlocksPageBlocksListBlockListItemsListItemInternalLink
>
type RegularListItem = Typename<
{
list_item: {
@@ -54,7 +39,6 @@ type RegularListItem = Typename<
export type ListItem =
| ExternalLinkListItem
| InternalLinkListItem
| RegularListItem
export type List = {

View File

@@ -1,8 +1,9 @@
import type { Edges } from "../utils/edges"
import type { Puff } from "../puff"
export type PuffBlock = {
puffs: {
puffsConnection: Edges<Puff>
puffs: {
puff: Puff
}[]
}
}

View File

@@ -13,8 +13,6 @@ import type {
Typename,
PagesTypenameEnum,
} from "./utils/typename"
import type { EmbedEnum } from "./utils/embeds"
import type { Edges } from "./utils/edges"
export type Asides =
| Typename<Contact, AsideTypenameEnum.CurrentBlocksPageAsideContact>
@@ -25,22 +23,13 @@ export type Blocks =
| Typename<PuffBlock, BlocksTypenameEnum.CurrentBlocksPageBlocksPuffs>
| Typename<Text, BlocksTypenameEnum.CurrentBlocksPageBlocksText>
interface SharedBreadcrumb {
breadcrumbs: {
title?: string
} | null
export type Breadcrumb = {
href: string
title: string
url: string
}
export interface CurrentBlocksPageBreadcrumb extends SharedBreadcrumb {
__typename: EmbedEnum.CurrentBlocksPage
}
export type Breadcrumb = CurrentBlocksPageBreadcrumb
export type Breadcrumbs = {
parentsConnection: Edges<Breadcrumb>
parents: Breadcrumb[]
title: string
}
@@ -51,13 +40,13 @@ export type BlockPage = {
breadcrumbs: Breadcrumbs
hero: Hero
preamble: Preamble
title: string
url: string
system: {
uid: string
created_at: string
uid: string
updated_at: string
}
title: string
url: string
}
export type GetCurrentBlockPageData = {

View File

@@ -1,4 +1,3 @@
import type { SysAsset } from "./utils/asset"
import type { PageLinkType } from "./utils/pageLink"
export type Embeds = PageLinkType | SysAsset
export type Embeds = SysAsset

View File

@@ -1,33 +1,19 @@
import type { AllRequestResponse } from "./utils/all"
import type { Edges } from "./utils/edges"
import type { Image } from "../image"
import type { PageLink } from "./utils/pageLink"
type AppDownload = {
href: string
imageConnection: Edges<Image>
}
export type InternalLink = {
__typename: "FooterNavigationLinksInternalLink"
internal_link: {
link_text: string
pageConnection: Edges<PageLink>
}
}
export type ExternalLink = {
__typename: "FooterNavigationLinksExternalLink"
external_link: {
link: {
href: string
title: string
}
}
export type Link = {
href: string
title: string
}
export type NavigationItem = {
links: (ExternalLink | InternalLink)[]
links: Link[]
title: string
}

35
types/requests/header.ts Normal file
View File

@@ -0,0 +1,35 @@
import type { Edges } from "./utils/edges"
import type { Image } from "../image"
export type HeaderLink = {
href: string
title: string
}
export type TopMenuHeaderLink = {
link: {
href: string
title: string
}
show_on_mobile: boolean
sort_order_mobile: number
}
export type HeaderLinks = {
links: HeaderLink[]
}
export type TopMenuHeaderLinks = {
links: TopMenuHeaderLink[]
}
export type HeaderQueryData = {
all_header: {
items: {
frontpage_link_text: string
logoConnection: Edges<Image>
menu: HeaderLinks
top_menu: TopMenuHeaderLinks
}[]
}
}

View File

@@ -0,0 +1,11 @@
type LanguageResult = {
url: string
}
export type LanguageSwitcherQueryData = {
de: LanguageResult | undefined
en: LanguageResult | undefined
fi: LanguageResult | undefined
no: LanguageResult | undefined
sv: LanguageResult | undefined
}

View File

@@ -1,22 +1,14 @@
import type { Image } from "../image"
import type { Edges } from "./utils/edges"
import type { Embeds } from "./embeds"
import type { PageLink } from "./utils/pageLink"
import type { RTEDocument } from "../rte/node"
export type Puff = {
imageConnection: Edges<Image>
is_internal: boolean
link: {
href: string
title: string
}
pageConnection: Edges<PageLink>
system: {
uid: string
}
text: {
embedded_itemsConnection: Edges<Embeds>
json: RTEDocument
}
title: string

View File

@@ -7,7 +7,6 @@ export type AsideTypename = keyof typeof AsideTypenameEnum
export enum BlocksTypenameEnum {
CurrentBlocksPageBlocksList = "CurrentBlocksPageBlocksList",
CurrentBlocksPageBlocksPreamble = "CurrentBlocksPageBlocksPreamble",
CurrentBlocksPageBlocksPuffs = "CurrentBlocksPageBlocksPuffs",
CurrentBlocksPageBlocksText = "CurrentBlocksPageBlocksText",
}