Merged in chore/cleanup-scandic-web (pull request #2831)

chore: Cleanup scandic-web

* Remove unused files

* Remove unused and add missing packages

* Remove unused exports


Approved-by: Linus Flood
This commit is contained in:
Anton Gunnarsson
2025-09-18 15:33:00 +00:00
parent cc99f26727
commit 08804e8675
113 changed files with 45 additions and 2891 deletions

View File

@@ -2,7 +2,7 @@ import { mapRewardToIcon } from "./data"
import type { LogoAndIllustrationProps } from "@scandic-hotels/design-system/Icons"
export interface RewardIconProps extends LogoAndIllustrationProps {
interface RewardIconProps extends LogoAndIllustrationProps {
rewardId: string
iconSize?: "small" | "medium" | "large"
}

View File

@@ -119,4 +119,3 @@ Carousel.Previous = CarouselPrevious
Carousel.Dots = CarouselDots
export { Carousel }
export type { CarouselApi }

View File

@@ -13,7 +13,7 @@ export const usePageType = () => {
return context
}
export type PageTypeProviderProps = {
type PageTypeProviderProps = {
value: "city" | "country" | "overview" | null
}

View File

@@ -21,7 +21,7 @@ export async function getConferenceRoomTexts(
return { seatingText, roomText }
}
export async function getRoomText(roomSizes: number[]) {
async function getRoomText(roomSizes: number[]) {
const largestRoom = Math.max(...roomSizes)
const smallestRoom = Math.min(...roomSizes)
const intl = await getIntl()
@@ -47,7 +47,7 @@ export async function getRoomText(roomSizes: number[]) {
return roomText
}
export async function getSeatingText(roomSeating: number[]) {
async function getSeatingText(roomSeating: number[]) {
const biggestSeating = Math.max(...roomSeating)
const smallestSeating = Math.min(...roomSeating)
const intl = await getIntl()

View File

@@ -1,20 +0,0 @@
import { nodesToHtml } from "./utils"
import styles from "./jsontohtml.module.css"
import type { DeprecatedJsonToHtmlProps } from "@/types/components/deprecatedjsontohtml"
export default function DeprecatedJsonToHtml({
embeds,
nodes,
renderOptions = {},
}: DeprecatedJsonToHtmlProps) {
if (!Array.isArray(nodes) || !nodes.length) {
return null
}
return (
<section className={styles.container}>
{nodesToHtml(nodes, embeds, renderOptions).filter(Boolean)}
</section>
)
}

View File

@@ -1,68 +0,0 @@
.image {
max-width: 100%;
width: 100%;
height: 365px;
object-fit: cover;
border-radius: var(--Corner-radius-md);
margin: var(--Spacing-x1) var(--Spacing-x0);
}
.ul,
.ol {
padding: var(--Spacing-x2) var(--Spacing-x0);
display: grid;
gap: var(--Spacing-x1);
}
.ol > li::marker {
color: var(--Primary-Light-On-Surface-Accent);
}
.li:has(.heart),
.li:has(.check) {
list-style: none;
}
.li {
margin-left: var(--Spacing-x3);
}
.li:has(.heart):before {
content: url("/_static/icons/heart.svg");
position: relative;
height: 8px;
top: 3px;
margin-right: var(--Spacing-x1);
margin-left: calc(var(--Spacing-x3) * -1);
}
.li:has(.check)::before {
content: url("/_static/icons/check-ring.svg");
position: relative;
height: 8px;
top: 3px;
margin-right: var(--Spacing-x1);
margin-left: calc(var(--Spacing-x3) * -1);
}
.container {
display: grid;
gap: var(--Spacing-x3);
max-width: 1197px;
}
.li > p {
display: inline;
}
.tableContainer {
max-width: 100%;
overflow-x: auto;
}
@media screen and (min-width: 768px) {
.ol:has(li:nth-last-child(n + 5)),
.ul:has(li:nth-last-child(n + 5)) {
grid-template-columns: 1fr 1fr;
grid-auto-flow: column;
}
}

View File

@@ -1,687 +0,0 @@
import {
type DeprecatedImageVaultAssetResponse,
type ImageVaultAssetResponse,
mapImageVaultAssetResponseToImageVaultAsset,
mapInsertResponseToImageVaultAsset,
} from "@scandic-hotels/common/utils/imageVault"
import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url"
import Body from "@scandic-hotels/design-system/Body"
import Caption from "@scandic-hotels/design-system/Caption"
import { Divider } from "@scandic-hotels/design-system/Divider"
import Footnote from "@scandic-hotels/design-system/Footnote"
import Image from "@scandic-hotels/design-system/Image"
import ImageContainer from "@scandic-hotels/design-system/ImageContainer"
import Link from "@scandic-hotels/design-system/Link"
import Subtitle from "@scandic-hotels/design-system/Subtitle"
import Table from "@scandic-hotels/design-system/Table"
import Title from "@scandic-hotels/design-system/Title"
import {
AvailableParagraphFormatEnum,
RTEItemTypeEnum,
RTETypeEnum,
} from "@scandic-hotels/trpc/types/RTEenums"
import BiroScript from "../TempDesignSystem/Text/BiroScript"
import { hasAvailableParagraphFormat, hasAvailableULFormat } from "./utils"
import styles from "./jsontohtml.module.css"
import type { EmbedByUid } from "@/types/components/deprecatedjsontohtml"
import { EmbedEnum } from "@/types/requests/utils/embeds"
import type { Attributes } from "@/types/rte/attrs"
import {
type RTEDefaultNode,
type RTEImageNode,
RTEMarkType,
type RTENext,
type RTENode,
type RTERegularNode,
type RTETextNode,
} from "@/types/rte/node"
import type { RenderOptions } from "@/types/rte/option"
function extractPossibleAttributes(attrs: Attributes | undefined) {
if (!attrs) return {}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const props: Record<string, any> = {}
if (attrs.id) {
props.id = attrs.id
}
if (attrs.class) {
props.className = attrs.class
} else if (attrs["class-name"]) {
props.className = attrs["class-name"]
} else if (attrs.classname) {
props.className = attrs.classname
} else if (attrs?.style?.["text-align"]) {
props.style = {
textAlign: attrs?.style?.["text-align"],
}
}
return props
}
export const renderOptions: RenderOptions = {
[RTETypeEnum.a]: (
node: RTERegularNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
if (node.attrs.url) {
const props = extractPossibleAttributes(node.attrs)
return (
<Link
{...props}
href={node.attrs.url}
key={node.uid}
target={node.attrs.target ?? "_blank"}
textDecoration="underline"
>
{next(node.children, embeds, fullRenderOptions)}
</Link>
)
}
return null
},
[RTETypeEnum.blockquote]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<BiroScript key={node.uid} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</BiroScript>
)
},
[RTETypeEnum.code]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<code key={node.uid} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</code>
)
},
[RTETypeEnum.embed]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
if (node.attrs.src) {
props.src = node.attrs.src
}
if (node.attrs.url) {
props.src = node.attrs.url
}
if (!props.src) {
return null
}
return (
<iframe key={node.uid} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</iframe>
)
},
[RTETypeEnum.h1]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<Title key={node.uid} {...props} level="h1">
{next(node.children, embeds, fullRenderOptions)}
</Title>
)
},
[RTETypeEnum.h2]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<Title key={node.uid} {...props} level="h2">
{next(node.children, embeds, fullRenderOptions)}
</Title>
)
},
[RTETypeEnum.h3]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<Title key={node.uid} {...props} level="h3">
{next(node.children, embeds, fullRenderOptions)}
</Title>
)
},
[RTETypeEnum.h4]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<Title key={node.uid} {...props} level="h4">
{next(node.children, embeds, fullRenderOptions)}
</Title>
)
},
[RTETypeEnum.h5]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<Title key={node.uid} {...props} level="h5">
{next(node.children, embeds, fullRenderOptions)}
</Title>
)
},
[RTETypeEnum.hr]: () => {
return <Divider color="burgundy" />
},
[RTETypeEnum.li]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<li key={node.uid} {...props} className={styles.li}>
{next(node.children, embeds, fullRenderOptions)}
</li>
)
},
[RTETypeEnum.ol]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
// Set the number of rows dynamically to create even rows for each column. We want the li:s
// to flow with the column, so therefore this is needed.
let numberOfRows: number | undefined
if (node.children.length > 4) {
const half = node.children.length / 2
numberOfRows = Math.ceil(half)
}
return (
<ol
key={node.uid}
{...props}
className={styles.ol}
style={
numberOfRows
? { gridTemplateRows: `repeat(${numberOfRows}, auto)` }
: {}
}
>
{next(node.children, embeds, fullRenderOptions)}
</ol>
)
},
[RTETypeEnum.p]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
const hasFormat = node.children.some((item) =>
hasAvailableParagraphFormat((item as RTETextNode)?.classname)
)
// If a child node has an available format as className, we wrap it in a
// span and render the children with the correct component
if (hasFormat) {
return next(node.children, embeds, fullRenderOptions)
}
return (
<Body {...props} key={node.uid}>
{next(node.children, embeds, fullRenderOptions)}
</Body>
)
},
[RTETypeEnum.reference]: (
node: RTENode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
if ("attrs" in node) {
const type = node.attrs.type
if (type === RTEItemTypeEnum.asset) {
const image = embeds?.[node?.attrs?.["asset-uid"]]
if (image?.node.__typename === EmbedEnum.SysAsset) {
const alt = image?.node?.title ?? node.attrs.alt
const props = extractPossibleAttributes(node.attrs)
props.className = styles.image
return (
<Image
key={node.uid}
alt={alt}
height={image.node.dimension.height}
src={image?.node?.url}
width={image.node.dimension.width}
{...props}
/>
)
}
} else if (type === RTEItemTypeEnum.entry) {
const entry = embeds?.[node?.attrs?.["entry-uid"]]
if (entry?.node.__typename === EmbedEnum.ImageContainer) {
if (entry.node.image_left && entry.node.image_right) {
return (
<ImageContainer
leftImage={entry.node.image_left}
rightImage={entry.node.image_right}
/>
)
}
return null
} else if (
entry?.node.__typename === EmbedEnum.LoyaltyPage ||
entry?.node.__typename === EmbedEnum.ContentPage ||
entry?.node.__typename === EmbedEnum.AccountPage
) {
// If entry is not an ImageContainer, it is a page and we return it as a link
const props = extractPossibleAttributes(node.attrs)
let href = ""
if (entry?.node.__typename === EmbedEnum.AccountPage) {
href = removeMultipleSlashes(
`/${entry.node.system.locale}${entry.node.url}`
)
} else {
href =
entry.node?.web?.original_url ||
removeMultipleSlashes(
`/${entry.node.system.locale}${entry.node.url}`
)
}
return (
<Link
{...props}
href={href}
key={node.uid}
textDecoration="underline"
>
{next(node.children, embeds, fullRenderOptions)}
</Link>
)
}
}
}
return null
},
[RTETypeEnum.ImageVault]: (node: RTEImageNode) => {
const type = node.type
if (!("attrs" in node) || type !== RTETypeEnum.ImageVault) {
return null
}
let image = undefined
if ("imageVaultId" in node.attrs && "fileName" in node.attrs) {
image = mapImageVaultAssetResponseToImageVaultAsset(
node.attrs as unknown as ImageVaultAssetResponse
)
}
if ("Name" in node.attrs && "Id" in node.attrs) {
image = mapInsertResponseToImageVaultAsset(
node.attrs as unknown as DeprecatedImageVaultAssetResponse
)
}
if (!image) {
return null
}
const alt = image.meta.alt ?? image.title
const props = extractPossibleAttributes(node.attrs)
return (
<section key={node.uid}>
<Image
alt={alt}
className={styles.image}
fill
sizes="(min-width: 1367px) 800px, (max-width: 1366px) and (min-width: 1200px) 1200px, 100vw"
src={image.url}
focalPoint={image.focalPoint}
dimensions={image.dimensions}
{...props}
/>
<Caption>{image.meta.caption}</Caption>
</section>
)
return null
},
[RTETypeEnum.table]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<div className={styles.tableContainer}>
<Table key={node.uid} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</Table>
</div>
)
},
[RTETypeEnum.thead]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
// Override the styling of p tags inside the thead tag
const theadChildPRenderOptions = {
...fullRenderOptions,
[RTETypeEnum.p]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => (
<Body color={"burgundy"} textTransform={"bold"}>
{next(node.children, embeds, fullRenderOptions)}
</Body>
),
}
const props = extractPossibleAttributes(node.attrs)
return (
<Table.THead key={node.uid} {...props}>
{next(node.children, embeds, theadChildPRenderOptions)}
</Table.THead>
)
},
[RTETypeEnum.tbody]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<Table.TBody key={node.uid} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</Table.TBody>
)
},
[RTETypeEnum.tfoot]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<tfoot key={node.uid} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</tfoot>
)
},
[RTETypeEnum.fragment]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
return <>{next(node.children, embeds, fullRenderOptions)}</>
},
[RTETypeEnum.tr]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<Table.TR key={node.uid} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</Table.TR>
)
},
[RTETypeEnum.th]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<Table.TH key={node.uid} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</Table.TH>
)
},
[RTETypeEnum.td]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
return (
<Table.TD key={node.uid} {...props}>
{next(node.children, embeds, fullRenderOptions)}
</Table.TD>
)
},
[RTETypeEnum.ul]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
const props = extractPossibleAttributes(node.attrs)
// Set the number of rows dynamically to create even rows for each column. We want the li:s
// to flow with the column, so therefore this is needed.
let numberOfRows: number | undefined
if (node.children.length > 4) {
const half = node.children.length / 2
numberOfRows = Math.ceil(half)
}
return (
<ul
key={node.uid}
{...props}
className={styles.ul}
style={
numberOfRows
? {
gridTemplateRows: `repeat(${numberOfRows}, auto)`,
}
: {}
}
>
{next(node.children, embeds, fullRenderOptions)}
</ul>
)
},
/** TextNode wrappers */
[RTEMarkType.bold]: (children: React.ReactNode) => {
return <strong>{children}</strong>
},
[RTEMarkType.italic]: (children: React.ReactNode) => {
return <em>{children}</em>
},
[RTEMarkType.underline]: (children: React.ReactNode) => {
return <u>{children}</u>
},
[RTEMarkType.strikethrough]: (children: React.ReactNode) => {
return <s>{children}</s>
},
[RTEMarkType.inlineCode]: (children: React.ReactNode) => {
return <span>{children}</span>
},
[RTEMarkType.subscript]: (children: React.ReactNode) => {
return <sub>{children}</sub>
},
[RTEMarkType.superscript]: (children: React.ReactNode) => {
return <sup>{children}</sup>
},
[RTEMarkType.break]: (children: React.ReactNode) => {
return (
<>
<br />
{children}
</>
)
},
[RTEMarkType.classnameOrId]: (
children: React.ReactNode,
className?: string,
id?: string
) => {
const props = {
className,
id,
}
if (!className) {
delete props.className
}
if (!id) {
delete props.id
}
if (className) {
if (hasAvailableULFormat(className)) {
props.className = styles[className]
}
}
if (className === AvailableParagraphFormatEnum.footnote) {
return (
<Footnote key={id} {...props}>
{children}
</Footnote>
)
}
if (className === AvailableParagraphFormatEnum.caption) {
return (
<Caption key={id} {...props}>
{children}
</Caption>
)
}
if (className === AvailableParagraphFormatEnum["script-1"]) {
return (
<BiroScript key={id} type="one" {...props}>
{children}
</BiroScript>
)
}
if (className === AvailableParagraphFormatEnum["script-2"]) {
return (
<BiroScript key={id} type="two" {...props}>
{children}
</BiroScript>
)
}
if (className === AvailableParagraphFormatEnum["subtitle-1"]) {
return (
<Subtitle key={id} {...props} type="one">
{children}
</Subtitle>
)
}
if (className === AvailableParagraphFormatEnum["subtitle-2"]) {
return (
<Subtitle key={id} {...props} type="two">
{children}
</Subtitle>
)
}
return (
<span key={id} {...props}>
{children}
</span>
)
},
/**
* Contentstack can return something called `default` as seen here in their
* own SDK (https://github.com/contentstack/contentstack-utils-javascript/blob/master/src/options/default-node-options.ts#L89)
*/
default: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
return next(node.children, embeds, fullRenderOptions)
},
}

View File

@@ -1,150 +0,0 @@
import { cloneElement } from "react"
import {
AvailableParagraphFormatEnum,
AvailableULFormatEnum,
RTETypeEnum,
} from "@scandic-hotels/trpc/types/RTEenums"
import { renderOptions } from "./renderOptions"
import type { Node } from "@scandic-hotels/trpc/types/edges"
import type { EmbedByUid } from "@/types/components/deprecatedjsontohtml"
import type { Embeds } from "@/types/requests/embeds"
import {
RTEMarkType,
type RTENode,
type RTERenderMark,
type RTERenderOptionComponent,
type RTETextNode,
} from "@/types/rte/node"
import type { RenderOptions } from "@/types/rte/option"
export function groupEmbedsByUid(embedsArray: Node<Embeds>[]) {
const embedsByUid = embedsArray.reduce<EmbedByUid>((acc, embed) => {
if (embed.node.system?.uid) {
acc[embed.node.system.uid] = embed
}
return acc
}, {})
return embedsByUid
}
export function nodeChildrenToHtml(
nodes: RTENode[],
embeds: EmbedByUid,
fullRenderOptions: RenderOptions
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): any {
return nodes
.map((node, i) => {
// This function either returns a JSX element or null
const element = nodeToHtml(node, embeds, fullRenderOptions)
if (!element) {
return null
}
return cloneElement(element, {
key: `child-rte-${i}`,
})
})
.filter(Boolean)
}
export function textNodeToHtml(
node: RTETextNode,
fullRenderOptions: RenderOptions
) {
let text = <>{node.text}</>
if (node.classname || node.id) {
text = (fullRenderOptions[RTEMarkType.classnameOrId] as RTERenderMark)(
text,
node.classname,
node.id
)
}
if (node.break) {
text = (fullRenderOptions[RTEMarkType.break] as RTERenderMark)(text)
}
if (node.superscript) {
text = (fullRenderOptions[RTEMarkType.superscript] as RTERenderMark)(text)
}
if (node.subscript) {
text = (fullRenderOptions[RTEMarkType.subscript] as RTERenderMark)(text)
}
if (node.inlineCode) {
text = (fullRenderOptions[RTEMarkType.inlineCode] as RTERenderMark)(text)
}
if (node.strikethrough) {
text = (fullRenderOptions[RTEMarkType.strikethrough] as RTERenderMark)(text)
}
if (node.underline) {
text = (fullRenderOptions[RTEMarkType.underline] as RTERenderMark)(text)
}
if (node.italic) {
text = (fullRenderOptions[RTEMarkType.italic] as RTERenderMark)(text)
}
if (node.bold) {
text = (fullRenderOptions[RTEMarkType.bold] as RTERenderMark)(text)
}
return text
}
function next(
nodes: RTENode[],
embeds: EmbedByUid,
fullRenderOptions: RenderOptions
) {
return nodeChildrenToHtml(nodes, embeds, fullRenderOptions)
}
export function hasAvailableParagraphFormat(className?: string) {
if (!className) {
return false
}
return Object.keys(AvailableParagraphFormatEnum).includes(className)
}
export function hasAvailableULFormat(className?: string) {
if (!className) {
return false
}
return Object.keys(AvailableULFormatEnum).includes(className)
}
export function nodeToHtml(
node: RTENode,
embeds: EmbedByUid,
fullRenderOptions: RenderOptions
) {
if ("type" in node === false) {
return textNodeToHtml(node, fullRenderOptions)
} else {
if (fullRenderOptions[node.type] !== undefined) {
if (node.type === RTETypeEnum.doc) {
return null
}
return (fullRenderOptions[node.type] as RTERenderOptionComponent)(
node,
embeds,
next,
fullRenderOptions
)
} else {
return next(node.children, embeds, fullRenderOptions)
}
}
}
export function nodesToHtml(
nodes: RTENode[],
embedsArray: Node<Embeds>[],
overrideRenderOptions: RenderOptions
) {
const embeds = groupEmbedsByUid(embedsArray)
const fullRenderOptions = { ...renderOptions, ...overrideRenderOptions }
return nodes.map((node) => nodeToHtml(node, embeds, fullRenderOptions))
}

View File

@@ -1,83 +0,0 @@
"use client"
import { useEffect, useState } from "react"
import { clearPaymentInfoSessionStorage } from "@scandic-hotels/booking-flow/components/EnterDetails/Payment/helpers"
import { useSearchHistory } from "@scandic-hotels/booking-flow/hooks/useSearchHistory"
import { useBookingConfirmationStore } from "@scandic-hotels/booking-flow/stores/booking-confirmation"
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
import useLang from "@/hooks/useLang"
import { getTracking } from "./tracking"
import type { Room } from "@scandic-hotels/booking-flow/types/stores/booking-confirmation"
import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation"
export default function Tracking({
bookingConfirmation,
refId,
}: {
bookingConfirmation: BookingConfirmation
refId: string
}) {
const lang = useLang()
const bookingRooms = useBookingConfirmationStore((state) => state.rooms)
const [loadedBookingConfirmationRefId] = useState(() => {
if (typeof window !== "undefined") {
return sessionStorage.getItem("loadedBookingConfirmationRefId")
}
return null
})
useEffect(() => {
sessionStorage.setItem("loadedBookingConfirmationRefId", refId)
}, [refId])
const searchHistory = useSearchHistory()
const searchTerm = searchHistory.searchHistory[0]?.name
let trackingData = null
if (bookingRooms.every(Boolean)) {
const rooms = bookingRooms.filter((room): room is Room => !!room)
trackingData = getTracking(
lang,
bookingConfirmation.booking,
bookingConfirmation.hotel,
rooms,
searchTerm
)
}
useEffect(() => {
if (trackingData?.paymentInfo) {
clearPaymentInfoSessionStorage()
}
}, [trackingData])
if (!trackingData) {
return null
}
const { hotelsTrackingData, pageTrackingData, paymentInfo, ancillaries } =
trackingData
return (
<TrackingSDK
pageData={pageTrackingData}
hotelInfo={
loadedBookingConfirmationRefId === refId
? undefined
: hotelsTrackingData
}
paymentInfo={
loadedBookingConfirmationRefId === refId ? undefined : paymentInfo
}
ancillaries={
loadedBookingConfirmationRefId === refId ? undefined : ancillaries
}
/>
)
}

View File

@@ -1,213 +0,0 @@
import { createHash } from "crypto"
import { differenceInCalendarDays, format, isWeekend } from "date-fns"
import { readPaymentInfoFromSessionStorage } from "@scandic-hotels/booking-flow/components/EnterDetails/Payment/helpers"
import { invertedBedTypeMap } from "@scandic-hotels/booking-flow/utils/SelectRate"
import { getSpecialRoomType } from "@scandic-hotels/booking-flow/utils/specialRoomType"
import { CancellationRuleEnum } from "@scandic-hotels/common/constants/booking"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { RateEnum } from "@scandic-hotels/common/constants/rate"
import {
TrackingChannelEnum,
type TrackingSDKAncillaries,
type TrackingSDKHotelInfo,
type TrackingSDKPageData,
type TrackingSDKPaymentInfo,
} from "@scandic-hotels/tracking/types"
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
import type { Room } from "@scandic-hotels/booking-flow/types/stores/booking-confirmation"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation"
import type { RateDefinition } from "@scandic-hotels/trpc/types/roomAvailability"
function getRate(cancellationRule: RateDefinition["cancellationRule"] | null) {
switch (cancellationRule) {
case "CancellableBefore6PM":
return RateEnum.flex
case "Changeable":
return RateEnum.change
case "NotCancellable":
return RateEnum.save
default:
return ""
}
}
function mapAncillaryPackage(
ancillaryPackage: BookingConfirmation["booking"]["packages"][number],
operaId: string
) {
const isPoints = ancillaryPackage.currency === CurrencyEnum.POINTS
return {
hotelid: operaId,
productCategory: "", // TODO: Add category
productId: ancillaryPackage.code,
productName: ancillaryPackage.description,
productPoints: isPoints ? ancillaryPackage.totalPrice : 0,
productPrice: isPoints ? 0 : ancillaryPackage.totalPrice,
productType:
ancillaryPackage.code === BreakfastPackageEnum.REGULAR_BREAKFAST
? "food"
: "room preference",
productUnits: ancillaryPackage.totalUnit,
productDeliveryTime: "",
}
}
export function getTracking(
lang: Lang,
booking: BookingConfirmation["booking"],
hotel: BookingConfirmation["hotel"],
rooms: Room[],
searchTerm?: string
) {
const arrivalDate = new Date(booking.checkInDate)
const departureDate = new Date(booking.checkOutDate)
const paymentInfoSessionData = readPaymentInfoFromSessionStorage()
const pageTrackingData: TrackingSDKPageData = {
channel: TrackingChannelEnum.hotelreservation,
domainLanguage: lang,
pageId: "booking-confirmation",
pageName: `hotelreservation|confirmation`,
pageType: "confirmation",
siteSections: `hotelreservation|confirmation`,
siteVersion: "new-web",
}
const noOfAdults = rooms.map((r) => r.adults).join(",")
const noOfChildren = rooms.map((r) => r.childrenAges?.length ?? 0).join(",")
const noOfRooms = rooms.length
const isFlexBooking =
booking.rateDefinition.cancellationRule ===
CancellationRuleEnum.CancellableBefore6PM
const isGuaranteedFlexBooking = booking.guaranteeInfo && isFlexBooking
const ancillaries: TrackingSDKAncillaries = rooms
.flatMap((r) => r.packages)
.filter(
(p) =>
p.code === RoomPackageCodeEnum.PET_ROOM ||
p.code === BreakfastPackageEnum.REGULAR_BREAKFAST
)
.map((pkg) => mapAncillaryPackage(pkg, hotel.operaId))
const hotelsTrackingData: TrackingSDKHotelInfo = {
ageOfChildren: rooms.map((r) => r.childrenAges?.join(",") ?? "").join("|"),
analyticsRateCode: rooms
.map((r) => getRate(r.rateDefinition.cancellationRule))
.join("|"),
arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
bedType: rooms
.map((r) => r.bedType)
.join(",")
.toLowerCase(),
bnr: rooms.map((r) => r.confirmationNumber).join(","),
bookingCode: rooms.map((room) => room.bookingCode ?? "n/a").join(", "),
bookingCodeAvailability: booking.bookingCode
? rooms.map((room) => (room.bookingCode ? "true" : "false")).join(", ")
: undefined,
bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
breakfastOption: rooms
.map((r) => {
if (r.breakfastIncluded || r.breakfast) {
return "breakfast buffet"
}
return "no breakfast"
})
.join(","),
childBedPreference: rooms
.map(
(r) =>
r.childBedPreferences
.map((cbp) =>
Array(cbp.quantity).fill(invertedBedTypeMap[cbp.bedType])
)
.join(",") ?? ""
)
.join("|"),
country: hotel?.address.country,
departureDate: format(departureDate, "yyyy-MM-dd"),
duration: differenceInCalendarDays(departureDate, arrivalDate),
hotelID: hotel.operaId,
leadTime: differenceInCalendarDays(arrivalDate, new Date()),
noOfAdults,
noOfChildren,
noOfRooms,
rateCode: rooms.map((r) => r.rateDefinition.rateCode).join(","),
rateCodeCancellationRule: rooms
.map((r) => r.rateDefinition.cancellationRule)
.join(",")
.toLowerCase(),
rateCodeName: rooms.map(constructRateCodeName).join(","),
rateCodeType: rooms.map((r) => r.rateCodeType?.toLowerCase()).join(","),
region: hotel?.address.city,
revenueCurrencyCode: [...new Set(rooms.map((r) => r.currencyCode))].join(
","
),
rewardNight: booking.roomPoints > 0 ? "yes" : "no",
rewardNightAvailability: booking.roomPoints > 0 ? "true" : "false",
points: booking.roomPoints > 0 ? booking.roomPoints : undefined,
roomPrice: rooms.reduce((total, room) => total + room.roomPrice, 0),
roomTypeCode: rooms.map((r) => r.roomTypeCode ?? "").join(","),
searchTerm,
searchType: "hotel",
specialRoomType: rooms
.map((room) => getSpecialRoomType(room.packages))
.join(","),
totalPrice: rooms.reduce((total, room) => total + room.totalPrice, 0),
lateArrivalGuarantee: booking.rateDefinition.mustBeGuaranteed
? "mandatory"
: isFlexBooking
? booking.guaranteeInfo
? "yes"
: "no"
: "na",
guaranteedProduct: isGuaranteedFlexBooking ? "room" : "na",
emailId: getSHAHash(booking.guest.email),
mobileNumber: getSHAHash(booking.guest.phoneNumber),
}
const paymentInfo: TrackingSDKPaymentInfo = {
paymentStatus: isGuaranteedFlexBooking
? "glacardsaveconfirmed"
: "confirmed",
type:
booking.guaranteeInfo?.cardType ?? paymentInfoSessionData?.paymentMethod,
}
return {
hotelsTrackingData,
pageTrackingData,
paymentInfo,
ancillaries,
}
}
function constructRateCodeName(room: Room) {
if (room.cheques) {
return "corporate cheque"
} else if (room.vouchers) {
return "voucher"
} else if (room.roomPoints) {
return "redemption"
}
const rate = getRate(room.rateDefinition.cancellationRule)
const bookingCodeStr = room.bookingCode ? room.bookingCode.toUpperCase() : ""
const breakfastIncludedStr = room.breakfastIncluded
? "incl. breakfast"
: "excl. breakfast"
return [bookingCodeStr, rate, breakfastIncludedStr]
.filter(Boolean)
.join(" - ")
}
function getSHAHash(key: string) {
return createHash("sha256").update(key).digest("hex")
}

View File

@@ -1,6 +1,6 @@
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
export function hasBreakfastPackageFromBookingFlow(
function hasBreakfastPackageFromBookingFlow(
packages: {
code: string
}[]
@@ -13,26 +13,6 @@ export function hasBreakfastPackageFromBookingFlow(
)
}
export function getBreakfastPackagesFromBookingFlow<T extends { code: string }>(
packages: T[]
): T[] | undefined {
// Since `FREE_CHILD_BREAKFAST` has the same code when breakfast is added
// in the booking flow and as ancillary we can't just do a simple filter on the codes.
// So we shortcircuit if there are no booking flow specific packages.
if (!packages || !hasBreakfastPackageFromBookingFlow(packages)) {
return undefined
}
return packages.filter(
(p) =>
p.code === BreakfastPackageEnum.REGULAR_BREAKFAST ||
p.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST ||
p.code === BreakfastPackageEnum.CHILD_PAYING_BREAKFAST ||
p.code === BreakfastPackageEnum.FREE_CHILD_BREAKFAST ||
p.code === BreakfastPackageEnum.SPECIAL_PACKAGE_BREAKFAST
)
}
export function getBreakfastPackagesFromAncillaryFlow<
T extends { code: string },
>(packages: T[]): T[] | undefined {

View File

@@ -1,27 +0,0 @@
.avatar {
display: inline-flex;
align-items: center;
justify-content: center;
vertical-align: middle;
overflow: hidden;
cursor: pointer;
width: 35px;
height: 35px;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.05);
}
.avatarInitialsText {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--Primary-Dark-Surface-Normal);
color: var(--Primary-Dark-On-Surface-Text);
font-size: var(--typography-Caption-Bold-fontSize);
font-family: var(--typography-Body-Regular-fontFamily);
font-weight: var(--typography-Caption-Bold-fontWeight);
line-height: 150%;
letter-spacing: 0.096px;
}

View File

@@ -1,22 +0,0 @@
import { getInitials } from "@/utils/user"
import styles from "./avatar.module.css"
import type { User } from "@scandic-hotels/trpc/types/user"
export default function Avatar({
firstName,
lastName,
}: {
firstName: User["firstName"]
lastName: User["lastName"]
}) {
const initials = getInitials(firstName, lastName)
return (
<span className={styles.avatar}>
<span data-hj-suppress className={styles.avatarInitialsText}>
{initials}
</span>
</span>
)
}

View File

@@ -10,7 +10,7 @@ import styles from "./progressSection.module.css"
import type { ProgressCalculation } from "../../types"
export interface ProgressSectionProps {
interface ProgressSectionProps {
earned: number
progress: ProgressCalculation
toKeepCurrent?: number

View File

@@ -5,7 +5,7 @@ import { getIntl } from "@/i18n"
import styles from "./successCard.module.css"
export interface SuccessCardProps {
interface SuccessCardProps {
pointsEarned?: number | null
}

View File

@@ -5,7 +5,7 @@ import SectionLink from "../Link"
import styles from "./header.module.css"
export type HeaderProps = {
type HeaderProps = {
link?: {
href: string
text: string

View File

@@ -40,7 +40,7 @@ type PartialHotelRoom = Pick<
"descriptions" | "images" | "name" | "roomFacilities" | "roomTypes"
>
export type Room = Pick<
type Room = Pick<
BookingConfirmationSchema,
| "adults"
| "bookingCode"

View File

@@ -17,7 +17,7 @@ import { debounce } from "@scandic-hotels/common/utils/debounce"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { Arrow } from "../Popover/Arrow"
import { Arrow } from "./Arrow"
import { Breadcrumb } from "./Breadcrumb"
import { splitBreadcrumbs } from "./utils"
import { breadcrumbsVariants } from "./variants"

View File

@@ -4,11 +4,11 @@ import type { TypenameInterface } from "@/types/requests/utils/typename"
export type Order = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
export type ColSpan = 2 | 3 | 4 | 6 | 8
type ColSpan = 2 | 3 | 4 | 6 | 8
export type RowSpan = 1 | 2 | 3 | 6
// TODO: Extend query and fix type accordingly
export interface Row extends TypenameInterface<"Card"> {
interface Row extends TypenameInterface<"Card"> {
title: string
}
@@ -19,7 +19,7 @@ type Column = {
}[]
}
export interface Grid {
interface Grid {
columns: Column[]
}

View File

@@ -1,51 +0,0 @@
import {
Button,
Dialog,
DialogTrigger,
OverlayArrow,
Popover as RAPopover,
} from "react-aria-components"
import useSetOverFlowVisibleOnRA from "@scandic-hotels/common/hooks/useSetOverflowVisibleOnRA"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Arrow } from "./Arrow"
import styles from "./popover.module.css"
import type { PopoverProps } from "./popover"
export default function Popover({
triggerContent,
children,
...props
}: PopoverProps) {
const setOverflowVisible = useSetOverFlowVisibleOnRA()
return (
<DialogTrigger onOpenChange={setOverflowVisible}>
<Button className={styles.trigger}>{triggerContent}</Button>
<RAPopover
{...props}
offset={16}
crossOffset={-24}
className={styles.root}
>
<OverlayArrow>
<Arrow />
</OverlayArrow>
<Dialog>
{({ close }) => (
<>
<Button className={styles.closeButton} onPress={close}>
<MaterialIcon icon="close" size={20} />
</Button>
{children}
</>
)}
</Dialog>
</RAPopover>
</DialogTrigger>
)
}

View File

@@ -1,28 +0,0 @@
.root {
background-color: var(--Base-Surface-Primary-light-Normal);
border-radius: var(--Corner-radius-md);
box-shadow: var(--popup-box-shadow);
padding: var(--Spacing-x2);
max-width: calc(360px + var(--Spacing-x2) * 2);
overflow-y: auto;
}
.root section:focus-visible {
outline: none;
}
.trigger {
background: none;
border: none;
padding: 0;
cursor: pointer;
}
.closeButton {
position: absolute;
top: 8px;
right: 8px;
background: none;
border: none;
cursor: pointer;
padding: 0;
}

View File

@@ -1,6 +0,0 @@
import type { PopoverProps as RAPopoverProps } from "react-aria-components"
export interface PopoverProps extends Omit<RAPopoverProps, "children"> {
triggerContent: React.ReactNode
children: React.ReactNode
}

View File

@@ -1,12 +0,0 @@
import styles from "./progressbar.module.css"
import type { ProgressbarProps } from "./progressbar"
export default function ProgressBar({ className, progress }: ProgressbarProps) {
className = className ?? ""
return (
<div className={`${className} ${styles.bar}`}>
<div className={styles.progress} style={{ width: `${progress}%` }} />
</div>
)
}

View File

@@ -1,12 +0,0 @@
.bar {
background-color: var(--Main-Grey-White);
border-radius: 40px;
height: 20px;
width: 100%;
}
.progress {
background-color: var(--UI-Opacity-Almost-Black-100);
border-radius: 40px;
height: 20px;
}

View File

@@ -1,3 +0,0 @@
export interface ProgressbarProps extends React.HTMLAttributes<HTMLDivElement> {
progress: number
}

View File

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

View File

@@ -10,47 +10,7 @@ export const languages: Record<Lang, string> = {
[Lang.sv]: "Svenska",
}
export const localeToLang: Record<string, Lang> = {
en: Lang.en,
"en-US": Lang.en,
"en-GB": Lang.en,
"en-DE": Lang.en,
"en-DK": Lang.en,
"en-SE": Lang.en,
"en-FI": Lang.en,
sv: Lang.sv,
"se-SE": Lang.sv,
"sv-SE": Lang.sv,
"sv-FI": Lang.sv,
fi: Lang.fi,
"fi-FI": Lang.fi,
"se-FI": Lang.fi,
"smn-FI": Lang.fi,
dk: Lang.da,
da: Lang.da,
"da-DK": Lang.da,
"fo-DK": Lang.da,
de: Lang.de,
"de-DE": Lang.de,
"dsb-DE": Lang.de,
"ksh-DE": Lang.de,
"nds-DE": Lang.de,
"hsb-DE": Lang.de,
"de-CH": Lang.de,
"de-AU": Lang.de,
no: Lang.no,
nb: Lang.no,
"nb-NO": Lang.no,
"nn-NO": Lang.no,
"se-NO": Lang.no,
} as const
export const languageSelect = [
const languageSelect = [
{ label: "Danish", value: ApiLang.Da },
{ label: "German", value: ApiLang.De },
{ label: "English", value: ApiLang.En },

View File

@@ -25,7 +25,7 @@ export const benefits = {
sv: `${myPages.sv}/formaner`,
}
export const points = {
const points = {
da: `${myPages.da}/point`,
de: `${myPages.de}/punkte`,
en: `${myPages.en}/points`,
@@ -34,7 +34,7 @@ export const points = {
sv: `${myPages.sv}/poang`,
}
export const programOverview = {
const programOverview = {
da: `/da/webview/scandic-friends`,
de: `/de/webview/scandic-friends`,
en: `/en/webview/scandic-friends`,

View File

@@ -1,5 +1,3 @@
import { redirect } from "next/navigation"
import { cache } from "@/utils/cache"
import { serverClient } from "../server"
@@ -7,7 +5,6 @@ import { serverClient } from "../server"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { GetHotelsByCSFilterInput } from "@scandic-hotels/trpc/routers/hotels/input"
import type { GetSavedPaymentCardsInput } from "@scandic-hotels/trpc/routers/user/input"
import type { RoomsAvailabilityExtendedInputSchema } from "@scandic-hotels/trpc/types/availability"
import type { Country } from "@scandic-hotels/trpc/types/country"
import type { HotelInput } from "@scandic-hotels/trpc/types/hotel"
import type {
@@ -96,13 +93,6 @@ export const getHeader = cache(async function getMemoizedHeader() {
return caller.contentstack.base.header()
})
export const getSiteConfig = cache(async function getMemoizedSiteConfig(
lang: Lang
) {
const caller = await serverClient()
return caller.contentstack.base.siteConfig({ lang })
})
export const getAncillaryPackages = cache(
async function getMemoizedAncillaryPackages(input: AncillaryPackagesInput) {
const caller = await serverClient()
@@ -165,16 +155,6 @@ export const getMeetingRooms = cache(
}
)
export const getAdditionalData = cache(
async function getMemoizedAdditionalData(input: {
hotelId: string
language: Lang
}) {
const caller = await serverClient()
return caller.hotel.additionalData(input)
}
)
export const getDestinationOverviewPage = cache(
async function getMemoizedDestinationOverviewPage() {
const caller = await serverClient()
@@ -236,33 +216,11 @@ export const getStartPage = cache(async function getMemoizedStartPage() {
return caller.contentstack.startPage.get()
})
export const getPageSettings = cache(async function getMemoizedPageSettings(
lang: Lang
) {
const caller = await serverClient()
return caller.contentstack.pageSettings.get({ lang })
})
export const getJobylonFeed = cache(async function getMemoizedJobylonFeed() {
const caller = await serverClient()
return caller.partner.jobylon.feed.get()
})
export const getSelectedRoomsAvailabilityEnterDetails = cache(
async function getMemoizedSelectedRoomsAvailability(
input: RoomsAvailabilityExtendedInputSchema
) {
const caller = await serverClient()
const result = await caller.hotel.availability.enterDetails(input)
if (typeof result === "string") {
redirect(result)
}
return result
}
)
export const getCampaignPage = cache(async function getMemoizedCampaignPage() {
const caller = await serverClient()
return caller.contentstack.campaignPage.get()

View File

@@ -52,22 +52,19 @@
"@tanstack/react-table": "^8.21.3",
"@testing-library/dom": "^10.4.0",
"@trpc/client": "^11.1.2",
"@trpc/react-query": "^11.1.2",
"@trpc/server": "^11.1.2",
"@tsparticles/confetti": "^3.8.1",
"@types/geojson": "^7946.0.16",
"@types/supercluster": "^7.1.3",
"@vis.gl/react-google-maps": "^1.5.2",
"class-variance-authority": "^0.7.1",
"client-only": "^0.0.1",
"contentstack": "^3.25.3",
"date-fns": "^4.1.0",
"dayjs": "^1.11.13",
"deepmerge": "^4.3.1",
"dialogshift-webchat-sdk": "^2.10.1",
"downshift": "^9.0.9",
"embla-carousel": "^8.6.0",
"embla-carousel-react": "^8.6.0",
"fast-deep-equal": "^3.1.3",
"flat": "^6.0.1",
"graphql-tag": "^2.12.6",
"html-react-parser": "^5.2.3",
@@ -76,7 +73,6 @@
"input-otp": "^1.4.2",
"json-stable-stringify-without-jsonify": "^1.0.1",
"jsonwebtoken": "^9.0.2",
"libphonenumber-js": "^1.12.7",
"lodash-es": "^4.17.21",
"md5": "^2.3.0",
"motion": "^12.10.0",

View File

@@ -51,14 +51,3 @@ export function isValidSortOption(
): value is HotelSortOption {
return sortItems.map((item) => item.value).includes(value as HotelSortOption)
}
export function getBasePathNameWithoutFilters(
pathname: string,
filterSlugs: string[]
) {
const pathSegments = pathname.split("/")
const filteredSegments = pathSegments.filter(
(segment) => !filterSlugs.includes(segment)
)
return filteredSegments.join("/")
}

View File

@@ -2,6 +2,8 @@ import { produce } from "immer"
import { useContext } from "react"
import { create, useStore } from "zustand"
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
import { clearAncillarySessionData } from "@/components/HotelReservation/MyStay/utils/ancillaries"
import { AddAncillaryContext } from "@/contexts/AddAncillary"
@@ -10,7 +12,6 @@ import type {
Ancillary,
SelectedAncillary,
} from "@/types/components/myPages/myStay/ancillaries"
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
import type { Room } from "@/types/stores/my-stay"
export enum AncillaryStepEnum {
@@ -40,7 +41,7 @@ export type BreakfastData = {
currency: string
}
export interface AddAncillaryState {
interface AddAncillaryState {
currentStep: number
steps: Steps
booking: Room

View File

@@ -1,136 +0,0 @@
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import type { AvailabilityError } from "@scandic-hotels/booking-flow/types/stores/rates"
import type {
Product,
RoomConfiguration,
RoomsAvailability,
} from "@scandic-hotels/trpc/types/roomAvailability"
export function findProduct(
rateCode: string,
product: Product,
counterRateCode = ""
) {
if ("corporateCheque" in product) {
return product.corporateCheque.rateCode === rateCode
}
if ("redemption" in product) {
return product.redemption.rateCode === rateCode
}
if ("voucher" in product) {
return product.voucher.rateCode === rateCode
}
const memberExists = "member" in product
const publicExists = "public" in product
const isRegularRate = memberExists && publicExists
if (isRegularRate) {
let isProduct = false
if (product.member) {
isProduct =
product.member.rateCode === rateCode ||
product.member.rateCode === counterRateCode
}
if (product.public) {
isProduct =
product.public.rateCode === rateCode ||
product.public.rateCode === counterRateCode
}
return isProduct
}
return null
}
export function findProductInRoom(
rateCode: string | undefined | null,
room: RoomConfiguration,
counterRateCode: string | undefined | null
) {
if (!rateCode) {
return null
}
if (room.campaign.length) {
const campaignProduct = room.campaign.find((product) =>
findProduct(rateCode, product, counterRateCode || "")
)
if (campaignProduct) {
return campaignProduct
}
}
if (room.code.length) {
const codeProduct = room.code.find((product) =>
findProduct(rateCode, product, counterRateCode || "")
)
if (codeProduct) {
return codeProduct
}
}
if (room.redemptions.length) {
const redemptionProduct = room.redemptions.find((product) =>
findProduct(rateCode, product)
)
if (redemptionProduct) {
return redemptionProduct
}
}
if (room.regular.length) {
const regularProduct = room.regular.find((product) =>
findProduct(rateCode, product, counterRateCode || "")
)
if (regularProduct) {
return regularProduct
}
}
}
export function findSelectedRate(
rateCode: string | undefined | null,
counterRateCode: string | undefined | null,
roomTypeCode: string | undefined | null,
rooms: RoomConfiguration[] | AvailabilityError
) {
if (!Array.isArray(rooms)) {
return null
}
if (!rateCode) {
return null
}
return rooms.find((room) => {
if (room.roomTypeCode !== roomTypeCode) {
return false
}
return findProductInRoom(rateCode, room, counterRateCode)
})
}
export function findDefaultCurrency(
roomsAvailability: (RoomsAvailability | AvailabilityError)[] | undefined
) {
if (!roomsAvailability || !roomsAvailability.length) {
return CurrencyEnum.Unknown
}
const availability = roomsAvailability.filter(
(room): room is RoomsAvailability => {
if ("error" in room) {
return false
}
return true
}
)[0]
const pkg = availability?.packages.find((pkg) => pkg.localPrice.currency)
if (!pkg) {
return CurrencyEnum.Unknown
}
const defaultCurrency = pkg.localPrice.currency
return defaultCurrency
}

View File

@@ -1,3 +0,0 @@
export type Prettify<T> = {
[K in keyof T]: T[K]
} & {}

View File

@@ -13,9 +13,3 @@ export interface AncillaryCardProps {
description?: string
}
}
export interface AncillaryChoiceCardProps extends AncillaryCardProps {
name: string
id?: string
value: string
}

View File

@@ -9,7 +9,7 @@ import type { Block as DestinationCountryPageBlock } from "@scandic-hotels/trpc/
import type { Block as DestinationOverviewPageBlock } from "@scandic-hotels/trpc/types/destinationOverviewPage"
import type { Block as LoyaltyPageBlock } from "@scandic-hotels/trpc/types/loyaltyPage"
export type Blocks =
type Blocks =
| AccountPageBlock
| CampaignPageBlock
| CampaignOverviewPageBlock

View File

@@ -9,14 +9,6 @@ type CardTheme = Exclude<
"image"
>
export const INFO_CARD_THEMES = [
"one",
"two",
"three",
"primaryInverted",
"primaryStrong",
] as const satisfies readonly CardTheme[]
export interface InfoCardProps {
scriptedTopTitle?: string
heading: string

View File

@@ -1,12 +0,0 @@
import type { Embeds } from "@/types/requests/embeds"
import type { Node } from "@scandic-hotels/trpc/types/edges"
import type { RTENode } from "../rte/node"
import type { RenderOptions } from "../rte/option"
export type DeprecatedJsonToHtmlProps = {
embeds: Node<Embeds>[]
nodes: RTENode[]
renderOptions?: RenderOptions
}
export type EmbedByUid = Record<string, Node<Embeds>>

View File

@@ -18,5 +18,3 @@ export enum DropdownTypeEnum {
HeaderLanguageSwitcherMobile = "headerLanguageSwitcherMobile",
FooterLanguageSwitcher = "footerLanguageSwitcher",
}
export type DropdownType = `${DropdownTypeEnum}`

View File

@@ -1,6 +0,0 @@
import type { ImageProps } from "next/image"
export interface AvatarProps {
image?: ImageProps
initials?: string | null
}

View File

@@ -68,11 +68,6 @@ export enum MeetingsHeading {
Default = "Great minds meet here",
}
export type HeadingEnum =
| RestaurantHeadings
| WellnessHeadings
| MeetingsHeading
export enum HealthFacilitiesEnum {
Jacuzzi = "Jacuzzi",
Gym = "Gym",
@@ -101,15 +96,6 @@ export enum IndoorPoolDetails {
DepthFrom = "DepthFrom",
}
export enum OutdoorPoolDetails {
Length = "Length",
Width = "Width",
DepthTo = "DepthTo",
DepthFrom = "DepthFrom",
OpenMonthFrom = "OpenMonthFrom",
OpenMonthTo = "OpenMonthTo",
}
export enum SaunaDetails {
SeparateMenAndWomen = "SeparateMenAndWomen",
}

View File

@@ -1,6 +1,6 @@
import type { meetingRoomsSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/meetingRoom"
import type { z } from "zod"
export type MeetingRoomData = z.output<typeof meetingRoomsSchema>
type MeetingRoomData = z.output<typeof meetingRoomsSchema>
export type MeetingRooms = MeetingRoomData["data"]
export type MeetingRoom = MeetingRooms[number]["attributes"]

View File

@@ -1,3 +0,0 @@
export type BedTypeInfoProps = {
hasMultipleBedTypes: boolean
}

View File

@@ -1,10 +1,6 @@
import type { Product } from "@scandic-hotels/trpc/types/roomAvailability"
import type { Price } from "../price"
export interface RoomPrice {
perNight: Price
perStay: Price
}
export type RoomRate = Product

View File

@@ -1,7 +1,5 @@
import { z } from "zod"
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
export const cancelStaySchema = z.object({
rooms: z.array(
z.object({
@@ -11,34 +9,4 @@ export const cancelStaySchema = z.object({
),
})
export interface CancelStayProps {
handleCloseModal: () => void
hotel: Hotel
}
export type CancelStayFormValues = z.output<typeof cancelStaySchema>
export interface RoomDetails {
id: string
roomName: string
roomTypeCode: string
rateDefinition: {
breakfastIncluded: boolean
cancellationRule: string | null
cancellationText: string | null
generalTerms: string[]
isMemberRate: boolean
mustBeGuaranteed: boolean
rateCode: string
title: string | null
}
isMainBooking?: boolean
}
export interface StayDetails {
checkInDate: string
checkOutDate: string
nightsText: string
adultsText: string
childrenText: string
}

View File

@@ -1,7 +1,5 @@
import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language"
export const changeDatesSchema = z.object({
checkInDate: z.string(),
checkOutDate: z.string(),
@@ -9,30 +7,6 @@ export const changeDatesSchema = z.object({
export type ChangeDatesSchema = z.output<typeof changeDatesSchema>
export interface QueryInput {
hotelId: string
roomStayStartDate: string
roomStayEndDate: string
adults: number
children: string
bookingCode: string
rateCode: string
roomTypeCode: string
lang: Lang
}
export const DEFAULT_QUERY_INPUT: QueryInput = {
hotelId: "",
roomStayStartDate: "",
roomStayEndDate: "",
adults: 1,
children: "",
bookingCode: "",
rateCode: "",
roomTypeCode: "",
lang: Lang.en,
}
export interface ChangeDatesStepsProps {
closeModal: () => void
}

View File

@@ -1,6 +1,4 @@
import { z } from "zod"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import type { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
interface TPrice {
additionalPrice?: number
@@ -14,18 +12,3 @@ export interface Price {
requested?: TPrice
local: TPrice
}
export const PointsPriceSchema = z
.object({
localPrice: z.object({
currency: z.nativeEnum(CurrencyEnum),
price: z.number(),
additionalPrice: z.number().optional(),
additionalPriceCurrency: z.nativeEnum(CurrencyEnum).optional(),
}),
})
.transform((data) => ({
local: {
...data.localPrice,
},
}))

View File

@@ -1,11 +0,0 @@
import type { ProductType } from "@scandic-hotels/trpc/types/availability"
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
export type HotelData = {
hotelData: Hotel
price: ProductType
}
export interface NullableHotelData extends Omit<HotelData, "hotelData"> {
hotelData: HotelData["hotelData"] | null
}

View File

@@ -1,20 +0,0 @@
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
export type HotelFilter = Hotel["detailedFacilities"][number] & {
hotelId: string
hotelIds: string[]
}
export type CategorizedHotelFilters = {
facilityFilters: HotelFilter[]
surroundingsFilters: HotelFilter[]
}
export type HotelFiltersProps = {
filters: CategorizedHotelFilters
className?: string
}
export type HotelFilterModalProps = {
filters: CategorizedHotelFilters
}

View File

@@ -1,36 +0,0 @@
import type { ProductTypeCheque } from "@scandic-hotels/trpc/types/availability"
import type { Amenities } from "@scandic-hotels/trpc/types/hotel"
import type { Coordinates } from "@/types/components/maps/coordinates"
export type HotelPin = {
bookingCode?: string | null
name: string
coordinates: Coordinates
chequePrice: ProductTypeCheque["localPrice"] | null
publicPrice: number | null
memberPrice: number | null
redemptionPrice: number | null
voucherPrice: number | null
rateType: string | null
currency: string
images: {
src: string
altText: string
altText_En: string
title: string
title_En: string
}[]
amenities: Amenities
ratings: number | null
operaId: string
facilityIds: number[]
hasEnoughPoints: boolean
}
export interface HotelCardDialogProps {
type?: "listing" | "standalone"
isOpen: boolean
data: HotelPin
handleClose: (event: { stopPropagation: () => void }) => void
}

View File

@@ -1,19 +0,0 @@
import type {
ProductTypeCheque,
ProductTypePrices,
ProductTypeVoucher,
} from "@scandic-hotels/trpc/types/availability"
export type PriceCardProps = {
productTypePrices: ProductTypePrices
isMemberPrice?: boolean
className?: string
}
export type VoucherCardProps = {
productTypeVoucher: ProductTypeVoucher
}
export type BonusChequeCardProps = {
productTypeVoucher: ProductTypeCheque
}

View File

@@ -1,15 +0,0 @@
import type { BookingSearchType } from "@scandic-hotels/booking-flow/searchType"
import type { Child } from "@scandic-hotels/trpc/types/child"
export type SelectHotelBooking = {
hotelId?: string
city?: string
fromDate: string
toDate: string
rooms: {
adults: number
childrenInRoom?: Child[]
}[]
bookingCode?: string
searchType?: BookingSearchType
}

View File

@@ -1,7 +0,0 @@
import type { SelectRateBooking } from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
export interface HotelInfoCardProps {
booking: SelectRateBooking
hotel: Hotel
}

View File

@@ -1,7 +0,0 @@
import type { Price } from "../price"
export interface MobileSummaryProps {
isAllRoomsSelected: boolean
isUserLoggedIn: boolean
totalPriceToShow: Price
}

View File

@@ -1,14 +0,0 @@
import type {
Product,
RoomConfiguration,
} from "@scandic-hotels/trpc/types/roomAvailability"
export interface RatesProps {
roomConfiguration: RoomConfiguration
}
export interface SharedRateCardProps
extends Pick<RoomConfiguration, "roomTypeCode"> {
handleSelectRate: (product: Product) => void
nights: number
}

View File

@@ -1,13 +0,0 @@
import type { Package } from "@scandic-hotels/trpc/types/packages"
import type { RoomConfiguration } from "@scandic-hotels/trpc/types/roomAvailability"
export type RoomListItemProps = {
roomConfiguration: RoomConfiguration
}
export type RoomListItemImageProps = Pick<
RoomConfiguration,
"roomType" | "roomTypeCode" | "roomsLeft"
> & {
roomPackages: Package[]
}

View File

@@ -1,34 +0,0 @@
import type { SelectRateBooking } from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
import type { Child } from "@scandic-hotels/trpc/types/child"
import type { Packages } from "@scandic-hotels/trpc/types/packages"
import type { RoomPrice, RoomRate } from "./enterDetails/details"
import type { Price } from "./price"
export type RoomsData = {
rateDetails: string[] | undefined
roomType: string
cancellationText: string
roomPrice: RoomPrice
adults: number
children?: Child[]
packages: Packages | null
}
export interface SelectRateSummaryProps {
booking: SelectRateBooking
isMember: boolean
totalPrice: Price
vat: number
rooms: Array<{
adults: number
childrenInRoom: Child[] | undefined
roomType: string
roomPrice: RoomPrice
roomRate: RoomRate
rateDetails: string[] | undefined
cancellationText: string
packages?: Packages
} | null>
toggleSummaryOpen: () => void
}

View File

@@ -7,7 +7,7 @@ export enum LanguageSwitcherTypesEnum {
Footer = "footer",
}
export type LanguageSwitcherTypes = `${LanguageSwitcherTypesEnum}`
type LanguageSwitcherTypes = `${LanguageSwitcherTypesEnum}`
export interface LanguageSwitcherProps {
type: LanguageSwitcherTypes
@@ -20,5 +20,6 @@ export interface LanguageSwitcherContentProps {
export interface LanguageSwitcherContainerProps {
type: LanguageSwitcherTypes
// eslint-disable-next-line @typescript-eslint/no-explicit-any
children: ReactElement<any, any>
}

View File

@@ -1,27 +1,17 @@
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { VariantProps } from "class-variance-authority"
import type { awardPointsVariants } from "@/components/Blocks/DynamicContent/Points/EarnAndBurn/AwardPoints/awardPointsVariants"
import type { UserQueryRouter } from "../user"
export type TransactionResponse = Awaited<
type TransactionResponse = Awaited<
ReturnType<UserQueryRouter["transaction"]["friendTransactions"]>
>
export type TransactionsNonNullResponseObject = NonNullable<TransactionResponse>
export type Transactions =
type TransactionsNonNullResponseObject = NonNullable<TransactionResponse>
type Transactions =
NonNullable<TransactionsNonNullResponseObject>["data"]["transactions"]
export type Transaction =
type Transaction =
NonNullable<TransactionsNonNullResponseObject>["data"]["transactions"][number]
export type ClientEarnAndBurnProps = {
initialData: TransactionsNonNullResponseObject
lang: Lang
}
export type EarnAndBurnProps = {
lang: Lang
}
export interface ClientTableProps {
transactions: Transactions
}
@@ -30,7 +20,4 @@ export interface RowProps {
transaction: Transaction
}
export interface AwardPointsProps extends Pick<Transaction, "awardPoints"> {}
export interface AwardPointsVariantProps
extends VariantProps<typeof awardPointsVariants> {}
export type AwardPointsVariantProps = VariantProps<typeof awardPointsVariants>

View File

@@ -8,27 +8,3 @@ export const enum Status {
error = "error",
success = "success",
}
type Data = Record<
string,
string | undefined | Record<string, string | undefined>
>
type Issue = {
field: string
message: string
}
export type State = {
data: Data
message: string
} & (
| {
issues: never
status: Status.success
}
| {
issues: Issue[]
status: Status.error
}
)

View File

@@ -24,15 +24,6 @@ export interface AddedAncillariesProps {
booking: Room
}
export interface AncillaryProps {
ancillary: Ancillary["ancillaryContent"][number]
}
export interface AncillaryGridModalProps {
ancillaries: Ancillaries
user: User | null
}
export interface AddAncillaryFlowModalProps {
booking: Room
packages: Packages | null
@@ -40,14 +31,6 @@ export interface AddAncillaryFlowModalProps {
savedCreditCards: CreditCard[] | null
}
export type DeliveryTimeOption = {
label: string
value: string
}
export interface DeliveryMethodStepProps {
deliveryTimeOptions: DeliveryTimeOption[]
}
export interface SelectQuantityStepProps {
user: User | null
}

View File

@@ -1,14 +1,10 @@
import type { UserQueryRouter } from "../user"
export type PreviousStaysResponse = Awaited<
type PreviousStaysResponse = Awaited<
ReturnType<UserQueryRouter["stays"]["previous"]>
>
export type PreviousStaysNonNullResponseObject =
NonNullable<PreviousStaysResponse>
export type PreviousStays =
NonNullable<PreviousStaysNonNullResponseObject>["data"]
export type PreviousStay =
NonNullable<PreviousStaysNonNullResponseObject>["data"][number]
export interface PreviousStaysClientProps {
initialPreviousStays: PreviousStaysNonNullResponseObject

View File

@@ -1,14 +1,10 @@
import type { UserQueryRouter } from "../user"
export type UpcomingStaysResponse = Awaited<
type UpcomingStaysResponse = Awaited<
ReturnType<UserQueryRouter["stays"]["upcoming"]>
>
export type UpcomingStaysNonNullResponseObject =
NonNullable<UpcomingStaysResponse>
export type UpcomingStays =
NonNullable<UpcomingStaysNonNullResponseObject>["data"]
export type UpcomingStay =
NonNullable<UpcomingStaysNonNullResponseObject>["data"][number]
export interface UpcomingStaysClientProps {
initialUpcomingStays: UpcomingStaysNonNullResponseObject

View File

@@ -1,8 +0,0 @@
export interface SearchProps {
handlePressEnter: () => void
}
export type SearchHistoryItem = {
type: "cities" | "hotels"
id: string
}

View File

@@ -1,12 +1,4 @@
import type { Room } from "@scandic-hotels/trpc/types/hotel"
import type { SafeUser } from "@/types/user"
export type BookedRoomSidePeekProps = {
close: () => void
confirmationNumber: string
room: Room
user: SafeUser
}
export type RoomDetailsProps = {
roomDescription: string

View File

@@ -1,7 +1,7 @@
import type { SidebarBlock as ContentPageSidebarBlock } from "@scandic-hotels/trpc/types/contentPage"
import type { SidebarBlock as LoyaltyPageSidebarBlock } from "@scandic-hotels/trpc/types/loyaltyPage"
export type Blocks = ContentPageSidebarBlock | LoyaltyPageSidebarBlock
type Blocks = ContentPageSidebarBlock | LoyaltyPageSidebarBlock
export interface SidebarProps {
blocks: Blocks[]

View File

@@ -1,9 +0,0 @@
export enum scriptedCardThemeEnum {
one = "one",
two = "two",
three = "three",
primaryDim = "primaryDim",
primaryDark = "primaryDark",
primaryInverted = "primaryInverted",
primaryStrong = "primaryStrong",
}

View File

@@ -1,8 +0,0 @@
export enum SignatureHotelEnum {
DowntownCamper = "879",
GrandHotelOslo = "340",
Haymarket = "890",
HotelNorge = "785",
Marski = "605",
TheDock = "796",
}

View File

@@ -1,16 +0,0 @@
// import type { MessageDescriptor } from "@formatjs/intl"
// Module augmentation
declare module "@formatjs/intl" {
// We are unable to override description field on MessageDescriptor from formatjs.
// Module augmentation does not allow for that. But we leave it here for reference.
// Instead we export our own LokaliseMessageDescriptor and use that where we control the code.
// For example in our custom formatter in i18n/formatter.ts
// interface MessageDescriptor {
// description?: {
// context?: string
// limit?: number
// tags?: string[]
// }
// }
}

View File

@@ -1,10 +0,0 @@
import type { MessageDescriptor } from "@formatjs/intl"
export interface LokaliseMessageDescriptor
extends Omit<MessageDescriptor, "description"> {
description: {
context?: string
limit?: number
tags?: string
}
}

View File

@@ -7,7 +7,7 @@ export type SearchParams<S = object> = {
searchParams: Promise<S & { [key: string]: string }>
}
export type Params<P = object> = {
type Params<P = object> = {
params: Promise<P>
}
@@ -41,15 +41,6 @@ export type UIDParams = {
uid: string
}
export type UriParams = {
uri: string | string[]
}
export type PreviewParams = {
uri?: string
live_preview?: string
}
export type LayoutArgs<P = undefined> = P extends undefined
? unknown
: Params<P>

View File

@@ -1,12 +0,0 @@
import type { SelectRateBooking } from "@scandic-hotels/booking-flow/types/components/selectRate/selectRate"
import type { AvailabilityError } from "@scandic-hotels/booking-flow/types/stores/rates"
import type { Room } from "@scandic-hotels/trpc/types/hotel"
import type { RoomsAvailability } from "@scandic-hotels/trpc/types/roomAvailability"
export interface RatesProviderProps extends React.PropsWithChildren {
booking: SelectRateBooking
hotelType: string | undefined
roomCategories: Room[]
roomsAvailability: (RoomsAvailability | AvailabilityError)[] | undefined
vat: number
}

View File

@@ -1,6 +0,0 @@
import type { SelectedRoom } from "@scandic-hotels/booking-flow/types/stores/rates"
export interface RoomProviderProps extends React.PropsWithChildren {
idx: number
room: SelectedRoom
}

View File

@@ -1,81 +0,0 @@
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { EdgesWithTotalCount } from "@scandic-hotels/trpc/types/edges"
import type { Typename } from "../utils/typename"
export enum Section {
ContactBlockSectionsExtraInfo = "ContactBlockSectionsExtraInfo",
ContactBlockSectionsMailingAddress = "ContactBlockSectionsMailingAddress",
ContactBlockSectionsPhone = "ContactBlockSectionsPhone",
ContactBlockSectionsTitle = "ContactBlockSectionsTitle",
ContactBlockSectionsVisitingAddress = "ContactBlockSectionsVisitingAddress",
}
type ExtraInfo = Typename<
{
extra_info: {
text: string[]
}
},
Section.ContactBlockSectionsExtraInfo
>
type MailingAddress = Typename<
{
mailing_address: {
city: string
country: string
name: string
street: string
zip: string
}
},
Section.ContactBlockSectionsMailingAddress
>
type Phone = Typename<
{
phone: {
number: number
title: string
}
},
Section.ContactBlockSectionsPhone
>
type Title = Typename<
{
title: {
text: string
}
},
Section.ContactBlockSectionsTitle
>
type VisitingAddress = Typename<
{
visiting_address: {
city: string
country: string
street: string
zip: string
}
},
Section.ContactBlockSectionsVisitingAddress
>
type Sections = ExtraInfo | MailingAddress | Phone | Title | VisitingAddress
export type ContactNode = {
sections: Sections[]
system: {
locale: Lang
uid: string
}
}
export type Contact = {
contact: {
contactConnection: EdgesWithTotalCount<ContactNode>
}
}

View File

@@ -1,5 +0,0 @@
import type { Puff } from "../puff"
export type PuffAside = {
puff: Puff
}

View File

@@ -1,49 +0,0 @@
import type { Typename } from "../utils/typename"
enum ListItemStyleEnum {
checkmark = "checkmark",
default = "default",
}
type ListItemStyle = keyof typeof ListItemStyleEnum
export enum BlockListItemsEnum {
CurrentBlocksPageBlocksListBlockListItemsListItem = "CurrentBlocksPageBlocksListBlockListItemsListItem",
CurrentBlocksPageBlocksListBlockListItemsListItemExternalLink = "CurrentBlocksPageBlocksListBlockListItemsListItemExternalLink",
}
type ExternalLinkListItem = Typename<
{
list_item_external_link: {
link: {
href: string
title: string
}
list_item_style: ListItemStyle
subtitle?: string
}
},
BlockListItemsEnum.CurrentBlocksPageBlocksListBlockListItemsListItemExternalLink
>
type RegularListItem = Typename<
{
list_item: {
list_item_style: ListItemStyle
subtitle?: string
title: string
}
},
BlockListItemsEnum.CurrentBlocksPageBlocksListBlockListItemsListItem
>
export type ListItem = ExternalLinkListItem | RegularListItem
export type List = {
list: {
title?: string
list_items: ListItem[]
}
}
export type ListProps = List

View File

@@ -1,9 +0,0 @@
import type { Puff } from "../puff"
export type PuffBlock = {
puffs: {
puffs: {
puff: Puff
}[]
}
}

View File

@@ -1,13 +0,0 @@
import type { EdgesWithTotalCount } from "@scandic-hotels/trpc/types/edges"
import type { RTEDocument } from "@/types/rte/node"
import type { Embeds } from "../embeds"
export type Text = {
text: {
content: {
embedded_itemsConnection: EdgesWithTotalCount<Embeds>
json: RTEDocument
}
}
}

View File

@@ -1,22 +0,0 @@
import type { ImageContainer } from "./imageContainer"
import type { SysAsset } from "./utils/asset"
import type { EmbedEnum } from "./utils/embeds"
import type { PageLink, PageLinkWithOriginalUrl } from "./utils/pageLink"
import type { TypenameInterface } from "./utils/typename"
interface AccountPage
extends TypenameInterface<EmbedEnum.AccountPage>,
PageLink {}
interface ContentPage
extends TypenameInterface<EmbedEnum.ContentPage>,
PageLinkWithOriginalUrl {}
interface LoyaltyPage
extends TypenameInterface<EmbedEnum.LoyaltyPage>,
PageLinkWithOriginalUrl {}
export type Embeds =
| AccountPage
| ContentPage
| ImageContainer
| LoyaltyPage
| SysAsset

View File

@@ -1,6 +0,0 @@
import type { EdgesWithTotalCount } from "@scandic-hotels/trpc/types/edges"
import type { Image } from "@scandic-hotels/trpc/types/image"
export type Hero = {
imagesConnection: EdgesWithTotalCount<Image>
}

View File

@@ -1,12 +0,0 @@
import type { ImageVaultAsset } from "@scandic-hotels/common/utils/imageVault"
import type { System } from "@scandic-hotels/trpc/routers/contentstack/schemas/system"
import type { EmbedEnum } from "./utils/embeds"
import type { TypenameInterface } from "./utils/typename"
export interface ImageContainer
extends TypenameInterface<EmbedEnum.ImageContainer>,
System {
image_left?: ImageVaultAsset
image_right?: ImageVaultAsset
}

View File

@@ -1,11 +0,0 @@
import type { EdgesWithTotalCount } from "@scandic-hotels/trpc/types/edges"
import type { RTEDocument } from "../rte/node"
import type { Embeds } from "./embeds"
export type Preamble = {
text: {
embedded_itemsConnection: EdgesWithTotalCount<Embeds>
json: RTEDocument
}
}

View File

@@ -1,22 +0,0 @@
import type { EdgesWithTotalCount } from "@scandic-hotels/trpc/types/edges"
import type { Image } from "@scandic-hotels/trpc/types/image"
import type { RTEDocument } from "../rte/node"
export enum PuffStyleEnum {
button = "button",
default = "default",
}
export type Puff = {
imageConnection: EdgesWithTotalCount<Image>
link: {
href: string
title?: string
}
puff_style: PuffStyleEnum
text: {
json: RTEDocument
}
title: string
}

View File

@@ -1,3 +0,0 @@
export type TrackingData = {
current_blocks_page: { url: string; title: string }
}

View File

@@ -1,8 +0,0 @@
export interface AllRequestResponse<T> {
items: T[]
}
export interface AllRequestResponseWithTotal<T> {
items: T[]
total: number
}

View File

@@ -1,8 +0,0 @@
import type { Image } from "@scandic-hotels/trpc/types/image"
import type { EmbedEnum } from "./embeds"
import type { TypenameInterface } from "./typename"
export interface SysAsset
extends TypenameInterface<EmbedEnum.SysAsset>,
Image {}

View File

@@ -1,11 +0,0 @@
export enum EmbedEnum {
CurrentBlocksPage = "CurrentBlocksPage",
SysAsset = "SysAsset",
ImageContainer = "ImageContainer",
LoyaltyPage = "LoyaltyPage",
AccountPage = "AccountPage",
ContentPage = "ContentPage",
HotelPage = "HotelPage",
}
export type Embed = keyof typeof EmbedEnum

View File

@@ -1,17 +0,0 @@
import type { z } from "zod"
import type {
extendedPageLinkSchema,
pageLinkSchema,
} from "@scandic-hotels/trpc/routers/contentstack/schemas/pageLinks"
import type { EmbedEnum } from "./embeds"
import type { TypenameInterface } from "./typename"
export interface PageLink extends z.output<typeof pageLinkSchema> {}
export interface PageLinkWithOriginalUrl
extends z.output<typeof extendedPageLinkSchema> {}
export interface PageLinkType
extends TypenameInterface<EmbedEnum.CurrentBlocksPage>,
PageLink {}

View File

@@ -1,22 +1,3 @@
export enum AsideTypenameEnum {
CurrentBlocksPageAsideContact = "CurrentBlocksPageAsideContact",
CurrentBlocksPageAsidePuff = "CurrentBlocksPageAsidePuff",
}
export type AsideTypename = keyof typeof AsideTypenameEnum
export enum BlocksTypenameEnum {
CurrentBlocksPageBlocksList = "CurrentBlocksPageBlocksList",
CurrentBlocksPageBlocksPuffs = "CurrentBlocksPageBlocksPuffs",
CurrentBlocksPageBlocksText = "CurrentBlocksPageBlocksText",
}
export type BlocksTypename = keyof typeof BlocksTypenameEnum
export type Typename<T, K> = T & {
__typename: K
}
export interface TypenameInterface<K> {
__typename: K
}

View File

@@ -1,51 +0,0 @@
import type { Lang } from "@scandic-hotels/common/constants/language"
import type {
ImageVaultAsset,
ImageVaultAssetResponse,
} from "@scandic-hotels/common/utils/imageVault"
import type {
EmbedTypesEnum,
RTEItemType,
RTEItemTypeEnum,
} from "@scandic-hotels/trpc/types/RTEenums"
export interface Attributes {
[key: string]: any
"class-name"?: string
type: RTEItemType
}
export interface RTEAssetAttrs extends Attributes {
alt: string
"asset-alt": string
"asset-link": string
"asset-name": string
"asset-type": "image/png" | "image/jpg" | "image/jpeg"
"asset-uid": string
"display-type": EmbedTypesEnum.display
"content-type-uid": "sys_assets"
inline: false
type: RTEItemTypeEnum.asset
}
export interface RTEAnchorAttrs extends Attributes {
target: string
url: string
}
export interface RTELinkAttrs extends Attributes {
"display-type": EmbedTypesEnum.link
"class-name": string
"content-type-uid": string
"entry-uid": string
locale: Lang
href: string
target: HTMLAnchorElement["target"]
type: RTEItemTypeEnum.entry
}
export type RTEImageVaultAttrs = Attributes & {
height: string
style: string[]
width: string
} & (ImageVaultAssetResponse | ImageVaultAsset)

View File

@@ -1,98 +0,0 @@
import type { RTETypeEnum } from "@scandic-hotels/trpc/types/RTEenums"
import type { JSX } from "react"
import type { EmbedByUid } from "../components/deprecatedjsontohtml"
import type {
Attributes,
RTEAnchorAttrs,
RTEAssetAttrs,
RTEImageVaultAttrs,
RTELinkAttrs,
} from "./attrs"
import type { RenderOptions } from "./option"
export interface RTEDefaultNode {
attrs: Attributes
children: RTENode[]
type: RTETypeEnum
uid: string
}
export interface RTELinkNode {
attrs: Attributes
children: RTENode[]
type: RTETypeEnum
uid: string
}
export interface RTEReferenceAssetNode extends RTEDefaultNode {
attrs: RTEAssetAttrs
}
export interface RTEAnchorNode extends RTEDefaultNode {
attrs: RTEAnchorAttrs
type: RTETypeEnum.a
}
export interface RTEReferenceLinkNode extends RTEDefaultNode {
attrs: RTELinkAttrs
}
export interface RTEImageVaultNode extends RTEDefaultNode {
attrs: RTEImageVaultAttrs
type: RTETypeEnum.ImageVault
}
export enum RTEMarkType {
bold = "bold",
break = "break",
classnameOrId = "classnameOrId",
inlineCode = "inlineCode",
italic = "italic",
strikethrough = "strikethrough",
subscript = "subscript",
superscript = "superscript",
underline = "underline",
}
type RTETextNodeOptionalKeys = {
[key in RTEMarkType]?: boolean
}
export type RTETextNode = RTETextNodeOptionalKeys & {
classname?: string
id?: string
text: string
}
export type RTERegularNode = RTEDefaultNode | RTEAnchorNode | RTEImageVaultNode
export type RTEImageNode = RTEDefaultNode | RTEImageVaultNode
export type RTEReferenceNode = RTEAnchorNode
export type RTENode = RTERegularNode | RTEReferenceNode | RTETextNode
export type RTERenderMark = (
children: React.ReactNode,
classname?: string,
id?: string
) => JSX.Element
export interface RTEDocument extends RTEDefaultNode {
type: RTETypeEnum.doc
_version: number
}
export type RTERenderOptionComponent = (
node: RTERegularNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => React.ReactNode
export type RTENext = (
nodes: RTENode[],
embeds: EmbedByUid,
fullRenderOptions: RenderOptions
) => string

View File

@@ -1,5 +0,0 @@
import type { RTERenderMark, RTERenderOptionComponent } from "./node"
export type RenderOptions = {
[type: string]: RTERenderOptionComponent | RTERenderMark
}

View File

@@ -1,5 +1,3 @@
export enum SidePeekEnum {
hotelDetails = "hotel-detail-side-peek",
roomDetails = "room-detail-side-peek",
bookedRoomDetails = "booked-room-detail-side-peek",
}

View File

@@ -20,12 +20,6 @@ interface Actions {
setIsLoading: (isLoading: boolean) => void
}
export interface SubmitCallbackData {
sort: HotelSortOption
defaultSort: HotelSortOption
filters: string[]
basePath: string
}
export interface DestinationDataState {
actions: Actions
allCities: DestinationCityListItem[]

Some files were not shown because too many files have changed in this diff Show More