Merge develop

This commit is contained in:
Linus Flood
2024-09-27 14:20:53 +02:00
171 changed files with 3507 additions and 5188 deletions

View File

@@ -0,0 +1,14 @@
"use client"
import { useEffect } from "react"
import LoadingSpinner from "@/components/LoadingSpinner"
export default function LogoutInterceptedRoute() {
// Reload the browser on logout in order to flush router cache. This is to make sure we don't show stale user specific data.
useEffect(() => {
window.location.reload()
}, [])
return <LoadingSpinner />
}

View File

@@ -46,10 +46,13 @@ export default async function ProtectedLayout({
redirect(redirectURL)
case "notfound":
console.error(`[layout:protected] notfound user loading error`)
break
case "unknown":
console.error(`[layout:protected] unknown user loading error`)
break
default:
console.error(`[layout:protected] unhandled user loading error`)
break
}
return <p>Something went wrong!</p>
}

View File

@@ -0,0 +1,90 @@
import { revalidateTag } from "next/cache"
import { headers } from "next/headers"
import { NextRequest } from "next/server"
import { z } from "zod"
import { Lang } from "@/constants/languages"
import { env } from "@/env/server"
import { badRequest, internalServerError, notFound } from "@/server/errors/next"
import { generateLoyaltyConfigTag } from "@/utils/generateTag"
enum LoyaltyConfigContentTypes {
loyalty_level = "loyalty_level",
rewards = "rewards",
}
const validateJsonBody = z.object({
data: z.object({
content_type: z.object({
uid: z.nativeEnum(LoyaltyConfigContentTypes),
}),
entry: z.object({
reward_id: z.string().optional(),
level_id: z.string().optional(),
locale: z.nativeEnum(Lang),
}),
}),
})
export async function POST(request: NextRequest) {
try {
const headersList = headers()
const secret = headersList.get("x-revalidate-secret")
if (secret !== env.REVALIDATE_SECRET) {
console.error(`Invalid Secret`)
console.error({ secret })
return badRequest({ revalidated: false, now: Date.now() })
}
const data = await request.json()
const validatedData = validateJsonBody.safeParse(data)
if (!validatedData.success) {
console.error(
"Bad validation for `validatedData` in loyaltyConfig revalidation"
)
console.error(validatedData.error)
return internalServerError({ revalidated: false, now: Date.now() })
}
const {
data: {
data: { content_type, entry },
},
} = validatedData
let tag = ""
if (
content_type.uid === LoyaltyConfigContentTypes.loyalty_level &&
entry.level_id
) {
tag = generateLoyaltyConfigTag(
entry.locale,
content_type.uid,
entry.level_id
)
} else if (
content_type.uid === LoyaltyConfigContentTypes.rewards &&
entry.reward_id
) {
tag = generateLoyaltyConfigTag(
entry.locale,
content_type.uid,
entry.reward_id
)
} else {
console.error("Invalid content_type")
return notFound({ revalidated: false, now: Date.now() })
}
console.info(`Revalidating loyalty config tag: ${tag}`)
revalidateTag(tag)
return Response.json({ revalidated: true, now: Date.now() })
} catch (error) {
console.error("Failed to revalidate tag(s) for loyalty config")
console.error(error)
return internalServerError({ revalidated: false, now: Date.now() })
}
}

View File

@@ -107,6 +107,7 @@
--main-menu-mobile-height: 75px;
--main-menu-desktop-height: 118px;
--booking-widget-desktop-height: 95px;
--hotel-page-map-desktop-width: 23.75rem;
/* Z-INDEX */

View File

@@ -1,9 +1,9 @@
import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header"
import Card from "@/components/TempDesignSystem/Card"
import ContentCard from "@/components/TempDesignSystem/ContentCard"
import Grids from "@/components/TempDesignSystem/Grids"
import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard"
import TeaserCard from "@/components/TempDesignSystem/TeaserCard"
import type { CardsGridProps } from "@/types/components/blocks/cardsGrid"
import { CardsGridEnum } from "@/types/enums/cardsGrid"
@@ -22,37 +22,37 @@ export default function CardsGrid({
<Grids.Stackable>
{cards_grid.cards.map((card) => {
switch (card.__typename) {
case CardsGridEnum.cards.Card: {
return card.isContentCard ? (
<ContentCard
case CardsGridEnum.cards.Card:
return (
<Card
theme={cards_grid.theme ?? "one"}
key={card.system.uid}
scriptedTopTitle={card.scripted_top_title}
heading={card.heading}
bodyText={card.body_text}
secondaryButton={card.secondaryButton}
primaryButton={card.primaryButton}
/>
)
case CardsGridEnum.cards.TeaserCard:
return (
<TeaserCard
key={card.system.uid}
title={card.heading}
description={card.body_text}
backgroundImage={card.backgroundImage}
primaryButton={card.primaryButton}
secondaryButton={card.secondaryButton}
sidePeekButton={card.sidePeekButton}
style="default"
title={card.heading}
/>
) : (
<Card
key={card.system.uid}
bodyText={card.body_text}
heading={card.heading}
primaryButton={card.primaryButton}
secondaryButton={card.secondaryButton}
scriptedTopTitle={card.scripted_top_title}
theme={cards_grid.theme || "one"}
image={card.image}
/>
)
}
case CardsGridEnum.cards.LoyaltyCard:
return (
<LoyaltyCard
key={card.system.uid}
bodyText={card.body_text}
heading={card.heading}
image={card.image}
heading={card.heading}
bodyText={card.body_text}
link={card.link}
/>
)

View File

@@ -1,58 +0,0 @@
import { MembershipLevelEnum } from "@/constants/membershipLevels"
import { getProfile } from "@/lib/trpc/memoizedRequests"
import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header"
import SectionLink from "@/components/Section/Link"
import Grids from "@/components/TempDesignSystem/Grids"
import Title from "@/components/TempDesignSystem/Text/Title"
import { getLang } from "@/i18n/serverContext"
import { getMembershipLevelObject } from "@/utils/membershipLevel"
import styles from "./current.module.css"
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
export default async function CurrentBenefitsBlock({
title,
subtitle,
link,
}: AccountPageComponentProps) {
const user = await getProfile()
// TAKE NOTE: we need clarification on how benefits stack from different levels
// in order to determine if a benefit is specific to a level or if it is a cumulative benefit
// we might have to add a new boolean property "exclusive" or similar
if (!user || "error" in user || !user.membership) {
return null
}
const currentLevel = getMembershipLevelObject(
user.membership.membershipLevel as MembershipLevelEnum,
getLang()
)
if (!currentLevel) {
// TODO: handle this case?
return null
}
return (
<SectionContainer>
<SectionHeader title={title} link={link} preamble={subtitle} />
<Grids.Stackable>
{currentLevel.benefits.map((benefit, idx) => (
<article className={styles.card} key={`${currentLevel}-${idx}`}>
<Title
as="h4"
level="h3"
textAlign="center"
textTransform="regular"
>
{benefit.title}
</Title>
</article>
))}
</Grids.Stackable>
<SectionLink link={link} variant="mobile" />
</SectionContainer>
)
}

View File

@@ -1,88 +1,43 @@
"use client"
import { notFound, useParams } from "next/navigation"
import { useIntl } from "react-intl"
import { Lang } from "@/constants/languages"
import { serverClient } from "@/lib/trpc/server"
import { CheckIcon } from "@/components/Icons"
import {
BestFriend,
CloseFriend,
DearFriend,
GoodFriend,
LoyalFriend,
NewFriend,
TrueFriend,
} from "@/components/Levels"
import MembershipLevelIcon from "@/components/Levels/Icon"
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Title from "@/components/TempDesignSystem/Text/Title"
import levelsData from "@/data/loyaltyLevels"
import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import SectionWrapper from "../SectionWrapper"
import styles from "./loyaltyLevels.module.css"
import type { LoyaltyLevelsProps } from "@/types/components/blocks/dynamicContent"
import type { Level, LevelCardProps } from "@/types/components/overviewTable"
import { LoyaltyLevelsProps } from "@/types/components/blocks/dynamicContent"
import type { LevelCardProps } from "@/types/components/overviewTable"
export default function LoyaltyLevels({
export default async function LoyaltyLevels({
dynamic_content,
firstItem,
}: LoyaltyLevelsProps) {
const params = useParams()
const lang = params.lang as Lang
const { formatMessage } = useIntl()
const uniqueLevels = await serverClient().contentstack.rewards.all({
unique: true,
})
const { levels } = levelsData[lang]
return (
<SectionWrapper dynamic_content={dynamic_content} firstItem={firstItem}>
<section className={styles.cardContainer}>
{levels.map((level: Level) => (
<LevelCard
key={level.level}
formatMessage={formatMessage}
lang={lang}
level={level}
/>
{uniqueLevels.map((level) => (
<LevelCard key={level.level_id} level={level} />
))}
</section>
</SectionWrapper>
)
}
function LevelCard({ formatMessage, lang, level }: LevelCardProps) {
let Level = null
switch (level.level) {
case 1:
Level = NewFriend
break
case 2:
Level = GoodFriend
break
case 3:
Level = CloseFriend
break
case 4:
Level = DearFriend
break
case 5:
Level = LoyalFriend
break
case 6:
Level = TrueFriend
break
case 7:
Level = BestFriend
break
default: {
const loyaltyLevel = level.level as never
console.error(`Unsupported loyalty level given: ${loyaltyLevel}`)
notFound()
}
}
const pointsString = `${level.requiredPoints.toLocaleString(lang)} ${formatMessage({ id: "points" })} `
async function LevelCard({ level }: LevelCardProps) {
const lang = getLang()
const intl = await getIntl()
const pointsString = `${level.required_points.toLocaleString(lang)} ${intl.formatMessage({ id: "points" })} `
return (
<article className={styles.card}>
@@ -92,24 +47,24 @@ function LevelCard({ formatMessage, lang, level }: LevelCardProps) {
color="primaryLightOnSurfaceAccent"
tilted="large"
>
{formatMessage({ id: "Level" })} {level.level}
{intl.formatMessage({ id: "Level" })} {level.user_facing_tag}
</BiroScript>
<Level color="red" />
<MembershipLevelIcon level={level.level_id} color="red" />
</header>
<Title textAlign="center" level="h5">
{pointsString}
{level.requiredNights ? (
{level.required_nights ? (
<span className={styles.redText}>
{formatMessage({ id: "or" })} {level.requiredNights}{" "}
{formatMessage({ id: "nights" })}
{intl.formatMessage({ id: "or" })} {level.required_nights}{" "}
{intl.formatMessage({ id: "nights" })}
</span>
) : null}
</Title>
<div className={styles.textContainer}>
{level.benefits.map((benefit) => (
{level.rewards.map((reward) => (
<Caption
className={styles.levelText}
key={benefit.title}
key={reward.reward_id}
textAlign="center"
color="textMediumContrast"
>
@@ -117,7 +72,7 @@ function LevelCard({ formatMessage, lang, level }: LevelCardProps) {
className={styles.checkIcon}
color="primaryLightOnSurfaceAccent"
/>
{benefit.title}
{reward.label}
</Caption>
))}
</div>

View File

@@ -1,36 +0,0 @@
import { membershipLevels } from "@/constants/membershipLevels"
import {
BestFriend,
CloseFriend,
DearFriend,
GoodFriend,
LoyalFriend,
NewFriend,
TrueFriend,
} from "@/components/Levels"
import styles from "./membershipLevel.module.css"
import type { MembershipLevelProps } from "@/types/components/myPages/membership"
export default function MembershipLevel({ level }: MembershipLevelProps) {
switch (level) {
case membershipLevels.L1:
return <NewFriend className={styles.level} color="pale" />
case membershipLevels.L2:
return <GoodFriend className={styles.level} color="pale" />
case membershipLevels.L3:
return <CloseFriend className={styles.level} color="pale" />
case membershipLevels.L4:
return <DearFriend className={styles.level} color="pale" />
case membershipLevels.L5:
return <LoyalFriend className={styles.level} color="pale" />
case membershipLevels.L6:
return <TrueFriend className={styles.level} color="pale" />
case membershipLevels.L7:
return <BestFriend className={styles.level} color="pale" />
default:
return null
}
}

View File

@@ -1,4 +0,0 @@
.level {
height: 105px;
width: 219px;
}

View File

@@ -1,12 +1,14 @@
import { membershipLevels } from "@/constants/membershipLevels"
import {
MembershipLevelEnum,
membershipLevels,
} from "@/constants/membershipLevels"
import MembershipLevelIcon from "@/components/Levels/Icon"
import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title"
import { getIntl } from "@/i18n"
import { isHighestMembership } from "@/utils/user"
import MembershipLevel from "./MembershipLevel"
import styles from "./friend.module.css"
import type { FriendProps } from "@/types/components/myPages/friend"
@@ -20,7 +22,6 @@ export default async function Friend({
if (!membership?.membershipLevel) {
return null
}
// @ts-expect-error: membershiplevel needs proper fix
const isHighestLevel = isHighestMembership(membership.membershipLevel)
return (
@@ -30,16 +31,14 @@ export default async function Friend({
{formatMessage(
isHighestLevel
? { id: "Highest level" }
: // @ts-expect-error: membershiplevel needs proper fix
{ id: `Level ${membershipLevels[membership.membershipLevel]}` }
: { id: `Level ${membershipLevels[membership.membershipLevel]}` }
)}
</Body>
{membership ? (
<MembershipLevel
// @ts-expect-error: membershiplevel needs proper fix
level={membershipLevels[membership.membershipLevel]}
/>
) : null}
<MembershipLevelIcon
level={MembershipLevelEnum[membership.membershipLevel]}
height="110"
width="220"
/>
</header>
<div className={styles.membership}>
<Title className={styles.name} color="pale" level="h3">

View File

@@ -1,8 +1,7 @@
import { MembershipLevelEnum } from "@/constants/membershipLevels"
import { serverClient } from "@/lib/trpc/server"
import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import { getMembershipLevelObject } from "@/utils/membershipLevel"
import { getMembership } from "@/utils/user"
import PointsContainer from "./Container"
@@ -14,10 +13,12 @@ export default async function Points({ user }: UserProps) {
const { formatMessage } = await getIntl()
const membership = getMembership(user.memberships)
const nextLevel = getMembershipLevelObject(
membership?.nextLevel as MembershipLevelEnum,
getLang()
)
const nextLevel = membership?.nextLevel
? await serverClient().contentstack.loyaltyLevels.byLevel({
level: MembershipLevelEnum[membership.nextLevel],
})
: null
return (
<PointsContainer>

View File

@@ -1,53 +0,0 @@
import { ChevronDown } from "react-feather"
import Title from "@/components/TempDesignSystem/Text/Title"
import BenefitValue from "../BenefitValue"
import styles from "./benefitCard.module.css"
import type { BenefitCardProps } from "@/types/components/overviewTable"
export default function BenefitCard({
comparedValues,
title,
description,
}: BenefitCardProps) {
return (
<div className={styles.benefitCard}>
<div className={styles.benefitInfo}>
<details className={styles.details}>
<summary className={styles.summary}>
<hgroup className={styles.benefitCardHeader}>
<Title
as="h5"
level="h2"
textTransform={"regular"}
className={styles.benefitCardTitle}
>
{title}
</Title>
<span className={styles.chevron}>
<ChevronDown />
</span>
</hgroup>
</summary>
<p
className={styles.benefitCardDescription}
dangerouslySetInnerHTML={{ __html: description }}
/>
</details>
</div>
<div className={styles.benefitComparison}>
{comparedValues.map((benefit, idx) => (
<div
key={`${benefit.valueDetails}-${idx}`}
className={styles.comparisonItem}
>
<BenefitValue benefit={benefit} />
</div>
))}
</div>
</div>
)
}

View File

@@ -1,31 +0,0 @@
import { findBenefit, getUnlockedBenefits } from "@/utils/loyaltyTable"
import BenefitCard from "../BenefitCard"
import styles from "./benefitList.module.css"
import type { BenefitListProps } from "@/types/components/overviewTable"
export default function BenefitList({ levels }: BenefitListProps) {
return getUnlockedBenefits(levels).map((benefit) => {
const levelBenefits = levels.map((level) => {
return findBenefit(benefit, level)
})
return (
<div key={benefit.name} className={styles.benefitCardWrapper}>
<BenefitCard
title={benefit.name}
description={benefit.description}
comparedValues={levelBenefits.map((benefit, idx) => {
return {
key: `${benefit.name}-${idx}`,
value: benefit.value,
unlocked: benefit.unlocked,
valueDetails: benefit.valueDetails,
}
})}
/>
</div>
)
})
}

View File

@@ -1,26 +0,0 @@
import { Minus } from "react-feather"
import CheckCircle from "@/components/Icons/CheckCircle"
import styles from "./benefitValue.module.css"
import type { BenefitValueProps } from "@/types/components/overviewTable"
export default function BenefitValue({ benefit }: BenefitValueProps) {
if (!benefit.unlocked) {
return <Minus color="var(--UI-Grey-40)" />
}
if (!benefit.value) {
return <CheckCircle height={32} width={32} color="green" />
}
return (
<div className={styles.benefitValueContainer}>
<span className={styles.benefitValue}>{benefit.value}</span>
{benefit.valueDetails && (
<span className={styles.benefitValueDetails}>
{benefit.valueDetails}
</span>
)}
</div>
)
}

View File

@@ -3,23 +3,18 @@
import { useReducer } from "react"
import { useIntl } from "react-intl"
import { Lang } from "@/constants/languages"
import { membershipLevels } from "@/constants/membershipLevels"
import {
MembershipLevel,
MembershipLevelEnum,
} from "@/constants/membershipLevels"
import Image from "@/components/Image"
import MembershipLevelIcon from "@/components/Levels/Icon"
import Select from "@/components/TempDesignSystem/Select"
import useLang from "@/hooks/useLang"
import { getMembership } from "@/utils/user"
import DA from "./data/DA.json"
import DE from "./data/DE.json"
import EN from "./data/EN.json"
import FI from "./data/FI.json"
import NO from "./data/NO.json"
import SV from "./data/SV.json"
import BenefitList from "./BenefitList"
import LargeTable from "./LargeTable"
import LevelSummary from "./LevelSummary"
import { getInitialState, getLevel, reducer } from "./reducer"
import RewardList from "./RewardList"
import YourLevel from "./YourLevelScript"
import styles from "./overviewTable.module.css"
@@ -30,147 +25,54 @@ import {
ComparisonLevel,
DesktopSelectColumns,
type MobileColumnHeaderProps,
overviewTableActionsEnum,
OverviewTableActionsEnum,
type OverviewTableClientProps,
OverviewTableReducerAction,
} from "@/types/components/overviewTable"
import type { User } from "@/types/user"
const levelsTranslations = {
[Lang.en]: EN,
[Lang.sv]: SV,
[Lang.no]: NO,
[Lang.da]: DA,
[Lang.fi]: FI,
[Lang.de]: DE,
}
function getTranslatedLevel(membershipLevel: membershipLevels, lang: Lang) {
return levelsTranslations[lang].levels.find(
(level) => level.level === membershipLevel
) as ComparisonLevel
}
function getInitialState({ user, lang }: { user?: User; lang: Lang }) {
const membership = user?.memberships ? getMembership(user.memberships) : null
if (!membership?.membershipLevel) {
return {
selectedLevelAMobile: getTranslatedLevel(1, lang),
selectedLevelBMobile: getTranslatedLevel(2, lang),
selectedLevelADesktop: getTranslatedLevel(1, lang),
selectedLevelBDesktop: getTranslatedLevel(2, lang),
selectedLevelCDesktop: getTranslatedLevel(3, lang),
}
}
if (!membership.membershipLevel) return null
// @ts-expect-error: membership levels needs proper fix
const level = membershipLevels[membership.membershipLevel]
switch (level) {
case 6:
return {
selectedLevelAMobile: getTranslatedLevel(6, lang),
selectedLevelBMobile: getTranslatedLevel(7, lang),
selectedLevelADesktop: getTranslatedLevel(5, lang),
selectedLevelBDesktop: getTranslatedLevel(6, lang),
selectedLevelCDesktop: getTranslatedLevel(7, lang),
}
case 7:
return {
selectedLevelAMobile: getTranslatedLevel(6, lang),
selectedLevelBMobile: getTranslatedLevel(7, lang),
selectedLevelADesktop: getTranslatedLevel(6, lang),
selectedLevelBDesktop: getTranslatedLevel(7, lang),
selectedLevelCDesktop: getTranslatedLevel(1, lang),
}
default:
return {
selectedLevelAMobile: getTranslatedLevel(level, lang),
selectedLevelBMobile: getTranslatedLevel(level + 1, lang),
selectedLevelADesktop: getTranslatedLevel(level, lang),
selectedLevelBDesktop: getTranslatedLevel(level + 1, lang),
selectedLevelCDesktop: getTranslatedLevel(level + 2, lang),
}
}
}
function reducer(state: any, action: OverviewTableReducerAction) {
switch (action.type) {
case overviewTableActionsEnum.SET_SELECTED_LEVEL_A_MOBILE:
return {
...state,
selectedLevelAMobile: action.payload,
}
case overviewTableActionsEnum.SET_SELECTED_LEVEL_B_MOBILE:
return {
...state,
selectedLevelBMobile: action.payload,
}
case overviewTableActionsEnum.SET_SELECTED_LEVEL_A_DESKTOP:
return {
...state,
selectedLevelADesktop: action.payload,
}
case overviewTableActionsEnum.SET_SELECTED_LEVEL_B_DESKTOP:
return {
...state,
selectedLevelBDesktop: action.payload,
}
case overviewTableActionsEnum.SET_SELECTED_LEVEL_C_DESKTOP:
return {
...state,
selectedLevelCDesktop: action.payload,
}
default:
return state
}
function getLevelNamesForSelect(level: MembershipLevel, levelName: string) {
const levelToNumber = MembershipLevelEnum[level]
return [levelToNumber, levelName].join(" - ")
}
export default function OverviewTableClient({
activeMembership,
levels,
}: OverviewTableClientProps) {
const intl = useIntl()
const lang = useLang()
const levelsData = levelsTranslations[lang]
const [selectionState, dispatch] = useReducer(
reducer,
{ activeMembership, lang },
{ activeMembership, levels },
getInitialState
)
function handleSelectChange(actionType: overviewTableActionsEnum) {
function handleSelectChange(actionType: OverviewTableActionsEnum) {
return (key: Key) => {
if (typeof key === "number") {
dispatch({
payload: getTranslatedLevel(key, lang),
type: actionType,
})
}
dispatch({
payload: getLevel(key as MembershipLevel, levels),
type: actionType,
})
}
}
const levelOptions = levelsData.levels.map((level) => ({
label: [level.level, level.name].join(" "),
value: level.level,
const levelOptions = levels.map((level) => ({
label: getLevelNamesForSelect(level.level_id, level.name),
value: level.level_id,
}))
let activeMembershipLevel: membershipLevels | null = null
if (activeMembership?.membershipLevel) {
// @ts-expect-error: membershiplevel needs proper fix
activeMembershipLevel = membershipLevels[activeMembership?.membershipLevel]
}
const activeMembershipLevel = activeMembership ?? null
function MobileColumnHeader({ column }: MobileColumnHeaderProps) {
let selectedLevelMobile: ComparisonLevel
let actionEnumMobile: overviewTableActionsEnum
let actionEnumMobile: OverviewTableActionsEnum
switch (column) {
case "A":
selectedLevelMobile = selectionState.selectedLevelAMobile
actionEnumMobile = overviewTableActionsEnum.SET_SELECTED_LEVEL_A_MOBILE
actionEnumMobile = OverviewTableActionsEnum.SET_SELECTED_LEVEL_A_MOBILE
break
case "B":
selectedLevelMobile = selectionState.selectedLevelBMobile
actionEnumMobile = overviewTableActionsEnum.SET_SELECTED_LEVEL_B_MOBILE
actionEnumMobile = OverviewTableActionsEnum.SET_SELECTED_LEVEL_B_MOBILE
break
default:
return null
@@ -178,30 +80,30 @@ export default function OverviewTableClient({
return (
<div className={styles.columnHeader}>
<div className={styles.icon}>
{activeMembershipLevel === selectedLevelMobile.level ? (
{activeMembershipLevel === selectedLevelMobile.level_id ? (
<YourLevel />
) : null}
<Image
src={selectedLevelMobile.icon}
alt={selectedLevelMobile.name}
height={50}
width={100}
<MembershipLevelIcon
level={selectedLevelMobile.level_id}
color="red"
height="50"
width="100"
/>
</div>
<LevelSummary
level={
levelsData.levels.find(
(level) => level.level === selectedLevelMobile.level
) as ComparisonLevel
levels.find(
(level) => level.level_id === selectedLevelMobile.level_id
)!
}
showDescription={false}
/>
<Select
aria-label={intl.formatMessage({ id: "Level" })}
name={`benefit` + column}
name={`reward` + column}
label={intl.formatMessage({ id: "Level" })}
items={levelOptions}
value={selectedLevelMobile.level}
value={selectedLevelMobile.level_id}
onSelect={handleSelectChange(actionEnumMobile)}
/>
</div>
@@ -210,22 +112,22 @@ export default function OverviewTableClient({
function SelectDesktop({ column }: DesktopSelectColumns) {
let selectedLevelDesktop: ComparisonLevel
let actionEnumDesktop: overviewTableActionsEnum
let actionEnumDesktop: OverviewTableActionsEnum
switch (column) {
case "A":
selectedLevelDesktop = selectionState.selectedLevelADesktop
actionEnumDesktop =
overviewTableActionsEnum.SET_SELECTED_LEVEL_A_DESKTOP
OverviewTableActionsEnum.SET_SELECTED_LEVEL_A_DESKTOP
break
case "B":
selectedLevelDesktop = selectionState.selectedLevelBDesktop
actionEnumDesktop =
overviewTableActionsEnum.SET_SELECTED_LEVEL_B_DESKTOP
OverviewTableActionsEnum.SET_SELECTED_LEVEL_B_DESKTOP
break
case "C":
selectedLevelDesktop = selectionState.selectedLevelCDesktop
actionEnumDesktop =
overviewTableActionsEnum.SET_SELECTED_LEVEL_C_DESKTOP
OverviewTableActionsEnum.SET_SELECTED_LEVEL_C_DESKTOP
break
default:
return null
@@ -233,15 +135,14 @@ export default function OverviewTableClient({
return (
<Select
aria-label={intl.formatMessage({ id: "Level" })}
name={`benefit` + column}
name={`reward` + column}
label={intl.formatMessage({ id: "Level" })}
items={levelOptions}
value={selectedLevelDesktop.level}
value={selectedLevelDesktop.level_id}
onSelect={handleSelectChange(actionEnumDesktop)}
/>
)
}
return (
<div>
<div className={styles.mobileColumns}>
@@ -249,7 +150,7 @@ export default function OverviewTableClient({
<MobileColumnHeader column={"A"} />
<MobileColumnHeader column={"B"} />
</div>
<BenefitList
<RewardList
levels={[
selectionState.selectedLevelAMobile,
selectionState.selectedLevelBMobile,
@@ -268,11 +169,7 @@ export default function OverviewTableClient({
/>
</div>
<div className={styles.largeTableContainer}>
{/* Remove `as` once we have real data */}
<LargeTable
levels={levelsData.levels as ComparisonLevel[]}
activeLevel={activeMembershipLevel}
/>
<LargeTable levels={levels} activeLevel={activeMembershipLevel} />
</div>
</div>
)

View File

@@ -1,4 +1,4 @@
import Image from "@/components/Image"
import MembershipLevelIcon from "@/components/Levels/Icon"
import LevelSummary from "../../LevelSummary"
import YourLevel from "../../YourLevelScript"
@@ -21,13 +21,13 @@ export default function DesktopHeader({
<th className={styles.verticalTableHeader} />
{levels.map((level, idx) => {
return (
<th key={"image" + level.level + idx} className={styles.iconTh}>
{activeLevel === level.level ? <YourLevel /> : null}
<Image
height={50}
width={100}
alt={level.name}
src={level.icon}
<th key={"image" + level.level_id + idx} className={styles.iconTh}>
{activeLevel === level.level_id ? <YourLevel /> : null}
<MembershipLevelIcon
color="red"
level={level.level_id}
height="50"
width="100"
/>
</th>
)
@@ -38,7 +38,7 @@ export default function DesktopHeader({
{levels.map((level, idx) => {
return (
<th
key={"summary" + level.level + idx}
key={"summary" + level.level_id + idx}
className={styles.summaryTh}
>
<LevelSummary level={level} />

View File

@@ -1,16 +1,20 @@
import { ChevronDown } from "react-feather"
import Title from "@/components/TempDesignSystem/Text/Title"
import { findBenefit, getUnlockedBenefits } from "@/utils/loyaltyTable"
import {
findAvailableRewards,
getGroupedLabelAndDescription,
getGroupedRewards,
} from "@/utils/loyaltyTable"
import BenefitValue from "../BenefitValue"
import RewardValue from "../RewardValue"
import DesktopHeader from "./DesktopHeader"
import styles from "./largeTable.module.css"
import type {
BenefitTableHeaderProps,
LargeTableProps,
RewardTableHeaderProps,
} from "@/types/components/overviewTable"
export default function LargeTable({
@@ -18,6 +22,8 @@ export default function LargeTable({
activeLevel,
Select,
}: LargeTableProps) {
const keyedGroupedRewards = getGroupedRewards(levels)
return (
<table className={styles.table}>
<DesktopHeader
@@ -26,41 +32,45 @@ export default function LargeTable({
Select={Select}
/>
<tbody className={styles.tbody}>
{getUnlockedBenefits(levels).map((benefit) => {
return (
<tr key={benefit.name} className={styles.tr}>
<th scope={"row"} className={styles.benefitTh}>
<BenefitTableHeader
name={benefit.name}
description={benefit.description}
/>
</th>
{levels.map((level, idx) => {
return (
<td key={"icon" + level.level + idx} className={styles.td}>
<BenefitValue benefit={findBenefit(benefit, level)} />
</td>
)
})}
</tr>
)
})}
{Object.entries(keyedGroupedRewards).map(
([key, groupedRewards], idx) => {
const { label, description } =
getGroupedLabelAndDescription(groupedRewards)
return (
<tr key={key + idx} className={styles.tr}>
<th scope={"row"} className={styles.rewardTh}>
<RewardTableHeader name={label} description={description} />
</th>
{levels.map((level, idx) => {
const rewardIdsInGroup = groupedRewards.map(
(b) => b.reward_id
)
const reward = findAvailableRewards(rewardIdsInGroup, level)
return (
<td
key={`${reward?.reward_id}-${idx}`}
className={styles.td}
>
<RewardValue reward={reward} />
</td>
)
})}
</tr>
)
}
)}
</tbody>
</table>
)
}
function BenefitTableHeader({ name, description }: BenefitTableHeaderProps) {
function RewardTableHeader({ name, description }: RewardTableHeaderProps) {
return (
<details className={styles.details}>
<summary className={styles.summary}>
<hgroup className={styles.benefitHeader}>
<Title
as="h5"
level="h2"
textTransform={"regular"}
className={styles.benefitTitle}
>
<hgroup className={styles.rewardHeader}>
<Title as="h5" level="h2" textTransform={"regular"}>
{name}
</Title>
<span className={styles.chevron}>
@@ -69,7 +79,7 @@ function BenefitTableHeader({ name, description }: BenefitTableHeaderProps) {
</hgroup>
</summary>
<p
className={styles.benefitDescription}
className={styles.rewardDescription}
dangerouslySetInnerHTML={{ __html: description }}
/>
</details>

View File

@@ -19,7 +19,7 @@
text-align: center;
}
.benefitTh {
.rewardTh {
padding: var(--Spacing-x3) var(--Spacing-x2);
font-size: var(--typography-Caption-Regular-fontSize);
font-weight: var(--typography-Caption-Regular-fontWeight);
@@ -29,14 +29,14 @@
transform: rotate(180deg);
}
.benefitHeader {
.rewardHeader {
display: grid;
gap: var(--Spacing-x1);
grid-template-columns: 1fr auto;
text-align: start;
}
.benefitDescription {
.rewardDescription {
margin: 0;
padding-top: var(--Spacing-x1);
text-align: start;

View File

@@ -1,3 +1,7 @@
import { useIntl } from "react-intl"
import useLang from "@/hooks/useLang"
import styles from "./levelSummary.module.css"
import type { LevelSummaryProps } from "@/types/components/overviewTable"
@@ -6,9 +10,17 @@ export default function LevelSummary({
level,
showDescription = true,
}: LevelSummaryProps) {
const lang = useLang()
const intl = useIntl()
return (
<div className={styles.levelSummary}>
<span className={styles.levelRequirements}>{level.requirement}</span>
<span className={styles.levelRequirements}>
{level.required_points.toLocaleString(lang)}
{"p "}
{level.required_nights
? `${intl.formatMessage({ id: "or" })} ${level.required_nights} ${intl.formatMessage({ id: "nights" })}`
: ""}
</span>
{showDescription && (
<p className={styles.levelSummaryText}>{level.description}</p>
)}

View File

@@ -0,0 +1,48 @@
import { ChevronDown } from "react-feather"
import Title from "@/components/TempDesignSystem/Text/Title"
import RewardValue from "../../RewardValue"
import styles from "./rewardCard.module.css"
import type { RewardCardProps } from "@/types/components/overviewTable"
export default function RewardCard({
comparedValues,
title,
description,
}: RewardCardProps) {
return (
<div className={styles.rewardCard}>
<div className={styles.rewardInfo}>
<details className={styles.details}>
<summary className={styles.summary}>
<hgroup className={styles.rewardCardHeader}>
<Title as="h5" level="h2" textTransform={"regular"}>
{title}
</Title>
<span className={styles.chevron}>
<ChevronDown />
</span>
</hgroup>
</summary>
<p
className={styles.rewardCardDescription}
dangerouslySetInnerHTML={{ __html: description }}
/>
</details>
</div>
<div className={styles.rewardComparison}>
{comparedValues.map((reward, idx) => (
<div
key={`${reward?.reward_id}-${idx}`}
className={styles.comparisonItem}
>
<RewardValue reward={reward} />
</div>
))}
</div>
</div>
)
}

View File

@@ -1,25 +1,25 @@
.benefitCard {
.rewardCard {
padding-bottom: var(--Spacing-x-one-and-half);
z-index: 2;
grid-column: 1/3;
}
.benefitCardHeader {
.rewardCardHeader {
display: grid;
grid-template-columns: 1fr auto;
}
.benefitCardDescription {
.rewardCardDescription {
font-size: var(--typography-Caption-Regular-fontSize);
line-height: 150%;
padding-right: var(--Spacing-x4);
}
.benefitInfo {
.rewardInfo {
padding-bottom: var(--Spacing-x-one-and-half);
}
.benefitComparison {
.rewardComparison {
display: grid;
grid-template-columns: 1fr 1fr;
}
@@ -50,7 +50,7 @@
}
@media screen and (min-width: 950px) {
.benefitComparison {
.rewardComparison {
grid-template-columns: 1fr 1fr 1fr;
}
}

View File

@@ -0,0 +1,38 @@
import {
findAvailableRewards,
getGroupedLabelAndDescription,
getGroupedRewards,
} from "@/utils/loyaltyTable"
import RewardCard from "./Card"
import styles from "./rewardList.module.css"
import type { RewardListProps } from "@/types/components/overviewTable"
export default function RewardList({ levels }: RewardListProps) {
const keyedGroupedRewards = getGroupedRewards(levels)
return Object.values(keyedGroupedRewards).map((groupedRewards) => {
const rewardIdsInGroup = groupedRewards.map((b) => b.reward_id)
const { label, description } = getGroupedLabelAndDescription(groupedRewards)
const levelRewards = levels.map((level) => {
return findAvailableRewards(rewardIdsInGroup, level)
})
return (
<div
key={levelRewards[0]?.reward_id ?? ""}
className={styles.rewardCardWrapper}
>
<RewardCard
title={label}
description={description}
comparedValues={levelRewards}
/>
</div>
)
})
}

View File

@@ -1,4 +1,4 @@
.benefitCardWrapper {
.rewardCardWrapper {
border-bottom: 1px solid var(--Base-Border-Subtle);
position: relative;
display: grid;
@@ -8,12 +8,12 @@
margin: var(--Spacing-x1) var(--Spacing-x2);
}
.benefitCardWrapper:last-child {
.rewardCardWrapper:last-child {
border: none;
}
@media screen and (min-width: 950px) {
.benefitCardWrapper {
.rewardCardWrapper {
grid-column: 1/4;
}
}

View File

@@ -0,0 +1,21 @@
import { Minus } from "react-feather"
import CheckCircle from "@/components/Icons/CheckCircle"
import styles from "./rewardValue.module.css"
import type { RewardValueProps } from "@/types/components/overviewTable"
export default function RewardValue({ reward }: RewardValueProps) {
if (!reward) {
return <Minus color="var(--UI-Grey-40)" />
}
if (!reward.value) {
return <CheckCircle height={32} width={32} color="green" />
}
return (
<div className={styles.rewardValueContainer}>
<span className={styles.rewardValue}>{reward.value}</span>
</div>
)
}

View File

@@ -1,4 +1,4 @@
.benefitValueContainer {
.rewardValueContainer {
display: flex;
flex-direction: column;
align-items: center;
@@ -7,12 +7,12 @@
text-wrap: balance;
}
.benefitValue {
.rewardValue {
font-size: var(--typography-Body-Bold-fontSize);
font-weight: var(--typography-Body-Bold-fontWeight);
}
.benefitValueDetails {
.rewardValueDetails {
font-size: var(--typography-Footnote-Regular-fontSize);
text-align: center;
color: var(--UI-Grey-80);

View File

@@ -1,538 +0,0 @@
{
"levels": [
{
"level": 1,
"name": "New Friend",
"requirement": "0p",
"description": "Det her er starten på noget smukt. Som New Friend kan du godt glæde dig til at opdage alt det skønne ved Scandic.",
"icon": "/_static/icons/loyaltylevels/new-friend.svg",
"benefits": [
{
"name": "Venlige priser",
"description": "Som vores ven får du altid de bedste tilbud, uanset tid og sted. Her er der ingen hemmelige aftaler eller bookingkoder du skal bare booke som normalt.",
"unlocked": true
},
{
"name": "Rabat på mad",
"description": "Mums! I weekenderne får du en lækker rabat på 10% i vores restaurant og hotelshop. Tilbuddet gælder, uanset om du overnatter eller bare kigger forbi for at få en bid mad. Så kom i gang med at forkæle dig selv og bestille ekstra roomservice.",
"unlocked": true,
"value": "10%"
},
{
"name": "Gratis mocktail til børn under opholdet",
"description": "Vi vil så gerne have, at børnene føler sig som de VIPs, de er, så vi giver en forfriskende mocktail under hvert ophold.",
"unlocked": true
},
{
"name": "Sen check ud, når tilgængeligt",
"description": "Vi ved godt, at muligheden for at checke sent ud virkelig kan redde turen, og nu skal du ikke længere skynde dig ud af sengen. Check ud en time senere uden ekstra omkostninger, og nyd at kunne sove lidt længere.",
"unlocked": false
},
{
"name": "Restaurantvoucher",
"description": "",
"unlocked": false
},
{
"name": "Venskabsboost",
"description": "",
"unlocked": false
},
{
"name": "Tidlig check ind, når tilgængeligt",
"description": "Vil du tage hul på opholdet lidt hurtigere? Intet problem. Check ind en time før uden ekstra omkostninger, og nyd at kunne slappe en smule mere af.",
"unlocked": false
},
{
"name": "Gratis opgraderinger, når tilgængelige",
"description": "På det her venskabsniveau opgraderer vi dit værelse, hvis det er muligt, så du får et endnu mere behageligt ophold.",
"unlocked": false
},
{
"name": "2-for-1 morgenmad",
"description": "Uanset om du overnatter hos os eller ej, kan du invitere en ven på morgenmad, for I kan spise for to personer og kun betale for én! Men husk, at tjekke detaljerne først, så alt er klar til et hyggeligt morgenmøde.",
"unlocked": false
},
{
"name": "48-timers værelsesgaranti",
"description": "Kun få af jer får denne særlige fordel! Selv hvis vi er fuldt bookede, er du garanteret et værelse, så længe du booker mindst 48 timer i forvejen.",
"unlocked": false
},
{
"name": "Gratis morgenmad altid",
"description": "Klar til noget lækkert om morgenen? Nu kan du starte dagen med gratis morgenmad og ved du hvad? Det gælder uanset om du overnatter hos os eller ej.",
"unlocked": false
},
{
"name": "En fantastisk gave hvert år",
"description": "Som vores Best Friend fortjener du virkelig en særlig behandling, og derfor har vi en eksklusiv og ret fantastisk gave klar til dig en gang om året. Hvad det er? Det er jo en overraskelse!",
"unlocked": false
},
{
"name": "Børneboost",
"description": "Dit barn er også vores ven, og det betyder en særlig boostgave, når I overnatter hos os. Hvorfor? Fordi børn er de bedste!",
"unlocked": false
}
]
},
{
"level": 2,
"name": "Good Friend",
"requirement": "5 000p",
"description": "Du har været her meget på det sidste! Lad os tage venskabet videre ét behageligt ophold ad gangen.",
"icon": "/_static/icons/loyaltylevels/good-friend.svg",
"benefits": [
{
"name": "Venlige priser",
"description": "Som vores ven får du altid de bedste tilbud, uanset tid og sted. Her er der ingen hemmelige aftaler eller bookingkoder du skal bare booke som normalt.",
"unlocked": true
},
{
"name": "Rabat på mad",
"description": "Hvad er mere lækkert end en rabat? Som vores ven får du i weekenderne og på udvalgte helligdage 10-15% rabat i vores restaurant og på maden i vores hotel shop og det er uanset om du overnatter hos os eller ej. Så kom i gang med at forkæle dig selv: Bestil noget roomservice!",
"unlocked": true,
"value": "15%"
},
{
"name": "Gratis mocktail til børn under opholdet",
"description": "Vi vil så gerne have, at børnenes føler sig som de VIPs, de er, så vi giver en forfriskende mocktail under hvert ophold.",
"unlocked": true
},
{
"name": "Sen check ud, når tilgængeligt",
"description": "Vi ved godt, at muligheden for at checke sent ud virkelig kan redde turen, og nu skal du ikke længere skynde dig ud af sengen. Check ud en time senere uden ekstra omkostninger, og nyd at kunne sove lidt længere.",
"unlocked": false
},
{
"name": "Restaurantvoucher",
"description": "",
"unlocked": false
},
{
"name": "Venskabsboost",
"description": "",
"unlocked": false
},
{
"name": "Tidlig check ind, når tilgængeligt",
"description": "Vil du tage hul på opholdet lidt hurtigere? Intet problem. Check ind en time før uden ekstra omkostninger, og nyd at kunne slappe en smule mere af.",
"unlocked": false
},
{
"name": "Gratis opgraderinger, når tilgængelige",
"description": "På det her venskabsniveau opgraderer vi dit værelse, hvis det er muligt, så du får et endnu mere behageligt ophold.",
"unlocked": false
},
{
"name": "2-for-1 morgenmad",
"description": "Uanset om du overnatter hos os eller ej, kan du invitere en ven på morgenmad, for I kan spise for to personer og kun betale for én! Men husk, at tjekke detaljerne først, så alt er klar til et hyggeligt morgenmøde.",
"unlocked": false
},
{
"name": "48-timers værelsesgaranti",
"description": "Kun få af jer får denne særlige fordel! Selv hvis vi er fuldt bookede, er du garanteret et værelse, så længe du booker mindst 48 timer i forvejen.",
"unlocked": false
},
{
"name": "Gratis morgenmad altid",
"description": "Klar til noget lækkert om morgenen? Nu kan du starte dagen med gratis morgenmad og ved du hvad? Det gælder uanset om du overnatter hos os eller ej.",
"unlocked": false
},
{
"name": "En fantastisk gave hvert år",
"description": "Som vores Best Friend fortjener du virkelig en særlig behandling, og derfor har vi en eksklusiv og ret fantastisk gave klar til dig en gang om året. Hvad det er? Det er jo en overraskelse!",
"unlocked": false
},
{
"name": "Børneboost",
"description": "Dit barn er også vores ven, og det betyder en særlig boostgave, når I overnatter hos os. Hvorfor? Fordi børn er de bedste!",
"unlocked": false
}
]
},
{
"level": 3,
"name": "Close Friend",
"requirement": "10 000p",
"description": "Vi har lært hinanden bedre at kende, og det gør din oplevelse hos Scandic meget mere personlig.",
"icon": "/_static/icons/loyaltylevels/close-friend.svg",
"benefits": [
{
"name": "Venlige priser",
"description": "Som vores ven får du altid de bedste tilbud, uanset tid og sted. Her er der ingen hemmelige aftaler eller bookingkoder du skal bare booke som normalt.",
"unlocked": true
},
{
"name": "Rabat på mad",
"description": "Hvad er mere lækkert end en rabat? Som vores ven får du i weekenderne og på udvalgte helligdage 10-15% rabat i vores restaurant og på maden i vores hotel shop og det er uanset om du overnatter hos os eller ej. Så kom i gang med at forkæle dig selv: Bestil noget roomservice!",
"unlocked": true,
"value": "15%"
},
{
"name": "Gratis mocktail til børn under opholdet",
"description": "Vi vil så gerne have, at børnenes føler sig som de VIPs, de er, så vi giver en forfriskende mocktail under hvert ophold.",
"unlocked": true
},
{
"name": "Sen check ud, når tilgængeligt",
"description": "Vi ved godt, at muligheden for at checke sent ud virkelig kan redde turen, og nu skal du ikke længere skynde dig ud af sengen. Check ud en time senere uden ekstra omkostninger, og nyd at kunne sove lidt længere.",
"unlocked": true
},
{
"name": "Restaurantvoucher",
"description": "For hver pointgivende overnatning hos os giver vi dig en restaurantvoucher på DKK 50,-.",
"unlocked": true,
"value": "DKK 50,-"
},
{
"name": "Venskabsboost",
"description": "",
"unlocked": false
},
{
"name": "Tidlig check ind, når tilgængeligt",
"description": "Vil du tage hul på opholdet lidt hurtigere? Intet problem. Check ind en time før uden ekstra omkostninger, og nyd at kunne slappe en smule mere af.",
"unlocked": false
},
{
"name": "Gratis opgraderinger, når tilgængelige",
"description": "På det her venskabsniveau opgraderer vi dit værelse, hvis det er muligt, så du får et endnu mere behageligt ophold.",
"unlocked": false
},
{
"name": "2-for-1 morgenmad",
"description": "Uanset om du overnatter hos os eller ej, kan du invitere en ven på morgenmad, for I kan spise for to personer og kun betale for én! Men husk, at tjekke detaljerne først, så alt er klar til et hyggeligt morgenmøde.",
"unlocked": false
},
{
"name": "48-timers værelsesgaranti",
"description": "Kun få af jer får denne særlige fordel! Selv hvis vi er fuldt bookede, er du garanteret et værelse, så længe du booker mindst 48 timer i forvejen.",
"unlocked": false
},
{
"name": "Gratis morgenmad altid",
"description": "Klar til noget lækkert om morgenen? Nu kan du starte dagen med gratis morgenmad og ved du hvad? Det gælder uanset om du overnatter hos os eller ej.",
"unlocked": false
},
{
"name": "En fantastisk gave hvert år",
"description": "Som vores Best Friend fortjener du virkelig en særlig behandling, og derfor har vi en eksklusiv og ret fantastisk gave klar til dig en gang om året. Hvad det er? Det er jo en overraskelse!",
"unlocked": false
},
{
"name": "Børneboost",
"description": "Dit barn er også vores ven, og det betyder en særlig boostgave, når I overnatter hos os. Hvorfor? Fordi børn er de bedste!",
"unlocked": false
}
]
},
{
"level": 4,
"name": "Dear Friend",
"requirement": "25 000p",
"description": "Her er vist grobund for et livslangt venskab, og det betyder, at du får adgang til meget mere med Scandic.",
"icon": "/_static/icons/loyaltylevels/dear-friend.svg",
"benefits": [
{
"name": "Venlige priser",
"description": "Som vores ven får du altid de bedste tilbud, uanset tid og sted. Her er der ingen hemmelige aftaler eller bookingkoder du skal bare booke som normalt.",
"unlocked": true
},
{
"name": "Rabat på mad",
"description": "Hvad er mere lækkert end en rabat? Som vores ven får du i weekenderne og på udvalgte helligdage 10-15% rabat i vores restaurant og på maden i vores hotelshop og det er uanset om du overnatter hos os eller ej. Så kom i gang med at forkæle dig selv: Bestil noget roomservice!",
"unlocked": true,
"value": "15%"
},
{
"name": "Gratis mocktail til børn under opholdet",
"description": "Vi vil så gerne have, at børnenes føler sig som de VIPs, de er, så vi giver en forfriskende mocktail under hvert ophold.",
"unlocked": true
},
{
"name": "Sen check ud, når tilgængeligt",
"description": "Vi ved godt, at muligheden for at checke sent ud virkelig kan redde turen, og nu skal du ikke længere skynde dig ud af sengen. Check ud en time senere uden ekstra omkostninger, og nyd at kunne sove lidt længere.",
"unlocked": true
},
{
"name": "Restaurantvoucher",
"description": "Vores venskab blev lige en smule bedre! Få en restaurantvoucher på DKK 75,- for hver pointgivende overnatning.",
"unlocked": true,
"value": "DKK 75,-"
},
{
"name": "Venskabsboost",
"description": "Hver gang du booster dine venskabspoint, får du 25% ekstra! Så kom i gang med at optjene point på ophold, måltider m.m., og pludselig har du en gratis overnatning.",
"unlocked": true,
"value": "25%"
},
{
"name": "Tidlig check ind, når tilgængeligt",
"description": "Vil du tage hul på opholdet lidt hurtigere? Intet problem. Check ind en time før uden ekstra omkostninger, og nyd at kunne slappe en smule mere af.",
"unlocked": true
},
{
"name": "Gratis opgraderinger, når tilgængelige",
"description": "På det her venskabsniveau opgraderer vi dit værelse, hvis det er muligt, så du får et endnu mere behageligt ophold.",
"unlocked": false
},
{
"name": "2-for-1 morgenmad",
"description": "Uanset om du overnatter hos os eller ej, kan du invitere en ven på morgenmad, for I kan spise for to personer og kun betale for én! Men husk, at tjekke detaljerne først, så alt er klar til et hyggeligt morgenmøde.",
"unlocked": false
},
{
"name": "48-timers værelsesgaranti",
"description": "Kun få af jer får denne særlige fordel! Selv hvis vi er fuldt bookede, er du garanteret et værelse, så længe du booker mindst 48 timer i forvejen.",
"unlocked": false
},
{
"name": "Gratis morgenmad altid",
"description": "Klar til noget lækkert om morgenen? Nu kan du starte dagen med gratis morgenmad og ved du hvad? Det gælder uanset om du overnatter hos os eller ej.",
"unlocked": false
},
{
"name": "En fantastisk gave hvert år",
"description": "Som vores Best Friend fortjener du virkelig en særlig behandling, og derfor har vi en eksklusiv og ret fantastisk gave klar til dig en gang om året. Hvad det er? Det er jo en overraskelse!",
"unlocked": false
},
{
"name": "Børneboost",
"description": "Dit barn er også vores ven, og det betyder en særlig boostgave, når I overnatter hos os. Hvorfor? Fordi børn er de bedste!",
"unlocked": false
}
]
},
{
"level": 5,
"name": "Loyal Friend",
"requirement": "100 000p",
"description": "Du har været hos os på mange ophold, så derfor vil vi gerne give dig nogle af vores bedste fordele.",
"icon": "/_static/icons/loyaltylevels/loyal-friend.svg",
"benefits": [
{
"name": "Venlige priser",
"description": "Som vores ven får du altid de bedste tilbud, uanset tid og sted. Her er der ingen hemmelige aftaler eller bookingkoder du skal bare booke som normalt.",
"unlocked": true
},
{
"name": "Rabat på mad",
"description": "Hvad er mere lækkert end en rabat? Som vores ven får du i weekenderne og på udvalgte helligdage 10-15% rabat i vores restaurant og på maden i vores hotel shop og det er uanset om du overnatter hos os eller ej. Så kom i gang med at forkæle dig selv: Bestil noget roomservice!",
"unlocked": true,
"value": "15%"
},
{
"name": "Gratis mocktail til børn under opholdet",
"description": "Vi vil så gerne have, at børnenes føler sig som de VIPs, de er, så vi giver en forfriskende mocktail under hvert ophold.",
"unlocked": true
},
{
"name": "Sen check ud, når tilgængeligt",
"description": "Vi ved godt, at muligheden for at checke sent ud virkelig kan redde turen, og nu skal du ikke længere skynde dig ud af sengen. Check ud en time senere uden ekstra omkostninger, og nyd at kunne sove lidt længere.",
"unlocked": true
},
{
"name": "Restaurantvoucher",
"description": "Vi giver dig en restaurantvoucher på DKK 100,- for hver pointgivende overnatning. Det kan blive til mange croissanter!",
"unlocked": true,
"value": "DKK 100,-"
},
{
"name": "Venskabsboost",
"description": "Hver gang du booster dine venskabspoint, får du 25% ekstra! Så kom i gang med at optjene point på ophold, måltider m.m., og pludselig har du en gratis overnatning.",
"unlocked": true,
"value": "25%"
},
{
"name": "Tidlig check ind, når tilgængeligt",
"description": "Vil du tage hul på opholdet lidt hurtigere? Intet problem. Check ind en time før uden ekstra omkostninger, og nyd at kunne slappe en smule mere af.",
"unlocked": true
},
{
"name": "Gratis opgraderinger, når tilgængelige",
"description": "På det her venskabsniveau opgraderer vi dit værelse, hvis det er muligt, så du får et endnu mere behageligt ophold.",
"unlocked": true
},
{
"name": "2-for-1 morgenmad",
"description": "Uanset om du overnatter hos os eller ej, kan du invitere en ven på morgenmad, for I kan spise for to personer og kun betale for én! Men husk, at tjekke detaljerne først, så alt er klar til et hyggeligt morgenmøde.",
"unlocked": true
},
{
"name": "48-timers værelsesgaranti",
"description": "Kun få af jer får denne særlige fordel! Selv hvis vi er fuldt bookede, er du garanteret et værelse, så længe du booker mindst 48 timer i forvejen.",
"unlocked": false
},
{
"name": "Gratis morgenmad altid",
"description": "Klar til noget lækkert om morgenen? Nu kan du starte dagen med gratis morgenmad og ved du hvad? Det gælder uanset om du overnatter hos os eller ej.",
"unlocked": false
},
{
"name": "En fantastisk gave hvert år",
"description": "Som vores Best Friend fortjener du virkelig en særlig behandling, og derfor har vi en eksklusiv og ret fantastisk gave klar til dig en gang om året. Hvad det er? Det er jo en overraskelse!",
"unlocked": false
},
{
"name": "Børneboost",
"description": "Dit barn er også vores ven, og det betyder en særlig boostgave, når I overnatter hos os. Hvorfor? Fordi børn er de bedste!",
"unlocked": false
}
]
},
{
"level": 6,
"name": "True Friend",
"requirement": "250 000p",
"description": "Vi er glade for, at du besøger os, uanset om det er høj- eller lavsæson. Derfor får du endnu flere skræddersyede fordele.",
"icon": "/_static/icons/loyaltylevels/true-friend.svg",
"benefits": [
{
"name": "Venlige priser",
"description": "Som vores ven får du altid de bedste tilbud, uanset tid og sted. Her er der ingen hemmelige aftaler eller bookingkoder du skal bare booke som normalt.",
"unlocked": true
},
{
"name": "Rabat på mad",
"description": "Hvad er mere lækkert end en rabat? Som vores ven får du i weekenderne og på udvalgte helligdage 10-15% rabat i vores restaurant og på maden i vores hotel shop og det er uanset om du overnatter hos os eller ej. Så kom i gang med at forkæle dig selv: Bestil noget roomservice!",
"unlocked": true,
"value": "15%"
},
{
"name": "Gratis mocktail til børn under opholdet",
"description": "Vi vil så gerne have, at børnenes føler sig som de VIPs, de er, så vi giver en forfriskende mocktail under hvert ophold.",
"unlocked": true
},
{
"name": "Sen check ud, når tilgængeligt",
"description": "Vi ved godt, at muligheden for at checke sent ud virkelig kan redde turen, og nu skal du ikke længere skynde dig ud af sengen. Check ud en time senere uden ekstra omkostninger, og nyd at kunne sove lidt længere.",
"unlocked": true
},
{
"name": "Restaurantvoucher",
"description": "Det går ret godt, så nu giver hver pointgivende overnatning en restaurantvoucher. Den er helt sikkert god at have ved hånden, næste gang du vil spise en god middag!",
"unlocked": true,
"value": "DKK 150,-"
},
{
"name": "Venskabsboost",
"description": "Du kan godt glæde dig. Hver gang du booster dine venskabspoint, får du 25-50% ekstra! Så kom i gang med at optjene point på ophold, måltider m.m., og pludselig har du en gratis overnatning.",
"unlocked": true,
"value": "50%"
},
{
"name": "Tidlig check ind, når tilgængeligt",
"description": "Vil du tage hul på opholdet lidt hurtigere? Intet problem. Check ind en time før uden ekstra omkostninger, og nyd at kunne slappe en smule mere af.",
"unlocked": true
},
{
"name": "Gratis opgraderinger, når tilgængelige",
"description": "På det her venskabsniveau opgraderer vi dit værelse, hvis det er muligt, så du får et endnu mere behageligt ophold.",
"unlocked": true
},
{
"name": "2-for-1 morgenmad",
"description": "Uanset om du overnatter hos os eller ej, kan du invitere en ven på morgenmad, for I kan spise for to personer og kun betale for én! Men husk, at tjekke detaljerne først, så alt er klar til et hyggeligt morgenmøde.",
"unlocked": false
},
{
"name": "48-timers værelsesgaranti",
"description": "Kun få af jer får denne særlige fordel! Selv hvis vi er fuldt bookede, er du garanteret et værelse, så længe du booker mindst 48 timer i forvejen.",
"unlocked": true
},
{
"name": "Gratis morgenmad altid",
"description": "Klar til noget lækkert om morgenen? Nu kan du starte dagen med gratis morgenmad og ved du hvad? Det gælder uanset om du overnatter hos os eller ej.",
"unlocked": true
},
{
"name": "En fantastisk gave hvert år",
"description": "Som vores Best Friend fortjener du virkelig en særlig behandling, og derfor har vi en eksklusiv og ret fantastisk gave klar til dig en gang om året. Hvad det er? Det er jo en overraskelse!",
"unlocked": false
},
{
"name": "Børneboost",
"description": "Dit barn er også vores ven, og det betyder en særlig boostgave, når I overnatter hos os. Hvorfor? Fordi børn er de bedste!",
"unlocked": false
}
]
},
{
"level": 7,
"name": "Best Friend",
"requirement": "400 000p eller 100 nætter",
"description": "Det bliver simpelthen ikke bedre end det her, når det kommer til de helt eksklusive oplevelser!",
"icon": "/_static/icons/loyaltylevels/best-friend.svg",
"benefits": [
{
"name": "Venlige priser",
"description": "Som vores ven får du altid de bedste tilbud, uanset tid og sted. Her er der ingen hemmelige aftaler eller bookingkoder du skal bare booke som normalt.",
"unlocked": true
},
{
"name": "Rabat på mad",
"description": "Hvad er mere lækkert end en rabat? Som vores ven får du i weekenderne og på udvalgte helligdage 10-15% rabat i vores restaurant og på maden i vores hotel shop og det er uanset om du overnatter hos os eller ej. Så kom i gang med at forkæle dig selv: Bestil noget roomservice!",
"unlocked": true,
"value": "15%"
},
{
"name": "Gratis mocktail til børn under opholdet",
"description": "Vi vil så gerne have, at børnenes føler sig som de VIPs, de er, så vi giver en forfriskende mocktail under hvert ophold.",
"unlocked": true
},
{
"name": "Sen check ud, når tilgængeligt",
"description": "Vi ved godt, at muligheden for at checke sent ud virkelig kan redde turen, og nu skal du ikke længere skynde dig ud af sengen. Check ud en time senere uden ekstra omkostninger, og nyd at kunne sove lidt længere.",
"unlocked": true
},
{
"name": "Restaurantvoucher",
"description": "Nu giver hver pointgivende overnatning en restaurantvoucher. Forestil dig lige, hvor mange croissanter du kan få!",
"unlocked": true,
"value": "DKK 200,-"
},
{
"name": "Venskabsboost",
"description": "Du kan godt glæde dig. Hver gang du booster dine venskabspoint, får du 25-50% ekstra! Så kom i gang med at optjene point på ophold, måltider m.m., og pludselig har du en gratis overnatning.",
"unlocked": true,
"value": "50%"
},
{
"name": "Tidlig check ind, når tilgængeligt",
"description": "Vil du tage hul på opholdet lidt hurtigere? Intet problem. Check ind en time før uden ekstra omkostninger, og nyd at kunne slappe en smule mere af.",
"unlocked": true
},
{
"name": "Gratis opgraderinger, når tilgængelige",
"description": "På det her venskabsniveau opgraderer vi dit værelse, hvis det er muligt, så du får et endnu mere behageligt ophold.",
"unlocked": true
},
{
"name": "2-for-1 morgenmad",
"description": "Uanset om du overnatter hos os eller ej, kan du invitere en ven på morgenmad, for I kan spise for to personer og kun betale for én! Men husk, at tjekke detaljerne først, så alt er klar til et hyggeligt morgenmøde.",
"unlocked": false
},
{
"name": "48-timers værelsesgaranti",
"description": "Kun få af jer får denne særlige fordel! Selv hvis vi er fuldt bookede, er du garanteret et værelse, så længe du booker mindst 48 timer i forvejen.",
"unlocked": true
},
{
"name": "Gratis morgenmad altid",
"description": "Klar til noget lækkert om morgenen? Nu kan du starte dagen med gratis morgenmad og ved du hvad? Det gælder uanset om du overnatter hos os eller ej.",
"unlocked": true
},
{
"name": "En fantastisk gave hvert år",
"description": "Som vores Best Friend fortjener du virkelig en særlig behandling, og derfor har vi en eksklusiv og ret fantastisk gave klar til dig en gang om året. Hvad det er? Det er jo en overraskelse!",
"unlocked": true
},
{
"name": "Børneboost",
"description": "Dit barn er også vores ven, og det betyder en særlig boostgave, når I overnatter hos os. Hvorfor? Fordi børn er de bedste!",
"unlocked": true
}
]
}
]
}

View File

@@ -1,538 +0,0 @@
{
"levels": [
{
"level": 1,
"name": "New Friend",
"requirement": "0 Punkte",
"description": "Dies ist der Beginn von etwas Wunderbarem: Als New Friend können Sie sich auf eine Reise voller herrlicher Scandic-Entdeckungen freuen.",
"icon": "/_static/icons/loyaltylevels/new-friend.svg",
"benefits": [
{
"name": "Freundschaftspreise",
"description": "Als unser Freund erhalten Sie immer das beste Angebot: an jedem Tag und an jedem Ort. Und dafür ist weder ein Handschlag noch ein Buchungscode erforderlich stürzen Sie sich einfach ins Buchungserlebnis und überzeugen Sie sich selbst.",
"unlocked": true
},
{
"name": "Rabatt auf Speisen",
"description": "Mmmh, lecker! Genießen Sie an den Wochenenden einen verlockenden Preisnachlass von 10% in unserem Restaurant und Hotelshop. Dieses Angebot gilt unabhängig davon, ob Sie bei uns übernachten oder nur auf einen Happen zu essen bei uns vorbeischauen. Worauf warten Sie also noch? Gönnen Sie sich etwas und bestellen Sie den zusätzlichen Zimmerservice.",
"unlocked": true,
"value": "10%"
},
{
"name": "Kostenloser Kinder-Mocktail während des Aufenthalts",
"description": "Oh, Sie haben ja die Kinder dabei! Wie schön hallo junger Freund! Wir möchten, dass sich Kinder bei uns wie VIPs fühlen, und deshalb bekommen sie von uns bei jedem Aufenthalt einen erfrischenden Mocktail spendiert.",
"unlocked": true
},
{
"name": "Später Check-out, wenn verfügbar",
"description": "Wir wissen, dass ein später Check-out manchmal eine große Erleichterung sein kann. Und an diesem Punkt unserer Freundschaft gibt es keinen Grund mehr, sich aus dem Bett zu quälen: Wir kommen Ihnen gerne entgegen. Checken Sie kostenlos 1Stunde später aus und schlafen Sie einfach etwas länger.",
"unlocked": false
},
{
"name": "Restaurantgutschein",
"description": "",
"unlocked": false
},
{
"name": "Freundschaftsbonus",
"description": "",
"unlocked": false
},
{
"name": "Früher Check-in, wenn verfügbar",
"description": "Sie treffen schon früher bei uns ein? Kein Problem! Checken Sie einfach kostenlos eine Stunde früher ein so können Sie sich gleich ein wenig ausruhen und haben noch mehr von Ihrem Aufenthalt.",
"unlocked": false
},
{
"name": "Kostenlose Upgrades wenn verfügbar",
"description": "Um auf den Punkt zu kommen wir setzen noch einen drauf. Auf dieser Stufe unserer Freundschaft bieten wir Ihnen, wann immer es möglich ist, ein Zimmer-Upgrade an so wird Ihr Aufenthalt noch komfortabler. Wenn das nicht großartig klingt…",
"unlocked": false
},
{
"name": "Frühstück für zwei zum Preis von einem",
"description": "Wenn das kein delikater Deal ist: Egal, ob Sie bei uns übernachten oder nicht, schnappen Sie sich einen Frühstückspartner, denn jetzt können Sie zu zweit zum Preis von einem bei uns essen! Informieren Sie sich einfach vorher über alle Details und genießen Sie ein gemütliches Frühstück zu zweit.",
"unlocked": false
},
{
"name": "48-Stunden-Zimmergarantie",
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
"unlocked": false
},
{
"name": "Ein kostenloses Frühstück jederzeit",
"description": "Lust auf einen leckeren Start in den Tag? Dann schauen Sie doch einfach bei uns vorbei! Denn jetzt können Sie Ihren Tag mit einem kostenlosen Frühstück beginnen und zwar ganz unabhängig davon, ob Sie bei uns übernachten oder nicht.",
"unlocked": false
},
{
"name": "Ein exklusives Geschenk pro Jahr",
"description": "Als Best Friend haben Sie natürlich eine königliche Behandlung verdient deshalb haben wir einmal im Jahr ein exklusives und ziemlich großartiges Geschenk für Sie. Neugierig? Tja, das bleibt leider eine Überraschung. Es wird noch nichts verraten!",
"unlocked": false
},
{
"name": "Ein Geschenk für Kinder",
"description": "An diesem Punkt unserer Freundschaft betrachten wir natürlich auch Ihr Kind als Freund was bedeutet, dass es bei jeder Übernachtung ein ganz spezielles Geschenk von uns erhält. Und warum? Weil Kinder einfach cool sind! Sie haben eine VIP-Behandlung verdient.",
"unlocked": false
}
]
},
{
"level": 2,
"name": "Good Friend",
"requirement": "5 000 Punkte",
"description": "Sie waren in letzter Zeit viel bei uns! Und ehrlich gesagt haben wir das Gefühl, dass wir auf einer Wellenlänge sind die vielen angenehmen Aufenthalte und lustigen Überraschungen sprechen für sich.",
"icon": "/_static/icons/loyaltylevels/good-friend.svg",
"benefits": [
{
"name": "Freundschaftspreise",
"description": "Als unser Freund erhalten Sie immer das beste Angebot: an jedem Tag und an jedem Ort. Und dafür ist weder ein Handschlag noch ein Buchungscode erforderlich stürzen Sie sich einfach ins Buchungserlebnis und überzeugen Sie sich selbst.",
"unlocked": true
},
{
"name": "Rabatt auf Speisen",
"description": "Was gibt es Schöneres als einen Preisnachlass? Als Freund erhalten Sie an Wochenenden und ausgewählten Feiertagen 10-15% Rabatt auf die Speisen in unseren Restaurants und Hotelshops und zwar unabhängig davon, ob Sie bei uns übernachten oder nicht. Also kommen Sie vorbei und tun Sie sich etwas Gutes! Zum Beispiel mit unserem zusätzlichen Zimmerservice.",
"unlocked": true,
"value": "15%"
},
{
"name": "Kostenloser Kinder-Mocktail während des Aufenthalts",
"description": "Oh, Sie haben ja die Kinder dabei! Wie schön hallo junger Freund! Wir möchten, dass sich Kinder bei uns wie VIPs fühlen, und deshalb bekommen sie von uns bei jedem Aufenthalt einen erfrischenden Mocktail spendiert.",
"unlocked": true
},
{
"name": "Später Check-out, wenn verfügbar",
"description": "Wir wissen, dass ein später Check-out manchmal eine große Erleichterung sein kann. Und an diesem Punkt unserer Freundschaft gibt es keinen Grund mehr, sich aus dem Bett zu quälen: Wir kommen Ihnen gerne entgegen. Checken Sie kostenlos 1Stunde später aus und schlafen Sie einfach etwas länger.",
"unlocked": false
},
{
"name": "Restaurantgutschein",
"description": "",
"unlocked": false
},
{
"name": "Freundschaftsbonus",
"description": "",
"unlocked": false
},
{
"name": "Früher Check-in, wenn verfügbar",
"description": "Sie treffen schon früher bei uns ein? Kein Problem! Checken Sie einfach kostenlos eine Stunde früher ein so können Sie sich gleich ein wenig ausruhen und haben noch mehr von Ihrem Aufenthalt.",
"unlocked": false
},
{
"name": "Kostenlose Upgrades wenn verfügbar",
"description": "Um auf den Punkt zu kommen wir setzen noch einen drauf. Auf dieser Stufe unserer Freundschaft bieten wir Ihnen, wann immer es möglich ist, ein Zimmer-Upgrade an so wird Ihr Aufenthalt noch komfortabler. Wenn das nicht großartig klingt…",
"unlocked": false
},
{
"name": "Frühstück für zwei zum Preis von einem",
"description": "Wenn das kein delikater Deal ist: Egal, ob Sie bei uns übernachten oder nicht, schnappen Sie sich einen Frühstückspartner, denn jetzt können Sie zu zweit zum Preis von einem bei uns essen! Informieren Sie sich einfach vorher über alle Details und genießen Sie ein gemütliches Frühstück zu zweit.",
"unlocked": false
},
{
"name": "48-Stunden-Zimmergarantie",
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
"unlocked": false
},
{
"name": "Ein kostenloses Frühstück jederzeit",
"description": "Lust auf einen leckeren Start in den Tag? Dann schauen Sie doch einfach bei uns vorbei! Denn jetzt können Sie Ihren Tag mit einem kostenlosen Frühstück beginnen und zwar ganz unabhängig davon, ob Sie bei uns übernachten oder nicht.",
"unlocked": false
},
{
"name": "Ein exklusives Geschenk pro Jahr",
"description": "Als Best Friend haben Sie natürlich eine königliche Behandlung verdient deshalb haben wir einmal im Jahr ein exklusives und ziemlich großartiges Geschenk für Sie. Neugierig? Tja, das bleibt leider eine Überraschung. Es wird noch nichts verraten!",
"unlocked": false
},
{
"name": "Ein Geschenk für Kinder",
"description": "An diesem Punkt unserer Freundschaft betrachten wir natürlich auch Ihr Kind als Freund was bedeutet, dass es bei jeder Übernachtung ein ganz spezielles Geschenk von uns erhält. Und warum? Weil Kinder einfach cool sind! Sie haben eine VIP-Behandlung verdient.",
"unlocked": false
}
]
},
{
"level": 3,
"name": "Close Friend",
"requirement": "10 000 Punkte",
"description": "Jetzt wird es ernst: Wir lernen uns wirklich besser kennen, was bedeutet, dass Ihre Zeit mit Scandic noch viel persönlicher wird.",
"icon": "/_static/icons/loyaltylevels/close-friend.svg",
"benefits": [
{
"name": "Freundschaftspreise",
"description": "Als unser Freund erhalten Sie immer das beste Angebot: an jedem Tag und an jedem Ort. Und dafür ist weder ein Handschlag noch ein Buchungscode erforderlich stürzen Sie sich einfach ins Buchungserlebnis und überzeugen Sie sich selbst.",
"unlocked": true
},
{
"name": "Rabatt auf Speisen",
"description": "Was gibt es Schöneres als einen Preisnachlass? Als Freund erhalten Sie an Wochenenden und ausgewählten Feiertagen 10-15% Rabatt auf die Speisen in unseren Restaurants und Hotelshops und zwar unabhängig davon, ob Sie bei uns übernachten oder nicht. Also kommen Sie vorbei und tun Sie sich etwas Gutes! Zum Beispiel mit unserem zusätzlichen Zimmerservice.",
"unlocked": true,
"value": "15%"
},
{
"name": "Kostenloser Kinder-Mocktail während des Aufenthalts",
"description": "Oh, Sie haben ja die Kinder dabei! Wie schön hallo junger Freund! Wir möchten, dass sich Kinder bei uns wie VIPs fühlen, und deshalb bekommen sie von uns bei jedem Aufenthalt einen erfrischenden Mocktail spendiert.",
"unlocked": true
},
{
"name": "Später Check-out, wenn verfügbar",
"description": "Wir wissen, dass ein später Check-out manchmal eine große Erleichterung sein kann. Und an diesem Punkt unserer Freundschaft gibt es keinen Grund mehr, sich aus dem Bett zu quälen: Wir kommen Ihnen gerne entgegen. Checken Sie kostenlos 1Stunde später aus und schlafen Sie einfach etwas länger.",
"unlocked": true
},
{
"name": "Restaurantgutschein",
"description": "Und jetzt kommt der Clou: Für jede Übernachtung, mit der Sie bei uns Freundschaftspunkte sammeln, schenken wir Ihnen zusätzlich einen Restaurantgutschein über 5€.",
"unlocked": true,
"value": "5 €"
},
{
"name": "Freundschaftsbonus",
"description": "",
"unlocked": false
},
{
"name": "Früher Check-in, wenn verfügbar",
"description": "Sie treffen schon früher bei uns ein? Kein Problem! Checken Sie einfach kostenlos eine Stunde früher ein so können Sie sich gleich ein wenig ausruhen und haben noch mehr von Ihrem Aufenthalt.",
"unlocked": false
},
{
"name": "Kostenlose Upgrades wenn verfügbar",
"description": "Um auf den Punkt zu kommen wir setzen noch einen drauf. Auf dieser Stufe unserer Freundschaft bieten wir Ihnen, wann immer es möglich ist, ein Zimmer-Upgrade an so wird Ihr Aufenthalt noch komfortabler. Wenn das nicht großartig klingt…",
"unlocked": false
},
{
"name": "Frühstück für zwei zum Preis von einem",
"description": "Wenn das kein delikater Deal ist: Egal, ob Sie bei uns übernachten oder nicht, schnappen Sie sich einen Frühstückspartner, denn jetzt können Sie zu zweit zum Preis von einem bei uns essen! Informieren Sie sich einfach vorher über alle Details und genießen Sie ein gemütliches Frühstück zu zweit.",
"unlocked": false
},
{
"name": "48-Stunden-Zimmergarantie",
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
"unlocked": false
},
{
"name": "Ein kostenloses Frühstück jederzeit",
"description": "Lust auf einen leckeren Start in den Tag? Dann schauen Sie doch einfach bei uns vorbei! Denn jetzt können Sie Ihren Tag mit einem kostenlosen Frühstück beginnen und zwar ganz unabhängig davon, ob Sie bei uns übernachten oder nicht.",
"unlocked": false
},
{
"name": "Ein exklusives Geschenk pro Jahr",
"description": "Als Best Friend haben Sie natürlich eine königliche Behandlung verdient deshalb haben wir einmal im Jahr ein exklusives und ziemlich großartiges Geschenk für Sie. Neugierig? Tja, das bleibt leider eine Überraschung. Es wird noch nichts verraten!",
"unlocked": false
},
{
"name": "Ein Geschenk für Kinder",
"description": "An diesem Punkt unserer Freundschaft betrachten wir natürlich auch Ihr Kind als Freund was bedeutet, dass es bei jeder Übernachtung ein ganz spezielles Geschenk von uns erhält. Und warum? Weil Kinder einfach cool sind! Sie haben eine VIP-Behandlung verdient.",
"unlocked": false
}
]
},
{
"level": 4,
"name": "Dear Friend",
"requirement": "25 000 Punkte",
"description": "Ein Hoch auf uns! Unser Verhältnis scheint sich in Richtung Freunde fürs Leben zu entwickeln was auch bedeutet, dass Sie Zugang zu einer ganzen Menge mehr Scandic bekommen.",
"icon": "/_static/icons/loyaltylevels/dear-friend.svg",
"benefits": [
{
"name": "Freundschaftspreise",
"description": "Als unser Freund erhalten Sie immer das beste Angebot: an jedem Tag und an jedem Ort. Und dafür ist weder ein Handschlag noch ein Buchungscode erforderlich stürzen Sie sich einfach ins Buchungserlebnis und überzeugen Sie sich selbst.",
"unlocked": true
},
{
"name": "Rabatt auf Speisen",
"description": "Was gibt es Schöneres als einen Preisnachlass? Als Freund erhalten Sie an Wochenenden und ausgewählten Feiertagen 10-15% Rabatt auf die Speisen in unseren Restaurants und Hotelshops und zwar unabhängig davon, ob Sie bei uns übernachten oder nicht. Also kommen Sie vorbei und tun Sie sich etwas Gutes! Zum Beispiel mit unserem zusätzlichen Zimmerservice.",
"unlocked": true,
"value": "15%"
},
{
"name": "Kostenloser Kinder-Mocktail während des Aufenthalts",
"description": "Oh, Sie haben ja die Kinder dabei! Wie schön hallo junger Freund! Wir möchten, dass sich Kinder bei uns wie VIPs fühlen, und deshalb bekommen sie von uns bei jedem Aufenthalt einen erfrischenden Mocktail spendiert.",
"unlocked": true
},
{
"name": "Später Check-out, wenn verfügbar",
"description": "Wir wissen, dass ein später Check-out manchmal eine große Erleichterung sein kann. Und an diesem Punkt unserer Freundschaft gibt es keinen Grund mehr, sich aus dem Bett zu quälen: Wir kommen Ihnen gerne entgegen. Checken Sie kostenlos 1Stunde später aus und schlafen Sie einfach etwas länger.",
"unlocked": true
},
{
"name": "Restaurantgutschein",
"description": "Unsere Freundschaft wird jetzt noch schöner! Genießen Sie einen Restaurantgutschein im Wert von 7,50€ für jede Übernachtung, mit der Sie bei uns Punkte sammeln. Ist das nicht aufregend?",
"unlocked": true,
"value": "7,50 €"
},
{
"name": "Freundschaftsbonus",
"description": "Hier haben wir etwas wirklich Großartiges für Sie: Jedes Mal, wenn Sie einen neuen Schub an Freundschaftspunkten erhalten, gibt es von uns 25% dazu ein ordentlicher Extra-Schub! Wenn Sie also anfangen, diese Punkte für Aufenthalte, Mahlzeiten und anderes zu sammeln, erhalten Sie im Handumdrehen eine kostenlose Übernachtung.",
"unlocked": true,
"value": "25%"
},
{
"name": "Früher Check-in, wenn verfügbar",
"description": "Sie treffen schon früher bei uns ein? Kein Problem! Checken Sie einfach kostenlos eine Stunde früher ein so können Sie sich gleich ein wenig ausruhen und haben noch mehr von Ihrem Aufenthalt.",
"unlocked": true
},
{
"name": "Kostenlose Upgrades wenn verfügbar",
"description": "Um auf den Punkt zu kommen wir setzen noch einen drauf. Auf dieser Stufe unserer Freundschaft bieten wir Ihnen, wann immer es möglich ist, ein Zimmer-Upgrade an so wird Ihr Aufenthalt noch komfortabler. Wenn das nicht großartig klingt…",
"unlocked": false
},
{
"name": "Frühstück für zwei zum Preis von einem",
"description": "Wenn das kein delikater Deal ist: Egal, ob Sie bei uns übernachten oder nicht, schnappen Sie sich einen Frühstückspartner, denn jetzt können Sie zu zweit zum Preis von einem bei uns essen! Informieren Sie sich einfach vorher über alle Details und genießen Sie ein gemütliches Frühstück zu zweit.",
"unlocked": false
},
{
"name": "48-Stunden-Zimmergarantie",
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
"unlocked": false
},
{
"name": "Ein kostenloses Frühstück jederzeit",
"description": "Lust auf einen leckeren Start in den Tag? Dann schauen Sie doch einfach bei uns vorbei! Denn jetzt können Sie Ihren Tag mit einem kostenlosen Frühstück beginnen und zwar ganz unabhängig davon, ob Sie bei uns übernachten oder nicht.",
"unlocked": false
},
{
"name": "Ein exklusives Geschenk pro Jahr",
"description": "Als Best Friend haben Sie natürlich eine königliche Behandlung verdient deshalb haben wir einmal im Jahr ein exklusives und ziemlich großartiges Geschenk für Sie. Neugierig? Tja, das bleibt leider eine Überraschung. Es wird noch nichts verraten!",
"unlocked": false
},
{
"name": "Ein Geschenk für Kinder",
"description": "An diesem Punkt unserer Freundschaft betrachten wir natürlich auch Ihr Kind als Freund was bedeutet, dass es bei jeder Übernachtung ein ganz spezielles Geschenk von uns erhält. Und warum? Weil Kinder einfach cool sind! Sie haben eine VIP-Behandlung verdient.",
"unlocked": false
}
]
},
{
"level": 5,
"name": "Loyal Friend",
"requirement": "100 000 Punkte",
"description": "Sie haben uns während zahlreicher Aufenthalte, Happy Hours und Workouts im Fitnessstudio die Treue gehalten deshalb wollen wir uns mit einigen unserer großartigsten Belohnungen bei Ihnen revanchieren.",
"icon": "/_static/icons/loyaltylevels/loyal-friend.svg",
"benefits": [
{
"name": "Freundschaftspreise",
"description": "Als unser Freund erhalten Sie immer das beste Angebot: an jedem Tag und an jedem Ort. Und dafür ist weder ein Handschlag noch ein Buchungscode erforderlich stürzen Sie sich einfach ins Buchungserlebnis und überzeugen Sie sich selbst.",
"unlocked": true
},
{
"name": "Rabatt auf Speisen",
"description": "Was gibt es Schöneres als einen Preisnachlass? Als Freund erhalten Sie an Wochenenden und ausgewählten Feiertagen 10-15% Rabatt auf die Speisen in unseren Restaurants und Hotelshops und zwar unabhängig davon, ob Sie bei uns übernachten oder nicht. Also kommen Sie vorbei und tun Sie sich etwas Gutes! Zum Beispiel mit unserem zusätzlichen Zimmerservice.",
"unlocked": true,
"value": "15%"
},
{
"name": "Kostenloser Kinder-Mocktail während des Aufenthalts",
"description": "Oh, Sie haben ja die Kinder dabei! Wie schön hallo junger Freund! Wir möchten, dass sich Kinder bei uns wie VIPs fühlen, und deshalb bekommen sie von uns bei jedem Aufenthalt einen erfrischenden Mocktail spendiert.",
"unlocked": true
},
{
"name": "Später Check-out, wenn verfügbar",
"description": "Wir wissen, dass ein später Check-out manchmal eine große Erleichterung sein kann. Und an diesem Punkt unserer Freundschaft gibt es keinen Grund mehr, sich aus dem Bett zu quälen: Wir kommen Ihnen gerne entgegen. Checken Sie kostenlos 1Stunde später aus und schlafen Sie einfach etwas länger.",
"unlocked": true
},
{
"name": "Restaurantgutschein",
"description": "Wir schenken Ihnen einen zusätzlichen Restaurantgutschein über 10€ für jede Übernachtung, mit der Sie bei uns Freundschaftspunkte sammeln. Dafür bekommen Sie schon einen ganzen Berg von Croissants! Klingt das nicht verlockend?",
"unlocked": true,
"value": "10 €"
},
{
"name": "Freundschaftsbonus",
"description": "Hier haben wir etwas wirklich Großartiges für Sie: Jedes Mal, wenn Sie einen neuen Schub an Freundschaftspunkten erhalten, gibt es von uns 25% dazu ein ordentlicher Extra-Schub! Wenn Sie also anfangen, diese Punkte für Aufenthalte, Mahlzeiten und anderes zu sammeln, erhalten Sie im Handumdrehen eine kostenlose Übernachtung.",
"unlocked": true,
"value": "25%"
},
{
"name": "Früher Check-in, wenn verfügbar",
"description": "Sie treffen schon früher bei uns ein? Kein Problem! Checken Sie einfach kostenlos eine Stunde früher ein so können Sie sich gleich ein wenig ausruhen und haben noch mehr von Ihrem Aufenthalt.",
"unlocked": true
},
{
"name": "Kostenlose Upgrades wenn verfügbar",
"description": "Um auf den Punkt zu kommen wir setzen noch einen drauf. Auf dieser Stufe unserer Freundschaft bieten wir Ihnen, wann immer es möglich ist, ein Zimmer-Upgrade an so wird Ihr Aufenthalt noch komfortabler. Wenn das nicht großartig klingt…",
"unlocked": true
},
{
"name": "Frühstück für zwei zum Preis von einem",
"description": "Wenn das kein delikater Deal ist: Egal, ob Sie bei uns übernachten oder nicht, schnappen Sie sich einen Frühstückspartner, denn jetzt können Sie zu zweit zum Preis von einem bei uns essen! Informieren Sie sich einfach vorher über alle Details und genießen Sie ein gemütliches Frühstück zu zweit.",
"unlocked": true
},
{
"name": "48-Stunden-Zimmergarantie",
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
"unlocked": false
},
{
"name": "Ein kostenloses Frühstück jederzeit",
"description": "Lust auf einen leckeren Start in den Tag? Dann schauen Sie doch einfach bei uns vorbei! Denn jetzt können Sie Ihren Tag mit einem kostenlosen Frühstück beginnen und zwar ganz unabhängig davon, ob Sie bei uns übernachten oder nicht.",
"unlocked": false
},
{
"name": "Ein exklusives Geschenk pro Jahr",
"description": "Als Best Friend haben Sie natürlich eine königliche Behandlung verdient deshalb haben wir einmal im Jahr ein exklusives und ziemlich großartiges Geschenk für Sie. Neugierig? Tja, das bleibt leider eine Überraschung. Es wird noch nichts verraten!",
"unlocked": false
},
{
"name": "Ein Geschenk für Kinder",
"description": "An diesem Punkt unserer Freundschaft betrachten wir natürlich auch Ihr Kind als Freund was bedeutet, dass es bei jeder Übernachtung ein ganz spezielles Geschenk von uns erhält. Und warum? Weil Kinder einfach cool sind! Sie haben eine VIP-Behandlung verdient.",
"unlocked": false
}
]
},
{
"level": 6,
"name": "True Friend",
"requirement": "250 000 Punkte",
"description": "Es spielt keine Rolle, ob Haupt- oder Nebensaison: Sie sind immer für uns da. Genießen Sie noch mehr individuelle Vorteile genau nach Ihrem Geschmack.",
"icon": "/_static/icons/loyaltylevels/true-friend.svg",
"benefits": [
{
"name": "Freundschaftspreise",
"description": "Als unser Freund erhalten Sie immer das beste Angebot: an jedem Tag und an jedem Ort. Und dafür ist weder ein Handschlag noch ein Buchungscode erforderlich stürzen Sie sich einfach ins Buchungserlebnis und überzeugen Sie sich selbst.",
"unlocked": true
},
{
"name": "Rabatt auf Speisen",
"description": "Was gibt es Schöneres als einen Preisnachlass? Als Freund erhalten Sie an Wochenenden und ausgewählten Feiertagen 10-15% Rabatt auf die Speisen in unseren Restaurants und Hotelshops und zwar unabhängig davon, ob Sie bei uns übernachten oder nicht. Also kommen Sie vorbei und tun Sie sich etwas Gutes! Zum Beispiel mit unserem zusätzlichen Zimmerservice.",
"unlocked": true,
"value": "15%"
},
{
"name": "Kostenloser Kinder-Mocktail während des Aufenthalts",
"description": "Oh, Sie haben ja die Kinder dabei! Wie schön hallo junger Freund! Wir möchten, dass sich Kinder bei uns wie VIPs fühlen, und deshalb bekommen sie von uns bei jedem Aufenthalt einen erfrischenden Mocktail spendiert.",
"unlocked": true
},
{
"name": "Später Check-out, wenn verfügbar",
"description": "Wir wissen, dass ein später Check-out manchmal eine große Erleichterung sein kann. Und an diesem Punkt unserer Freundschaft gibt es keinen Grund mehr, sich aus dem Bett zu quälen: Wir kommen Ihnen gerne entgegen. Checken Sie kostenlos 1Stunde später aus und schlafen Sie einfach etwas länger.",
"unlocked": true
},
{
"name": "Restaurantgutschein",
"description": "Zwischen uns läuft es inzwischen so gut, dass Sie für jede Nacht, in der Sie bei uns Freundschaftspunkte sammeln, zusätzlich noch einen Restaurantgutschein im Wert von 15€ erhalten. Perfekt für Ihr nächstes köstliches Frühstück oder Abendessen!",
"unlocked": true,
"value": "15 €"
},
{
"name": "Freundschaftsbonus",
"description": "Was für ein Spaß! Jedes Mal, wenn Sie einen neuen Schub an Freundschaftspunkten erhalten, gibt es von uns 25-50% dazu ein ordentlicher Extraschub! Je früher Sie also anfangen, Punkte für Ihre Aufenthalte, Mahlzeiten und vieles mehr zu sammeln, desto schneller erhalten Sie Ihre erste kostenlose Übernachtung.",
"unlocked": true,
"value": "50%"
},
{
"name": "Früher Check-in, wenn verfügbar",
"description": "Sie treffen schon früher bei uns ein? Kein Problem! Checken Sie einfach kostenlos eine Stunde früher ein so können Sie sich gleich ein wenig ausruhen und haben noch mehr von Ihrem Aufenthalt.",
"unlocked": true
},
{
"name": "Kostenlose Upgrades wenn verfügbar",
"description": "Um auf den Punkt zu kommen wir setzen noch einen drauf. Auf dieser Stufe unserer Freundschaft bieten wir Ihnen, wann immer es möglich ist, ein Zimmer-Upgrade an so wird Ihr Aufenthalt noch komfortabler. Wenn das nicht großartig klingt…",
"unlocked": true
},
{
"name": "Frühstück für zwei zum Preis von einem",
"description": "Wenn das kein delikater Deal ist: Egal, ob Sie bei uns übernachten oder nicht, schnappen Sie sich einen Frühstückspartner, denn jetzt können Sie zu zweit zum Preis von einem bei uns essen! Informieren Sie sich einfach vorher über alle Details und genießen Sie ein gemütliches Frühstück zu zweit.",
"unlocked": false
},
{
"name": "48-Stunden-Zimmergarantie",
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
"unlocked": true
},
{
"name": "Ein kostenloses Frühstück jederzeit",
"description": "Lust auf einen leckeren Start in den Tag? Dann schauen Sie doch einfach bei uns vorbei! Denn jetzt können Sie Ihren Tag mit einem kostenlosen Frühstück beginnen und zwar ganz unabhängig davon, ob Sie bei uns übernachten oder nicht.",
"unlocked": true
},
{
"name": "Ein exklusives Geschenk pro Jahr",
"description": "Als Best Friend haben Sie natürlich eine königliche Behandlung verdient deshalb haben wir einmal im Jahr ein exklusives und ziemlich großartiges Geschenk für Sie. Neugierig? Tja, das bleibt leider eine Überraschung. Es wird noch nichts verraten!",
"unlocked": false
},
{
"name": "Ein Geschenk für Kinder",
"description": "An diesem Punkt unserer Freundschaft betrachten wir natürlich auch Ihr Kind als Freund was bedeutet, dass es bei jeder Übernachtung ein ganz spezielles Geschenk von uns erhält. Und warum? Weil Kinder einfach cool sind! Sie haben eine VIP-Behandlung verdient.",
"unlocked": false
}
]
},
{
"level": 7,
"name": "Best Friend",
"requirement": "400 000 Punkte oder 100 Nächte",
"description": "Für eine Freundschaft wie diese gibt es im Grunde keine passenden Worte, aber wir versuchen es trotzdem: Denn es könnte gar nichts Besseres geben, wenn es um sehr, sehr exklusive Erlebnisse geht!",
"icon": "/_static/icons/loyaltylevels/best-friend.svg",
"benefits": [
{
"name": "Freundschaftspreise",
"description": "Als unser Freund erhalten Sie immer das beste Angebot: an jedem Tag und an jedem Ort. Und dafür ist weder ein Handschlag noch ein Buchungscode erforderlich stürzen Sie sich einfach ins Buchungserlebnis und überzeugen Sie sich selbst.",
"unlocked": true
},
{
"name": "Rabatt auf Speisen",
"description": "Was gibt es Schöneres als einen Preisnachlass? Als Freund erhalten Sie an Wochenenden und ausgewählten Feiertagen 10-15% Rabatt auf die Speisen in unseren Restaurants und Hotelshops und zwar unabhängig davon, ob Sie bei uns übernachten oder nicht. Also kommen Sie vorbei und tun Sie sich etwas Gutes! Zum Beispiel mit unserem zusätzlichen Zimmerservice.",
"unlocked": true,
"value": "15%"
},
{
"name": "Kostenloser Kinder-Mocktail während des Aufenthalts",
"description": "Oh, Sie haben ja die Kinder dabei! Wie schön hallo junger Freund! Wir möchten, dass sich Kinder bei uns wie VIPs fühlen, und deshalb bekommen sie von uns bei jedem Aufenthalt einen erfrischenden Mocktail spendiert.",
"unlocked": true
},
{
"name": "Später Check-out, wenn verfügbar",
"description": "Wir wissen, dass ein später Check-out manchmal eine große Erleichterung sein kann. Und an diesem Punkt unserer Freundschaft gibt es keinen Grund mehr, sich aus dem Bett zu quälen: Wir kommen Ihnen gerne entgegen. Checken Sie kostenlos 1Stunde später aus und schlafen Sie einfach etwas länger.",
"unlocked": true
},
{
"name": "Restaurantgutschein",
"description": "Für jede Nacht, in der Sie bei uns Freundschaftspunkte sammeln, erhalten Sie einen Restaurantgutschein in Höhe von 20€. Stellen Sie sich vor, wie viele Croissants Sie dafür essen könnten! Wenn das nicht verlockend ist?",
"unlocked": true,
"value": "20 €"
},
{
"name": "Freundschaftsbonus",
"description": "Was für ein Spaß! Jedes Mal, wenn Sie einen neuen Schub an Freundschaftspunkten erhalten, gibt es von uns 25-50% dazu ein ordentlicher Extraschub! Je früher Sie also anfangen, Punkte für Ihre Aufenthalte, Mahlzeiten und vieles mehr zu sammeln, desto schneller erhalten Sie Ihre erste kostenlose Übernachtung.",
"unlocked": true,
"value": "50%"
},
{
"name": "Früher Check-in, wenn verfügbar",
"description": "Sie treffen schon früher bei uns ein? Kein Problem! Checken Sie einfach kostenlos eine Stunde früher ein so können Sie sich gleich ein wenig ausruhen und haben noch mehr von Ihrem Aufenthalt.",
"unlocked": true
},
{
"name": "Kostenlose Upgrades wenn verfügbar",
"description": "Um auf den Punkt zu kommen wir setzen noch einen drauf. Auf dieser Stufe unserer Freundschaft bieten wir Ihnen, wann immer es möglich ist, ein Zimmer-Upgrade an so wird Ihr Aufenthalt noch komfortabler. Wenn das nicht großartig klingt…",
"unlocked": true
},
{
"name": "Frühstück für zwei zum Preis von einem",
"description": "Wenn das kein delikater Deal ist: Egal, ob Sie bei uns übernachten oder nicht, schnappen Sie sich einen Frühstückspartner, denn jetzt können Sie zu zweit zum Preis von einem bei uns essen! Informieren Sie sich einfach vorher über alle Details und genießen Sie ein gemütliches Frühstück zu zweit.",
"unlocked": false
},
{
"name": "48-Stunden-Zimmergarantie",
"description": "Pssst! Diesen ganz besonderen Leckerbissen bekommen bei uns nur die Wenigsten! Also aufgepasst: Selbst wenn wir völlig ausgebucht sind, erhalten Sie bei uns garantiert ein Zimmer, wenn Sie 48Stunden im Voraus buchen. Ist das nicht einfach unglaublich?",
"unlocked": true
},
{
"name": "Ein kostenloses Frühstück jederzeit",
"description": "Lust auf einen leckeren Start in den Tag? Dann schauen Sie doch einfach bei uns vorbei! Denn jetzt können Sie Ihren Tag mit einem kostenlosen Frühstück beginnen und zwar ganz unabhängig davon, ob Sie bei uns übernachten oder nicht.",
"unlocked": true
},
{
"name": "Ein exklusives Geschenk pro Jahr",
"description": "Als Best Friend haben Sie natürlich eine königliche Behandlung verdient deshalb haben wir einmal im Jahr ein exklusives und ziemlich großartiges Geschenk für Sie. Neugierig? Tja, das bleibt leider eine Überraschung. Es wird noch nichts verraten!",
"unlocked": true
},
{
"name": "Ein Geschenk für Kinder",
"description": "An diesem Punkt unserer Freundschaft betrachten wir natürlich auch Ihr Kind als Freund was bedeutet, dass es bei jeder Übernachtung ein ganz spezielles Geschenk von uns erhält. Und warum? Weil Kinder einfach cool sind! Sie haben eine VIP-Behandlung verdient.",
"unlocked": true
}
]
}
]
}

View File

@@ -1,538 +0,0 @@
{
"levels": [
{
"level": 1,
"name": "New Friend",
"requirement": "0p",
"description": "This is the start of something beautiful: as a New Friend, get ready for a journey of delightful Scandic discoveries.",
"icon": "/_static/icons/loyaltylevels/new-friend.svg",
"benefits": [
{
"name": "Friendly rates",
"description": "Being our friend means youre always scoring the best deal: any day, any place. No secret handshake or booking code needed just dive into the booking and see for yourself.",
"unlocked": true
},
{
"name": "Discount on food",
"description": "Yumm! Enjoy a savory 10% or more off in our restaurant and hotel shop on weekends. This offer stands whether you're our guest for the night or just dropping by for a bite. So, go ahead, treat yourself and order that extra room service.",
"unlocked": true,
"value": "10%"
},
{
"name": "Free kids mocktail during stay",
"description": "Got the kiddos with you? Thats amazing hello there, buddy! We wanna make sure that kids feel like the VIPs they really are, so every stay they get a refreshing mocktail on us.",
"unlocked": true
},
{
"name": "Late checkout when available",
"description": "We get it late checkout can be a total trip-saver at times. At this point in our friendship, theres no need to rush out of bed: weve got you covered. Check out 1 hour later at no cost and get those last, extra refreshing minutes of sleep.",
"unlocked": false
},
{
"name": "Restaurant voucher",
"description": "Yeah, thats right: for each friendship point-boosting night, you get a voucher to redeem when you dine or drink at our restaurants and bars.",
"unlocked": false
},
{
"name": "Friendship point boost",
"description": "Now heres something pretty sweet: every time you get a boost of friendship points, you get 25% or 50% extra a boost! So you know, start racking up those points on stays, meals and more and youll get a free night in no time.",
"unlocked": false
},
{
"name": "Early check-in when available",
"description": "Wanna get a head start on your stay? No problem, no problem. Check in 1 hour earlier at no cost and take the highway to relaxation and bliss.",
"unlocked": false
},
{
"name": "Free upgrades when available",
"description": "Let's cut to the chase were taking things up a notch. At this level in our friendship, we'll upgrade your room whenever possible for an even comfier stay. Sounds good, right?",
"unlocked": false
},
{
"name": "2-for-1 breakfast",
"description": "Now, this is a delicious deal: regardless if youre staying with us or not, go grab a breakfast buddy because you're eating two for the price of one! Just make sure to check the details first so everything is in place for your yummy get-together.",
"unlocked": false
},
{
"name": "48hr room guarantee",
"description": "Hush this one's a special treat just for a few of you! So listen up: even if we're fully booked, you're guaranteed a room as long as you book 48 hours in advance. Pretty incredible!",
"unlocked": false
},
{
"name": "Free breakfast always",
"description": "Care for a tasty morning treat? Come on over! You now get to kickstart your day with free breakfast and guess what: that goes regardless if youre staying with us or not.",
"unlocked": false
},
{
"name": "Yearly awesome gift",
"description": "As our Best Friend, you totally deserve princess treatment so weve got an exclusive, annual and pretty awesome gift all lined up. What it is? Well, thats a surprise. No peeking!",
"unlocked": false
},
{
"name": "Kids boost",
"description": "At this friendship level, your kid is our friend, too and that means a special kids boost gift when you stay with us. Why? Because kids are cool! They totally deserve VIP treatment.",
"unlocked": false
}
]
},
{
"level": 2,
"name": "Good Friend",
"requirement": "5 000p",
"description": "You've been around a lot lately! And honestly, we feel like we're vibing - one enjoyable stay at a time.",
"icon": "/_static/icons/loyaltylevels/good-friend.svg",
"benefits": [
{
"name": "Friendly rates",
"description": "Being our friend means youre always scoring the best deal: any day, any place. No secret handshake or booking code needed just dive into the booking and see for yourself.",
"unlocked": true
},
{
"name": "Discount on food",
"description": "Whats more delicious than a discount? As our friend, you get 10-15% off on our restaurant and hotel shop food on weekends and selected holidays and that goes for when youre staying with us and when youre not. So come on, spoil yourself! Get some room service.",
"unlocked": true,
"value": "15%"
},
{
"name": "Free kids mocktail during stay",
"description": "Got the kiddos with you? Thats amazing hello there, buddy! We wanna make sure that kids feel like the VIPs they really are, so every stay they get a refreshing mocktail on us.",
"unlocked": true
},
{
"name": "Late checkout when available",
"description": "We get it late checkout can be a total trip-saver at times. At this point in our friendship, theres no need to rush out of bed: weve got you covered. Check out 1 hour later at no cost and get those last, extra refreshing minutes of sleep.",
"unlocked": false
},
{
"name": "Restaurant voucher",
"description": "Yeah, thats right: for each friendship point-boosting night, you get a voucher to redeem when you dine or drink at our restaurants and bars.",
"unlocked": false
},
{
"name": "Friendship point boost",
"description": "Now heres something pretty sweet: every time you get a boost of friendship points, you get 25% or 50% extra a boost! So you know, start racking up those points on stays, meals and more and youll get a free night in no time.",
"unlocked": false
},
{
"name": "Early check-in when available",
"description": "Wanna get a head start on your stay? No problem, no problem. Check in 1 hour earlier at no cost and take the highway to relaxation and bliss.",
"unlocked": false
},
{
"name": "Free upgrades when available",
"description": "Let's cut to the chase were taking things up a notch. At this level in our friendship, we'll upgrade your room whenever possible for an even comfier stay. Sounds good, right?",
"unlocked": false
},
{
"name": "2-for-1 breakfast",
"description": "Now, this is a delicious deal: regardless if youre staying with us or not, go grab a breakfast buddy because you're eating two for the price of one! Just make sure to check the details first so everything is in place for your yummy get-together.",
"unlocked": false
},
{
"name": "48hr room guarantee",
"description": "Hush this one's a special treat just for a few of you! So listen up: even if we're fully booked, you're guaranteed a room as long as you book 48 hours in advance. Pretty incredible!",
"unlocked": false
},
{
"name": "Free breakfast always",
"description": "Care for a tasty morning treat? Come on over! You now get to kickstart your day with free breakfast and guess what: that goes regardless if youre staying with us or not.",
"unlocked": false
},
{
"name": "Yearly awesome gift",
"description": "As our Best Friend, you totally deserve princess treatment so weve got an exclusive, annual and pretty awesome gift all lined up. What it is? Well, thats a surprise. No peeking!",
"unlocked": false
},
{
"name": "Kids boost",
"description": "At this friendship level, your kid is our friend, too and that means a special kids boost gift when you stay with us. Why? Because kids are cool! They totally deserve VIP treatment.",
"unlocked": false
}
]
},
{
"level": 3,
"name": "Close Friend",
"requirement": "10 000p",
"description": "It's serious now: we're really getting to know each other which makes your Scandic experiences a lot more personal.",
"icon": "/_static/icons/loyaltylevels/close-friend.svg",
"benefits": [
{
"name": "Friendly rates",
"description": "Being our friend means youre always scoring the best deal: any day, any place. No secret handshake or booking code needed just dive into the booking and see for yourself.",
"unlocked": true
},
{
"name": "Discount on food",
"description": "Whats more delicious than a discount? As our friend, you get 10-15% off on our restaurant and hotel shop food on weekends and selected holidays and that goes for when youre staying with us and when youre not. So come on, spoil yourself! Get some room service.",
"unlocked": true,
"value": "15%"
},
{
"name": "Free kids mocktail during stay",
"description": "Got the kiddos with you? Thats amazing hello there, buddy! We wanna make sure that kids feel like the VIPs they really are, so every stay they get a refreshing mocktail on us.",
"unlocked": true
},
{
"name": "Late checkout when available",
"description": "We get it late checkout can be a total trip-saver at times. At this point in our friendship, theres no need to rush out of bed: weve got you covered. Check out 1 hour later at no cost and get those last, extra refreshing minutes of sleep.",
"unlocked": true
},
{
"name": "Restaurant voucher",
"description": "Yeah, thats right: for each friendship point-boosting night, you get a voucher to redeem when you dine or drink at our restaurants and bars.",
"unlocked": true,
"value": "€5"
},
{
"name": "Friendship point boost",
"description": "Now heres something pretty sweet: every time you get a boost of friendship points, you get 25% or 50% extra a boost! So you know, start racking up those points on stays, meals and more and youll get a free night in no time.",
"unlocked": false
},
{
"name": "Early check-in when available",
"description": "Wanna get a head start on your stay? No problem, no problem. Check in 1 hour earlier at no cost and take the highway to relaxation and bliss.",
"unlocked": false
},
{
"name": "Free upgrades when available",
"description": "Let's cut to the chase were taking things up a notch. At this level in our friendship, we'll upgrade your room whenever possible for an even comfier stay. Sounds good, right?",
"unlocked": false
},
{
"name": "2-for-1 breakfast",
"description": "Now, this is a delicious deal: regardless if youre staying with us or not, go grab a breakfast buddy because you're eating two for the price of one! Just make sure to check the details first so everything is in place for your yummy get-together.",
"unlocked": false
},
{
"name": "48hr room guarantee",
"description": "Hush this one's a special treat just for a few of you! So listen up: even if we're fully booked, you're guaranteed a room as long as you book 48 hours in advance. Pretty incredible!",
"unlocked": false
},
{
"name": "Free breakfast always",
"description": "Care for a tasty morning treat? Come on over! You now get to kickstart your day with free breakfast and guess what: that goes regardless if youre staying with us or not.",
"unlocked": false
},
{
"name": "Yearly awesome gift",
"description": "As our Best Friend, you totally deserve princess treatment so weve got an exclusive, annual and pretty awesome gift all lined up. What it is? Well, thats a surprise. No peeking!",
"unlocked": false
},
{
"name": "Kids boost",
"description": "At this friendship level, your kid is our friend, too and that means a special kids boost gift when you stay with us. Why? Because kids are cool! They totally deserve VIP treatment.",
"unlocked": false
}
]
},
{
"level": 4,
"name": "Dear Friend",
"requirement": "25 000p",
"description": "Cheers to us! This seems to be going in the direction of friends for life - and that comes with access to a-whole-lotta-more of Scandic.",
"icon": "/_static/icons/loyaltylevels/dear-friend.svg",
"benefits": [
{
"name": "Friendly rates",
"description": "Being our friend means youre always scoring the best deal: any day, any place. No secret handshake or booking code needed just dive into the booking and see for yourself.",
"unlocked": true
},
{
"name": "Discount on food",
"description": "Whats more delicious than a discount? As our friend, you get 10-15% off on our restaurant and hotel shop food on weekends and selected holidays and that goes for when youre staying with us and when youre not. So come on, spoil yourself! Get some room service.",
"unlocked": true,
"value": "15%"
},
{
"name": "Free kids mocktail during stay",
"description": "Got the kiddos with you? Thats amazing hello there, buddy! We wanna make sure that kids feel like the VIPs they really are, so every stay they get a refreshing mocktail on us.",
"unlocked": true
},
{
"name": "Late checkout when available",
"description": "We get it late checkout can be a total trip-saver at times. At this point in our friendship, theres no need to rush out of bed: weve got you covered. Check out 1 hour later at no cost and get those last, extra refreshing minutes of sleep.",
"unlocked": true
},
{
"name": "Restaurant voucher",
"description": "Yeah, thats right: for each friendship point-boosting night, you get a voucher to redeem when you dine or drink at our restaurants and bars.",
"unlocked": true,
"value": "€7.5"
},
{
"name": "Friendship point boost",
"description": "Now heres something pretty sweet: every time you get a boost of friendship points, you get 25% or 50% extra a boost! So you know, start racking up those points on stays, meals and more and youll get a free night in no time.",
"unlocked": true,
"value": "+25%"
},
{
"name": "Early check-in when available",
"description": "Wanna get a head start on your stay? No problem, no problem. Check in 1 hour earlier at no cost and take the highway to relaxation and bliss.",
"unlocked": true
},
{
"name": "Free upgrades when available",
"description": "Let's cut to the chase were taking things up a notch. At this level in our friendship, we'll upgrade your room whenever possible for an even comfier stay. Sounds good, right?",
"unlocked": false
},
{
"name": "2-for-1 breakfast",
"description": "Now, this is a delicious deal: regardless if youre staying with us or not, go grab a breakfast buddy because you're eating two for the price of one! Just make sure to check the details first so everything is in place for your yummy get-together.",
"unlocked": false
},
{
"name": "48hr room guarantee",
"description": "Hush this one's a special treat just for a few of you! So listen up: even if we're fully booked, you're guaranteed a room as long as you book 48 hours in advance. Pretty incredible!",
"unlocked": false
},
{
"name": "Free breakfast always",
"description": "Care for a tasty morning treat? Come on over! You now get to kickstart your day with free breakfast and guess what: that goes regardless if youre staying with us or not.",
"unlocked": false
},
{
"name": "Yearly awesome gift",
"description": "As our Best Friend, you totally deserve princess treatment so weve got an exclusive, annual and pretty awesome gift all lined up. What it is? Well, thats a surprise. No peeking!",
"unlocked": false
},
{
"name": "Kids boost",
"description": "At this friendship level, your kid is our friend, too and that means a special kids boost gift when you stay with us. Why? Because kids are cool! They totally deserve VIP treatment.",
"unlocked": false
}
]
},
{
"level": 5,
"name": "Loyal Friend",
"requirement": "100 000p",
"description": "You've stuck with us through stays, after works and gym sessions - so we'll stick with you through some of our very best rewards.",
"icon": "/_static/icons/loyaltylevels/loyal-friend.svg",
"benefits": [
{
"name": "Friendly rates",
"description": "Being our friend means youre always scoring the best deal: any day, any place. No secret handshake or booking code needed just dive into the booking and see for yourself.",
"unlocked": true
},
{
"name": "Discount on food",
"description": "Whats more delicious than a discount? As our friend, you get 10-15% off on our restaurant and hotel shop food on weekends and selected holidays and that goes for when youre staying with us and when youre not. So come on, spoil yourself! Get some room service.",
"unlocked": true,
"value": "15%"
},
{
"name": "Free kids mocktail during stay",
"description": "Got the kiddos with you? Thats amazing hello there, buddy! We wanna make sure that kids feel like the VIPs they really are, so every stay they get a refreshing mocktail on us.",
"unlocked": true
},
{
"name": "Late checkout when available",
"description": "We get it late checkout can be a total trip-saver at times. At this point in our friendship, theres no need to rush out of bed: weve got you covered. Check out 1 hour later at no cost and get those last, extra refreshing minutes of sleep.",
"unlocked": true
},
{
"name": "Restaurant voucher",
"description": "Yeah, thats right: for each friendship point-boosting night, you get a voucher to redeem when you dine or drink at our restaurants and bars.",
"unlocked": true,
"value": "€10"
},
{
"name": "Friendship point boost",
"description": "Now heres something pretty sweet: every time you get a boost of friendship points, you get 25% or 50% extra a boost! So you know, start racking up those points on stays, meals and more and youll get a free night in no time.",
"unlocked": true,
"value": "+25%"
},
{
"name": "Early check-in when available",
"description": "Wanna get a head start on your stay? No problem, no problem. Check in 1 hour earlier at no cost and take the highway to relaxation and bliss.",
"unlocked": true
},
{
"name": "Free upgrades when available",
"description": "Let's cut to the chase were taking things up a notch. At this level in our friendship, we'll upgrade your room whenever possible for an even comfier stay. Sounds good, right?",
"unlocked": true
},
{
"name": "2-for-1 breakfast",
"description": "Now, this is a delicious deal: regardless if youre staying with us or not, go grab a breakfast buddy because you're eating two for the price of one! Just make sure to check the details first so everything is in place for your yummy get-together.",
"unlocked": true
},
{
"name": "48hr room guarantee",
"description": "Hush this one's a special treat just for a few of you! So listen up: even if we're fully booked, you're guaranteed a room as long as you book 48 hours in advance. Pretty incredible!",
"unlocked": false
},
{
"name": "Free breakfast always",
"description": "Care for a tasty morning treat? Come on over! You now get to kickstart your day with free breakfast and guess what: that goes regardless if youre staying with us or not.",
"unlocked": false
},
{
"name": "Yearly awesome gift",
"description": "As our Best Friend, you totally deserve princess treatment so weve got an exclusive, annual and pretty awesome gift all lined up. What it is? Well, thats a surprise. No peeking!",
"unlocked": false
},
{
"name": "Kids boost",
"description": "At this friendship level, your kid is our friend, too and that means a special kids boost gift when you stay with us. Why? Because kids are cool! They totally deserve VIP treatment.",
"unlocked": false
}
]
},
{
"level": 6,
"name": "True Friend",
"requirement": "250 000p",
"description": "It doesn't matter if it's peak or off season, you're always there for us. Enjoy even more tailored perks - just the way you like them.",
"icon": "/_static/icons/loyaltylevels/true-friend.svg",
"benefits": [
{
"name": "Friendly rates",
"description": "Being our friend means youre always scoring the best deal: any day, any place. No secret handshake or booking code needed just dive into the booking and see for yourself.",
"unlocked": true
},
{
"name": "Discount on food",
"description": "Whats more delicious than a discount? As our friend, you get 10-15% off on our restaurant and hotel shop food on weekends and selected holidays and that goes for when youre staying with us and when youre not. So come on, spoil yourself! Get some room service.",
"unlocked": true,
"value": "15%"
},
{
"name": "Free kids mocktail during stay",
"description": "Got the kiddos with you? Thats amazing hello there, buddy! We wanna make sure that kids feel like the VIPs they really are, so every stay they get a refreshing mocktail on us.",
"unlocked": true
},
{
"name": "Late checkout when available",
"description": "We get it late checkout can be a total trip-saver at times. At this point in our friendship, theres no need to rush out of bed: weve got you covered. Check out 1 hour later at no cost and get those last, extra refreshing minutes of sleep.",
"unlocked": true
},
{
"name": "Restaurant voucher",
"description": "Yeah, thats right: for each friendship point-boosting night, you get a voucher to redeem when you dine or drink at our restaurants and bars.",
"unlocked": true,
"value": "€15"
},
{
"name": "Friendship point boost",
"description": "Now heres something pretty sweet: every time you get a boost of friendship points, you get 25% or 50% extra a boost! So you know, start racking up those points on stays, meals and more and youll get a free night in no time.",
"unlocked": true,
"value": "+50%"
},
{
"name": "Early check-in when available",
"description": "Wanna get a head start on your stay? No problem, no problem. Check in 1 hour earlier at no cost and take the highway to relaxation and bliss.",
"unlocked": true
},
{
"name": "Free upgrades when available",
"description": "Let's cut to the chase were taking things up a notch. At this level in our friendship, we'll upgrade your room whenever possible for an even comfier stay. Sounds good, right?",
"unlocked": true
},
{
"name": "2-for-1 breakfast",
"description": "Now, this is a delicious deal: regardless if youre staying with us or not, go grab a breakfast buddy because you're eating two for the price of one! Just make sure to check the details first so everything is in place for your yummy get-together.",
"unlocked": false
},
{
"name": "48hr room guarantee",
"description": "Hush this one's a special treat just for a few of you! So listen up: even if we're fully booked, you're guaranteed a room as long as you book 48 hours in advance. Pretty incredible!",
"unlocked": true
},
{
"name": "Free breakfast always",
"description": "Care for a tasty morning treat? Come on over! You now get to kickstart your day with free breakfast and guess what: that goes regardless if youre staying with us or not.",
"unlocked": true
},
{
"name": "Yearly awesome gift",
"description": "As our Best Friend, you totally deserve princess treatment so weve got an exclusive, annual and pretty awesome gift all lined up. What it is? Well, thats a surprise. No peeking!",
"unlocked": false
},
{
"name": "Kids boost",
"description": "At this friendship level, your kid is our friend, too and that means a special kids boost gift when you stay with us. Why? Because kids are cool! They totally deserve VIP treatment.",
"unlocked": false
}
]
},
{
"level": 7,
"name": "Best Friend",
"requirement": "400 000p or 100 nights",
"description": "There are no words for a bond like this, but here's a few anyway: It simply doesn't get any better when it comes to very, very exclusive experiences!",
"icon": "/_static/icons/loyaltylevels/best-friend.svg",
"benefits": [
{
"name": "Friendly rates",
"description": "Being our friend means youre always scoring the best deal: any day, any place. No secret handshake or booking code needed just dive into the booking and see for yourself.",
"unlocked": true
},
{
"name": "Discount on food",
"description": "Whats more delicious than a discount? As our friend, you get 10-15% off on our restaurant and hotel shop food on weekends and selected holidays and that goes for when youre staying with us and when youre not. So come on, spoil yourself! Get some room service.",
"unlocked": true,
"value": "15%"
},
{
"name": "Free kids mocktail during stay",
"description": "Got the kiddos with you? Thats amazing hello there, buddy! We wanna make sure that kids feel like the VIPs they really are, so every stay they get a refreshing mocktail on us.",
"unlocked": true
},
{
"name": "Late checkout when available",
"description": "We get it late checkout can be a total trip-saver at times. At this point in our friendship, theres no need to rush out of bed: weve got you covered. Check out 1 hour later at no cost and get those last, extra refreshing minutes of sleep.",
"unlocked": true
},
{
"name": "Restaurant voucher",
"description": "Yeah, thats right: for each friendship point-boosting night, you get a voucher to redeem when you dine or drink at our restaurants and bars.",
"unlocked": true,
"value": "€20"
},
{
"name": "Friendship point boost",
"description": "Now heres something pretty sweet: every time you get a boost of friendship points, you get 25% or 50% extra a boost! So you know, start racking up those points on stays, meals and more and youll get a free night in no time.",
"unlocked": true,
"value": "+50%"
},
{
"name": "Early check-in when available",
"description": "Wanna get a head start on your stay? No problem, no problem. Check in 1 hour earlier at no cost and take the highway to relaxation and bliss.",
"unlocked": true
},
{
"name": "Free upgrades when available",
"description": "Let's cut to the chase were taking things up a notch. At this level in our friendship, we'll upgrade your room whenever possible for an even comfier stay. Sounds good, right?",
"unlocked": true
},
{
"name": "2-for-1 breakfast",
"description": "Now, this is a delicious deal: regardless if youre staying with us or not, go grab a breakfast buddy because you're eating two for the price of one! Just make sure to check the details first so everything is in place for your yummy get-together.",
"unlocked": false
},
{
"name": "48hr room guarantee",
"description": "Hush this one's a special treat just for a few of you! So listen up: even if we're fully booked, you're guaranteed a room as long as you book 48 hours in advance. Pretty incredible!",
"unlocked": true
},
{
"name": "Free breakfast always",
"description": "Care for a tasty morning treat? Come on over! You now get to kickstart your day with free breakfast and guess what: that goes regardless if youre staying with us or not.",
"unlocked": true
},
{
"name": "Yearly awesome gift",
"description": "As our Best Friend, you totally deserve princess treatment so weve got an exclusive, annual and pretty awesome gift all lined up. What it is? Well, thats a surprise. No peeking!",
"unlocked": true
},
{
"name": "Kids boost",
"description": "At this friendship level, your kid is our friend, too and that means a special kids boost gift when you stay with us. Why? Because kids are cool! They totally deserve VIP treatment.",
"unlocked": true
}
]
}
]
}

View File

@@ -1,538 +0,0 @@
{
"levels": [
{
"level": 1,
"name": "New Friend",
"requirement": "0 p",
"description": "Ystävänämme pääset nauttimaan kaikesta ihanasta, mitä Scandic tarjoaa.",
"icon": "/_static/icons/loyaltylevels/new-friend.svg",
"benefits": [
{
"name": "Ystävähinnat",
"description": "Ystävänämme saat aina parhaan hinnan.",
"unlocked": true
},
{
"name": "Alennus ruoasta",
"description": "Mikä herkullinen etu! Hyödynnä 10%:n alennus hotelliemme ravintoloissa ja shopissa viikonloppuisin. Tarjous on voimassa niin majoittujille kuin hotellitunnelmaa hetkeksi etsiville. Hemmottele siis itseäsi ja löydä tie lähimpään Scandiciin.",
"unlocked": true,
"value": "10 %"
},
{
"name": "Mocktail lapsille maksutta",
"description": "Meillä lapset saavat raikkaan mocktailin majoittumisen yhteydessä.",
"unlocked": true
},
{
"name": "Myöhäinen uloskirjautuminen",
"description": "Kirjaudu ulos tuntia myöhemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": false
},
{
"name": "Ravintolakuponki",
"description": "",
"unlocked": false
},
{
"name": "Enemmän pisteitä",
"description": "",
"unlocked": false
},
{
"name": "Aikainen sisäänkirjautuminen",
"description": "Kirjaudu sisään tuntia aiemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": false
},
{
"name": "Maksuton huoneluokan korotus",
"description": "Saat huoneluokan korotuksen ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": false
},
{
"name": "Aamiainen kaksi yhden hinnalla",
"description": "Saat kaksi aamiaista yhden hinnalla, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "48tunnin huonetakuu",
"description": "Lupaamme sinulle huoneen, vaikka hotelli olisi täyteen buukattu, kunhan varaat sen vähintään 48 tuntia ennen saapumistasi.",
"unlocked": false
},
{
"name": "Aamiainen aina maksutta",
"description": "Tarjoamme sinulle hotelliaamiaisen maksutta, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "Upea vuotuinen lahja",
"description": "Palkitsemme sinut vuosittain mahtavalla ja eksklusiivisella yllätyslahjalla.",
"unlocked": false
},
{
"name": "Kids boost",
"description": "Muistamme lapsia pienellä lahjalla majoituksen yhteydessä.",
"unlocked": false
}
]
},
{
"level": 2,
"name": "Good Friend",
"requirement": "5 000 p",
"description": "Tästä on hyvä jatkaa, yksi yöpyminen ja iloinen yllätys kerrallaan!",
"icon": "/_static/icons/loyaltylevels/good-friend.svg",
"benefits": [
{
"name": "Ystävähinnat",
"description": "Ystävänämme saat aina parhaan hinnan.",
"unlocked": true
},
{
"name": "Alennus ruoasta",
"description": "Ystävänämme saat 10-15 % alennusta ruoasta hotelliemme ravintoloissa ja shopissa viikonloppuisin ja valittuina loma-aikoina.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Mocktail lapsille maksutta",
"description": "Meillä lapset saavat raikkaan mocktailin majoittumisen yhteydessä.",
"unlocked": true
},
{
"name": "Myöhäinen uloskirjautuminen",
"description": "Kirjaudu ulos tuntia myöhemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": false
},
{
"name": "Ravintolakuponki",
"description": "",
"unlocked": false
},
{
"name": "Enemmän pisteitä",
"description": "",
"unlocked": false
},
{
"name": "Aikainen sisäänkirjautuminen",
"description": "Kirjaudu sisään tuntia aiemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": false
},
{
"name": "Maksuton huoneluokan korotus",
"description": "Saat huoneluokan korotuksen ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": false
},
{
"name": "Aamiainen kaksi yhden hinnalla",
"description": "Saat kaksi aamiaista yhden hinnalla, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "48tunnin huonetakuu",
"description": "Lupaamme sinulle huoneen, vaikka hotelli olisi täyteen buukattu, kunhan varaat sen vähintään 48 tuntia ennen saapumistasi.",
"unlocked": false
},
{
"name": "Aamiainen aina maksutta",
"description": "Tarjoamme sinulle hotelliaamiaisen maksutta, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "Upea vuotuinen lahja",
"description": "Palkitsemme sinut vuosittain mahtavalla ja eksklusiivisella yllätyslahjalla.",
"unlocked": false
},
{
"name": "Kids boost",
"description": "Muistamme lapsia pienellä lahjalla majoituksen yhteydessä.",
"unlocked": false
}
]
},
{
"level": 3,
"name": "Close Friend",
"requirement": "10 000 p",
"description": "Nyt etusi vain paranevat, sillä olemmehan jo enemmän kuin hyvän päivän tuttuja.",
"icon": "/_static/icons/loyaltylevels/close-friend.svg",
"benefits": [
{
"name": "Ystävähinnat",
"description": "Ystävänämme saat aina parhaan hinnan.",
"unlocked": true
},
{
"name": "Alennus ruoasta",
"description": "Ystävänämme saat 10-15 % alennusta ruoasta hotelliemme ravintoloissa ja shopissa viikonloppuisin ja valittuina loma-aikoina.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Mocktail lapsille maksutta",
"description": "Meillä lapset saavat raikkaan mocktailin majoittumisen yhteydessä.",
"unlocked": true
},
{
"name": "Myöhäinen uloskirjautuminen",
"description": "Kirjaudu ulos tuntia myöhemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Ravintolakuponki",
"description": "Tässä sinulle makoisa herkku: saat 5 € ravintolakupongin jokaisesta pisteisiin oikeuttavasta yöstä. <a href=''>Katso tästä lisätiedot</a>.",
"unlocked": true,
"value": "5 €"
},
{
"name": "Enemmän pisteitä",
"description": "",
"unlocked": false
},
{
"name": "Aikainen sisäänkirjautuminen",
"description": "Kirjaudu sisään tuntia aiemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": false
},
{
"name": "Maksuton huoneluokan korotus",
"description": "Saat huoneluokan korotuksen ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": false
},
{
"name": "Aamiainen kaksi yhden hinnalla",
"description": "Saat kaksi aamiaista yhden hinnalla, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "48tunnin huonetakuu",
"description": "Lupaamme sinulle huoneen, vaikka hotelli olisi täyteen buukattu, kunhan varaat sen vähintään 48 tuntia ennen saapumistasi.",
"unlocked": false
},
{
"name": "Aamiainen aina maksutta",
"description": "Tarjoamme sinulle hotelliaamiaisen maksutta, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "Upea vuotuinen lahja",
"description": "Palkitsemme sinut vuosittain mahtavalla ja eksklusiivisella yllätyslahjalla.",
"unlocked": false
},
{
"name": "Kids boost",
"description": "Muistamme lapsia pienellä lahjalla majoituksen yhteydessä.",
"unlocked": false
}
]
},
{
"level": 4,
"name": "Dear Friend",
"requirement": "25 000 p",
"description": "Kippis syventyvälle ystävyydellemme. Nyt pääset nauttimaan liudasta uusia etuja.",
"icon": "/_static/icons/loyaltylevels/dear-friend.svg",
"benefits": [
{
"name": "Ystävähinnat",
"description": "Ystävänämme saat aina parhaan hinnan.",
"unlocked": true
},
{
"name": "Alennus ruoasta",
"description": "Ystävänämme saat 10-15 % alennusta ruoasta hotelliemme ravintoloissa ja shopissa viikonloppuisin ja valittuina loma-aikoina.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Mocktail lapsille maksutta",
"description": "Meillä lapset saavat raikkaan mocktailin majoittumisen yhteydessä.",
"unlocked": true
},
{
"name": "Myöhäinen uloskirjautuminen",
"description": "Kirjaudu ulos tuntia myöhemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Ravintolakuponki",
"description": "Ystävyytemme on entistä makeampaa! Saat 7,50 € ravintolakupongin jokaisesta pisteisiin oikeuttavasta yöstä. Huippua, vai mitä?",
"unlocked": true,
"value": "7,50 €"
},
{
"name": "Enemmän pisteitä",
"description": "Tässä lisäboostia sinulle: saat 25 % enemmän pisteitä joka kerta kun ansaitset pisteitä! Pistä tuulemaan ja haali pisteet yöpymisistä, aterioista ja muusta, niin pääset nauttimaan palkintoyöstä tuossa tuokiossa.",
"unlocked": true,
"value": "25 %"
},
{
"name": "Aikainen sisäänkirjautuminen",
"description": "Kirjaudu sisään tuntia aiemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Maksuton huoneluokan korotus",
"description": "Saat huoneluokan korotuksen ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": false
},
{
"name": "Aamiainen kaksi yhden hinnalla",
"description": "Saat kaksi aamiaista yhden hinnalla, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "48tunnin huonetakuu",
"description": "Lupaamme sinulle huoneen, vaikka hotelli olisi täyteen buukattu, kunhan varaat sen vähintään 48 tuntia ennen saapumistasi.",
"unlocked": false
},
{
"name": "Aamiainen aina maksutta",
"description": "Tarjoamme sinulle hotelliaamiaisen maksutta, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "Upea vuotuinen lahja",
"description": "Palkitsemme sinut vuosittain mahtavalla ja eksklusiivisella yllätyslahjalla.",
"unlocked": false
},
{
"name": "Kids boost",
"description": "Muistamme lapsia pienellä lahjalla majoituksen yhteydessä.",
"unlocked": false
}
]
},
{
"level": 5,
"name": "Loyal Friend",
"requirement": "100 000 p",
"description": "Haluamme panostaa ystävyyteemme myös jatkossa ja annammekin sinulle kasan uusia, ihania etuja.",
"icon": "/_static/icons/loyaltylevels/loyal-friend.svg",
"benefits": [
{
"name": "Ystävähinnat",
"description": "Ystävänämme saat aina parhaan hinnan.",
"unlocked": true
},
{
"name": "Alennus ruoasta",
"description": "Ystävänämme saat 10-15 % alennusta ruoasta hotelliemme ravintoloissa ja shopissa viikonloppuisin ja valittuina loma-aikoina.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Mocktail lapsille maksutta",
"description": "Meillä lapset saavat raikkaan mocktailin majoittumisen yhteydessä.",
"unlocked": true
},
{
"name": "Myöhäinen uloskirjautuminen",
"description": "Kirjaudu ulos tuntia myöhemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Ravintolakuponki",
"description": "Saat 10 € ravintolakupongin jokaisesta pisteisiin oikeuttavasta yöstä. Se tuo varmasti iloa seuraavaan herkutteluhetkeen.",
"unlocked": true,
"value": "10 €"
},
{
"name": "Enemmän pisteitä",
"description": "Tässä lisäboostia sinulle: saat 25 % enemmän pisteitä joka kerta kun ansaitset pisteitä! Pistä tuulemaan ja haali pisteet yöpymisistä, aterioista ja muusta, niin pääset nauttimaan palkintoyöstä tuossa tuokiossa.",
"unlocked": true,
"value": "25 %"
},
{
"name": "Aikainen sisäänkirjautuminen",
"description": "Kirjaudu sisään tuntia aiemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Maksuton huoneluokan korotus",
"description": "Saat huoneluokan korotuksen ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Aamiainen kaksi yhden hinnalla",
"description": "Saat kaksi aamiaista yhden hinnalla, majoitut meillä tai et.",
"unlocked": true
},
{
"name": "48tunnin huonetakuu",
"description": "Lupaamme sinulle huoneen, vaikka hotelli olisi täyteen buukattu, kunhan varaat sen vähintään 48 tuntia ennen saapumistasi.",
"unlocked": false
},
{
"name": "Aamiainen aina maksutta",
"description": "Tarjoamme sinulle hotelliaamiaisen maksutta, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "Upea vuotuinen lahja",
"description": "Palkitsemme sinut vuosittain mahtavalla ja eksklusiivisella yllätyslahjalla.",
"unlocked": false
},
{
"name": "Kids boost",
"description": "Muistamme lapsia pienellä lahjalla majoituksen yhteydessä.",
"unlocked": false
}
]
},
{
"level": 6,
"name": "True Friend",
"requirement": "250 000 p",
"description": "Tosiystävän tapaan haluamme palkita sinua entistä yksilöllisemmillä eduilla.",
"icon": "/_static/icons/loyaltylevels/true-friend.svg",
"benefits": [
{
"name": "Ystävähinnat",
"description": "Ystävänämme saat aina parhaan hinnan.",
"unlocked": true
},
{
"name": "Alennus ruoasta",
"description": "Ystävänämme saat 10-15 % alennusta ruoasta hotelliemme ravintoloissa ja shopissa viikonloppuisin ja valittuina loma-aikoina.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Mocktail lapsille maksutta",
"description": "Meillä lapset saavat raikkaan mocktailin majoittumisen yhteydessä.",
"unlocked": true
},
{
"name": "Myöhäinen uloskirjautuminen",
"description": "Kirjaudu ulos tuntia myöhemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Ravintolakuponki",
"description": "Ystävyytemme vain vahvistuu ja nyt saat 15 € ravintolakupongin jokaisesta pisteisiin oikeuttavasta yöstä. Kuinkahan paljon herkkuja sillä saisi?",
"unlocked": true,
"value": "15 €"
},
{
"name": "Enemmän pisteitä",
"description": "Saat 25 % tai 50 % enemmän pisteitä joka kerta kun ansaitset pisteitä!.",
"unlocked": true,
"value": "50 %"
},
{
"name": "Aikainen sisäänkirjautuminen",
"description": "Kirjaudu sisään tuntia aiemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Maksuton huoneluokan korotus",
"description": "Saat huoneluokan korotuksen ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Aamiainen kaksi yhden hinnalla",
"description": "Saat kaksi aamiaista yhden hinnalla, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "48tunnin huonetakuu",
"description": "Lupaamme sinulle huoneen, vaikka hotelli olisi täyteen buukattu, kunhan varaat sen vähintään 48 tuntia ennen saapumistasi.",
"unlocked": true
},
{
"name": "Aamiainen aina maksutta",
"description": "Tarjoamme sinulle hotelliaamiaisen maksutta, majoitut meillä tai et.",
"unlocked": true
},
{
"name": "Upea vuotuinen lahja",
"description": "Palkitsemme sinut vuosittain mahtavalla ja eksklusiivisella yllätyslahjalla.",
"unlocked": false
},
{
"name": "Kids boost",
"description": "Muistamme lapsia pienellä lahjalla majoituksen yhteydessä.",
"unlocked": false
}
]
},
{
"level": 7,
"name": "Best Friend",
"requirement": "400 000 p tai 100 yötä",
"description": "Koska sanat eivät riitä kiittämään ystävyydestämme, pääset nyt käsiksi kaikkein eksklusiivisimpiin elämyksiin.",
"icon": "/_static/icons/loyaltylevels/best-friend.svg",
"benefits": [
{
"name": "Ystävähinnat",
"description": "Ystävänämme saat aina parhaan hinnan.",
"unlocked": true
},
{
"name": "Alennus ruoasta",
"description": "Ystävänämme saat 10-15 % alennusta ruoasta hotelliemme ravintoloissa ja shopissa viikonloppuisin ja valittuina loma-aikoina.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Mocktail lapsille maksutta",
"description": "Meillä lapset saavat raikkaan mocktailin majoittumisen yhteydessä.",
"unlocked": true
},
{
"name": "Myöhäinen uloskirjautuminen",
"description": "Kirjaudu ulos tuntia myöhemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Ravintolakuponki",
"description": "Ystävänämme saat ravintolakupongin jokaisesta pisteisiin oikeuttavasta yöstä.",
"unlocked": true,
"value": "20 €"
},
{
"name": "Enemmän pisteitä",
"description": "Saat 25 % tai 50 % enemmän pisteitä joka kerta kun ansaitset pisteitä!.",
"unlocked": true,
"value": "50 %"
},
{
"name": "Aikainen sisäänkirjautuminen",
"description": "Kirjaudu sisään tuntia aiemmin ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Maksuton huoneluokan korotus",
"description": "Saat huoneluokan korotuksen ilman lisämaksua. Saatavilla varaustilanteen mukaan.",
"unlocked": true
},
{
"name": "Aamiainen kaksi yhden hinnalla",
"description": "Saat kaksi aamiaista yhden hinnalla, majoitut meillä tai et.",
"unlocked": false
},
{
"name": "48tunnin huonetakuu",
"description": "Lupaamme sinulle huoneen, vaikka hotelli olisi täyteen buukattu, kunhan varaat sen vähintään 48 tuntia ennen saapumistasi.",
"unlocked": true
},
{
"name": "Aamiainen aina maksutta",
"description": "Tarjoamme sinulle hotelliaamiaisen maksutta, majoitut meillä tai et.",
"unlocked": true
},
{
"name": "Upea vuotuinen lahja",
"description": "Palkitsemme sinut vuosittain mahtavalla ja eksklusiivisella yllätyslahjalla.",
"unlocked": true
},
{
"name": "Kids boost",
"description": "Muistamme lapsia pienellä lahjalla majoituksen yhteydessä.",
"unlocked": true
}
]
}
]
}

View File

@@ -1,538 +0,0 @@
{
"levels": [
{
"level": 1,
"name": "New Friend",
"requirement": "0p",
"description": "Dette er starten på noe vakkert: Som en New Friend, gjør deg klar for en reise fylt av fine Scandic-opplevelser.",
"icon": "/_static/icons/loyaltylevels/new-friend.svg",
"benefits": [
{
"name": "Vennlige priser",
"description": "Å være vennen vår betyr at du alltid får det beste tilbudet: Når som helst, hvor som helst. Det er ikke nødvendig med noe hemmelig håndtrykk eller bestillingskode bare dykk inn i bestillingene og se selv.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Nam! Nyt en smakfull 10 % rabatt i restauranten og shoppen vår i helgene. Dette tilbudet gjelder enten du er gjesten vår over natten eller bare kommer innom for en matbit. Så, sett i gang, unn deg selv noe godt.",
"unlocked": true,
"value": "10 %"
},
{
"name": "Gratis barne-mocktail under oppholdet",
"description": "Har du barna med deg? Det er fantastisk hei der, kompis! Vi vil sørge for at ungene føler seg som de VIP-ene de faktisk er, og spanderer en forfriskende mocktail på dem ved hvert opphold.",
"unlocked": true
},
{
"name": "Sen utsjekking når tilgjengelig",
"description": "Vi skjønner sen utsjekkingen kan til tider være redningen for en reise. På dette punktet i vennskapet vårt, trenger du ikke skynde deg ut av sengen: Vi går god for deg, så sjekk ut én time senere kostnadsfritt, og få de siste, ekstra oppfriskende minuttene med søvn!",
"unlocked": false
},
{
"name": "Restaurantkupong",
"description": "",
"unlocked": false
},
{
"name": "Friendsboost",
"description": "",
"unlocked": false
},
{
"name": "Tidlig innsjekk når tilgjengelig",
"description": "Vil du få et forsprang på oppholdet ditt? Det er ikke noe problem. Sjekk inn én time tidligere kostnadsfritt, og ta snarveien til avslapning og lykke.",
"unlocked": false
},
{
"name": "Gratis oppgraderinger (når tilgjengelig)",
"description": "La oss ikke gå som katten rundt grøten vi tar ting opp et nivå. På dette nivået i vennskapet vårt vil vi oppgradere rommet ditt når det er mulig, for et enda mer komfortabelt opphold. Høres ikke det bra ut?",
"unlocked": false
},
{
"name": "2-for-1 frokost",
"description": "Dette er virkelig et deilig tilbud: Uavhengig om du bor hos oss eller ikke, finn deg en frokostkompis, og dere kan spise for prisen av én! Bare sørg for å sjekke detaljene først, slik at alt er på plass for deres smakfulle sammenkomst.",
"unlocked": false
},
{
"name": "Romgaranti i 48timer",
"description": "Hysj dette er en spesiell godbit, kun for noen få av dere! Så merk deg: Selv om vi er fullbooket, er du garantert et rom så lenge du bestiller 48 timer på forhånd. Ganske utrolig!",
"unlocked": false
},
{
"name": "Gratis frokost alltid",
"description": "Har du lyst på en smakfull morgengodbit? Bare kom innom! Nå kan du sparke i gang dagen din med gratis frokost og gjett hva: Det gjelder uavhengig om du bor hos oss eller ikke.",
"unlocked": false
},
{
"name": "Årlig fantastisk gave",
"description": "Som vår Best Friend fortjener du absolutt spesialbehandling, så vi har en eksklusiv, årlig, og ganske fantastisk gave klar til deg. Hva det er? Vel, det er en overraskelse. Ingen sniktitting!",
"unlocked": false
},
{
"name": "Boost for barn",
"description": "På dette vennskapsnivået er barnet ditt også vår venn og det betyr en spesiell boost-gave for barn når du bor hos oss. Hvorfor? Fordi barn er kule! De fortjener virkelig VIP-behandling.",
"unlocked": false
}
]
},
{
"level": 2,
"name": "Good Friend",
"requirement": "5 000p",
"description": "Du har vært her mye i det siste! Og ærlig talt føler vi at vi er på bølgelengde ett behagelig opphold og én morsom overraskelse om gangen.",
"icon": "/_static/icons/loyaltylevels/good-friend.svg",
"benefits": [
{
"name": "Vennlige priser",
"description": "Å være vennen vår betyr at du alltid får det beste tilbudet: Når som helst, hvor som helst. Det er ikke nødvendig med noe hemmelig håndtrykk eller bestillingskode bare dykk inn i bestillingene og se selv.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Hva er bedre enn rabatt? Som vår Friend får du 10-15 % rabatt på mat i vår restaurant og shop i helger og utvalgte ferier og helligdager. Det gjelder uansett om du bor hos oss eller ikke. Så kom igjen, skjem deg bort.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Gratis barne-mocktail under oppholdet",
"description": "Har du barna med deg? Det er fantastisk hei der, kompis! Vi vil sørge for at ungene føler seg som de VIP-ene de faktisk er, og spanderer en forfriskende mocktail på dem ved hvert opphold.",
"unlocked": true
},
{
"name": "Sen utsjekking når tilgjengelig",
"description": "Vi skjønner sen utsjekkingen kan til tider være redningen for en reise. På dette punktet i vennskapet vårt, trenger du ikke skynde deg ut av sengen: Vi går god for deg, så sjekk ut én time senere kostnadsfritt, og få de siste, ekstra oppfriskende minuttene med søvn!",
"unlocked": false
},
{
"name": "Restaurantkupong",
"description": "",
"unlocked": false
},
{
"name": "Friendsboost",
"description": "",
"unlocked": false
},
{
"name": "Tidlig innsjekk når tilgjengelig",
"description": "Vil du få et forsprang på oppholdet ditt? Det er ikke noe problem. Sjekk inn én time tidligere kostnadsfritt, og ta snarveien til avslapning og lykke.",
"unlocked": false
},
{
"name": "Gratis oppgraderinger (når tilgjengelig)",
"description": "La oss ikke gå som katten rundt grøten vi tar ting opp et nivå. På dette nivået i vennskapet vårt vil vi oppgradere rommet ditt når det er mulig, for et enda mer komfortabelt opphold. Høres ikke det bra ut?",
"unlocked": false
},
{
"name": "2-for-1 frokost",
"description": "Dette er virkelig et deilig tilbud: Uavhengig om du bor hos oss eller ikke, finn deg en frokostkompis, og dere kan spise for prisen av én! Bare sørg for å sjekke detaljene først, slik at alt er på plass for deres smakfulle sammenkomst.",
"unlocked": false
},
{
"name": "Romgaranti i 48timer",
"description": "Hysj dette er en spesiell godbit, kun for noen få av dere! Så merk deg: Selv om vi er fullbooket, er du garantert et rom så lenge du bestiller 48 timer på forhånd. Ganske utrolig!",
"unlocked": false
},
{
"name": "Gratis frokost alltid",
"description": "Har du lyst på en smakfull morgengodbit? Bare kom innom! Nå kan du sparke i gang dagen din med gratis frokost og gjett hva: Det gjelder uavhengig om du bor hos oss eller ikke.",
"unlocked": false
},
{
"name": "Årlig fantastisk gave",
"description": "Som vår Best Friend fortjener du absolutt spesialbehandling, så vi har en eksklusiv, årlig, og ganske fantastisk gave klar til deg. Hva det er? Vel, det er en overraskelse. Ingen sniktitting!",
"unlocked": false
},
{
"name": "Boost for barn",
"description": "På dette vennskapsnivået er barnet ditt også vår venn og det betyr en spesiell boost-gave for barn når du bor hos oss. Hvorfor? Fordi barn er kule! De fortjener virkelig VIP-behandling.",
"unlocked": false
}
]
},
{
"level": 3,
"name": "Close Friend",
"requirement": "10 000p",
"description": "Nå er det seriøst: Vi begynner virkelig å bli kjent med hverandre, noe som gjør Scandic-opplevelsen din mye mer personlig.",
"icon": "/_static/icons/loyaltylevels/close-friend.svg",
"benefits": [
{
"name": "Vennlige priser",
"description": "Å være vennen vår betyr at du alltid får det beste tilbudet: Når som helst, hvor som helst. Det er ikke nødvendig med noe hemmelig håndtrykk eller bestillingskode bare dykk inn i bestillingene og se selv.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Hva er bedre enn en rabatt? Som vår venn får du 10-15 % rabatt på mat i restauranten og shoppen vår i helger og på utvalgte helligdager og det gjelder både når du bor hos oss og når du ikke har det. Så kom igjen, skjem deg selv bort.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Gratis barne-mocktail under oppholdet",
"description": "Har du barna med deg? Det er fantastisk hei der, kompis! Vi vil sørge for at ungene føler seg som de VIP-ene de faktisk er, og spanderer en forfriskende mocktail på dem ved hvert opphold.",
"unlocked": true
},
{
"name": "Sen utsjekking når tilgjengelig",
"description": "Vi skjønner sen utsjekkingen kan til tider være redningen for en reise. På dette punktet i vennskapet vårt, trenger du ikke skynde deg ut av sengen: Vi går god for deg, så sjekk ut én time senere kostnadsfritt, og få de siste, ekstra oppfriskende minuttene med søvn!",
"unlocked": true
},
{
"name": "Restaurantkupong",
"description": "Så, her er godbiten: For hver vennskapspoenggivende natt du bor hos oss, gir vi deg en restaurantkupong på 50 NOK.",
"unlocked": true,
"value": "50 NOK"
},
{
"name": "Friendsboost",
"description": "",
"unlocked": false
},
{
"name": "Tidlig innsjekk når tilgjengelig",
"description": "Vil du få et forsprang på oppholdet ditt? Det er ikke noe problem. Sjekk inn én time tidligere kostnadsfritt, og ta snarveien til avslapning og lykke.",
"unlocked": false
},
{
"name": "Gratis oppgraderinger (når tilgjengelig)",
"description": "La oss ikke gå som katten rundt grøten vi tar ting opp et nivå. På dette nivået i vennskapet vårt vil vi oppgradere rommet ditt når det er mulig, for et enda mer komfortabelt opphold. Høres ikke det bra ut?",
"unlocked": false
},
{
"name": "2-for-1 frokost",
"description": "Dette er virkelig et deilig tilbud: Uavhengig om du bor hos oss eller ikke, finn deg en frokostkompis, og dere kan spise for prisen av én! Bare sørg for å sjekke detaljene først, slik at alt er på plass for deres smakfulle sammenkomst.",
"unlocked": false
},
{
"name": "Romgaranti i 48timer",
"description": "Hysj dette er en spesiell godbit, kun for noen få av dere! Så merk deg: Selv om vi er fullbooket, er du garantert et rom så lenge du bestiller 48 timer på forhånd. Ganske utrolig!",
"unlocked": false
},
{
"name": "Gratis frokost alltid",
"description": "Har du lyst på en smakfull morgengodbit? Bare kom innom! Nå kan du sparke i gang dagen din med gratis frokost og gjett hva: Det gjelder uavhengig om du bor hos oss eller ikke.",
"unlocked": false
},
{
"name": "Årlig fantastisk gave",
"description": "Som vår Best Friend fortjener du absolutt spesialbehandling, så vi har en eksklusiv, årlig, og ganske fantastisk gave klar til deg. Hva det er? Vel, det er en overraskelse. Ingen sniktitting!",
"unlocked": false
},
{
"name": "Boost for barn",
"description": "På dette vennskapsnivået er barnet ditt også vår venn og det betyr en spesiell boost-gave for barn når du bor hos oss. Hvorfor? Fordi barn er kule! De fortjener virkelig VIP-behandling.",
"unlocked": false
}
]
},
{
"level": 4,
"name": "Dear Friend",
"requirement": "25 000p",
"description": "Hurra for oss! Dette ser ut til å gå i retning av venner for livet og det kommer med tilgang til mye mer av Scandic.",
"icon": "/_static/icons/loyaltylevels/dear-friend.svg",
"benefits": [
{
"name": "Vennlige priser",
"description": "Å være vennen vår betyr at du alltid får det beste tilbudet: Når som helst, hvor som helst. Det er ikke nødvendig med noe hemmelig håndtrykk eller bestillingskode bare dykk inn i bestillingene og se selv.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Hva er bedre enn rabatt? Som vår Friend får du 10-15 % rabatt på mat i vår restaurant og shop i helger og utvalgte ferier og helligdager. Det gjelder uansett om du bor hos oss eller ikke. Så kom igjen, skjem deg bort.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Gratis barne-mocktail under oppholdet",
"description": "Har du barna med deg? Det er fantastisk hei der, kompis! Vi vil sørge for at ungene føler seg som de VIP-ene de faktisk er, og spanderer en forfriskende mocktail på dem ved hvert opphold.",
"unlocked": true
},
{
"name": "Sen utsjekking når tilgjengelig",
"description": "Vi skjønner sen utsjekkingen kan til tider være redningen for en reise. På dette punktet i vennskapet vårt, trenger du ikke skynde deg ut av sengen: Vi går god for deg, så sjekk ut én time senere kostnadsfritt, og få de siste, ekstra oppfriskende minuttene med søvn!",
"unlocked": true
},
{
"name": "Restaurantkupong",
"description": "Vennskapet vårt ble nettopp enda bedre! Nyt en restaurantkupong på 75 NOK for hver poenggivende natt. Bra, ikke sant?",
"unlocked": true,
"value": "75 NOK"
},
{
"name": "Friendsboost",
"description": "Her har du noe veldig bra: Hver gang du øker antall vennskapspoeng, får du 25 % ekstra ekstra på det ekstra! Så, begynn å samle poeng på opphold, måltider og mer, og du vil veldig snart få et gratis opphold.",
"unlocked": true,
"value": "25 %"
},
{
"name": "Tidlig innsjekk når tilgjengelig",
"description": "Vil du få et forsprang på oppholdet ditt? Det er ikke noe problem. Sjekk inn én time tidligere kostnadsfritt, og ta snarveien til avslapning og lykke.",
"unlocked": true
},
{
"name": "Gratis oppgraderinger (når tilgjengelig)",
"description": "La oss ikke gå som katten rundt grøten vi tar ting opp et nivå. På dette nivået i vennskapet vårt vil vi oppgradere rommet ditt når det er mulig, for et enda mer komfortabelt opphold. Høres ikke det bra ut?",
"unlocked": false
},
{
"name": "2-for-1 frokost",
"description": "Dette er virkelig et deilig tilbud: Uavhengig om du bor hos oss eller ikke, finn deg en frokostkompis, og dere kan spise for prisen av én! Bare sørg for å sjekke detaljene først, slik at alt er på plass for deres smakfulle sammenkomst.",
"unlocked": false
},
{
"name": "Romgaranti i 48timer",
"description": "Hysj dette er en spesiell godbit, kun for noen få av dere! Så merk deg: Selv om vi er fullbooket, er du garantert et rom så lenge du bestiller 48 timer på forhånd. Ganske utrolig!",
"unlocked": false
},
{
"name": "Gratis frokost alltid",
"description": "Har du lyst på en smakfull morgengodbit? Bare kom innom! Nå kan du sparke i gang dagen din med gratis frokost og gjett hva: Det gjelder uavhengig om du bor hos oss eller ikke.",
"unlocked": false
},
{
"name": "Årlig fantastisk gave",
"description": "Som vår Best Friend fortjener du absolutt spesialbehandling, så vi har en eksklusiv, årlig, og ganske fantastisk gave klar til deg. Hva det er? Vel, det er en overraskelse. Ingen sniktitting!",
"unlocked": false
},
{
"name": "Boost for barn",
"description": "På dette vennskapsnivået er barnet ditt også vår venn og det betyr en spesiell boost-gave for barn når du bor hos oss. Hvorfor? Fordi barn er kule! De fortjener virkelig VIP-behandling.",
"unlocked": false
}
]
},
{
"level": 5,
"name": "Loyal Friend",
"requirement": "100 000p",
"description": "Du har vært lojal mot oss gjennom opphold, happy hours og treningsøkter så vi er der for deg med noen av de aller beste fordelene våre.",
"icon": "/_static/icons/loyaltylevels/loyal-friend.svg",
"benefits": [
{
"name": "Vennlige priser",
"description": "Å være vennen vår betyr at du alltid får det beste tilbudet: Når som helst, hvor som helst. Det er ikke nødvendig med noe hemmelig håndtrykk eller bestillingskode bare dykk inn i bestillingene og se selv.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Hva er bedre enn rabatt? Som vår Friend får du 10-15 % rabatt på mat i vår restaurant og shop i helger og utvalgte ferier og helligdager. Det gjelder uansett om du bor hos oss eller ikke. Så kom igjen, skjem deg bort.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Gratis barne-mocktail under oppholdet",
"description": "Har du barna med deg? Det er fantastisk hei der, kompis! Vi vil sørge for at ungene føler seg som de VIP-ene de faktisk er, og spanderer en forfriskende mocktail på dem ved hvert opphold.",
"unlocked": true
},
{
"name": "Sen utsjekking når tilgjengelig",
"description": "Vi skjønner sen utsjekkingen kan til tider være redningen for en reise. På dette punktet i vennskapet vårt, trenger du ikke skynde deg ut av sengen: Vi går god for deg, så sjekk ut én time senere kostnadsfritt, og få de siste, ekstra oppfriskende minuttene med søvn!",
"unlocked": true
},
{
"name": "Restaurantkupong",
"description": "Vi gir deg en restaurantkupong på 100 NOK for hver vennskapspoenggivende natt du bor. Det kan gi deg et fjell av croissanter! Herlig, hva?",
"unlocked": true,
"value": "100 NOK"
},
{
"name": "Friendsboost",
"description": "Her har du noe veldig bra: Hver gang du øker antall vennskapspoeng, får du 25 % ekstra ekstra på det ekstra! Så, begynn å samle poeng på opphold, måltider og mer, og du vil veldig snart få et gratis opphold.",
"unlocked": true,
"value": "25 %"
},
{
"name": "Tidlig innsjekk når tilgjengelig",
"description": "Vil du få et forsprang på oppholdet ditt? Det er ikke noe problem. Sjekk inn én time tidligere kostnadsfritt, og ta snarveien til avslapning og lykke.",
"unlocked": true
},
{
"name": "Gratis oppgraderinger (når tilgjengelig)",
"description": "La oss ikke gå som katten rundt grøten vi tar ting opp et nivå. På dette nivået i vennskapet vårt vil vi oppgradere rommet ditt når det er mulig, for et enda mer komfortabelt opphold. Høres ikke det bra ut?",
"unlocked": true
},
{
"name": "2-for-1 frokost",
"description": "Dette er virkelig et deilig tilbud: Uavhengig om du bor hos oss eller ikke, finn deg en frokostkompis, og dere kan spise for prisen av én! Bare sørg for å sjekke detaljene først, slik at alt er på plass for deres smakfulle sammenkomst.",
"unlocked": true
},
{
"name": "Romgaranti i 48timer",
"description": "Hysj dette er en spesiell godbit, kun for noen få av dere! Så merk deg: Selv om vi er fullbooket, er du garantert et rom så lenge du bestiller 48 timer på forhånd. Ganske utrolig!",
"unlocked": false
},
{
"name": "Gratis frokost alltid",
"description": "Har du lyst på en smakfull morgengodbit? Bare kom innom! Nå kan du sparke i gang dagen din med gratis frokost og gjett hva: Det gjelder uavhengig om du bor hos oss eller ikke.",
"unlocked": false
},
{
"name": "Årlig fantastisk gave",
"description": "Som vår Best Friend fortjener du absolutt spesialbehandling, så vi har en eksklusiv, årlig, og ganske fantastisk gave klar til deg. Hva det er? Vel, det er en overraskelse. Ingen sniktitting!",
"unlocked": false
},
{
"name": "Boost for barn",
"description": "På dette vennskapsnivået er barnet ditt også vår venn og det betyr en spesiell boost-gave for barn når du bor hos oss. Hvorfor? Fordi barn er kule! De fortjener virkelig VIP-behandling.",
"unlocked": false
}
]
},
{
"level": 6,
"name": "True Friend",
"requirement": "250 000p",
"description": "Det spiller ingen rolle om det er høysesong eller lavsesong, du er alltid der for oss. Nyt enda flere skreddersydde fordeler akkurat slik du liker dem.",
"icon": "/_static/icons/loyaltylevels/true-friend.svg",
"benefits": [
{
"name": "Vennlige priser",
"description": "Å være vennen vår betyr at du alltid får det beste tilbudet: Når som helst, hvor som helst. Det er ikke nødvendig med noe hemmelig håndtrykk eller bestillingskode bare dykk inn i bestillingene og se selv.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Hva er bedre enn rabatt? Som vår Friend får du 10-15 % rabatt på mat i vår restaurant og shop i helger og utvalgte ferier og helligdager. Det gjelder uansett om du bor hos oss eller ikke. Så kom igjen, skjem deg bort.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Gratis barne-mocktail under oppholdet",
"description": "Har du barna med deg? Det er fantastisk hei der, kompis! Vi vil sørge for at ungene føler seg som de VIP-ene de faktisk er, og spanderer en forfriskende mocktail på dem ved hvert opphold.",
"unlocked": true
},
{
"name": "Sen utsjekking når tilgjengelig",
"description": "Vi skjønner sen utsjekkingen kan til tider være redningen for en reise. På dette punktet i vennskapet vårt, trenger du ikke skynde deg ut av sengen: Vi går god for deg, så sjekk ut én time senere kostnadsfritt, og få de siste, ekstra oppfriskende minuttene med søvn!",
"unlocked": true
},
{
"name": "Restaurantkupong",
"description": "Dette går virkelig flott! Nå vil hver vennskapspoenggivende natt gi deg en restaurantkupong på 150 NOK. Det vil garantert være nyttig for den neste lekre frokosten eller middagsdaten din!",
"unlocked": true,
"value": "150 NOK"
},
{
"name": "Friendsboost",
"description": "Gled deg! Hver gang du tjener nye Friends-poeng får du 25 % eller 50 % ekstra poeng som en superboost! Begynn å tjene poeng ved å bo og spise hos oss, og du vil få en bonusnatt før du aner det.",
"unlocked": true,
"value": "50 %"
},
{
"name": "Tidlig innsjekk når tilgjengelig",
"description": "Vil du få et forsprang på oppholdet ditt? Det er ikke noe problem. Sjekk inn én time tidligere kostnadsfritt, og ta snarveien til avslapning og lykke.",
"unlocked": true
},
{
"name": "Gratis oppgraderinger (når tilgjengelig)",
"description": "La oss ikke gå som katten rundt grøten vi tar ting opp et nivå. På dette nivået i vennskapet vårt vil vi oppgradere rommet ditt når det er mulig, for et enda mer komfortabelt opphold. Høres ikke det bra ut?",
"unlocked": true
},
{
"name": "2-for-1 frokost",
"description": "Dette er virkelig et deilig tilbud: Uavhengig om du bor hos oss eller ikke, finn deg en frokostkompis, og dere kan spise for prisen av én! Bare sørg for å sjekke detaljene først, slik at alt er på plass for deres smakfulle sammenkomst.",
"unlocked": false
},
{
"name": "Romgaranti i 48timer",
"description": "Hysj dette er en spesiell godbit, kun for noen få av dere! Så merk deg: Selv om vi er fullbooket, er du garantert et rom så lenge du bestiller 48 timer på forhånd. Ganske utrolig!",
"unlocked": true
},
{
"name": "Gratis frokost alltid",
"description": "Har du lyst på en smakfull morgengodbit? Bare kom innom! Nå kan du sparke i gang dagen din med gratis frokost og gjett hva: Det gjelder uavhengig om du bor hos oss eller ikke.",
"unlocked": true
},
{
"name": "Årlig fantastisk gave",
"description": "Som vår Best Friend fortjener du absolutt spesialbehandling, så vi har en eksklusiv, årlig, og ganske fantastisk gave klar til deg. Hva det er? Vel, det er en overraskelse. Ingen sniktitting!",
"unlocked": false
},
{
"name": "Boost for barn",
"description": "På dette vennskapsnivået er barnet ditt også vår venn og det betyr en spesiell boost-gave for barn når du bor hos oss. Hvorfor? Fordi barn er kule! De fortjener virkelig VIP-behandling.",
"unlocked": false
}
]
},
{
"level": 7,
"name": "Best Friend",
"requirement": "400 000p eller 100 netter",
"description": "Det finnes ikke ord for et bånd som dette, men vi gjør et forsøk allikevel: Det blir bare ikke bedre når det gjelder svært eksklusive opplevelser!",
"icon": "/_static/icons/loyaltylevels/best-friend.svg",
"benefits": [
{
"name": "Vennlige priser",
"description": "Å være vennen vår betyr at du alltid får det beste tilbudet: Når som helst, hvor som helst. Det er ikke nødvendig med noe hemmelig håndtrykk eller bestillingskode bare dykk inn i bestillingene og se selv.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Hva er bedre enn rabatt? Som vår Friend får du 10-15 % rabatt på mat i vår restaurant og shop i helger og utvalgte ferier og helligdager. Det gjelder uansett om du bor hos oss eller ikke. Så kom igjen, skjem deg bort.",
"unlocked": true,
"value": "15 %"
},
{
"name": "Gratis barne-mocktail under oppholdet",
"description": "Har du barna med deg? Det er fantastisk hei der, kompis! Vi vil sørge for at ungene føler seg som de VIP-ene de faktisk er, og spanderer en forfriskende mocktail på dem ved hvert opphold.",
"unlocked": true
},
{
"name": "Sen utsjekking når tilgjengelig",
"description": "Vi skjønner sen utsjekkingen kan til tider være redningen for en reise. På dette punktet i vennskapet vårt, trenger du ikke skynde deg ut av sengen: Vi går god for deg, så sjekk ut én time senere kostnadsfritt, og få de siste, ekstra oppfriskende minuttene med søvn!",
"unlocked": true
},
{
"name": "Restaurantkupong",
"description": "Vi gir deg en restaurantkupong på 200 NOK for hver vennskapspoenggivende natt du bor. Det kan gi deg et fjell av croissanter! Herlig, hva?",
"unlocked": true,
"value": "200 NOK"
},
{
"name": "Friendsboost",
"description": "Gled deg! Hver gang du tjener nye Friends-poeng får du 25 % eller 50 % ekstra poeng som en superboost! Begynn å tjene poeng ved å bo og spise hos oss, og du vil få en bonusnatt før du aner det.",
"unlocked": true,
"value": "50 %"
},
{
"name": "Tidlig innsjekk når tilgjengelig",
"description": "Vil du få et forsprang på oppholdet ditt? Det er ikke noe problem. Sjekk inn én time tidligere kostnadsfritt, og ta snarveien til avslapning og lykke.",
"unlocked": true
},
{
"name": "Gratis oppgraderinger (når tilgjengelig)",
"description": "La oss ikke gå som katten rundt grøten vi tar ting opp et nivå. På dette nivået i vennskapet vårt vil vi oppgradere rommet ditt når det er mulig, for et enda mer komfortabelt opphold. Høres ikke det bra ut?",
"unlocked": true
},
{
"name": "2-for-1 frokost",
"description": "Dette er virkelig et deilig tilbud: Uavhengig om du bor hos oss eller ikke, finn deg en frokostkompis, og dere kan spise for prisen av én! Bare sørg for å sjekke detaljene først, slik at alt er på plass for deres smakfulle sammenkomst.",
"unlocked": false
},
{
"name": "Romgaranti i 48timer",
"description": "Hysj dette er en spesiell godbit, kun for noen få av dere! Så merk deg: Selv om vi er fullbooket, er du garantert et rom så lenge du bestiller 48 timer på forhånd. Ganske utrolig!",
"unlocked": true
},
{
"name": "Gratis frokost alltid",
"description": "Har du lyst på en smakfull morgengodbit? Bare kom innom! Nå kan du sparke i gang dagen din med gratis frokost og gjett hva: Det gjelder uavhengig om du bor hos oss eller ikke.",
"unlocked": true
},
{
"name": "Årlig fantastisk gave",
"description": "Som vår Best Friend fortjener du absolutt spesialbehandling, så vi har en eksklusiv, årlig, og ganske fantastisk gave klar til deg. Hva det er? Vel, det er en overraskelse. Ingen sniktitting!",
"unlocked": true
},
{
"name": "Boost for barn",
"description": "På dette vennskapsnivået er barnet ditt også vår venn og det betyr en spesiell boost-gave for barn når du bor hos oss. Hvorfor? Fordi barn er kule! De fortjener virkelig VIP-behandling.",
"unlocked": true
}
]
}
]
}

View File

@@ -1,538 +0,0 @@
{
"levels": [
{
"level": 1,
"name": "New Friend",
"requirement": "0p",
"description": "Det är nu den börjar, vår härliga resa tillsammans. Som en New Friend kommer du att upptäcka oss om och om igen under dina äventyr med Scandic.",
"icon": "/_static/icons/loyaltylevels/new-friend.svg",
"benefits": [
{
"name": "Friendspriser på rum",
"description": "Som vår vän får du alltid det bästa erbjudandet: oavsett tid och plats. Inga hemliga deals eller bokningskoder behövs det är bara att boka.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Mums! Prova vår välsmakande 10 % rabatt i våra restauranger och hotellshoppar på helgerna, oavsett om du övernattar eller bara tittar förbi på en bit mat. Så passa på att unna dig det lilla extra.",
"unlocked": true,
"value": "10%"
},
{
"name": "Fri mocktail för barn under vistelse",
"description": "Har du barnen med dig på resan? Fantastiskt hallå där, kompis! Hos oss är barn värdefulla små VIP-gäster, så därför bjuder vi på en läskande och god mocktail vid ankomst.",
"unlocked": true
},
{
"name": "Sen utcheckning om möjligt",
"description": "Vi fattar, ibland kan man behöva en sovmorgon. Som de vänner vi är låter vi dig checka ut en timme senare utan kostnad. Lite extra tid kan sätta hela känslan för resten av dagen.",
"unlocked": false
},
{
"name": "Restaurangkupong",
"description": "Finns det nåt bättre än en bra rabatt? Som vår vän får du rabatt i våra restauranger och hotellshoppen på helger och utvalda helgdagar, oavsett om du övernattar eller inte. Så, passa att unna dig det lilla extra.",
"unlocked": false
},
{
"name": "Friendsboost",
"description": "Sweet! Varje gång du tjänar nya friendspoäng bjuder vi dig på 25-50 % extra poäng som en superboost! Börja samla poäng genom att bo och äta så har du kirrat en bonusnatt på nolltid.",
"unlocked": false
},
{
"name": "Tidig incheckning om möjligt",
"description": "Vill du ha ditt rum lite i förväg? Inga problem. Checka in en timme tidigare utan kostnad och få en timmes extra avkoppling.",
"unlocked": false
},
{
"name": "Fri uppgradering om möjligt",
"description": "Vi steppar upp saker ett snäpp. Vår vänskap har nått nivån där vi kommer uppgradera ditt rum när det är möjligt, så du kan få en ännu skönare vistelse. Bra, eller hur?",
"unlocked": false
},
{
"name": "Frukost 2 för 1",
"description": "Okej, nu snackar vi. Oavsett om du bor hos oss eller inte, ta med en frukostkompis eftersom du äter två frukostar till priset av en! Kom bara ihåg att dubbelkolla detaljerna först så att allt är i sin ordning för en maxad frukost.",
"unlocked": false
},
{
"name": "48timmars rumsgaranti",
"description": "Schhh en hemlis för våra närmaste vänner: vi garanterar dig ett rum, även om vi är fullbokade, så länge du gör bokningen minst 48 timmar i förväg. Bra va?",
"unlocked": false
},
{
"name": "Alltid fri frukost",
"description": "Kickstarta dagen med en kostnadsfri frukost hos oss och du, det gäller oavsett om du bor hos oss eller inte. Det är bara att komma förbi!",
"unlocked": false
},
{
"name": "Årlig spännande present",
"description": "Som Best Friend förtjänar du det allra bästa. Därför ska du få en spännande present varje år, men vad det blir är en överraskning. Tjuvkika inte!",
"unlocked": false
},
{
"name": "Boost för barn",
"description": "Vid det här laget räknar vi även dina barn som våra vänner, vilket betyder att ni får en speciell överraskning när ni bor hos oss. Lite VIP-behandling. För att barn är coola.",
"unlocked": false
}
]
},
{
"level": 2,
"name": "Good Friend",
"requirement": "5 000p",
"description": "Vi har fått se ganska mycket av varandra senaste tiden! Helt ärligt, det känns som att vi har nåt på gång, men vi tar det i vår takt, en vistelse i taget.",
"icon": "/_static/icons/loyaltylevels/good-friend.svg",
"benefits": [
{
"name": "Friendspriser på rum",
"description": "Som vår vän får du alltid det bästa erbjudandet: oavsett tid och plats. Inga hemliga deals eller bokningskoder behövs det är bara att boka.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Mums! Prova vår välsmakande 10-15% rabatt i våra restauranger och hotellshoppar på helgerna, oavsett om du övernattar eller bara tittar förbi på en bit mat. Så passa på att unna dig det lilla extra.",
"unlocked": true,
"value": "15%"
},
{
"name": "Fri mocktail för barn under vistelse",
"description": "Har du barnen med dig på resan? Fantastiskt hallå där, kompis! Hos oss är barn värdefulla små VIP-gäster, så därför bjuder vi på en läskande och god mocktail vid ankomst.",
"unlocked": true
},
{
"name": "Sen utcheckning om möjligt",
"description": "Vi fattar, ibland kan man behöva en sovmorgon. Som de vänner vi är låter vi dig checka ut en timme senare utan kostnad. Lite extra tid kan sätta hela känslan för resten av dagen.",
"unlocked": false
},
{
"name": "Restaurangkupong",
"description": "Finns det nåt bättre än en bra rabatt? Som vår vän får du rabatt i våra restauranger och hotellshoppen på helger och utvalda helgdagar, oavsett om du övernattar eller inte. Så, passa att unna dig det lilla extra.",
"unlocked": false
},
{
"name": "Friendsboost",
"description": "Sweet! Varje gång du tjänar nya friendspoäng bjuder vi dig på 25-50 % extra poäng som en superboost! Börja samla poäng genom att bo och äta så har du kirrat en bonusnatt på nolltid.",
"unlocked": false
},
{
"name": "Tidig incheckning om möjligt",
"description": "Vill du ha ditt rum lite i förväg? Inga problem. Checka in en timme tidigare utan kostnad och få en timmes extra avkoppling.",
"unlocked": false
},
{
"name": "Fri uppgradering om möjligt",
"description": "Vi steppar upp saker ett snäpp. Vår vänskap har nått nivån där vi kommer uppgradera ditt rum när det är möjligt, så du kan få en ännu skönare vistelse. Bra, eller hur?",
"unlocked": false
},
{
"name": "Frukost 2 för 1",
"description": "Okej, nu snackar vi. Oavsett om du bor hos oss eller inte, ta med en frukostkompis eftersom du äter två frukostar till priset av en! Kom bara ihåg att dubbelkolla detaljerna först så att allt är i sin ordning för en maxad frukost.",
"unlocked": false
},
{
"name": "48timmars rumsgaranti",
"description": "Schhh en hemlis för våra närmaste vänner: vi garanterar dig ett rum, även om vi är fullbokade, så länge du gör bokningen minst 48 timmar i förväg. Bra va?",
"unlocked": false
},
{
"name": "Alltid fri frukost",
"description": "Kickstarta dagen med en kostnadsfri frukost hos oss och du, det gäller oavsett om du bor hos oss eller inte. Det är bara att komma förbi!",
"unlocked": false
},
{
"name": "Årlig spännande present",
"description": "Som Best Friend förtjänar du det allra bästa. Därför ska du få en spännande present varje år, men vad det blir är en överraskning. Tjuvkika inte!",
"unlocked": false
},
{
"name": "Boost för barn",
"description": "Vid det här laget räknar vi även dina barn som våra vänner, vilket betyder att ni får en speciell överraskning när ni bor hos oss. Lite VIP-behandling. För att barn är coola.",
"unlocked": false
}
]
},
{
"level": 3,
"name": "Close Friend",
"requirement": "10 000p",
"description": "Det börjar bli seriöst nu: vi har verkligen lärt känna varann. Därför tycker vi att det är dags att din upplevelse hos oss blir ännu mer personlig.",
"icon": "/_static/icons/loyaltylevels/close-friend.svg",
"benefits": [
{
"name": "Friendspriser på rum",
"description": "Som vår vän får du alltid det bästa erbjudandet: oavsett tid och plats. Inga hemliga deals eller bokningskoder behövs det är bara att boka.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Mums! Prova vår välsmakande 10-15% rabatt i våra restauranger och hotellshoppar på helgerna, oavsett om du övernattar eller bara tittar förbi på en bit mat. Så passa på att unna dig det lilla extra.",
"unlocked": true,
"value": "15%"
},
{
"name": "Fri mocktail för barn under vistelse",
"description": "Har du barnen med dig på resan? Fantastiskt hallå där, kompis! Hos oss är barn värdefulla små VIP-gäster, så därför bjuder vi på en läskande och god mocktail vid ankomst.",
"unlocked": true
},
{
"name": "Sen utcheckning om möjligt",
"description": "Vi fattar, ibland kan man behöva en sovmorgon. Som de vänner vi är låter vi dig checka ut en timme senare utan kostnad. Lite extra tid kan sätta hela känslan för resten av dagen.",
"unlocked": true
},
{
"name": "Restaurangkupong",
"description": "Okej, såhär: för varje natt som ger friendspoäng bjuder vi även på en restaurangkupong värd 50 kr.",
"unlocked": true,
"value": "50 kr"
},
{
"name": "Friendsboost",
"description": "",
"unlocked": false
},
{
"name": "Tidig incheckning om möjligt",
"description": "Vill du ha ditt rum lite i förväg? Inga problem. Checka in en timme tidigare utan kostnad och få en timmes extra avkoppling.",
"unlocked": false
},
{
"name": "Fri uppgradering om möjligt",
"description": "Vi steppar upp saker ett snäpp. Vår vänskap har nått nivån där vi kommer uppgradera ditt rum när det är möjligt, så du kan få en ännu skönare vistelse. Bra, eller hur?",
"unlocked": false
},
{
"name": "Frukost 2 för 1",
"description": "Okej, nu snackar vi. Oavsett om du bor hos oss eller inte, ta med en frukostkompis eftersom du äter två frukostar till priset av en! Kom bara ihåg att dubbelkolla detaljerna först så att allt är i sin ordning för en maxad frukost.",
"unlocked": false
},
{
"name": "48timmars rumsgaranti",
"description": "Schhh en hemlis för våra närmaste vänner: vi garanterar dig ett rum, även om vi är fullbokade, så länge du gör bokningen minst 48 timmar i förväg. Bra va?",
"unlocked": false
},
{
"name": "Alltid fri frukost",
"description": "Kickstarta dagen med en kostnadsfri frukost hos oss och du, det gäller oavsett om du bor hos oss eller inte. Det är bara att komma förbi!",
"unlocked": false
},
{
"name": "Årlig spännande present",
"description": "Som Best Friend förtjänar du det allra bästa. Därför ska du få en spännande present varje år, men vad det blir är en överraskning. Tjuvkika inte!",
"unlocked": false
},
{
"name": "Boost för barn",
"description": "Vid det här laget räknar vi även dina barn som våra vänner, vilket betyder att ni får en speciell överraskning när ni bor hos oss. Lite VIP-behandling. För att barn är coola.",
"unlocked": false
}
]
},
{
"level": 4,
"name": "Dear Friend",
"requirement": "25 000p",
"description": "Skål för oss! Vi känner tydliga vänner för livet-vibbar i luften och det öppnar upp för ännu fler Scandicfördelar.",
"icon": "/_static/icons/loyaltylevels/dear-friend.svg",
"benefits": [
{
"name": "Friendspriser på rum",
"description": "Som vår vän får du alltid det bästa erbjudandet: oavsett tid och plats. Inga hemliga deals eller bokningskoder behövs det är bara att boka.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Mums! Prova vår välsmakande 10-15% rabatt i våra restauranger och hotellshoppar på helgerna, oavsett om du övernattar eller bara tittar förbi på en bit mat. Så passa på att unna dig det lilla extra.",
"unlocked": true,
"value": "15%"
},
{
"name": "Fri mocktail för barn under vistelse",
"description": "Har du barnen med dig på resan? Fantastiskt hallå där, kompis! Hos oss är barn värdefulla små VIP-gäster, så därför bjuder vi på en läskande och god mocktail vid ankomst.",
"unlocked": true
},
{
"name": "Sen utcheckning om möjligt",
"description": "Vi fattar, ibland kan man behöva en sovmorgon. Som de vänner vi är låter vi dig checka ut en timme senare utan kostnad. Lite extra tid kan sätta hela känslan för resten av dagen.",
"unlocked": true
},
{
"name": "Restaurangkupong",
"description": "Vår vänskap har vuxit sig starkare. Njut av en restaurangkupong värd 75 kr för varje natt som ger friendspoäng.",
"unlocked": true,
"value": "75 kr"
},
{
"name": "Friendsboost",
"description": "Sweet! Varje gång du tjänar nya friendspoäng bjuder vi dig på 25-50 % extra poäng som en superboost! Börja samla poäng genom att bo och äta så har du kirrat en bonusnatt på nolltid.",
"unlocked": true,
"value": "25%"
},
{
"name": "Tidig incheckning om möjligt",
"description": "Vill du ha ditt rum lite i förväg? Inga problem. Checka in en timme tidigare utan kostnad och få en timmes extra avkoppling.",
"unlocked": true
},
{
"name": "Fri uppgradering om möjligt",
"description": "Vi steppar upp saker ett snäpp. Vår vänskap har nått nivån där vi kommer uppgradera ditt rum när det är möjligt, så du kan få en ännu skönare vistelse. Bra, eller hur?",
"unlocked": false
},
{
"name": "Frukost 2 för 1",
"description": "Okej, nu snackar vi. Oavsett om du bor hos oss eller inte, ta med en frukostkompis eftersom du äter två frukostar till priset av en! Kom bara ihåg att dubbelkolla detaljerna först så att allt är i sin ordning för en maxad frukost.",
"unlocked": false
},
{
"name": "48timmars rumsgaranti",
"description": "Schhh en hemlis för våra närmaste vänner: vi garanterar dig ett rum, även om vi är fullbokade, så länge du gör bokningen minst 48 timmar i förväg. Bra va?",
"unlocked": false
},
{
"name": "Alltid fri frukost",
"description": "Kickstarta dagen med en kostnadsfri frukost hos oss och du, det gäller oavsett om du bor hos oss eller inte. Det är bara att komma förbi!",
"unlocked": false
},
{
"name": "Årlig spännande present",
"description": "Som Best Friend förtjänar du det allra bästa. Därför ska du få en spännande present varje år, men vad det blir är en överraskning. Tjuvkika inte!",
"unlocked": false
},
{
"name": "Boost för barn",
"description": "Vid det här laget räknar vi även dina barn som våra vänner, vilket betyder att ni får en speciell överraskning när ni bor hos oss. Lite VIP-behandling. För att barn är coola.",
"unlocked": false
}
]
},
{
"level": 5,
"name": "Loyal Friend",
"requirement": "100 000p",
"description": "Du har bott hos oss otaliga nätter, hängt med oss sena timmar och genom ur och skur. Det är dags att du får några av våra allra bästa förmåner.",
"icon": "/_static/icons/loyaltylevels/loyal-friend.svg",
"benefits": [
{
"name": "Friendspriser på rum",
"description": "Som vår vän får du alltid det bästa erbjudandet: oavsett tid och plats. Inga hemliga deals eller bokningskoder behövs det är bara att boka.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Mums! Prova vår välsmakande 10-15% rabatt i våra restauranger och hotellshoppar på helgerna, oavsett om du övernattar eller bara tittar förbi på en bit mat. Så passa på att unna dig det lilla extra.",
"unlocked": true,
"value": "15%"
},
{
"name": "Fri mocktail för barn under vistelse",
"description": "Har du barnen med dig på resan? Fantastiskt hallå där, kompis! Hos oss är barn värdefulla små VIP-gäster, så därför bjuder vi på en läskande och god mocktail vid ankomst.",
"unlocked": true
},
{
"name": "Sen utcheckning om möjligt",
"description": "Vi fattar, ibland kan man behöva en sovmorgon. Som de vänner vi är låter vi dig checka ut en timme senare utan kostnad. Lite extra tid kan sätta hela känslan för resten av dagen.",
"unlocked": true
},
{
"name": "Restaurangkupong",
"description": "Vi bjuder dig på en restaurangkupong värd 100 kr för varje natt som ger friendsspoäng. Det motsvarar ett mindre berg av croissanter!",
"unlocked": true,
"value": "100 kr"
},
{
"name": "Friendsboost",
"description": "Sweet! Varje gång du tjänar nya friendspoäng bjuder vi dig på 25-50 % extra poäng som en superboost! Börja samla poäng genom att bo och äta så har du kirrat en bonusnatt på nolltid.",
"unlocked": true,
"value": "25%"
},
{
"name": "Tidig incheckning om möjligt",
"description": "Vill du ha ditt rum lite i förväg? Inga problem. Checka in en timme tidigare utan kostnad och få en timmes extra avkoppling.",
"unlocked": true
},
{
"name": "Fri uppgradering om möjligt",
"description": "Vi steppar upp saker ett snäpp. Vår vänskap har nått nivån där vi kommer uppgradera ditt rum när det är möjligt, så du kan få en ännu skönare vistelse. Bra, eller hur?",
"unlocked": true
},
{
"name": "Frukost 2 för 1",
"description": "Okej, nu snackar vi. Oavsett om du bor hos oss eller inte, ta med en frukostkompis eftersom du äter två frukostar till priset av en! Kom bara ihåg att dubbelkolla detaljerna först så att allt är i sin ordning för en maxad frukost.",
"unlocked": true
},
{
"name": "48timmars rumsgaranti",
"description": "Schhh en hemlis för våra närmaste vänner: vi garanterar dig ett rum, även om vi är fullbokade, så länge du gör bokningen minst 48 timmar i förväg. Bra va?",
"unlocked": false
},
{
"name": "Alltid fri frukost",
"description": "Kickstarta dagen med en kostnadsfri frukost hos oss och du, det gäller oavsett om du bor hos oss eller inte. Det är bara att komma förbi!",
"unlocked": false
},
{
"name": "Årlig spännande present",
"description": "Som Best Friend förtjänar du det allra bästa. Därför ska du få en spännande present varje år, men vad det blir är en överraskning. Tjuvkika inte!",
"unlocked": false
},
{
"name": "Boost för barn",
"description": "Vid det här laget räknar vi även dina barn som våra vänner, vilket betyder att ni får en speciell överraskning när ni bor hos oss. Lite VIP-behandling. För att barn är coola.",
"unlocked": false
}
]
},
{
"level": 6,
"name": "True Friend",
"requirement": "250 000p",
"description": "Dig kan man lita på, oavsett säsong finns du där för oss. Därför vill vi att du ska få njuta av fler förmåner anpassade efter dig.",
"icon": "/_static/icons/loyaltylevels/true-friend.svg",
"benefits": [
{
"name": "Friendspriser på rum",
"description": "Som vår vän får du alltid det bästa erbjudandet: oavsett tid och plats. Inga hemliga deals eller bokningskoder behövs det är bara att boka.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Mums! Prova vår välsmakande 10-15% rabatt i våra restauranger och hotellshoppar på helgerna, oavsett om du övernattar eller bara tittar förbi på en bit mat. Så passa på att unna dig det lilla extra.",
"unlocked": true,
"value": "15%"
},
{
"name": "Fri mocktail för barn under vistelse",
"description": "Har du barnen med dig på resan? Fantastiskt hallå där, kompis! Hos oss är barn värdefulla små VIP-gäster, så därför bjuder vi på en läskande och god mocktail vid ankomst.",
"unlocked": true
},
{
"name": "Sen utcheckning om möjligt",
"description": "Vi fattar, ibland kan man behöva en sovmorgon. Som de vänner vi är låter vi dig checka ut en timme senare utan kostnad. Lite extra tid kan sätta hela känslan för resten av dagen.",
"unlocked": true
},
{
"name": "Restaurangkupong",
"description": "Det blir bättre och bättre! Nu får du en restaurangkupong värd 150 kr för varje natt som ge friendsspoäng. Perfekt när du känner för en lyxig frukost eller ska på middagsdate!",
"unlocked": true,
"value": "150 kr"
},
{
"name": "Friendsboost",
"description": "Sweet! Varje gång du tjänar nya friendspoäng bjuder vi dig på 25-50 % extra poäng som en superboost! Börja samla poäng genom att bo och äta så har du kirrat en bonusnatt på nolltid.",
"unlocked": true,
"value": "50%"
},
{
"name": "Tidig incheckning om möjligt",
"description": "Vill du ha ditt rum lite i förväg? Inga problem. Checka in en timme tidigare utan kostnad och få en timmes extra avkoppling.",
"unlocked": true
},
{
"name": "Fri uppgradering om möjligt",
"description": "Vi steppar upp saker ett snäpp. Vår vänskap har nått nivån där vi kommer uppgradera ditt rum när det är möjligt, så du kan få en ännu skönare vistelse. Bra, eller hur?",
"unlocked": true
},
{
"name": "Frukost 2 för 1",
"description": "Okej, nu snackar vi. Oavsett om du bor hos oss eller inte, ta med en frukostkompis eftersom du äter två frukostar till priset av en! Kom bara ihåg att dubbelkolla detaljerna först så att allt är i sin ordning för en maxad frukost.",
"unlocked": false
},
{
"name": "48timmars rumsgaranti",
"description": "Schhh en hemlis för våra närmaste vänner: vi garanterar dig ett rum, även om vi är fullbokade, så länge du gör bokningen minst 48 timmar i förväg. Bra va?",
"unlocked": true
},
{
"name": "Alltid fri frukost",
"description": "Kickstarta dagen med en kostnadsfri frukost hos oss och du, det gäller oavsett om du bor hos oss eller inte. Det är bara att komma förbi!",
"unlocked": true
},
{
"name": "Årlig spännande present",
"description": "Som Best Friend förtjänar du det allra bästa. Därför ska du få en spännande present varje år, men vad det blir är en överraskning. Tjuvkika inte!",
"unlocked": false
},
{
"name": "Boost för barn",
"description": "Vid det här laget räknar vi även dina barn som våra vänner, vilket betyder att ni får en speciell överraskning när ni bor hos oss. Lite VIP-behandling. För att barn är coola.",
"unlocked": false
}
]
},
{
"level": 7,
"name": "Best Friend",
"requirement": "400 000p eller 100 nätter",
"description": "Det finns inga ord för en vänskap som vår, men vi vill ändå säga: Bättre upplevelser än så här går inte att få, nu vi talar om exklusiva upplevelser!",
"icon": "/_static/icons/loyaltylevels/best-friend.svg",
"benefits": [
{
"name": "Friendspriser på rum",
"description": "Som vår vän får du alltid det bästa erbjudandet: oavsett tid och plats. Inga hemliga deals eller bokningskoder behövs det är bara att boka.",
"unlocked": true
},
{
"name": "Rabatt på mat",
"description": "Mums! Prova vår välsmakande 10-15 % rabatt i våra restauranger och hotellshoppar på helgerna, oavsett om du övernattar eller bara tittar förbi på en bit mat. Så passa på att unna dig det lilla extra.",
"unlocked": true,
"value": "15%"
},
{
"name": "Fri mocktail för barn under vistelse",
"description": "Har du barnen med dig på resan? Fantastiskt hallå där, kompis! Hos oss är barn värdefulla små VIP-gäster, så därför bjuder vi på en läskande och god mocktail vid ankomst.",
"unlocked": true
},
{
"name": "Sen utcheckning om möjligt",
"description": "Vi fattar, ibland kan man behöva en sovmorgon. Som de vänner vi är låter vi dig checka ut en timme senare utan kostnad. Lite extra tid kan sätta hela känslan för resten av dagen.",
"unlocked": true
},
{
"name": "Restaurangkupong",
"description": "Det blir bättre och bättre! Nu får du en restaurangkupong för varje natt som ger friendsspoäng. Perfekt när du känner för en lyxig frukost eller ska på middagsdate!",
"unlocked": true,
"value": "200 kr"
},
{
"name": "Friendsboost",
"description": "Sweet! Varje gång du tjänar nya friendspoäng bjuder vi dig på 25-50 % extra poäng som en superboost! Börja samla poäng genom att bo och äta så har du kirrat en bonusnatt på nolltid.",
"unlocked": true,
"value": "50%"
},
{
"name": "Tidig incheckning om möjligt",
"description": "Vill du ha ditt rum lite i förväg? Inga problem. Checka in en timme tidigare utan kostnad och få en timmes extra avkoppling.",
"unlocked": true
},
{
"name": "Fri uppgradering om möjligt",
"description": "Vi steppar upp saker ett snäpp. Vår vänskap har nått nivån där vi kommer uppgradera ditt rum när det är möjligt, så du kan få en ännu skönare vistelse. Bra, eller hur?",
"unlocked": true
},
{
"name": "Frukost 2 för 1",
"description": "Okej, nu snackar vi. Oavsett om du bor hos oss eller inte, ta med en frukostkompis eftersom du äter två frukostar till priset av en! Kom bara ihåg att dubbelkolla detaljerna först så att allt är i sin ordning för en maxad frukost.",
"unlocked": false
},
{
"name": "48timmars rumsgaranti",
"description": "Schhh en hemlis för våra närmaste vänner: vi garanterar dig ett rum, även om vi är fullbokade, så länge du gör bokningen minst 48 timmar i förväg. Bra va?",
"unlocked": true
},
{
"name": "Alltid fri frukost",
"description": "Kickstarta dagen med en kostnadsfri frukost hos oss och du, det gäller oavsett om du bor hos oss eller inte. Det är bara att komma förbi!",
"unlocked": true
},
{
"name": "Årlig spännande present",
"description": "Som Best Friend förtjänar du det allra bästa. Därför ska du få en spännande present varje år, men vad det blir är en överraskning. Tjuvkika inte!",
"unlocked": true
},
{
"name": "Boost för barn",
"description": "Vid det här laget räknar vi även dina barn som våra vänner, vilket betyder att ni får en speciell överraskning när ni bor hos oss. Lite VIP-behandling. För att barn är coola.",
"unlocked": true
}
]
}
]
}

View File

@@ -3,16 +3,20 @@ import { serverClient } from "@/lib/trpc/server"
import SectionWrapper from "../SectionWrapper"
import OverviewTableClient from "./Client"
import type { OverviewTableProps } from "@/types/components/blocks/dynamicContent"
import { OverviewTableProps } from "@/types/components/blocks/dynamicContent"
export default async function OverviewTable({
dynamic_content,
firstItem,
}: OverviewTableProps) {
const levels = await serverClient().contentstack.rewards.all()
const membershipLevel = await serverClient().user.safeMembershipLevel()
return (
<SectionWrapper dynamic_content={dynamic_content} firstItem={firstItem}>
<OverviewTableClient activeMembership={membershipLevel} />
<OverviewTableClient
levels={levels}
activeMembership={membershipLevel?.membershipLevel ?? null}
/>
</SectionWrapper>
)
}

View File

@@ -0,0 +1,95 @@
import {
type MembershipLevel,
MembershipLevelEnum,
} from "@/constants/membershipLevels"
import { getSteppedUpLevel } from "@/utils/user"
import {
type LevelWithRewards,
OverviewTableActionsEnum,
type OverviewTableClientProps,
OverviewTableReducerAction,
} from "@/types/components/overviewTable"
export function getLevel(
membershipLevel: MembershipLevel,
levels: LevelWithRewards[]
) {
return levels.find((level) => level.level_id === membershipLevel)!
}
export function getInitialState({
activeMembership,
levels,
}: OverviewTableClientProps) {
if (!activeMembership) {
return {
selectedLevelAMobile: getLevel(MembershipLevelEnum.L1, levels),
selectedLevelBMobile: getLevel(MembershipLevelEnum.L2, levels),
selectedLevelADesktop: getLevel(MembershipLevelEnum.L1, levels),
selectedLevelBDesktop: getLevel(MembershipLevelEnum.L2, levels),
selectedLevelCDesktop: getLevel(MembershipLevelEnum.L3, levels),
}
}
const level = MembershipLevelEnum[activeMembership]
switch (level) {
case MembershipLevelEnum.L6:
return {
selectedLevelAMobile: getLevel(MembershipLevelEnum.L6, levels),
selectedLevelBMobile: getLevel(MembershipLevelEnum.L7, levels),
selectedLevelADesktop: getLevel(MembershipLevelEnum.L5, levels),
selectedLevelBDesktop: getLevel(MembershipLevelEnum.L6, levels),
selectedLevelCDesktop: getLevel(MembershipLevelEnum.L7, levels),
}
case MembershipLevelEnum.L7:
return {
selectedLevelAMobile: getLevel(MembershipLevelEnum.L6, levels),
selectedLevelBMobile: getLevel(MembershipLevelEnum.L7, levels),
selectedLevelADesktop: getLevel(MembershipLevelEnum.L6, levels),
selectedLevelBDesktop: getLevel(MembershipLevelEnum.L7, levels),
selectedLevelCDesktop: getLevel(MembershipLevelEnum.L1, levels),
}
default:
return {
selectedLevelAMobile: getLevel(level, levels),
selectedLevelBMobile: getLevel(getSteppedUpLevel(level, 1), levels),
selectedLevelADesktop: getLevel(level, levels),
selectedLevelBDesktop: getLevel(getSteppedUpLevel(level, 1), levels),
selectedLevelCDesktop: getLevel(getSteppedUpLevel(level, 2), levels),
}
}
}
export function reducer(state: any, action: OverviewTableReducerAction) {
switch (action.type) {
case OverviewTableActionsEnum.SET_SELECTED_LEVEL_A_MOBILE:
return {
...state,
selectedLevelAMobile: action.payload,
}
case OverviewTableActionsEnum.SET_SELECTED_LEVEL_B_MOBILE:
return {
...state,
selectedLevelBMobile: action.payload,
}
case OverviewTableActionsEnum.SET_SELECTED_LEVEL_A_DESKTOP:
return {
...state,
selectedLevelADesktop: action.payload,
}
case OverviewTableActionsEnum.SET_SELECTED_LEVEL_B_DESKTOP:
return {
...state,
selectedLevelBDesktop: action.payload,
}
case OverviewTableActionsEnum.SET_SELECTED_LEVEL_C_DESKTOP:
return {
...state,
selectedLevelCDesktop: action.payload,
}
default:
return state
}
}

View File

@@ -1,7 +1,7 @@
import { MembershipLevelEnum } from "@/constants/membershipLevels"
import { serverClient } from "@/lib/trpc/server"
import { getIntl } from "@/i18n"
import { getMembershipLevelObject } from "@/utils/membershipLevel"
import { getMembership } from "@/utils/user"
import PointsContainer from "../../../Overview/Stats/Points/Container"
@@ -20,11 +20,12 @@ export default async function Points({ user, lang }: UserProps & LangParams) {
const { formatMessage } = await getIntl()
const membership = getMembership(user.memberships)
const nextLevel = getMembershipLevelObject(
membership?.nextLevel as MembershipLevelEnum,
lang
)
if (!membership?.nextLevel) {
return null
}
const nextLevel = await serverClient().contentstack.loyaltyLevels.byLevel({
level: MembershipLevelEnum[membership.nextLevel],
})
return (
<PointsContainer>
<YourPointsColumn points={membership?.currentPoints} />

View File

@@ -0,0 +1,76 @@
"use client"
import { trpc } from "@/lib/trpc/client"
import { Reward } from "@/server/routers/contentstack/reward/output"
import LoadingSpinner from "@/components/LoadingSpinner"
import Grids from "@/components/TempDesignSystem/Grids"
import ShowMoreButton from "@/components/TempDesignSystem/ShowMoreButton"
import Title from "@/components/TempDesignSystem/Text/Title"
import useLang from "@/hooks/useLang"
import styles from "./current.module.css"
type CurrentRewardsClientProps = {
initialCurrentRewards: { rewards: Reward[]; nextCursor: number | undefined }
}
export default function ClientCurrentRewards({
initialCurrentRewards,
}: CurrentRewardsClientProps) {
const lang = useLang()
const { data, isFetching, fetchNextPage, hasNextPage, isLoading } =
trpc.contentstack.rewards.current.useInfiniteQuery(
{
limit: 3,
lang,
},
{
getNextPageParam: (lastPage) => lastPage?.nextCursor,
initialData: {
pageParams: [undefined, 1],
pages: [initialCurrentRewards],
},
}
)
function loadMoreData() {
if (hasNextPage) {
fetchNextPage()
}
}
const filteredRewards =
data?.pages.filter((page) => page && page.rewards) ?? []
const rewards = filteredRewards.flatMap((page) => page?.rewards) as Reward[]
if (isLoading) {
return <LoadingSpinner />
}
if (!rewards.length) {
return null
}
return (
<div>
<Grids.Stackable>
{rewards.map((reward, idx) => (
<article className={styles.card} key={`${reward.reward_id}-${idx}`}>
<Title
as="h4"
level="h3"
textAlign="center"
textTransform="regular"
>
{reward.label}
</Title>
</article>
))}
</Grids.Stackable>
{hasNextPage &&
(isFetching ? (
<LoadingSpinner />
) : (
<ShowMoreButton loadMoreData={loadMoreData} />
))}
</div>
)
}

View File

@@ -0,0 +1,32 @@
import { serverClient } from "@/lib/trpc/server"
import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header"
import SectionLink from "@/components/Section/Link"
import ClientCurrentRewards from "./Client"
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
export default async function CurrentRewardsBlock({
title,
subtitle,
link,
}: AccountPageComponentProps) {
const initialCurrentRewards =
await serverClient().contentstack.rewards.current({
limit: 3,
})
if (!initialCurrentRewards) {
return null
}
return (
<SectionContainer>
<SectionHeader title={title} link={link} preamble={subtitle} />
<ClientCurrentRewards initialCurrentRewards={initialCurrentRewards} />
<SectionLink link={link} variant="mobile" />
</SectionContainer>
)
}

View File

@@ -1,7 +1,7 @@
import { Lock } from "react-feather"
import { MembershipLevelEnum } from "@/constants/membershipLevels"
import { getProfile } from "@/lib/trpc/memoizedRequests"
import { serverClient } from "@/lib/trpc/server"
import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header"
@@ -11,38 +11,39 @@ import Grids from "@/components/TempDesignSystem/Grids"
import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title"
import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import { getMembershipLevelObject } from "@/utils/membershipLevel"
import styles from "./next.module.css"
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
export default async function NextLevelBenefitsBlock({
export default async function NextLevelRewardsBlock({
title,
subtitle,
link,
}: AccountPageComponentProps) {
const intl = await getIntl()
const user = await getProfile()
if (!user || "error" in user || !user.membership) {
const membershipLevel = await serverClient().user.membershipLevel()
if (!membershipLevel || !membershipLevel?.nextLevel) {
return null
}
const nextLevel = getMembershipLevelObject(
user.membership.nextLevel as MembershipLevelEnum,
getLang()
)
if (!nextLevel) {
// TODO: handle this case, when missing or when user is top level?
const nextLevelRewards = await serverClient().contentstack.rewards.byLevel({
level_id: MembershipLevelEnum[membershipLevel?.nextLevel],
unique: true,
})
// TODO: handle this case, when missing or when user is top level?
if (!nextLevelRewards) {
return null
}
// TODO: how to handle different count of unlockable benefits?
return (
<SectionContainer>
<SectionHeader title={title} preamble={subtitle} link={link} />
<Grids.Stackable columns={2}>
{nextLevel.benefits.map((benefit) => (
<article key={benefit.title} className={styles.card}>
{nextLevelRewards.rewards.map((reward) => (
<article key={reward.reward_id} className={styles.card}>
<Chip>
<Lock height={16} />
{intl.formatMessage({ id: "Level up to unlock" })}
@@ -51,11 +52,11 @@ export default async function NextLevelBenefitsBlock({
<Body color="peach50" textAlign="center">
{intl.formatMessage(
{ id: "As our" },
{ level: nextLevel.name }
{ level: nextLevelRewards.level?.name }
)}
</Body>{" "}
</Body>
<Title level="h4" as="h5" color="pale" textAlign="center">
{benefit.title}
{reward.label}
</Title>
</div>
</article>

View File

@@ -1,5 +1,3 @@
import CurrentBenefitsBlock from "@/components/Blocks/DynamicContent/Benefits/CurrentLevel"
import NextLevelBenefitsBlock from "@/components/Blocks/DynamicContent/Benefits/NextLevel"
import HowItWorks from "@/components/Blocks/DynamicContent/HowItWorks"
import LoyaltyLevels from "@/components/Blocks/DynamicContent/LoyaltyLevels"
import Overview from "@/components/Blocks/DynamicContent/Overview"
@@ -7,6 +5,8 @@ import OverviewTable from "@/components/Blocks/DynamicContent/OverviewTable"
import EarnAndBurn from "@/components/Blocks/DynamicContent/Points/EarnAndBurn"
import ExpiringPoints from "@/components/Blocks/DynamicContent/Points/ExpiringPoints"
import PointsOverview from "@/components/Blocks/DynamicContent/Points/Overview"
import CurrentRewardsBlock from "@/components/Blocks/DynamicContent/Rewards/CurrentLevel"
import NextLevelRewardsBlock from "@/components/Blocks/DynamicContent/Rewards/NextLevel"
import PreviousStays from "@/components/Blocks/DynamicContent/Stays/Previous"
import SoonestStays from "@/components/Blocks/DynamicContent/Stays/Soonest"
import UpcomingStays from "@/components/Blocks/DynamicContent/Stays/Upcoming"
@@ -20,7 +20,7 @@ export default async function DynamicContent({
}: DynamicContentProps) {
switch (dynamic_content.component) {
case DynamicContentEnum.Blocks.components.current_benefits:
return <CurrentBenefitsBlock {...dynamic_content} />
return <CurrentRewardsBlock {...dynamic_content} />
case DynamicContentEnum.Blocks.components.earn_and_burn:
return <EarnAndBurn {...dynamic_content} />
case DynamicContentEnum.Blocks.components.expiring_points:
@@ -39,7 +39,7 @@ export default async function DynamicContent({
case DynamicContentEnum.Blocks.components.membership_overview:
return <Overview {...dynamic_content} />
case DynamicContentEnum.Blocks.components.next_benefits:
return <NextLevelBenefitsBlock {...dynamic_content} />
return <NextLevelRewardsBlock {...dynamic_content} />
case DynamicContentEnum.Blocks.components.overview_table:
return (
<OverviewTable

View File

@@ -0,0 +1,35 @@
import { getIconByIconName } from "@/components/Icons/get-icon-by-icon-name"
import JsonToHtml from "@/components/JsonToHtml"
import { renderOptions } from "./renderOptions"
import { getUspIconName } from "./utils"
import styles from "./uspgrid.module.css"
import type { UspGridProps, UspIcon } from "@/types/components/blocks/uspGrid"
function UspIcon({ icon }: { icon: UspIcon }) {
const iconName = getUspIconName(icon)
const Icon = iconName ? getIconByIconName(iconName) : null
return Icon ? <Icon color="red" /> : null
}
export default function UspGrid({ usp_grid }: UspGridProps) {
return (
<div className={styles.grid}>
{usp_grid.usp_card.map(
(usp) =>
usp.text.json && (
<div key={usp.text.json.uid} className={styles.usp}>
<UspIcon icon={usp.icon} />
<JsonToHtml
embeds={usp.text.embedded_itemsConnection?.edges}
nodes={usp.text.json.children}
renderOptions={renderOptions}
/>
</div>
)
)}
</div>
)
}

View File

@@ -0,0 +1,73 @@
import Link from "@/components/TempDesignSystem/Link"
import { removeMultipleSlashes } from "@/utils/url"
import styles from "./uspgrid.module.css"
import { EmbedEnum } from "@/types/requests/utils/embeds"
import type { EmbedByUid } from "@/types/transitionTypes/jsontohtml"
import { RTEItemTypeEnum, RTETypeEnum } from "@/types/transitionTypes/rte/enums"
import type {
RTEDefaultNode,
RTENext,
RTENode,
RTERegularNode,
} from "@/types/transitionTypes/rte/node"
import type { RenderOptions } from "@/types/transitionTypes/rte/option"
export const renderOptions: RenderOptions = {
[RTETypeEnum.p]: (
node: RTEDefaultNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
return (
<p key={node.uid} className={styles.p}>
{next(node.children, embeds, fullRenderOptions)}
</p>
)
},
[RTETypeEnum.a]: (
node: RTERegularNode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
if (node.attrs.url) {
return (
<a
href={node.attrs.url}
target={node.attrs.target ?? "_blank"}
key={node.uid}
className={styles.a}
>
{next(node.children, embeds, fullRenderOptions)}
</a>
)
}
return null
},
[RTETypeEnum.reference]: (
node: RTENode,
embeds: EmbedByUid,
next: RTENext,
fullRenderOptions: RenderOptions
) => {
if ("attrs" in node) {
const type = node.attrs.type
if (type !== RTEItemTypeEnum.asset) {
const href = node.attrs?.locale
? `/${node.attrs.locale}${node.attrs.href}`
: node.attrs.href
return (
<Link href={href} key={node.uid} className={styles.a}>
{next(node.children, embeds, fullRenderOptions)}
</Link>
)
}
return null
}
},
}

View File

@@ -0,0 +1,28 @@
.grid {
display: grid;
gap: var(--Spacing-x3);
padding: var(--Spacing-x3) var(--Spacing-x4);
}
@media screen and (min-width: 767px) {
.grid {
grid-template-columns: repeat(3, 1fr);
}
.grid:has(.usp:nth-child(4)) {
grid-template-columns: repeat(2, 1fr);
}
}
.usp {
display: flex;
flex-direction: column;
gap: var(--Spacing-x3);
}
.p {
margin: 0;
font-size: var(--typography-Caption-Regular-fontSize);
color: var(--UI-Text-Medium-contrast);
line-height: 21px; /* Caption variable for line-height is 139.9999976158142%, but it set to 21px in design */
}
.a {
font-size: var(--typography-Caption-Regular-fontSize);
color: var(--Base-Text-High-contrast);
}

View File

@@ -0,0 +1,11 @@
import { UspIcon } from "@/types/components/blocks/uspGrid"
import { IconName } from "@/types/components/icon"
export function getUspIconName(icon?: UspIcon | null) {
switch (icon) {
case "Snowflake":
return IconName.Snowflake
default:
return IconName.Snowflake
}
}

View File

@@ -2,6 +2,7 @@ import CardsGrid from "@/components/Blocks/CardsGrid"
import DynamicContent from "@/components/Blocks/DynamicContent"
import Shortcuts from "@/components/Blocks/Shortcuts"
import TextCols from "@/components/Blocks/TextCols"
import UspGrid from "@/components/Blocks/UspGrid"
import JsonToHtml from "@/components/JsonToHtml"
import type { BlocksProps } from "@/types/components/blocks"
@@ -57,6 +58,8 @@ export default function Blocks({ blocks }: BlocksProps) {
/>
</section>
)
case BlocksEnums.block.UspGrid:
return <UspGrid usp_grid={block.usp_grid} />
default:
return null
}

View File

@@ -11,6 +11,18 @@
padding: var(--Spacing-x4) var(--Spacing-x2);
}
.headerContent {
display: grid;
gap: var(--Spacing-x3);
max-width: var(--max-width-content);
margin: 0 auto;
}
.headerIntro {
display: grid;
max-width: var(--max-width-text-block);
gap: var(--Spacing-x3);
}
.content {
padding: var(--Spacing-x4) var(--Spacing-x2);
display: grid;
@@ -21,3 +33,9 @@
width: 100%;
max-width: var(--max-width-content);
}
@media (min-width: 768px) {
.headerIntro {
gap: var(--Spacing-x3);
}
}

View File

@@ -2,8 +2,8 @@ import { serverClient } from "@/lib/trpc/server"
import Blocks from "@/components/Blocks"
import Hero from "@/components/Hero"
import Intro from "@/components/Intro"
import Sidebar from "@/components/Sidebar"
import LinkChips from "@/components/TempDesignSystem/LinkChips"
import Preamble from "@/components/TempDesignSystem/Text/Preamble"
import Title from "@/components/TempDesignSystem/Text/Title"
import TrackingSDK from "@/components/TrackingSDK"
@@ -18,31 +18,34 @@ export default async function ContentPage() {
}
const { tracking, contentPage } = contentPageRes
const heroImage = contentPage.hero_image
const { blocks, hero_image, header, sidebar } = contentPage
return (
<>
<section className={styles.contentPage}>
{contentPage.sidebar?.length ? (
<Sidebar blocks={contentPage.sidebar} />
) : null}
{sidebar?.length ? <Sidebar blocks={sidebar} /> : null}
<header className={styles.header}>
<Intro>
<Title as="h2">{contentPage.header.heading}</Title>
<Preamble>{contentPage.header.preamble}</Preamble>
</Intro>
<div className={styles.headerContent}>
<div className={styles.headerIntro}>
<Title as="h2">{header.heading}</Title>
<Preamble>{header.preamble}</Preamble>
</div>
{header.navigation_links ? (
<LinkChips chips={header.navigation_links} />
) : null}
</div>
</header>
<main className={styles.content}>
<div className={styles.innerContent}>
{heroImage ? (
{hero_image ? (
<Hero
alt={heroImage.meta.alt || heroImage.meta.caption || ""}
src={heroImage.url}
alt={hero_image.meta.alt || hero_image.meta.caption || ""}
src={hero_image.url}
/>
) : null}
{contentPage.blocks ? <Blocks blocks={contentPage.blocks} /> : null}
{blocks ? <Blocks blocks={blocks} /> : null}
</div>
</main>
</section>

View File

@@ -6,6 +6,8 @@
display: grid;
gap: var(--Spacing-x-one-and-half);
height: fit-content;
width: 100%;
max-width: 300px;
}
.amenityItemList {
@@ -26,6 +28,5 @@
@media screen and (min-width: 1367px) {
.amenitiesContainer {
margin-top: var(--Spacing-x5);
width: 300px;
}
}

View File

@@ -80,7 +80,8 @@ export default function MapContent({
className={`${styles.poi} ${activePoi === poi.name ? styles.active : ""}`}
>
<PoiMarker
category={poi.category}
group={poi.group}
categoryName={poi.categoryName}
className={styles.poiMarker}
size={activePoi === poi.name ? 20 : 16}
/>

View File

@@ -20,12 +20,10 @@ export default function Sidebar({
}: SidebarProps) {
const intl = useIntl()
const [isFullScreenSidebar, setIsFullScreenSidebar] = useState(false)
const poiCategories = new Set(
pointsOfInterest.map(({ category }) => category)
)
const poisInCategories = Array.from(poiCategories).map((category) => ({
category,
pois: pointsOfInterest.filter((poi) => poi.category === category),
const poiGroups = new Set(pointsOfInterest.map(({ group }) => group))
const poisInGroups = Array.from(poiGroups).map((group) => ({
group,
pois: pointsOfInterest.filter((poi) => poi.group === group),
}))
function toggleFullScreenSidebar() {
@@ -60,9 +58,9 @@ export default function Sidebar({
)}
</Title>
{poisInCategories.map(({ category, pois }) =>
{poisInGroups.map(({ group, pois }) =>
pois.length ? (
<div key={category} className={styles.poiGroup}>
<div key={group} className={styles.poiGroup}>
<Body
color="black"
textTransform="bold"
@@ -70,8 +68,8 @@ export default function Sidebar({
asChild
>
<h3>
<PoiMarker category={category} />
{intl.formatMessage({ id: category })}
<PoiMarker group={group} />
{intl.formatMessage({ id: group })}
</h3>
</Body>
<ul className={styles.poiList}>

View File

@@ -39,7 +39,12 @@ export default function MapCard({ hotelName, pois }: MapCardProps) {
<ul className={styles.poiList}>
{pois.map((poi) => (
<li key={poi.name} className={styles.poiItem}>
<PoiMarker category={poi.category} skipBackground size={20} />
<PoiMarker
group={poi.group}
categoryName={poi.categoryName}
skipBackground
size={20}
/>
<Body color="black">{poi.name}</Body>
<Caption>{poi.distance} km</Caption>
</li>

View File

@@ -1,13 +1,13 @@
.mapCard {
display: grid;
position: absolute;
bottom: 15%;
left: var(--Spacing-x2);
right: var(--Spacing-x2);
bottom: 0;
left: 0;
right: 0;
background-color: var(--Base-Surface-Primary-light-Normal);
padding: var(--Spacing-x2);
box-shadow: 0 0 2.5rem 0 rgba(0, 0, 0, 0.12);
border-radius: var(--Corner-radius-Medium);
border-top-left-radius: var(--Corner-radius-Medium);
border-top-right-radius: var(--Corner-radius-Medium);
}
.ctaButton {

View File

@@ -18,9 +18,9 @@ export default async function StaticMap({
}: StaticMapProps) {
const intl = await getIntl()
const mapId = env.GOOGLE_STATIC_MAP_ID
const mapHeight = 785
const mapHeight = 640
const markerHeight = 100
const mapLatitudeInPx = mapHeight * 0.2
const mapLatitudeInPx = mapHeight * 0.25
const mapCoordinates = {
lat: calculateLatWithOffset(coordinates.lat, mapLatitudeInPx, zoomLevel),
lng: coordinates.lng,

View File

@@ -40,16 +40,28 @@
}
.mainSection {
grid-area: mainSection;
padding: var(--Spacing-x6) var(--Spacing-x4) 0;
padding: var(--Spacing-x6) var(--Spacing-x4);
}
.mapContainer {
display: flex;
grid-area: mapContainer;
align-self: start;
position: sticky;
top: 0;
justify-content: center;
width: 100%;
height: 100%;
background-color: var(--Base-Surface-Primary-light-Normal);
}
.mapWithCard {
position: sticky;
top: 0;
min-height: 500px; /* Fixed min to not cover the marker with the card */
height: calc(
100vh - var(--main-menu-desktop-height) -
var(--booking-widget-desktop-height)
); /* Full height without the header + booking widget */
max-height: 935px; /* Fixed max according to figma */
overflow: hidden;
}
.pageContainer > nav {

View File

@@ -124,8 +124,10 @@ export default async function HotelPage() {
{googleMapsApiKey ? (
<>
<aside className={styles.mapContainer}>
<StaticMap coordinates={coordinates} hotelName={hotelName} />
<MapCard hotelName={hotelName} pois={topThreePois} />
<div className={styles.mapWithCard}>
<StaticMap coordinates={coordinates} hotelName={hotelName} />
<MapCard hotelName={hotelName} pois={topThreePois} />
</div>
</aside>
<MobileMapToggle />
<DynamicMap

View File

@@ -24,7 +24,6 @@ export default function MobileMenu({
}: React.PropsWithChildren<MobileMenuProps>) {
const intl = useIntl()
const {
handleHamburgerClick,
toggleDropdown,
isHamburgerMenuOpen,
isMyPagesMobileMenuOpen,
@@ -32,6 +31,12 @@ export default function MobileMenu({
isFooterLanguageSwitcherOpen,
} = useDropdownStore()
const isHamburgerExtended =
isHamburgerMenuOpen ||
isMyPagesMobileMenuOpen ||
isHeaderLanguageSwitcherMobileOpen ||
isFooterLanguageSwitcherOpen
useHandleKeyUp((event: KeyboardEvent) => {
if (event.key === "Escape" && isHamburgerMenuOpen) {
toggleDropdown(DropdownTypeEnum.HamburgerMenu)
@@ -49,18 +54,15 @@ export default function MobileMenu({
<>
<button
type="button"
className={`${styles.hamburger} ${isHamburgerMenuOpen || isMyPagesMobileMenuOpen || isFooterLanguageSwitcherOpen || isHeaderLanguageSwitcherMobileOpen ? styles.isExpanded : ""}`}
className={`${styles.hamburger} ${isHamburgerExtended ? styles.isExpanded : ""}`}
aria-label={intl.formatMessage({
id: isHamburgerMenuOpen ? "Close menu" : "Open menu",
id: isHamburgerExtended ? "Close menu" : "Open menu",
})}
onClick={handleHamburgerClick}
onClick={() => toggleDropdown(DropdownTypeEnum.HamburgerMenu)}
>
<span className={styles.bar}></span>
</button>
<Modal
className={styles.modal}
isOpen={isHamburgerMenuOpen || isHeaderLanguageSwitcherMobileOpen}
>
<Modal className={styles.modal} isOpen={isHamburgerMenuOpen}>
<Dialog
className={styles.dialog}
aria-label={intl.formatMessage({ id: "Menu" })}

View File

@@ -36,11 +36,10 @@ export default function MyPagesMenu({
return (
<div className={styles.myPagesMenu}>
<MainMenuButton
className={styles.button}
onClick={() => toggleDropdown(DropdownTypeEnum.MyPagesMenu)}
>
<Avatar initials={getInitials(user.firstName, user.lastName)} />
<Subtitle type="two" className={styles.userName}>
<Subtitle type="two">
{intl.formatMessage({ id: "Hi" })} {user.firstName}!
</Subtitle>
<ChevronDownIcon

View File

@@ -26,21 +26,4 @@
min-width: 20rem;
z-index: var(--menu-overlay-z-index);
}
/* Triangle above dropdown */
.dropdown::before {
content: "";
position: absolute;
top: -1.25rem;
right: 2.4rem;
transform: rotate(180deg);
border-width: 0.75rem;
border-style: solid;
border-color: var(--Base-Surface-Primary-light-Normal) transparent
transparent transparent;
}
.dropdown.isExpanded {
display: block;
}
}

View File

@@ -1,18 +1,18 @@
"use client"
import Link from "next/link"
import { useIntl } from "react-intl"
import { MembershipLevelEnum } from "@/constants/membershipLevels"
import { logout } from "@/constants/routes/handleAuth"
import { trpc } from "@/lib/trpc/client"
import { ArrowRightIcon } from "@/components/Icons"
import Divider from "@/components/TempDesignSystem/Divider"
import Link from "@/components/TempDesignSystem/Link"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import useLang from "@/hooks/useLang"
import { useTrapFocus } from "@/hooks/useTrapFocus"
import { getMembershipLevelObject } from "@/utils/membershipLevel"
import styles from "./myPagesMenuContent.module.css"
@@ -28,12 +28,13 @@ export default function MyPagesMenuContent({
const lang = useLang()
const myPagesMenuContentRef = useTrapFocus()
const membershipLevel = membership?.membershipLevel
? getMembershipLevelObject(
membership.membershipLevel as MembershipLevelEnum,
lang
)
: null
const membershipLevel = trpc.contentstack.loyaltyLevels.byLevel.useQuery(
{
level: MembershipLevelEnum[membership?.membershipLevel!],
},
{ enabled: !!membership?.membershipLevel }
).data
const membershipPoints = membership?.currentPoints
const introClassName =
membershipLevel && membershipPoints
@@ -52,7 +53,7 @@ export default function MyPagesMenuContent({
{membershipLevel && membershipPoints ? (
<Caption className={styles.friendTypeWrapper}>
<span className={styles.friendType}>{membershipLevel.name}</span>
<span className={styles.friendPoints}>
<span>
{membershipPoints} {intl.formatMessage({ id: "points" })}
</span>
</Caption>
@@ -61,10 +62,7 @@ export default function MyPagesMenuContent({
<ul className={styles.groups}>
{navigation.menuItems.map((menuItem, idx) => (
<li
key={`${menuItem.display_sign_out_link}-${idx}`}
className={styles.group}
>
<li key={`${menuItem.display_sign_out_link}-${idx}`}>
<Divider color="subtle" className={styles.divider} />
<ul className={styles.menuItems}>
{menuItem.links.map((link) => (
@@ -72,6 +70,7 @@ export default function MyPagesMenuContent({
<Link
href={link.originalUrl || link.url}
onClick={toggleOpenStateFn}
variant="menu"
className={`${styles.link} ${menuItem.display_sign_out_link ? styles.smallLink : ""}`}
>
{link.linkText}
@@ -84,6 +83,7 @@ export default function MyPagesMenuContent({
<Link
href={logout[lang]}
prefetch={false}
variant="menu"
className={`${styles.link} ${styles.smallLink}`}
>
{intl.formatMessage({ id: "Log out" })}

View File

@@ -32,26 +32,6 @@
list-style: none;
}
.link {
display: flex;
align-items: center;
justify-content: space-between;
text-decoration: none;
padding: var(--Spacing-x1);
gap: var(--Spacing-x-one-and-half);
color: var(--Base-Text-High-contrast);
font-family: var(--typography-Body-Bold-fontFamily);
font-size: var(--typography-Body-Bold-fontSize);
font-weight: var(--typography-Body-Bold-fontWeight);
line-height: var(--typography-Body-Bold-lineHeight);
letter-spacing: var(--typography-Body-Bold-letterSpacing);
border-radius: var(--Corner-radius-Medium);
}
.link:hover {
background-color: var(--Base-Surface-Primary-light-Hover-alt);
}
.link.smallLink {
font-family: var(--typography-Body-Regular-fontFamily);
font-size: var(--typography-Body-Regular-fontSize);
@@ -64,6 +44,10 @@
opacity: 0;
}
.arrow {
flex-shrink: 0;
}
@media screen and (min-width: 768px) {
.myPagesMenuContent {
padding: var(--Spacing-x2) var(--Spacing-x4);

View File

@@ -0,0 +1,113 @@
"use client"
import useDropdownStore from "@/stores/main-menu"
import { ArrowRightIcon, ChevronLeftIcon } from "@/components/Icons"
import Card from "@/components/TempDesignSystem/Card"
import Link from "@/components/TempDesignSystem/Link"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { useTrapFocus } from "@/hooks/useTrapFocus"
import styles from "./megaMenu.module.css"
import { DropdownTypeEnum } from "@/types/components/dropdown/dropdown"
import type { MegaMenuProps } from "@/types/components/header/megaMenu"
export default function MegaMenu({
isMobile,
title,
seeAllLink,
submenu,
card,
}: MegaMenuProps) {
const { toggleMegaMenu, toggleDropdown, isHamburgerMenuOpen } =
useDropdownStore()
const megaMenuRef = useTrapFocus()
function handleNavigate() {
toggleMegaMenu(false)
if (isHamburgerMenuOpen) {
toggleDropdown(DropdownTypeEnum.HamburgerMenu)
}
}
return (
<nav className={styles.megaMenu}>
{isMobile ? (
<div className={styles.backWrapper}>
<button
type="button"
className={styles.backButton}
onClick={() => toggleMegaMenu(false)}
>
<ChevronLeftIcon color="red" />
<Subtitle type="one" color="burgundy">
{title}
</Subtitle>
</button>
</div>
) : null}
<div className={styles.megaMenuContent} ref={megaMenuRef}>
<div className={styles.seeAllLink}>
{seeAllLink?.link ? (
<Link
href={seeAllLink.link.url}
color="burgundy"
variant="icon"
onClick={handleNavigate}
>
{seeAllLink.title}
<ArrowRightIcon color="burgundy" />
</Link>
) : null}
</div>
<ul className={styles.submenus}>
{submenu.map((item) => (
<li key={item.title} className={styles.submenusItem}>
<Caption textTransform="uppercase" asChild>
<span className={styles.submenuTitle}>{item.title}</span>
</Caption>
<ul className={styles.submenu}>
{item.links.map(({ title, link }) =>
link ? (
<li key={title} className={styles.submenuItem}>
<Link
href={link.url}
variant="menu"
className={styles.link}
onClick={handleNavigate}
>
{title}
<ArrowRightIcon
color="burgundy"
className={styles.arrow}
/>
</Link>
</li>
) : null
)}
</ul>
</li>
))}
</ul>
{card ? (
<div className={styles.cardWrapper}>
<Card
className={styles.card}
backgroundImage={card.backgroundImage}
bodyText={card.body_text}
heading={card.heading}
primaryButton={card.primaryButton}
secondaryButton={card.secondaryButton}
scriptedTopTitle={card.scripted_top_title}
onPrimaryButtonClick={handleNavigate}
onSecondaryButtonClick={handleNavigate}
theme="image"
/>
</div>
) : null}
</div>
</nav>
)
}

View File

@@ -0,0 +1,140 @@
.megaMenuContent {
display: grid;
}
.seeAllLink {
display: flex;
padding: var(--Spacing-x2) var(--Spacing-x3);
align-items: center;
gap: var(--Spacing-x1);
background-color: var(--Base-Surface-Secondary-light-Normal);
}
.submenus {
list-style: none;
display: grid;
}
.submenuTitle {
padding-left: var(--Spacing-x1);
}
.submenu {
list-style: none;
}
.submenuItem {
display: flex;
}
.submenusItem {
display: grid;
gap: var(--Spacing-x1);
align-content: start;
}
.linkIcon {
display: none;
}
.link:not(:hover) .arrow {
opacity: 0;
}
.arrow {
flex-shrink: 0;
}
.backWrapper {
padding: var(--Spacing-x2);
}
.backButton {
background-color: transparent;
border: none;
padding: 0;
cursor: pointer;
display: flex;
align-items: center;
gap: var(--Spacing-x1);
width: 100%;
}
@media screen and (max-width: 767px) {
.megaMenu {
display: flex;
flex-direction: column;
height: 100%;
}
.megaMenuContent {
flex-grow: 1;
grid-template-rows: max-content 1fr;
gap: var(--Spacing-x2);
align-items: start;
}
.megaMenuContent:has(.cardWrapper) {
grid-template-rows: max-content 1fr max-content;
}
.submenus {
padding: var(--Spacing-x2);
}
.submenusItem:first-child {
padding-bottom: var(--Spacing-x2);
border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle);
}
.submenusItem:last-child {
padding-top: var(--Spacing-x3);
}
.cardWrapper {
background-color: var(--Base-Surface-Secondary-light-Normal);
padding: var(--Spacing-x4) var(--Spacing-x2);
}
}
@media screen and (min-width: 768px) {
.megaMenuContent {
grid-template-rows: auto 1fr;
grid-template-areas:
"seeAllLink"
"submenus";
width: 600px;
max-width: calc(100vw - var(--Spacing-x4));
}
.megaMenuContent:has(.cardWrapper) {
width: 900px;
grid-template-columns: repeat(3, 1fr);
grid-template-areas:
"seeAllLink seeAllLink card"
"submenus submenus card";
}
.seeAllLink {
grid-area: seeAllLink;
}
.submenus {
grid-area: submenus;
grid-template-columns: repeat(2, 1fr);
padding: var(--Spacing-x2) var(--Spacing-x3);
}
.submenusItem:first-child {
padding-right: var(--Spacing-x5);
border-right: 1px solid var(--Primary-Light-On-Surface-Divider-subtle);
}
.submenusItem:last-child {
padding-left: var(--Spacing-x5);
}
.cardWrapper {
grid-area: card;
}
.cardWrapper .card {
border-radius: 0;
}
}

View File

@@ -1,39 +1,59 @@
"use client"
import { useState } from "react"
import useDropdownStore from "@/stores/main-menu"
import { ChevronDownIcon, ChevronRightIcon } from "@/components/Icons"
import Link from "@/components/TempDesignSystem/Link"
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
import MainMenuButton from "../../MainMenuButton"
import MegaMenu from "../MegaMenu"
import styles from "./navigationMenuItem.module.css"
import type { NavigationMenuItemProps } from "@/types/components/header/navigationMenuItem"
export default function MenuItem({ item, isMobile }: NavigationMenuItemProps) {
const { submenu, title, link } = item
const [isExpanded, setIsExpanded] = useState(false) // TODO: Use store to manage this state when adding the menu itself.
const { openMegaMenu, toggleMegaMenu } = useDropdownStore()
const { submenu, title, link, seeAllLink, card } = item
const isMegaMenuOpen = openMegaMenu === title
function handleButtonClick() {
setIsExpanded((prev) => !prev)
}
useHandleKeyUp((event: KeyboardEvent) => {
if (event.key === "Escape" && isMegaMenuOpen) {
toggleMegaMenu(false)
}
})
return submenu.length ? (
<MainMenuButton
onClick={handleButtonClick}
className={`${styles.navigationMenuItem} ${isMobile ? styles.mobile : styles.desktop}`}
>
{title}
{isMobile ? (
<ChevronRightIcon className={`${styles.chevron}`} color="red" />
) : (
<ChevronDownIcon
className={`${styles.chevron} ${isExpanded ? styles.isExpanded : ""}`}
color="red"
/>
)}
</MainMenuButton>
<>
<MainMenuButton
onClick={() => toggleMegaMenu(title)}
className={`${styles.navigationMenuItem} ${isMobile ? styles.mobile : styles.desktop}`}
>
{title}
{isMobile ? (
<ChevronRightIcon className={`${styles.chevron}`} color="red" />
) : (
<ChevronDownIcon
className={`${styles.chevron} ${isMegaMenuOpen ? styles.isExpanded : ""}`}
color="red"
/>
)}
</MainMenuButton>
<div
className={`${styles.dropdown} ${isMegaMenuOpen ? styles.isExpanded : ""}`}
>
{isMegaMenuOpen ? (
<MegaMenu
isMobile={isMobile}
title={title}
seeAllLink={seeAllLink}
submenu={submenu}
card={card}
/>
) : null}
</div>
</>
) : (
<Link
className={`${styles.navigationMenuItem} ${isMobile ? styles.mobile : styles.desktop}`}

View File

@@ -12,3 +12,40 @@
.chevron.isExpanded {
transform: rotate(180deg);
}
.dropdown {
background-color: var(--Base-Surface-Primary-light-Normal);
z-index: var(--menu-overlay-z-index);
overflow: hidden;
}
@media screen and (max-width: 767px) {
.dropdown {
position: fixed;
width: 100%;
top: var(--main-menu-mobile-height);
right: -100vw;
bottom: 0;
transition: right 0.3s;
overflow-y: auto;
}
.dropdown.isExpanded {
right: 0;
}
}
@media screen and (min-width: 768px) {
.dropdown {
display: none;
position: absolute;
top: var(--main-menu-desktop-height);
/* top: var(--Spacing-x5); */
left: 50%;
transform: translateX(-50%);
border-radius: var(--Corner-radius-Large);
box-shadow: 0 0 14px 6px rgba(0, 0, 0, 0.1);
}
.dropdown.isExpanded {
display: grid;
}
}

View File

@@ -0,0 +1,40 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function AirplaneIcon({
className,
color,
...props
}: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<mask
id="mask0_4597_1552"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_4597_1552)">
<path
d="M9.9251 21.125L7.4501 16.525L2.8501 14.05L4.6251 12.3L8.2501 12.925L10.8001 10.375L2.8751 7L4.9751 4.85L14.6001 6.55L17.7001 3.45C18.0834 3.06667 18.5584 2.875 19.1251 2.875C19.6918 2.875 20.1668 3.06667 20.5501 3.45C20.9334 3.83333 21.1251 4.30417 21.1251 4.8625C21.1251 5.42083 20.9334 5.89167 20.5501 6.275L17.4251 9.4L19.1251 19L17.0001 21.125L13.6001 13.2L11.0501 15.75L11.7001 19.35L9.9251 21.125Z"
fill="#26201E"
/>
</g>
</svg>
)
}

View File

@@ -0,0 +1,40 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function BusinessIcon({
className,
color,
...props
}: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<mask
id="mask0_69_3397"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_69_3397)">
<path
d="M4.125 20.75C3.60937 20.75 3.16796 20.5664 2.80077 20.1992C2.43359 19.832 2.25 19.3906 2.25 18.875V7.875C2.25 7.35937 2.43359 6.91796 2.80077 6.55078C3.16796 6.18359 3.60937 6 4.125 6H8.15V4.1239C8.15 3.60797 8.33359 3.16667 8.70078 2.8C9.06796 2.43333 9.50937 2.25 10.025 2.25H13.975C14.4906 2.25 14.932 2.43359 15.2992 2.80078C15.6664 3.16796 15.85 3.60937 15.85 4.125V6H19.875C20.3906 6 20.832 6.18359 21.1992 6.55078C21.5664 6.91796 21.75 7.35937 21.75 7.875V18.875C21.75 19.3906 21.5664 19.832 21.1992 20.1992C20.832 20.5664 20.3906 20.75 19.875 20.75H4.125ZM10.025 6H13.975V4.125H10.025V6ZM19.875 14.925H14.8625V15.9125C14.8625 16.1708 14.7708 16.3917 14.5875 16.575C14.4042 16.7583 14.1833 16.85 13.925 16.85H10.075C9.81667 16.85 9.59583 16.7583 9.4125 16.575C9.22917 16.3917 9.1375 16.1708 9.1375 15.9125V14.925H4.125V18.875H19.875V14.925ZM11.0125 14.975H12.9875V13H11.0125V14.975ZM4.125 13.05H9.1375V12.0625C9.1375 11.8042 9.22917 11.5833 9.4125 11.4C9.59583 11.2167 9.81667 11.125 10.075 11.125H13.925C14.1833 11.125 14.4042 11.2167 14.5875 11.4C14.7708 11.5833 14.8625 11.8042 14.8625 12.0625V13.05H19.875V7.875H4.125V13.05Z"
fill="#26201E"
/>
</g>
</svg>
)
}

View File

@@ -8,16 +8,29 @@ export default function CameraIcon({ className, color, ...props }: IconProps) {
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="20"
height="18"
viewBox="0 0 20 18"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<path
d="M10 14.375C11.225 14.375 12.2625 13.95 13.1125 13.1C13.9625 12.25 14.3875 11.2125 14.3875 9.9875C14.3875 8.7625 13.9625 7.725 13.1125 6.875C12.2625 6.025 11.225 5.6 10 5.6C8.775 5.6 7.7375 6.025 6.8875 6.875C6.0375 7.725 5.6125 8.7625 5.6125 9.9875C5.6125 11.2125 6.0375 12.25 6.8875 13.1C7.7375 13.95 8.775 14.375 10 14.375ZM9.9949 12.5C9.28997 12.5 8.69583 12.2583 8.2125 11.775C7.72917 11.2917 7.4875 10.6975 7.4875 9.9926C7.4875 9.28767 7.72917 8.69183 8.2125 8.2051C8.69583 7.71837 9.28997 7.475 9.9949 7.475C10.6998 7.475 11.2957 7.71837 11.7824 8.2051C12.2691 8.69183 12.5125 9.28767 12.5125 9.9926C12.5125 10.6975 12.2691 11.2917 11.7824 11.775C11.2957 12.2583 10.6998 12.5 9.9949 12.5ZM2.125 17.75C1.60937 17.75 1.16796 17.5664 0.800775 17.1992C0.433592 16.832 0.25 16.3906 0.25 15.875V4.1125C0.25 3.59687 0.433592 3.15546 0.800775 2.78828C1.16796 2.42109 1.60937 2.2375 2.125 2.2375H5.275L6.5125 0.875C6.68652 0.6827 6.89613 0.530459 7.14133 0.418275C7.38651 0.306092 7.64357 0.25 7.9125 0.25H12.0875C12.3564 0.25 12.6135 0.306092 12.8587 0.418275C13.1039 0.530459 13.3135 0.6827 13.4875 0.875L14.725 2.2375H17.875C18.3906 2.2375 18.832 2.42109 19.1992 2.78828C19.5664 3.15546 19.75 3.59687 19.75 4.1125V15.875C19.75 16.3906 19.5664 16.832 19.1992 17.1992C18.832 17.5664 18.3906 17.75 17.875 17.75H2.125ZM2.125 15.875H17.875V4.1125H13.8853L12.0875 2.125H7.9162L6.125 4.1125H2.125V15.875Z"
fill="#26201E"
/>
<mask
id="mask0_69_3288"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_69_3288)">
<path
d="M12 17.375C13.225 17.375 14.2625 16.95 15.1125 16.1C15.9625 15.25 16.3875 14.2125 16.3875 12.9875C16.3875 11.7625 15.9625 10.725 15.1125 9.875C14.2625 9.025 13.225 8.6 12 8.6C10.775 8.6 9.7375 9.025 8.8875 9.875C8.0375 10.725 7.6125 11.7625 7.6125 12.9875C7.6125 14.2125 8.0375 15.25 8.8875 16.1C9.7375 16.95 10.775 17.375 12 17.375ZM11.9949 15.5C11.29 15.5 10.6958 15.2583 10.2125 14.775C9.72917 14.2917 9.4875 13.6975 9.4875 12.9926C9.4875 12.2877 9.72917 11.6918 10.2125 11.2051C10.6958 10.7184 11.29 10.475 11.9949 10.475C12.6998 10.475 13.2957 10.7184 13.7824 11.2051C14.2691 11.6918 14.5125 12.2877 14.5125 12.9926C14.5125 13.6975 14.2691 14.2917 13.7824 14.775C13.2957 15.2583 12.6998 15.5 11.9949 15.5ZM4.125 20.75C3.60937 20.75 3.16796 20.5664 2.80077 20.1992C2.43359 19.832 2.25 19.3906 2.25 18.875V7.1125C2.25 6.59687 2.43359 6.15546 2.80077 5.78828C3.16796 5.42109 3.60937 5.2375 4.125 5.2375H7.275L8.5125 3.875C8.68652 3.6827 8.89613 3.53046 9.14133 3.41828C9.38651 3.30609 9.64357 3.25 9.9125 3.25H14.0875C14.3564 3.25 14.6135 3.30609 14.8587 3.41828C15.1039 3.53046 15.3135 3.6827 15.4875 3.875L16.725 5.2375H19.875C20.3906 5.2375 20.832 5.42109 21.1992 5.78828C21.5664 6.15546 21.75 6.59687 21.75 7.1125V18.875C21.75 19.3906 21.5664 19.832 21.1992 20.1992C20.832 20.5664 20.3906 20.75 19.875 20.75H4.125ZM4.125 18.875H19.875V7.1125H15.8853L14.0875 5.125H9.9162L8.125 7.1125H4.125V18.875Z"
fill="#26201E"
/>
</g>
</svg>
)
}

View File

@@ -0,0 +1,40 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function ChevronRightSmallIcon({
className,
color,
...props
}: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<mask
id="mask0_69_3311"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_69_3311)">
<path
d="M12.65 12L8.77495 8.12497C8.59995 7.94997 8.51245 7.73538 8.51245 7.48122C8.51245 7.22705 8.59995 7.0083 8.77495 6.82497C8.94995 6.64163 9.16662 6.54788 9.42495 6.54372C9.68328 6.53955 9.90412 6.62913 10.0875 6.81247L14.6125 11.3375C14.7041 11.4291 14.7729 11.5312 14.8187 11.6437C14.8645 11.7562 14.8875 11.875 14.8875 12C14.8875 12.125 14.8645 12.2437 14.8187 12.3562C14.7729 12.4687 14.7041 12.5708 14.6125 12.6625L10.0875 17.1875C9.90412 17.3708 9.68328 17.4604 9.42495 17.4562C9.16662 17.4521 8.94995 17.3583 8.77495 17.175C8.59995 16.9916 8.51245 16.7729 8.51245 16.5187C8.51245 16.2646 8.59995 16.05 8.77495 15.875L12.65 12Z"
fill="#26201E"
/>
</g>
</svg>
)
}

View File

@@ -0,0 +1,27 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function SnowflakeIcon({
className,
color,
...props
}: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
width="48"
height="48"
viewBox="0 0 48 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M24.0005 48C23.6026 48 23.2211 47.842 22.9398 47.5607C22.6585 47.2794 22.5005 46.8978 22.5005 46.5V42.621L20.5625 44.562C20.2808 44.8433 19.8989 45.0011 19.5009 45.0008C19.1028 45.0006 18.7212 44.8422 18.44 44.5605C18.1587 44.2788 18.0008 43.897 18.0011 43.4989C18.0014 43.1009 18.1598 42.7193 18.4415 42.438L22.5005 38.379V26.598L12.3005 32.487L10.8125 38.037C10.709 38.4213 10.4572 38.7488 10.1123 38.9474C9.7674 39.146 9.35775 39.1994 8.97345 39.096C8.58915 38.9926 8.26168 38.7407 8.06308 38.3958C7.86448 38.0509 7.81102 37.6413 7.91445 37.257L8.62545 34.611L5.26545 36.549C4.92112 36.7448 4.51332 36.7964 4.13106 36.6926C3.74879 36.5888 3.4231 36.3381 3.22504 35.995C3.02699 35.652 2.97265 35.2446 3.0739 34.8616C3.17514 34.4787 3.42374 34.1513 3.76545 33.951L7.12545 32.013L4.47345 31.302C4.28317 31.2508 4.10483 31.1626 3.94863 31.0425C3.79243 30.9223 3.66142 30.7726 3.56308 30.6018C3.46475 30.4311 3.40101 30.2426 3.3755 30.0472C3.35 29.8518 3.36324 29.6533 3.41445 29.463C3.46567 29.2727 3.55386 29.0944 3.674 28.9382C3.79413 28.782 3.94386 28.651 4.11463 28.5526C4.28539 28.4543 4.47386 28.3906 4.66926 28.3651C4.86466 28.3396 5.06317 28.3528 5.25345 28.404L10.7975 29.889L21.0005 24L10.8005 18.111L5.25045 19.596C4.86865 19.6912 4.46475 19.6325 4.12581 19.4326C3.78687 19.2328 3.54003 18.9077 3.4385 18.5276C3.33697 18.1474 3.38888 17.7426 3.58303 17.4004C3.77719 17.0581 4.09805 16.8059 4.47645 16.698L7.12545 15.987L3.76545 14.049C3.59348 13.9512 3.44255 13.8204 3.32135 13.6641C3.20015 13.5077 3.11107 13.3289 3.05924 13.138C3.00741 12.9471 2.99385 12.7479 3.01935 12.5517C3.04485 12.3555 3.1089 12.1663 3.20781 11.995C3.30672 11.8237 3.43854 11.6736 3.59568 11.5535C3.75283 11.4333 3.93218 11.3454 4.12343 11.2949C4.31468 11.2443 4.51404 11.2321 4.71004 11.2589C4.90604 11.2857 5.0948 11.351 5.26545 11.451L8.62545 13.389L7.91445 10.74C7.85987 10.5485 7.84395 10.3481 7.86761 10.1504C7.89128 9.95275 7.95407 9.76176 8.05231 9.58859C8.15055 9.41542 8.28228 9.26354 8.43983 9.14181C8.59737 9.02007 8.77757 8.93092 8.96992 8.87955C9.16227 8.82817 9.36293 8.81561 9.56019 8.84258C9.75745 8.86955 9.94737 8.93552 10.1189 9.03665C10.2904 9.13778 10.44 9.27203 10.5591 9.43159C10.6782 9.59115 10.7643 9.77281 10.8125 9.966L12.2975 15.513L22.5005 21.402V9.621L18.4415 5.562C18.302 5.42273 18.1913 5.25736 18.1158 5.07532C18.0402 4.89329 18.0013 4.69815 18.0011 4.50106C18.001 4.30397 18.0397 4.10878 18.115 3.92664C18.1903 3.74449 18.3007 3.57896 18.44 3.4395C18.5792 3.30004 18.7446 3.18937 18.9266 3.11382C19.1087 3.03826 19.3038 2.99931 19.5009 2.99917C19.698 2.99903 19.8932 3.03771 20.0753 3.113C20.2575 3.1883 20.423 3.29873 20.5625 3.438L22.5005 5.379V1.5C22.5005 1.10218 22.6585 0.720644 22.9398 0.43934C23.2211 0.158035 23.6026 0 24.0005 0C24.3983 0 24.7798 0.158035 25.0611 0.43934C25.3424 0.720644 25.5005 1.10218 25.5005 1.5V5.379L27.4415 3.438C27.7231 3.15674 28.105 2.99888 28.503 2.99917C28.9011 2.99945 29.2827 3.15784 29.564 3.4395C29.8452 3.72116 30.0031 4.10301 30.0028 4.50106C30.0025 4.89911 29.8441 5.28074 29.5625 5.562L25.5005 9.621V21.402L35.7005 15.513L37.1885 9.963C37.2919 9.5787 37.5437 9.25123 37.8886 9.05263C38.2335 8.85403 38.6432 8.80057 39.0275 8.904C39.4118 9.00743 39.7392 9.25929 39.9378 9.60417C40.1364 9.94905 40.1899 10.3587 40.0865 10.743L39.3785 13.389L42.7385 11.451C43.0828 11.2552 43.4906 11.2036 43.8728 11.3074C44.2551 11.4112 44.5808 11.6619 44.7789 12.005C44.9769 12.348 45.0313 12.7554 44.93 13.1384C44.8288 13.5213 44.5802 13.8487 44.2385 14.049L40.8785 15.987L43.5275 16.698C43.9118 16.8014 44.2392 17.0533 44.4378 17.3982C44.6364 17.7431 44.6899 18.1527 44.5865 18.537C44.483 18.9213 44.2312 19.2488 43.8863 19.4474C43.5414 19.646 43.1318 19.6994 42.7475 19.596L37.2035 18.111L27.0005 24L37.2005 29.889L42.7475 28.404C43.1318 28.301 43.5412 28.3548 43.8858 28.5537C44.2304 28.7526 44.4819 29.0802 44.585 29.4645C44.688 29.8488 44.6341 30.2583 44.4353 30.6029C44.2364 30.9475 43.9088 31.199 43.5245 31.302L40.8755 32.013L44.2355 33.951C44.5772 34.1513 44.8258 34.4787 44.927 34.8616C45.0283 35.2446 44.9739 35.652 44.7759 35.995C44.5778 36.3381 44.2521 36.5888 43.8699 36.6926C43.4876 36.7964 43.0798 36.7448 42.7355 36.549L39.3755 34.611L40.0835 37.26C40.138 37.4515 40.154 37.6519 40.1303 37.8496C40.1066 38.0472 40.0438 38.2382 39.9456 38.4114C39.8474 38.5846 39.7156 38.7365 39.5581 38.8582C39.4005 38.9799 39.2203 39.0691 39.028 39.1205C38.8356 39.1718 38.635 39.1844 38.4377 39.1574C38.2405 39.1305 38.0505 39.0645 37.879 38.9633C37.7075 38.8622 37.5579 38.728 37.4388 38.5684C37.3197 38.4089 37.2336 38.2272 37.1855 38.034L35.7005 32.487L25.5005 26.598V38.379L29.5595 42.438C29.8411 42.7193 29.9995 43.1009 29.9998 43.4989C30.0001 43.897 29.8422 44.2788 29.561 44.5605C29.2797 44.8422 28.8981 45.0006 28.5 45.0008C28.102 45.0011 27.7201 44.8433 27.4385 44.562L25.4975 42.621V46.5C25.4975 46.8978 25.3394 47.2794 25.0581 47.5607C24.7768 47.842 24.3983 48 24.0005 48Z"
fill="#CD0921"
/>
</svg>
)
}

View File

@@ -6,9 +6,11 @@ import TripAdvisorIcon from "./TripAdvisor"
import {
AccessibilityIcon,
AccountCircleIcon,
AirplaneIcon,
ArrowRightIcon,
BarIcon,
BikingIcon,
BusinessIcon,
CalendarIcon,
CameraIcon,
CellphoneIcon,
@@ -17,6 +19,7 @@ import {
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
ChevronRightSmallIcon,
CloseIcon,
CloseLarge,
CoffeeIcon,
@@ -49,6 +52,7 @@ import {
SearchIcon,
ServiceIcon,
ShoppingIcon,
SnowflakeIcon,
StarFilledIcon,
TrainIcon,
TshirtWashIcon,
@@ -64,12 +68,16 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
return AccessibilityIcon
case IconName.AccountCircle:
return AccountCircleIcon
case IconName.Airplane:
return AirplaneIcon
case IconName.ArrowRight:
return ArrowRightIcon
case IconName.Bar:
return BarIcon
case IconName.Biking:
return BikingIcon
case IconName.Business:
return BusinessIcon
case IconName.Calendar:
return CalendarIcon
case IconName.Camera:
@@ -88,6 +96,8 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
return ChevronLeftIcon
case IconName.ChevronRight:
return ChevronRightIcon
case IconName.ChevronRightSmall:
return ChevronRightSmallIcon
case IconName.Close:
return CloseIcon
case IconName.CloseLarge:
@@ -154,6 +164,8 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
return ServiceIcon
case IconName.Shopping:
return ShoppingIcon
case IconName.Snowflake:
return SnowflakeIcon
case IconName.StarFilled:
return StarFilledIcon
case IconName.Train:

View File

@@ -1,8 +1,10 @@
export { default as AccessibilityIcon } from "./Accessibility"
export { default as AccountCircleIcon } from "./AccountCircle"
export { default as AirplaneIcon } from "./Airplane"
export { default as ArrowRightIcon } from "./ArrowRight"
export { default as BarIcon } from "./Bar"
export { default as BikingIcon } from "./Biking"
export { default as BusinessIcon } from "./Business"
export { default as CalendarIcon } from "./Calendar"
export { default as CameraIcon } from "./Camera"
export { default as CellphoneIcon } from "./Cellphone"
@@ -11,6 +13,7 @@ export { default as CheckCircleIcon } from "./CheckCircle"
export { default as ChevronDownIcon } from "./ChevronDown"
export { default as ChevronLeftIcon } from "./ChevronLeft"
export { default as ChevronRightIcon } from "./ChevronRight"
export { default as ChevronRightSmallIcon } from "./ChevronRightSmall"
export { default as CloseIcon } from "./Close"
export { default as CloseLarge } from "./CloseLarge"
export { default as CoffeeIcon } from "./Coffee"
@@ -48,6 +51,7 @@ export { default as ScandicLogoIcon } from "./ScandicLogo"
export { default as SearchIcon } from "./Search"
export { default as ServiceIcon } from "./Service"
export { default as ShoppingIcon } from "./Shopping"
export { default as SnowflakeIcon } from "./Snowflake"
export { default as StarFilledIcon } from "./StarFilled"
export { default as TrainIcon } from "./Train"
export { default as TshirtWashIcon } from "./TshirtWash"

View File

@@ -1,11 +0,0 @@
import { PropsWithChildren } from "react"
import styles from "./intro.module.css"
export default async function Intro({ children }: PropsWithChildren) {
return (
<div className={styles.intro}>
<div className={styles.content}>{children}</div>
</div>
)
}

View File

@@ -1,16 +0,0 @@
.intro {
max-width: var(--max-width-content);
margin: 0 auto;
}
.content {
display: grid;
max-width: var(--max-width-text-block);
gap: var(--Spacing-x2);
}
@media (min-width: 768px) {
.content {
gap: var(--Spacing-x3);
}
}

View File

@@ -85,17 +85,6 @@
transform: rotate(180deg);
}
/* Triangle dropdown */
.dropdown::before {
content: "";
position: absolute;
right: 2.4rem;
border-width: 0.75rem;
border-style: solid;
border-color: var(--Base-Surface-Primary-light-Normal) transparent
transparent transparent;
}
.bottom .dropdown {
transition: none;
height: auto;

View File

@@ -0,0 +1,38 @@
import { MembershipLevelEnum } from "@/constants/membershipLevels"
import {
BestFriend,
CloseFriend,
DearFriend,
GoodFriend,
LoyalFriend,
NewFriend,
TrueFriend,
} from "@/components/Levels"
import type { MembershipLevelIconProps } from "@/types/components/myPages/membership"
export default function MembershipLevelIcon({
level,
color = "pale",
...props
}: MembershipLevelIconProps) {
switch (level) {
case MembershipLevelEnum.L1:
return <NewFriend color={color} {...props} />
case MembershipLevelEnum.L2:
return <GoodFriend color={color} {...props} />
case MembershipLevelEnum.L3:
return <CloseFriend color={color} {...props} />
case MembershipLevelEnum.L4:
return <DearFriend color={color} {...props} />
case MembershipLevelEnum.L5:
return <LoyalFriend color={color} {...props} />
case MembershipLevelEnum.L6:
return <TrueFriend color={color} {...props} />
case MembershipLevelEnum.L7:
return <BestFriend color={color} {...props} />
default:
return null
}
}

View File

@@ -2,7 +2,13 @@ import { levelVariants } from "../variants"
import type { LevelProps } from "../levels"
export default function BestFriend({ className, color, ...props }: LevelProps) {
export default function BestFriend({
className,
color,
height = "75",
width = "159",
...props
}: LevelProps) {
const classNames = levelVariants({
className,
color,
@@ -11,9 +17,9 @@ export default function BestFriend({ className, color, ...props }: LevelProps) {
<svg
className={classNames}
fill="none"
height="75"
height={height}
viewBox="0 0 159 75"
width="159"
width={width}
xmlns="http://www.w3.org/2000/svg"
{...props}
>

View File

@@ -5,6 +5,8 @@ import type { LevelProps } from "../levels"
export default function CloseFriend({
className,
color,
height = "75",
width = "158",
...props
}: LevelProps) {
const classNames = levelVariants({
@@ -15,9 +17,9 @@ export default function CloseFriend({
<svg
className={classNames}
fill="none"
height="75"
height={height}
viewBox="0 0 158 75"
width="158"
width={width}
xmlns="http://www.w3.org/2000/svg"
{...props}
>

View File

@@ -2,7 +2,13 @@ import { levelVariants } from "../variants"
import type { LevelProps } from "../levels"
export default function DearFriend({ className, color, ...props }: LevelProps) {
export default function DearFriend({
className,
color,
height = "75",
width = "159",
...props
}: LevelProps) {
const classNames = levelVariants({
className,
color,
@@ -11,9 +17,9 @@ export default function DearFriend({ className, color, ...props }: LevelProps) {
<svg
className={classNames}
fill="none"
height="75"
height={height}
viewBox="0 0 159 75"
width="159"
width={width}
xmlns="http://www.w3.org/2000/svg"
{...props}
>

View File

@@ -2,7 +2,13 @@ import { levelVariants } from "../variants"
import type { LevelProps } from "../levels"
export default function GoodFriend({ className, color, ...props }: LevelProps) {
export default function GoodFriend({
className,
color,
height = "75",
width = "159",
...props
}: LevelProps) {
const classNames = levelVariants({
className,
color,
@@ -11,9 +17,9 @@ export default function GoodFriend({ className, color, ...props }: LevelProps) {
<svg
className={classNames}
fill="none"
height="75"
height={height}
viewBox="0 0 159 75"
width="159"
width={width}
xmlns="http://www.w3.org/2000/svg"
{...props}
>

View File

@@ -5,6 +5,8 @@ import type { LevelProps } from "../levels"
export default function LoyalFriend({
className,
color,
height = "75",
width = "158",
...props
}: LevelProps) {
const classNames = levelVariants({
@@ -15,9 +17,9 @@ export default function LoyalFriend({
<svg
className={classNames}
fill="none"
height="75"
height={height}
viewBox="0 0 158 75"
width="158"
width={width}
xmlns="http://www.w3.org/2000/svg"
{...props}
>

View File

@@ -2,7 +2,13 @@ import { levelVariants } from "../variants"
import type { LevelProps } from "../levels"
export default function NewFriend({ className, color, ...props }: LevelProps) {
export default function NewFriend({
className,
color,
height = "75",
width = "159",
...props
}: LevelProps) {
const classNames = levelVariants({
className,
color,
@@ -11,9 +17,9 @@ export default function NewFriend({ className, color, ...props }: LevelProps) {
<svg
className={classNames}
fill="none"
height="75"
height={height}
viewBox="0 0 159 75"
width="159"
width={width}
xmlns="http://www.w3.org/2000/svg"
{...props}
>

View File

@@ -2,7 +2,13 @@ import { levelVariants } from "../variants"
import type { LevelProps } from "../levels"
export default function TrueFriend({ className, color, ...props }: LevelProps) {
export default function TrueFriend({
className,
color,
height = "75",
width = "159",
...props
}: LevelProps) {
const classNames = levelVariants({
className,
color,
@@ -11,9 +17,9 @@ export default function TrueFriend({ className, color, ...props }: LevelProps) {
<svg
className={classNames}
fill="none"
height="75"
height={height}
viewBox="0 0 159 75"
width="159"
width={width}
xmlns="http://www.w3.org/2000/svg"
{...props}
>

View File

@@ -4,4 +4,7 @@ import type { VariantProps } from "class-variance-authority"
export interface LevelProps
extends Omit<React.HTMLAttributes<HTMLOrSVGElement>, "color">,
VariantProps<typeof levelVariants> {}
VariantProps<typeof levelVariants> {
height?: string
width?: string
}

View File

@@ -1,19 +1,20 @@
import { getIconByIconName } from "@/components/Icons/get-icon-by-icon-name"
import { getCategoryIconName } from "../utils"
import { getIconByPoiGroupAndCategory } from "../utils"
import { poiVariants } from "./variants"
import type { PoiMarkerProps } from "@/types/components/maps/poiMarker"
export default function PoiMarker({
category,
group,
categoryName,
skipBackground,
size = 16,
className = "",
}: PoiMarkerProps) {
const iconName = getCategoryIconName(category)
const iconName = getIconByPoiGroupAndCategory(group, categoryName)
const Icon = iconName ? getIconByIconName(iconName) : null
const classNames = poiVariants({ category, skipBackground, className })
const classNames = poiVariants({ group, skipBackground, className })
return Icon ? (
<span className={classNames}>

View File

@@ -7,46 +7,29 @@ This will be handled later. */
align-items: center;
padding: var(--Spacing-x-half);
border-radius: var(--Corner-radius-Rounded);
background-color: var(--Scandic-Beige-90);
}
.airport,
.amusementPark,
.busTerminal,
.fair,
.hospital,
.hotel,
.marketingCity {
background-color: var(--UI-Text-Placeholder);
}
.museum {
background: var(--Base-Interactive-Surface-Secondary-normal);
.attractions {
background-color: var(--Base-Interactive-Surface-Secondary-normal);
}
.nearbyCompanies,
.parkingGarage {
.business {
background-color: var(--Scandic-Yellow-50);
}
.restaurant {
background: var(--Scandic-Peach-50);
.location {
background-color: var(--UI-Text-Placeholder);
}
.shopping {
background: var(--Base-Interactive-Surface-Primary-normal);
.parking {
background-color: var(--UI-Text-Active);
}
.sports,
.theatre {
.publicTransport {
background-color: var(--Base-Interactive-Surface-Tertiary-normal);
}
.tourist {
background: var(--Scandic-Yellow-60);
}
.transportations {
background: var(--Base-Interactive-Surface-Tertiary-normal);
}
.zoo {
.shoppingDining {
background-color: var(--Base-Interactive-Surface-Primary-normal);
}
.icon.transparent {
background: transparent;
background-color: transparent;
padding: 0;
}

View File

@@ -2,26 +2,17 @@ import { cva } from "class-variance-authority"
import styles from "./poi.module.css"
import { PointOfInterestGroupEnum } from "@/types/hotel"
export const poiVariants = cva(styles.icon, {
variants: {
category: {
Airport: styles.airport,
"Amusement park": styles.amusementPark,
"Bus terminal": styles.busTerminal,
Fair: styles.fair,
Hospital: styles.hospital,
Hotel: styles.hotel,
"Marketing city": styles.marketingCity,
Museum: styles.museum,
"Nearby companies": styles.nearbyCompanies,
"Parking / Garage": styles.parkingGarage,
Restaurant: styles.restaurant,
Shopping: styles.shopping,
Sports: styles.sports,
Theatre: styles.theatre,
Tourist: styles.tourist,
Transportations: styles.transportations,
Zoo: styles.zoo,
group: {
[PointOfInterestGroupEnum.ATTRACTIONS]: styles.attractions,
[PointOfInterestGroupEnum.BUSINESS]: styles.business,
[PointOfInterestGroupEnum.LOCATION]: styles.location,
[PointOfInterestGroupEnum.PARKING]: styles.parking,
[PointOfInterestGroupEnum.PUBLIC_TRANSPORT]: styles.publicTransport,
[PointOfInterestGroupEnum.SHOPPING_DINING]: styles.shoppingDining,
},
skipBackground: {
true: styles.transparent,

View File

@@ -1,21 +1,30 @@
import { IconName } from "@/types/components/icon"
import type { PointOfInterestCategory } from "@/types/hotel"
import {
PointOfInterestCategoryNameEnum,
PointOfInterestGroupEnum,
} from "@/types/hotel"
/* 2024-09-18: At the moment, the icons for the different categories is unknown.
This will be handled later. */
export function getCategoryIconName(category?: PointOfInterestCategory | null) {
switch (category) {
case "Transportations":
return IconName.Train
case "Shopping":
export function getIconByPoiGroupAndCategory(
group: PointOfInterestGroupEnum,
category?: PointOfInterestCategoryNameEnum
) {
switch (group) {
case PointOfInterestGroupEnum.PUBLIC_TRANSPORT:
return category === PointOfInterestCategoryNameEnum.AIRPORT
? IconName.Airplane
: IconName.Train
case PointOfInterestGroupEnum.ATTRACTIONS:
return category === PointOfInterestCategoryNameEnum.MUSEUM
? IconName.Museum
: IconName.Camera
case PointOfInterestGroupEnum.BUSINESS:
return IconName.Business
case PointOfInterestGroupEnum.PARKING:
return IconName.Parking
case PointOfInterestGroupEnum.SHOPPING_DINING:
return IconName.Shopping
case "Museum":
return IconName.Museum
case "Tourist":
return IconName.Cultural
case "Restaurant":
return IconName.Restaurant
case PointOfInterestGroupEnum.LOCATION:
default:
return IconName.StarFilled
return IconName.Location
}
}

View File

@@ -1,10 +1,11 @@
.container {
align-items: center;
position: relative;
display: flex;
border-radius: var(--Corner-radius-Medium);
align-items: center;
flex-direction: column;
height: 320px; /* Fixed height from Figma */
justify-content: center;
border-radius: var(--Corner-radius-Medium);
height: 320px; /* Fixed height from Figma */
margin-right: var(--Spacing-x2);
text-align: center;
width: 100%;
@@ -12,11 +13,29 @@
overflow: hidden;
}
.imageWrapper {
display: flex;
}
.imageWrapper::after {
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.36) 50%,
rgba(0, 0, 0, 0.75) 100%
);
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.image {
object-fit: cover;
overflow: hidden;
width: 100%;
height: auto;
height: 100%;
min-height: 320px; /* Fixed height from Figma */
}
@@ -78,9 +97,10 @@
.themeImage {
--font-color: var(--Base-Text-Inverted);
--script-color: var(--Base-Text-Inverted);
}
border: 1px; /* px from Figma */
border-color: var(--Base-Border-Subtle);
.themeImage .content {
position: absolute;
}
.scriptContainer {
@@ -88,7 +108,7 @@
gap: var(--Spacing-x1);
}
span.scriptedTitle {
.scriptedTitle {
color: var(--script-color);
padding: var(--Spacing-x1);
margin: 0;
@@ -98,7 +118,7 @@ span.scriptedTitle {
color: var(--font-color);
}
p.bodyText {
.bodyText {
color: var(--font-color);
}

View File

@@ -11,16 +11,18 @@ export interface CardProps
href: string
title: string
openInNewTab?: boolean
isExternal: boolean
}
isExternal?: boolean
} | null
secondaryButton?: {
href: string
title: string
openInNewTab?: boolean
isExternal: boolean
}
isExternal?: boolean
} | null
scriptedTopTitle?: string | null
heading?: string | null
bodyText?: string | null
backgroundImage?: ImageVaultAsset
onPrimaryButtonClick?: () => void
onSecondaryButtonClick?: () => void
}

View File

@@ -21,24 +21,28 @@ export default function Card({
className,
theme,
backgroundImage,
onPrimaryButtonClick,
onSecondaryButtonClick,
}: CardProps) {
const { buttonTheme, primaryLinkColor, secondaryLinkColor } = getTheme(theme)
return (
<article
className={cardVariants({
className,
theme,
className,
})}
>
{backgroundImage && (
<Image
src={backgroundImage.url}
className={styles.image}
alt={backgroundImage.meta.alt || backgroundImage.title}
width={420}
height={320}
/>
<div className={styles.imageWrapper}>
<Image
src={backgroundImage.url}
className={styles.image}
alt={backgroundImage.meta.alt || backgroundImage.title}
width={backgroundImage.dimensions.width || 420}
height={backgroundImage.dimensions.height || 320}
/>
</div>
)}
<div className={styles.content}>
{scriptedTopTitle ? (
@@ -73,6 +77,7 @@ export default function Card({
href={primaryButton.href}
target={primaryButton.openInNewTab ? "_blank" : undefined}
color={primaryLinkColor}
onClick={onPrimaryButtonClick}
>
{primaryButton.title}
</Link>
@@ -90,6 +95,7 @@ export default function Card({
href={secondaryButton.href}
target={secondaryButton.openInNewTab ? "_blank" : undefined}
color={secondaryLinkColor}
onClick={onSecondaryButtonClick}
>
{secondaryButton.title}
</Link>

View File

@@ -196,3 +196,25 @@
letter-spacing: var(--typography-Caption-Bold-letterSpacing);
line-height: var(--typography-Caption-Bold-lineHeight);
}
.menu {
display: flex;
align-items: center;
justify-content: space-between;
text-decoration: none;
width: 100%;
padding: var(--Spacing-x1);
gap: var(--Spacing-x-one-and-half);
color: var(--Base-Text-High-contrast);
font-family: var(--typography-Body-Bold-fontFamily);
font-size: var(--typography-Body-Bold-fontSize);
font-weight: var(--typography-Body-Bold-fontWeight);
line-height: var(--typography-Body-Bold-lineHeight);
letter-spacing: var(--typography-Body-Bold-letterSpacing);
border-radius: var(--Corner-radius-Medium);
}
.menu:hover {
color: var(--Base-Text-High-contrast);
background-color: var(--Base-Surface-Primary-light-Hover-alt);
}

View File

@@ -31,6 +31,7 @@ export const linkVariants = cva(styles.link, {
icon: styles.icon,
underscored: styles.underscored,
myPageMobileDropdown: styles.myPageMobileDropdown,
menu: styles.menu,
shortcut: styles.shortcut,
sidebar: styles.sidebar,
tab: styles.tab,
@@ -40,6 +41,7 @@ export const linkVariants = cva(styles.link, {
color: "black",
variant: "default",
textDecoration: "none",
size: "regular",
},
compoundVariants: [
{

View File

@@ -0,0 +1,14 @@
.linkChip {
display: flex;
align-items: center;
gap: var(--Spacing-x-half);
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
border-radius: var(--Corner-radius-Small);
background-color: var(--Base-Button-Inverted-Fill-Normal);
transition: background-color 0.3s;
text-decoration: none;
}
.linkChip:hover {
background-color: var(--Base-Button-Inverted-Fill-Hover-alt);
}

View File

@@ -0,0 +1,4 @@
export interface LinkChipProps {
url: string
title: string
}

View File

@@ -0,0 +1,19 @@
import Link from "next/link"
import { ChevronRightSmallIcon } from "@/components/Icons"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import styles from "./chip.module.css"
import type { LinkChipProps } from "./chip"
export default function LinkChip({ url, title }: LinkChipProps) {
return (
<Caption textTransform="bold" color="burgundy" asChild>
<Link href={url} className={styles.linkChip}>
{title}
<ChevronRightSmallIcon color="burgundy" width={20} height={20} />
</Link>
</Caption>
)
}

View File

@@ -0,0 +1,21 @@
import LinkChip from "./Chip"
import styles from "./linkChips.module.css"
import type { LinkChipsProps } from "./linkChips"
export default function LinkChips({ chips }: LinkChipsProps) {
if (!chips.length) {
return null
}
return (
<ul className={styles.linkChips}>
{chips.map(({ url, title }) => (
<li key={`link-chip-${title}`}>
<LinkChip url={url} title={title} />
</li>
))}
</ul>
)
}

View File

@@ -0,0 +1,8 @@
.linkChips {
list-style: none;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
gap: var(--Spacing-x1);
}

View File

@@ -0,0 +1,5 @@
import type { LinkChipProps } from "./Chip/chip"
export interface LinkChipsProps {
chips: LinkChipProps[]
}

View File

@@ -0,0 +1,4 @@
.container {
display: flex;
justify-content: center;
}

View File

@@ -0,0 +1,32 @@
"use client"
import { useIntl } from "react-intl"
import { ChevronDownIcon } from "@/components/Icons"
import Button from "@/components/TempDesignSystem/Button"
import styles from "./button.module.css"
import type { ShowMoreButtonParams } from "@/types/components/myPages/stays/button"
export default function ShowMoreButton({
disabled,
loadMoreData,
}: ShowMoreButtonParams) {
const { formatMessage } = useIntl()
return (
<div className={styles.container}>
<Button
disabled={disabled}
onClick={loadMoreData}
variant="icon"
type="button"
theme="base"
intent="text"
>
<ChevronDownIcon />
{formatMessage({ id: "Show more" })}
</Button>
</div>
)
}

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