Merged in feat/SW-1450-destination-page-cs-components (pull request #1204)
feat(SW-1450): added components in destination pages from cs * feat(SW-1450): added components in destination pages from cs * feat(SW-1450): added correct refs and removed classNames Approved-by: Fredrik Thorsson
This commit is contained in:
@@ -7,12 +7,18 @@ import { setLang } from "@/i18n/serverContext"
|
||||
import type { ContentTypeParams, LangParams, PageArgs } from "@/types/params"
|
||||
import { PageContentTypeEnum } from "@/types/requests/contentType"
|
||||
|
||||
const IGNORED_CONTENT_TYPES = [
|
||||
PageContentTypeEnum.hotelPage,
|
||||
PageContentTypeEnum.destinationCityPage,
|
||||
PageContentTypeEnum.destinationCountryPage,
|
||||
]
|
||||
|
||||
export default function PageBreadcrumbs({
|
||||
params,
|
||||
}: PageArgs<LangParams & ContentTypeParams>) {
|
||||
setLang(params.lang)
|
||||
|
||||
if (params.contentType === PageContentTypeEnum.hotelPage) {
|
||||
if (IGNORED_CONTENT_TYPES.includes(params.contentType)) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import { isSignupPage } from "@/constants/routes/signup"
|
||||
import { env } from "@/env/server"
|
||||
import { getHotelPage } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import DestinationCityPage from "@/components/ContentType/DestinationCityPage"
|
||||
import DestinationCountryPage from "@/components/ContentType/DestinationCountryPage"
|
||||
import DestinationOverviewPage from "@/components/ContentType/DestinationOverviewPage"
|
||||
import DestinationCityPage from "@/components/ContentType/DestinationPage/DestinationCityPage"
|
||||
import DestinationCountryPage from "@/components/ContentType/DestinationPage/DestinationCountryPage"
|
||||
import HotelPage from "@/components/ContentType/HotelPage"
|
||||
import LoyaltyPage from "@/components/ContentType/LoyaltyPage"
|
||||
import StartPage from "@/components/ContentType/StartPage"
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
.pageContainer {
|
||||
display: grid;
|
||||
max-width: var(--max-width);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.pageContainer {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Suspense } from "react"
|
||||
|
||||
import { getDestinationCityPage } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
|
||||
import styles from "./destinationCityPage.module.css"
|
||||
|
||||
export default async function DestinationCityPage() {
|
||||
const pageData = await getDestinationCityPage()
|
||||
|
||||
if (!pageData) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { tracking, destinationCityPage } = pageData
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.pageContainer}>
|
||||
<h1>Destination City Page</h1>
|
||||
</div>
|
||||
<Suspense fallback={null}>
|
||||
<TrackingSDK pageData={tracking} />
|
||||
</Suspense>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
.pageContainer {
|
||||
display: grid;
|
||||
max-width: var(--max-width);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.pageContainer {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Suspense } from "react"
|
||||
|
||||
import { getDestinationCountryPage } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
|
||||
import styles from "./destinationCountryPage.module.css"
|
||||
|
||||
export default async function DestinationCountryPage() {
|
||||
const pageData = await getDestinationCountryPage()
|
||||
|
||||
if (!pageData) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { tracking, destinationCountryPage } = pageData
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.pageContainer}>
|
||||
<h1>Destination Country Page</h1>
|
||||
</div>
|
||||
<Suspense fallback={null}>
|
||||
<TrackingSDK pageData={tracking} />
|
||||
</Suspense>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
.pageContainer {
|
||||
--map-desktop-width: 23.75rem;
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"header"
|
||||
"sidebar"
|
||||
"mainSection";
|
||||
width: 100%;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.header {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
.mainSection {
|
||||
grid-area: mainSection;
|
||||
padding-bottom: var(--Spacing-x7);
|
||||
min-height: 500px; /* This is a temporary value because of no content atm */
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
grid-area: sidebar;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--Base-Surface-Subtle-Normal);
|
||||
}
|
||||
|
||||
.experienceList {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.mapWrapper {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mapWrapper img {
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.pageContainer {
|
||||
max-width: var(--max-width-page);
|
||||
margin: 0 auto;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.pageContainer {
|
||||
grid-template-areas:
|
||||
"header sidebar"
|
||||
"mainSection sidebar";
|
||||
grid-template-columns: 1fr var(--map-desktop-width);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import { Suspense } from "react"
|
||||
|
||||
import { getDestinationCityPage } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import Breadcrumbs from "@/components/Breadcrumbs"
|
||||
import StaticMap from "@/components/Maps/StaticMap"
|
||||
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
|
||||
import Chip from "@/components/TempDesignSystem/Chip"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import SidebarContentWrapper from "../SidebarContentWrapper"
|
||||
import DestinationPageSidePeek from "../Sidepeek"
|
||||
import TopImages from "../TopImages"
|
||||
import { mapExperiencesToListData } from "../utils"
|
||||
|
||||
import styles from "./destinationCityPage.module.css"
|
||||
|
||||
import { PageContentTypeEnum } from "@/types/requests/contentType"
|
||||
|
||||
export default async function DestinationCityPage() {
|
||||
const [intl, pageData] = await Promise.all([
|
||||
getIntl(),
|
||||
getDestinationCityPage(),
|
||||
])
|
||||
|
||||
if (!pageData) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { tracking, destinationCityPage } = pageData
|
||||
const {
|
||||
images,
|
||||
heading,
|
||||
preamble,
|
||||
experiences,
|
||||
has_sidepeek,
|
||||
sidepeek_button_text,
|
||||
sidepeek_content,
|
||||
destination_settings,
|
||||
} = destinationCityPage
|
||||
const experiencesList = await mapExperiencesToListData(experiences)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.pageContainer}>
|
||||
<header className={styles.header}>
|
||||
<Suspense fallback={<BreadcrumbsSkeleton />}>
|
||||
<Breadcrumbs variant={PageContentTypeEnum.destinationCityPage} />
|
||||
</Suspense>
|
||||
<TopImages images={images} />
|
||||
</header>
|
||||
<main className={styles.mainSection}>
|
||||
{/* TODO: Add hotel listing by cityIdentifier */}
|
||||
{">>>> MAIN CONTENT <<<<"}
|
||||
</main>
|
||||
<aside className={styles.sidebar}>
|
||||
<SidebarContentWrapper>
|
||||
<Title level="h2">{heading}</Title>
|
||||
<Body color="uiTextMediumContrast">{preamble}</Body>
|
||||
<ul className={styles.experienceList}>
|
||||
{experiencesList.map(({ Icon, name }) => (
|
||||
<li key={name}>
|
||||
<Chip variant="tag">
|
||||
<Icon width={20} height={20} />
|
||||
{name}
|
||||
</Chip>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{has_sidepeek && (
|
||||
<DestinationPageSidePeek
|
||||
buttonText={sidepeek_button_text}
|
||||
sidePeekContent={sidepeek_content}
|
||||
/>
|
||||
)}
|
||||
|
||||
{destination_settings.city && (
|
||||
<div className={styles.mapWrapper}>
|
||||
<StaticMap
|
||||
city={destination_settings.city}
|
||||
country={destination_settings.country}
|
||||
width={320}
|
||||
height={200}
|
||||
zoomLevel={10}
|
||||
altText={intl.formatMessage({ id: "Map of the city center" })}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</SidebarContentWrapper>
|
||||
</aside>
|
||||
</div>
|
||||
<Suspense fallback={null}>
|
||||
<TrackingSDK pageData={tracking} />
|
||||
</Suspense>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
.pageContainer {
|
||||
--map-desktop-width: 23.75rem;
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"header"
|
||||
"sidebar"
|
||||
"mainSection";
|
||||
width: 100%;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.header {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
.mainSection {
|
||||
grid-area: mainSection;
|
||||
padding-bottom: var(--Spacing-x7);
|
||||
min-height: 500px; /* This is a temporary value because of no content atm */
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
grid-area: sidebar;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--Base-Surface-Subtle-Normal);
|
||||
}
|
||||
|
||||
.experienceList {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.mapWrapper {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mapWrapper img {
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.pageContainer {
|
||||
max-width: var(--max-width-page);
|
||||
margin: 0 auto;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.pageContainer {
|
||||
grid-template-areas:
|
||||
"header sidebar"
|
||||
"mainSection sidebar";
|
||||
grid-template-columns: 1fr var(--map-desktop-width);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { Suspense } from "react"
|
||||
|
||||
import { getDestinationCountryPage } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import Breadcrumbs from "@/components/Breadcrumbs"
|
||||
import StaticMap from "@/components/Maps/StaticMap"
|
||||
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
|
||||
import Chip from "@/components/TempDesignSystem/Chip"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import SidebarContentWrapper from "../SidebarContentWrapper"
|
||||
import DestinationPageSidePeek from "../Sidepeek"
|
||||
import TopImages from "../TopImages"
|
||||
import { mapExperiencesToListData } from "../utils"
|
||||
|
||||
import styles from "./destinationCountryPage.module.css"
|
||||
|
||||
import { PageContentTypeEnum } from "@/types/requests/contentType"
|
||||
|
||||
export default async function DestinationCountryPage() {
|
||||
const [intl, pageData] = await Promise.all([
|
||||
getIntl(),
|
||||
getDestinationCountryPage(),
|
||||
])
|
||||
|
||||
if (!pageData) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { tracking, destinationCountryPage } = pageData
|
||||
const {
|
||||
images,
|
||||
heading,
|
||||
preamble,
|
||||
experiences,
|
||||
has_sidepeek,
|
||||
sidepeek_button_text,
|
||||
sidepeek_content,
|
||||
destination_settings,
|
||||
} = destinationCountryPage
|
||||
const experiencesList = await mapExperiencesToListData(experiences)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.pageContainer}>
|
||||
<header className={styles.header}>
|
||||
<Suspense fallback={<BreadcrumbsSkeleton />}>
|
||||
<Breadcrumbs variant={PageContentTypeEnum.destinationCityPage} />
|
||||
</Suspense>
|
||||
<TopImages images={images} />
|
||||
</header>
|
||||
<main className={styles.mainSection}>
|
||||
{/* TODO: Add city listing by cityIdentifier */}
|
||||
{">>>> MAIN CONTENT <<<<"}
|
||||
</main>
|
||||
<aside className={styles.sidebar}>
|
||||
<SidebarContentWrapper>
|
||||
<Title level="h2">{heading}</Title>
|
||||
<Body color="uiTextMediumContrast">{preamble}</Body>
|
||||
<ul className={styles.experienceList}>
|
||||
{experiencesList.map(({ Icon, name }) => (
|
||||
<li key={name}>
|
||||
<Chip variant="tag">
|
||||
<Icon width={20} height={20} />
|
||||
{name}
|
||||
</Chip>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{has_sidepeek && (
|
||||
<DestinationPageSidePeek
|
||||
buttonText={sidepeek_button_text}
|
||||
sidePeekContent={sidepeek_content}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className={styles.mapWrapper}>
|
||||
<StaticMap
|
||||
country={destination_settings.country}
|
||||
width={320}
|
||||
height={200}
|
||||
zoomLevel={3}
|
||||
altText={intl.formatMessage({ id: "Map of the country" })}
|
||||
/>
|
||||
</div>
|
||||
</SidebarContentWrapper>
|
||||
</aside>
|
||||
</div>
|
||||
<Suspense fallback={null}>
|
||||
<TrackingSDK pageData={tracking} />
|
||||
</Suspense>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
"use client"
|
||||
|
||||
import { useRef } from "react"
|
||||
|
||||
import { StickyElementNameEnum } from "@/stores/sticky-position"
|
||||
|
||||
import useStickyPosition from "@/hooks/useStickyPosition"
|
||||
|
||||
import styles from "./sidebarContentWrapper.module.css"
|
||||
|
||||
export default function SidebarContentWrapper({
|
||||
children,
|
||||
}: React.PropsWithChildren) {
|
||||
const sidebarRef = useRef<HTMLDivElement>(null)
|
||||
useStickyPosition({
|
||||
ref: sidebarRef,
|
||||
name: StickyElementNameEnum.DESTINATION_SIDEBAR,
|
||||
})
|
||||
|
||||
return (
|
||||
<div ref={sidebarRef} className={styles.sidebarContent}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
.sidebarContent {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: var(--Spacing-x2);
|
||||
padding: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.sidebarContent {
|
||||
position: sticky;
|
||||
}
|
||||
}
|
||||
52
components/ContentType/DestinationPage/Sidepeek/index.tsx
Normal file
52
components/ContentType/DestinationPage/Sidepeek/index.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
|
||||
import { ChevronRightSmallIcon } from "@/components/Icons"
|
||||
import JsonToHtml from "@/components/JsonToHtml"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||
|
||||
import type { DestinationCityPageData } from "@/types/trpc/routers/contentstack/destinationCityPage"
|
||||
import type { DestinationCountryPageData } from "@/types/trpc/routers/contentstack/destinationCountryPage"
|
||||
|
||||
interface DestinationPageSidepeekProps {
|
||||
buttonText: string
|
||||
sidePeekContent: NonNullable<
|
||||
| DestinationCityPageData["sidepeek_content"]
|
||||
| DestinationCountryPageData["sidepeek_content"]
|
||||
>
|
||||
}
|
||||
export default function DestinationPageSidepeek({
|
||||
buttonText,
|
||||
sidePeekContent,
|
||||
}: DestinationPageSidepeekProps) {
|
||||
const [sidePeekIsOpen, setSidePeekIsOpen] = useState(false)
|
||||
const { heading, content } = sidePeekContent
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
onPress={() => setSidePeekIsOpen(true)}
|
||||
theme="base"
|
||||
variant="icon"
|
||||
intent="text"
|
||||
size="small"
|
||||
wrapping
|
||||
>
|
||||
{buttonText}
|
||||
<ChevronRightSmallIcon />
|
||||
</Button>
|
||||
<SidePeek
|
||||
title={heading}
|
||||
isOpen={sidePeekIsOpen}
|
||||
handleClose={() => setSidePeekIsOpen(false)}
|
||||
>
|
||||
<JsonToHtml
|
||||
nodes={content.json.children}
|
||||
embeds={content.embedded_itemsConnection.edges}
|
||||
/>
|
||||
</SidePeek>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
.ctaContainer {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
34
components/ContentType/DestinationPage/TopImages/index.tsx
Normal file
34
components/ContentType/DestinationPage/TopImages/index.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
"use client"
|
||||
|
||||
import Image from "@/components/Image"
|
||||
|
||||
import styles from "./topImages.module.css"
|
||||
|
||||
import type { ImageVaultAsset } from "@/types/components/imageVault"
|
||||
|
||||
interface TopImageProps {
|
||||
images: ImageVaultAsset[]
|
||||
}
|
||||
|
||||
export default function TopImages({ images }: TopImageProps) {
|
||||
const maxWidth = 1020
|
||||
|
||||
return (
|
||||
<div className={styles.imageWrapper}>
|
||||
{images.slice(0, 3).map((image, index) => (
|
||||
<Image
|
||||
key={image.url}
|
||||
src={image.url}
|
||||
alt={image.meta.alt || image.meta.caption || ""}
|
||||
width={index === 0 ? maxWidth : maxWidth / 3}
|
||||
height={Math.ceil(
|
||||
(index === 0 ? maxWidth : maxWidth / 3) /
|
||||
image.dimensions.aspectRatio
|
||||
)}
|
||||
focalPoint={image.focalPoint}
|
||||
className={styles.image}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
.imageWrapper {
|
||||
max-width: var(--max-width-page);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.image {
|
||||
height: 200px;
|
||||
max-height: 40dvh;
|
||||
width: 100%;
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
.image:not(:first-child) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.imageWrapper {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.imageWrapper > .image:first-child {
|
||||
grid-column: span 2;
|
||||
grid-row: span 2;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.imageWrapper > .image:only-child {
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
||||
.imageWrapper > .image:nth-child(2):nth-last-child(1) {
|
||||
grid-column: span 1;
|
||||
grid-row: span 2;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.imageWrapper > .image:nth-child(2):nth-last-child(2),
|
||||
.imageWrapper > .image:nth-child(3) {
|
||||
grid-column: span 1;
|
||||
grid-row: span 1;
|
||||
height: calc(150px - var(--Spacing-x-half));
|
||||
max-height: calc(20dvh - var(--Spacing-x-half));
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.imageWrapper > .image:first-child,
|
||||
.imageWrapper > .image:nth-child(2):nth-last-child(1) {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.imageWrapper > .image:nth-child(2):nth-last-child(2),
|
||||
.imageWrapper > .image:nth-child(3) {
|
||||
height: calc(200px - var(--Spacing-x-half));
|
||||
}
|
||||
}
|
||||
59
components/ContentType/DestinationPage/utils.ts
Normal file
59
components/ContentType/DestinationPage/utils.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
BikeIcon,
|
||||
CityIcon,
|
||||
FamilyIcon,
|
||||
KayakingIcon,
|
||||
MuseumIcon,
|
||||
NightlifeIcon,
|
||||
StarFilledIcon,
|
||||
} from "@/components/Icons"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import type { FC } from "react"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export async function mapExperiencesToListData(
|
||||
experiences: string[]
|
||||
): Promise<{ Icon: FC<IconProps>; name: string }[]> {
|
||||
const intl = await getIntl()
|
||||
|
||||
return experiences.map((experience) => {
|
||||
switch (experience) {
|
||||
case "Hiking":
|
||||
return {
|
||||
Icon: StarFilledIcon,
|
||||
name: intl.formatMessage({ id: "Hiking" }),
|
||||
}
|
||||
case "Kayaking":
|
||||
return {
|
||||
Icon: KayakingIcon,
|
||||
name: intl.formatMessage({ id: "Kayaking" }),
|
||||
}
|
||||
case "Bike friendly":
|
||||
return {
|
||||
Icon: BikeIcon,
|
||||
name: intl.formatMessage({ id: "Bike friendly" }),
|
||||
}
|
||||
case "Museums":
|
||||
return { Icon: MuseumIcon, name: intl.formatMessage({ id: "Museums" }) }
|
||||
case "Family friendly":
|
||||
return {
|
||||
Icon: FamilyIcon,
|
||||
name: intl.formatMessage({ id: "Family friendly" }),
|
||||
}
|
||||
case "City pulse":
|
||||
return {
|
||||
Icon: CityIcon,
|
||||
name: intl.formatMessage({ id: "City pulse" }),
|
||||
}
|
||||
case "Nightlife":
|
||||
return {
|
||||
Icon: NightlifeIcon,
|
||||
name: intl.formatMessage({ id: "Nightlife" }),
|
||||
}
|
||||
default:
|
||||
return { Icon: StarFilledIcon, name: experience }
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export default function HotelCardDialogImage({
|
||||
/>
|
||||
)}
|
||||
<div className={styles.tripAdvisor}>
|
||||
<Chip intent="secondary" className={styles.tripAdvisor}>
|
||||
<Chip className={styles.tripAdvisor}>
|
||||
<TripAdvisorIcon color="burgundy" />
|
||||
{ratings}
|
||||
</Chip>
|
||||
|
||||
@@ -14,14 +14,16 @@ function getCenter({
|
||||
city?: string
|
||||
country?: string
|
||||
}): string | undefined {
|
||||
switch (true) {
|
||||
case !!coordinates:
|
||||
return `${coordinates.lat},${coordinates.lng}`
|
||||
case !!country:
|
||||
return `${city}, ${country}`
|
||||
default:
|
||||
return city
|
||||
if (coordinates) {
|
||||
return `${coordinates.lat},${coordinates.lng}`
|
||||
}
|
||||
if (city && country) {
|
||||
return `${city}, ${country}`
|
||||
}
|
||||
if (country) {
|
||||
return country
|
||||
}
|
||||
return city
|
||||
}
|
||||
|
||||
export default function StaticMap({
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.hotelHeaderWidth.breadcrumbs {
|
||||
.headerWidth.breadcrumbs {
|
||||
max-width: min(var(--max-width-page), calc(100% - var(--max-width-spacing)));
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ export const breadcrumbsVariants = cva(styles.breadcrumbs, {
|
||||
[PageContentTypeEnum.accountPage]: styles.fullWidth,
|
||||
[PageContentTypeEnum.contentPage]: styles.contentWidth,
|
||||
[PageContentTypeEnum.collectionPage]: styles.contentWidth,
|
||||
[PageContentTypeEnum.destinationOverviewPage]: styles.contentWidth,
|
||||
[PageContentTypeEnum.destinationCountryPage]: styles.contentWidth,
|
||||
[PageContentTypeEnum.destinationCityPage]: styles.contentWidth,
|
||||
[PageContentTypeEnum.hotelPage]: styles.hotelHeaderWidth,
|
||||
[PageContentTypeEnum.destinationOverviewPage]: styles.fullWidth,
|
||||
[PageContentTypeEnum.destinationCountryPage]: styles.fullWidth,
|
||||
[PageContentTypeEnum.destinationCityPage]: styles.fullWidth,
|
||||
[PageContentTypeEnum.hotelPage]: styles.headerWidth,
|
||||
[PageContentTypeEnum.loyaltyPage]: styles.fullWidth,
|
||||
[PageContentTypeEnum.startPage]: styles.contentWidth,
|
||||
default: styles.fullWidth,
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
div.chip {
|
||||
align-items: center;
|
||||
border-radius: var(--Corner-radius-xLarge);
|
||||
--chip-text-color: var(--Base-Text-High-contrast);
|
||||
--chip-background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
display: flex;
|
||||
gap: var(--Spacing-x-half);
|
||||
height: 22px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: var(--Spacing-x-half) var(--Spacing-x1);
|
||||
gap: var(--Spacing-x-half);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
color: var(--chip-text-color);
|
||||
background-color: var(--chip-background-color);
|
||||
}
|
||||
|
||||
.primary {
|
||||
background-color: var(--Scandic-Red-90);
|
||||
color: var(--Primary-Dark-On-Surface-Accent);
|
||||
.chip *,
|
||||
.chip svg * {
|
||||
fill: var(--chip-text-color);
|
||||
}
|
||||
|
||||
.secondary {
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
color: var(--Primary-Light-On-Surface-Text);
|
||||
.chip.burgundy {
|
||||
--chip-text-color: var(--Primary-Dark-On-Surface-Text);
|
||||
--chip-background-color: var(--Base-Text-High-contrast);
|
||||
}
|
||||
|
||||
.chip.transparent {
|
||||
--chip-text-color: var(--UI-Input-Controls-On-Fill-Normal);
|
||||
--chip-background-color: rgba(64, 57, 55, 0.9);
|
||||
}
|
||||
|
||||
.chip.tag {
|
||||
--chip-background-color: var(--Base-Surface-Subtle-Hover);
|
||||
}
|
||||
|
||||
@@ -4,15 +4,9 @@ import { chipVariants } from "./variants"
|
||||
|
||||
import type { ChipProps } from "./chip"
|
||||
|
||||
export default function Chip({
|
||||
children,
|
||||
className,
|
||||
intent,
|
||||
variant,
|
||||
}: ChipProps) {
|
||||
export default function Chip({ children, className, variant }: ChipProps) {
|
||||
const classNames = chipVariants({
|
||||
className,
|
||||
intent,
|
||||
variant,
|
||||
})
|
||||
return (
|
||||
|
||||
@@ -4,16 +4,14 @@ import styles from "./chip.module.css"
|
||||
|
||||
export const chipVariants = cva(styles.chip, {
|
||||
variants: {
|
||||
intent: {
|
||||
primary: styles.primary,
|
||||
secondary: styles.secondary,
|
||||
},
|
||||
variant: {
|
||||
default: styles.default,
|
||||
burgundy: styles.burgundy,
|
||||
transparent: styles.transparent,
|
||||
tag: styles.tag,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
intent: "primary",
|
||||
variant: "default",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"Bed": "Seng type",
|
||||
"Bed options": "Sengemuligheder",
|
||||
"Bed type": "Seng type",
|
||||
"Bike friendly": "Cykelvenlig",
|
||||
"Birth date": "Fødselsdato",
|
||||
"Book": "Book",
|
||||
"Book a table online": "Book et bord online",
|
||||
@@ -91,6 +92,7 @@
|
||||
"Choose room": "Vælg rum",
|
||||
"Cities": "Byer",
|
||||
"City": "By",
|
||||
"City pulse": "Byens puls",
|
||||
"City/State": "By/Stat",
|
||||
"Clear all filters": "Ryd alle filtre",
|
||||
"Clear searches": "Ryd søgninger",
|
||||
@@ -159,6 +161,7 @@
|
||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kreditkort. Prøv venligst igen senere.",
|
||||
"Failed to verify membership": "Medlemskab ikke verificeret",
|
||||
"Fair": "Messe",
|
||||
"Family friendly": "Familievenlig",
|
||||
"Filter": "Filter",
|
||||
"Filter and sort": "Filtrer og sorter",
|
||||
"Filter by": "Filtrer efter",
|
||||
@@ -191,6 +194,7 @@
|
||||
"Hi {firstName}!": "Hei {firstName}!",
|
||||
"High floor": "Højt niveau",
|
||||
"Highest level": "Højeste niveau",
|
||||
"Hiking": "Vandring",
|
||||
"Home": "Hjem",
|
||||
"Hospital": "Hospital",
|
||||
"Hotel": "Hotel",
|
||||
@@ -219,6 +223,7 @@
|
||||
"Join at no cost": "Tilmeld dig uden omkostninger",
|
||||
"Join now": "Tilmeld dig nu",
|
||||
"Join or log in while booking for member pricing.": "Tilmeld dig eller log ind under booking for medlemspris.",
|
||||
"Kayaking": "Kajakroning",
|
||||
"King bed": "Kingsize-seng",
|
||||
"Language": "Sprog",
|
||||
"Last name": "Efternavn",
|
||||
@@ -247,6 +252,8 @@
|
||||
"Main menu": "Hovedmenu",
|
||||
"Manage preferences": "Administrer præferencer",
|
||||
"Map": "Kort",
|
||||
"Map of the city center": "Kort over byens centrum",
|
||||
"Map of the country": "Kort over landet",
|
||||
"Map of {hotelName}": "Map of {hotelName}",
|
||||
"Marketing city": "Marketing by",
|
||||
"Max {max, plural, one {{range} guest} other {{range} guests}}": "Maks {max, plural, one {{range} gæst} other {{range} gæster}}",
|
||||
@@ -269,6 +276,7 @@
|
||||
"Monday": "Mandag",
|
||||
"Month": "Måned",
|
||||
"Museum": "Museum",
|
||||
"Museums": "Museer",
|
||||
"My communication preferences": "Mine kommunikationspræferencer",
|
||||
"My membership cards": "Mine medlemskort",
|
||||
"My pages": "Mine sider",
|
||||
@@ -282,6 +290,7 @@
|
||||
"Nearby companies": "Nærliggende virksomheder",
|
||||
"New password": "Nyt kodeord",
|
||||
"Next": "Næste",
|
||||
"Nightlife": "Natteliv",
|
||||
"Nights needed to level up": "Nætter nødvendige for at komme i niveau",
|
||||
"No": "Nej",
|
||||
"No availability": "Ingen tilgængelighed",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"Bed": "Bettentyp",
|
||||
"Bed options": "Bettoptionen",
|
||||
"Bed type": "Bettentyp",
|
||||
"Bike friendly": "Fahrradfreundlich",
|
||||
"Birth date": "Geburtsdatum",
|
||||
"Book": "Buchen",
|
||||
"Book a table online": "Tisch online buchen",
|
||||
@@ -90,6 +91,7 @@
|
||||
"Choose room": "Zimmer wählen",
|
||||
"Cities": "Städte",
|
||||
"City": "Stadt",
|
||||
"City pulse": "Stadtpuls",
|
||||
"City/State": "Stadt/Zustand",
|
||||
"Clear all filters": "Alle Filter löschen",
|
||||
"Clear searches": "Suche löschen",
|
||||
@@ -158,6 +160,7 @@
|
||||
"Failed to delete credit card, please try again later.": "Kreditkarte konnte nicht gelöscht werden. Bitte versuchen Sie es später noch einmal.",
|
||||
"Failed to verify membership": "Medlemskab nicht verifiziert",
|
||||
"Fair": "Messe",
|
||||
"Family friendly": "Familienfreundlich",
|
||||
"Filter": "Filter",
|
||||
"Filter and sort": "Filtern und sortieren",
|
||||
"Filter by": "Filtern nach",
|
||||
@@ -190,6 +193,7 @@
|
||||
"Hi {firstName}!": "Hallo {firstName}!",
|
||||
"High floor": "Hohes Level",
|
||||
"Highest level": "Höchstes Level",
|
||||
"Hiking": "Wandern",
|
||||
"Home": "Heim",
|
||||
"Hospital": "Krankenhaus",
|
||||
"Hotel": "Hotel",
|
||||
@@ -218,6 +222,7 @@
|
||||
"Join at no cost": "Kostenlos beitreten",
|
||||
"Join now": "Mitglied werden",
|
||||
"Join or log in while booking for member pricing.": "Treten Sie Scandic Friends bei oder loggen Sie sich ein, um den Mitgliederpreis zu erhalten.",
|
||||
"Kayaking": "Kajakfahren",
|
||||
"King bed": "Kingsize-Bett",
|
||||
"Language": "Sprache",
|
||||
"Last name": "Nachname",
|
||||
@@ -246,6 +251,8 @@
|
||||
"Main menu": "Hauptmenü",
|
||||
"Manage preferences": "Verwalten von Voreinstellungen",
|
||||
"Map": "Karte",
|
||||
"Map of the city center": "Karte des Stadtzentrums",
|
||||
"Map of the country": "Karte des Landes",
|
||||
"Map of {hotelName}": "Map of {hotelName}",
|
||||
"Marketing city": "Marketingstadt",
|
||||
"Max {max, plural, one {{range} guest} other {{range} guests}}": "Max {max, plural, one {{range} gast} other {{range} gäste}}",
|
||||
@@ -267,6 +274,7 @@
|
||||
"Monday": "Montag",
|
||||
"Month": "Monat",
|
||||
"Museum": "Museum",
|
||||
"Museums": "Museen",
|
||||
"My communication preferences": "Meine Kommunikationseinstellungen",
|
||||
"My membership cards": "Meine Mitgliedskarten",
|
||||
"My pages": "Meine Seiten",
|
||||
@@ -280,6 +288,7 @@
|
||||
"Nearby companies": "Nahe gelegene Unternehmen",
|
||||
"New password": "Neues Kennwort",
|
||||
"Next": "Nächste",
|
||||
"Nightlife": "Nachtleben",
|
||||
"Nights needed to level up": "Nächte, die zum Levelaufstieg benötigt werden",
|
||||
"No": "Nein",
|
||||
"No availability": "Keine Verfügbarkeit",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"Bed": "Bed",
|
||||
"Bed options": "Bed options",
|
||||
"Bed type": "Bed type",
|
||||
"Bike friendly": "Bike friendly",
|
||||
"Birth date": "Birth date",
|
||||
"Book": "Book",
|
||||
"Book a table online": "Book a table online",
|
||||
@@ -98,6 +99,7 @@
|
||||
"Choose room": "Choose room",
|
||||
"Cities": "Cities",
|
||||
"City": "City",
|
||||
"City pulse": "City pulse",
|
||||
"City/State": "City/State",
|
||||
"Clear all filters": "Clear all filters",
|
||||
"Clear searches": "Clear searches",
|
||||
@@ -170,6 +172,7 @@
|
||||
"Failed to delete credit card, please try again later.": "Failed to delete credit card, please try again later.",
|
||||
"Failed to verify membership": "Failed to verify membership",
|
||||
"Fair": "Fair",
|
||||
"Family friendly": "Family friendly",
|
||||
"Filter": "Filter",
|
||||
"Filter and sort": "Filter and sort",
|
||||
"Filter by": "Filter by",
|
||||
@@ -207,6 +210,7 @@
|
||||
"Hi {firstName}!": "Hi {firstName}!",
|
||||
"High floor": "High floor",
|
||||
"Highest level": "Highest level",
|
||||
"Hiking": "Hiking",
|
||||
"Home": "Home",
|
||||
"Hospital": "Hospital",
|
||||
"Hotel": "Hotel",
|
||||
@@ -235,6 +239,7 @@
|
||||
"Join at no cost": "Join at no cost",
|
||||
"Join now": "Join now",
|
||||
"Join or log in while booking for member pricing.": "Join or log in while booking for member pricing.",
|
||||
"Kayaking": "Kayaking",
|
||||
"King bed": "King bed",
|
||||
"Language": "Language",
|
||||
"Last name": "Last name",
|
||||
@@ -265,6 +270,8 @@
|
||||
"Manage booking": "Manage booking",
|
||||
"Manage preferences": "Manage preferences",
|
||||
"Map": "Map",
|
||||
"Map of the city center": "Map of the city center",
|
||||
"Map of the country": "Map of the country",
|
||||
"Map of {hotelName}": "Map of {hotelName}",
|
||||
"Marketing city": "Marketing city",
|
||||
"Max {max, plural, one {{range} guest} other {{range} guests}}": "Max {max, plural, one {{range} guest} other {{range} guests}}",
|
||||
@@ -291,6 +298,7 @@
|
||||
"Monday": "Monday",
|
||||
"Month": "Month",
|
||||
"Museum": "Museum",
|
||||
"Museums": "Museums",
|
||||
"My communication preferences": "My communication preferences",
|
||||
"My membership cards": "My membership cards",
|
||||
"My pages": "My pages",
|
||||
@@ -304,6 +312,7 @@
|
||||
"Nearby companies": "Nearby companies",
|
||||
"New password": "New password",
|
||||
"Next": "Next",
|
||||
"Nightlife": "Nightlife",
|
||||
"Nights needed to level up": "Nights needed to level up",
|
||||
"No": "No",
|
||||
"No availability": "No availability",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"Bed": "Vuodetyyppi",
|
||||
"Bed options": "Vuodevaihtoehdot",
|
||||
"Bed type": "Vuodetyyppi",
|
||||
"Bike friendly": "Pyöräystävällinen",
|
||||
"Birth date": "Syntymäaika",
|
||||
"Book": "Varaa",
|
||||
"Book a table online": "Varaa pöytä verkossa",
|
||||
@@ -91,6 +92,7 @@
|
||||
"Choose room": "Valitse huone",
|
||||
"Cities": "Kaupungit",
|
||||
"City": "Kaupunki",
|
||||
"City pulse": "Kaupungin syke",
|
||||
"City/State": "Kaupunki/Osavaltio",
|
||||
"Clear all filters": "Tyhjennä kaikki suodattimet",
|
||||
"Clear searches": "Tyhjennä haut",
|
||||
@@ -159,6 +161,7 @@
|
||||
"Failed to delete credit card, please try again later.": "Luottokortin poistaminen epäonnistui, yritä myöhemmin uudelleen.",
|
||||
"Failed to verify membership": "Jäsenyys ei verifioitu",
|
||||
"Fair": "Messukeskus",
|
||||
"Family friendly": "Perheystävällinen",
|
||||
"Filter": "Suodatin",
|
||||
"Filter and sort": "Suodata ja lajittele",
|
||||
"Filter by": "Suodatusperuste",
|
||||
@@ -191,6 +194,7 @@
|
||||
"Hi {firstName}!": "Hi {firstName}!",
|
||||
"High floor": "Korkea taso",
|
||||
"Highest level": "Korkein taso",
|
||||
"Hiking": "Hiking",
|
||||
"Home": "Kotiin",
|
||||
"Hospital": "Sairaala",
|
||||
"Hotel": "Hotelli",
|
||||
@@ -219,6 +223,7 @@
|
||||
"Join at no cost": "Liity maksutta",
|
||||
"Join now": "Liity jäseneksi",
|
||||
"Join or log in while booking for member pricing.": "Liity tai kirjaudu sisään, kun varaat jäsenhinnan.",
|
||||
"Kayaking": "Melonta",
|
||||
"King bed": "King-vuode",
|
||||
"Language": "Kieli",
|
||||
"Last name": "Sukunimi",
|
||||
@@ -247,6 +252,8 @@
|
||||
"Main menu": "Päävalikko",
|
||||
"Manage preferences": "Asetusten hallinta",
|
||||
"Map": "Kartta",
|
||||
"Map of the city center": "Kartta kaupungin keskustasta",
|
||||
"Map of the country": "Kartta maasta",
|
||||
"Map of {hotelName}": "Map of {hotelName}",
|
||||
"Marketing city": "Markkinointikaupunki",
|
||||
"Max {max, plural, one {{range} guest} other {{range} guests}}": "Max {max, plural, one {{range} vieras} other {{range} vieraita}}",
|
||||
@@ -269,6 +276,7 @@
|
||||
"Monday": "Maanantai",
|
||||
"Month": "Kuukausi",
|
||||
"Museum": "Museo",
|
||||
"Museums": "Museot",
|
||||
"My communication preferences": "Viestintämieltymykseni",
|
||||
"My membership cards": "Jäsenkorttini",
|
||||
"My pages": "Omat sivut",
|
||||
@@ -282,6 +290,7 @@
|
||||
"Nearby companies": "Läheiset yritykset",
|
||||
"New password": "Uusi salasana",
|
||||
"Next": "Seuraava",
|
||||
"Nightlife": "Yöelämä",
|
||||
"Nights needed to level up": "Yöt, joita tarvitaan tasolle",
|
||||
"No": "Ei",
|
||||
"No availability": "Ei saatavuutta",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"Bed": "Seng type",
|
||||
"Bed options": "Sengemuligheter",
|
||||
"Bed type": "Seng type",
|
||||
"Bike friendly": "Sykkelvennlig",
|
||||
"Birth date": "Fødselsdato",
|
||||
"Book": "Bestill",
|
||||
"Book a table online": "Bestill bord online",
|
||||
@@ -91,6 +92,7 @@
|
||||
"Choose room": "Velg rom",
|
||||
"Cities": "Byer",
|
||||
"City": "By",
|
||||
"City pulse": "Byens puls",
|
||||
"City/State": "By/Stat",
|
||||
"Clear all filters": "Fjern alle filtre",
|
||||
"Clear searches": "Tømme søk",
|
||||
@@ -158,6 +160,7 @@
|
||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.",
|
||||
"Failed to verify membership": "Medlemskap ikke verifisert",
|
||||
"Fair": "Messe",
|
||||
"Family friendly": "Familievennlig",
|
||||
"Filter": "Filter",
|
||||
"Filter and sort": "Filtrer og sorter",
|
||||
"Filter by": "Filtrer etter",
|
||||
@@ -190,6 +193,7 @@
|
||||
"Hi {firstName}!": "Hei {firstName}!",
|
||||
"High floor": "Høy nivå",
|
||||
"Highest level": "Høyeste nivå",
|
||||
"Hiking": "Fotturer",
|
||||
"Home": "Hjem",
|
||||
"Hospital": "Sykehus",
|
||||
"Hotel": "Hotel",
|
||||
@@ -218,6 +222,7 @@
|
||||
"Join at no cost": "Bli med uten kostnad",
|
||||
"Join now": "Bli medlem nå",
|
||||
"Join or log in while booking for member pricing.": "Bli med eller logg inn under bestilling for medlemspris.",
|
||||
"Kayaking": "Kajakkpadling",
|
||||
"King bed": "King-size-seng",
|
||||
"Language": "Språk",
|
||||
"Last name": "Etternavn",
|
||||
@@ -246,7 +251,9 @@
|
||||
"Main menu": "Hovedmeny",
|
||||
"Manage preferences": "Administrer preferanser",
|
||||
"Map": "Kart",
|
||||
"Map of {hotelName}": "Map of {hotelName}",
|
||||
"Map of the city center": "Kart over sentrum",
|
||||
"Map of the country": "Kart over landet",
|
||||
"Map of {hotelName}": "Kart over {hotelName}",
|
||||
"Marketing city": "Markedsføringsby",
|
||||
"Max {max, plural, one {{range} guest} other {{range} guests}}": "Maks {max, plural, one {{range} gjest} other {{range} gjester}}",
|
||||
"Meetings & Conferences": "Møter & Konferanser",
|
||||
@@ -268,6 +275,7 @@
|
||||
"Monday": "Mandag",
|
||||
"Month": "Måned",
|
||||
"Museum": "Museum",
|
||||
"Museums": "Museums",
|
||||
"My communication preferences": "Mine kommunikasjonspreferanser",
|
||||
"My membership cards": "Mine medlemskort",
|
||||
"My pages": "Mine sider",
|
||||
@@ -281,6 +289,7 @@
|
||||
"Nearby companies": "Nærliggende selskaper",
|
||||
"New password": "Nytt passord",
|
||||
"Next": "Neste",
|
||||
"Nightlife": "Natteliv",
|
||||
"Nights needed to level up": "Netter som trengs for å komme opp i nivå",
|
||||
"No": "Nei",
|
||||
"No availability": "Ingen tilgjengelighet",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"Bed": "Sängtyp",
|
||||
"Bed options": "Sängalternativ",
|
||||
"Bed type": "Sängtyp",
|
||||
"Bike friendly": "Cykelvänligt",
|
||||
"Birth date": "Födelsedatum",
|
||||
"Book": "Boka",
|
||||
"Book a table online": "Boka ett bord online",
|
||||
@@ -91,6 +92,7 @@
|
||||
"Choose room": "Välj rum",
|
||||
"Cities": "Städer",
|
||||
"City": "Ort",
|
||||
"City pulse": "Stadspuls",
|
||||
"City/State": "Ort",
|
||||
"Clear all filters": "Rensa alla filter",
|
||||
"Clear searches": "Rensa tidigare sökningar",
|
||||
@@ -158,6 +160,7 @@
|
||||
"Failed to delete credit card, please try again later.": "Det gick inte att ta bort kreditkortet, försök igen senare.",
|
||||
"Failed to verify membership": "Medlemskap inte verifierat",
|
||||
"Fair": "Mässa",
|
||||
"Family friendly": "Familjevänligt",
|
||||
"Filter": "Filter",
|
||||
"Filter and sort": "Filtrera och sortera",
|
||||
"Filter by": "Filtrera på",
|
||||
@@ -190,6 +193,7 @@
|
||||
"Hi {firstName}!": "Hej {firstName}!",
|
||||
"High floor": "Högt upp",
|
||||
"Highest level": "Högsta nivå",
|
||||
"Hiking": "Vandring",
|
||||
"Home": "Hem",
|
||||
"Hospital": "Sjukhus",
|
||||
"Hotel": "Hotell",
|
||||
@@ -218,6 +222,7 @@
|
||||
"Join at no cost": "Gå med utan kostnad",
|
||||
"Join now": "Gå med nu",
|
||||
"Join or log in while booking for member pricing.": "Bli medlem eller logga in när du bokar för medlemspriser.",
|
||||
"Kayaking": "Kajakpaddling",
|
||||
"King bed": "King size-säng",
|
||||
"Language": "Språk",
|
||||
"Last name": "Efternamn",
|
||||
@@ -246,7 +251,9 @@
|
||||
"Main menu": "Huvudmeny",
|
||||
"Manage preferences": "Hantera inställningar",
|
||||
"Map": "Karta",
|
||||
"Map of {hotelName}": "Map of {hotelName}",
|
||||
"Map of the city center": "Karta över stadskärnan",
|
||||
"Map of the country": "Karta över landet",
|
||||
"Map of {hotelName}": "Karta över {hotelName}",
|
||||
"Marketing city": "Marknadsföringsstad",
|
||||
"Max {max, plural, one {{range} guest} other {{range} guests}}": "Max {max, plural, one {{range} gäst} other {{range} gäster}}",
|
||||
"Meetings & Conferences": "Möten & Konferenser",
|
||||
@@ -268,6 +275,7 @@
|
||||
"Monday": "Måndag",
|
||||
"Month": "Månad",
|
||||
"Museum": "Museum",
|
||||
"Museums": "Museer",
|
||||
"My communication preferences": "Mina kommunikationspreferenser",
|
||||
"My membership cards": "Mina medlemskort",
|
||||
"My pages": "Mina sidor",
|
||||
@@ -281,6 +289,7 @@
|
||||
"Nearby companies": "Närliggande företag",
|
||||
"New password": "Nytt lösenord",
|
||||
"Next": "Nästa",
|
||||
"Nightlife": "Nattliv",
|
||||
"Nights needed to level up": "Nätter som behövs för att gå upp i nivå",
|
||||
"No": "Nej",
|
||||
"No availability": "Ingen tillgänglighet",
|
||||
|
||||
7
lib/graphql/Fragments/DestinationCityPage/Ref.graphql
Normal file
7
lib/graphql/Fragments/DestinationCityPage/Ref.graphql
Normal file
@@ -0,0 +1,7 @@
|
||||
#import "../System.graphql"
|
||||
|
||||
fragment DestinationCityPageRef on DestinationCityPage {
|
||||
system {
|
||||
...System
|
||||
}
|
||||
}
|
||||
7
lib/graphql/Fragments/DestinationCountryPage/Ref.graphql
Normal file
7
lib/graphql/Fragments/DestinationCountryPage/Ref.graphql
Normal file
@@ -0,0 +1,7 @@
|
||||
#import "../System.graphql"
|
||||
|
||||
fragment DestinationCountryPageRef on DestinationCountryPage {
|
||||
system {
|
||||
...System
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,69 @@
|
||||
#import "../../Fragments/System.graphql"
|
||||
#import "../../Fragments/PageLink/ContentPageLink.graphql"
|
||||
#import "../../Fragments/PageLink/LoyaltyPageLink.graphql"
|
||||
#import "../../Fragments/PageLink/AccountPageLink.graphql"
|
||||
#import "../../Fragments/PageLink/CollectionPageLink.graphql"
|
||||
#import "../../Fragments/PageLink/HotelPageLink.graphql"
|
||||
|
||||
#import "../../Fragments/AccountPage/Ref.graphql"
|
||||
#import "../../Fragments/ContentPage/Ref.graphql"
|
||||
#import "../../Fragments/LoyaltyPage/Ref.graphql"
|
||||
#import "../../Fragments/HotelPage/Ref.graphql"
|
||||
#import "../../Fragments/CollectionPage/Ref.graphql"
|
||||
#import "../../Fragments/DestinationCountryPage/Ref.graphql"
|
||||
|
||||
query GetDestinationCityPage($locale: String!, $uid: String!) {
|
||||
destination_city_page(uid: $uid, locale: $locale) {
|
||||
title
|
||||
destination_settings {
|
||||
countryConnection {
|
||||
edges {
|
||||
node {
|
||||
... on DestinationCountryPage {
|
||||
title
|
||||
url
|
||||
destination_settings {
|
||||
country
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
city_denmark
|
||||
city_finland
|
||||
city_germany
|
||||
city_norway
|
||||
city_poland
|
||||
city_sweden
|
||||
}
|
||||
heading
|
||||
preamble
|
||||
experiences {
|
||||
destination_experiences
|
||||
}
|
||||
images {
|
||||
image
|
||||
}
|
||||
has_sidepeek
|
||||
sidepeek_button_text
|
||||
sidepeek_content {
|
||||
heading
|
||||
content {
|
||||
embedded_itemsConnection {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...AccountPageLink
|
||||
...ContentPageLink
|
||||
...CollectionPageLink
|
||||
...HotelPageLink
|
||||
...LoyaltyPageLink
|
||||
}
|
||||
}
|
||||
}
|
||||
json
|
||||
}
|
||||
}
|
||||
system {
|
||||
...System
|
||||
created_at
|
||||
@@ -16,6 +77,32 @@ query GetDestinationCityPage($locale: String!, $uid: String!) {
|
||||
|
||||
query GetDestinationCityPageRefs($locale: String!, $uid: String!) {
|
||||
destination_city_page(locale: $locale, uid: $uid) {
|
||||
destination_settings {
|
||||
countryConnection {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...DestinationCountryPageRef
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sidepeek_content {
|
||||
content {
|
||||
embedded_itemsConnection {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...AccountPageRef
|
||||
...ContentPageRef
|
||||
...LoyaltyPageRef
|
||||
...HotelPageRef
|
||||
...CollectionPageRef
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
system {
|
||||
...System
|
||||
}
|
||||
|
||||
@@ -1,8 +1,50 @@
|
||||
#import "../../Fragments/System.graphql"
|
||||
#import "../../Fragments/PageLink/ContentPageLink.graphql"
|
||||
#import "../../Fragments/PageLink/LoyaltyPageLink.graphql"
|
||||
#import "../../Fragments/PageLink/AccountPageLink.graphql"
|
||||
#import "../../Fragments/PageLink/CollectionPageLink.graphql"
|
||||
#import "../../Fragments/PageLink/HotelPageLink.graphql"
|
||||
|
||||
#import "../../Fragments/AccountPage/Ref.graphql"
|
||||
#import "../../Fragments/ContentPage/Ref.graphql"
|
||||
#import "../../Fragments/LoyaltyPage/Ref.graphql"
|
||||
#import "../../Fragments/HotelPage/Ref.graphql"
|
||||
#import "../../Fragments/CollectionPage/Ref.graphql"
|
||||
|
||||
query GetDestinationCountryPage($locale: String!, $uid: String!) {
|
||||
destination_country_page(uid: $uid, locale: $locale) {
|
||||
title
|
||||
destination_settings {
|
||||
country
|
||||
}
|
||||
heading
|
||||
preamble
|
||||
experiences {
|
||||
destination_experiences
|
||||
}
|
||||
images {
|
||||
image
|
||||
}
|
||||
has_sidepeek
|
||||
sidepeek_button_text
|
||||
sidepeek_content {
|
||||
heading
|
||||
content {
|
||||
embedded_itemsConnection {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...AccountPageLink
|
||||
...ContentPageLink
|
||||
...CollectionPageLink
|
||||
...HotelPageLink
|
||||
...LoyaltyPageLink
|
||||
}
|
||||
}
|
||||
}
|
||||
json
|
||||
}
|
||||
}
|
||||
system {
|
||||
...System
|
||||
created_at
|
||||
@@ -16,6 +58,22 @@ query GetDestinationCountryPage($locale: String!, $uid: String!) {
|
||||
|
||||
query GetDestinationCountryPageRefs($locale: String!, $uid: String!) {
|
||||
destination_country_page(locale: $locale, uid: $uid) {
|
||||
sidepeek_content {
|
||||
content {
|
||||
embedded_itemsConnection {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
...AccountPageRef
|
||||
...ContentPageRef
|
||||
...LoyaltyPageRef
|
||||
...HotelPageRef
|
||||
...CollectionPageRef
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
system {
|
||||
...System
|
||||
}
|
||||
|
||||
@@ -1,25 +1,180 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import * as pageLinks from "@/server/routers/contentstack/schemas/pageLinks"
|
||||
|
||||
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
|
||||
import { systemSchema } from "../schemas/system"
|
||||
|
||||
export const destinationCityPageSchema = z.object({
|
||||
destination_city_page: z.object({
|
||||
title: z.string(),
|
||||
system: systemSchema.merge(
|
||||
z.object({
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
trackingProps: z.object({
|
||||
url: z.string(),
|
||||
}),
|
||||
})
|
||||
import type { ImageVaultAsset } from "@/types/components/imageVault"
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKPageData,
|
||||
} from "@/types/components/tracking"
|
||||
import { Country } from "@/types/enums/country"
|
||||
|
||||
export const destinationCityPageSchema = z
|
||||
.object({
|
||||
destination_city_page: z.object({
|
||||
title: z.string(),
|
||||
destination_settings: z
|
||||
.object({
|
||||
countryConnection: z
|
||||
.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.object({
|
||||
destination_settings: z.object({
|
||||
country: z.nativeEnum(Country),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
),
|
||||
})
|
||||
.transform(
|
||||
(countryConnection) =>
|
||||
countryConnection.edges[0].node.destination_settings.country
|
||||
),
|
||||
city_denmark: z.string().optional().nullable(),
|
||||
city_finland: z.string().optional().nullable(),
|
||||
city_germany: z.string().optional().nullable(),
|
||||
city_poland: z.string().optional().nullable(),
|
||||
city_norway: z.string().optional().nullable(),
|
||||
city_sweden: z.string().optional().nullable(),
|
||||
})
|
||||
.transform(
|
||||
({
|
||||
countryConnection: country,
|
||||
city_denmark,
|
||||
city_finland,
|
||||
city_germany,
|
||||
city_norway,
|
||||
city_poland,
|
||||
city_sweden,
|
||||
}) => {
|
||||
switch (country) {
|
||||
case Country.Denmark:
|
||||
return { country, city: city_denmark }
|
||||
case Country.Finland:
|
||||
return { country, city: city_finland }
|
||||
case Country.Germany:
|
||||
return { country, city: city_germany }
|
||||
case Country.Poland:
|
||||
return { country, city: city_poland }
|
||||
case Country.Norway:
|
||||
return { country, city: city_norway }
|
||||
case Country.Sweden:
|
||||
return { country, city: city_sweden }
|
||||
default:
|
||||
throw new Error(`Invalid country: ${country}`)
|
||||
}
|
||||
}
|
||||
),
|
||||
heading: z.string(),
|
||||
preamble: z.string(),
|
||||
experiences: z
|
||||
.object({
|
||||
destination_experiences: z.array(z.string()),
|
||||
})
|
||||
.transform(({ destination_experiences }) => destination_experiences),
|
||||
images: z
|
||||
.array(z.object({ image: tempImageVaultAssetSchema }))
|
||||
.transform((images) =>
|
||||
images
|
||||
.map((image) => image.image)
|
||||
.filter((image): image is ImageVaultAsset => !!image)
|
||||
),
|
||||
has_sidepeek: z.boolean().default(false),
|
||||
sidepeek_button_text: z.string().default(""),
|
||||
sidepeek_content: z.object({
|
||||
heading: z.string(),
|
||||
content: z.object({
|
||||
json: z.any(),
|
||||
embedded_itemsConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z
|
||||
.discriminatedUnion("__typename", [
|
||||
pageLinks.accountPageSchema,
|
||||
pageLinks.contentPageSchema,
|
||||
pageLinks.hotelPageSchema,
|
||||
pageLinks.loyaltyPageSchema,
|
||||
pageLinks.collectionPageSchema,
|
||||
])
|
||||
.transform((data) => {
|
||||
const link = pageLinks.transform(data)
|
||||
if (link) {
|
||||
return link
|
||||
}
|
||||
return data
|
||||
}),
|
||||
})
|
||||
),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
system: systemSchema.merge(
|
||||
z.object({
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
trackingProps: z.object({
|
||||
url: z.string(),
|
||||
}),
|
||||
})
|
||||
.transform((data) => {
|
||||
const destinationCityPage = data.destination_city_page
|
||||
const system = destinationCityPage.system
|
||||
const trackingUrl = data.trackingProps.url
|
||||
|
||||
const tracking: TrackingSDKPageData = {
|
||||
pageId: system.uid,
|
||||
domainLanguage: system.locale,
|
||||
publishDate: system.updated_at,
|
||||
createDate: system.created_at,
|
||||
channel: TrackingChannelEnum["destination-page"],
|
||||
pageType: "staticcontentpage",
|
||||
pageName: trackingUrl,
|
||||
siteSections: trackingUrl,
|
||||
siteVersion: "new-web",
|
||||
}
|
||||
|
||||
return {
|
||||
destinationCityPage,
|
||||
tracking,
|
||||
}
|
||||
})
|
||||
|
||||
/** REFS */
|
||||
export const destinationCityPageRefsSchema = z.object({
|
||||
destination_city_page: z.object({
|
||||
destination_settings: z.object({
|
||||
countryConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: pageLinks.destinationCountryPageRefSchema,
|
||||
})
|
||||
),
|
||||
}),
|
||||
}),
|
||||
sidepeek_content: z.object({
|
||||
content: z.object({
|
||||
embedded_itemsConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.discriminatedUnion("__typename", [
|
||||
pageLinks.accountPageRefSchema,
|
||||
pageLinks.contentPageRefSchema,
|
||||
pageLinks.hotelPageRefSchema,
|
||||
pageLinks.loyaltyPageRefSchema,
|
||||
pageLinks.collectionPageRefSchema,
|
||||
]),
|
||||
})
|
||||
),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
system: systemSchema,
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -20,11 +20,8 @@ import {
|
||||
getDestinationCityPageRefsSuccessCounter,
|
||||
getDestinationCityPageSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import { generatePageTags } from "./utils"
|
||||
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKPageData,
|
||||
} from "@/types/components/tracking"
|
||||
import type {
|
||||
GetDestinationCityPageData,
|
||||
GetDestinationCityPageRefsSchema,
|
||||
@@ -91,6 +88,8 @@ export const destinationCityPageQueryRouter = router({
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
)
|
||||
|
||||
const tags = generatePageTags(validatedRefsData.data, lang)
|
||||
|
||||
getDestinationCityPageCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationCityPage start",
|
||||
@@ -107,7 +106,7 @@ export const destinationCityPageQueryRouter = router({
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [generateTag(lang, uid)],
|
||||
tags,
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -129,22 +128,22 @@ export const destinationCityPageQueryRouter = router({
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const destinationCityPage = destinationCityPageSchema.safeParse(
|
||||
const validatedDestinationCityPage = destinationCityPageSchema.safeParse(
|
||||
response.data
|
||||
)
|
||||
|
||||
if (!destinationCityPage.success) {
|
||||
if (!validatedDestinationCityPage.success) {
|
||||
getDestinationCityPageFailCounter.add(1, {
|
||||
lang,
|
||||
uid: `${uid}`,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(destinationCityPage.error),
|
||||
error: JSON.stringify(validatedDestinationCityPage.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.destinationCityPage validation error",
|
||||
JSON.stringify({
|
||||
query: { lang, uid },
|
||||
error: destinationCityPage.error,
|
||||
error: validatedDestinationCityPage.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
@@ -158,22 +157,6 @@ export const destinationCityPageQueryRouter = router({
|
||||
})
|
||||
)
|
||||
|
||||
const system = destinationCityPage.data.destination_city_page.system
|
||||
const tracking: TrackingSDKPageData = {
|
||||
pageId: system.uid,
|
||||
domainLanguage: lang,
|
||||
publishDate: system.updated_at,
|
||||
createDate: system.created_at,
|
||||
channel: TrackingChannelEnum["destination-page"],
|
||||
pageType: "staticcontentpage",
|
||||
pageName: destinationCityPage.data.trackingProps.url,
|
||||
siteSections: destinationCityPage.data.trackingProps.url,
|
||||
siteVersion: "new-web",
|
||||
}
|
||||
|
||||
return {
|
||||
destinationCityPage: destinationCityPage.data.destination_city_page,
|
||||
tracking,
|
||||
}
|
||||
return validatedDestinationCityPage.data
|
||||
}),
|
||||
})
|
||||
|
||||
37
server/routers/contentstack/destinationCityPage/utils.ts
Normal file
37
server/routers/contentstack/destinationCityPage/utils.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
||||
|
||||
import type { System } from "@/types/requests/system"
|
||||
import type { GetDestinationCityPageRefsSchema } from "@/types/trpc/routers/contentstack/destinationCityPage"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
export function generatePageTags(
|
||||
validatedData: GetDestinationCityPageRefsSchema,
|
||||
lang: Lang
|
||||
): string[] {
|
||||
const connections = getConnections(validatedData)
|
||||
return [
|
||||
generateTagsFromSystem(lang, connections),
|
||||
generateTag(lang, validatedData.destination_city_page.system.uid),
|
||||
].flat()
|
||||
}
|
||||
|
||||
export function getConnections({
|
||||
destination_city_page,
|
||||
}: GetDestinationCityPageRefsSchema) {
|
||||
const connections: System["system"][] = [destination_city_page.system]
|
||||
|
||||
connections.push(
|
||||
destination_city_page.destination_settings.countryConnection.edges[0].node
|
||||
.system
|
||||
)
|
||||
|
||||
if (destination_city_page.sidepeek_content) {
|
||||
destination_city_page.sidepeek_content.content.embedded_itemsConnection.edges.forEach(
|
||||
({ node }) => {
|
||||
connections.push(node.system)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return connections
|
||||
}
|
||||
@@ -1,25 +1,120 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import * as pageLinks from "@/server/routers/contentstack/schemas/pageLinks"
|
||||
|
||||
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
|
||||
import { systemSchema } from "../schemas/system"
|
||||
|
||||
export const destinationCountryPageSchema = z.object({
|
||||
destination_country_page: z.object({
|
||||
title: z.string(),
|
||||
system: systemSchema.merge(
|
||||
z.object({
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
trackingProps: z.object({
|
||||
url: z.string(),
|
||||
}),
|
||||
})
|
||||
import type { ImageVaultAsset } from "@/types/components/imageVault"
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKPageData,
|
||||
} from "@/types/components/tracking"
|
||||
import { Country } from "@/types/enums/country"
|
||||
|
||||
export const destinationCountryPageSchema = z
|
||||
.object({
|
||||
destination_country_page: z.object({
|
||||
title: z.string(),
|
||||
destination_settings: z.object({
|
||||
country: z.nativeEnum(Country),
|
||||
}),
|
||||
heading: z.string(),
|
||||
preamble: z.string(),
|
||||
experiences: z
|
||||
.object({
|
||||
destination_experiences: z.array(z.string()),
|
||||
})
|
||||
.transform(({ destination_experiences }) => destination_experiences),
|
||||
images: z
|
||||
.array(z.object({ image: tempImageVaultAssetSchema }))
|
||||
.transform((images) =>
|
||||
images
|
||||
.map((image) => image.image)
|
||||
.filter((image): image is ImageVaultAsset => !!image)
|
||||
),
|
||||
has_sidepeek: z.boolean().default(false),
|
||||
sidepeek_button_text: z.string().default(""),
|
||||
sidepeek_content: z.object({
|
||||
heading: z.string(),
|
||||
content: z.object({
|
||||
json: z.any(),
|
||||
embedded_itemsConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z
|
||||
.discriminatedUnion("__typename", [
|
||||
pageLinks.accountPageSchema,
|
||||
pageLinks.contentPageSchema,
|
||||
pageLinks.hotelPageSchema,
|
||||
pageLinks.loyaltyPageSchema,
|
||||
pageLinks.collectionPageSchema,
|
||||
])
|
||||
.transform((data) => {
|
||||
const link = pageLinks.transform(data)
|
||||
if (link) {
|
||||
return link
|
||||
}
|
||||
return data
|
||||
}),
|
||||
})
|
||||
),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
system: systemSchema.merge(
|
||||
z.object({
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
trackingProps: z.object({
|
||||
url: z.string(),
|
||||
}),
|
||||
})
|
||||
.transform((data) => {
|
||||
const countryPageData = data.destination_country_page
|
||||
const system = countryPageData.system
|
||||
const trackingUrl = data.trackingProps.url
|
||||
const tracking: TrackingSDKPageData = {
|
||||
pageId: system.uid,
|
||||
domainLanguage: system.locale,
|
||||
publishDate: system.updated_at,
|
||||
createDate: system.created_at,
|
||||
channel: TrackingChannelEnum["destination-page"],
|
||||
pageType: "staticcontentpage",
|
||||
pageName: trackingUrl,
|
||||
siteSections: trackingUrl,
|
||||
siteVersion: "new-web",
|
||||
}
|
||||
|
||||
return {
|
||||
destinationCountryPage: countryPageData,
|
||||
tracking,
|
||||
}
|
||||
})
|
||||
|
||||
/** REFS */
|
||||
export const destinationCountryPageRefsSchema = z.object({
|
||||
destination_country_page: z.object({
|
||||
sidepeek_content: z.object({
|
||||
content: z.object({
|
||||
embedded_itemsConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.discriminatedUnion("__typename", [
|
||||
pageLinks.accountPageRefSchema,
|
||||
pageLinks.contentPageRefSchema,
|
||||
pageLinks.hotelPageRefSchema,
|
||||
pageLinks.loyaltyPageRefSchema,
|
||||
pageLinks.collectionPageRefSchema,
|
||||
]),
|
||||
})
|
||||
),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
system: systemSchema,
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -20,11 +20,8 @@ import {
|
||||
getDestinationCountryPageRefsSuccessCounter,
|
||||
getDestinationCountryPageSuccessCounter,
|
||||
} from "./telemetry"
|
||||
import { generatePageTags } from "./utils"
|
||||
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKPageData,
|
||||
} from "@/types/components/tracking"
|
||||
import type {
|
||||
GetDestinationCountryPageData,
|
||||
GetDestinationCountryPageRefsSchema,
|
||||
@@ -91,6 +88,8 @@ export const destinationCountryPageQueryRouter = router({
|
||||
JSON.stringify({ query: { lang, uid } })
|
||||
)
|
||||
|
||||
const tags = generatePageTags(validatedRefsData.data, lang)
|
||||
|
||||
getDestinationCountryPageCounter.add(1, { lang, uid: `${uid}` })
|
||||
console.info(
|
||||
"contentstack.destinationCountryPage start",
|
||||
@@ -107,7 +106,7 @@ export const destinationCountryPageQueryRouter = router({
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [generateTag(lang, uid)],
|
||||
tags,
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -158,23 +157,6 @@ export const destinationCountryPageQueryRouter = router({
|
||||
})
|
||||
)
|
||||
|
||||
const system = destinationCountryPage.data.destination_country_page.system
|
||||
const tracking: TrackingSDKPageData = {
|
||||
pageId: system.uid,
|
||||
domainLanguage: lang,
|
||||
publishDate: system.updated_at,
|
||||
createDate: system.created_at,
|
||||
channel: TrackingChannelEnum["destination-page"],
|
||||
pageType: "staticcontentpage",
|
||||
pageName: destinationCountryPage.data.trackingProps.url,
|
||||
siteSections: destinationCountryPage.data.trackingProps.url,
|
||||
siteVersion: "new-web",
|
||||
}
|
||||
|
||||
return {
|
||||
destinationCountryPage:
|
||||
destinationCountryPage.data.destination_country_page,
|
||||
tracking,
|
||||
}
|
||||
return destinationCountryPage.data
|
||||
}),
|
||||
})
|
||||
|
||||
32
server/routers/contentstack/destinationCountryPage/utils.ts
Normal file
32
server/routers/contentstack/destinationCountryPage/utils.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { generateTag, generateTagsFromSystem } from "@/utils/generateTag"
|
||||
|
||||
import type { System } from "@/types/requests/system"
|
||||
import type { GetDestinationCountryPageRefsSchema } from "@/types/trpc/routers/contentstack/destinationCountryPage"
|
||||
import type { Lang } from "@/constants/languages"
|
||||
|
||||
export function generatePageTags(
|
||||
validatedData: GetDestinationCountryPageRefsSchema,
|
||||
lang: Lang
|
||||
): string[] {
|
||||
const connections = getConnections(validatedData)
|
||||
return [
|
||||
generateTagsFromSystem(lang, connections),
|
||||
generateTag(lang, validatedData.destination_country_page.system.uid),
|
||||
].flat()
|
||||
}
|
||||
|
||||
export function getConnections({
|
||||
destination_country_page,
|
||||
}: GetDestinationCountryPageRefsSchema) {
|
||||
const connections: System["system"][] = [destination_country_page.system]
|
||||
|
||||
if (destination_country_page.sidepeek_content) {
|
||||
destination_country_page.sidepeek_content.content.embedded_itemsConnection.edges.forEach(
|
||||
({ node }) => {
|
||||
connections.push(node.system)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return connections
|
||||
}
|
||||
@@ -52,6 +52,33 @@ export const contentPageRefSchema = z.object({
|
||||
system: systemSchema,
|
||||
})
|
||||
|
||||
export const destinationCityPageSchema = z
|
||||
.object({
|
||||
__typename: z.literal(ContentEnum.blocks.DestinationCityPage),
|
||||
})
|
||||
.merge(pageLinkSchema)
|
||||
|
||||
export const destinationCityPageRefSchema = z.object({
|
||||
__typename: z.literal(ContentEnum.blocks.DestinationCityPage),
|
||||
system: systemSchema,
|
||||
})
|
||||
|
||||
export const destinationCountryPageSchema = z
|
||||
.object({
|
||||
__typename: z.literal(ContentEnum.blocks.DestinationCountryPage),
|
||||
})
|
||||
.merge(pageLinkSchema)
|
||||
|
||||
export const destinationCountryPageRefSchema = z.object({
|
||||
__typename: z.literal(ContentEnum.blocks.DestinationCountryPage),
|
||||
system: systemSchema,
|
||||
})
|
||||
|
||||
export const destinationOverviewPageRefSchema = z.object({
|
||||
__typename: z.literal(ContentEnum.blocks.DestinationOverviewPage),
|
||||
system: systemSchema,
|
||||
})
|
||||
|
||||
export const hotelPageSchema = z
|
||||
.object({
|
||||
__typename: z.literal(ContentEnum.blocks.HotelPage),
|
||||
|
||||
@@ -5,6 +5,7 @@ export enum StickyElementNameEnum {
|
||||
BOOKING_WIDGET = "BOOKING_WIDGET",
|
||||
HOTEL_TAB_NAVIGATION = "HOTEL_TAB_NAVIGATION",
|
||||
HOTEL_STATIC_MAP = "HOTEL_STATIC_MAP",
|
||||
DESTINATION_SIDEBAR = "DESTINATION_SIDEBAR",
|
||||
}
|
||||
|
||||
export interface StickyElement {
|
||||
@@ -34,6 +35,8 @@ const priorityMap: Record<StickyElementNameEnum, number> = {
|
||||
|
||||
[StickyElementNameEnum.HOTEL_TAB_NAVIGATION]: 3,
|
||||
[StickyElementNameEnum.HOTEL_STATIC_MAP]: 3,
|
||||
|
||||
[StickyElementNameEnum.DESTINATION_SIDEBAR]: 3,
|
||||
}
|
||||
|
||||
const useStickyPositionStore = create<StickyStore>((set, get) => ({
|
||||
|
||||
@@ -3,6 +3,9 @@ export namespace ContentEnum {
|
||||
AccountPage = "AccountPage",
|
||||
CollectionPage = "CollectionPage",
|
||||
ContentPage = "ContentPage",
|
||||
DestinationCityPage = "DestinationCityPage",
|
||||
DestinationCountryPage = "DestinationCountryPage",
|
||||
DestinationOverviewPage = "DestinationOverviewPage",
|
||||
HotelPage = "HotelPage",
|
||||
ImageContainer = "ImageContainer",
|
||||
LoyaltyPage = "LoyaltyPage",
|
||||
|
||||
@@ -7,8 +7,9 @@ import type {
|
||||
|
||||
export interface GetDestinationCityPageData
|
||||
extends z.input<typeof destinationCityPageSchema> {}
|
||||
export interface DestinationCityPage
|
||||
interface DestinationCityPage
|
||||
extends z.output<typeof destinationCityPageSchema> {}
|
||||
export type DestinationCityPageData = DestinationCityPage["destinationCityPage"]
|
||||
|
||||
export interface GetDestinationCityPageRefsSchema
|
||||
extends z.input<typeof destinationCityPageRefsSchema> {}
|
||||
|
||||
@@ -7,8 +7,10 @@ import type {
|
||||
|
||||
export interface GetDestinationCountryPageData
|
||||
extends z.input<typeof destinationCountryPageSchema> {}
|
||||
export interface DestinationCountryPage
|
||||
interface DestinationCountryPage
|
||||
extends z.output<typeof destinationCountryPageSchema> {}
|
||||
export type DestinationCountryPageData =
|
||||
DestinationCountryPage["destinationCountryPage"]
|
||||
|
||||
export interface GetDestinationCountryPageRefsSchema
|
||||
extends z.input<typeof destinationCountryPageRefsSchema> {}
|
||||
|
||||
Reference in New Issue
Block a user