Merged develop into feature/sw-352-page-loading
This commit is contained in:
@@ -5,9 +5,9 @@ import styles from "./layout.module.css"
|
||||
export default async function MyPagesLayout({
|
||||
breadcrumbs,
|
||||
children,
|
||||
}: React.PropsWithChildren & {
|
||||
}: React.PropsWithChildren<{
|
||||
breadcrumbs: React.ReactNode
|
||||
}) {
|
||||
}>) {
|
||||
return (
|
||||
<section className={styles.layout}>
|
||||
{breadcrumbs}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async function CreditCardSlot({ params }: PageArgs<LangParams>) {
|
||||
<section className={styles.container}>
|
||||
<article className={styles.content}>
|
||||
<Subtitle type="two" color="black">
|
||||
{formatMessage({ id: "My credit cards" })}
|
||||
{formatMessage({ id: "My payment cards" })}
|
||||
</Subtitle>
|
||||
<Body color="black">
|
||||
{formatMessage({
|
||||
|
||||
1
app/[lang]/(live)/@bookingwidget/[...paths]/page.tsx
Normal file
1
app/[lang]/(live)/@bookingwidget/[...paths]/page.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "../page"
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "../../page"
|
||||
1
app/[lang]/(live)/@bookingwidget/default.tsx
Normal file
1
app/[lang]/(live)/@bookingwidget/default.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./page"
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "../../page"
|
||||
18
app/[lang]/(live)/@bookingwidget/page.tsx
Normal file
18
app/[lang]/(live)/@bookingwidget/page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import BookingWidget from "@/components/BookingWidget"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
export default async function BookingWidgetPage() {
|
||||
// Get the booking widget show/hide status based on page specific settings
|
||||
const bookingWidgetToggle =
|
||||
await serverClient().contentstack.bookingwidget.getToggle()
|
||||
|
||||
return (
|
||||
<>
|
||||
{bookingWidgetToggle && bookingWidgetToggle.hideBookingWidget ? null : (
|
||||
<BookingWidget />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { baseUrls } from "@/constants/routes/baseUrls"
|
||||
|
||||
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
||||
|
||||
export default function Error() {
|
||||
return <LanguageSwitcher urls={baseUrls} />
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function LanguageSwitcherRoute({
|
||||
params,
|
||||
}: PageArgs<LangParams>) {
|
||||
setLang(params.lang)
|
||||
|
||||
const data = await serverClient().contentstack.languageSwitcher.get()
|
||||
if (!data) {
|
||||
return null
|
||||
}
|
||||
return <LanguageSwitcher urls={data.urls} />
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function MyPagesMobileDropdownPage({
|
||||
params,
|
||||
}: PageArgs<LangParams>) {
|
||||
setLang(params.lang)
|
||||
const navigation = await serverClient().contentstack.myPages.navigation.get()
|
||||
if (!navigation) return null
|
||||
return <MyPagesMobileDropdown navigation={navigation} />
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import Header from "@/components/Current/Header"
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import type { LangParams, LayoutArgs } from "@/types/params"
|
||||
|
||||
export default function HeaderLayout({
|
||||
languageSwitcher,
|
||||
myPagesMobileDropdown,
|
||||
params,
|
||||
}: LayoutArgs<LangParams> & {
|
||||
languageSwitcher: React.ReactNode
|
||||
myPagesMobileDropdown: React.ReactNode
|
||||
}) {
|
||||
setLang(params.lang)
|
||||
return (
|
||||
<Header
|
||||
myPagesMobileDropdown={myPagesMobileDropdown}
|
||||
languageSwitcher={languageSwitcher}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1 @@
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default function EmptyHeaderPage({ params }: PageArgs<LangParams>) {
|
||||
setLang(params.lang)
|
||||
return null
|
||||
}
|
||||
export { default } from "../page"
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { baseUrls } from "@/constants/routes/baseUrls"
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import Header from "@/components/Current/Header"
|
||||
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
||||
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
|
||||
import Header from "@/components/Header"
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import { LangParams, PageArgs } from "@/types/params"
|
||||
@@ -11,11 +6,5 @@ import { LangParams, PageArgs } from "@/types/params"
|
||||
export default async function HeaderPage({ params }: PageArgs<LangParams>) {
|
||||
setLang(params.lang)
|
||||
|
||||
const navigation = await serverClient().contentstack.myPages.navigation.get()
|
||||
return (
|
||||
<Header
|
||||
myPagesMobileDropdown={<MyPagesMobileDropdown navigation={navigation} />}
|
||||
languageSwitcher={<LanguageSwitcher urls={baseUrls} />}
|
||||
/>
|
||||
)
|
||||
return <Header />
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ export default async function RootLayout({
|
||||
children,
|
||||
params,
|
||||
header,
|
||||
bookingwidget,
|
||||
}: React.PropsWithChildren<
|
||||
LayoutArgs<LangParams> & {
|
||||
header: React.ReactNode
|
||||
bookingwidget: React.ReactNode
|
||||
}
|
||||
>) {
|
||||
setLang(params.lang)
|
||||
@@ -52,6 +54,7 @@ export default async function RootLayout({
|
||||
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
||||
<TrpcProvider>
|
||||
{header}
|
||||
{bookingwidget}
|
||||
{children}
|
||||
<ToastHandler />
|
||||
<Footer />
|
||||
|
||||
@@ -4,6 +4,7 @@ import "@scandic-hotels/design-system/style.css"
|
||||
import Script from "next/script"
|
||||
|
||||
import TokenRefresher from "@/components/Auth/TokenRefresher"
|
||||
import BookingWidget from "@/components/BookingWidget"
|
||||
import AdobeScript from "@/components/Current/AdobeScript"
|
||||
import Footer from "@/components/Current/Footer"
|
||||
import Header from "@/components/Current/Header"
|
||||
@@ -71,6 +72,7 @@ export default async function RootLayout({
|
||||
myPagesMobileDropdown={myPagesMobileDropdown}
|
||||
languageSwitcher={languageSwitcher}
|
||||
/>
|
||||
<BookingWidget />
|
||||
{children}
|
||||
<Footer />
|
||||
<TokenRefresher />
|
||||
|
||||
@@ -6,6 +6,7 @@ import { z } from "zod"
|
||||
import { Lang } from "@/constants/languages"
|
||||
import { env } from "@/env/server"
|
||||
import { internalServerError } from "@/server/errors/next"
|
||||
import { affix as bookingwidgetAffix } from "@/server/routers/contentstack/bookingwidget/utils"
|
||||
import { affix as breadcrumbsAffix } from "@/server/routers/contentstack/breadcrumbs/utils"
|
||||
import { languageSwitcherAffix } from "@/server/routers/contentstack/languageSwitcher/utils"
|
||||
|
||||
@@ -29,6 +30,11 @@ const validateJsonBody = z.object({
|
||||
locale: z.nativeEnum(Lang),
|
||||
uid: z.string(),
|
||||
url: z.string().optional(),
|
||||
page_settings: z
|
||||
.object({
|
||||
hide_booking_widget: z.boolean(),
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
@@ -105,6 +111,17 @@ export async function POST(request: NextRequest) {
|
||||
revalidateTag(breadcrumbsTag)
|
||||
}
|
||||
|
||||
if (entry.page_settings?.hide_booking_widget) {
|
||||
const bookingwidgetTag = generateTag(
|
||||
entry.locale,
|
||||
entry.uid,
|
||||
bookingwidgetAffix
|
||||
)
|
||||
|
||||
console.info(`Revalidating breadcrumbsTag: ${bookingwidgetTag}`)
|
||||
revalidateTag(bookingwidgetTag)
|
||||
}
|
||||
|
||||
return Response.json({ revalidated: true, now: Date.now() })
|
||||
} catch (error) {
|
||||
console.error("Failed to revalidate tag(s)")
|
||||
|
||||
@@ -97,10 +97,18 @@
|
||||
}
|
||||
|
||||
:root {
|
||||
--max-width: 113.5rem;
|
||||
--current-max-width: 113.5rem;
|
||||
|
||||
--max-width: 94.5rem;
|
||||
--max-width-content: 74.75rem;
|
||||
--max-width-text-block: 49.5rem;
|
||||
--mobile-site-header-height: 70.047px;
|
||||
--current-mobile-site-header-height: 70.047px;
|
||||
--max-width-navigation: 89.5rem;
|
||||
|
||||
--main-menu-mobile-height: 75px;
|
||||
|
||||
--header-z-index: 1;
|
||||
--menu-overlay-z-index: 10;
|
||||
}
|
||||
|
||||
* {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Form from "../Forms/BookingWidget"
|
||||
import Form from "@/components/Forms/BookingWidget"
|
||||
|
||||
import styles from "./bookingWidget.module.css"
|
||||
|
||||
export function BookingWidget() {
|
||||
export default async function BookingWidget() {
|
||||
return (
|
||||
<section className={styles.container}>
|
||||
<Form />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
position: relative;
|
||||
max-width: var(--max-width-text-block);
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
|
||||
@@ -20,7 +20,6 @@ export default async function PreviewImages({ images }: PreviewImagesProps) {
|
||||
title={image.title}
|
||||
width={index === 0 ? 752 : 292}
|
||||
height={index === 0 ? 540 : 266}
|
||||
objectFit="cover"
|
||||
className={styles.image}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
.imageWrapper {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"main"
|
||||
"main"
|
||||
"main";
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: var(--Spacing-x2);
|
||||
background-color: var(--Base-Surface-Subtle-Normal);
|
||||
padding: var(--Spacing-x2) var(--Spacing-x2) 0;
|
||||
}
|
||||
|
||||
.image {
|
||||
object-fit: cover;
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.imageWrapper > :first-child {
|
||||
grid-area: main;
|
||||
max-height: 30vh;
|
||||
}
|
||||
|
||||
.imageWrapper > :nth-child(2),
|
||||
@@ -33,20 +24,29 @@
|
||||
bottom: var(--Spacing-x2);
|
||||
right: var(--Spacing-x4);
|
||||
z-index: 1;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.imageWrapper {
|
||||
grid-template-columns: 72fr 28fr;
|
||||
grid-template-columns: 70% 30%;
|
||||
grid-template-rows: repeat(2, 16.625rem);
|
||||
grid-template-areas:
|
||||
"main side1"
|
||||
"main side2";
|
||||
padding: var(--Spacing-x2) var(--Spacing-x5) 0;
|
||||
}
|
||||
|
||||
.image {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.imageWrapper > :first-child {
|
||||
grid-area: main;
|
||||
}
|
||||
|
||||
.imageWrapper > :nth-child(2),
|
||||
.imageWrapper > :nth-child(3) {
|
||||
display: block;
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.imageWrapper > :nth-child(2) {
|
||||
@@ -56,4 +56,8 @@
|
||||
.desktopGrid > :nth-child(3) {
|
||||
grid-area: side2;
|
||||
}
|
||||
|
||||
.seeAllButton {
|
||||
right: var(--Spacing-x7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { ImageIcon } from "@/components/Icons"
|
||||
import { ChevronRightIcon, ImageIcon } from "@/components/Icons"
|
||||
import Image from "@/components/Image"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
|
||||
@@ -62,14 +62,10 @@ export function RoomCard({
|
||||
</Title>
|
||||
<Body color="grey">{subtitle}</Body>
|
||||
</div>
|
||||
<Link
|
||||
href="#"
|
||||
color="peach80"
|
||||
variant="underscored"
|
||||
onClick={handleRoomCtaClick}
|
||||
>
|
||||
<Button variant="icon" intent="tertiary" onClick={handleRoomCtaClick}>
|
||||
{intl.formatMessage({ id: "See room details" })}
|
||||
</Link>
|
||||
<ChevronRightIcon className={styles.chevron} />
|
||||
</Button>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
|
||||
@@ -54,7 +54,7 @@ export function Rooms({ rooms }: RoomsProps) {
|
||||
<SectionHeader
|
||||
textTransform="uppercase"
|
||||
title={intl.formatMessage({ id: "Rooms" })}
|
||||
subtitle={null}
|
||||
preamble={null}
|
||||
/>
|
||||
<Grids.Stackable>
|
||||
{mappedRooms.map(
|
||||
@@ -80,7 +80,7 @@ export function Rooms({ rooms }: RoomsProps) {
|
||||
<div className={styles.ctaContainer}>
|
||||
<Button
|
||||
onClick={handleToggleShowMore}
|
||||
intent="text"
|
||||
intent="tertiary"
|
||||
variant="icon"
|
||||
className={`${styles.showMoreButton} ${allRoomsVisible ? styles.showLess : ""}`}
|
||||
>
|
||||
|
||||
@@ -3,9 +3,14 @@
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
gap: var(--Spacing-x4);
|
||||
background: var(--Base-Surface-Subtle-Normal);
|
||||
justify-content: flex-start;
|
||||
padding-left: var(--Spacing-x4);
|
||||
padding-right: var(--Spacing-x3);
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
padding: 0 var(--Spacing-x2);
|
||||
max-width: 100vw;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.tabsContainer {
|
||||
padding: 0 var(--Spacing-x5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,46 @@
|
||||
.pageContainer {
|
||||
overflow-x: auto;
|
||||
display: grid;
|
||||
max-width: var(--max-width);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.pageContainer > * {
|
||||
padding-left: var(--Spacing-x4);
|
||||
padding-right: var(--Spacing-x3);
|
||||
.topSection {
|
||||
background-color: var(--Base-Surface-Subtle-Normal);
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
}
|
||||
|
||||
.mainSection {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x9);
|
||||
padding: var(--Spacing-x5) var(--Spacing-x3) var(--Spacing-x4);
|
||||
padding: var(--Spacing-x4) var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.mapContainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.introContainer {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.pageContainer {
|
||||
grid-template-areas:
|
||||
"topSection mapContainer"
|
||||
"mainSection mapContainer";
|
||||
grid-template-columns: 1fr 23.75rem;
|
||||
}
|
||||
.topSection {
|
||||
grid-area: topSection;
|
||||
}
|
||||
.mainSection {
|
||||
padding: var(--Spacing-x9) var(--Spacing-x5);
|
||||
grid-area: mainSection;
|
||||
padding: var(--Spacing-x6) var(--Spacing-x5);
|
||||
}
|
||||
.mapContainer {
|
||||
grid-area: mapContainer;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pageContainer > nav {
|
||||
@@ -29,9 +48,9 @@
|
||||
padding-right: var(--Spacing-x5);
|
||||
}
|
||||
.introContainer {
|
||||
display: grid;
|
||||
/* use justify-content: space between once we have the map component*/
|
||||
gap: var(--Spacing-x9);
|
||||
grid-template-columns: 607px 340px;
|
||||
grid-template-columns: 38rem minmax(max-content, 16rem);
|
||||
justify-content: space-between;
|
||||
gap: var(--Spacing-x2);
|
||||
align-items: end;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ export default async function HotelPage() {
|
||||
if (!hotelData) {
|
||||
return null
|
||||
}
|
||||
|
||||
const {
|
||||
hotelName,
|
||||
hotelDescription,
|
||||
@@ -31,8 +32,10 @@ export default async function HotelPage() {
|
||||
|
||||
return (
|
||||
<div className={styles.pageContainer}>
|
||||
<PreviewImages images={hotelImages} />
|
||||
<TabNavigation />
|
||||
<div className={styles.topSection}>
|
||||
<PreviewImages images={hotelImages} />
|
||||
<TabNavigation />
|
||||
</div>
|
||||
<main className={styles.mainSection}>
|
||||
<div className={styles.introContainer}>
|
||||
<IntroSection
|
||||
@@ -42,12 +45,13 @@ export default async function HotelPage() {
|
||||
address={hotelAddress}
|
||||
tripAdvisor={hotelRatings?.tripAdvisor}
|
||||
/>
|
||||
<SidePeeks />
|
||||
<AmenitiesList detailedFacilities={hotelDetailedFacilities} />
|
||||
</div>
|
||||
<Rooms rooms={roomCategories} />
|
||||
<Facilities facilities={MOCK_FACILITIES} />
|
||||
</main>
|
||||
<aside className={styles.mapContainer}></aside>
|
||||
<SidePeeks />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 99999;
|
||||
height: var(--mobile-site-header-height);
|
||||
height: var(--current-mobile-site-header-height);
|
||||
}
|
||||
|
||||
.container {
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
|
||||
@media screen and (max-width: 1366px) {
|
||||
.header {
|
||||
height: var(--mobile-site-header-height);
|
||||
height: var(--current-mobile-site-header-height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { homeHrefs } from "@/constants/homeHrefs"
|
||||
import { env } from "@/env/server"
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import { BookingWidget } from "@/components/BookingWidget"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import { MainMenu } from "./MainMenu"
|
||||
@@ -24,11 +23,6 @@ export default async function Header({
|
||||
|
||||
const user = await serverClient().user.name()
|
||||
|
||||
/**
|
||||
* ToDo: Create logic to get this info from ContentStack based on page
|
||||
* */
|
||||
const hideBookingWidget = true
|
||||
|
||||
if (!data) {
|
||||
return null
|
||||
}
|
||||
@@ -60,7 +54,6 @@ export default async function Header({
|
||||
bookingHref={homeHref}
|
||||
user={user}
|
||||
/>
|
||||
{hideBookingWidget ? null : <BookingWidget />}
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
6
components/Header/HeaderLink/headerLink.module.css
Normal file
6
components/Header/HeaderLink/headerLink.module.css
Normal file
@@ -0,0 +1,6 @@
|
||||
.headerLink {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
font-size: var(--typography-Caption-Regular-fontSize);
|
||||
}
|
||||
13
components/Header/HeaderLink/index.tsx
Normal file
13
components/Header/HeaderLink/index.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
|
||||
import styles from "./headerLink.module.css"
|
||||
|
||||
import type { HeaderLinkProps } from "@/types/components/header/headerLink"
|
||||
|
||||
export default function HeaderLink({ children, ...props }: HeaderLinkProps) {
|
||||
return (
|
||||
<Link color="burgundy" className={styles.headerLink} {...props}>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
16
components/Header/MainMenu/Avatar/avatar.module.css
Normal file
16
components/Header/MainMenu/Avatar/avatar.module.css
Normal file
@@ -0,0 +1,16 @@
|
||||
.avatar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
border-radius: var(--Corner-radius-Rounded);
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background-color: var(--Main-Grey-40);
|
||||
}
|
||||
|
||||
.initials {
|
||||
font-size: 0.75rem;
|
||||
color: var(--Base-Text-Inverted);
|
||||
background-color: var(--Base-Icon-Low-contrast);
|
||||
}
|
||||
19
components/Header/MainMenu/Avatar/index.tsx
Normal file
19
components/Header/MainMenu/Avatar/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { PersonIcon } from "@/components/Icons"
|
||||
import Image from "@/components/Image"
|
||||
|
||||
import styles from "./avatar.module.css"
|
||||
|
||||
import type { AvatarProps } from "@/types/components/header/avatar"
|
||||
|
||||
export default function Avatar({ image, initials }: AvatarProps) {
|
||||
let classNames = [styles.avatar]
|
||||
let element = <PersonIcon color="white" />
|
||||
if (image) {
|
||||
classNames.push(styles.image)
|
||||
element = <Image src={image.src} alt={image.alt} width={28} height={28} />
|
||||
} else if (initials) {
|
||||
classNames.push(styles.initials)
|
||||
element = <span>{initials}</span>
|
||||
}
|
||||
return <span className={classNames.join(" ")}>{element}</span>
|
||||
}
|
||||
16
components/Header/MainMenu/MainMenuButton/index.tsx
Normal file
16
components/Header/MainMenu/MainMenuButton/index.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import styles from "./menuButton.module.css"
|
||||
|
||||
import type { MainMenuButtonProps } from "@/types/components/header/mainMenuButton"
|
||||
|
||||
export default function MainMenuButton({
|
||||
className = "",
|
||||
...props
|
||||
}: MainMenuButtonProps) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.menuButton} ${className}`}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
.menuButton {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
color: var(--Base-Text-High-contrast);
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
font-family: var(--typography-Body-Bold-fontFamily);
|
||||
font-weight: var(--typography-Body-Bold-fontWeight);
|
||||
font-size: var(--typography-Body-Bold-fontSize);
|
||||
}
|
||||
88
components/Header/MainMenu/MobileMenu/index.tsx
Normal file
88
components/Header/MainMenu/MobileMenu/index.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
"use client"
|
||||
|
||||
import { Dialog, Modal } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import useDropdownStore from "@/stores/main-menu"
|
||||
|
||||
import { GiftIcon, SearchIcon, ServiceIcon } from "@/components/Icons"
|
||||
import LanguageSwitcher from "@/components/LanguageSwitcher"
|
||||
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
|
||||
|
||||
import HeaderLink from "../../HeaderLink"
|
||||
import NavigationMenu from "../NavigationMenu"
|
||||
|
||||
import styles from "./mobileMenu.module.css"
|
||||
|
||||
import type { MobileMenuProps } from "@/types/components/header/mobileMenu"
|
||||
|
||||
export default function MobileMenu({
|
||||
mainNavigation,
|
||||
languageUrls,
|
||||
}: MobileMenuProps) {
|
||||
const intl = useIntl()
|
||||
const {
|
||||
isHamburgerMenuOpen,
|
||||
isMyPagesMobileMenuOpen,
|
||||
isLanguageSwitcherOpen,
|
||||
toggleHamburgerMenu,
|
||||
toggleMyPagesMobileMenu,
|
||||
toggleLanguageSwitcher,
|
||||
} = useDropdownStore()
|
||||
|
||||
useHandleKeyUp((event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && isHamburgerMenuOpen) {
|
||||
toggleHamburgerMenu()
|
||||
}
|
||||
})
|
||||
|
||||
function handleHamburgerClick() {
|
||||
if (isMyPagesMobileMenuOpen) {
|
||||
toggleMyPagesMobileMenu()
|
||||
} else {
|
||||
if (isLanguageSwitcherOpen) {
|
||||
toggleLanguageSwitcher()
|
||||
}
|
||||
|
||||
toggleHamburgerMenu()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.hamburger} ${isHamburgerMenuOpen || isMyPagesMobileMenuOpen ? styles.isExpanded : ""}`}
|
||||
aria-label={intl.formatMessage({
|
||||
id: isHamburgerMenuOpen ? "Close menu" : "Open menu",
|
||||
})}
|
||||
onClick={handleHamburgerClick}
|
||||
>
|
||||
<span className={styles.bar}></span>
|
||||
</button>
|
||||
<Modal className={styles.modal} isOpen={isHamburgerMenuOpen}>
|
||||
<Dialog
|
||||
className={styles.dialog}
|
||||
aria-label={intl.formatMessage({ id: "Menu" })}
|
||||
>
|
||||
<NavigationMenu isMobile={true} items={mainNavigation} />
|
||||
<footer className={styles.footer}>
|
||||
<HeaderLink href="#">
|
||||
<SearchIcon width={20} height={20} color="burgundy" />
|
||||
{intl.formatMessage({ id: "Find booking" })}
|
||||
</HeaderLink>
|
||||
<HeaderLink href="#">
|
||||
<GiftIcon width={20} height={20} color="burgundy" />
|
||||
{intl.formatMessage({ id: "Join Scandic Friends" })}
|
||||
</HeaderLink>
|
||||
<HeaderLink href="#">
|
||||
<ServiceIcon width={20} height={20} color="burgundy" />
|
||||
{intl.formatMessage({ id: "Customer service" })}
|
||||
</HeaderLink>
|
||||
<LanguageSwitcher type="mobileHeader" urls={languageUrls} />
|
||||
</footer>
|
||||
</Dialog>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
103
components/Header/MainMenu/MobileMenu/mobileMenu.module.css
Normal file
103
components/Header/MainMenu/MobileMenu/mobileMenu.module.css
Normal file
@@ -0,0 +1,103 @@
|
||||
@keyframes slide-in {
|
||||
from {
|
||||
right: -100vw;
|
||||
}
|
||||
|
||||
to {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hamburger {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
justify-self: flex-start;
|
||||
padding: 11px 8px 16px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.bar,
|
||||
.bar::after,
|
||||
.bar::before {
|
||||
background: var(--Base-Text-High-contrast);
|
||||
border-radius: 2.3px;
|
||||
display: inline-block;
|
||||
height: 3px;
|
||||
position: relative;
|
||||
transition: all 0.3s;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.bar::after,
|
||||
.bar::before {
|
||||
content: "";
|
||||
left: 0;
|
||||
position: absolute;
|
||||
transform-origin: 2.286px center;
|
||||
}
|
||||
|
||||
.bar::after {
|
||||
top: -8px;
|
||||
}
|
||||
|
||||
.bar::before {
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
.isExpanded .bar {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.isExpanded .bar::after,
|
||||
.isExpanded .bar::before {
|
||||
top: 0;
|
||||
transform-origin: 50% 50%;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.isExpanded .bar::after {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.isExpanded .bar::before {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: var(--main-menu-mobile-height);
|
||||
right: auto;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
transition: right 0.3s;
|
||||
z-index: var(--menu-overlay-z-index);
|
||||
}
|
||||
|
||||
.modal[data-entering] {
|
||||
animation: slide-in 0.3s;
|
||||
}
|
||||
.modal[data-exiting] {
|
||||
animation: slide-in 0.3s reverse;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
display: grid;
|
||||
align-content: space-between;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: var(--Base-Surface-Subtle-Normal);
|
||||
padding: var(--Spacing-x4) var(--Spacing-x2);
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.hamburger {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
60
components/Header/MainMenu/MyPagesMenu/index.tsx
Normal file
60
components/Header/MainMenu/MyPagesMenu/index.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import useDropdownStore from "@/stores/main-menu"
|
||||
|
||||
import { ChevronDownIcon } from "@/components/Icons"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { getInitials } from "@/utils/user"
|
||||
|
||||
import Avatar from "../Avatar"
|
||||
import MainMenuButton from "../MainMenuButton"
|
||||
import MyPagesMenuContent from "../MyPagesMenuContent"
|
||||
|
||||
import styles from "./myPagesMenu.module.css"
|
||||
|
||||
import type { MyPagesMenuProps } from "@/types/components/header/myPagesMenu"
|
||||
|
||||
export default function MyPagesMenu({
|
||||
membership,
|
||||
navigation,
|
||||
user,
|
||||
}: MyPagesMenuProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const { toggleMyPagesMenu, isMyPagesMenuOpen } = useDropdownStore()
|
||||
|
||||
useHandleKeyUp((event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && isMyPagesMenuOpen) {
|
||||
toggleMyPagesMenu()
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={styles.myPagesMenu}>
|
||||
<MainMenuButton className={styles.button} onClick={toggleMyPagesMenu}>
|
||||
<Avatar initials={getInitials(user.firstName, user.lastName)} />
|
||||
<Subtitle type="two" className={styles.userName}>
|
||||
{intl.formatMessage({ id: "Hi" })} {user.firstName}!
|
||||
</Subtitle>
|
||||
<ChevronDownIcon
|
||||
className={`${styles.chevron} ${isMyPagesMenuOpen ? styles.isExpanded : ""}`}
|
||||
color="red"
|
||||
/>
|
||||
</MainMenuButton>
|
||||
{isMyPagesMenuOpen ? (
|
||||
<div className={styles.dropdown}>
|
||||
<MyPagesMenuContent
|
||||
navigation={navigation}
|
||||
user={user}
|
||||
membership={membership}
|
||||
toggleOpenStateFn={toggleMyPagesMenu}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
.myPagesMenu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.myPagesMenu {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chevron {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.chevron.isExpanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: absolute;
|
||||
top: 2.875rem; /* 2.875rem is the height of the main menu + bottom padding */
|
||||
right: 0;
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
box-shadow: 0 0 0.875rem 0.375rem rgba(0, 0, 0, 0.1);
|
||||
min-width: 20rem;
|
||||
z-index: var(--menu-overlay-z-index);
|
||||
}
|
||||
|
||||
/* Triangle above dropdown */
|
||||
.dropdown::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -1.25rem;
|
||||
right: 2.4rem;
|
||||
transform: rotate(180deg);
|
||||
border-width: 0.75rem;
|
||||
border-style: solid;
|
||||
border-color: var(--Base-Surface-Primary-light-Normal) transparent
|
||||
transparent transparent;
|
||||
}
|
||||
|
||||
.dropdown.isExpanded {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
99
components/Header/MainMenu/MyPagesMenuContent/index.tsx
Normal file
99
components/Header/MainMenu/MyPagesMenuContent/index.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { MembershipLevelEnum } from "@/constants/membershipLevels"
|
||||
import { logout } from "@/constants/routes/handleAuth"
|
||||
|
||||
import { ArrowRightIcon } from "@/components/Icons"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { useTrapFocus } from "@/hooks/useTrapFocus"
|
||||
import { getMembershipLevelObject } from "@/utils/membershipLevel"
|
||||
|
||||
import styles from "./myPagesMenuContent.module.css"
|
||||
|
||||
import type { MyPagesMenuContentProps } from "@/types/components/header/myPagesMenu"
|
||||
|
||||
export default function MyPagesMenuContent({
|
||||
membership,
|
||||
navigation,
|
||||
toggleOpenStateFn,
|
||||
user,
|
||||
}: MyPagesMenuContentProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const myPagesMenuContentRef = useTrapFocus()
|
||||
|
||||
const membershipLevel = membership?.membershipLevel
|
||||
? getMembershipLevelObject(
|
||||
membership.membershipLevel as MembershipLevelEnum,
|
||||
lang
|
||||
)
|
||||
: null
|
||||
const membershipPoints = membership?.currentPoints
|
||||
const introClassName =
|
||||
membershipLevel && membershipPoints
|
||||
? `${styles.intro}`
|
||||
: `${styles.intro} ${styles.noMembership}`
|
||||
if (!navigation) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className={styles.myPagesMenuContent} ref={myPagesMenuContentRef}>
|
||||
<div className={introClassName}>
|
||||
<Subtitle type="two" className={styles.userName}>
|
||||
{intl.formatMessage({ id: "Hi" })} {user.firstName}!
|
||||
</Subtitle>
|
||||
{membershipLevel && membershipPoints ? (
|
||||
<Caption className={styles.friendTypeWrapper}>
|
||||
<span className={styles.friendType}>{membershipLevel.name}</span>
|
||||
<span className={styles.friendPoints}>
|
||||
{membershipPoints} {intl.formatMessage({ id: "points" })}
|
||||
</span>
|
||||
</Caption>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<ul className={styles.groups}>
|
||||
{navigation.menuItems.map((menuItem, idx) => (
|
||||
<li
|
||||
key={`${menuItem.display_sign_out_link}-${idx}`}
|
||||
className={styles.group}
|
||||
>
|
||||
<Divider color="subtle" className={styles.divider} />
|
||||
<ul className={styles.menuItems}>
|
||||
{menuItem.links.map((link) => (
|
||||
<li key={link.uid}>
|
||||
<Link
|
||||
href={link.originalUrl || link.url}
|
||||
onClick={toggleOpenStateFn}
|
||||
className={`${styles.link} ${menuItem.display_sign_out_link ? styles.smallLink : ""}`}
|
||||
>
|
||||
{link.linkText}
|
||||
<ArrowRightIcon className={styles.arrow} color="burgundy" />
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
{menuItem.display_sign_out_link ? (
|
||||
<li>
|
||||
<Link
|
||||
href={logout[lang]}
|
||||
prefetch={false}
|
||||
className={`${styles.link} ${styles.smallLink}`}
|
||||
>
|
||||
{intl.formatMessage({ id: "Log out" })}
|
||||
</Link>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
.myPagesMenuContent {
|
||||
padding: var(--Spacing-x3) var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.intro {
|
||||
padding: 0 var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.myPagesMenuContent .friendTypeWrapper {
|
||||
color: var(--UI-Text-Medium-contrast);
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: var(--Spacing-x2) 0;
|
||||
}
|
||||
|
||||
.friendType {
|
||||
font-family: var(--typography-Title-5-fontFamily);
|
||||
letter-spacing: var(--typography-Title-5-letterSpacing);
|
||||
font-size: var(--typography-Caption-Bold-fontSize);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.friendType::after {
|
||||
content: " · ";
|
||||
display: inline;
|
||||
padding: 0 var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.groups,
|
||||
.menuItems {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
text-decoration: none;
|
||||
padding: var(--Spacing-x1);
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
color: var(--Base-Text-High-contrast);
|
||||
font-family: var(--typography-Body-Bold-fontFamily);
|
||||
font-size: var(--typography-Body-Bold-fontSize);
|
||||
font-weight: var(--typography-Body-Bold-fontWeight);
|
||||
line-height: var(--typography-Body-Bold-lineHeight);
|
||||
letter-spacing: var(--typography-Body-Bold-letterSpacing);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
background-color: var(--Base-Surface-Primary-light-Hover-alt);
|
||||
}
|
||||
|
||||
.link.smallLink {
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
font-size: var(--typography-Body-Regular-fontSize);
|
||||
font-weight: var(--typography-Body-Regular-fontWeight);
|
||||
line-height: var(--typography-Body-Regular-lineHeight);
|
||||
letter-spacing: var(--typography-Body-Regular-letterSpacing);
|
||||
}
|
||||
|
||||
.link:not(:hover) .arrow {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.myPagesMenuContent {
|
||||
padding: var(--Spacing-x2) var(--Spacing-x4);
|
||||
}
|
||||
.intro.noMembership,
|
||||
.userName {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
58
components/Header/MainMenu/MyPagesMobileMenu/index.tsx
Normal file
58
components/Header/MainMenu/MyPagesMobileMenu/index.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
"use client"
|
||||
|
||||
import { Dialog, Modal } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import useDropdownStore from "@/stores/main-menu"
|
||||
|
||||
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
|
||||
import { getInitials } from "@/utils/user"
|
||||
|
||||
import Avatar from "../Avatar"
|
||||
import MainMenuButton from "../MainMenuButton"
|
||||
import MyPagesMenuContent from "../MyPagesMenuContent"
|
||||
|
||||
import styles from "./myPagesMobileMenu.module.css"
|
||||
|
||||
import type { MyPagesMenuProps } from "@/types/components/header/myPagesMenu"
|
||||
|
||||
export default function MyPagesMobileMenu({
|
||||
membership,
|
||||
navigation,
|
||||
user,
|
||||
}: MyPagesMenuProps) {
|
||||
const intl = useIntl()
|
||||
const { toggleMyPagesMobileMenu, isMyPagesMobileMenuOpen } =
|
||||
useDropdownStore()
|
||||
|
||||
useHandleKeyUp((event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && isMyPagesMobileMenuOpen) {
|
||||
toggleMyPagesMobileMenu()
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={styles.myPagesMobileMenu}>
|
||||
<MainMenuButton
|
||||
className={styles.button}
|
||||
onClick={toggleMyPagesMobileMenu}
|
||||
aria-label={intl.formatMessage({ id: "Open my pages menu" })}
|
||||
>
|
||||
<Avatar initials={getInitials(user.firstName, user.lastName)} />
|
||||
</MainMenuButton>
|
||||
<Modal className={styles.modal} isOpen={isMyPagesMobileMenuOpen}>
|
||||
<Dialog
|
||||
className={styles.dialog}
|
||||
aria-label={intl.formatMessage({ id: "My pages menu" })}
|
||||
>
|
||||
<MyPagesMenuContent
|
||||
membership={membership}
|
||||
navigation={navigation}
|
||||
user={user}
|
||||
toggleOpenStateFn={toggleMyPagesMobileMenu}
|
||||
/>
|
||||
</Dialog>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
@keyframes slide-in {
|
||||
from {
|
||||
right: -100vw;
|
||||
}
|
||||
|
||||
to {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: var(--main-menu-mobile-height);
|
||||
right: auto;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
z-index: var(--menu-overlay-z-index);
|
||||
}
|
||||
|
||||
.modal[data-entering] {
|
||||
animation: slide-in 0.3s;
|
||||
}
|
||||
.modal[data-exiting] {
|
||||
animation: slide-in 0.3s reverse;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.myPagesMobileMenu,
|
||||
.modal {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
|
||||
import { ChevronDownIcon, ChevronRightIcon } from "@/components/Icons"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
|
||||
import MainMenuButton from "../../MainMenuButton"
|
||||
|
||||
import styles from "./navigationMenuItem.module.css"
|
||||
|
||||
import type { NavigationMenuItemProps } from "@/types/components/header/navigationMenuItem"
|
||||
|
||||
export default function MenuItem({ item, isMobile }: NavigationMenuItemProps) {
|
||||
const { children, title, href, seeAllLinkText, infoCard } = item
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
|
||||
function handleButtonClick() {
|
||||
setIsExpanded((prev) => !prev)
|
||||
}
|
||||
|
||||
return children?.length ? (
|
||||
<MainMenuButton
|
||||
onClick={handleButtonClick}
|
||||
className={`${styles.navigationMenuItem} ${isMobile ? styles.mobile : styles.desktop}`}
|
||||
>
|
||||
{title}
|
||||
{isMobile ? (
|
||||
<ChevronRightIcon className={`${styles.chevron}`} color="red" />
|
||||
) : (
|
||||
<ChevronDownIcon
|
||||
className={`${styles.chevron} ${isExpanded ? styles.isExpanded : ""}`}
|
||||
color="red"
|
||||
/>
|
||||
)}
|
||||
</MainMenuButton>
|
||||
) : (
|
||||
<Link
|
||||
href={href}
|
||||
color="burgundy"
|
||||
className={`${styles.navigationMenuItem} ${isMobile ? styles.mobile : styles.desktop}`}
|
||||
>
|
||||
{title}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
.navigationMenuItem.mobile {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: var(--Spacing-x2) 0;
|
||||
font-size: var(--typography-Subtitle-1-Mobile-fontSize);
|
||||
}
|
||||
|
||||
.chevron {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.chevron.isExpanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
22
components/Header/MainMenu/NavigationMenu/index.tsx
Normal file
22
components/Header/MainMenu/NavigationMenu/index.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import NavigationMenuItem from "./NavigationMenuItem"
|
||||
|
||||
import styles from "./navigationMenu.module.css"
|
||||
|
||||
import type { NavigationMenuProps } from "@/types/components/header/navigationMenu"
|
||||
|
||||
export default function NavigationMenu({
|
||||
items,
|
||||
isMobile,
|
||||
}: NavigationMenuProps) {
|
||||
return (
|
||||
<ul
|
||||
className={`${styles.navigationMenu} ${isMobile ? styles.mobile : styles.desktop}`}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<li key={item.id} className={styles.item}>
|
||||
<NavigationMenuItem isMobile={isMobile} item={item} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
.navigationMenu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x4);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navigationMenu.mobile {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
gap: 0;
|
||||
justify-content: stretch;
|
||||
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2) var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.navigationMenu.mobile .item {
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.navigationMenu.desktop {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
81
components/Header/MainMenu/index.tsx
Normal file
81
components/Header/MainMenu/index.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import NextLink from "next/link"
|
||||
|
||||
import { myPages } from "@/constants/routes/myPages"
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import Image from "@/components/Image"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import { navigationMenuItems } from "../tempHeaderData"
|
||||
import Avatar from "./Avatar"
|
||||
import MobileMenu from "./MobileMenu"
|
||||
import MyPagesMenu from "./MyPagesMenu"
|
||||
import MyPagesMobileMenu from "./MyPagesMobileMenu"
|
||||
import NavigationMenu from "./NavigationMenu"
|
||||
|
||||
import styles from "./mainMenu.module.css"
|
||||
|
||||
import type { MainMenuProps } from "@/types/components/header/mainMenu"
|
||||
|
||||
export default async function MainMenu({ languageUrls }: MainMenuProps) {
|
||||
const intl = await getIntl()
|
||||
const lang = getLang()
|
||||
const myPagesNavigation =
|
||||
await serverClient().contentstack.myPages.navigation.get()
|
||||
|
||||
const user = await serverClient().user.name()
|
||||
const membership = await serverClient().user.membershipLevel()
|
||||
|
||||
return (
|
||||
<div className={styles.mainMenu}>
|
||||
<nav className={styles.nav}>
|
||||
<NextLink className={styles.logoLink} href={`/${lang}`}>
|
||||
<Image
|
||||
alt={intl.formatMessage({ id: "Back to scandichotels.com" })}
|
||||
className={styles.logo}
|
||||
data-js="scandiclogoimg"
|
||||
data-nosvgsrc="/_static/img/scandic-logotype.png"
|
||||
itemProp="logo"
|
||||
height={22}
|
||||
src="/_static/img/scandic-logotype.svg"
|
||||
width={103}
|
||||
/>
|
||||
</NextLink>
|
||||
<div className={styles.menus}>
|
||||
<NavigationMenu items={navigationMenuItems} isMobile={false} />
|
||||
{user ? (
|
||||
<>
|
||||
<MyPagesMenu
|
||||
membership={membership}
|
||||
navigation={myPagesNavigation}
|
||||
user={user}
|
||||
/>
|
||||
<MyPagesMobileMenu
|
||||
membership={membership}
|
||||
navigation={myPagesNavigation}
|
||||
user={user}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Link
|
||||
href={myPages[lang]}
|
||||
className={styles.loginLink}
|
||||
aria-label={intl.formatMessage({ id: "Log in/Join" })}
|
||||
>
|
||||
<Avatar />
|
||||
<span className={styles.userName}>
|
||||
{intl.formatMessage({ id: "Log in/Join" })}
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
<MobileMenu
|
||||
languageUrls={languageUrls}
|
||||
mainNavigation={navigationMenuItems}
|
||||
/>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
54
components/Header/MainMenu/mainMenu.module.css
Normal file
54
components/Header/MainMenu/mainMenu.module.css
Normal file
@@ -0,0 +1,54 @@
|
||||
.mainMenu {
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
padding: var(--Spacing-x2);
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
}
|
||||
|
||||
.nav {
|
||||
max-width: var(--max-width-navigation);
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.menus {
|
||||
display: flex;
|
||||
justify-self: end;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.logoLink {
|
||||
display: inline-flex;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 1.375rem;
|
||||
}
|
||||
|
||||
.loginLink {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.userName {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: var(--Spacing-x3);
|
||||
}
|
||||
.menus {
|
||||
display: contents;
|
||||
}
|
||||
.userName {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
31
components/Header/TopMenu/index.tsx
Normal file
31
components/Header/TopMenu/index.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { GiftIcon, SearchIcon } from "@/components/Icons"
|
||||
import LanguageSwitcher from "@/components/LanguageSwitcher"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import HeaderLink from "../HeaderLink"
|
||||
|
||||
import styles from "./topMenu.module.css"
|
||||
|
||||
import type { TopMenuProps } from "@/types/components/header/topMenu"
|
||||
|
||||
export default async function TopMenu({ languageUrls }: TopMenuProps) {
|
||||
const intl = await getIntl()
|
||||
|
||||
return (
|
||||
<div className={styles.topMenu}>
|
||||
<div className={styles.content}>
|
||||
<HeaderLink href="#">
|
||||
<GiftIcon width={20} height={20} color="burgundy" />
|
||||
{intl.formatMessage({ id: "Join Scandic Friends" })}
|
||||
</HeaderLink>
|
||||
<div className={styles.right}>
|
||||
<LanguageSwitcher type="desktopHeader" urls={languageUrls} />
|
||||
<HeaderLink href="#">
|
||||
<SearchIcon width={20} height={20} color="burgundy" />
|
||||
{intl.formatMessage({ id: "Find booking" })}
|
||||
</HeaderLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
26
components/Header/TopMenu/topMenu.module.css
Normal file
26
components/Header/TopMenu/topMenu.module.css
Normal file
@@ -0,0 +1,26 @@
|
||||
.topMenu {
|
||||
display: none;
|
||||
background-color: var(--Base-Surface-Subtle-Normal);
|
||||
padding: var(--Spacing-x2) var(--Spacing-x-one-and-half);
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: var(--max-width-navigation);
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x2);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.topMenu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
7
components/Header/header.module.css
Normal file
7
components/Header/header.module.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.header {
|
||||
position: relative;
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
color: var(--Base-Text-High-contrast);
|
||||
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.08);
|
||||
z-index: var(--header-z-index);
|
||||
}
|
||||
21
components/Header/index.tsx
Normal file
21
components/Header/index.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import MainMenu from "./MainMenu"
|
||||
import TopMenu from "./TopMenu"
|
||||
|
||||
import styles from "./header.module.css"
|
||||
|
||||
export default async function Header() {
|
||||
const languages = await serverClient().contentstack.languageSwitcher.get()
|
||||
|
||||
if (!languages) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<header className={styles.header}>
|
||||
<TopMenu languageUrls={languages.urls} />
|
||||
<MainMenu languageUrls={languages.urls} />
|
||||
</header>
|
||||
)
|
||||
}
|
||||
75
components/Header/tempHeaderData.ts
Normal file
75
components/Header/tempHeaderData.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import type { MainNavigationItem } from "@/types/components/header/mainNavigationItem"
|
||||
|
||||
export const navigationMenuItems: MainNavigationItem[] = [
|
||||
{
|
||||
id: "hotels",
|
||||
title: "Hotels",
|
||||
href: "/hotels",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: "business",
|
||||
title: "Business",
|
||||
href: "/business",
|
||||
children: [
|
||||
{
|
||||
groupTitle: "Top conference venues",
|
||||
children: [
|
||||
{
|
||||
id: "stockholm",
|
||||
title: "Stockholm",
|
||||
href: "/stockholm",
|
||||
},
|
||||
{
|
||||
id: "bergen",
|
||||
title: "Bergen",
|
||||
href: "/bergen",
|
||||
},
|
||||
{
|
||||
id: "copenhagen",
|
||||
title: "Copenhagen",
|
||||
href: "/copenhagen",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupTitle: "Scandic for business",
|
||||
children: [
|
||||
{
|
||||
id: "book-a-venue",
|
||||
title: "Book a venue",
|
||||
href: "/book-a-venue",
|
||||
},
|
||||
{
|
||||
id: "conference-packages",
|
||||
title: "Conference packages",
|
||||
href: "/conference-packages",
|
||||
},
|
||||
{
|
||||
id: "co-working",
|
||||
title: "Co-working",
|
||||
href: "/co-working",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
seeAllLinkText: "See all conference & meeting venues",
|
||||
infoCard: {
|
||||
scriptedTitle: "Stockholm",
|
||||
title: "Meeting venues in Stockholm",
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et felis metus. Sed et felis metus.",
|
||||
ctaLink: "/stockholm",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "offers",
|
||||
title: "Offers",
|
||||
href: "/offers",
|
||||
},
|
||||
{
|
||||
id: "restaurants",
|
||||
title: "Restaurants",
|
||||
href: "/restaurants",
|
||||
},
|
||||
]
|
||||
@@ -7,27 +7,27 @@ export default function CheckIcon({ className, color, ...props }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
fill="none"
|
||||
height="25"
|
||||
viewBox="0 0 24 25"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
height="25"
|
||||
id="mask0_1333_19690"
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask0_2570_1776"
|
||||
style={{ maskType: "alpha" }}
|
||||
width="24"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<rect y="0.629639" width="24" height="24" fill="#D9D9D9" />
|
||||
<rect width="24" height="24" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_1333_19690)">
|
||||
<g mask="url(#mask0_2570_1776)">
|
||||
<path
|
||||
d="M9.99967 16.6738L6.35547 13.0296L7.39967 11.9854L9.99967 14.5854L16.5997 7.98535L17.6439 9.02955L9.99967 16.6738Z"
|
||||
d="M9.57552 15.1752L17.9505 6.8002C18.1422 6.60853 18.3651 6.5127 18.6193 6.5127C18.8734 6.5127 19.0964 6.60853 19.288 6.8002C19.4797 6.99186 19.5755 7.21478 19.5755 7.46895C19.5755 7.72311 19.4797 7.94603 19.288 8.1377L10.238 17.1877C10.0464 17.3794 9.82552 17.4752 9.57552 17.4752C9.32552 17.4752 9.10469 17.3794 8.91302 17.1877L4.71302 12.9877C4.52136 12.796 4.42761 12.5731 4.43177 12.3189C4.43594 12.0648 4.53386 11.8419 4.72552 11.6502C4.91719 11.4585 5.14011 11.3627 5.39427 11.3627C5.64844 11.3627 5.87136 11.4585 6.06302 11.6502L9.57552 15.1752Z"
|
||||
fill="#4D001B"
|
||||
/>
|
||||
</g>
|
||||
|
||||
40
components/Icons/ChevronLeft.tsx
Normal file
40
components/Icons/ChevronLeft.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function ChevronLeftIcon({
|
||||
className,
|
||||
color,
|
||||
...props
|
||||
}: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
id="mask0_2291_1760"
|
||||
style={{ maskType: "alpha" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="20"
|
||||
height="20"
|
||||
>
|
||||
<rect width="20" height="20" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_2291_1760)">
|
||||
<path
|
||||
d="M7.75 10.0001L13.5729 15.823C13.7535 16.0036 13.8455 16.2258 13.849 16.4897C13.8524 16.7536 13.7639 16.9758 13.5833 17.1563C13.4028 17.3369 13.1806 17.4272 12.9167 17.4272C12.6528 17.4272 12.4306 17.3369 12.25 17.1563L6.08333 10.9793C5.9375 10.8334 5.83507 10.6807 5.77604 10.5209C5.71701 10.3612 5.6875 10.1876 5.6875 10.0001C5.6875 9.8126 5.71701 9.63899 5.77604 9.47927C5.83507 9.31954 5.9375 9.16677 6.08333 9.02093L12.25 2.85426C12.4306 2.67371 12.651 2.5817 12.9115 2.57822C13.1719 2.57475 13.3924 2.66329 13.5729 2.84385C13.7535 3.0244 13.8438 3.24663 13.8438 3.51051C13.8438 3.7744 13.7535 3.99663 13.5729 4.17718L7.75 10.0001Z"
|
||||
fill="#CD0921"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -11,32 +11,29 @@ export default function ChevronRightIcon({
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<g id="chevron_right_small">
|
||||
<mask
|
||||
id="mask0_4140_3161"
|
||||
style={{ maskType: "alpha" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="20"
|
||||
height="20"
|
||||
>
|
||||
<rect id="Bounding box" width="20" height="20" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_4140_3161)">
|
||||
<path
|
||||
id="Vector"
|
||||
d="M10.5417 10.0001L7.3125 6.77095C7.16667 6.62512 7.09375 6.4463 7.09375 6.23449C7.09375 6.02269 7.16667 5.84039 7.3125 5.68762C7.45833 5.53484 7.63889 5.45671 7.85417 5.45324C8.06944 5.44977 8.25347 5.52442 8.40625 5.6772L12.1771 9.44803C12.2535 9.52442 12.3108 9.60949 12.349 9.70324C12.3872 9.79699 12.4063 9.89595 12.4063 10.0001C12.4063 10.1043 12.3872 10.2032 12.349 10.297C12.3108 10.3907 12.2535 10.4758 12.1771 10.5522L8.40625 14.323C8.25347 14.4758 8.06944 14.5505 7.85417 14.547C7.63889 14.5435 7.45833 14.4654 7.3125 14.3126C7.16667 14.1598 7.09375 13.9775 7.09375 13.7657C7.09375 13.5539 7.16667 13.3751 7.3125 13.2293L10.5417 10.0001Z"
|
||||
fill="#8F4350"
|
||||
/>
|
||||
</g>
|
||||
<mask
|
||||
id="mask0_2291_1656"
|
||||
style={{ maskType: "alpha" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="20"
|
||||
height="20"
|
||||
>
|
||||
<rect width="20" height="20" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_2291_1656)">
|
||||
<path
|
||||
d="M12.1042 9.99967L6.28125 4.17676C6.10069 3.9962 6.00868 3.77572 6.00521 3.5153C6.00174 3.25488 6.09028 3.0344 6.27083 2.85384C6.45139 2.67329 6.67188 2.58301 6.93229 2.58301C7.19271 2.58301 7.41319 2.67329 7.59375 2.85384L13.7708 9.02051C13.9167 9.16634 14.0191 9.31912 14.0781 9.47884C14.1372 9.63856 14.1667 9.81217 14.1667 9.99967C14.1667 10.1872 14.1372 10.3608 14.0781 10.5205C14.0191 10.6802 13.9167 10.833 13.7708 10.9788L7.60417 17.1455C7.42361 17.3261 7.20139 17.4181 6.9375 17.4215C6.67361 17.425 6.45139 17.3365 6.27083 17.1559C6.09028 16.9754 6 16.7549 6 16.4945C6 16.234 6.09028 16.0136 6.27083 15.833L12.1042 9.99967Z"
|
||||
fill="#CD0921"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
|
||||
36
components/Icons/Gift.tsx
Normal file
36
components/Icons/Gift.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function GiftIcon({ className, color, ...props }: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
id="mask0_4278_5138"
|
||||
style={{ maskType: "alpha" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<rect width="24" height="24" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_4278_5138)">
|
||||
<path
|
||||
d="M4 22V11H2V5H7.2C7.11667 4.85 7.0625 4.69167 7.0375 4.525C7.0125 4.35833 7 4.18333 7 4C7 3.16667 7.29167 2.45833 7.875 1.875C8.45833 1.29167 9.16667 1 10 1C10.3833 1 10.7417 1.07083 11.075 1.2125C11.4083 1.35417 11.7167 1.55 12 1.8C12.2833 1.53333 12.5917 1.33333 12.925 1.2C13.2583 1.06667 13.6167 1 14 1C14.8333 1 15.5417 1.29167 16.125 1.875C16.7083 2.45833 17 3.16667 17 4C17 4.18333 16.9833 4.35417 16.95 4.5125C16.9167 4.67083 16.8667 4.83333 16.8 5H22V11H20V22H4ZM14 3C13.7167 3 13.4792 3.09583 13.2875 3.2875C13.0958 3.47917 13 3.71667 13 4C13 4.28333 13.0958 4.52083 13.2875 4.7125C13.4792 4.90417 13.7167 5 14 5C14.2833 5 14.5208 4.90417 14.7125 4.7125C14.9042 4.52083 15 4.28333 15 4C15 3.71667 14.9042 3.47917 14.7125 3.2875C14.5208 3.09583 14.2833 3 14 3ZM9 4C9 4.28333 9.09583 4.52083 9.2875 4.7125C9.47917 4.90417 9.71667 5 10 5C10.2833 5 10.5208 4.90417 10.7125 4.7125C10.9042 4.52083 11 4.28333 11 4C11 3.71667 10.9042 3.47917 10.7125 3.2875C10.5208 3.09583 10.2833 3 10 3C9.71667 3 9.47917 3.09583 9.2875 3.2875C9.09583 3.47917 9 3.71667 9 4ZM4 7V9H11V7H4ZM11 20V11H6V20H11ZM13 20H18V11H13V20ZM20 9V7H13V9H20Z"
|
||||
fill="#1C1B1F"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -7,28 +7,28 @@ export default function PersonIcon({ className, color, ...props }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
id="mask0_1572_4293"
|
||||
id="mask0_69_3302"
|
||||
style={{ maskType: "alpha" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="20"
|
||||
height="20"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<rect width="20" height="20" fill="#D9D9D9" />
|
||||
<rect width="24" height="24" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_1572_4293)">
|
||||
<g mask="url(#mask0_69_3302)">
|
||||
<path
|
||||
d="M10 10C9.16667 10 8.45833 9.70833 7.875 9.125C7.29167 8.54167 7 7.83333 7 7C7 6.16667 7.29167 5.45833 7.875 4.875C8.45833 4.29167 9.16667 4 10 4C10.8333 4 11.5417 4.29167 12.125 4.875C12.7083 5.45833 13 6.16667 13 7C13 7.83333 12.7083 8.54167 12.125 9.125C11.5417 9.70833 10.8333 10 10 10ZM4 14.5V14C4 13.6806 4.08681 13.3785 4.26042 13.0938C4.43403 12.809 4.67361 12.5694 4.97917 12.375C5.74306 11.9306 6.55064 11.5903 7.40192 11.3542C8.25321 11.1181 9.11779 11 9.99567 11C10.8736 11 11.7396 11.1181 12.5938 11.3542C13.4479 11.5903 14.2569 11.9306 15.0208 12.375C15.3264 12.5556 15.566 12.7917 15.7396 13.0833C15.9132 13.375 16 13.6806 16 14V14.5C16 14.9125 15.853 15.2656 15.5591 15.5594C15.2652 15.8531 14.9119 16 14.4992 16H5.4941C5.08137 16 4.72917 15.8531 4.4375 15.5594C4.14583 15.2656 4 14.9125 4 14.5ZM5.5 14.5H14.5V14C14.5 13.9281 14.479 13.8628 14.437 13.8039C14.395 13.7451 14.3396 13.6993 14.2708 13.6667C13.6319 13.2778 12.9514 12.9861 12.2292 12.7917C11.5069 12.5972 10.7639 12.5 10 12.5C9.23611 12.5 8.49306 12.5972 7.77083 12.7917C7.04861 12.9861 6.36806 13.2778 5.72917 13.6667C5.65972 13.7222 5.60417 13.7759 5.5625 13.8276C5.52083 13.8793 5.5 13.9368 5.5 14V14.5ZM10.0044 8.5C10.4181 8.5 10.7708 8.35269 11.0625 8.05808C11.3542 7.76346 11.5 7.40929 11.5 6.99558C11.5 6.58186 11.3527 6.22917 11.0581 5.9375C10.7635 5.64583 10.4093 5.5 9.99558 5.5C9.58186 5.5 9.22917 5.64731 8.9375 5.94192C8.64583 6.23654 8.5 6.59071 8.5 7.00442C8.5 7.41814 8.64731 7.77083 8.94192 8.0625C9.23654 8.35417 9.59071 8.5 10.0044 8.5Z"
|
||||
fill="#1C1B1F"
|
||||
d="M12 12.025C10.925 12.025 10.0104 11.6479 9.25623 10.8938C8.50206 10.1396 8.12498 9.22503 8.12498 8.15002C8.12498 7.07502 8.50206 6.16044 9.25623 5.40627C10.0104 4.65211 10.925 4.27502 12 4.27502C13.075 4.27502 13.9896 4.65211 14.7437 5.40627C15.4979 6.16044 15.875 7.07502 15.875 8.15002C15.875 9.22503 15.4979 10.1396 14.7437 10.8938C13.9896 11.6479 13.075 12.025 12 12.025ZM4.22498 17.8V17.0243C4.22498 16.4748 4.36456 15.9729 4.64373 15.5188C4.92289 15.0646 5.30016 14.7166 5.77553 14.4748C6.79183 13.9749 7.81873 13.5979 8.85623 13.3438C9.89373 13.0896 10.9416 12.9625 12 12.9625C13.0583 12.9625 14.1062 13.0896 15.1437 13.3438C16.1812 13.5979 17.2081 13.9749 18.2244 14.4748C18.6998 14.7166 19.0771 15.0646 19.3562 15.5188C19.6354 15.9729 19.775 16.4748 19.775 17.0243V17.8C19.775 18.3157 19.5914 18.7571 19.2242 19.1243C18.857 19.4914 18.4156 19.675 17.9 19.675H6.09998C5.58434 19.675 5.14293 19.4914 4.77575 19.1243C4.40857 18.7571 4.22498 18.3157 4.22498 17.8ZM6.09998 17.8H17.9V17.025C17.9 16.8385 17.8541 16.669 17.7625 16.5164C17.6708 16.3638 17.5458 16.2459 17.3875 16.1625C16.4958 15.7209 15.6027 15.3896 14.7081 15.1688C13.8135 14.9479 12.9108 14.8375 12 14.8375C11.0833 14.8375 10.1791 14.9479 9.28748 15.1688C8.39581 15.3896 7.50414 15.7209 6.61248 16.1625C6.45414 16.2459 6.32914 16.3638 6.23748 16.5164C6.14581 16.669 6.09998 16.8385 6.09998 17.025V17.8ZM12 10.15C12.55 10.15 13.0208 9.95419 13.4125 9.56252C13.8041 9.17086 14 8.70003 14 8.15002C14 7.60002 13.8041 7.12919 13.4125 6.73752C13.0208 6.34586 12.55 6.15002 12 6.15002C11.45 6.15002 10.9791 6.34586 10.5875 6.73752C10.1958 7.12919 9.99998 7.60002 9.99998 8.15002C9.99998 8.70003 10.1958 9.17086 10.5875 9.56252C10.9791 9.95419 11.45 10.15 12 10.15Z"
|
||||
fill="#26201E"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
36
components/Icons/Search.tsx
Normal file
36
components/Icons/Search.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function SearchIcon({ className, color, ...props }: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
id="mask0_69_3280"
|
||||
style={{ maskType: "alpha" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<rect width="24" height="24" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_69_3280)">
|
||||
<path
|
||||
d="M9.54147 15.85C7.77261 15.85 6.27557 15.2379 5.05035 14.0137C3.82513 12.7894 3.21252 11.2936 3.21252 9.52616C3.21252 7.75873 3.82464 6.26251 5.04887 5.03751C6.27311 3.81251 7.76894 3.20001 9.53637 3.20001C11.3038 3.20001 12.8 3.81262 14.025 5.03784C15.25 6.26305 15.8625 7.7601 15.8625 9.52896C15.8625 10.243 15.75 10.9229 15.525 11.5688C15.3 12.2146 14.9917 12.7833 14.6 13.275L20.1404 18.8035C20.3218 18.9845 20.4125 19.2042 20.4125 19.4625C20.4125 19.7208 20.3209 19.9417 20.1375 20.125C19.9542 20.3083 19.7334 20.4 19.475 20.4C19.2167 20.4 18.9969 20.3094 18.8156 20.1283L13.279 14.5875C12.7847 14.9875 12.2167 15.2979 11.575 15.5188C10.9334 15.7396 10.2555 15.85 9.54147 15.85ZM9.53752 13.975C10.7792 13.975 11.8313 13.5438 12.6938 12.6813C13.5563 11.8188 13.9875 10.7667 13.9875 9.52501C13.9875 8.28335 13.5563 7.23126 12.6938 6.36876C11.8313 5.50626 10.7792 5.07501 9.53752 5.07501C8.29586 5.07501 7.24377 5.50626 6.38127 6.36876C5.51877 7.23126 5.08752 8.28335 5.08752 9.52501C5.08752 10.7667 5.51877 11.8188 6.38127 12.6813C7.24377 13.5438 8.29586 13.975 9.53752 13.975Z"
|
||||
fill="#26201E"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
36
components/Icons/Service.tsx
Normal file
36
components/Icons/Service.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function ServiceIcon({ className, color, ...props }: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
id="mask0_2291_1682"
|
||||
style={{ maskType: "alpha" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="0"
|
||||
width="20"
|
||||
height="20"
|
||||
>
|
||||
<rect width="20" height="20" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_2291_1682)">
|
||||
<path
|
||||
d="M15.792 18.3337C14.0559 18.3337 12.3406 17.9552 10.6462 17.1982C8.95171 16.4413 7.41005 15.3684 6.02116 13.9795C4.63227 12.5906 3.55935 11.0489 2.80241 9.35449C2.04546 7.66005 1.66699 5.94477 1.66699 4.20866C1.66699 3.95866 1.75033 3.75033 1.91699 3.58366C2.08366 3.41699 2.29199 3.33366 2.54199 3.33366H5.91699C6.11144 3.33366 6.28505 3.39963 6.43783 3.53158C6.5906 3.66352 6.68088 3.81977 6.70866 4.00033L7.25033 6.91699C7.2781 7.13921 7.27116 7.32671 7.22949 7.47949C7.18783 7.63227 7.11144 7.76421 7.00033 7.87533L4.97949 9.91699C5.25727 10.4309 5.58713 10.9274 5.96908 11.4066C6.35102 11.8857 6.77116 12.3475 7.22949 12.792C7.66005 13.2225 8.11144 13.6219 8.58366 13.9899C9.05588 14.358 9.55588 14.6948 10.0837 15.0003L12.042 13.042C12.167 12.917 12.3302 12.8232 12.5316 12.7607C12.733 12.6982 12.9309 12.6809 13.1253 12.7087L16.0003 13.292C16.1948 13.3475 16.3545 13.4482 16.4795 13.5941C16.6045 13.7399 16.667 13.9031 16.667 14.0837V17.4587C16.667 17.7087 16.5837 17.917 16.417 18.0837C16.2503 18.2503 16.042 18.3337 15.792 18.3337ZM4.18783 8.33366L5.56283 6.95866L5.20866 5.00033H3.35449C3.42394 5.56977 3.52116 6.13227 3.64616 6.68783C3.77116 7.24338 3.95171 7.79199 4.18783 8.33366ZM11.6462 15.792C12.1878 16.0281 12.7399 16.2156 13.3024 16.3545C13.8649 16.4934 14.4309 16.5837 15.0003 16.6253V14.792L13.042 14.3962L11.6462 15.792ZM14.167 10.0003C13.0142 10.0003 12.0316 9.59408 11.2191 8.78158C10.4066 7.96908 10.0003 6.98644 10.0003 5.83366C10.0003 4.68088 10.4066 3.69824 11.2191 2.88574C12.0316 2.07324 13.0142 1.66699 14.167 1.66699C15.3198 1.66699 16.3024 2.07324 17.1149 2.88574C17.9274 3.69824 18.3337 4.68088 18.3337 5.83366C18.3337 6.98644 17.9274 7.96908 17.1149 8.78158C16.3024 9.59408 15.3198 10.0003 14.167 10.0003ZM13.7503 8.33366H14.5837V5.00033H13.7503V8.33366ZM14.167 4.16699C14.2781 4.16699 14.3753 4.12533 14.4587 4.04199C14.542 3.95866 14.5837 3.86144 14.5837 3.75033C14.5837 3.63921 14.542 3.54199 14.4587 3.45866C14.3753 3.37533 14.2781 3.33366 14.167 3.33366C14.0559 3.33366 13.9587 3.37533 13.8753 3.45866C13.792 3.54199 13.7503 3.63921 13.7503 3.75033C13.7503 3.86144 13.792 3.95866 13.8753 4.04199C13.9587 4.12533 14.0559 4.16699 14.167 4.16699Z"
|
||||
fill="#4D001B"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
CheckCircleIcon,
|
||||
CheckIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
CloseIcon,
|
||||
CloseLarge,
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
ElectricBikeIcon,
|
||||
EmailIcon,
|
||||
FitnessIcon,
|
||||
GiftIcon,
|
||||
GlobeIcon,
|
||||
HouseIcon,
|
||||
ImageIcon,
|
||||
@@ -39,6 +41,8 @@ import {
|
||||
PlusCircleIcon,
|
||||
RestaurantIcon,
|
||||
SaunaIcon,
|
||||
SearchIcon,
|
||||
ServiceIcon,
|
||||
TshirtWashIcon,
|
||||
WarningTriangle,
|
||||
WifiIcon,
|
||||
@@ -72,6 +76,8 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
|
||||
return CheckCircleIcon
|
||||
case IconName.ChevronDown:
|
||||
return ChevronDownIcon
|
||||
case IconName.ChevronLeft:
|
||||
return ChevronLeftIcon
|
||||
case IconName.ChevronRight:
|
||||
return ChevronRightIcon
|
||||
case IconName.Close:
|
||||
@@ -92,6 +98,8 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
|
||||
return FacebookIcon
|
||||
case IconName.Fitness:
|
||||
return FitnessIcon
|
||||
case IconName.Gift:
|
||||
return GiftIcon
|
||||
case IconName.Globe:
|
||||
return GlobeIcon
|
||||
case IconName.House:
|
||||
@@ -122,6 +130,10 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
|
||||
return RestaurantIcon
|
||||
case IconName.Sauna:
|
||||
return SaunaIcon
|
||||
case IconName.Search:
|
||||
return SearchIcon
|
||||
case IconName.Service:
|
||||
return ServiceIcon
|
||||
case IconName.Tripadvisor:
|
||||
return TripAdvisorIcon
|
||||
case IconName.TshirtWash:
|
||||
|
||||
@@ -9,6 +9,7 @@ export { default as CellphoneIcon } from "./Cellphone"
|
||||
export { default as CheckIcon } from "./Check"
|
||||
export { default as CheckCircleIcon } from "./CheckCircle"
|
||||
export { default as ChevronDownIcon } from "./ChevronDown"
|
||||
export { default as ChevronLeftIcon } from "./ChevronLeft"
|
||||
export { default as ChevronRightIcon } from "./ChevronRight"
|
||||
export { default as CloseIcon } from "./Close"
|
||||
export { default as CloseLarge } from "./CloseLarge"
|
||||
@@ -21,6 +22,7 @@ export { default as DoorOpenIcon } from "./DoorOpen"
|
||||
export { default as ElectricBikeIcon } from "./ElectricBike"
|
||||
export { default as EmailIcon } from "./Email"
|
||||
export { default as FitnessIcon } from "./Fitness"
|
||||
export { default as GiftIcon } from "./Gift"
|
||||
export { default as GlobeIcon } from "./Globe"
|
||||
export { default as HouseIcon } from "./House"
|
||||
export { default as ImageIcon } from "./Image"
|
||||
@@ -37,6 +39,8 @@ export { default as PriceTagIcon } from "./PriceTag"
|
||||
export { default as RestaurantIcon } from "./Restaurant"
|
||||
export { default as SaunaIcon } from "./Sauna"
|
||||
export { default as ScandicLogoIcon } from "./ScandicLogo"
|
||||
export { default as SearchIcon } from "./Search"
|
||||
export { default as ServiceIcon } from "./Service"
|
||||
export { default as TshirtWashIcon } from "./TshirtWash"
|
||||
export { default as WarningTriangle } from "./WarningTriangle"
|
||||
export { default as WifiIcon } from "./Wifi"
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Lang, languages } from "@/constants/languages"
|
||||
import useDropdownStore from "@/stores/main-menu"
|
||||
|
||||
import { CheckIcon, ChevronLeftIcon } from "@/components/Icons"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { useTrapFocus } from "@/hooks/useTrapFocus"
|
||||
|
||||
import styles from "./languageSwitcherContent.module.css"
|
||||
|
||||
import type { LanguageSwitcherProps } from "@/types/components/languageSwitcher/languageSwitcher"
|
||||
|
||||
export default function LanguageSwitcherContent({
|
||||
urls,
|
||||
type,
|
||||
}: LanguageSwitcherProps) {
|
||||
const intl = useIntl()
|
||||
const currentLanguage = useLang()
|
||||
const { toggleLanguageSwitcher } = useDropdownStore()
|
||||
const languageSwitcherRef = useTrapFocus()
|
||||
const urlKeys = Object.keys(urls) as Lang[]
|
||||
|
||||
return (
|
||||
<div className={styles.languageSwitcherContent} ref={languageSwitcherRef}>
|
||||
{type === "mobileHeader" ? (
|
||||
<div className={styles.backWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.backButton}
|
||||
onClick={toggleLanguageSwitcher}
|
||||
>
|
||||
<ChevronLeftIcon color="red" />
|
||||
<Subtitle type="one">Main Menu</Subtitle>
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className={styles.languageWrapper}>
|
||||
<Subtitle className={styles.subtitle} type="two">
|
||||
{intl.formatMessage({ id: "Select your language" })}
|
||||
</Subtitle>
|
||||
<ul className={styles.list}>
|
||||
{urlKeys.map((key) => {
|
||||
const url = urls[key]?.url
|
||||
const isActive = currentLanguage === key
|
||||
if (url) {
|
||||
return (
|
||||
<li key={key}>
|
||||
<Link
|
||||
className={`${styles.link} ${isActive ? styles.active : ""}`}
|
||||
href={url}
|
||||
>
|
||||
{languages[key]}
|
||||
{isActive ? <CheckIcon color="burgundy" /> : null}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
.backWrapper {
|
||||
background-color: var(--Base-Surface-Secondary-light-Normal);
|
||||
padding: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.backButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: var(--Base-Text-High-contrast);
|
||||
font-family: var(--typography-Subtitle-1-fontFamily);
|
||||
font-weight: var(--typography-Subtitle-1-fontWeight);
|
||||
font-size: var(--typography-Subtitle-1-Mobile-fontSize);
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.languageWrapper {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x3);
|
||||
padding: var(--Spacing-x3) var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-family: var(--typography-Subtitle-2-fontFamily);
|
||||
font-size: var(--typography-Subtitle-2-Mobile-fontSize);
|
||||
font-weight: var(--typography-Subtitle-2-fontWeight);
|
||||
color: var(--Base-Text-High-contrast);
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: var(--Scandic-Brand-Burgundy);
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
font-size: var(--typography-Body-Regular-fontSize);
|
||||
line-height: var(--typography-Body-Regular-lineHeight);
|
||||
letter-spacing: var(--typography-Body-Regular-letterSpacing);
|
||||
padding: var(--Spacing-x1);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
|
||||
.link.active,
|
||||
.link:hover {
|
||||
background-color: var(--Base-Surface-Primary-light-Hover-alt);
|
||||
font-weight: var(--typography-Body-Bold-fontWeight);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.backWrapper,
|
||||
.backButton {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.languageWrapper {
|
||||
padding: var(--Spacing-x2) var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.link.active:not(:hover) {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
63
components/LanguageSwitcher/index.tsx
Normal file
63
components/LanguageSwitcher/index.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { languages } from "@/constants/languages"
|
||||
import useDropdownStore from "@/stores/main-menu"
|
||||
|
||||
import { ChevronDownIcon, GlobeIcon } from "@/components/Icons"
|
||||
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import LanguageSwitcherContent from "./LanguageSwitcherContent"
|
||||
|
||||
import styles from "./languageSwitcher.module.css"
|
||||
|
||||
import type { LanguageSwitcherProps } from "@/types/components/languageSwitcher/languageSwitcher"
|
||||
|
||||
export default function LanguageSwitcher({
|
||||
urls,
|
||||
type,
|
||||
}: LanguageSwitcherProps) {
|
||||
const intl = useIntl()
|
||||
const currentLanguage = useLang()
|
||||
const { toggleLanguageSwitcher, isLanguageSwitcherOpen } = useDropdownStore()
|
||||
|
||||
useHandleKeyUp((event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && isLanguageSwitcherOpen) {
|
||||
toggleLanguageSwitcher()
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={styles.languageSwitcher}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.button}
|
||||
aria-label={intl.formatMessage({
|
||||
id: isLanguageSwitcherOpen
|
||||
? "Close language menu"
|
||||
: "Open language menu",
|
||||
})}
|
||||
onClick={toggleLanguageSwitcher}
|
||||
>
|
||||
<GlobeIcon width={20} height={20} color="burgundy" />
|
||||
<span>{languages[currentLanguage]}</span>
|
||||
<ChevronDownIcon
|
||||
className={`${styles.chevron} ${isLanguageSwitcherOpen ? styles.isExpanded : ""}`}
|
||||
width={20}
|
||||
height={20}
|
||||
color="burgundy"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<div
|
||||
className={`${styles.dropdown} ${isLanguageSwitcherOpen ? styles.isExpanded : ""}`}
|
||||
>
|
||||
{isLanguageSwitcherOpen ? (
|
||||
<LanguageSwitcherContent urls={urls} type={type} />
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
76
components/LanguageSwitcher/languageSwitcher.module.css
Normal file
76
components/LanguageSwitcher/languageSwitcher.module.css
Normal file
@@ -0,0 +1,76 @@
|
||||
.button {
|
||||
background-color: transparent;
|
||||
color: var(--Base-Text-High-contrast);
|
||||
font-family: var(--typography-Caption-Regular-fontFamily);
|
||||
font-size: var(--typography-Caption-Regular-fontSize);
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, max-content) 1fr;
|
||||
gap: var(--Spacing-x1);
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chevron {
|
||||
justify-self: end;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.chevron.isExpanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: fixed;
|
||||
top: var(--main-menu-mobile-height);
|
||||
right: -100vw;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
transition: right 0.3s;
|
||||
z-index: var(--menu-overlay-z-index);
|
||||
}
|
||||
|
||||
.dropdown.isExpanded {
|
||||
display: block;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.languageSwitcher {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: absolute;
|
||||
top: 2.25rem;
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
box-shadow: 0px 0px 14px 6px #0000001a;
|
||||
display: none;
|
||||
min-width: 12.5rem;
|
||||
z-index: 1;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
/* Triangle above dropdown */
|
||||
.dropdown::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -1.25rem;
|
||||
right: 2.4rem;
|
||||
transform: rotate(180deg);
|
||||
border-width: 0.75rem;
|
||||
border-style: solid;
|
||||
border-color: var(--Base-Surface-Primary-light-Normal) transparent
|
||||
transparent transparent;
|
||||
}
|
||||
|
||||
.button {
|
||||
grid-template-columns: repeat(3, max-content);
|
||||
font-size: var(--typography-Body-Bold-fontSize);
|
||||
font-family: var(--typography-Body-Bold-fontFamily);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
}
|
||||
|
||||
.mobileGallery {
|
||||
margin-top: var(--mobile-site-header-height);
|
||||
margin-top: var(--current-mobile-site-header-height);
|
||||
height: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -112,7 +112,7 @@
|
||||
}
|
||||
|
||||
.fullViewContainer {
|
||||
margin-top: var(--mobile-site-header-height);
|
||||
margin-top: var(--current-mobile-site-header-height);
|
||||
background-color: var(--UI-Text-High-contrast);
|
||||
height: 100%;
|
||||
padding: var(--Spacing-x2);
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function CardsGrid({
|
||||
<SectionContainer>
|
||||
<SectionHeader
|
||||
title={cards_grid.title}
|
||||
subtitle={cards_grid.preamble}
|
||||
preamble={cards_grid.preamble}
|
||||
topTitle={firstItem}
|
||||
/>
|
||||
<Grids.Stackable>
|
||||
|
||||
@@ -53,7 +53,7 @@ export default function DynamicContent({
|
||||
) : displayHeader ? (
|
||||
<SectionHeader
|
||||
link={dynamicContent.link}
|
||||
subtitle={dynamicContent.subtitle}
|
||||
preamble={dynamicContent.subtitle}
|
||||
title={dynamicContent.title}
|
||||
topTitle={firstItem}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.container {
|
||||
max-width: var(--max-width, 1140px);
|
||||
max-width: var(--current-max-width, 1140px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export default async function CurrentBenefitsBlock({
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader title={title} link={link} subtitle={subtitle} />
|
||||
<SectionHeader title={title} link={link} preamble={subtitle} />
|
||||
<Grids.Stackable>
|
||||
{currentLevel.benefits.map((benefit, idx) => (
|
||||
<article className={styles.card} key={`${currentLevel}-${idx}`}>
|
||||
|
||||
@@ -39,7 +39,7 @@ export default async function NextLevelBenefitsBlock({
|
||||
// TODO: how to handle different count of unlockable benefits?
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader title={title} subtitle={subtitle} link={link} />
|
||||
<SectionHeader title={title} preamble={subtitle} link={link} />
|
||||
<Grids.Stackable columns={2}>
|
||||
{nextLevel.benefits.map((benefit) => (
|
||||
<article key={benefit.title} className={styles.card}>
|
||||
|
||||
@@ -26,7 +26,7 @@ export default async function Overview({
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader link={link} subtitle={subtitle} title={title} topTitle />
|
||||
<SectionHeader link={link} preamble={subtitle} title={title} topTitle />
|
||||
<Hero color="red">
|
||||
<Friend user={user} color="burgundy" />
|
||||
<Divider className={styles.divider} color="peach" />
|
||||
|
||||
@@ -13,7 +13,7 @@ export default async function EarnAndBurn({
|
||||
}: AccountPageComponentProps) {
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader title={title} link={link} subtitle={subtitle} />
|
||||
<SectionHeader title={title} link={link} preamble={subtitle} />
|
||||
<JourneyTable />
|
||||
<SectionLink link={link} variant="mobile" />
|
||||
</SectionContainer>
|
||||
|
||||
@@ -25,7 +25,7 @@ export default async function PointsOverview({
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader link={link} subtitle={subtitle} title={title} topTitle />
|
||||
<SectionHeader link={link} preamble={subtitle} title={title} topTitle />
|
||||
<Hero color="burgundy">
|
||||
<Friend user={user} color="red" />
|
||||
<Divider className={styles.divider} color="peach" />
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function Shortcuts({
|
||||
}: ShortcutsProps) {
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader subtitle={subtitle} title={title} topTitle={firstItem} />
|
||||
<SectionHeader preamble={subtitle} title={title} topTitle={firstItem} />
|
||||
<section className={styles.links}>
|
||||
{shortcuts.map((shortcut) => (
|
||||
<Link
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async function PreviousStays({
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader title={title} subtitle={subtitle} link={link} />
|
||||
<SectionHeader title={title} preamble={subtitle} link={link} />
|
||||
<ClientPreviousStays initialPreviousStays={initialPreviousStays} />
|
||||
<SectionLink link={link} variant="mobile" />
|
||||
</SectionContainer>
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async function SoonestStays({
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<SectionHeader title={title} subtitle={subtitle} link={link} />
|
||||
<SectionHeader title={title} preamble={subtitle} link={link} />
|
||||
{response.data.length ? (
|
||||
<Grids.Stackable>
|
||||
{response.data.map((stay) => (
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async function UpcomingStays({
|
||||
|
||||
return (
|
||||
<SectionContainer className={styles.container}>
|
||||
<SectionHeader title={title} subtitle={subtitle} link={link} />
|
||||
<SectionHeader title={title} preamble={subtitle} link={link} />
|
||||
{initialUpcomingStays?.data.length ? (
|
||||
<ClientUpcomingStays initialUpcomingStays={initialUpcomingStays} />
|
||||
) : (
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
.title,
|
||||
.subtitle {
|
||||
.preamble {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
grid-column: 1 / 2;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
.preamble {
|
||||
grid-column: 1 / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import Preamble from "@/components/TempDesignSystem/Text/Preamble"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
|
||||
import SectionLink from "../Link"
|
||||
@@ -9,7 +9,7 @@ import type { HeaderProps } from "@/types/components/myPages/header"
|
||||
|
||||
export default function SectionHeader({
|
||||
link,
|
||||
subtitle,
|
||||
preamble,
|
||||
title,
|
||||
topTitle = false,
|
||||
textTransform,
|
||||
@@ -24,7 +24,7 @@ export default function SectionHeader({
|
||||
>
|
||||
{title}
|
||||
</Title>
|
||||
{subtitle && <Subtitle className={styles.subtitle}>{subtitle}</Subtitle>}
|
||||
{preamble && <Preamble className={styles.preamble}>{preamble}</Preamble>}
|
||||
<SectionLink link={link} variant="desktop" />
|
||||
</header>
|
||||
)
|
||||
|
||||
@@ -20,8 +20,8 @@ export default function SectionLink({ link, variant }: SectionLinkProps) {
|
||||
href={link.href}
|
||||
variant="underscored"
|
||||
>
|
||||
<ArrowRight color="burgundy" className={styles.icon} />
|
||||
{link.text}
|
||||
<ArrowRight color="burgundy" className={styles.icon} />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,26 +41,18 @@
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
font-size: var(--typography-Body-Regular-fontSize);
|
||||
line-height: var(--typography-Body-Regular-lineHeight);
|
||||
letter-spacing: 0.096px;
|
||||
padding: var(--Spacing-x1) var(--Spacing-x1) var(--Spacing-x1)
|
||||
var(--Spacing-x-one-and-half);
|
||||
width: 100%;
|
||||
letter-spacing: var(--typography-Body-Regular-letterSpacing);
|
||||
padding: var(--Spacing-x1);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
gap: 4px;
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.myPageMobileDropdown.active {
|
||||
background-color: var(--Scandic-Brand-Pale-Peach);
|
||||
.myPageMobileDropdown:hover {
|
||||
background-color: var(--Base-Surface-Primary-light-Hover-alt);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
font-family: var(--typography-Body-Underline-fontFamily);
|
||||
font-size: var(--typography-Body-Underline-fontSize);
|
||||
font-weight: var(--typography-Body-Underline-fontWeight);
|
||||
letter-spacing: var(--typography-Body-Underline-letterSpacing);
|
||||
line-height: var(--typography-Body-Underline-lineHeight);
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
@@ -131,7 +123,7 @@
|
||||
}
|
||||
|
||||
.peach80 {
|
||||
color: var(--Primary-Light-On-Surface-Accent);
|
||||
color: var(--Base-Text-Medium-contrast);
|
||||
}
|
||||
|
||||
.red {
|
||||
@@ -140,12 +132,12 @@
|
||||
|
||||
.peach80:hover,
|
||||
.peach80:active {
|
||||
color: var(--Primary-Light-On-Surface-Hover);
|
||||
color: var(--Base-Text-High-contrast);
|
||||
}
|
||||
|
||||
.peach80:hover *,
|
||||
.peach80:active * {
|
||||
fill: var(--Primary-Light-On-Surface-Hover);
|
||||
fill: var(--Base-Text-High-contrast);
|
||||
}
|
||||
|
||||
.white {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.sidePeek {
|
||||
position: fixed;
|
||||
top: var(--mobile-site-header-height);
|
||||
top: var(--current-mobile-site-header-height);
|
||||
right: auto;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: calc(100vh - var(--mobile-site-header-height));
|
||||
height: calc(100vh - var(--current-mobile-site-header-height));
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
z-index: 100;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.85);
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: var(--mobile-site-header-height);
|
||||
top: var(--current-mobile-site-header-height);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@@ -46,7 +46,7 @@
|
||||
}
|
||||
|
||||
to {
|
||||
top: var(--mobile-site-header-height);
|
||||
top: var(--current-mobile-site-header-height);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const config = {
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
color: "burgundy",
|
||||
color: "black",
|
||||
textAlign: "left",
|
||||
textTransform: "regular",
|
||||
type: "one",
|
||||
|
||||
@@ -18,7 +18,6 @@ import "./commands"
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
||||
import { mount } from "cypress/react18"
|
||||
|
||||
// Augment the Cypress namespace to include type definitions for
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client"
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect } from "react"
|
||||
|
||||
export function useHandleKeyPress(callback: (event: KeyboardEvent) => void) {
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', callback);
|
||||
window.addEventListener("keydown", callback)
|
||||
return () => {
|
||||
window.removeEventListener('keydown', callback);
|
||||
};
|
||||
}, [callback]);
|
||||
}
|
||||
window.removeEventListener("keydown", callback)
|
||||
}
|
||||
}, [callback])
|
||||
}
|
||||
|
||||
12
hooks/useHandleKeyUp.ts
Normal file
12
hooks/useHandleKeyUp.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect } from "react"
|
||||
|
||||
export function useHandleKeyUp(callback: (event: KeyboardEvent) => void) {
|
||||
useEffect(() => {
|
||||
window.addEventListener("keyup", callback)
|
||||
return () => {
|
||||
window.removeEventListener("keyup", callback)
|
||||
}
|
||||
}, [callback])
|
||||
}
|
||||
83
hooks/useTrapFocus.ts
Normal file
83
hooks/useTrapFocus.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
"use client"
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
|
||||
import { useHandleKeyPress } from "@/hooks/useHandleKeyPress"
|
||||
import findTabbableDescendants from "@/utils/tabbable"
|
||||
|
||||
const TAB_KEY = "Tab"
|
||||
const optionsDefault = { focusOnRender: true, returnFocus: true }
|
||||
type OptionsType = {
|
||||
focusOnRender?: boolean
|
||||
returnFocus?: boolean
|
||||
}
|
||||
export function useTrapFocus(opts?: OptionsType) {
|
||||
const options = opts ? { ...optionsDefault, ...opts } : optionsDefault
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const previouseFocusedElement = useRef<HTMLElement>(
|
||||
document.activeElement as HTMLElement
|
||||
)
|
||||
const [tabbableElements, setTabbableElements] = useState<HTMLElement[]>([])
|
||||
// Handle initial focus of the referenced element, and return focus to previously focused element on cleanup
|
||||
// and find all the tabbable elements in the referenced element
|
||||
|
||||
useEffect(() => {
|
||||
const { current } = ref
|
||||
if (current) {
|
||||
const focusableChildNodes = findTabbableDescendants(current)
|
||||
if (options.focusOnRender) {
|
||||
current.focus()
|
||||
}
|
||||
|
||||
setTabbableElements(focusableChildNodes)
|
||||
}
|
||||
return () => {
|
||||
const { current } = previouseFocusedElement
|
||||
if (current instanceof HTMLElement && options.returnFocus) {
|
||||
current.focus()
|
||||
}
|
||||
}
|
||||
}, [options.focusOnRender, options.returnFocus, ref, setTabbableElements])
|
||||
|
||||
const handleUserKeyPress = useCallback(
|
||||
(event: KeyboardEvent) => {
|
||||
const { code, shiftKey } = event
|
||||
const first = tabbableElements[0]
|
||||
const last = tabbableElements[tabbableElements.length - 1]
|
||||
|
||||
const currentActiveElement = document.activeElement
|
||||
// Scope current tabs to current root element
|
||||
if (isWithinCurrentElementScope([...tabbableElements, ref.current])) {
|
||||
if (code === TAB_KEY) {
|
||||
if (
|
||||
currentActiveElement === first ||
|
||||
currentActiveElement === ref.current
|
||||
) {
|
||||
// move focus to last element if shift+tab while currently focusing the first tabbable element
|
||||
if (shiftKey) {
|
||||
event.preventDefault()
|
||||
last.focus()
|
||||
}
|
||||
}
|
||||
if (currentActiveElement === last) {
|
||||
// move focus back to first if tabbing while currently focusing the last tabbable element
|
||||
if (!shiftKey) {
|
||||
event.preventDefault()
|
||||
first.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[ref, tabbableElements]
|
||||
)
|
||||
useHandleKeyPress(handleUserKeyPress)
|
||||
|
||||
return ref
|
||||
}
|
||||
function isWithinCurrentElementScope(
|
||||
elementList: (HTMLInputElement | Element | null)[]
|
||||
) {
|
||||
const currentActiveElement = document.activeElement
|
||||
return elementList.includes(currentActiveElement)
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
"As our Close Friend": "Som vores nære ven",
|
||||
"At latest": "Senest",
|
||||
"At the hotel": "På hotellet",
|
||||
"Back to scandichotels.com": "Tilbage til scandichotels.com",
|
||||
"Bed type": "Seng type",
|
||||
"Book": "Book",
|
||||
"Book reward night": "Book bonusnat",
|
||||
@@ -34,6 +35,9 @@
|
||||
"City/State": "By/Stat",
|
||||
"Click here to log in": "Klik her for at logge ind",
|
||||
"Close": "Tæt",
|
||||
"Close language menu": "Luk sprogmenu",
|
||||
"Close menu": "Luk menu",
|
||||
"Close my pages menu": "Luk mine sider menu",
|
||||
"Coming up": "Er lige om hjørnet",
|
||||
"Compare all levels": "Sammenlign alle niveauer",
|
||||
"Contact us": "Kontakt os",
|
||||
@@ -43,6 +47,7 @@
|
||||
"Country code": "Landekode",
|
||||
"Credit card deleted successfully": "Kreditkort blev slettet",
|
||||
"Current password": "Nuværende kodeord",
|
||||
"Customer service": "Kundeservice",
|
||||
"Date of Birth": "Fødselsdato",
|
||||
"Day": "Dag",
|
||||
"Description": "Beskrivelse",
|
||||
@@ -64,6 +69,7 @@
|
||||
"from your member profile?": "fra din medlemsprofil?",
|
||||
"Get inspired": "Bliv inspireret",
|
||||
"Go back to overview": "Gå tilbage til oversigten",
|
||||
"Hi": "Hei",
|
||||
"Highest level": "Højeste niveau",
|
||||
"Hotel facilities": "Hotel faciliteter",
|
||||
"Hotel surroundings": "Hotel omgivelser",
|
||||
@@ -82,6 +88,7 @@
|
||||
"Level up to unlock": "Stig i niveau for at låse op",
|
||||
"Log in": "Log på",
|
||||
"Log in here": "Log ind her",
|
||||
"Log in/Join": "Log på/Tilmeld dig",
|
||||
"Log out": "Log ud",
|
||||
"Manage preferences": "Administrer præferencer",
|
||||
"Meetings & Conferences": "Møder & Konferencer",
|
||||
@@ -91,12 +98,14 @@
|
||||
"Members": "Medlemmer",
|
||||
"Membership cards": "Medlemskort",
|
||||
"Membership ID": "Medlems-id",
|
||||
"Menu": "Menu",
|
||||
"Modify": "Ændre",
|
||||
"Month": "Måned",
|
||||
"My communication preferences": "Mine kommunikationspræferencer",
|
||||
"My credit cards": "Mine kreditkort",
|
||||
"My membership cards": "Mine medlemskort",
|
||||
"My pages": "Mine sider",
|
||||
"My pages menu": "Mine sider menu",
|
||||
"My payment cards": "Mine betalingskort",
|
||||
"My wishes": "Mine ønsker",
|
||||
"New password": "Nyt kodeord",
|
||||
"Next": "Næste",
|
||||
@@ -114,6 +123,9 @@
|
||||
"number": "nummer",
|
||||
"On your journey": "På din rejse",
|
||||
"Open": "Åben",
|
||||
"Open language menu": "Åbn sprogmenuen",
|
||||
"Open menu": "Åbn menuen",
|
||||
"Open my pages menu": "Åbn mine sider menuen",
|
||||
"or": "eller",
|
||||
"Overview": "Oversigt",
|
||||
"Password": "Adgangskode",
|
||||
@@ -125,7 +137,7 @@
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer",
|
||||
"Points": "Point",
|
||||
"points": "Point",
|
||||
"points": "point",
|
||||
"Points being calculated": "Point udregnes",
|
||||
"Points earned prior to May 1, 2021": "Point optjent inden 1. maj 2021",
|
||||
"Points may take up to 10 days to be displayed.": "Det kan tage op til 10 dage at få vist point.",
|
||||
@@ -152,6 +164,7 @@
|
||||
"Select country of residence": "Vælg bopælsland",
|
||||
"Select date of birth": "Vælg fødselsdato",
|
||||
"Select language": "Vælg sprog",
|
||||
"Select your language": "Vælg dit sprog",
|
||||
"Show all amenities": "Vis alle faciliteter",
|
||||
"Show less": "Vis mindre",
|
||||
"Show map": "Vis kort",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"As our Close Friend": "Als unser enger Freund",
|
||||
"At latest": "Spätestens",
|
||||
"At the hotel": "Im Hotel",
|
||||
"Back to scandichotels.com": "Zurück zu scandichotels.com",
|
||||
"Bed type": "Bettentyp",
|
||||
"Book": "Buchen",
|
||||
"Book reward night": "Bonusnacht buchen",
|
||||
@@ -33,6 +34,9 @@
|
||||
"City/State": "Stadt/Zustand",
|
||||
"Click here to log in": "Klicken Sie hier, um sich einzuloggen",
|
||||
"Close": "Schließen",
|
||||
"Close language menu": "Sprachmenü schließen",
|
||||
"Close menu": "Menü schließen",
|
||||
"Close my pages menu": "Meine Seiten Menü schließen",
|
||||
"Coming up": "Demnächst",
|
||||
"Compare all levels": "Vergleichen Sie alle Levels",
|
||||
"Contact us": "Kontaktieren Sie uns",
|
||||
@@ -42,6 +46,7 @@
|
||||
"Country code": "Landesvorwahl",
|
||||
"Credit card deleted successfully": "Kreditkarte erfolgreich gelöscht",
|
||||
"Current password": "Aktuelles Passwort",
|
||||
"Customer service": "Kundendienst",
|
||||
"Date of Birth": "Geburtsdatum",
|
||||
"Day": "Tag",
|
||||
"Description": "Beschreibung",
|
||||
@@ -63,6 +68,7 @@
|
||||
"from your member profile?": "wirklich aus Ihrem Mitgliedsprofil entfernen?",
|
||||
"Get inspired": "Lassen Sie sich inspieren",
|
||||
"Go back to overview": "Zurück zur Übersicht",
|
||||
"Hi": "Hallo",
|
||||
"Highest level": "Höchstes Level",
|
||||
"Hotel facilities": "Hotel-Infos",
|
||||
"Hotel surroundings": "Umgebung des Hotels",
|
||||
@@ -81,6 +87,7 @@
|
||||
"Level up to unlock": "Zum Freischalten aufsteigen",
|
||||
"Log in": "Anmeldung",
|
||||
"Log in here": "Hier einloggen",
|
||||
"Log in/Join": "Log in/Anmelden",
|
||||
"Log out": "Ausloggen",
|
||||
"Manage preferences": "Verwalten von Voreinstellungen",
|
||||
"Membership ID copied to clipboard": "Mitglieds-ID in die Zwischenablage kopiert",
|
||||
@@ -89,12 +96,14 @@
|
||||
"Members": "Mitglieder",
|
||||
"Membership cards": "Mitgliedskarten",
|
||||
"Membership ID": "Mitglieds-ID",
|
||||
"Menu": "Menu",
|
||||
"Modify": "Ändern",
|
||||
"Month": "Monat",
|
||||
"My communication preferences": "Meine Kommunikationseinstellungen",
|
||||
"My credit cards": "Meine Kreditkarten",
|
||||
"My membership cards": "Meine Mitgliedskarten",
|
||||
"My pages": "Meine Seiten",
|
||||
"My pages menu": "Meine Seite Menü",
|
||||
"My payment cards": "Meine Zahlungskarten",
|
||||
"My wishes": "Meine Wünsche",
|
||||
"New password": "Neues Kennwort",
|
||||
"Next": "Nächste",
|
||||
@@ -112,6 +121,9 @@
|
||||
"number": "nummer",
|
||||
"On your journey": "Auf deiner Reise",
|
||||
"Open": "Offen",
|
||||
"Open language menu": "Sprachmenü öffnen",
|
||||
"Open menu": "Menü öffnen",
|
||||
"Open my pages menu": "Meine Seiten Menü öffnen",
|
||||
"or": "oder",
|
||||
"Password": "Passwort",
|
||||
"Pay later": "Später bezahlen",
|
||||
@@ -147,6 +159,7 @@
|
||||
"Select country of residence": "Wählen Sie das Land Ihres Wohnsitzes aus",
|
||||
"Select date of birth": "Geburtsdatum auswählen",
|
||||
"Select language": "Sprache auswählen",
|
||||
"Select your language": "Wählen Sie Ihre Sprache",
|
||||
"Show all amenities": "Alle Annehmlichkeiten anzeigen",
|
||||
"Show less": "Weniger anzeigen",
|
||||
"Show map": "Karte anzeigen",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"As our Close Friend": "As our Close Friend",
|
||||
"At latest": "At latest",
|
||||
"At the hotel": "At the hotel",
|
||||
"Back to scandichotels.com": "Back to scandichotels.com",
|
||||
"Bed type": "Bed type",
|
||||
"Book": "Book",
|
||||
"Book reward night": "Book reward night",
|
||||
@@ -34,6 +35,9 @@
|
||||
"City/State": "City/State",
|
||||
"Click here to log in": "Click here to log in",
|
||||
"Close": "Close",
|
||||
"Close language menu": "Close language menu",
|
||||
"Close menu": "Close menu",
|
||||
"Close my pages menu": "Close my pages menu",
|
||||
"Coming up": "Coming up",
|
||||
"Compare all levels": "Compare all levels",
|
||||
"Contact us": "Contact us",
|
||||
@@ -43,6 +47,7 @@
|
||||
"Country code": "Country code",
|
||||
"Credit card deleted successfully": "Credit card deleted successfully",
|
||||
"Current password": "Current password",
|
||||
"Customer service": "Customer service",
|
||||
"Date of Birth": "Date of Birth",
|
||||
"Day": "Day",
|
||||
"Description": "Description",
|
||||
@@ -66,6 +71,7 @@
|
||||
"from your member profile?": "from your member profile?",
|
||||
"Get inspired": "Get inspired",
|
||||
"Go back to overview": "Go back to overview",
|
||||
"Hi": "Hi",
|
||||
"Highest level": "Highest level",
|
||||
"Hotel facilities": "Hotel facilities",
|
||||
"Hotel surroundings": "Hotel surroundings",
|
||||
@@ -87,6 +93,7 @@
|
||||
"Level up to unlock": "Level up to unlock",
|
||||
"Log in": "Log in",
|
||||
"Log in here": "Log in here",
|
||||
"Log in/Join": "Log in/Join",
|
||||
"Log out": "Log out",
|
||||
"Manage preferences": "Manage preferences",
|
||||
"Meetings & Conferences": "Meetings & Conferences",
|
||||
@@ -96,12 +103,14 @@
|
||||
"Members": "Members",
|
||||
"Membership cards": "Membership cards",
|
||||
"Membership ID": "Membership ID",
|
||||
"Menu": "Menu",
|
||||
"Modify": "Modify",
|
||||
"Month": "Month",
|
||||
"My communication preferences": "My communication preferences",
|
||||
"My credit cards": "My credit cards",
|
||||
"My membership cards": "My membership cards",
|
||||
"My pages": "My pages",
|
||||
"My pages menu": "My pages menu",
|
||||
"My payment cards": "My payment cards",
|
||||
"My wishes": "My wishes",
|
||||
"New password": "New password",
|
||||
"Next": "Next",
|
||||
@@ -119,6 +128,9 @@
|
||||
"number": "number",
|
||||
"On your journey": "On your journey",
|
||||
"Open": "Open",
|
||||
"Open language menu": "Open language menu",
|
||||
"Open menu": "Open menu",
|
||||
"Open my pages menu": "Open my pages menu",
|
||||
"or": "or",
|
||||
"Overview": "Overview",
|
||||
"Password": "Password",
|
||||
@@ -130,7 +142,7 @@
|
||||
"Phone number": "Phone number",
|
||||
"Please enter a valid phone number": "Please enter a valid phone number",
|
||||
"Points": "Points",
|
||||
"points": "Points",
|
||||
"points": "points",
|
||||
"Points being calculated": "Points being calculated",
|
||||
"Points earned prior to May 1, 2021": "Points earned prior to May 1, 2021",
|
||||
"Points may take up to 10 days to be displayed.": "Points may take up to 10 days to be displayed.",
|
||||
@@ -158,6 +170,7 @@
|
||||
"Select country of residence": "Select country of residence",
|
||||
"Select date of birth": "Select date of birth",
|
||||
"Select language": "Select language",
|
||||
"Select your language": "Select your language",
|
||||
"Show all amenities": "Show all amenities",
|
||||
"Show less": "Show less",
|
||||
"Show map": "Show map",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"As our Close Friend": "Läheisenä ystävänämme",
|
||||
"At latest": "Viimeistään",
|
||||
"At the hotel": "Hotellissa",
|
||||
"Back to scandichotels.com": "Takaisin scandichotels.com",
|
||||
"Bed type": "Vuodetyyppi",
|
||||
"Book": "Varaa",
|
||||
"Book reward night": "Kirjapalkinto-ilta",
|
||||
@@ -34,6 +35,9 @@
|
||||
"City/State": "Kaupunki/Osavaltio",
|
||||
"Click here to log in": "Napsauta tästä kirjautuaksesi sisään",
|
||||
"Close": "Kiinni",
|
||||
"Close language menu": "Sulje kielivalikko",
|
||||
"Close menu": "Sulje valikko",
|
||||
"Close my pages menu": "Sulje omat sivut -valikko",
|
||||
"Coming up": "Tulossa",
|
||||
"Compare all levels": "Vertaa kaikkia tasoja",
|
||||
"Contact us": "Ota meihin yhteyttä",
|
||||
@@ -43,6 +47,7 @@
|
||||
"Country code": "Maatunnus",
|
||||
"Credit card deleted successfully": "Luottokortti poistettu onnistuneesti",
|
||||
"Current password": "Nykyinen salasana",
|
||||
"Customer service": "Asiakaspalvelu",
|
||||
"Date of Birth": "Syntymäaika",
|
||||
"Day": "Päivä",
|
||||
"Description": "Kuvaus",
|
||||
@@ -64,6 +69,7 @@
|
||||
"from your member profile?": "jäsenprofiilistasi?",
|
||||
"Get inspired": "Inspiroidu",
|
||||
"Go back to overview": "Palaa yleiskatsaukseen",
|
||||
"Hi": "Hi",
|
||||
"Highest level": "Korkein taso",
|
||||
"Hotel facilities": "Hotellin palvelut",
|
||||
"Hotel surroundings": "Hotellin ympäristö",
|
||||
@@ -79,9 +85,9 @@
|
||||
"Level 5": "Taso 5",
|
||||
"Level 6": "Taso 6",
|
||||
"Level 7": "Taso 7",
|
||||
"Level up to unlock": "Nouse seuraavalle tasolle ja avaat seuraavan edun",
|
||||
"Log in": "Kirjaudu sisään",
|
||||
"Log in here": "Kirjaudu sisään",
|
||||
"Log in/Join": "Kirjaudu sisään/Liittyä",
|
||||
"Log out": "Kirjaudu ulos",
|
||||
"Manage preferences": "Asetusten hallinta",
|
||||
"Meetings & Conferences": "Kokoukset & Konferenssit",
|
||||
@@ -91,12 +97,14 @@
|
||||
"Members": "Jäsenet",
|
||||
"Membership cards": "Jäsenkortit",
|
||||
"Membership ID": "Jäsentunnus",
|
||||
"Menu": "Valikko",
|
||||
"Modify": "Muokkaa",
|
||||
"Month": "Kuukausi",
|
||||
"My communication preferences": "Viestintämieltymykseni",
|
||||
"My credit cards": "Luottokorttini",
|
||||
"My membership cards": "Jäsenkorttini",
|
||||
"My pages": "Omat sivut",
|
||||
"My pages menu": "Omat sivut -valikko",
|
||||
"My payment cards": "Minun maksukortit",
|
||||
"My wishes": "Toiveeni",
|
||||
"New password": "Uusi salasana",
|
||||
"Next": "Seuraava",
|
||||
@@ -114,6 +122,9 @@
|
||||
"number": "määrä",
|
||||
"On your journey": "Matkallasi",
|
||||
"Open": "Avata",
|
||||
"Open language menu": "Avaa kielivalikko",
|
||||
"Open menu": "Avaa valikko",
|
||||
"Open my pages menu": "Avaa omat sivut -valikko",
|
||||
"or": "tai",
|
||||
"Overview": "Yleiskatsaus",
|
||||
"Password": "Salasana",
|
||||
@@ -152,6 +163,7 @@
|
||||
"Select country of residence": "Valitse asuinmaa",
|
||||
"Select date of birth": "Valitse syntymäaika",
|
||||
"Select language": "Valitse kieli",
|
||||
"Select your language": "Valitse kieli",
|
||||
"Show all amenities": "Näytä kaikki mukavuudet",
|
||||
"Show less": "Näytä vähemmän",
|
||||
"Show map": "Näytä kartta",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"As our Close Friend": "Som vår nære venn",
|
||||
"At latest": "Senest",
|
||||
"At the hotel": "På hotellet",
|
||||
"Back to scandichotels.com": "Tilbake til scandichotels.com",
|
||||
"Bed type": "Seng type",
|
||||
"Book": "Bestill",
|
||||
"Book reward night": "Bestill belønningskveld",
|
||||
@@ -34,6 +35,9 @@
|
||||
"City/State": "By/Stat",
|
||||
"Click here to log in": "Klikk her for å logge inn",
|
||||
"Close": "Lukk",
|
||||
"Close language menu": "Lukk språkmeny",
|
||||
"Close menu": "Lukk meny",
|
||||
"Close my pages menu": "Lukk mine sidermenyn",
|
||||
"Coming up": "Kommer opp",
|
||||
"Compare all levels": "Sammenlign alle nivåer",
|
||||
"Contact us": "Kontakt oss",
|
||||
@@ -43,6 +47,7 @@
|
||||
"Country code": "Landskode",
|
||||
"Credit card deleted successfully": "Kredittkort slettet",
|
||||
"Current password": "Nåværende passord",
|
||||
"Customer service": "Kundeservice",
|
||||
"Date of Birth": "Fødselsdato",
|
||||
"Day": "Dag",
|
||||
"Description": "Beskrivelse",
|
||||
@@ -64,6 +69,7 @@
|
||||
"from your member profile?": "fra medlemsprofilen din?",
|
||||
"Get inspired": "Bli inspirert",
|
||||
"Go back to overview": "Gå tilbake til oversikten",
|
||||
"Hi": "Hei",
|
||||
"Highest level": "Høyeste nivå",
|
||||
"Hotel facilities": "Hotelfaciliteter",
|
||||
"Hotel surroundings": "Hotellomgivelser",
|
||||
@@ -82,6 +88,7 @@
|
||||
"Level up to unlock": "Nivå opp for å låse opp",
|
||||
"Log in": "Logg Inn",
|
||||
"Log in here": "Logg inn her",
|
||||
"Log in/Join": "Logg på/Bli med",
|
||||
"Log out": "Logg ut",
|
||||
"Manage preferences": "Administrer preferanser",
|
||||
"Meetings & Conferences": "Møter & Konferanser",
|
||||
@@ -91,12 +98,14 @@
|
||||
"Members": "Medlemmer",
|
||||
"Membership cards": "Medlemskort",
|
||||
"Membership ID": "Medlems-ID",
|
||||
"Menu": "Menu",
|
||||
"Modify": "Endre",
|
||||
"Month": "Måned",
|
||||
"My communication preferences": "Mine kommunikasjonspreferanser",
|
||||
"My credit cards": "Kredittkortene mine",
|
||||
"My membership cards": "Mine medlemskort",
|
||||
"My pages": "Mine sider",
|
||||
"My pages menu": "Mine sider-menyen",
|
||||
"My payment cards": "Mine betalingskort",
|
||||
"My wishes": "Mine ønsker",
|
||||
"New password": "Nytt passord",
|
||||
"Next": "Neste",
|
||||
@@ -114,6 +123,9 @@
|
||||
"number": "antall",
|
||||
"On your journey": "På reisen din",
|
||||
"Open": "Åpen",
|
||||
"Open language menu": "Åpne språkmenyen",
|
||||
"Open menu": "Åpne menyen",
|
||||
"Open my pages menu": "Åpne mine sider menyen",
|
||||
"or": "eller",
|
||||
"Overview": "Oversikt",
|
||||
"Password": "Passord",
|
||||
@@ -124,7 +136,7 @@
|
||||
"Phone is required": "Telefon kreves",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer",
|
||||
"points": "Poeng",
|
||||
"points": "poeng",
|
||||
"Points": "Poeng",
|
||||
"Points being calculated": "Poeng beregnes",
|
||||
"Points earned prior to May 1, 2021": "Opptjente poeng før 1. mai 2021",
|
||||
@@ -152,6 +164,7 @@
|
||||
"Select country of residence": "Velg bostedsland",
|
||||
"Select date of birth": "Velg fødselsdato",
|
||||
"Select language": "Velg språk",
|
||||
"Select your language": "Velg språk",
|
||||
"Show all amenities": "Vis alle fasiliteter",
|
||||
"Show less": "Vis mindre",
|
||||
"Show map": "Vis kart",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"As our Close Friend": "Som vår nära vän",
|
||||
"At latest": "Senast",
|
||||
"At the hotel": "På hotellet",
|
||||
"Back to scandichotels.com": "Tillbaka till scandichotels.com",
|
||||
"Bed type": "Sängtyp",
|
||||
"Book": "Boka",
|
||||
"Book reward night": "Boka frinatt",
|
||||
@@ -34,6 +35,9 @@
|
||||
"City/State": "Ort",
|
||||
"Click here to log in": "Klicka här för att logga in",
|
||||
"Close": "Stäng",
|
||||
"Close language menu": "Stäng språkmenyn",
|
||||
"Close menu": "Stäng menyn",
|
||||
"Close my pages menu": "Stäng mina sidor menyn",
|
||||
"Coming up": "Kommer härnäst",
|
||||
"Compare all levels": "Jämför alla nivåer",
|
||||
"Contact us": "Kontakta oss",
|
||||
@@ -43,6 +47,7 @@
|
||||
"Country code": "Landskod",
|
||||
"Credit card deleted successfully": "Kreditkort har tagits bort",
|
||||
"Current password": "Nuvarande lösenord",
|
||||
"Customer service": "Kundservice",
|
||||
"Date of Birth": "Födelsedatum",
|
||||
"Day": "Dag",
|
||||
"Description": "Beskrivning",
|
||||
@@ -64,6 +69,7 @@
|
||||
"from your member profile?": "från din medlemsprofil?",
|
||||
"Get inspired": "Bli inspirerad",
|
||||
"Go back to overview": "Gå tillbaka till översikten",
|
||||
"Hi": "Hej",
|
||||
"Highest level": "Högsta nivå",
|
||||
"Hotel facilities": "Hotellfaciliteter",
|
||||
"Hotel surroundings": "Hotellomgivning",
|
||||
@@ -84,6 +90,7 @@
|
||||
"Level up to unlock": "Levla upp för att låsa upp",
|
||||
"Log in": "Logga in",
|
||||
"Log in here": "Logga in här",
|
||||
"Log in/Join": "Logga in/Gå med",
|
||||
"Log out": "Logga ut",
|
||||
"Manage preferences": "Hantera inställningar",
|
||||
"Meetings & Conferences": "Möten & Konferenser",
|
||||
@@ -93,12 +100,14 @@
|
||||
"Members": "Medlemmar",
|
||||
"Membership cards": "Medlemskort",
|
||||
"Membership ID": "Medlems-ID",
|
||||
"Menu": "Meny",
|
||||
"Modify": "Ändra",
|
||||
"Month": "Månad",
|
||||
"My communication preferences": "Mina kommunikationspreferenser",
|
||||
"My credit cards": "Mina kreditkort",
|
||||
"My membership cards": "Mina medlemskort",
|
||||
"My pages": "Mina sidor",
|
||||
"My pages menu": "Mina sidor meny",
|
||||
"My payment cards": "Mina betalningskort",
|
||||
"My wishes": "Mina önskningar",
|
||||
"New password": "Nytt lösenord",
|
||||
"Next": "Nästa",
|
||||
@@ -116,6 +125,9 @@
|
||||
"number": "nummer",
|
||||
"On your journey": "På din resa",
|
||||
"Open": "Öppna",
|
||||
"Open language menu": "Öppna språkmenyn",
|
||||
"Open menu": "Öppna menyn",
|
||||
"Open my pages menu": "Öppna mina sidor menyn",
|
||||
"or": "eller",
|
||||
"Overview": "Översikt",
|
||||
"Password": "Lösenord",
|
||||
@@ -155,6 +167,7 @@
|
||||
"Select country of residence": "Välj bosättningsland",
|
||||
"Select date of birth": "Välj födelsedatum",
|
||||
"Select language": "Välj språk",
|
||||
"Select your language": "Välj ditt språk",
|
||||
"Show all amenities": "Visa alla bekvämligheter",
|
||||
"Show less": "Visa mindre",
|
||||
"Show map": "Visa karta",
|
||||
@@ -183,7 +196,7 @@
|
||||
"Type of room": "Rumstyp",
|
||||
"uppercase letter": "stor bokstav",
|
||||
"Use bonus cheque": "Use bonus cheque",
|
||||
"User information": "Användar information",
|
||||
"User information": "Användarinformation",
|
||||
"View your booking": "Visa din bokning",
|
||||
"Visiting address": "Besöksadress",
|
||||
"We could not add a card right now, please try again later.": "Vi kunde inte lägga till ett kort just nu, vänligen försök igen senare.",
|
||||
|
||||
39
lib/graphql/Query/BookingWidgetToggle.graphql
Normal file
39
lib/graphql/Query/BookingWidgetToggle.graphql
Normal file
@@ -0,0 +1,39 @@
|
||||
query GetAccountPageSettings($uid: String!, $locale: String!) {
|
||||
account_page(uid: $uid, locale: $locale) {
|
||||
page_settings {
|
||||
hide_booking_widget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query GetLoyaltyPageSettings($uid: String!, $locale: String!) {
|
||||
loyalty_page(uid: $uid, locale: $locale) {
|
||||
page_settings {
|
||||
hide_booking_widget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query GetContentPageSettings($uid: String!, $locale: String!) {
|
||||
content_page(uid: $uid, locale: $locale) {
|
||||
page_settings {
|
||||
hide_booking_widget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query GetHotelPageSettings($uid: String!, $locale: String!) {
|
||||
hotel_page(uid: $uid, locale: $locale) {
|
||||
page_settings {
|
||||
hide_booking_widget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query GetCurrentBlocksPageSettings($uid: String!, $locale: String!) {
|
||||
current_blocks_page(uid: $uid, locale: $locale) {
|
||||
page_settings {
|
||||
hide_booking_widget
|
||||
}
|
||||
}
|
||||
}
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -19,7 +19,7 @@
|
||||
"@radix-ui/react-dialog": "^1.1.1",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@react-aria/ssr": "^3.9.5",
|
||||
"@scandic-hotels/design-system": "git+https://x-token-auth:$DESIGN_SYSTEM_ACCESS_TOKEN@bitbucket.org/scandic-swap/design-system.git#v0.1.0-rc.8",
|
||||
"@scandic-hotels/design-system": "git+https://x-token-auth:$DESIGN_SYSTEM_ACCESS_TOKEN@bitbucket.org/scandic-swap/design-system.git#v0.1.0-rc.9",
|
||||
"@t3-oss/env-nextjs": "^0.9.2",
|
||||
"@tanstack/react-query": "^5.28.6",
|
||||
"@trpc/client": "^11.0.0-rc.467",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"@radix-ui/react-dialog": "^1.1.1",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@react-aria/ssr": "^3.9.5",
|
||||
"@scandic-hotels/design-system": "git+https://x-token-auth:$DESIGN_SYSTEM_ACCESS_TOKEN@bitbucket.org/scandic-swap/design-system.git#v0.1.0-rc.8",
|
||||
"@scandic-hotels/design-system": "git+https://x-token-auth:$DESIGN_SYSTEM_ACCESS_TOKEN@bitbucket.org/scandic-swap/design-system.git#v0.1.0-rc.9",
|
||||
"@t3-oss/env-nextjs": "^0.9.2",
|
||||
"@tanstack/react-query": "^5.28.6",
|
||||
"@trpc/client": "^11.0.0-rc.467",
|
||||
|
||||
@@ -13,11 +13,7 @@ import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
||||
|
||||
import {
|
||||
generateRefsResponseTag,
|
||||
generateRefTag,
|
||||
generateTag,
|
||||
} from "@/utils/generateTag"
|
||||
import { generateRefsResponseTag, generateTag } from "@/utils/generateTag"
|
||||
|
||||
import { langInput } from "./input"
|
||||
import {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user