Merge branch 'develop' of bitbucket.org:scandic-swap/web into feature/tracking
This commit is contained in:
@@ -1,14 +0,0 @@
|
|||||||
"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 />
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { serverClient } from "@/lib/trpc/server"
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
import { getHotelDataSchema } from "@/server/routers/hotels/output"
|
||||||
import tempHotelData from "@/server/routers/hotels/tempHotelData.json"
|
import tempHotelData from "@/server/routers/hotels/tempHotelData.json"
|
||||||
|
|
||||||
|
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
|
||||||
import BedSelection from "@/components/HotelReservation/SelectRate/BedSelection"
|
import BedSelection from "@/components/HotelReservation/SelectRate/BedSelection"
|
||||||
import BreakfastSelection from "@/components/HotelReservation/SelectRate/BreakfastSelection"
|
import BreakfastSelection from "@/components/HotelReservation/SelectRate/BreakfastSelection"
|
||||||
import Details from "@/components/HotelReservation/SelectRate/Details"
|
import Details from "@/components/HotelReservation/SelectRate/Details"
|
||||||
@@ -79,7 +81,7 @@ export default async function SectionsPage({
|
|||||||
setLang(params.lang)
|
setLang(params.lang)
|
||||||
|
|
||||||
// TODO: Use real endpoint.
|
// TODO: Use real endpoint.
|
||||||
const hotel = tempHotelData.data.attributes
|
const hotel = getHotelDataSchema.parse(tempHotelData)
|
||||||
|
|
||||||
const rooms = await serverClient().hotel.rates.get({
|
const rooms = await serverClient().hotel.rates.get({
|
||||||
// TODO: pass the correct hotel ID and all other parameters that should be included in the search
|
// TODO: pass the correct hotel ID and all other parameters that should be included in the search
|
||||||
@@ -107,8 +109,7 @@ export default async function SectionsPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/* TODO: Add Hotel Listing Card */}
|
<HotelSelectionHeader hotel={hotel.data.attributes} />
|
||||||
<div>Hotel Listing Card TBI</div>
|
|
||||||
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.main}>
|
<div className={styles.main}>
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
.main {
|
||||||
|
display: grid;
|
||||||
|
background-color: var(--Scandic-Brand-Warm-White);
|
||||||
|
min-height: 100dvh;
|
||||||
|
grid-template-columns: 420px 1fr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
|
import {
|
||||||
|
fetchAvailableHotels,
|
||||||
|
getFiltersFromHotels,
|
||||||
|
} from "@/app/[lang]/(live)/(public)/hotelreservation/select-hotel/utils"
|
||||||
|
import SelectHotelMap from "@/components/HotelReservation/SelectHotel/SelectHotelMap"
|
||||||
|
import { setLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
import styles from "./page.module.css"
|
||||||
|
|
||||||
|
import {
|
||||||
|
PointOfInterest,
|
||||||
|
PointOfInterestCategoryNameEnum,
|
||||||
|
PointOfInterestGroupEnum,
|
||||||
|
} from "@/types/hotel"
|
||||||
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function SelectHotelMapPage({
|
||||||
|
params,
|
||||||
|
}: PageArgs<LangParams, {}>) {
|
||||||
|
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
|
||||||
|
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
|
||||||
|
setLang(params.lang)
|
||||||
|
|
||||||
|
const hotels = await fetchAvailableHotels({
|
||||||
|
cityId: "8ec4bba3-1c38-4606-82d1-bbe3f6738e54",
|
||||||
|
roomStayStartDate: "2024-11-02",
|
||||||
|
roomStayEndDate: "2024-11-03",
|
||||||
|
adults: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
const filters = getFiltersFromHotels(hotels)
|
||||||
|
|
||||||
|
// TODO: this is just a quick transformation to get something there. May need rework
|
||||||
|
const pointOfInterests: PointOfInterest[] = hotels.map((hotel) => ({
|
||||||
|
coordinates: {
|
||||||
|
lat: hotel.hotelData.location.latitude,
|
||||||
|
lng: hotel.hotelData.location.longitude,
|
||||||
|
},
|
||||||
|
name: hotel.hotelData.name,
|
||||||
|
distance: hotel.hotelData.location.distanceToCentre,
|
||||||
|
categoryName: PointOfInterestCategoryNameEnum.HOTEL,
|
||||||
|
group: PointOfInterestGroupEnum.LOCATION,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className={styles.main}>
|
||||||
|
<SelectHotelMap
|
||||||
|
apiKey={googleMapsApiKey}
|
||||||
|
// TODO: use correct coordinates. The city center?
|
||||||
|
coordinates={{ lat: 59.32, lng: 18.01 }}
|
||||||
|
pointsOfInterest={pointOfInterests}
|
||||||
|
mapId={googleMapId}
|
||||||
|
/>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,45 +1,21 @@
|
|||||||
import { serverClient } from "@/lib/trpc/server"
|
import { selectHotelMap } from "@/constants/routes/hotelReservation"
|
||||||
|
|
||||||
|
import {
|
||||||
|
fetchAvailableHotels,
|
||||||
|
getFiltersFromHotels,
|
||||||
|
} from "@/app/[lang]/(live)/(public)/hotelreservation/select-hotel/utils"
|
||||||
import HotelCardListing from "@/components/HotelReservation/HotelCardListing"
|
import HotelCardListing from "@/components/HotelReservation/HotelCardListing"
|
||||||
import HotelFilter from "@/components/HotelReservation/SelectHotel/HotelFilter"
|
import HotelFilter from "@/components/HotelReservation/SelectHotel/HotelFilter"
|
||||||
import { ChevronRightIcon } from "@/components/Icons"
|
import { ChevronRightIcon } from "@/components/Icons"
|
||||||
import StaticMap from "@/components/Maps/StaticMap"
|
import StaticMap from "@/components/Maps/StaticMap"
|
||||||
import Link from "@/components/TempDesignSystem/Link"
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
import { getLang, setLang } from "@/i18n/serverContext"
|
import { setLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
import styles from "./page.module.css"
|
import styles from "./page.module.css"
|
||||||
|
|
||||||
import { AvailabilityInput } from "@/types/components/hotelReservation/selectHotel/availabilityInput"
|
|
||||||
import { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
|
||||||
import { LangParams, PageArgs } from "@/types/params"
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
async function getAvailableHotels(
|
|
||||||
input: AvailabilityInput
|
|
||||||
): Promise<HotelData[]> {
|
|
||||||
const getAvailableHotels = await serverClient().hotel.availability.get(input)
|
|
||||||
|
|
||||||
if (!getAvailableHotels) throw new Error()
|
|
||||||
|
|
||||||
const { availability } = getAvailableHotels
|
|
||||||
|
|
||||||
const hotels = availability.map(async (hotel) => {
|
|
||||||
const hotelData = await serverClient().hotel.hotelData.get({
|
|
||||||
hotelId: hotel.hotelId.toString(),
|
|
||||||
language: getLang(),
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!hotelData) throw new Error()
|
|
||||||
|
|
||||||
return {
|
|
||||||
hotelData: hotelData.data.attributes,
|
|
||||||
price: hotel.bestPricePerNight,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return await Promise.all(hotels)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function SelectHotelPage({
|
export default async function SelectHotelPage({
|
||||||
params,
|
params,
|
||||||
}: PageArgs<LangParams>) {
|
}: PageArgs<LangParams>) {
|
||||||
@@ -48,54 +24,34 @@ export default async function SelectHotelPage({
|
|||||||
const tempSearchTerm = "Stockholm"
|
const tempSearchTerm = "Stockholm"
|
||||||
const intl = await getIntl()
|
const intl = await getIntl()
|
||||||
|
|
||||||
const hotels = await getAvailableHotels({
|
const hotels = await fetchAvailableHotels({
|
||||||
cityId: "8ec4bba3-1c38-4606-82d1-bbe3f6738e54",
|
cityId: "8ec4bba3-1c38-4606-82d1-bbe3f6738e54",
|
||||||
roomStayStartDate: "2024-11-02",
|
roomStayStartDate: "2024-11-02",
|
||||||
roomStayEndDate: "2024-11-03",
|
roomStayEndDate: "2024-11-03",
|
||||||
adults: 1,
|
adults: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
const filters = hotels.flatMap((data) => data.hotelData.detailedFacilities)
|
const filterList = getFiltersFromHotels(hotels)
|
||||||
|
|
||||||
const filterIds = [...new Set(filters.map((data) => data.id))]
|
|
||||||
const filterList: {
|
|
||||||
name: string
|
|
||||||
id: number
|
|
||||||
applyToAllHotels: boolean
|
|
||||||
public: boolean
|
|
||||||
icon: string
|
|
||||||
sortOrder: number
|
|
||||||
code?: string
|
|
||||||
iconName?: string
|
|
||||||
}[] = filterIds
|
|
||||||
.map((id) => filters.find((find) => find.id === id))
|
|
||||||
.filter(
|
|
||||||
(
|
|
||||||
filter
|
|
||||||
): filter is {
|
|
||||||
name: string
|
|
||||||
id: number
|
|
||||||
applyToAllHotels: boolean
|
|
||||||
public: boolean
|
|
||||||
icon: string
|
|
||||||
sortOrder: number
|
|
||||||
code?: string
|
|
||||||
iconName?: string
|
|
||||||
} => filter !== undefined
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className={styles.main}>
|
<main className={styles.main}>
|
||||||
<section className={styles.section}>
|
<section className={styles.section}>
|
||||||
<StaticMap
|
<Link href={selectHotelMap[params.lang]} keepSearchParams>
|
||||||
city={tempSearchTerm}
|
<StaticMap
|
||||||
width={340}
|
city={tempSearchTerm}
|
||||||
height={180}
|
width={340}
|
||||||
zoomLevel={11}
|
height={180}
|
||||||
mapType="roadmap"
|
zoomLevel={11}
|
||||||
altText={`Map of ${tempSearchTerm} city center`}
|
mapType="roadmap"
|
||||||
/>
|
altText={`Map of ${tempSearchTerm} city center`}
|
||||||
<Link className={styles.link} color="burgundy" href="#">
|
/>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
className={styles.link}
|
||||||
|
color="burgundy"
|
||||||
|
href={selectHotelMap[params.lang]}
|
||||||
|
keepSearchParams
|
||||||
|
>
|
||||||
{intl.formatMessage({ id: "Show map" })}
|
{intl.formatMessage({ id: "Show map" })}
|
||||||
<ChevronRightIcon color="burgundy" />
|
<ChevronRightIcon color="burgundy" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
import { AvailabilityInput } from "@/types/components/hotelReservation/selectHotel/availabilityInput"
|
||||||
|
import { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||||
|
import { Filter } from "@/types/components/hotelReservation/selectHotel/hotelFilters"
|
||||||
|
|
||||||
|
export async function fetchAvailableHotels(
|
||||||
|
input: AvailabilityInput
|
||||||
|
): Promise<HotelData[]> {
|
||||||
|
const availableHotels = await serverClient().hotel.availability.get(input)
|
||||||
|
|
||||||
|
if (!availableHotels) throw new Error()
|
||||||
|
|
||||||
|
const language = getLang()
|
||||||
|
const hotels = availableHotels.availability.map(async (hotel) => {
|
||||||
|
const hotelData = await serverClient().hotel.hotelData.get({
|
||||||
|
hotelId: hotel.hotelId.toString(),
|
||||||
|
language,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!hotelData) throw new Error()
|
||||||
|
|
||||||
|
return {
|
||||||
|
hotelData: hotelData.data.attributes,
|
||||||
|
price: hotel.bestPricePerNight,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return await Promise.all(hotels)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFiltersFromHotels(hotels: HotelData[]) {
|
||||||
|
const filters = hotels.flatMap((hotel) => hotel.hotelData.detailedFacilities)
|
||||||
|
|
||||||
|
const uniqueFilterIds = [...new Set(filters.map((filter) => filter.id))]
|
||||||
|
const filterList: Filter[] = uniqueFilterIds
|
||||||
|
.map((filterId) => filters.find((filter) => filter.id === filterId))
|
||||||
|
.filter((filter): filter is Filter => filter !== undefined)
|
||||||
|
|
||||||
|
return filterList
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import { generateLoyaltyConfigTag } from "@/utils/generateTag"
|
|||||||
|
|
||||||
enum LoyaltyConfigContentTypes {
|
enum LoyaltyConfigContentTypes {
|
||||||
loyalty_level = "loyalty_level",
|
loyalty_level = "loyalty_level",
|
||||||
rewards = "rewards",
|
reward = "reward",
|
||||||
}
|
}
|
||||||
|
|
||||||
const validateJsonBody = z.object({
|
const validateJsonBody = z.object({
|
||||||
@@ -65,7 +65,7 @@ export async function POST(request: NextRequest) {
|
|||||||
entry.level_id
|
entry.level_id
|
||||||
)
|
)
|
||||||
} else if (
|
} else if (
|
||||||
content_type.uid === LoyaltyConfigContentTypes.rewards &&
|
content_type.uid === LoyaltyConfigContentTypes.reward &&
|
||||||
entry.reward_id
|
entry.reward_id
|
||||||
) {
|
) {
|
||||||
tag = generateLoyaltyConfigTag(
|
tag = generateLoyaltyConfigTag(
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ async function PointsColumn({
|
|||||||
</Title>
|
</Title>
|
||||||
{subtitle ? (
|
{subtitle ? (
|
||||||
<Body color="white" textAlign="center">
|
<Body color="white" textAlign="center">
|
||||||
{subtitle}
|
{formatMessage({ id: subtitle })}
|
||||||
</Body>
|
</Body>
|
||||||
) : null}
|
) : null}
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function Stats({ user }: UserProps) {
|
|||||||
return (
|
return (
|
||||||
<section className={styles.stats}>
|
<section className={styles.stats}>
|
||||||
<Points user={user} />
|
<Points user={user} />
|
||||||
<Divider variant="default" color="pale" />
|
<Divider color="pale" />
|
||||||
<ExpiringPoints user={user} />
|
<ExpiringPoints user={user} />
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
107
components/BookingWidget/Client.tsx
Normal file
107
components/BookingWidget/Client.tsx
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
"use client"
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
import { FormProvider, useForm } from "react-hook-form"
|
||||||
|
|
||||||
|
import { dt } from "@/lib/dt"
|
||||||
|
|
||||||
|
import Form from "@/components/Forms/BookingWidget"
|
||||||
|
import { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema"
|
||||||
|
import { CloseLarge } from "@/components/Icons"
|
||||||
|
import { debounce } from "@/utils/debounce"
|
||||||
|
|
||||||
|
import MobileToggleButton from "./MobileToggleButton"
|
||||||
|
|
||||||
|
import styles from "./bookingWidget.module.css"
|
||||||
|
|
||||||
|
import type {
|
||||||
|
BookingWidgetClientProps,
|
||||||
|
BookingWidgetSchema,
|
||||||
|
} from "@/types/components/bookingWidget"
|
||||||
|
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
||||||
|
|
||||||
|
export default function BookingWidgetClient({
|
||||||
|
locations,
|
||||||
|
}: BookingWidgetClientProps) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
|
||||||
|
const sessionStorageSearchData =
|
||||||
|
typeof window !== "undefined"
|
||||||
|
? sessionStorage.getItem("searchData")
|
||||||
|
: undefined
|
||||||
|
const initialSelectedLocation: Location | undefined = sessionStorageSearchData
|
||||||
|
? JSON.parse(sessionStorageSearchData)
|
||||||
|
: undefined
|
||||||
|
const methods = useForm<BookingWidgetSchema>({
|
||||||
|
defaultValues: {
|
||||||
|
search: initialSelectedLocation?.name ?? "",
|
||||||
|
location: sessionStorageSearchData
|
||||||
|
? encodeURIComponent(sessionStorageSearchData)
|
||||||
|
: undefined,
|
||||||
|
date: {
|
||||||
|
// UTC is required to handle requests from far away timezones https://scandichotels.atlassian.net/browse/SWAP-6375 & PET-507
|
||||||
|
// This is specifically to handle timezones falling in different dates.
|
||||||
|
from: dt().utc().format("YYYY-MM-DD"),
|
||||||
|
to: dt().utc().add(1, "day").format("YYYY-MM-DD"),
|
||||||
|
},
|
||||||
|
bookingCode: "",
|
||||||
|
redemption: false,
|
||||||
|
voucher: false,
|
||||||
|
rooms: [
|
||||||
|
{
|
||||||
|
adults: 1,
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
shouldFocusError: false,
|
||||||
|
mode: "all",
|
||||||
|
resolver: zodResolver(bookingWidgetSchema),
|
||||||
|
reValidateMode: "onChange",
|
||||||
|
})
|
||||||
|
|
||||||
|
function closeMobileSearch() {
|
||||||
|
setIsOpen(false)
|
||||||
|
document.body.style.overflowY = "visible"
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMobileSearch() {
|
||||||
|
setIsOpen(true)
|
||||||
|
document.body.style.overflowY = "hidden"
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const debouncedResizeHandler = debounce(function ([
|
||||||
|
entry,
|
||||||
|
]: ResizeObserverEntry[]) {
|
||||||
|
if (entry.contentRect.width > 1366) {
|
||||||
|
closeMobileSearch()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const observer = new ResizeObserver(debouncedResizeHandler)
|
||||||
|
|
||||||
|
observer.observe(document.body)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (observer) {
|
||||||
|
observer.unobserve(document.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormProvider {...methods}>
|
||||||
|
<section className={styles.container} data-open={isOpen}>
|
||||||
|
<button
|
||||||
|
className={styles.close}
|
||||||
|
onClick={closeMobileSearch}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<CloseLarge />
|
||||||
|
</button>
|
||||||
|
<Form locations={locations} />
|
||||||
|
</section>
|
||||||
|
<MobileToggleButton openMobileSearch={openMobileSearch} />
|
||||||
|
</FormProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
.complete,
|
||||||
|
.partial {
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0px 8px 24px 0px rgba(0, 0, 0, 0.16);
|
||||||
|
cursor: pointer;
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x-one-and-half);
|
||||||
|
padding: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.complete {
|
||||||
|
grid-template-columns: 1fr 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partial {
|
||||||
|
grid-template-columns: min(1fr, 150px) min-content min(1fr, 150px) 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--Base-Button-Primary-Fill-Normal);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
height: 36px;
|
||||||
|
justify-content: center;
|
||||||
|
justify-self: flex-end;
|
||||||
|
width: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
.complete,
|
||||||
|
.partial {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
102
components/BookingWidget/MobileToggleButton/index.tsx
Normal file
102
components/BookingWidget/MobileToggleButton/index.tsx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
"use client"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
import { useWatch } from "react-hook-form"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { dt } from "@/lib/dt"
|
||||||
|
|
||||||
|
import { EditIcon, SearchIcon } from "@/components/Icons"
|
||||||
|
import Divider from "@/components/TempDesignSystem/Divider"
|
||||||
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
|
import styles from "./button.module.css"
|
||||||
|
|
||||||
|
import type {
|
||||||
|
BookingWidgetSchema,
|
||||||
|
BookingWidgetToggleButtonProps,
|
||||||
|
} from "@/types/components/bookingWidget"
|
||||||
|
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
||||||
|
|
||||||
|
export default function MobileToggleButton({
|
||||||
|
openMobileSearch,
|
||||||
|
}: BookingWidgetToggleButtonProps) {
|
||||||
|
const [hasMounted, setHasMounted] = useState(false)
|
||||||
|
const intl = useIntl()
|
||||||
|
const lang = useLang()
|
||||||
|
const d = useWatch({ name: "date" })
|
||||||
|
const location = useWatch({ name: "location" })
|
||||||
|
const rooms: BookingWidgetSchema["rooms"] = useWatch({ name: "rooms" })
|
||||||
|
|
||||||
|
const parsedLocation: Location | null = location
|
||||||
|
? JSON.parse(decodeURIComponent(location))
|
||||||
|
: null
|
||||||
|
|
||||||
|
const nights = dt(d.to).diff(dt(d.from), "days")
|
||||||
|
|
||||||
|
const selectedFromDate = dt(d.from).locale(lang).format("D MMM")
|
||||||
|
const selectedToDate = dt(d.to).locale(lang).format("D MMM")
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setHasMounted(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!hasMounted) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedLocation && d) {
|
||||||
|
const totalRooms = rooms.length
|
||||||
|
const totalAdults = rooms.reduce((acc, room) => {
|
||||||
|
if (room.adults) {
|
||||||
|
acc = acc + room.adults
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, 0)
|
||||||
|
return (
|
||||||
|
<div className={styles.complete} onClick={openMobileSearch} role="button">
|
||||||
|
<div>
|
||||||
|
<Caption color="red">{parsedLocation.name}</Caption>
|
||||||
|
<Caption>
|
||||||
|
{`${selectedFromDate} - ${selectedToDate} (${intl.formatMessage(
|
||||||
|
{ id: "booking.nights" },
|
||||||
|
{ totalNights: nights }
|
||||||
|
)}) ${intl.formatMessage({ id: "booking.adults" }, { totalAdults })}, ${intl.formatMessage({ id: "booking.rooms" }, { totalRooms })}`}
|
||||||
|
</Caption>
|
||||||
|
</div>
|
||||||
|
<div className={styles.icon}>
|
||||||
|
<EditIcon color="white" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.partial} onClick={openMobileSearch} role="button">
|
||||||
|
<div>
|
||||||
|
<Caption color="red">{intl.formatMessage({ id: "Where to" })}</Caption>
|
||||||
|
<Body color="uiTextPlaceholder">
|
||||||
|
{parsedLocation
|
||||||
|
? parsedLocation.name
|
||||||
|
: intl.formatMessage({ id: "Destination" })}
|
||||||
|
</Body>
|
||||||
|
</div>
|
||||||
|
<Divider color="baseSurfaceSubtleNormal" variant="vertical" />
|
||||||
|
<div>
|
||||||
|
<Caption color="red">
|
||||||
|
{intl.formatMessage(
|
||||||
|
{ id: "booking.nights" },
|
||||||
|
{ totalNights: nights }
|
||||||
|
)}
|
||||||
|
</Caption>
|
||||||
|
<Body>
|
||||||
|
{selectedFromDate} - {selectedToDate}
|
||||||
|
</Body>
|
||||||
|
</div>
|
||||||
|
<div className={styles.icon}>
|
||||||
|
<SearchIcon color="white" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,12 +1,38 @@
|
|||||||
.container {
|
@media screen and (max-width: 1366px) {
|
||||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
.container {
|
||||||
border-top: 1px solid var(--Base-Border-Subtle);
|
background-color: var(--UI-Input-Controls-Surface-Normal);
|
||||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
bottom: -100%;
|
||||||
padding: var(--Spacing-x2) var(--Spacing-x5);
|
display: grid;
|
||||||
|
gap: var(--Spacing-x3);
|
||||||
|
grid-template-rows: 36px 1fr;
|
||||||
|
height: 100dvh;
|
||||||
|
padding: var(--Spacing-x3) var(--Spacing-x2) var(--Spacing-x7);
|
||||||
|
position: fixed;
|
||||||
|
transition: bottom 300ms ease;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container[data-open="true"] {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
justify-self: flex-end;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 1367px) {
|
@media screen and (min-width: 1367px) {
|
||||||
.container {
|
.container {
|
||||||
|
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||||
|
border-top: 1px solid var(--Base-Border-Subtle);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { getLocations } from "@/lib/trpc/memoizedRequests"
|
import { getLocations } from "@/lib/trpc/memoizedRequests"
|
||||||
|
|
||||||
import Form from "@/components/Forms/BookingWidget"
|
import BookingWidgetClient from "./Client"
|
||||||
|
|
||||||
import styles from "./bookingWidget.module.css"
|
|
||||||
|
|
||||||
export function preload() {
|
export function preload() {
|
||||||
void getLocations()
|
void getLocations()
|
||||||
@@ -15,9 +13,5 @@ export default async function BookingWidget() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <BookingWidgetClient locations={locations.data} />
|
||||||
<section className={styles.container}>
|
|
||||||
<Form locations={locations.data} />
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
/*nav:has(+ .contentPage) {*/
|
|
||||||
/* background-color: var(--Base-Surface-Subtle-Normal);*/
|
|
||||||
/*}*/
|
|
||||||
|
|
||||||
.contentPage {
|
.contentPage {
|
||||||
padding-bottom: var(--Spacing-x9);
|
padding-bottom: var(--Spacing-x9);
|
||||||
|
container-name: content-page;
|
||||||
|
container-type: inline-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@@ -17,21 +15,32 @@
|
|||||||
max-width: var(--max-width-content);
|
max-width: var(--max-width-content);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headerIntro {
|
.headerIntro {
|
||||||
display: grid;
|
display: grid;
|
||||||
max-width: var(--max-width-text-block);
|
max-width: var(--max-width-text-block);
|
||||||
gap: var(--Spacing-x3);
|
gap: var(--Spacing-x3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.heroContainer {
|
||||||
|
width: 100%;
|
||||||
padding: var(--Spacing-x4) var(--Spacing-x2);
|
padding: var(--Spacing-x4) var(--Spacing-x2);
|
||||||
display: grid;
|
|
||||||
justify-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.innerContent {
|
.heroContainer img {
|
||||||
width: 100%;
|
|
||||||
max-width: var(--max-width-content);
|
max-width: var(--max-width-content);
|
||||||
|
margin: 0 auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentContainer {
|
||||||
|
display: grid;
|
||||||
|
padding: var(--Spacing-x4) var(--Spacing-x2) 0;
|
||||||
|
gap: var(--Spacing-x4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainContent {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
@@ -39,3 +48,16 @@
|
|||||||
gap: var(--Spacing-x3);
|
gap: var(--Spacing-x3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1367px) {
|
||||||
|
.heroContainer {
|
||||||
|
padding: var(--Spacing-x4) 0;
|
||||||
|
}
|
||||||
|
.contentContainer {
|
||||||
|
grid-template-columns: var(--max-width-text-block) 1fr;
|
||||||
|
gap: var(--Spacing-x9);
|
||||||
|
max-width: var(--max-width-content);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: var(--Spacing-x4) 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,31 +23,38 @@ export default async function ContentPage() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className={styles.contentPage}>
|
<section className={styles.contentPage}>
|
||||||
{sidebar?.length ? <Sidebar blocks={sidebar} /> : null}
|
|
||||||
|
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
<div className={styles.headerContent}>
|
<div className={styles.headerContent}>
|
||||||
<div className={styles.headerIntro}>
|
{header ? (
|
||||||
<Title as="h2">{header.heading}</Title>
|
<>
|
||||||
<Preamble>{header.preamble}</Preamble>
|
<div className={styles.headerIntro}>
|
||||||
</div>
|
<Title as="h2">{header.heading}</Title>
|
||||||
{header.navigation_links ? (
|
<Preamble>{header.preamble}</Preamble>
|
||||||
<LinkChips chips={header.navigation_links} />
|
</div>
|
||||||
|
{header.navigation_links ? (
|
||||||
|
<LinkChips chips={header.navigation_links} />
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className={styles.content}>
|
{hero_image ? (
|
||||||
<div className={styles.innerContent}>
|
<div className={styles.heroContainer}>
|
||||||
{hero_image ? (
|
<Hero
|
||||||
<Hero
|
alt={hero_image.meta.alt || hero_image.meta.caption || ""}
|
||||||
alt={hero_image.meta.alt || hero_image.meta.caption || ""}
|
src={hero_image.url}
|
||||||
src={hero_image.url}
|
/>
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{blocks ? <Blocks blocks={blocks} /> : null}
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
) : null}
|
||||||
|
|
||||||
|
<div className={styles.contentContainer}>
|
||||||
|
<main className={styles.mainContent}>
|
||||||
|
{blocks ? <Blocks blocks={blocks} /> : null}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{sidebar?.length ? <Sidebar blocks={sidebar} /> : null}
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<TrackingSDK pageData={tracking} />
|
<TrackingSDK pageData={tracking} />
|
||||||
|
|||||||
@@ -14,3 +14,9 @@
|
|||||||
top: var(--main-menu-desktop-height);
|
top: var(--main-menu-desktop-height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
pointer-events: initial;
|
||||||
|
box-shadow: var(--button-box-shadow);
|
||||||
|
gap: var(--Spacing-x-half);
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import { useIntl } from "react-intl"
|
|||||||
|
|
||||||
import useHotelPageStore from "@/stores/hotel-page"
|
import useHotelPageStore from "@/stores/hotel-page"
|
||||||
|
|
||||||
|
import CloseLargeIcon from "@/components/Icons/CloseLarge"
|
||||||
|
import InteractiveMap from "@/components/Maps/InteractiveMap"
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
|
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
|
||||||
|
|
||||||
import MapContent from "./Map"
|
|
||||||
import Sidebar from "./Sidebar"
|
import Sidebar from "./Sidebar"
|
||||||
|
|
||||||
import styles from "./dynamicMap.module.css"
|
import styles from "./dynamicMap.module.css"
|
||||||
@@ -52,6 +54,20 @@ export default function DynamicMap({
|
|||||||
}
|
}
|
||||||
}, [isDynamicMapOpen, scrollHeightWhenOpened])
|
}, [isDynamicMapOpen, scrollHeightWhenOpened])
|
||||||
|
|
||||||
|
const closeButton = (
|
||||||
|
<Button
|
||||||
|
theme="base"
|
||||||
|
intent="inverted"
|
||||||
|
variant="icon"
|
||||||
|
size="small"
|
||||||
|
className={styles.closeButton}
|
||||||
|
onClick={closeDynamicMap}
|
||||||
|
>
|
||||||
|
<CloseLargeIcon color="burgundy" />
|
||||||
|
<span>{intl.formatMessage({ id: "Close the map" })}</span>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<APIProvider apiKey={apiKey}>
|
<APIProvider apiKey={apiKey}>
|
||||||
<Modal isOpen={isDynamicMapOpen}>
|
<Modal isOpen={isDynamicMapOpen}>
|
||||||
@@ -68,7 +84,8 @@ export default function DynamicMap({
|
|||||||
pointsOfInterest={pointsOfInterest}
|
pointsOfInterest={pointsOfInterest}
|
||||||
onActivePoiChange={setActivePoi}
|
onActivePoiChange={setActivePoi}
|
||||||
/>
|
/>
|
||||||
<MapContent
|
<InteractiveMap
|
||||||
|
closeButton={closeButton}
|
||||||
coordinates={coordinates}
|
coordinates={coordinates}
|
||||||
pointsOfInterest={pointsOfInterest}
|
pointsOfInterest={pointsOfInterest}
|
||||||
activePoi={activePoi}
|
activePoi={activePoi}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export default function MapCard({ hotelName, pois }: MapCardProps) {
|
|||||||
theme="base"
|
theme="base"
|
||||||
intent="secondary"
|
intent="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
|
fullWidth
|
||||||
className={styles.ctaButton}
|
className={styles.ctaButton}
|
||||||
onClick={openDynamicMap}
|
onClick={openDynamicMap}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
container-name: loyalty-page;
|
||||||
|
container-type: inline-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blocks {
|
.blocks {
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
"use client"
|
|
||||||
import { da, de, fi, nb, sv } from "date-fns/locale"
|
|
||||||
import { useState } from "react"
|
|
||||||
import { type DateRange, DayPicker } from "react-day-picker"
|
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
|
||||||
import { dt } from "@/lib/dt"
|
|
||||||
|
|
||||||
import useLang from "@/hooks/useLang"
|
|
||||||
|
|
||||||
import classNames from "react-day-picker/style.module.css"
|
|
||||||
|
|
||||||
const locales = {
|
|
||||||
[Lang.da]: da,
|
|
||||||
[Lang.de]: de,
|
|
||||||
[Lang.fi]: fi,
|
|
||||||
[Lang.no]: nb,
|
|
||||||
[Lang.sv]: sv,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DatePickerProps {
|
|
||||||
handleOnSelect: (selected: DateRange) => void
|
|
||||||
initialSelected?: DateRange
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DatePicker({
|
|
||||||
handleOnSelect,
|
|
||||||
initialSelected = {
|
|
||||||
from: undefined,
|
|
||||||
to: undefined,
|
|
||||||
},
|
|
||||||
}: DatePickerProps) {
|
|
||||||
const lang = useLang()
|
|
||||||
const [selectedDate, setSelectedDate] = useState<DateRange>(initialSelected)
|
|
||||||
|
|
||||||
function handleSelectDate(selected: DateRange) {
|
|
||||||
handleOnSelect(selected)
|
|
||||||
setSelectedDate(selected)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** English is default language and doesn't need to be imported */
|
|
||||||
const locale = lang === Lang.en ? undefined : locales[lang]
|
|
||||||
|
|
||||||
const currentDate = dt().toDate()
|
|
||||||
const startOfMonth = dt(currentDate).set("date", 1).toDate()
|
|
||||||
const yesterday = dt(currentDate).subtract(1, "day").toDate()
|
|
||||||
return (
|
|
||||||
<DayPicker
|
|
||||||
classNames={classNames}
|
|
||||||
disabled={{ from: startOfMonth, to: yesterday }}
|
|
||||||
excludeDisabled
|
|
||||||
locale={locale}
|
|
||||||
mode="range"
|
|
||||||
onSelect={handleSelectDate}
|
|
||||||
pagedNavigation
|
|
||||||
required
|
|
||||||
selected={selectedDate}
|
|
||||||
showWeekNumber
|
|
||||||
startMonth={currentDate}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
127
components/DatePicker/Screen/Desktop.tsx
Normal file
127
components/DatePicker/Screen/Desktop.tsx
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
"use client"
|
||||||
|
import { DayPicker } from "react-day-picker"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { dt } from "@/lib/dt"
|
||||||
|
|
||||||
|
import { ChevronLeftIcon } from "@/components/Icons"
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
import Divider from "@/components/TempDesignSystem/Divider"
|
||||||
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
|
import styles from "./desktop.module.css"
|
||||||
|
import classNames from "react-day-picker/style.module.css"
|
||||||
|
|
||||||
|
import type { DatePickerProps } from "@/types/components/datepicker"
|
||||||
|
|
||||||
|
export default function DatePickerDesktop({
|
||||||
|
close,
|
||||||
|
handleOnSelect,
|
||||||
|
locales,
|
||||||
|
selectedDate,
|
||||||
|
}: DatePickerProps) {
|
||||||
|
const lang = useLang()
|
||||||
|
const intl = useIntl()
|
||||||
|
|
||||||
|
/** English is default language and doesn't need to be imported */
|
||||||
|
const locale = lang === Lang.en ? undefined : locales[lang]
|
||||||
|
const currentDate = dt().toDate()
|
||||||
|
const startOfMonth = dt(currentDate).set("date", 1).toDate()
|
||||||
|
const yesterday = dt(currentDate).subtract(1, "day").toDate()
|
||||||
|
return (
|
||||||
|
<DayPicker
|
||||||
|
classNames={{
|
||||||
|
...classNames,
|
||||||
|
caption_label: `${classNames.caption_label} ${styles.captionLabel}`,
|
||||||
|
day: `${classNames.day} ${styles.day}`,
|
||||||
|
day_button: `${classNames.day_button} ${styles.dayButton}`,
|
||||||
|
footer: styles.footer,
|
||||||
|
month_caption: `${classNames.month_caption} ${styles.monthCaption}`,
|
||||||
|
months: `${classNames.months} ${styles.months}`,
|
||||||
|
range_end: styles.rangeEnd,
|
||||||
|
range_middle: styles.rangeMiddle,
|
||||||
|
range_start: styles.rangeStart,
|
||||||
|
root: `${classNames.root} ${styles.container}`,
|
||||||
|
week: styles.week,
|
||||||
|
weekday: `${classNames.weekday} ${styles.weekDay}`,
|
||||||
|
}}
|
||||||
|
disabled={{ from: startOfMonth, to: yesterday }}
|
||||||
|
excludeDisabled
|
||||||
|
footer
|
||||||
|
formatters={{
|
||||||
|
formatWeekdayName(weekday) {
|
||||||
|
return dt(weekday).locale(lang).format("ddd")
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
lang={lang}
|
||||||
|
locale={locale}
|
||||||
|
mode="range"
|
||||||
|
numberOfMonths={2}
|
||||||
|
onSelect={handleOnSelect}
|
||||||
|
pagedNavigation
|
||||||
|
required
|
||||||
|
selected={selectedDate}
|
||||||
|
startMonth={currentDate}
|
||||||
|
weekStartsOn={1}
|
||||||
|
components={{
|
||||||
|
Chevron(props) {
|
||||||
|
return <ChevronLeftIcon {...props} height={20} width={20} />
|
||||||
|
},
|
||||||
|
Footer(props) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Divider className={styles.divider} color="primaryLightSubtle" />
|
||||||
|
<footer className={props.className}>
|
||||||
|
<Button
|
||||||
|
intent="tertiary"
|
||||||
|
onPress={close}
|
||||||
|
size="small"
|
||||||
|
theme="base"
|
||||||
|
>
|
||||||
|
<Caption color="white" textTransform="bold">
|
||||||
|
{intl.formatMessage({ id: "Select dates" })}
|
||||||
|
</Caption>
|
||||||
|
</Button>
|
||||||
|
</footer>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
MonthCaption(props) {
|
||||||
|
return (
|
||||||
|
<div className={props.className}>
|
||||||
|
<Subtitle asChild type="two">
|
||||||
|
{props.children}
|
||||||
|
</Subtitle>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Nav(props) {
|
||||||
|
if (Array.isArray(props.children)) {
|
||||||
|
const prevButton = props.children?.[0]
|
||||||
|
const nextButton = props.children?.[1]
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{prevButton ? (
|
||||||
|
<nav
|
||||||
|
className={`${props.className} ${styles.previousButton}`}
|
||||||
|
>
|
||||||
|
{prevButton}
|
||||||
|
</nav>
|
||||||
|
) : null}
|
||||||
|
{nextButton ? (
|
||||||
|
<nav className={`${props.className} ${styles.nextButton}`}>
|
||||||
|
{nextButton}
|
||||||
|
</nav>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return <></>
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
140
components/DatePicker/Screen/Mobile.tsx
Normal file
140
components/DatePicker/Screen/Mobile.tsx
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
"use client"
|
||||||
|
import { type ChangeEvent, useState } from "react"
|
||||||
|
import { DayPicker } from "react-day-picker"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { dt } from "@/lib/dt"
|
||||||
|
|
||||||
|
import { CloseLarge } from "@/components/Icons"
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
|
import styles from "./mobile.module.css"
|
||||||
|
import classNames from "react-day-picker/style.module.css"
|
||||||
|
|
||||||
|
import type { DatePickerProps } from "@/types/components/datepicker"
|
||||||
|
|
||||||
|
function addOneYear(_: undefined, i: number) {
|
||||||
|
return new Date().getFullYear() + i
|
||||||
|
}
|
||||||
|
|
||||||
|
const fiftyYearsAhead = Array.from({ length: 50 }, addOneYear)
|
||||||
|
|
||||||
|
export default function DatePickerMobile({
|
||||||
|
close,
|
||||||
|
handleOnSelect,
|
||||||
|
locales,
|
||||||
|
selectedDate,
|
||||||
|
}: DatePickerProps) {
|
||||||
|
const [selectedYear, setSelectedYear] = useState(() => dt().year())
|
||||||
|
const lang = useLang()
|
||||||
|
const intl = useIntl()
|
||||||
|
|
||||||
|
function handleSelectYear(evt: ChangeEvent<HTMLSelectElement>) {
|
||||||
|
setSelectedYear(Number(evt.currentTarget.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** English is default language and doesn't need to be imported */
|
||||||
|
const locale = lang === Lang.en ? undefined : locales[lang]
|
||||||
|
const currentDate = dt().toDate()
|
||||||
|
const startOfCurrentMonth = dt(currentDate).set("date", 1).toDate()
|
||||||
|
const yesterday = dt(currentDate).subtract(1, "day").toDate()
|
||||||
|
|
||||||
|
const startMonth = dt().set("year", selectedYear).startOf("year").toDate()
|
||||||
|
const decemberOfYear = dt().set("year", selectedYear).endOf("year").toDate()
|
||||||
|
return (
|
||||||
|
<DayPicker
|
||||||
|
classNames={{
|
||||||
|
...classNames,
|
||||||
|
caption_label: `${classNames.caption_label} ${styles.captionLabel}`,
|
||||||
|
day: `${classNames.day} ${styles.day}`,
|
||||||
|
day_button: `${classNames.day_button} ${styles.dayButton}`,
|
||||||
|
footer: styles.footer,
|
||||||
|
month: styles.month,
|
||||||
|
month_caption: `${classNames.month_caption} ${styles.monthCaption}`,
|
||||||
|
months: styles.months,
|
||||||
|
range_end: styles.rangeEnd,
|
||||||
|
range_middle: styles.rangeMiddle,
|
||||||
|
range_start: styles.rangeStart,
|
||||||
|
root: `${classNames.root} ${styles.container}`,
|
||||||
|
week: styles.week,
|
||||||
|
weekday: `${classNames.weekday} ${styles.weekDay}`,
|
||||||
|
}}
|
||||||
|
disabled={{ from: startOfCurrentMonth, to: yesterday }}
|
||||||
|
endMonth={decemberOfYear}
|
||||||
|
excludeDisabled
|
||||||
|
footer
|
||||||
|
formatters={{
|
||||||
|
formatWeekdayName(weekday) {
|
||||||
|
return dt(weekday).locale(lang).format("ddd")
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
hideNavigation
|
||||||
|
lang={lang}
|
||||||
|
locale={locale}
|
||||||
|
mode="range"
|
||||||
|
/** Showing full year or what's left of it */
|
||||||
|
numberOfMonths={12}
|
||||||
|
onSelect={handleOnSelect}
|
||||||
|
required
|
||||||
|
selected={selectedDate}
|
||||||
|
startMonth={startMonth}
|
||||||
|
weekStartsOn={1}
|
||||||
|
components={{
|
||||||
|
Footer(props) {
|
||||||
|
return (
|
||||||
|
<footer className={props.className}>
|
||||||
|
<Button
|
||||||
|
className={styles.button}
|
||||||
|
intent="tertiary"
|
||||||
|
onPress={close}
|
||||||
|
size="large"
|
||||||
|
theme="base"
|
||||||
|
>
|
||||||
|
<Body color="white" textTransform="bold">
|
||||||
|
{intl.formatMessage({ id: "Select dates" })}
|
||||||
|
</Body>
|
||||||
|
</Button>
|
||||||
|
<div className={styles.backdrop} />
|
||||||
|
</footer>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
MonthCaption(props) {
|
||||||
|
return (
|
||||||
|
<div className={props.className}>
|
||||||
|
<Subtitle asChild type="two">
|
||||||
|
{props.children}
|
||||||
|
</Subtitle>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Root({ children, ...props }) {
|
||||||
|
return (
|
||||||
|
<div {...props}>
|
||||||
|
<header className={styles.header}>
|
||||||
|
<select
|
||||||
|
className={styles.select}
|
||||||
|
defaultValue={selectedYear}
|
||||||
|
onChange={handleSelectYear}
|
||||||
|
>
|
||||||
|
{fiftyYearsAhead.map((year) => (
|
||||||
|
<option key={year} value={year}>
|
||||||
|
{year}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<button className={styles.close} onClick={close} type="button">
|
||||||
|
<CloseLarge />
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
120
components/DatePicker/Screen/desktop.module.css
Normal file
120
components/DatePicker/Screen/desktop.module.css
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
@media screen and (max-width: 1366px) {
|
||||||
|
.container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.months {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monthCaption {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captionLabel {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day,
|
||||||
|
td.rangeEnd,
|
||||||
|
td.rangeStart {
|
||||||
|
font-family: var(--typography-Body-Bold-fontFamily);
|
||||||
|
font-size: var(--typography-Body-Bold-fontSize);
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: var(--typography-Body-Bold-letterSpacing);
|
||||||
|
line-height: var(--typography-Body-Bold-lineHeight);
|
||||||
|
text-decoration: var(--typography-Body-Bold-textDecoration);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.rangeEnd,
|
||||||
|
td.rangeStart {
|
||||||
|
background: var(--Base-Background-Primary-Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.rangeEnd[aria-selected="true"]:not([data-outside="true"]) {
|
||||||
|
border-radius: 0 50% 50% 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.rangeStart[aria-selected="true"] {
|
||||||
|
border-radius: 50% 0 0 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.rangeEnd[aria-selected="true"] button.dayButton:hover,
|
||||||
|
td.rangeStart[aria-selected="true"] button.dayButton:hover {
|
||||||
|
background: var(--Primary-Light-On-Surface-Accent);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.rangeEnd[aria-selected="true"]:not([data-outside="true"]) button.dayButton,
|
||||||
|
td.rangeStart[aria-selected="true"]:not([data-outside="true"])
|
||||||
|
button.dayButton {
|
||||||
|
background: var(--Primary-Light-On-Surface-Accent);
|
||||||
|
border: none;
|
||||||
|
color: var(--Base-Button-Inverted-Fill-Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day,
|
||||||
|
td.day[data-today="true"] {
|
||||||
|
color: var(--UI-Text-High-contrast);
|
||||||
|
height: 40px;
|
||||||
|
padding: var(--Spacing-x-half);
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day button.dayButton:hover {
|
||||||
|
background: var(--Base-Surface-Secondary-light-Hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day[data-outside="true"] button.dayButton {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day:not(td.rangeEnd, td.rangeStart)[aria-selected="true"],
|
||||||
|
td.rangeMiddle[aria-selected="true"] button.dayButton {
|
||||||
|
background: var(--Base-Background-Primary-Normal);
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day[data-disabled="true"],
|
||||||
|
td.day[data-disabled="true"] button.dayButton,
|
||||||
|
td.day[data-outside="true"] ~ td.day[data-disabled="true"],
|
||||||
|
td.day[data-outside="true"] ~ td.day[data-disabled="true"] button.dayButton,
|
||||||
|
.week:has(td.day[data-outside="true"] ~ td.day[data-disabled="true"])
|
||||||
|
td.day[data-outside="true"]
|
||||||
|
button.dayButton {
|
||||||
|
background: none;
|
||||||
|
color: var(--Base-Text-Disabled);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weekDay {
|
||||||
|
color: var(--UI-Text-Placeholder);
|
||||||
|
font-family: var(--typography-Footnote-Labels-fontFamily);
|
||||||
|
font-size: var(--typography-Footnote-Labels-fontSize);
|
||||||
|
font-weight: var(--typography-Footnote-Labels-fontWeight);
|
||||||
|
letter-spacing: var(--typography-Footnote-Labels-letterSpacing);
|
||||||
|
line-height: var(--typography-Footnote-Labels-lineHeight);
|
||||||
|
text-decoration: var(--typography-Footnote-Labels-textDecoration);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
margin-top: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nextButton {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previousButton {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
173
components/DatePicker/Screen/mobile.module.css
Normal file
173
components/DatePicker/Screen/mobile.module.css
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
.container {
|
||||||
|
--header-height: 68px;
|
||||||
|
--sticky-button-height: 120px;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"header"
|
||||||
|
"content";
|
||||||
|
grid-template-rows: var(--header-height) calc(100dvh - var(--header-height));
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
align-self: flex-start;
|
||||||
|
background-color: var(--Main-Grey-White);
|
||||||
|
display: grid;
|
||||||
|
grid-area: header;
|
||||||
|
grid-template-columns: 1fr 24px;
|
||||||
|
padding: var(--Spacing-x3) var(--Spacing-x2) var(--Spacing-x2);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select {
|
||||||
|
justify-self: center;
|
||||||
|
min-width: 100px;
|
||||||
|
transform: translateX(24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
align-items: center;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.months {
|
||||||
|
display: grid;
|
||||||
|
grid-area: content;
|
||||||
|
overflow-y: scroll;
|
||||||
|
scroll-snap-type: y mandatory;
|
||||||
|
}
|
||||||
|
|
||||||
|
.month {
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
scroll-snap-align: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.month:last-of-type {
|
||||||
|
padding-bottom: var(--sticky-button-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.monthCaption {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captionLabel {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
align-self: flex-start;
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(255, 255, 255, 0) 7.5%,
|
||||||
|
#ffffff 82.5%
|
||||||
|
);
|
||||||
|
display: flex;
|
||||||
|
grid-area: content;
|
||||||
|
padding: var(--Spacing-x1) var(--Spacing-x2) var(--Spacing-x7);
|
||||||
|
position: sticky;
|
||||||
|
top: calc(100vh - var(--sticky-button-height));
|
||||||
|
width: 100%;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer .button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day,
|
||||||
|
td.rangeEnd,
|
||||||
|
td.rangeStart {
|
||||||
|
font-family: var(--typography-Body-Bold-fontFamily);
|
||||||
|
font-size: var(--typography-Body-Bold-fontSize);
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: var(--typography-Body-Bold-letterSpacing);
|
||||||
|
line-height: var(--typography-Body-Bold-lineHeight);
|
||||||
|
text-decoration: var(--typography-Body-Bold-textDecoration);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.rangeEnd,
|
||||||
|
td.rangeStart {
|
||||||
|
background: var(--Base-Background-Primary-Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.rangeEnd[aria-selected="true"]:not([data-outside="true"]) {
|
||||||
|
border-radius: 0 50% 50% 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.rangeStart[aria-selected="true"] {
|
||||||
|
border-radius: 50% 0 0 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.rangeEnd[aria-selected="true"] button.dayButton:hover,
|
||||||
|
td.rangeStart[aria-selected="true"] button.dayButton:hover {
|
||||||
|
background: var(--Primary-Light-On-Surface-Accent);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.rangeEnd[aria-selected="true"]:not([data-outside="true"]) button.dayButton,
|
||||||
|
td.rangeStart[aria-selected="true"]:not([data-outside="true"])
|
||||||
|
button.dayButton {
|
||||||
|
background: var(--Primary-Light-On-Surface-Accent);
|
||||||
|
border: none;
|
||||||
|
color: var(--Base-Button-Inverted-Fill-Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day,
|
||||||
|
td.day[data-today="true"] {
|
||||||
|
color: var(--UI-Text-High-contrast);
|
||||||
|
height: 40px;
|
||||||
|
padding: var(--Spacing-x-half);
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day button.dayButton:hover {
|
||||||
|
background: var(--Base-Surface-Secondary-light-Hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day[data-outside="true"] button.dayButton {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day:not(td.rangeEnd, td.rangeStart)[aria-selected="true"],
|
||||||
|
td.rangeMiddle[aria-selected="true"] button.dayButton {
|
||||||
|
background: var(--Base-Background-Primary-Normal);
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.day[data-disabled="true"],
|
||||||
|
td.day[data-disabled="true"] button.dayButton,
|
||||||
|
td.day[data-outside="true"] ~ td.day[data-disabled="true"],
|
||||||
|
td.day[data-outside="true"] ~ td.day[data-disabled="true"] button.dayButton,
|
||||||
|
.week:has(td.day[data-outside="true"] ~ td.day[data-disabled="true"])
|
||||||
|
td.day[data-outside="true"]
|
||||||
|
button.dayButton {
|
||||||
|
background: none;
|
||||||
|
color: var(--Base-Text-Disabled);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weekDay {
|
||||||
|
color: var(--Base-Text-Medium-contrast);
|
||||||
|
font-family: var(--typography-Footnote-Labels-fontFamily);
|
||||||
|
font-size: var(--typography-Footnote-Labels-fontSize);
|
||||||
|
font-weight: var(--typography-Footnote-Labels-fontWeight);
|
||||||
|
letter-spacing: var(--typography-Footnote-Labels-letterSpacing);
|
||||||
|
line-height: var(--typography-Footnote-Labels-lineHeight);
|
||||||
|
text-decoration: var(--typography-Footnote-Labels-textDecoration);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1367px) {
|
||||||
|
.container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,16 +7,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hideWrapper {
|
|
||||||
background-color: var(--Main-Grey-White);
|
|
||||||
border-radius: var(--Corner-radius-Medium);
|
|
||||||
box-shadow: 0px 16px 24px 0px rgba(0, 0, 0, 0.08);
|
|
||||||
padding: var(--Spacing-x-one-and-half);
|
|
||||||
position: absolute;
|
|
||||||
/** BookingWidget padding + border-width */
|
|
||||||
top: calc(100% + var(--Spacing-x2) + 1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -29,3 +19,43 @@
|
|||||||
.body {
|
.body {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hideWrapper {
|
||||||
|
background-color: var(--Main-Grey-White);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1366px) {
|
||||||
|
.container {
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hideWrapper {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
overflow: auto;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 100%;
|
||||||
|
transition: top 300ms ease;
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container[data-isopen="true"] .hideWrapper {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1367px) {
|
||||||
|
.hideWrapper {
|
||||||
|
border-radius: var(--Corner-radius-Large);
|
||||||
|
box-shadow: 0px 0px 14px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
padding: var(--Spacing-x2) var(--Spacing-x3);
|
||||||
|
position: absolute;
|
||||||
|
/**
|
||||||
|
BookingWidget padding +
|
||||||
|
border-width +
|
||||||
|
wanted space below booking widget
|
||||||
|
*/
|
||||||
|
top: calc(100% + var(--Spacing-x2) + 1px + var(--Spacing-x4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
import { da, de, fi, nb, sv } from "date-fns/locale"
|
||||||
import { useEffect, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
import { useFormContext, useWatch } from "react-hook-form"
|
import { useFormContext, useWatch } from "react-hook-form"
|
||||||
|
|
||||||
|
import { Lang } from "@/constants/languages"
|
||||||
import { dt } from "@/lib/dt"
|
import { dt } from "@/lib/dt"
|
||||||
|
|
||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
import useLang from "@/hooks/useLang"
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
import DatePicker from "./DatePicker"
|
import DatePickerDesktop from "./Screen/Desktop"
|
||||||
|
import DatePickerMobile from "./Screen/Mobile"
|
||||||
|
|
||||||
import styles from "./date-picker.module.css"
|
import styles from "./date-picker.module.css"
|
||||||
|
|
||||||
@@ -15,6 +18,14 @@ import type { DateRange } from "react-day-picker"
|
|||||||
|
|
||||||
import type { DatePickerFormProps } from "@/types/components/datepicker"
|
import type { DatePickerFormProps } from "@/types/components/datepicker"
|
||||||
|
|
||||||
|
const locales = {
|
||||||
|
[Lang.da]: da,
|
||||||
|
[Lang.de]: de,
|
||||||
|
[Lang.fi]: fi,
|
||||||
|
[Lang.no]: nb,
|
||||||
|
[Lang.sv]: sv,
|
||||||
|
}
|
||||||
|
|
||||||
export default function DatePickerForm({ name = "date" }: DatePickerFormProps) {
|
export default function DatePickerForm({ name = "date" }: DatePickerFormProps) {
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
@@ -22,6 +33,10 @@ export default function DatePickerForm({ name = "date" }: DatePickerFormProps) {
|
|||||||
const { register, setValue } = useFormContext()
|
const { register, setValue } = useFormContext()
|
||||||
const ref = useRef<HTMLDivElement | null>(null)
|
const ref = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
setIsOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
function handleOnClick() {
|
function handleOnClick() {
|
||||||
setIsOpen((prevIsOpen) => !prevIsOpen)
|
setIsOpen((prevIsOpen) => !prevIsOpen)
|
||||||
}
|
}
|
||||||
@@ -40,11 +55,9 @@ export default function DatePickerForm({ name = "date" }: DatePickerFormProps) {
|
|||||||
setIsOpen(false)
|
setIsOpen(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
document.body.addEventListener("click", handleClickOutside)
|
||||||
document.addEventListener("click", handleClickOutside)
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("click", handleClickOutside)
|
document.body.removeEventListener("click", handleClickOutside)
|
||||||
}
|
}
|
||||||
}, [setIsOpen])
|
}, [setIsOpen])
|
||||||
|
|
||||||
@@ -63,9 +76,17 @@ export default function DatePickerForm({ name = "date" }: DatePickerFormProps) {
|
|||||||
<input {...register("date.from")} type="hidden" />
|
<input {...register("date.from")} type="hidden" />
|
||||||
<input {...register("date.to")} type="hidden" />
|
<input {...register("date.to")} type="hidden" />
|
||||||
<div aria-modal className={styles.hideWrapper} role="dialog">
|
<div aria-modal className={styles.hideWrapper} role="dialog">
|
||||||
<DatePicker
|
<DatePickerDesktop
|
||||||
|
close={close}
|
||||||
handleOnSelect={handleSelectDate}
|
handleOnSelect={handleSelectDate}
|
||||||
initialSelected={selectedDate}
|
locales={locales}
|
||||||
|
selectedDate={selectedDate}
|
||||||
|
/>
|
||||||
|
<DatePickerMobile
|
||||||
|
close={close}
|
||||||
|
handleOnSelect={handleSelectDate}
|
||||||
|
locales={locales}
|
||||||
|
selectedDate={selectedDate}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
outline: none;
|
outline: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +1,80 @@
|
|||||||
.input {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--Spacing-x2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input input[type="text"] {
|
|
||||||
border: none;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rooms,
|
|
||||||
.vouchers,
|
|
||||||
.when,
|
|
||||||
.where {
|
|
||||||
border-right: 1px solid var(--Base-Surface-Subtle-Normal);
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rooms,
|
|
||||||
.when {
|
|
||||||
max-width: 240px;
|
|
||||||
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
|
||||||
}
|
|
||||||
|
|
||||||
.vouchers {
|
|
||||||
max-width: 200px;
|
|
||||||
padding: var(--Spacing-x1) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.where {
|
|
||||||
max-width: 280px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.options {
|
.options {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 158px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.option {
|
.option {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1366px) {
|
||||||
|
.input {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rooms,
|
||||||
|
.vouchers,
|
||||||
|
.when,
|
||||||
|
.where {
|
||||||
|
background-color: var(--Base-Background-Primary-Normal);
|
||||||
|
border-radius: var(--Corner-radius-Medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rooms,
|
||||||
|
.vouchers,
|
||||||
|
.when {
|
||||||
|
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
margin-top: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1367px) {
|
||||||
|
.input {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rooms,
|
||||||
|
.vouchers,
|
||||||
|
.when,
|
||||||
|
.where {
|
||||||
|
border-right: 1px solid var(--Base-Surface-Subtle-Normal);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input input[type="text"] {
|
||||||
|
border: none;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rooms,
|
||||||
|
.when {
|
||||||
|
max-width: 240px;
|
||||||
|
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vouchers {
|
||||||
|
max-width: 200px;
|
||||||
|
padding: var(--Spacing-x1) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.where {
|
||||||
|
max-width: 280px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
max-width: 158px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ export default function FormContent({
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const selectedDate = useWatch({ name: "date" })
|
const selectedDate = useWatch({ name: "date" })
|
||||||
|
|
||||||
const rooms = intl.formatMessage({ id: "Rooms & Guests" })
|
const rooms = intl.formatMessage({ id: "Guests & Rooms" })
|
||||||
const vouchers = intl.formatMessage({ id: "Booking codes and vouchers" })
|
const vouchers = intl.formatMessage({ id: "Code / Voucher" })
|
||||||
const bonus = intl.formatMessage({ id: "Use bonus cheque" })
|
const bonus = intl.formatMessage({ id: "Use bonus cheque" })
|
||||||
const reward = intl.formatMessage({ id: "Book reward night" })
|
const reward = intl.formatMessage({ id: "Book reward night" })
|
||||||
|
|
||||||
@@ -33,10 +33,10 @@ export default function FormContent({
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.when}>
|
<div className={styles.when}>
|
||||||
<Caption color="red" textTransform="bold">
|
<Caption color="red" textTransform="bold">
|
||||||
{nights}{" "}
|
{intl.formatMessage(
|
||||||
{nights > 1
|
{ id: "booking.nights" },
|
||||||
? intl.formatMessage({ id: "nights" })
|
{ totalNights: nights }
|
||||||
: intl.formatMessage({ id: "night" })}
|
)}
|
||||||
</Caption>
|
</Caption>
|
||||||
<DatePicker />
|
<DatePicker />
|
||||||
</div>
|
</div>
|
||||||
@@ -47,20 +47,20 @@ export default function FormContent({
|
|||||||
<input type="text" placeholder={rooms} />
|
<input type="text" placeholder={rooms} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.vouchers}>
|
<div className={styles.vouchers}>
|
||||||
<Caption color="textMediumContrast" textTransform="bold">
|
<Caption color="uiTextMediumContrast" textTransform="bold">
|
||||||
{vouchers}
|
{vouchers}
|
||||||
</Caption>
|
</Caption>
|
||||||
<input type="text" placeholder={vouchers} />
|
<input type="text" placeholder={vouchers} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.options}>
|
<div className={styles.options}>
|
||||||
<div className={styles.option}>
|
<label className={styles.option}>
|
||||||
<input type="checkbox" />
|
<input type="checkbox" />
|
||||||
<Caption color="textMediumContrast">{bonus}</Caption>
|
<Caption color="textMediumContrast">{bonus}</Caption>
|
||||||
</div>
|
</label>
|
||||||
<div className={styles.option}>
|
<label className={styles.option}>
|
||||||
<input type="checkbox" />
|
<input type="checkbox" />
|
||||||
<Caption color="textMediumContrast">{reward}</Caption>
|
<Caption color="textMediumContrast">{reward}</Caption>
|
||||||
</div>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,15 +1,36 @@
|
|||||||
.section {
|
.section {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: grid;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: var(--max-width-navigation);
|
max-width: var(--max-width-navigation);
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.form {
|
||||||
width: 118px;
|
display: grid;
|
||||||
justify-content: center;
|
gap: var(--Spacing-x2);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1366px) {
|
||||||
|
.form {
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
align-self: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1367px) {
|
||||||
|
.section {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
justify-content: center;
|
||||||
|
width: 118px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { FormProvider, useForm } from "react-hook-form"
|
import { useFormContext } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { dt } from "@/lib/dt"
|
|
||||||
|
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
|
||||||
import FormContent from "./FormContent"
|
import FormContent from "./FormContent"
|
||||||
import { bookingWidgetSchema } from "./schema"
|
|
||||||
|
|
||||||
import styles from "./form.module.css"
|
import styles from "./form.module.css"
|
||||||
|
|
||||||
import type { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
import type { BookingWidgetSchema } from "@/types/components/bookingWidget"
|
||||||
import type { BookingWidgetFormProps } from "@/types/components/form/bookingwidget"
|
import type { BookingWidgetFormProps } from "@/types/components/form/bookingwidget"
|
||||||
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
|
||||||
|
|
||||||
const formId = "booking-widget"
|
const formId = "booking-widget"
|
||||||
|
|
||||||
@@ -24,40 +19,8 @@ export default function Form({ locations }: BookingWidgetFormProps) {
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const sessionStorageSearchData =
|
const { formState, handleSubmit, register } =
|
||||||
typeof window !== "undefined"
|
useFormContext<BookingWidgetSchema>()
|
||||||
? sessionStorage.getItem("searchData")
|
|
||||||
: undefined
|
|
||||||
const initialSelectedLocation: Location | undefined = sessionStorageSearchData
|
|
||||||
? JSON.parse(sessionStorageSearchData)
|
|
||||||
: undefined
|
|
||||||
const methods = useForm<BookingWidgetSchema>({
|
|
||||||
defaultValues: {
|
|
||||||
search: initialSelectedLocation?.name ?? "",
|
|
||||||
location: sessionStorageSearchData
|
|
||||||
? encodeURIComponent(sessionStorageSearchData)
|
|
||||||
: undefined,
|
|
||||||
date: {
|
|
||||||
// UTC is required to handle requests from far away timezones https://scandichotels.atlassian.net/browse/SWAP-6375 & PET-507
|
|
||||||
// This is specifically to handle timezones falling in different dates.
|
|
||||||
from: dt().utc().format("YYYY-MM-DD"),
|
|
||||||
to: dt().utc().add(1, "day").format("YYYY-MM-DD"),
|
|
||||||
},
|
|
||||||
bookingCode: "",
|
|
||||||
redemption: false,
|
|
||||||
voucher: false,
|
|
||||||
rooms: [
|
|
||||||
{
|
|
||||||
adults: 1,
|
|
||||||
childs: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
shouldFocusError: false,
|
|
||||||
mode: "all",
|
|
||||||
resolver: zodResolver(bookingWidgetSchema),
|
|
||||||
reValidateMode: "onChange",
|
|
||||||
})
|
|
||||||
|
|
||||||
function onSubmit(data: BookingWidgetSchema) {
|
function onSubmit(data: BookingWidgetSchema) {
|
||||||
data.location = JSON.parse(decodeURIComponent(data.location))
|
data.location = JSON.parse(decodeURIComponent(data.location))
|
||||||
@@ -70,25 +33,24 @@ export default function Form({ locations }: BookingWidgetFormProps) {
|
|||||||
return (
|
return (
|
||||||
<section className={styles.section}>
|
<section className={styles.section}>
|
||||||
<form
|
<form
|
||||||
onSubmit={methods.handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
className={styles.form}
|
className={styles.form}
|
||||||
id={formId}
|
id={formId}
|
||||||
>
|
>
|
||||||
<FormProvider {...methods}>
|
<input {...register("location")} type="hidden" />
|
||||||
<input {...methods.register("location")} type="hidden" />
|
<FormContent locations={locations} />
|
||||||
<FormContent locations={locations} />
|
|
||||||
</FormProvider>
|
|
||||||
</form>
|
</form>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
className={styles.button}
|
||||||
|
disabled={!formState.isValid}
|
||||||
form={formId}
|
form={formId}
|
||||||
|
intent="primary"
|
||||||
size="small"
|
size="small"
|
||||||
theme="base"
|
theme="base"
|
||||||
intent="primary"
|
type="submit"
|
||||||
className={styles.button}
|
|
||||||
>
|
>
|
||||||
<Caption color="white" textTransform="bold">
|
<Caption color="white" textTransform="bold">
|
||||||
{intl.formatMessage({ id: "Find hotels" })}
|
{intl.formatMessage({ id: "Search" })}
|
||||||
</Caption>
|
</Caption>
|
||||||
</Button>
|
</Button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { z } from "zod"
|
|||||||
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
||||||
|
|
||||||
export const bookingWidgetSchema = z.object({
|
export const bookingWidgetSchema = z.object({
|
||||||
search: z.string({ coerce: true }).min(1, "Required"),
|
bookingCode: z.string(), // Update this as required when working with booking codes component
|
||||||
date: z.object({
|
date: z.object({
|
||||||
|
// Update this as required once started working with Date picker in Nights component
|
||||||
from: z.string(),
|
from: z.string(),
|
||||||
to: z.string(),
|
to: z.string(),
|
||||||
}),
|
}),
|
||||||
@@ -23,14 +24,12 @@ export const bookingWidgetSchema = z.object({
|
|||||||
},
|
},
|
||||||
{ message: "Required" }
|
{ message: "Required" }
|
||||||
),
|
),
|
||||||
bookingCode: z.string(), // Update this as required when working with booking codes component
|
|
||||||
redemption: z.boolean().default(false),
|
redemption: z.boolean().default(false),
|
||||||
voucher: z.boolean().default(false),
|
|
||||||
rooms: z.array(
|
rooms: z.array(
|
||||||
// This will be updated when working in guests component
|
// This will be updated when working in guests component
|
||||||
z.object({
|
z.object({
|
||||||
adults: z.number().default(1),
|
adults: z.number().default(1),
|
||||||
childs: z.array(
|
children: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
age: z.number(),
|
age: z.number(),
|
||||||
bed: z.number(),
|
bed: z.number(),
|
||||||
@@ -38,4 +37,6 @@ export const bookingWidgetSchema = z.object({
|
|||||||
),
|
),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
search: z.string({ coerce: true }).min(1, "Required"),
|
||||||
|
voucher: z.boolean().default(false),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { Link } from "react-feather"
|
|
||||||
|
|
||||||
import { myPages } from "@/constants/routes/myPages"
|
import { myPages } from "@/constants/routes/myPages"
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
import { getLang } from "@/i18n/serverContext"
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
|||||||
@@ -48,12 +48,6 @@
|
|||||||
gap: var(--Spacing-x-half);
|
gap: var(--Spacing-x-half);
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
|
||||||
display: flex;
|
|
||||||
padding: var(--Spacing-x2) var(--Spacing-x0);
|
|
||||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
|
||||||
}
|
|
||||||
|
|
||||||
.prices {
|
.prices {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -115,7 +109,7 @@
|
|||||||
padding-bottom: var(--Spacing-x2);
|
padding-bottom: var(--Spacing-x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.detailsButton {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
import { useIntl } from "react-intl"
|
|
||||||
|
|
||||||
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
||||||
import {
|
import { PriceTagIcon, ScandicLogoIcon } from "@/components/Icons"
|
||||||
ChevronRightIcon,
|
|
||||||
PriceTagIcon,
|
|
||||||
ScandicLogoIcon,
|
|
||||||
} from "@/components/Icons"
|
|
||||||
import TripAdvisorIcon from "@/components/Icons/TripAdvisor"
|
import TripAdvisorIcon from "@/components/Icons/TripAdvisor"
|
||||||
import Image from "@/components/Image"
|
import Image from "@/components/Image"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
@@ -14,13 +8,16 @@ import Link from "@/components/TempDesignSystem/Link"
|
|||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||||
|
import { getIntl } from "@/i18n"
|
||||||
|
|
||||||
|
import ReadMore from "../ReadMore"
|
||||||
|
|
||||||
import styles from "./hotelCard.module.css"
|
import styles from "./hotelCard.module.css"
|
||||||
|
|
||||||
import { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps"
|
import { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps"
|
||||||
|
|
||||||
export default function HotelCard({ hotel }: HotelCardProps) {
|
export default async function HotelCard({ hotel }: HotelCardProps) {
|
||||||
const intl = useIntl()
|
const intl = await getIntl()
|
||||||
|
|
||||||
const { hotelData } = hotel
|
const { hotelData } = hotel
|
||||||
const { price } = hotel
|
const { price } = hotel
|
||||||
@@ -51,7 +48,7 @@ export default function HotelCard({ hotel }: HotelCardProps) {
|
|||||||
<Title as="h4" textTransform="capitalize">
|
<Title as="h4" textTransform="capitalize">
|
||||||
{hotelData.name}
|
{hotelData.name}
|
||||||
</Title>
|
</Title>
|
||||||
<Footnote color="textMediumContrast" className={styles.adress}>
|
<Footnote color="textMediumContrast">
|
||||||
{`${hotelData.address.streetAddress}, ${hotelData.address.city}`}
|
{`${hotelData.address.streetAddress}, ${hotelData.address.city}`}
|
||||||
</Footnote>
|
</Footnote>
|
||||||
<Footnote color="textMediumContrast">
|
<Footnote color="textMediumContrast">
|
||||||
@@ -70,10 +67,7 @@ export default function HotelCard({ hotel }: HotelCardProps) {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<Link href="#" color="burgundy" className={styles.link}>
|
<ReadMore hotelId={hotelData.operaId} hotel={hotelData} />
|
||||||
{intl.formatMessage({ id: "See hotel details" })}
|
|
||||||
<ChevronRightIcon color="burgundy" />
|
|
||||||
</Link>
|
|
||||||
</section>
|
</section>
|
||||||
<section className={styles.prices}>
|
<section className={styles.prices}>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||||
|
|
||||||
import HotelCard from "../HotelCard"
|
import HotelCard from "../HotelCard"
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--Spacing-x3);
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useState } from "react"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import ChevronRightSmallIcon from "@/components/Icons/ChevronRightSmall"
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||||
|
|
||||||
|
import styles from "./hotelDetailSidePeek.module.css"
|
||||||
|
|
||||||
|
export default function HotelDetailSidePeek() {
|
||||||
|
const intl = useIntl()
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
|
||||||
|
function toggleSidePeek() {
|
||||||
|
setIsOpen(!isOpen)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={styles.buttons}>
|
||||||
|
<Button
|
||||||
|
variant="icon"
|
||||||
|
theme="base"
|
||||||
|
intent="text"
|
||||||
|
wrapping
|
||||||
|
onClick={toggleSidePeek}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "See hotel details",
|
||||||
|
})}
|
||||||
|
<ChevronRightSmallIcon aria-hidden="true" color="burgundy" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="icon"
|
||||||
|
theme="base"
|
||||||
|
intent="text"
|
||||||
|
wrapping
|
||||||
|
onClick={toggleSidePeek}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "Show all amenities",
|
||||||
|
})}
|
||||||
|
<ChevronRightSmallIcon aria-hidden="true" color="burgundy" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<SidePeek
|
||||||
|
contentKey="hotel-detail-side-peek"
|
||||||
|
title="Hotel Details"
|
||||||
|
isOpen={isOpen}
|
||||||
|
handleClose={() => setIsOpen(false)}
|
||||||
|
>
|
||||||
|
<div>TBD</div>
|
||||||
|
</SidePeek>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
.hotelSelectionHeader {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--Base-Surface-Subtle-Normal);
|
||||||
|
padding: var(--Spacing-x3) var(--Spacing-x2);
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--Spacing-x3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--Spacing-x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.descriptionContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Spacing-x-one-and-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.address {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--Spacing-x-one-and-half);
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dividerContainer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.hotelSelectionHeader {
|
||||||
|
flex-direction: row;
|
||||||
|
padding: var(--Spacing-x4) var(--Spacing-x5);
|
||||||
|
gap: var(--Spacing-x6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleContainer > h1 {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dividerContainer {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address {
|
||||||
|
gap: var(--Spacing-x3);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
components/HotelReservation/HotelSelectionHeader/index.tsx
Normal file
52
components/HotelReservation/HotelSelectionHeader/index.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import Divider from "@/components/TempDesignSystem/Divider"
|
||||||
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||||
|
import { getIntl } from "@/i18n"
|
||||||
|
|
||||||
|
import HotelDetailSidePeek from "./HotelDetailSidePeek"
|
||||||
|
|
||||||
|
import styles from "./hotelSelectionHeader.module.css"
|
||||||
|
|
||||||
|
import { HotelSelectionHeaderProps } from "@/types/components/hotelReservation/selectRate/hotelSelectionHeader"
|
||||||
|
|
||||||
|
export default async function HotelSelectionHeader({
|
||||||
|
hotel,
|
||||||
|
}: HotelSelectionHeaderProps) {
|
||||||
|
const intl = await getIntl()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className={styles.hotelSelectionHeader}>
|
||||||
|
<div className={styles.titleContainer}>
|
||||||
|
<Title as="h3" level="h1">
|
||||||
|
{hotel.name}
|
||||||
|
</Title>
|
||||||
|
<address className={styles.address}>
|
||||||
|
<Caption color="textMediumContrast">
|
||||||
|
{hotel.address.streetAddress}, {hotel.address.city}
|
||||||
|
</Caption>
|
||||||
|
<div>
|
||||||
|
<Divider variant="vertical" color="subtle" />
|
||||||
|
</div>
|
||||||
|
<Caption color="textMediumContrast">
|
||||||
|
{intl.formatMessage(
|
||||||
|
{
|
||||||
|
id: "Distance to city centre",
|
||||||
|
},
|
||||||
|
{ number: hotel.location.distanceToCentre }
|
||||||
|
)}
|
||||||
|
</Caption>
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
<div className={styles.dividerContainer}>
|
||||||
|
<Divider variant="vertical" color="subtle" />
|
||||||
|
</div>
|
||||||
|
<div className={styles.descriptionContainer}>
|
||||||
|
<Body color="textHighContrast">
|
||||||
|
{hotel.hotelContent.texts.descriptions.short}
|
||||||
|
</Body>
|
||||||
|
<HotelDetailSidePeek />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
.wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
font-family: var(--typography-Body-Regular-fontFamily);
|
||||||
|
}
|
||||||
|
|
||||||
|
.address,
|
||||||
|
.contactInfo {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: subgrid;
|
||||||
|
grid-template-rows: subgrid;
|
||||||
|
grid-column: 1 / 3;
|
||||||
|
grid-row: 1 / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactInfo > li {
|
||||||
|
font-style: normal;
|
||||||
|
list-style-type: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.soMeIcons {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--Spacing-x-one-and-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecoLabel {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
column-gap: var(--Spacing-x-one-and-half);
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
grid-row: 3 / 4;
|
||||||
|
font-size: var(--typography-Footnote-Regular-fontSize);
|
||||||
|
line-height: ();
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecoLabelText {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
85
components/HotelReservation/ReadMore/Contact/index.tsx
Normal file
85
components/HotelReservation/ReadMore/Contact/index.tsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import FacebookIcon from "@/components/Icons/Facebook"
|
||||||
|
import InstagramIcon from "@/components/Icons/Instagram"
|
||||||
|
import Image from "@/components/Image"
|
||||||
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
|
import styles from "./contact.module.css"
|
||||||
|
|
||||||
|
import { ContactProps } from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||||
|
|
||||||
|
export default function Contact({ hotel }: ContactProps) {
|
||||||
|
const lang = useLang()
|
||||||
|
const intl = useIntl()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.wrapper}>
|
||||||
|
<address className={styles.address}>
|
||||||
|
<ul className={styles.contactInfo}>
|
||||||
|
<li>
|
||||||
|
<span className={styles.heading}>
|
||||||
|
{intl.formatMessage({ id: "Address" })}
|
||||||
|
</span>
|
||||||
|
<span>{hotel.address.streetAddress}</span>
|
||||||
|
<span>{hotel.address.city}</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className={styles.heading}>
|
||||||
|
{intl.formatMessage({ id: "Driving directions" })}
|
||||||
|
</span>
|
||||||
|
<Link href="#">{intl.formatMessage({ id: "Google Maps" })}</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className={styles.heading}>
|
||||||
|
{intl.formatMessage({ id: "Email" })}
|
||||||
|
</span>
|
||||||
|
<Link href={`mailto:${hotel.contactInformation.email}`}>
|
||||||
|
{hotel.contactInformation.email}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className={styles.heading}>
|
||||||
|
{intl.formatMessage({ id: "Contact us" })}
|
||||||
|
</span>
|
||||||
|
<Link href={`tel:${hotel.contactInformation.phoneNumber}`}>
|
||||||
|
{hotel.contactInformation.phoneNumber}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className={styles.heading}>
|
||||||
|
{intl.formatMessage({ id: "Follow us" })}
|
||||||
|
</span>
|
||||||
|
<div className={styles.soMeIcons}>
|
||||||
|
<Link href="#" target="_blank">
|
||||||
|
<InstagramIcon color="burgundy" />
|
||||||
|
</Link>
|
||||||
|
<Link href="#" target="_blank">
|
||||||
|
<FacebookIcon color="burgundy" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</address>
|
||||||
|
{hotel.hotelFacts.ecoLabels.nordicEcoLabel ? (
|
||||||
|
<div className={styles.ecoLabel}>
|
||||||
|
<Image
|
||||||
|
height={38}
|
||||||
|
width={38}
|
||||||
|
alt={intl.formatMessage({ id: "Nordic Swan Ecolabel" })}
|
||||||
|
src={`/_static/img/icons/swan-eco/swan_eco_dark_${lang}.png`}
|
||||||
|
/>
|
||||||
|
<div className={styles.ecoLabelText}>
|
||||||
|
<span>{intl.formatMessage({ id: "Nordic Swan Ecolabel" })}</span>
|
||||||
|
<span>
|
||||||
|
{hotel.hotelFacts.ecoLabels.svanenEcoLabelCertificateNumber}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
119
components/HotelReservation/ReadMore/index.tsx
Normal file
119
components/HotelReservation/ReadMore/index.tsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useState } from "react"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { ChevronRightIcon } from "@/components/Icons"
|
||||||
|
import Accordion from "@/components/TempDesignSystem/Accordion"
|
||||||
|
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||||
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
|
|
||||||
|
import Contact from "./Contact"
|
||||||
|
|
||||||
|
import styles from "./readMore.module.css"
|
||||||
|
|
||||||
|
import {
|
||||||
|
DetailedAmenity,
|
||||||
|
ParkingProps,
|
||||||
|
ReadMoreProps,
|
||||||
|
} from "@/types/components/hotelReservation/selectHotel/selectHotel"
|
||||||
|
import { Hotel } from "@/types/hotel"
|
||||||
|
|
||||||
|
function getAmenitiesList(hotel: Hotel) {
|
||||||
|
const detailedAmenities: DetailedAmenity[] = Object.entries(
|
||||||
|
hotel.hotelFacts.hotelFacilityDetail
|
||||||
|
).map(([key, value]) => ({ name: key, ...value }))
|
||||||
|
|
||||||
|
// Remove Parking facilities since parking accordion is based on hotel.parking
|
||||||
|
const simpleAmenities = hotel.detailedFacilities.filter(
|
||||||
|
(facility) => !facility.name.startsWith("Parking")
|
||||||
|
)
|
||||||
|
return [...detailedAmenities, ...simpleAmenities]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ReadMore({ hotel, hotelId }: ReadMoreProps) {
|
||||||
|
const intl = useIntl()
|
||||||
|
|
||||||
|
const [sidePeekOpen, setSidePeekOpen] = useState(false)
|
||||||
|
|
||||||
|
const amenitiesList = getAmenitiesList(hotel)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
setSidePeekOpen(true)
|
||||||
|
}}
|
||||||
|
intent={"text"}
|
||||||
|
color="burgundy"
|
||||||
|
className={styles.detailsButton}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: "See hotel details" })}
|
||||||
|
<ChevronRightIcon color="burgundy" />
|
||||||
|
</Button>
|
||||||
|
<SidePeek
|
||||||
|
title={hotel.name}
|
||||||
|
isOpen={sidePeekOpen}
|
||||||
|
contentKey={`${hotelId}`}
|
||||||
|
handleClose={() => {
|
||||||
|
setSidePeekOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className={styles.content}>
|
||||||
|
<Subtitle>
|
||||||
|
{intl.formatMessage({ id: "Practical information" })}
|
||||||
|
</Subtitle>
|
||||||
|
<Contact hotel={hotel} />
|
||||||
|
<Accordion>
|
||||||
|
{/* parking */}
|
||||||
|
{hotel.parking.length ? (
|
||||||
|
<AccordionItem title={intl.formatMessage({ id: "Parking" })}>
|
||||||
|
{hotel.parking.map((p) => (
|
||||||
|
<Parking key={p.name} parking={p} />
|
||||||
|
))}
|
||||||
|
</AccordionItem>
|
||||||
|
) : null}
|
||||||
|
<AccordionItem title={intl.formatMessage({ id: "Accessibility" })}>
|
||||||
|
TODO: What content should be in the accessibility section?
|
||||||
|
</AccordionItem>
|
||||||
|
{amenitiesList.map((amenity) => {
|
||||||
|
return "description" in amenity ? (
|
||||||
|
<AccordionItem key={amenity.name} title={amenity.heading}>
|
||||||
|
{amenity.description}
|
||||||
|
</AccordionItem>
|
||||||
|
) : (
|
||||||
|
<div key={amenity.id} className={styles.amenity}>
|
||||||
|
{amenity.name}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Accordion>
|
||||||
|
{/* TODO: handle linking to Hotel Page */}
|
||||||
|
<Button theme={"base"}>To the hotel</Button>
|
||||||
|
</div>
|
||||||
|
</SidePeek>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Parking({ parking }: ParkingProps) {
|
||||||
|
const intl = useIntl()
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Body>{`${intl.formatMessage({ id: parking.type })} (${parking.name})`}</Body>
|
||||||
|
<ul className={styles.list}>
|
||||||
|
<li>
|
||||||
|
{`${intl.formatMessage({
|
||||||
|
id: "Number of charging points for electric cars",
|
||||||
|
})}: ${parking.numberOfChargingSpaces}`}
|
||||||
|
</li>
|
||||||
|
<li>{`${intl.formatMessage({ id: "Parking can be reserved in advance" })}: ${parking.canMakeReservation ? intl.formatMessage({ id: "Yes" }) : intl.formatMessage({ id: "No" })}`}</li>
|
||||||
|
<li>{`${intl.formatMessage({ id: "Number of parking spots" })}: ${parking.numberOfParkingSpots}`}</li>
|
||||||
|
<li>{`${intl.formatMessage({ id: "Distance to hotel" })}: ${parking.distanceToHotel}`}</li>
|
||||||
|
<li>{`${intl.formatMessage({ id: "Address" })}: ${parking.address}`}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
25
components/HotelReservation/ReadMore/readMore.module.css
Normal file
25
components/HotelReservation/ReadMore/readMore.module.css
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
.detailsButton {
|
||||||
|
align-self: start;
|
||||||
|
border-radius: 0;
|
||||||
|
height: auto;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.amenity {
|
||||||
|
font-family: var(--typography-Body-Regular-fontFamily);
|
||||||
|
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||||
|
/* padding set to align with AccordionItem which has a different composition */
|
||||||
|
padding: var(--Spacing-x2)
|
||||||
|
calc(var(--Spacing-x1) + var(--Spacing-x-one-and-half));
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
font-family: var(--typography-Body-Regular-fontFamily);
|
||||||
|
list-style: inside;
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import { useIntl } from "react-intl"
|
|||||||
|
|
||||||
import styles from "./hotelFilter.module.css"
|
import styles from "./hotelFilter.module.css"
|
||||||
|
|
||||||
import { HotelFiltersProps } from "@/types/components/hotelReservation/selectHotel/hotelFiltersProps"
|
import { HotelFiltersProps } from "@/types/components/hotelReservation/selectHotel/hotelFilters"
|
||||||
|
|
||||||
export default function HotelFilter({ filters }: HotelFiltersProps) {
|
export default function HotelFilter({ filters }: HotelFiltersProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { HotelListingProps } from "@/types/components/hotelReservation/selectHotel/map"
|
||||||
|
|
||||||
|
// TODO: This component is copied from
|
||||||
|
// components/ContentType/HotelPage/Map/DynamicMap/Sidebar.
|
||||||
|
// Look at that for inspiration on how to do the interaction with the map.
|
||||||
|
|
||||||
|
export default function HotelListing({}: HotelListingProps) {
|
||||||
|
return <section>Hotel listing TBI</section>
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
"use client"
|
||||||
|
import { APIProvider } from "@vis.gl/react-google-maps"
|
||||||
|
import { useState } from "react"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { selectHotel } from "@/constants/routes/hotelReservation"
|
||||||
|
|
||||||
|
import { CloseIcon } from "@/components/Icons"
|
||||||
|
import InteractiveMap from "@/components/Maps/InteractiveMap"
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
|
import HotelListing from "./HotelListing"
|
||||||
|
|
||||||
|
import styles from "./selectHotelMap.module.css"
|
||||||
|
|
||||||
|
import { SelectHotelMapProps } from "@/types/components/hotelReservation/selectHotel/map"
|
||||||
|
|
||||||
|
export default function SelectHotelMap({
|
||||||
|
apiKey,
|
||||||
|
coordinates,
|
||||||
|
pointsOfInterest,
|
||||||
|
mapId,
|
||||||
|
}: SelectHotelMapProps) {
|
||||||
|
const lang = useLang()
|
||||||
|
const intl = useIntl()
|
||||||
|
const [activePoi, setActivePoi] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const closeButton = (
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
intent="inverted"
|
||||||
|
size="small"
|
||||||
|
theme="base"
|
||||||
|
className={styles.closeButton}
|
||||||
|
>
|
||||||
|
<Link href={selectHotel[lang]} keepSearchParams color="burgundy">
|
||||||
|
<CloseIcon color="burgundy" />
|
||||||
|
{intl.formatMessage({ id: "Close the map" })}
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<APIProvider apiKey={apiKey}>
|
||||||
|
<HotelListing />
|
||||||
|
<InteractiveMap
|
||||||
|
closeButton={closeButton}
|
||||||
|
coordinates={coordinates}
|
||||||
|
pointsOfInterest={pointsOfInterest}
|
||||||
|
activePoi={activePoi}
|
||||||
|
onActivePoiChange={setActivePoi}
|
||||||
|
mapId={mapId}
|
||||||
|
/>
|
||||||
|
</APIProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.closeButton {
|
||||||
|
pointer-events: initial;
|
||||||
|
box-shadow: var(--button-box-shadow);
|
||||||
|
gap: var(--Spacing-x-half);
|
||||||
|
}
|
||||||
36
components/Icons/Edit.tsx
Normal file
36
components/Icons/Edit.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { iconVariants } from "./variants"
|
||||||
|
|
||||||
|
import type { IconProps } from "@/types/components/icon"
|
||||||
|
|
||||||
|
export default function EditIcon({ className, color, ...props }: IconProps) {
|
||||||
|
const classNames = iconVariants({ className, color })
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className={classNames}
|
||||||
|
fill="none"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
width="20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<mask
|
||||||
|
height="20"
|
||||||
|
id="mask0_162_2666"
|
||||||
|
maskUnits="userSpaceOnUse"
|
||||||
|
style={{ maskType: "alpha" }}
|
||||||
|
width="20"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
>
|
||||||
|
<rect width="20" height="20" fill="#D9D9D9" />
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask0_162_2666)">
|
||||||
|
<path
|
||||||
|
d="M4.58333 16.8125C4.19949 16.8125 3.87088 16.6758 3.59752 16.4025C3.32417 16.1291 3.1875 15.8005 3.1875 15.4167V4.58333C3.1875 4.19375 3.32313 3.86024 3.5944 3.58281C3.86566 3.30538 4.19531 3.17361 4.58333 3.1875H11.7292L10.3333 4.58333H4.58333V15.4167H15.4167V9.65625L16.8125 8.26042V15.4167C16.8125 15.8005 16.6758 16.1291 16.4025 16.4025C16.1291 16.6758 15.8005 16.8125 15.4167 16.8125H4.58333ZM8.05208 11.9479V8.83333L15.4583 1.42708C15.6042 1.28125 15.7569 1.17882 15.9167 1.11979C16.0764 1.06076 16.245 1.03125 16.4224 1.03125C16.6116 1.03125 16.7899 1.06076 16.9573 1.11979C17.1247 1.17882 17.2808 1.27974 17.4259 1.42256L18.5625 2.5625C18.7083 2.70833 18.8125 2.86585 18.875 3.03504C18.9375 3.20424 18.9688 3.38006 18.9688 3.5625C18.9688 3.74529 18.9373 3.91971 18.8745 4.08575C18.8118 4.25179 18.7077 4.40724 18.5625 4.55208L11.1667 11.9479H8.05208ZM9.44792 10.5521H10.5938L15.4583 5.67708L14.8958 5.09375L14.3229 4.54167L9.44792 9.40625V10.5521Z"
|
||||||
|
fill="#26201E"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ export { default as CrossCircle } from "./CrossCircle"
|
|||||||
export { default as CulturalIcon } from "./Cultural"
|
export { default as CulturalIcon } from "./Cultural"
|
||||||
export { default as DeleteIcon } from "./Delete"
|
export { default as DeleteIcon } from "./Delete"
|
||||||
export { default as DoorOpenIcon } from "./DoorOpen"
|
export { default as DoorOpenIcon } from "./DoorOpen"
|
||||||
|
export { default as EditIcon } from "./Edit"
|
||||||
export { default as ElectricBikeIcon } from "./ElectricBike"
|
export { default as ElectricBikeIcon } from "./ElectricBike"
|
||||||
export { default as EmailIcon } from "./Email"
|
export { default as EmailIcon } from "./Email"
|
||||||
export { default as ErrorCircleIcon } from "./ErrorCircle"
|
export { default as ErrorCircleIcon } from "./ErrorCircle"
|
||||||
|
|||||||
@@ -18,16 +18,12 @@
|
|||||||
color: var(--Primary-Light-On-Surface-Accent);
|
color: var(--Primary-Light-On-Surface-Accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.li:has(.heart),
|
|
||||||
.li:has(.check) {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.li {
|
.li {
|
||||||
margin-left: var(--Spacing-x3);
|
margin-left: var(--Spacing-x3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.li:has(.heart):before {
|
.heart > .li::before,
|
||||||
|
.li:has(.heart)::before {
|
||||||
content: url("/_static/icons/heart.svg");
|
content: url("/_static/icons/heart.svg");
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
@@ -36,6 +32,14 @@
|
|||||||
margin-left: calc(var(--Spacing-x3) * -1);
|
margin-left: calc(var(--Spacing-x3) * -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.heart > .li,
|
||||||
|
.check > .li,
|
||||||
|
.li:has(.check),
|
||||||
|
.li:has(.heart) {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check > .li::before,
|
||||||
.li:has(.check)::before {
|
.li:has(.check)::before {
|
||||||
content: url("/_static/icons/check-ring.svg");
|
content: url("/_static/icons/check-ring.svg");
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -59,6 +63,7 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.ol:has(li:nth-last-child(n + 5)),
|
.ol:has(li:nth-last-child(n + 5)),
|
||||||
.ul:has(li:nth-last-child(n + 5)) {
|
.ul:has(li:nth-last-child(n + 5)) {
|
||||||
@@ -66,3 +71,11 @@
|
|||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@container sidebar (max-width: 360px) {
|
||||||
|
.ol,
|
||||||
|
.ul {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ import Caption from "../TempDesignSystem/Text/Caption"
|
|||||||
import Footnote from "../TempDesignSystem/Text/Footnote"
|
import Footnote from "../TempDesignSystem/Text/Footnote"
|
||||||
import Subtitle from "../TempDesignSystem/Text/Subtitle"
|
import Subtitle from "../TempDesignSystem/Text/Subtitle"
|
||||||
import Title from "../TempDesignSystem/Text/Title"
|
import Title from "../TempDesignSystem/Text/Title"
|
||||||
import { hasAvailableParagraphFormat, hasAvailableULFormat } from "./utils"
|
import {
|
||||||
|
hasAvailableParagraphFormat,
|
||||||
|
hasAvailableULFormat,
|
||||||
|
makeCssModuleCompatibleClassName,
|
||||||
|
} from "./utils"
|
||||||
|
|
||||||
import styles from "./jsontohtml.module.css"
|
import styles from "./jsontohtml.module.css"
|
||||||
|
|
||||||
@@ -217,8 +221,16 @@ export const renderOptions: RenderOptions = {
|
|||||||
fullRenderOptions: RenderOptions
|
fullRenderOptions: RenderOptions
|
||||||
) => {
|
) => {
|
||||||
const props = extractPossibleAttributes(node.attrs)
|
const props = extractPossibleAttributes(node.attrs)
|
||||||
|
const compatibleClassName = makeCssModuleCompatibleClassName(
|
||||||
|
props.className,
|
||||||
|
"ul"
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
<li key={node.uid} {...props} className={styles.li}>
|
<li
|
||||||
|
key={node.uid}
|
||||||
|
{...props}
|
||||||
|
className={`${styles.li} ${compatibleClassName}`}
|
||||||
|
>
|
||||||
{next(node.children, embeds, fullRenderOptions)}
|
{next(node.children, embeds, fullRenderOptions)}
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
@@ -525,6 +537,10 @@ export const renderOptions: RenderOptions = {
|
|||||||
fullRenderOptions: RenderOptions
|
fullRenderOptions: RenderOptions
|
||||||
) => {
|
) => {
|
||||||
const props = extractPossibleAttributes(node.attrs)
|
const props = extractPossibleAttributes(node.attrs)
|
||||||
|
const compatibleClassName = makeCssModuleCompatibleClassName(
|
||||||
|
props.className,
|
||||||
|
"ul"
|
||||||
|
)
|
||||||
|
|
||||||
// Set the number of rows dynamically to create even rows for each column. We want the li:s
|
// Set the number of rows dynamically to create even rows for each column. We want the li:s
|
||||||
// to flow with the column, so therefore this is needed.
|
// to flow with the column, so therefore this is needed.
|
||||||
@@ -538,7 +554,7 @@ export const renderOptions: RenderOptions = {
|
|||||||
<ul
|
<ul
|
||||||
key={node.uid}
|
key={node.uid}
|
||||||
{...props}
|
{...props}
|
||||||
className={styles.ul}
|
className={`${styles.ul} ${compatibleClassName}`}
|
||||||
style={
|
style={
|
||||||
numberOfRows
|
numberOfRows
|
||||||
? {
|
? {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { renderOptions } from "./renderOptions"
|
import { renderOptions } from "./renderOptions"
|
||||||
|
|
||||||
|
import styles from "./jsontohtml.module.css"
|
||||||
|
|
||||||
import type { Node } from "@/types/requests/utils/edges"
|
import type { Node } from "@/types/requests/utils/edges"
|
||||||
import type { EmbedByUid } from "@/types/transitionTypes/jsontohtml"
|
import type { EmbedByUid } from "@/types/transitionTypes/jsontohtml"
|
||||||
import {
|
import {
|
||||||
@@ -136,3 +138,19 @@ export function nodesToHtml(
|
|||||||
const fullRenderOptions = { ...renderOptions, ...overrideRenderOptions }
|
const fullRenderOptions = { ...renderOptions, ...overrideRenderOptions }
|
||||||
return nodes.map((node) => nodeToHtml(node, embeds, fullRenderOptions))
|
return nodes.map((node) => nodeToHtml(node, embeds, fullRenderOptions))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function makeCssModuleCompatibleClassName(
|
||||||
|
className: string | undefined,
|
||||||
|
formatType: "ul"
|
||||||
|
): string {
|
||||||
|
if (!className) return ""
|
||||||
|
|
||||||
|
if (formatType === "ul" && hasAvailableULFormat(className)) {
|
||||||
|
// @ts-ignore: We want to set css modules classNames even if it does not correspond
|
||||||
|
// to an existing class in the module style sheet. Due to our css modules plugin for
|
||||||
|
// typescript, we cannot do this without the ts-ignore
|
||||||
|
return styles[className] || className
|
||||||
|
}
|
||||||
|
|
||||||
|
return className
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import type { LanguageSwitcherContentProps } from "@/types/components/languageSw
|
|||||||
|
|
||||||
export default function LanguageSwitcherContent({
|
export default function LanguageSwitcherContent({
|
||||||
urls,
|
urls,
|
||||||
|
onLanguageSwitch,
|
||||||
}: LanguageSwitcherContentProps) {
|
}: LanguageSwitcherContentProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const currentLanguage = useLang()
|
const currentLanguage = useLang()
|
||||||
@@ -39,6 +40,7 @@ export default function LanguageSwitcherContent({
|
|||||||
<Link
|
<Link
|
||||||
className={`${styles.link} ${isActive ? styles.active : ""}`}
|
className={`${styles.link} ${isActive ? styles.active : ""}`}
|
||||||
href={url}
|
href={url}
|
||||||
|
onClick={onLanguageSwitch}
|
||||||
>
|
>
|
||||||
{languages[key]}
|
{languages[key]}
|
||||||
{isActive ? <CheckIcon color="burgundy" /> : null}
|
{isActive ? <CheckIcon color="burgundy" /> : null}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
|
import { useEffect, useRef } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { languages } from "@/constants/languages"
|
import { languages } from "@/constants/languages"
|
||||||
@@ -28,6 +29,7 @@ export default function LanguageSwitcher({
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const currentLanguage = useLang()
|
const currentLanguage = useLang()
|
||||||
const toggleDropdown = useDropdownStore((state) => state.toggleDropdown)
|
const toggleDropdown = useDropdownStore((state) => state.toggleDropdown)
|
||||||
|
const languageSwitcherRef = useRef<HTMLDivElement>(null)
|
||||||
const isFooterLanguageSwitcherOpen = useDropdownStore(
|
const isFooterLanguageSwitcherOpen = useDropdownStore(
|
||||||
(state) => state.isFooterLanguageSwitcherOpen
|
(state) => state.isFooterLanguageSwitcherOpen
|
||||||
)
|
)
|
||||||
@@ -70,10 +72,37 @@ export default function LanguageSwitcher({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleClickOutside(evt: Event) {
|
||||||
|
const target = evt.target as HTMLElement
|
||||||
|
if (
|
||||||
|
languageSwitcherRef.current &&
|
||||||
|
target &&
|
||||||
|
!languageSwitcherRef.current.contains(target) &&
|
||||||
|
isLanguageSwitcherOpen &&
|
||||||
|
!isHeaderLanguageSwitcherMobileOpen
|
||||||
|
) {
|
||||||
|
toggleDropdown(dropdownType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (languageSwitcherRef.current) {
|
||||||
|
document.addEventListener("click", handleClickOutside)
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("click", handleClickOutside)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
dropdownType,
|
||||||
|
toggleDropdown,
|
||||||
|
isLanguageSwitcherOpen,
|
||||||
|
isHeaderLanguageSwitcherMobileOpen,
|
||||||
|
])
|
||||||
|
|
||||||
const classNames = languageSwitcherVariants({ color, position })
|
const classNames = languageSwitcherVariants({ color, position })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames}>
|
<div className={classNames} ref={languageSwitcherRef}>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
@@ -99,7 +128,10 @@ export default function LanguageSwitcher({
|
|||||||
>
|
>
|
||||||
{isLanguageSwitcherOpen ? (
|
{isLanguageSwitcherOpen ? (
|
||||||
<LanguageSwitcherContainer type={type}>
|
<LanguageSwitcherContainer type={type}>
|
||||||
<LanguageSwitcherContent urls={urls} />
|
<LanguageSwitcherContent
|
||||||
|
urls={urls}
|
||||||
|
onLanguageSwitch={() => toggleDropdown(dropdownType)}
|
||||||
|
/>
|
||||||
</LanguageSwitcherContainer>
|
</LanguageSwitcherContainer>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,29 +8,26 @@ import {
|
|||||||
} from "@vis.gl/react-google-maps"
|
} from "@vis.gl/react-google-maps"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import useHotelPageStore from "@/stores/hotel-page"
|
|
||||||
|
|
||||||
import { MinusIcon, PlusIcon } from "@/components/Icons"
|
import { MinusIcon, PlusIcon } from "@/components/Icons"
|
||||||
import CloseLargeIcon from "@/components/Icons/CloseLarge"
|
|
||||||
import PoiMarker from "@/components/Maps/Markers/Poi"
|
import PoiMarker from "@/components/Maps/Markers/Poi"
|
||||||
import ScandicMarker from "@/components/Maps/Markers/Scandic"
|
import ScandicMarker from "@/components/Maps/Markers/Scandic"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
|
||||||
import styles from "./map.module.css"
|
import styles from "./interactiveMap.module.css"
|
||||||
|
|
||||||
import type { MapContentProps } from "@/types/components/hotelPage/map/mapContent"
|
import type { InteractiveMapProps } from "@/types/components/hotelPage/map/interactiveMap"
|
||||||
|
|
||||||
export default function MapContent({
|
export default function InteractiveMap({
|
||||||
coordinates,
|
coordinates,
|
||||||
pointsOfInterest,
|
pointsOfInterest,
|
||||||
activePoi,
|
activePoi,
|
||||||
mapId,
|
mapId,
|
||||||
onActivePoiChange,
|
onActivePoiChange,
|
||||||
}: MapContentProps) {
|
closeButton,
|
||||||
|
}: InteractiveMapProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const { closeDynamicMap } = useHotelPageStore()
|
|
||||||
const map = useMap()
|
const map = useMap()
|
||||||
|
|
||||||
const mapOptions: MapProps = {
|
const mapOptions: MapProps = {
|
||||||
@@ -98,17 +95,7 @@ export default function MapContent({
|
|||||||
))}
|
))}
|
||||||
</Map>
|
</Map>
|
||||||
<div className={styles.ctaButtons}>
|
<div className={styles.ctaButtons}>
|
||||||
<Button
|
{closeButton}
|
||||||
theme="base"
|
|
||||||
intent="inverted"
|
|
||||||
variant="icon"
|
|
||||||
size="small"
|
|
||||||
className={styles.closeButton}
|
|
||||||
onClick={closeDynamicMap}
|
|
||||||
>
|
|
||||||
<CloseLargeIcon color="burgundy" />
|
|
||||||
<span>{intl.formatMessage({ id: "Close the map" })}</span>
|
|
||||||
</Button>
|
|
||||||
<div className={styles.zoomButtons}>
|
<div className={styles.zoomButtons}>
|
||||||
<Button
|
<Button
|
||||||
theme="base"
|
theme="base"
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
.aside {
|
.aside {
|
||||||
display: none;
|
display: grid;
|
||||||
|
container-name: sidebar;
|
||||||
|
container-type: inline-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding: var(--Spacing-x0) var(--Spacing-x2);
|
padding: var(--Spacing-x0) var(--Spacing-x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1366px) {
|
@media screen and (min-width: 1367px) {
|
||||||
.aside {
|
.aside {
|
||||||
align-content: flex-start;
|
align-content: flex-start;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--Spacing-x4);
|
gap: var(--Spacing-x4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@container loyalty-page (max-width: 1366px) {
|
||||||
|
.aside {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
.btn {
|
.btn {
|
||||||
background: none;
|
background: none;
|
||||||
/* No variable yet for radius 50px */
|
border-radius: var(--Corner-radius-Rounded);
|
||||||
border-radius: 50px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -10,12 +9,12 @@
|
|||||||
background-color 300ms ease,
|
background-color 300ms ease,
|
||||||
color 300ms ease;
|
color 300ms ease;
|
||||||
|
|
||||||
/* TODO: Waiting for variables for buttons from Design team */
|
|
||||||
font-family: var(--typography-Body-Bold-fontFamily);
|
font-family: var(--typography-Body-Bold-fontFamily);
|
||||||
font-size: var(--typography-Body-Bold-fontSize);
|
font-size: var(--typography-Body-Bold-fontSize);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 24px;
|
line-height: var(--typography-Body-Bold-lineHeight);
|
||||||
letter-spacing: 0.6%;
|
letter-spacing: 0.6%;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapping {
|
.wrapping {
|
||||||
@@ -23,6 +22,10 @@
|
|||||||
padding-right: 0 !important;
|
padding-right: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fullWidth {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/* INTENT */
|
/* INTENT */
|
||||||
.primary,
|
.primary,
|
||||||
a.primary {
|
a.primary {
|
||||||
@@ -69,21 +72,33 @@ a.default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* SIZES */
|
/* SIZES */
|
||||||
.small {
|
.btn.small {
|
||||||
|
font-size: var(--typography-Caption-Bold-fontSize);
|
||||||
|
line-height: var(--typography-Caption-Bold-lineHeight);
|
||||||
gap: var(--Spacing-x-quarter);
|
gap: var(--Spacing-x-quarter);
|
||||||
height: 40px;
|
padding: calc(var(--Spacing-x1) + 2px) var(--Spacing-x2); /* Special case padding to adjust the missing border */
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.small.secondary {
|
||||||
padding: var(--Spacing-x1) var(--Spacing-x2);
|
padding: var(--Spacing-x1) var(--Spacing-x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium {
|
.btn.medium {
|
||||||
gap: var(--Spacing-x-half);
|
gap: var(--Spacing-x-half);
|
||||||
height: 48px;
|
padding: calc(var(--Spacing-x-one-and-half) + 2px) var(--Spacing-x2); /* Special case padding to adjust the missing border */
|
||||||
|
}
|
||||||
|
|
||||||
|
.medium.secondary {
|
||||||
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
|
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.large {
|
.btn.large {
|
||||||
|
gap: var(--Spacing-x-half);
|
||||||
|
padding: calc(var(--Spacing-x2) + 2px) var(--Spacing-x3); /* Special case padding to adjust the missing border */
|
||||||
|
}
|
||||||
|
|
||||||
|
.large.secondary {
|
||||||
gap: var(--Spacing-x-half);
|
gap: var(--Spacing-x-half);
|
||||||
height: 56px;
|
|
||||||
padding: var(--Spacing-x2) var(--Spacing-x3);
|
padding: var(--Spacing-x2) var(--Spacing-x3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,19 +185,19 @@ a.default {
|
|||||||
fill: var(--Base-Button-Secondary-On-Fill-Disabled);
|
fill: var(--Base-Button-Secondary-On-Fill-Disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
.baseTertiary {
|
.btn.baseTertiary {
|
||||||
background-color: var(--Base-Button-Tertiary-Fill-Normal);
|
background-color: var(--Base-Button-Tertiary-Fill-Normal);
|
||||||
color: var(--Base-Button-Tertiary-On-Fill-Normal);
|
color: var(--Base-Button-Tertiary-On-Fill-Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.baseTertiary:active,
|
.btn.baseTertiary:active,
|
||||||
.baseTertiary:focus,
|
.btn.baseTertiary:focus,
|
||||||
.baseTertiary:hover {
|
.btn.baseTertiary:hover {
|
||||||
background-color: var(--Base-Button-Tertiary-Fill-Hover);
|
background-color: var(--Base-Button-Tertiary-Fill-Hover);
|
||||||
color: var(--Base-Button-Tertiary-On-Fill-Hover);
|
color: var(--Base-Button-Tertiary-On-Fill-Hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.baseTertiary:disabled {
|
.btn.baseTertiary:disabled {
|
||||||
background-color: var(--Base-Button-Tertiary-Fill-Disabled);
|
background-color: var(--Base-Button-Tertiary-Fill-Disabled);
|
||||||
color: var(--Base-Button-Tertiary-On-Fill-Disabled);
|
color: var(--Base-Button-Tertiary-On-Fill-Disabled);
|
||||||
}
|
}
|
||||||
@@ -800,4 +815,4 @@ a.default {
|
|||||||
.icon.tertiaryLightSecondary:disabled svg,
|
.icon.tertiaryLightSecondary:disabled svg,
|
||||||
.icon.tertiaryLightSecondary:disabled svg * {
|
.icon.tertiaryLightSecondary:disabled svg * {
|
||||||
fill: var(--Tertiary-Light-Button-Secondary-On-Fill-Disabled);
|
fill: var(--Tertiary-Light-Button-Secondary-On-Fill-Disabled);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,23 @@ import { buttonVariants } from "./variants"
|
|||||||
import type { ButtonProps } from "./button"
|
import type { ButtonProps } from "./button"
|
||||||
|
|
||||||
export default function Button(props: ButtonProps) {
|
export default function Button(props: ButtonProps) {
|
||||||
const { className, intent, size, theme, wrapping, variant, ...restProps } =
|
const {
|
||||||
props
|
className,
|
||||||
|
intent,
|
||||||
|
size,
|
||||||
|
theme,
|
||||||
|
fullWidth,
|
||||||
|
wrapping,
|
||||||
|
variant,
|
||||||
|
...restProps
|
||||||
|
} = props
|
||||||
|
|
||||||
const classNames = buttonVariants({
|
const classNames = buttonVariants({
|
||||||
className,
|
className,
|
||||||
intent,
|
intent,
|
||||||
size,
|
size,
|
||||||
theme,
|
theme,
|
||||||
|
fullWidth,
|
||||||
wrapping,
|
wrapping,
|
||||||
variant,
|
variant,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ export const buttonVariants = cva(styles.btn, {
|
|||||||
wrapping: {
|
wrapping: {
|
||||||
true: styles.wrapping,
|
true: styles.wrapping,
|
||||||
},
|
},
|
||||||
|
fullWidth: {
|
||||||
|
true: styles.fullWidth,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
intent: "primary",
|
intent: "primary",
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ export interface CardProps
|
|||||||
heading?: string | null
|
heading?: string | null
|
||||||
bodyText?: string | null
|
bodyText?: string | null
|
||||||
backgroundImage?: ImageVaultAsset
|
backgroundImage?: ImageVaultAsset
|
||||||
|
imageHeight?: number
|
||||||
|
imageWidth?: number
|
||||||
onPrimaryButtonClick?: () => void
|
onPrimaryButtonClick?: () => void
|
||||||
onSecondaryButtonClick?: () => void
|
onSecondaryButtonClick?: () => void
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
import Image from "@/components/Image"
|
import Image from "@/components/Image"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import Link from "@/components/TempDesignSystem/Link"
|
|
||||||
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||||
@@ -21,10 +22,19 @@ export default function Card({
|
|||||||
className,
|
className,
|
||||||
theme,
|
theme,
|
||||||
backgroundImage,
|
backgroundImage,
|
||||||
|
imageHeight,
|
||||||
|
imageWidth,
|
||||||
onPrimaryButtonClick,
|
onPrimaryButtonClick,
|
||||||
onSecondaryButtonClick,
|
onSecondaryButtonClick,
|
||||||
}: CardProps) {
|
}: CardProps) {
|
||||||
const { buttonTheme, primaryLinkColor, secondaryLinkColor } = getTheme(theme)
|
const buttonTheme = getTheme(theme)
|
||||||
|
|
||||||
|
imageHeight = imageHeight || 320
|
||||||
|
imageWidth =
|
||||||
|
imageWidth ||
|
||||||
|
(backgroundImage
|
||||||
|
? backgroundImage.dimensions.aspectRatio * imageHeight
|
||||||
|
: 420)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article
|
<article
|
||||||
@@ -39,8 +49,8 @@ export default function Card({
|
|||||||
src={backgroundImage.url}
|
src={backgroundImage.url}
|
||||||
className={styles.image}
|
className={styles.image}
|
||||||
alt={backgroundImage.meta.alt || backgroundImage.title}
|
alt={backgroundImage.meta.alt || backgroundImage.title}
|
||||||
width={backgroundImage.dimensions.width || 420}
|
width={imageWidth}
|
||||||
height={backgroundImage.dimensions.height || 320}
|
height={imageHeight}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -76,7 +86,6 @@ export default function Card({
|
|||||||
<Link
|
<Link
|
||||||
href={primaryButton.href}
|
href={primaryButton.href}
|
||||||
target={primaryButton.openInNewTab ? "_blank" : undefined}
|
target={primaryButton.openInNewTab ? "_blank" : undefined}
|
||||||
color={primaryLinkColor}
|
|
||||||
onClick={onPrimaryButtonClick}
|
onClick={onPrimaryButtonClick}
|
||||||
>
|
>
|
||||||
{primaryButton.title}
|
{primaryButton.title}
|
||||||
@@ -94,7 +103,6 @@ export default function Card({
|
|||||||
<Link
|
<Link
|
||||||
href={secondaryButton.href}
|
href={secondaryButton.href}
|
||||||
target={secondaryButton.openInNewTab ? "_blank" : undefined}
|
target={secondaryButton.openInNewTab ? "_blank" : undefined}
|
||||||
color={secondaryLinkColor}
|
|
||||||
onClick={onSecondaryButtonClick}
|
onClick={onSecondaryButtonClick}
|
||||||
>
|
>
|
||||||
{secondaryButton.title}
|
{secondaryButton.title}
|
||||||
|
|||||||
@@ -1,36 +1,47 @@
|
|||||||
.divider {
|
.divider {
|
||||||
border-bottom-style: solid;
|
pointer-events: none;
|
||||||
border-bottom-width: 1px;
|
}
|
||||||
|
|
||||||
|
.horizontal {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dotted {
|
.vertical {
|
||||||
border-bottom-style: dotted;
|
height: 100%;
|
||||||
|
width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.burgundy {
|
.burgundy {
|
||||||
border-bottom-color: var(--Scandic-Brand-Burgundy);
|
background-color: var(--Scandic-Brand-Burgundy);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pale {
|
.pale {
|
||||||
border-bottom-color: var(--Primary-Dark-On-Surface-Text);
|
background-color: var(--Primary-Dark-On-Surface-Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.peach {
|
.peach {
|
||||||
border-bottom-color: var(--Primary-Light-On-Surface-Divider);
|
background-color: var(--Primary-Light-On-Surface-Divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
.beige {
|
.beige {
|
||||||
border-bottom-color: var(--Scandic-Beige-20);
|
background-color: var(--Scandic-Beige-20);
|
||||||
}
|
}
|
||||||
|
|
||||||
.white {
|
.white {
|
||||||
border-bottom-color: var(--UI-Opacity-White-100);
|
background-color: var(--UI-Opacity-White-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtle {
|
.subtle {
|
||||||
border-bottom-color: var(--Base-Border-Subtle);
|
background-color: var(--Base-Border-Subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.primaryLightSubtle {
|
||||||
|
background-color: var(--Primary-Light-On-Surface-Divider-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.baseSurfaceSubtleNormal {
|
||||||
|
background-color: var(--Base-Surface-Subtle-Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.opacity100 {
|
.opacity100 {
|
||||||
|
|||||||
@@ -5,25 +5,27 @@ import styles from "./divider.module.css"
|
|||||||
export const dividerVariants = cva(styles.divider, {
|
export const dividerVariants = cva(styles.divider, {
|
||||||
variants: {
|
variants: {
|
||||||
color: {
|
color: {
|
||||||
burgundy: styles.burgundy,
|
baseSurfaceSubtleNormal: styles.baseSurfaceSubtleNormal,
|
||||||
peach: styles.peach,
|
|
||||||
beige: styles.beige,
|
beige: styles.beige,
|
||||||
white: styles.white,
|
burgundy: styles.burgundy,
|
||||||
subtle: styles.subtle,
|
|
||||||
pale: styles.pale,
|
pale: styles.pale,
|
||||||
|
peach: styles.peach,
|
||||||
|
primaryLightSubtle: styles.primaryLightSubtle,
|
||||||
|
subtle: styles.subtle,
|
||||||
|
white: styles.white,
|
||||||
},
|
},
|
||||||
opacity: {
|
opacity: {
|
||||||
100: styles.opacity100,
|
100: styles.opacity100,
|
||||||
8: styles.opacity8,
|
8: styles.opacity8,
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
default: styles.default,
|
horizontal: styles.horizontal,
|
||||||
dotted: styles.dotted,
|
vertical: styles.vertical,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
color: "burgundy",
|
color: "burgundy",
|
||||||
opacity: 100,
|
opacity: 100,
|
||||||
variant: "default",
|
variant: "horizontal",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import NextLink from "next/link"
|
import NextLink from "next/link"
|
||||||
import { usePathname, useRouter } from "next/navigation"
|
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
||||||
import { startTransition, useCallback } from "react"
|
import { startTransition, useCallback, useMemo } from "react"
|
||||||
|
|
||||||
import { trackClick } from "@/utils/tracking"
|
import { trackClick } from "@/utils/tracking"
|
||||||
|
|
||||||
@@ -22,9 +22,14 @@ export default function Link({
|
|||||||
variant,
|
variant,
|
||||||
trackingId,
|
trackingId,
|
||||||
onClick,
|
onClick,
|
||||||
|
/**
|
||||||
|
* Decides if the link should include the current search params in the URL
|
||||||
|
*/
|
||||||
|
keepSearchParams,
|
||||||
...props
|
...props
|
||||||
}: LinkProps) {
|
}: LinkProps) {
|
||||||
const currentPageSlug = usePathname()
|
const currentPageSlug = usePathname()
|
||||||
|
const searchParams = useSearchParams()
|
||||||
let isActive = active || currentPageSlug === href
|
let isActive = active || currentPageSlug === href
|
||||||
|
|
||||||
if (partialMatch && !isActive) {
|
if (partialMatch && !isActive) {
|
||||||
@@ -42,6 +47,12 @@ export default function Link({
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
const fullUrl = useMemo(() => {
|
||||||
|
const search =
|
||||||
|
keepSearchParams && searchParams.size ? `?${searchParams}` : ""
|
||||||
|
return `${href}${search}`
|
||||||
|
}, [href, searchParams, keepSearchParams])
|
||||||
|
|
||||||
const trackClickById = useCallback(() => {
|
const trackClickById = useCallback(() => {
|
||||||
if (trackingId) {
|
if (trackingId) {
|
||||||
trackClick(trackingId)
|
trackClick(trackingId)
|
||||||
@@ -65,12 +76,17 @@ export default function Link({
|
|||||||
// track navigation nor start a router transition.
|
// track navigation nor start a router transition.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (href.startsWith("tel:") || href.startsWith("mailto:")) {
|
||||||
|
// If href contains tel or mailto protocols we don't want to
|
||||||
|
// track navigation nor start a router transition.
|
||||||
|
return
|
||||||
|
}
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
router.push(href, { scroll })
|
router.push(fullUrl, { scroll })
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
href={href}
|
href={fullUrl}
|
||||||
id={trackingId}
|
id={trackingId}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ export interface LinkProps
|
|||||||
partialMatch?: boolean
|
partialMatch?: boolean
|
||||||
prefetch?: boolean
|
prefetch?: boolean
|
||||||
trackingId?: string
|
trackingId?: string
|
||||||
|
keepSearchParams?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useIsSSR } from "@react-aria/ssr"
|
import { useIsSSR } from "@react-aria/ssr"
|
||||||
import { useContext } from "react"
|
import { useContext, useState } from "react"
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
@@ -29,6 +29,15 @@ function SidePeek({
|
|||||||
}: React.PropsWithChildren<SidePeekProps>) {
|
}: React.PropsWithChildren<SidePeekProps>) {
|
||||||
const isSSR = useIsSSR()
|
const isSSR = useIsSSR()
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
|
const [rootDiv, setRootDiv] = useState<HTMLDivElement | undefined>(undefined)
|
||||||
|
|
||||||
|
function setRef(node: HTMLDivElement | null) {
|
||||||
|
if (node) {
|
||||||
|
setRootDiv(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const context = useContext(SidePeekContext)
|
const context = useContext(SidePeekContext)
|
||||||
function onClose() {
|
function onClose() {
|
||||||
const closeHandler = handleClose || context?.handleClose
|
const closeHandler = handleClose || context?.handleClose
|
||||||
@@ -44,42 +53,45 @@ function SidePeek({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<DialogTrigger>
|
<div ref={setRef}>
|
||||||
<ModalOverlay
|
<DialogTrigger>
|
||||||
className={styles.overlay}
|
<ModalOverlay
|
||||||
isOpen={isOpen || contentKey === context?.activeSidePeek}
|
UNSTABLE_portalContainer={rootDiv}
|
||||||
onOpenChange={onClose}
|
className={styles.overlay}
|
||||||
isDismissable
|
isOpen={isOpen || contentKey === context?.activeSidePeek}
|
||||||
>
|
onOpenChange={onClose}
|
||||||
<Modal className={styles.modal}>
|
isDismissable
|
||||||
<Dialog className={styles.dialog}>
|
>
|
||||||
<aside className={styles.sidePeek}>
|
<Modal className={styles.modal}>
|
||||||
<header className={styles.header}>
|
<Dialog className={styles.dialog}>
|
||||||
{title ? (
|
<aside className={styles.sidePeek}>
|
||||||
<Title
|
<header className={styles.header}>
|
||||||
color="burgundy"
|
{title ? (
|
||||||
textTransform="uppercase"
|
<Title
|
||||||
level="h2"
|
color="burgundy"
|
||||||
as="h3"
|
textTransform="uppercase"
|
||||||
|
level="h2"
|
||||||
|
as="h3"
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</Title>
|
||||||
|
) : null}
|
||||||
|
<Button
|
||||||
|
aria-label={intl.formatMessage({ id: "Close" })}
|
||||||
|
className={styles.closeButton}
|
||||||
|
intent="text"
|
||||||
|
onPress={onClose}
|
||||||
>
|
>
|
||||||
{title}
|
<CloseIcon color="burgundy" height={32} width={32} />
|
||||||
</Title>
|
</Button>
|
||||||
) : null}
|
</header>
|
||||||
<Button
|
<div className={styles.sidePeekContent}>{children}</div>
|
||||||
aria-label={intl.formatMessage({ id: "Close" })}
|
</aside>
|
||||||
className={styles.closeButton}
|
</Dialog>
|
||||||
intent="text"
|
</Modal>
|
||||||
onPress={onClose}
|
</ModalOverlay>
|
||||||
>
|
</DialogTrigger>
|
||||||
<CloseIcon color="burgundy" height={32} width={32} />
|
</div>
|
||||||
</Button>
|
|
||||||
</header>
|
|
||||||
<div className={styles.sidePeekContent}>{children}</div>
|
|
||||||
</aside>
|
|
||||||
</Dialog>
|
|
||||||
</Modal>
|
|
||||||
</ModalOverlay>
|
|
||||||
</DialogTrigger>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,7 @@
|
|||||||
|
|
||||||
.sidePeekContent {
|
.sidePeekContent {
|
||||||
padding: var(--Spacing-x4);
|
padding: var(--Spacing-x4);
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 1367px) {
|
@media screen and (min-width: 1367px) {
|
||||||
.modal {
|
.modal {
|
||||||
@@ -94,8 +95,4 @@
|
|||||||
.modal[data-exiting] {
|
.modal[data-exiting] {
|
||||||
animation: slide-in 250ms reverse;
|
animation: slide-in 250ms reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,10 @@
|
|||||||
color: var(--UI-Text-Medium-contrast);
|
color: var(--UI-Text-Medium-contrast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.textHighContrast {
|
||||||
|
color: var(--UI-Text-High-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
.white {
|
.white {
|
||||||
color: var(--UI-Opacity-White-100);
|
color: var(--UI-Opacity-White-100);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const config = {
|
|||||||
pale: styles.pale,
|
pale: styles.pale,
|
||||||
red: styles.red,
|
red: styles.red,
|
||||||
textMediumContrast: styles.textMediumContrast,
|
textMediumContrast: styles.textMediumContrast,
|
||||||
|
textHighContrast: styles.textHighContrast,
|
||||||
white: styles.white,
|
white: styles.white,
|
||||||
peach50: styles.peach50,
|
peach50: styles.peach50,
|
||||||
peach80: styles.peach80,
|
peach80: styles.peach80,
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ p.caption {
|
|||||||
color: var(--UI-Text-Active);
|
color: var(--UI-Text-Active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.uiTextMediumContrast {
|
||||||
|
color: var(--UI-Text-Medium-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const config = {
|
|||||||
red: styles.red,
|
red: styles.red,
|
||||||
white: styles.white,
|
white: styles.white,
|
||||||
uiTextActive: styles.uiTextActive,
|
uiTextActive: styles.uiTextActive,
|
||||||
|
uiTextMediumContrast: styles.uiTextMediumContrast,
|
||||||
},
|
},
|
||||||
textTransform: {
|
textTransform: {
|
||||||
bold: styles.bold,
|
bold: styles.bold,
|
||||||
|
|||||||
@@ -8,4 +8,24 @@ export const hotelReservation = {
|
|||||||
de: "/de/hotelreservierung",
|
de: "/de/hotelreservierung",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Translate paths
|
||||||
|
export const selectHotel = {
|
||||||
|
en: `${hotelReservation.en}/select-hotel`,
|
||||||
|
sv: `${hotelReservation.sv}/select-hotel`,
|
||||||
|
no: `${hotelReservation.no}/select-hotel`,
|
||||||
|
fi: `${hotelReservation.fi}/select-hotel`,
|
||||||
|
da: `${hotelReservation.da}/select-hotel`,
|
||||||
|
de: `${hotelReservation.de}/select-hotel`,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Translate paths
|
||||||
|
export const selectHotelMap = {
|
||||||
|
en: `${selectHotel.en}/map`,
|
||||||
|
sv: `${selectHotel.sv}/map`,
|
||||||
|
no: `${selectHotel.no}/map`,
|
||||||
|
fi: `${selectHotel.fi}/map`,
|
||||||
|
da: `${selectHotel.da}/map`,
|
||||||
|
de: `${selectHotel.de}/map`,
|
||||||
|
}
|
||||||
|
|
||||||
export const bookingFlow = [...Object.values(hotelReservation)]
|
export const bookingFlow = [...Object.values(hotelReservation)]
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
"Any changes you've made will be lost.": "Alle ændringer, du har foretaget, går tabt.",
|
"Any changes you've made will be lost.": "Alle ændringer, du har foretaget, går tabt.",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på, at du vil fjerne kortet, der slutter me {lastFourDigits} fra din medlemsprofil?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på, at du vil fjerne kortet, der slutter me {lastFourDigits} fra din medlemsprofil?",
|
||||||
"Arrival date": "Ankomstdato",
|
"Arrival date": "Ankomstdato",
|
||||||
"as of today": "fra idag",
|
|
||||||
"As our": "Som vores {level}",
|
"As our": "Som vores {level}",
|
||||||
"As our Close Friend": "Som vores nære ven",
|
"As our Close Friend": "Som vores nære ven",
|
||||||
"At latest": "Senest",
|
"At latest": "Senest",
|
||||||
@@ -24,16 +23,14 @@
|
|||||||
"Bed type": "Seng type",
|
"Bed type": "Seng type",
|
||||||
"Book": "Book",
|
"Book": "Book",
|
||||||
"Book reward night": "Book bonusnat",
|
"Book reward night": "Book bonusnat",
|
||||||
"Booking codes and vouchers": "Bookingkoder og vouchers",
|
"Code / Voucher": "Bookingkoder / voucher",
|
||||||
"Booking number": "Bookingnummer",
|
"Booking number": "Bookingnummer",
|
||||||
"Breakfast": "Morgenmad",
|
"Breakfast": "Morgenmad",
|
||||||
"Breakfast excluded": "Morgenmad ikke inkluderet",
|
"Breakfast excluded": "Morgenmad ikke inkluderet",
|
||||||
"Breakfast included": "Morgenmad inkluderet",
|
"Breakfast included": "Morgenmad inkluderet",
|
||||||
"Bus terminal": "Busstation",
|
"Bus terminal": "Busstation",
|
||||||
"Business": "Forretning",
|
"Business": "Forretning",
|
||||||
"by": "inden",
|
|
||||||
"Cancel": "Afbestille",
|
"Cancel": "Afbestille",
|
||||||
"characters": "tegn",
|
|
||||||
"Check in": "Check ind",
|
"Check in": "Check ind",
|
||||||
"Check out": "Check ud",
|
"Check out": "Check ud",
|
||||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tjek de kreditkort, der er gemt på din profil. Betal med et gemt kort, når du er logget ind for en mere jævn weboplevelse.",
|
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tjek de kreditkort, der er gemt på din profil. Betal med et gemt kort, når du er logget ind for en mere jævn weboplevelse.",
|
||||||
@@ -75,9 +72,9 @@
|
|||||||
"Explore all levels and benefits": "Udforsk alle niveauer og fordele",
|
"Explore all levels and benefits": "Udforsk alle niveauer og fordele",
|
||||||
"Explore nearby": "Udforsk i nærheden",
|
"Explore nearby": "Udforsk i nærheden",
|
||||||
"Extras to your booking": "Tillæg til din booking",
|
"Extras to your booking": "Tillæg til din booking",
|
||||||
|
"FAQ": "Ofte stillede spørgsmål",
|
||||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kreditkort. Prøv venligst igen senere.",
|
"Failed to delete credit card, please try again later.": "Kunne ikke slette kreditkort. Prøv venligst igen senere.",
|
||||||
"Fair": "Messe",
|
"Fair": "Messe",
|
||||||
"FAQ": "Ofte stillede spørgsmål",
|
|
||||||
"Find booking": "Find booking",
|
"Find booking": "Find booking",
|
||||||
"Find hotels": "Find hotel",
|
"Find hotels": "Find hotel",
|
||||||
"Flexibility": "Fleksibilitet",
|
"Flexibility": "Fleksibilitet",
|
||||||
@@ -94,15 +91,11 @@
|
|||||||
"Hotel": "Hotel",
|
"Hotel": "Hotel",
|
||||||
"Hotel facilities": "Hotel faciliteter",
|
"Hotel facilities": "Hotel faciliteter",
|
||||||
"Hotel surroundings": "Hotel omgivelser",
|
"Hotel surroundings": "Hotel omgivelser",
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
|
||||||
"hotelPages.rooms.roomCard.persons": "personer",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se værelsesdetaljer",
|
|
||||||
"Hotels": "Hoteller",
|
"Hotels": "Hoteller",
|
||||||
"How do you want to sleep?": "Hvordan vil du sove?",
|
"How do you want to sleep?": "Hvordan vil du sove?",
|
||||||
"How it works": "Hvordan det virker",
|
"How it works": "Hvordan det virker",
|
||||||
"Image gallery": "Billedgalleri",
|
"Image gallery": "Billedgalleri",
|
||||||
"Join Scandic Friends": "Tilmeld dig Scandic Friends",
|
"Join Scandic Friends": "Tilmeld dig Scandic Friends",
|
||||||
"km to city center": "km til byens centrum",
|
|
||||||
"Language": "Sprog",
|
"Language": "Sprog",
|
||||||
"Latest searches": "Seneste søgninger",
|
"Latest searches": "Seneste søgninger",
|
||||||
"Level": "Niveau",
|
"Level": "Niveau",
|
||||||
@@ -129,9 +122,9 @@
|
|||||||
"Member price": "Medlemspris",
|
"Member price": "Medlemspris",
|
||||||
"Member price from": "Medlemspris fra",
|
"Member price from": "Medlemspris fra",
|
||||||
"Members": "Medlemmer",
|
"Members": "Medlemmer",
|
||||||
"Membership cards": "Medlemskort",
|
|
||||||
"Membership ID": "Medlems-id",
|
"Membership ID": "Medlems-id",
|
||||||
"Membership ID copied to clipboard": "Medlems-ID kopieret til udklipsholder",
|
"Membership ID copied to clipboard": "Medlems-ID kopieret til udklipsholder",
|
||||||
|
"Membership cards": "Medlemskort",
|
||||||
"Menu": "Menu",
|
"Menu": "Menu",
|
||||||
"Modify": "Ændre",
|
"Modify": "Ændre",
|
||||||
"Month": "Måned",
|
"Month": "Måned",
|
||||||
@@ -146,9 +139,6 @@
|
|||||||
"Nearby companies": "Nærliggende virksomheder",
|
"Nearby companies": "Nærliggende virksomheder",
|
||||||
"New password": "Nyt kodeord",
|
"New password": "Nyt kodeord",
|
||||||
"Next": "Næste",
|
"Next": "Næste",
|
||||||
"next level:": "Næste niveau:",
|
|
||||||
"night": "nat",
|
|
||||||
"nights": "nætter",
|
|
||||||
"Nights needed to level up": "Nætter nødvendige for at komme i niveau",
|
"Nights needed to level up": "Nætter nødvendige for at komme i niveau",
|
||||||
"No content published": "Intet indhold offentliggjort",
|
"No content published": "Intet indhold offentliggjort",
|
||||||
"No matching location found": "Der blev ikke fundet nogen matchende placering",
|
"No matching location found": "Der blev ikke fundet nogen matchende placering",
|
||||||
@@ -159,13 +149,11 @@
|
|||||||
"Non-refundable": "Ikke-refunderbart",
|
"Non-refundable": "Ikke-refunderbart",
|
||||||
"Not found": "Ikke fundet",
|
"Not found": "Ikke fundet",
|
||||||
"Nr night, nr adult": "{nights, number} nat, {adults, number} voksen",
|
"Nr night, nr adult": "{nights, number} nat, {adults, number} voksen",
|
||||||
"number": "nummer",
|
|
||||||
"On your journey": "På din rejse",
|
"On your journey": "På din rejse",
|
||||||
"Open": "Åben",
|
"Open": "Åben",
|
||||||
"Open language menu": "Åbn sprogmenuen",
|
"Open language menu": "Åbn sprogmenuen",
|
||||||
"Open menu": "Åbn menuen",
|
"Open menu": "Åbn menuen",
|
||||||
"Open my pages menu": "Åbn mine sider menuen",
|
"Open my pages menu": "Åbn mine sider menuen",
|
||||||
"or": "eller",
|
|
||||||
"Overview": "Oversigt",
|
"Overview": "Oversigt",
|
||||||
"Parking": "Parkering",
|
"Parking": "Parkering",
|
||||||
"Parking / Garage": "Parkering / Garage",
|
"Parking / Garage": "Parkering / Garage",
|
||||||
@@ -177,7 +165,6 @@
|
|||||||
"Phone is required": "Telefonnummer er påkrævet",
|
"Phone is required": "Telefonnummer er påkrævet",
|
||||||
"Phone number": "Telefonnummer",
|
"Phone number": "Telefonnummer",
|
||||||
"Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer",
|
"Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer",
|
||||||
"points": "Point",
|
|
||||||
"Points": "Point",
|
"Points": "Point",
|
||||||
"Points being calculated": "Point udregnes",
|
"Points being calculated": "Point udregnes",
|
||||||
"Points earned prior to May 1, 2021": "Point optjent inden 1. maj 2021",
|
"Points earned prior to May 1, 2021": "Point optjent inden 1. maj 2021",
|
||||||
@@ -196,10 +183,11 @@
|
|||||||
"Room & Terms": "Værelse & Vilkår",
|
"Room & Terms": "Værelse & Vilkår",
|
||||||
"Room facilities": "Værelsesfaciliteter",
|
"Room facilities": "Værelsesfaciliteter",
|
||||||
"Rooms": "Værelser",
|
"Rooms": "Værelser",
|
||||||
"Rooms & Guests": "Værelser & gæster",
|
"Guests & Rooms": "Gæster & værelser",
|
||||||
"Save": "Gemme",
|
"Save": "Gemme",
|
||||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||||
|
"Search": "Søge",
|
||||||
"See all photos": "Se alle billeder",
|
"See all photos": "Se alle billeder",
|
||||||
"See hotel details": "Se hoteloplysninger",
|
"See hotel details": "Se hoteloplysninger",
|
||||||
"See room details": "Se værelsesdetaljer",
|
"See room details": "Se værelsesdetaljer",
|
||||||
@@ -220,29 +208,25 @@
|
|||||||
"Something went wrong and we couldn't add your card. Please try again later.": "Noget gik galt, og vi kunne ikke tilføje dit kort. Prøv venligst igen senere.",
|
"Something went wrong and we couldn't add your card. Please try again later.": "Noget gik galt, og vi kunne ikke tilføje dit kort. Prøv venligst igen senere.",
|
||||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Noget gik galt, og vi kunne ikke fjerne dit kort. Prøv venligst igen senere.",
|
"Something went wrong and we couldn't remove your card. Please try again later.": "Noget gik galt, og vi kunne ikke fjerne dit kort. Prøv venligst igen senere.",
|
||||||
"Something went wrong!": "Noget gik galt!",
|
"Something went wrong!": "Noget gik galt!",
|
||||||
"special character": "speciel karakter",
|
|
||||||
"spendable points expiring by": "{points} Brugbare point udløber den {date}",
|
|
||||||
"Sports": "Sport",
|
"Sports": "Sport",
|
||||||
"Standard price": "Standardpris",
|
"Standard price": "Standardpris",
|
||||||
"Street": "Gade",
|
"Street": "Gade",
|
||||||
"Successfully updated profile!": "Profilen er opdateret med succes!",
|
"Successfully updated profile!": "Profilen er opdateret med succes!",
|
||||||
"Summary": "Opsummering",
|
"Summary": "Opsummering",
|
||||||
|
"TUI Points": "TUI Points",
|
||||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Fortæl os, hvilke oplysninger og opdateringer du gerne vil modtage, og hvordan, ved at klikke på linket nedenfor.",
|
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Fortæl os, hvilke oplysninger og opdateringer du gerne vil modtage, og hvordan, ved at klikke på linket nedenfor.",
|
||||||
"Thank you": "Tak",
|
"Thank you": "Tak",
|
||||||
"Theatre": "Teater",
|
"Theatre": "Teater",
|
||||||
"There are no transactions to display": "Der er ingen transaktioner at vise",
|
"There are no transactions to display": "Der er ingen transaktioner at vise",
|
||||||
"Things nearby HOTEL_NAME": "Ting i nærheden af {hotelName}",
|
"Things nearby HOTEL_NAME": "Ting i nærheden af {hotelName}",
|
||||||
"to": "til",
|
|
||||||
"Total Points": "Samlet antal point",
|
"Total Points": "Samlet antal point",
|
||||||
"Tourist": "Turist",
|
"Tourist": "Turist",
|
||||||
"Transaction date": "Overførselsdato",
|
"Transaction date": "Overførselsdato",
|
||||||
"Transactions": "Transaktioner",
|
"Transactions": "Transaktioner",
|
||||||
"Transportations": "Transport",
|
"Transportations": "Transport",
|
||||||
"Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)",
|
"Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)",
|
||||||
"TUI Points": "TUI Points",
|
|
||||||
"Type of bed": "Sengtype",
|
"Type of bed": "Sengtype",
|
||||||
"Type of room": "Værelsestype",
|
"Type of room": "Værelsestype",
|
||||||
"uppercase letter": "stort bogstav",
|
|
||||||
"Use bonus cheque": "Brug Bonus Cheque",
|
"Use bonus cheque": "Brug Bonus Cheque",
|
||||||
"User information": "Brugeroplysninger",
|
"User information": "Brugeroplysninger",
|
||||||
"View as list": "Vis som liste",
|
"View as list": "Vis som liste",
|
||||||
@@ -268,9 +252,9 @@
|
|||||||
"You canceled adding a new credit card.": "Du har annulleret tilføjelsen af et nyt kreditkort.",
|
"You canceled adding a new credit card.": "Du har annulleret tilføjelsen af et nyt kreditkort.",
|
||||||
"You have no previous stays.": "Du har ingen tidligere ophold.",
|
"You have no previous stays.": "Du har ingen tidligere ophold.",
|
||||||
"You have no upcoming stays.": "Du har ingen kommende ophold.",
|
"You have no upcoming stays.": "Du har ingen kommende ophold.",
|
||||||
|
"Your Challenges Conquer & Earn!": "Dine udfordringer Overvind og tjen!",
|
||||||
"Your card was successfully removed!": "Dit kort blev fjernet!",
|
"Your card was successfully removed!": "Dit kort blev fjernet!",
|
||||||
"Your card was successfully saved!": "Dit kort blev gemt!",
|
"Your card was successfully saved!": "Dit kort blev gemt!",
|
||||||
"Your Challenges Conquer & Earn!": "Dine udfordringer Overvind og tjen!",
|
|
||||||
"Your current level": "Dit nuværende niveau",
|
"Your current level": "Dit nuværende niveau",
|
||||||
"Your details": "Dine oplysninger",
|
"Your details": "Dine oplysninger",
|
||||||
"Your level": "Dit niveau",
|
"Your level": "Dit niveau",
|
||||||
@@ -278,5 +262,23 @@
|
|||||||
"Zip code": "Postnummer",
|
"Zip code": "Postnummer",
|
||||||
"Zoo": "Zoo",
|
"Zoo": "Zoo",
|
||||||
"Zoom in": "Zoom ind",
|
"Zoom in": "Zoom ind",
|
||||||
"Zoom out": "Zoom ud"
|
"Zoom out": "Zoom ud",
|
||||||
|
"as of today": "pr. dags dato",
|
||||||
|
"booking.nights": "{totalNights, plural, one {# nat} other {# nætter}}",
|
||||||
|
"by": "inden",
|
||||||
|
"characters": "tegn",
|
||||||
|
"hotelPages.rooms.roomCard.person": "person",
|
||||||
|
"hotelPages.rooms.roomCard.persons": "personer",
|
||||||
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Se værelsesdetaljer",
|
||||||
|
"km to city center": "km til byens centrum",
|
||||||
|
"next level:": "Næste niveau:",
|
||||||
|
"night": "nat",
|
||||||
|
"nights": "nætter",
|
||||||
|
"number": "nummer",
|
||||||
|
"or": "eller",
|
||||||
|
"points": "Point",
|
||||||
|
"special character": "speciel karakter",
|
||||||
|
"spendable points expiring by": "{points} Brugbare point udløber den {date}",
|
||||||
|
"to": "til",
|
||||||
|
"uppercase letter": "stort bogstav"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
"Any changes you've made will be lost.": "Alle Änderungen, die Sie vorgenommen haben, gehen verloren.",
|
"Any changes you've made will be lost.": "Alle Änderungen, die Sie vorgenommen haben, gehen verloren.",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Möchten Sie die Karte mit der Endung {lastFourDigits} wirklich aus Ihrem Mitgliedsprofil entfernen?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Möchten Sie die Karte mit der Endung {lastFourDigits} wirklich aus Ihrem Mitgliedsprofil entfernen?",
|
||||||
"Arrival date": "Ankunftsdatum",
|
"Arrival date": "Ankunftsdatum",
|
||||||
"as of today": "Stand heute",
|
|
||||||
"As our": "Als unser {level}",
|
"As our": "Als unser {level}",
|
||||||
"As our Close Friend": "Als unser enger Freund",
|
"As our Close Friend": "Als unser enger Freund",
|
||||||
"At latest": "Spätestens",
|
"At latest": "Spätestens",
|
||||||
@@ -24,16 +23,14 @@
|
|||||||
"Bed type": "Bettentyp",
|
"Bed type": "Bettentyp",
|
||||||
"Book": "Buchen",
|
"Book": "Buchen",
|
||||||
"Book reward night": "Bonusnacht buchen",
|
"Book reward night": "Bonusnacht buchen",
|
||||||
"Booking codes and vouchers": "Buchungscodes und Gutscheine",
|
"Code / Voucher": "Buchungscodes / Gutscheine",
|
||||||
"Booking number": "Buchungsnummer",
|
"Booking number": "Buchungsnummer",
|
||||||
"Breakfast": "Frühstück",
|
"Breakfast": "Frühstück",
|
||||||
"Breakfast excluded": "Frühstück nicht inbegriffen",
|
"Breakfast excluded": "Frühstück nicht inbegriffen",
|
||||||
"Breakfast included": "Frühstück inbegriffen",
|
"Breakfast included": "Frühstück inbegriffen",
|
||||||
"Bus terminal": "Busbahnhof",
|
"Bus terminal": "Busbahnhof",
|
||||||
"Business": "Geschäft",
|
"Business": "Geschäft",
|
||||||
"by": "bis",
|
|
||||||
"Cancel": "Stornieren",
|
"Cancel": "Stornieren",
|
||||||
"characters": "figuren",
|
|
||||||
"Check in": "Einchecken",
|
"Check in": "Einchecken",
|
||||||
"Check out": "Auschecken",
|
"Check out": "Auschecken",
|
||||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sehen Sie sich die in Ihrem Profil gespeicherten Kreditkarten an. Bezahlen Sie mit einer gespeicherten Karte, wenn Sie angemeldet sind, für ein reibungsloseres Web-Erlebnis.",
|
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sehen Sie sich die in Ihrem Profil gespeicherten Kreditkarten an. Bezahlen Sie mit einer gespeicherten Karte, wenn Sie angemeldet sind, für ein reibungsloseres Web-Erlebnis.",
|
||||||
@@ -75,9 +72,9 @@
|
|||||||
"Explore all levels and benefits": "Entdecken Sie alle Levels und Vorteile",
|
"Explore all levels and benefits": "Entdecken Sie alle Levels und Vorteile",
|
||||||
"Explore nearby": "Erkunden Sie die Umgebung",
|
"Explore nearby": "Erkunden Sie die Umgebung",
|
||||||
"Extras to your booking": "Extras zu Ihrer Buchung",
|
"Extras to your booking": "Extras zu Ihrer Buchung",
|
||||||
|
"FAQ": "Häufig gestellte Fragen",
|
||||||
"Failed to delete credit card, please try again later.": "Kreditkarte konnte nicht gelöscht werden. Bitte versuchen Sie es später noch einmal.",
|
"Failed to delete credit card, please try again later.": "Kreditkarte konnte nicht gelöscht werden. Bitte versuchen Sie es später noch einmal.",
|
||||||
"Fair": "Messe",
|
"Fair": "Messe",
|
||||||
"FAQ": "Häufig gestellte Fragen",
|
|
||||||
"Find booking": "Buchung finden",
|
"Find booking": "Buchung finden",
|
||||||
"Find hotels": "Hotels finden",
|
"Find hotels": "Hotels finden",
|
||||||
"Flexibility": "Flexibilität",
|
"Flexibility": "Flexibilität",
|
||||||
@@ -94,15 +91,11 @@
|
|||||||
"Hotel": "Hotel",
|
"Hotel": "Hotel",
|
||||||
"Hotel facilities": "Hotel-Infos",
|
"Hotel facilities": "Hotel-Infos",
|
||||||
"Hotel surroundings": "Umgebung des Hotels",
|
"Hotel surroundings": "Umgebung des Hotels",
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
|
||||||
"hotelPages.rooms.roomCard.persons": "personen",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Zimmerdetails ansehen",
|
|
||||||
"Hotels": "Hotels",
|
"Hotels": "Hotels",
|
||||||
"How do you want to sleep?": "Wie möchtest du schlafen?",
|
"How do you want to sleep?": "Wie möchtest du schlafen?",
|
||||||
"How it works": "Wie es funktioniert",
|
"How it works": "Wie es funktioniert",
|
||||||
"Image gallery": "Bildergalerie",
|
"Image gallery": "Bildergalerie",
|
||||||
"Join Scandic Friends": "Treten Sie Scandic Friends bei",
|
"Join Scandic Friends": "Treten Sie Scandic Friends bei",
|
||||||
"km to city center": "km bis zum Stadtzentrum",
|
|
||||||
"Language": "Sprache",
|
"Language": "Sprache",
|
||||||
"Latest searches": "Letzte Suchanfragen",
|
"Latest searches": "Letzte Suchanfragen",
|
||||||
"Level": "Level",
|
"Level": "Level",
|
||||||
@@ -129,9 +122,9 @@
|
|||||||
"Member price": "Mitgliederpreis",
|
"Member price": "Mitgliederpreis",
|
||||||
"Member price from": "Mitgliederpreis ab",
|
"Member price from": "Mitgliederpreis ab",
|
||||||
"Members": "Mitglieder",
|
"Members": "Mitglieder",
|
||||||
"Membership cards": "Mitgliedskarten",
|
|
||||||
"Membership ID": "Mitglieds-ID",
|
"Membership ID": "Mitglieds-ID",
|
||||||
"Membership ID copied to clipboard": "Mitglieds-ID in die Zwischenablage kopiert",
|
"Membership ID copied to clipboard": "Mitglieds-ID in die Zwischenablage kopiert",
|
||||||
|
"Membership cards": "Mitgliedskarten",
|
||||||
"Menu": "Menu",
|
"Menu": "Menu",
|
||||||
"Modify": "Ändern",
|
"Modify": "Ändern",
|
||||||
"Month": "Monat",
|
"Month": "Monat",
|
||||||
@@ -146,9 +139,6 @@
|
|||||||
"Nearby companies": "Nahe gelegene Unternehmen",
|
"Nearby companies": "Nahe gelegene Unternehmen",
|
||||||
"New password": "Neues Kennwort",
|
"New password": "Neues Kennwort",
|
||||||
"Next": "Nächste",
|
"Next": "Nächste",
|
||||||
"next level:": "Nächstes Level:",
|
|
||||||
"night": "nacht",
|
|
||||||
"nights": "Nächte",
|
|
||||||
"Nights needed to level up": "Nächte, die zum Levelaufstieg benötigt werden",
|
"Nights needed to level up": "Nächte, die zum Levelaufstieg benötigt werden",
|
||||||
"No content published": "Kein Inhalt veröffentlicht",
|
"No content published": "Kein Inhalt veröffentlicht",
|
||||||
"No matching location found": "Kein passender Standort gefunden",
|
"No matching location found": "Kein passender Standort gefunden",
|
||||||
@@ -159,13 +149,11 @@
|
|||||||
"Non-refundable": "Nicht erstattungsfähig",
|
"Non-refundable": "Nicht erstattungsfähig",
|
||||||
"Not found": "Nicht gefunden",
|
"Not found": "Nicht gefunden",
|
||||||
"Nr night, nr adult": "{nights, number} Nacht, {adults, number} Erwachsener",
|
"Nr night, nr adult": "{nights, number} Nacht, {adults, number} Erwachsener",
|
||||||
"number": "nummer",
|
|
||||||
"On your journey": "Auf deiner Reise",
|
"On your journey": "Auf deiner Reise",
|
||||||
"Open": "Offen",
|
"Open": "Offen",
|
||||||
"Open language menu": "Sprachmenü öffnen",
|
"Open language menu": "Sprachmenü öffnen",
|
||||||
"Open menu": "Menü öffnen",
|
"Open menu": "Menü öffnen",
|
||||||
"Open my pages menu": "Meine Seiten Menü öffnen",
|
"Open my pages menu": "Meine Seiten Menü öffnen",
|
||||||
"or": "oder",
|
|
||||||
"Overview": "Übersicht",
|
"Overview": "Übersicht",
|
||||||
"Parking": "Parken",
|
"Parking": "Parken",
|
||||||
"Parking / Garage": "Parken / Garage",
|
"Parking / Garage": "Parken / Garage",
|
||||||
@@ -177,7 +165,6 @@
|
|||||||
"Phone number": "Telefonnummer",
|
"Phone number": "Telefonnummer",
|
||||||
"Please enter a valid phone number": "Bitte geben Sie eine gültige Telefonnummer ein",
|
"Please enter a valid phone number": "Bitte geben Sie eine gültige Telefonnummer ein",
|
||||||
"Points": "Punkte",
|
"Points": "Punkte",
|
||||||
"points": "Punkte",
|
|
||||||
"Points being calculated": "Punkte werden berechnet",
|
"Points being calculated": "Punkte werden berechnet",
|
||||||
"Points earned prior to May 1, 2021": "Zusammengeführte Punkte vor dem 1. Mai 2021",
|
"Points earned prior to May 1, 2021": "Zusammengeführte Punkte vor dem 1. Mai 2021",
|
||||||
"Points may take up to 10 days to be displayed.": "Es kann bis zu 10 Tage dauern, bis Punkte angezeigt werden.",
|
"Points may take up to 10 days to be displayed.": "Es kann bis zu 10 Tage dauern, bis Punkte angezeigt werden.",
|
||||||
@@ -195,10 +182,11 @@
|
|||||||
"Room & Terms": "Zimmer & Bedingungen",
|
"Room & Terms": "Zimmer & Bedingungen",
|
||||||
"Room facilities": "Zimmerausstattung",
|
"Room facilities": "Zimmerausstattung",
|
||||||
"Rooms": "Räume",
|
"Rooms": "Räume",
|
||||||
"Rooms & Guests": "Zimmer & Gäste",
|
"Guests & Rooms": "Gäste & Zimmer",
|
||||||
"Save": "Speichern",
|
"Save": "Speichern",
|
||||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||||
|
"Search": "Suchen",
|
||||||
"See all photos": "Alle Fotos ansehen",
|
"See all photos": "Alle Fotos ansehen",
|
||||||
"See hotel details": "Hotelinformationen ansehen",
|
"See hotel details": "Hotelinformationen ansehen",
|
||||||
"See room details": "Zimmerdetails ansehen",
|
"See room details": "Zimmerdetails ansehen",
|
||||||
@@ -219,29 +207,25 @@
|
|||||||
"Something went wrong and we couldn't add your card. Please try again later.": "Ein Fehler ist aufgetreten und wir konnten Ihre Karte nicht hinzufügen. Bitte versuchen Sie es später erneut.",
|
"Something went wrong and we couldn't add your card. Please try again later.": "Ein Fehler ist aufgetreten und wir konnten Ihre Karte nicht hinzufügen. Bitte versuchen Sie es später erneut.",
|
||||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Ein Fehler ist aufgetreten und wir konnten Ihre Karte nicht entfernen. Bitte versuchen Sie es später noch einmal.",
|
"Something went wrong and we couldn't remove your card. Please try again later.": "Ein Fehler ist aufgetreten und wir konnten Ihre Karte nicht entfernen. Bitte versuchen Sie es später noch einmal.",
|
||||||
"Something went wrong!": "Etwas ist schief gelaufen!",
|
"Something went wrong!": "Etwas ist schief gelaufen!",
|
||||||
"special character": "sonderzeichen",
|
|
||||||
"spendable points expiring by": "{points} Einlösbare punkte verfallen bis zum {date}",
|
|
||||||
"Sports": "Sport",
|
"Sports": "Sport",
|
||||||
"Standard price": "Standardpreis",
|
"Standard price": "Standardpreis",
|
||||||
"Street": "Straße",
|
"Street": "Straße",
|
||||||
"Successfully updated profile!": "Profil erfolgreich aktualisiert!",
|
"Successfully updated profile!": "Profil erfolgreich aktualisiert!",
|
||||||
"Summary": "Zusammenfassung",
|
"Summary": "Zusammenfassung",
|
||||||
|
"TUI Points": "TUI Points",
|
||||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Teilen Sie uns mit, welche Informationen und Updates Sie wie erhalten möchten, indem Sie auf den unten stehenden Link klicken.",
|
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Teilen Sie uns mit, welche Informationen und Updates Sie wie erhalten möchten, indem Sie auf den unten stehenden Link klicken.",
|
||||||
"Thank you": "Danke",
|
"Thank you": "Danke",
|
||||||
"Theatre": "Theater",
|
"Theatre": "Theater",
|
||||||
"There are no transactions to display": "Es sind keine Transaktionen zum Anzeigen vorhanden",
|
"There are no transactions to display": "Es sind keine Transaktionen zum Anzeigen vorhanden",
|
||||||
"Things nearby HOTEL_NAME": "Dinge in der Nähe von {hotelName}",
|
"Things nearby HOTEL_NAME": "Dinge in der Nähe von {hotelName}",
|
||||||
"to": "zu",
|
|
||||||
"Total Points": "Gesamtpunktzahl",
|
"Total Points": "Gesamtpunktzahl",
|
||||||
"Tourist": "Tourist",
|
"Tourist": "Tourist",
|
||||||
"Transaction date": "Transaktionsdatum",
|
"Transaction date": "Transaktionsdatum",
|
||||||
"Transactions": "Transaktionen",
|
"Transactions": "Transaktionen",
|
||||||
"Transportations": "Transportmittel",
|
"Transportations": "Transportmittel",
|
||||||
"Tripadvisor reviews": "{rating} ({count} Bewertungen auf Tripadvisor)",
|
"Tripadvisor reviews": "{rating} ({count} Bewertungen auf Tripadvisor)",
|
||||||
"TUI Points": "TUI Points",
|
|
||||||
"Type of bed": "Bettentyp",
|
"Type of bed": "Bettentyp",
|
||||||
"Type of room": "Zimmerart",
|
"Type of room": "Zimmerart",
|
||||||
"uppercase letter": "großbuchstabe",
|
|
||||||
"Use bonus cheque": "Bonusscheck nutzen",
|
"Use bonus cheque": "Bonusscheck nutzen",
|
||||||
"User information": "Nutzerinformation",
|
"User information": "Nutzerinformation",
|
||||||
"View as list": "Als Liste anzeigen",
|
"View as list": "Als Liste anzeigen",
|
||||||
@@ -267,9 +251,9 @@
|
|||||||
"You canceled adding a new credit card.": "Sie haben das Hinzufügen einer neuen Kreditkarte abgebrochen.",
|
"You canceled adding a new credit card.": "Sie haben das Hinzufügen einer neuen Kreditkarte abgebrochen.",
|
||||||
"You have no previous stays.": "Sie haben keine vorherigen Aufenthalte.",
|
"You have no previous stays.": "Sie haben keine vorherigen Aufenthalte.",
|
||||||
"You have no upcoming stays.": "Sie haben keine bevorstehenden Aufenthalte.",
|
"You have no upcoming stays.": "Sie haben keine bevorstehenden Aufenthalte.",
|
||||||
|
"Your Challenges Conquer & Earn!": "Meistern Sie Ihre Herausforderungen und verdienen Sie Geld!",
|
||||||
"Your card was successfully removed!": "Ihre Karte wurde erfolgreich entfernt!",
|
"Your card was successfully removed!": "Ihre Karte wurde erfolgreich entfernt!",
|
||||||
"Your card was successfully saved!": "Ihre Karte wurde erfolgreich gespeichert!",
|
"Your card was successfully saved!": "Ihre Karte wurde erfolgreich gespeichert!",
|
||||||
"Your Challenges Conquer & Earn!": "Meistern Sie Ihre Herausforderungen und verdienen Sie Geld!",
|
|
||||||
"Your current level": "Ihr aktuelles Level",
|
"Your current level": "Ihr aktuelles Level",
|
||||||
"Your details": "Ihre Angaben",
|
"Your details": "Ihre Angaben",
|
||||||
"Your level": "Dein level",
|
"Your level": "Dein level",
|
||||||
@@ -277,5 +261,23 @@
|
|||||||
"Zip code": "PLZ",
|
"Zip code": "PLZ",
|
||||||
"Zoo": "Zoo",
|
"Zoo": "Zoo",
|
||||||
"Zoom in": "Vergrößern",
|
"Zoom in": "Vergrößern",
|
||||||
"Zoom out": "Verkleinern"
|
"Zoom out": "Verkleinern",
|
||||||
|
"as of today": "Stand heute",
|
||||||
|
"booking.nights": "{totalNights, plural, one {# nacht} other {# Nächte}}",
|
||||||
|
"by": "bis",
|
||||||
|
"characters": "figuren",
|
||||||
|
"hotelPages.rooms.roomCard.person": "person",
|
||||||
|
"hotelPages.rooms.roomCard.persons": "personen",
|
||||||
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Zimmerdetails ansehen",
|
||||||
|
"km to city center": "km bis zum Stadtzentrum",
|
||||||
|
"next level:": "Nächstes Level:",
|
||||||
|
"night": "nacht",
|
||||||
|
"nights": "Nächte",
|
||||||
|
"number": "nummer",
|
||||||
|
"or": "oder",
|
||||||
|
"points": "Punkte",
|
||||||
|
"special character": "sonderzeichen",
|
||||||
|
"spendable points expiring by": "{points} Einlösbare punkte verfallen bis zum {date}",
|
||||||
|
"to": "zu",
|
||||||
|
"uppercase letter": "großbuchstabe"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
"Any changes you've made will be lost.": "Any changes you've made will be lost.",
|
"Any changes you've made will be lost.": "Any changes you've made will be lost.",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?",
|
||||||
"Arrival date": "Arrival date",
|
"Arrival date": "Arrival date",
|
||||||
"as of today": "as of today",
|
|
||||||
"As our": "As our {level}",
|
"As our": "As our {level}",
|
||||||
"As our Close Friend": "As our Close Friend",
|
"As our Close Friend": "As our Close Friend",
|
||||||
"At latest": "At latest",
|
"At latest": "At latest",
|
||||||
@@ -24,16 +23,14 @@
|
|||||||
"Bed type": "Bed type",
|
"Bed type": "Bed type",
|
||||||
"Book": "Book",
|
"Book": "Book",
|
||||||
"Book reward night": "Book reward night",
|
"Book reward night": "Book reward night",
|
||||||
"Booking codes and vouchers": "Booking codes and vouchers",
|
"Code / Voucher": "Code / Voucher",
|
||||||
"Booking number": "Booking number",
|
"Booking number": "Booking number",
|
||||||
"Breakfast": "Breakfast",
|
"Breakfast": "Breakfast",
|
||||||
"Breakfast excluded": "Breakfast excluded",
|
"Breakfast excluded": "Breakfast excluded",
|
||||||
"Breakfast included": "Breakfast included",
|
"Breakfast included": "Breakfast included",
|
||||||
"Bus terminal": "Bus terminal",
|
"Bus terminal": "Bus terminal",
|
||||||
"Business": "Business",
|
"Business": "Business",
|
||||||
"by": "by",
|
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"characters": "characters",
|
|
||||||
"Check in": "Check in",
|
"Check in": "Check in",
|
||||||
"Check out": "Check out",
|
"Check out": "Check out",
|
||||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.",
|
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.",
|
||||||
@@ -63,6 +60,7 @@
|
|||||||
"Day": "Day",
|
"Day": "Day",
|
||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
"Destinations & hotels": "Destinations & hotels",
|
"Destinations & hotels": "Destinations & hotels",
|
||||||
|
"Destination": "Destination",
|
||||||
"Discard changes": "Discard changes",
|
"Discard changes": "Discard changes",
|
||||||
"Discard unsaved changes?": "Discard unsaved changes?",
|
"Discard unsaved changes?": "Discard unsaved changes?",
|
||||||
"Distance to city centre": "{number}km to city centre",
|
"Distance to city centre": "{number}km to city centre",
|
||||||
@@ -75,9 +73,9 @@
|
|||||||
"Explore all levels and benefits": "Explore all levels and benefits",
|
"Explore all levels and benefits": "Explore all levels and benefits",
|
||||||
"Explore nearby": "Explore nearby",
|
"Explore nearby": "Explore nearby",
|
||||||
"Extras to your booking": "Extras to your booking",
|
"Extras to your booking": "Extras to your booking",
|
||||||
|
"FAQ": "FAQ",
|
||||||
"Failed to delete credit card, please try again later.": "Failed to delete credit card, please try again later.",
|
"Failed to delete credit card, please try again later.": "Failed to delete credit card, please try again later.",
|
||||||
"Fair": "Fair",
|
"Fair": "Fair",
|
||||||
"FAQ": "FAQ",
|
|
||||||
"Find booking": "Find booking",
|
"Find booking": "Find booking",
|
||||||
"Find hotels": "Find hotels",
|
"Find hotels": "Find hotels",
|
||||||
"Flexibility": "Flexibility",
|
"Flexibility": "Flexibility",
|
||||||
@@ -94,15 +92,11 @@
|
|||||||
"Hotel": "Hotel",
|
"Hotel": "Hotel",
|
||||||
"Hotel facilities": "Hotel facilities",
|
"Hotel facilities": "Hotel facilities",
|
||||||
"Hotel surroundings": "Hotel surroundings",
|
"Hotel surroundings": "Hotel surroundings",
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
|
||||||
"hotelPages.rooms.roomCard.persons": "persons",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
|
|
||||||
"Hotels": "Hotels",
|
"Hotels": "Hotels",
|
||||||
"How do you want to sleep?": "How do you want to sleep?",
|
"How do you want to sleep?": "How do you want to sleep?",
|
||||||
"How it works": "How it works",
|
"How it works": "How it works",
|
||||||
"Image gallery": "Image gallery",
|
"Image gallery": "Image gallery",
|
||||||
"Join Scandic Friends": "Join Scandic Friends",
|
"Join Scandic Friends": "Join Scandic Friends",
|
||||||
"km to city center": "km to city center",
|
|
||||||
"Language": "Language",
|
"Language": "Language",
|
||||||
"Latest searches": "Latest searches",
|
"Latest searches": "Latest searches",
|
||||||
"Level": "Level",
|
"Level": "Level",
|
||||||
@@ -129,9 +123,9 @@
|
|||||||
"Member price": "Member price",
|
"Member price": "Member price",
|
||||||
"Member price from": "Member price from",
|
"Member price from": "Member price from",
|
||||||
"Members": "Members",
|
"Members": "Members",
|
||||||
"Membership cards": "Membership cards",
|
|
||||||
"Membership ID": "Membership ID",
|
"Membership ID": "Membership ID",
|
||||||
"Membership ID copied to clipboard": "Membership ID copied to clipboard",
|
"Membership ID copied to clipboard": "Membership ID copied to clipboard",
|
||||||
|
"Membership cards": "Membership cards",
|
||||||
"Menu": "Menu",
|
"Menu": "Menu",
|
||||||
"Modify": "Modify",
|
"Modify": "Modify",
|
||||||
"Month": "Month",
|
"Month": "Month",
|
||||||
@@ -146,9 +140,6 @@
|
|||||||
"Nearby companies": "Nearby companies",
|
"Nearby companies": "Nearby companies",
|
||||||
"New password": "New password",
|
"New password": "New password",
|
||||||
"Next": "Next",
|
"Next": "Next",
|
||||||
"next level:": "next level:",
|
|
||||||
"night": "night",
|
|
||||||
"nights": "nights",
|
|
||||||
"Nights needed to level up": "Nights needed to level up",
|
"Nights needed to level up": "Nights needed to level up",
|
||||||
"No content published": "No content published",
|
"No content published": "No content published",
|
||||||
"No matching location found": "No matching location found",
|
"No matching location found": "No matching location found",
|
||||||
@@ -159,13 +150,11 @@
|
|||||||
"Non-refundable": "Non-refundable",
|
"Non-refundable": "Non-refundable",
|
||||||
"Not found": "Not found",
|
"Not found": "Not found",
|
||||||
"Nr night, nr adult": "{nights, number} night, {adults, number} adult",
|
"Nr night, nr adult": "{nights, number} night, {adults, number} adult",
|
||||||
"number": "number",
|
|
||||||
"On your journey": "On your journey",
|
"On your journey": "On your journey",
|
||||||
"Open": "Open",
|
"Open": "Open",
|
||||||
"Open language menu": "Open language menu",
|
"Open language menu": "Open language menu",
|
||||||
"Open menu": "Open menu",
|
"Open menu": "Open menu",
|
||||||
"Open my pages menu": "Open my pages menu",
|
"Open my pages menu": "Open my pages menu",
|
||||||
"or": "or",
|
|
||||||
"Overview": "Overview",
|
"Overview": "Overview",
|
||||||
"Parking": "Parking",
|
"Parking": "Parking",
|
||||||
"Parking / Garage": "Parking / Garage",
|
"Parking / Garage": "Parking / Garage",
|
||||||
@@ -178,7 +167,6 @@
|
|||||||
"Phone number": "Phone number",
|
"Phone number": "Phone number",
|
||||||
"Please enter a valid phone number": "Please enter a valid phone number",
|
"Please enter a valid phone number": "Please enter a valid phone number",
|
||||||
"Points": "Points",
|
"Points": "Points",
|
||||||
"points": "Points",
|
|
||||||
"Points being calculated": "Points being calculated",
|
"Points being calculated": "Points being calculated",
|
||||||
"Points earned prior to May 1, 2021": "Points earned prior to May 1, 2021",
|
"Points earned prior to May 1, 2021": "Points earned prior to May 1, 2021",
|
||||||
"Points may take up to 10 days to be displayed.": "Points may take up to 10 days to be displayed.",
|
"Points may take up to 10 days to be displayed.": "Points may take up to 10 days to be displayed.",
|
||||||
@@ -196,17 +184,19 @@
|
|||||||
"Room & Terms": "Room & Terms",
|
"Room & Terms": "Room & Terms",
|
||||||
"Room facilities": "Room facilities",
|
"Room facilities": "Room facilities",
|
||||||
"Rooms": "Rooms",
|
"Rooms": "Rooms",
|
||||||
"Rooms & Guests": "Rooms & Guests",
|
"Guests & Rooms": "Guests & Rooms",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||||
"See all photos": "See all photos",
|
"See all photos": "See all photos",
|
||||||
|
"Search": "Search",
|
||||||
"See hotel details": "See hotel details",
|
"See hotel details": "See hotel details",
|
||||||
"See room details": "See room details",
|
"See room details": "See room details",
|
||||||
"See rooms": "See rooms",
|
"See rooms": "See rooms",
|
||||||
"Select a country": "Select a country",
|
"Select a country": "Select a country",
|
||||||
"Select country of residence": "Select country of residence",
|
"Select country of residence": "Select country of residence",
|
||||||
"Select date of birth": "Select date of birth",
|
"Select date of birth": "Select date of birth",
|
||||||
|
"Select dates": "Select dates",
|
||||||
"Select language": "Select language",
|
"Select language": "Select language",
|
||||||
"Select your language": "Select your language",
|
"Select your language": "Select your language",
|
||||||
"Shopping": "Shopping",
|
"Shopping": "Shopping",
|
||||||
@@ -220,29 +210,25 @@
|
|||||||
"Something went wrong and we couldn't add your card. Please try again later.": "Something went wrong and we couldn't add your card. Please try again later.",
|
"Something went wrong and we couldn't add your card. Please try again later.": "Something went wrong and we couldn't add your card. Please try again later.",
|
||||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Something went wrong and we couldn't remove your card. Please try again later.",
|
"Something went wrong and we couldn't remove your card. Please try again later.": "Something went wrong and we couldn't remove your card. Please try again later.",
|
||||||
"Something went wrong!": "Something went wrong!",
|
"Something went wrong!": "Something went wrong!",
|
||||||
"special character": "special character",
|
|
||||||
"spendable points expiring by": "{points} spendable points expiring by {date}",
|
|
||||||
"Sports": "Sports",
|
"Sports": "Sports",
|
||||||
"Standard price": "Standard price",
|
"Standard price": "Standard price",
|
||||||
"Street": "Street",
|
"Street": "Street",
|
||||||
"Successfully updated profile!": "Successfully updated profile!",
|
"Successfully updated profile!": "Successfully updated profile!",
|
||||||
"Summary": "Summary",
|
"Summary": "Summary",
|
||||||
|
"TUI Points": "TUI Points",
|
||||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Tell us what information and updates you'd like to receive, and how, by clicking the link below.",
|
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Tell us what information and updates you'd like to receive, and how, by clicking the link below.",
|
||||||
"Thank you": "Thank you",
|
"Thank you": "Thank you",
|
||||||
"Theatre": "Theatre",
|
"Theatre": "Theatre",
|
||||||
"There are no transactions to display": "There are no transactions to display",
|
"There are no transactions to display": "There are no transactions to display",
|
||||||
"Things nearby HOTEL_NAME": "Things nearby {hotelName}",
|
"Things nearby HOTEL_NAME": "Things nearby {hotelName}",
|
||||||
"to": "to",
|
|
||||||
"Total Points": "Total Points",
|
"Total Points": "Total Points",
|
||||||
"Tourist": "Tourist",
|
"Tourist": "Tourist",
|
||||||
"Transaction date": "Transaction date",
|
"Transaction date": "Transaction date",
|
||||||
"Transactions": "Transactions",
|
"Transactions": "Transactions",
|
||||||
"Transportations": "Transportations",
|
"Transportations": "Transportations",
|
||||||
"Tripadvisor reviews": "{rating} ({count} reviews on Tripadvisor)",
|
"Tripadvisor reviews": "{rating} ({count} reviews on Tripadvisor)",
|
||||||
"TUI Points": "TUI Points",
|
|
||||||
"Type of bed": "Type of bed",
|
"Type of bed": "Type of bed",
|
||||||
"Type of room": "Type of room",
|
"Type of room": "Type of room",
|
||||||
"uppercase letter": "uppercase letter",
|
|
||||||
"Use bonus cheque": "Use bonus cheque",
|
"Use bonus cheque": "Use bonus cheque",
|
||||||
"User information": "User information",
|
"User information": "User information",
|
||||||
"View as list": "View as list",
|
"View as list": "View as list",
|
||||||
@@ -268,9 +254,9 @@
|
|||||||
"You canceled adding a new credit card.": "You canceled adding a new credit card.",
|
"You canceled adding a new credit card.": "You canceled adding a new credit card.",
|
||||||
"You have no previous stays.": "You have no previous stays.",
|
"You have no previous stays.": "You have no previous stays.",
|
||||||
"You have no upcoming stays.": "You have no upcoming stays.",
|
"You have no upcoming stays.": "You have no upcoming stays.",
|
||||||
|
"Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!",
|
||||||
"Your card was successfully removed!": "Your card was successfully removed!",
|
"Your card was successfully removed!": "Your card was successfully removed!",
|
||||||
"Your card was successfully saved!": "Your card was successfully saved!",
|
"Your card was successfully saved!": "Your card was successfully saved!",
|
||||||
"Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!",
|
|
||||||
"Your current level": "Your current level",
|
"Your current level": "Your current level",
|
||||||
"Your details": "Your details",
|
"Your details": "Your details",
|
||||||
"Your level": "Your level",
|
"Your level": "Your level",
|
||||||
@@ -278,5 +264,25 @@
|
|||||||
"Zip code": "Zip code",
|
"Zip code": "Zip code",
|
||||||
"Zoo": "Zoo",
|
"Zoo": "Zoo",
|
||||||
"Zoom in": "Zoom in",
|
"Zoom in": "Zoom in",
|
||||||
"Zoom out": "Zoom out"
|
"Zoom out": "Zoom out",
|
||||||
|
"as of today": "as of today",
|
||||||
|
"booking.adults": "{totalAdults, plural, one {# adult} other {# adults}}",
|
||||||
|
"booking.nights": "{totalNights, plural, one {# night} other {# nights}}",
|
||||||
|
"booking.rooms": "{totalRooms, plural, one {# room} other {# rooms}}",
|
||||||
|
"by": "by",
|
||||||
|
"characters": "characters",
|
||||||
|
"hotelPages.rooms.roomCard.person": "person",
|
||||||
|
"hotelPages.rooms.roomCard.persons": "persons",
|
||||||
|
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
|
||||||
|
"km to city center": "km to city center",
|
||||||
|
"next level:": "next level:",
|
||||||
|
"night": "night",
|
||||||
|
"nights": "nights",
|
||||||
|
"number": "number",
|
||||||
|
"or": "or",
|
||||||
|
"points": "Points",
|
||||||
|
"special character": "special character",
|
||||||
|
"spendable points expiring by": "{points} spendable points expiring by {date}",
|
||||||
|
"to": "to",
|
||||||
|
"uppercase letter": "uppercase letter"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
"Any changes you've made will be lost.": "Kaikki tekemäsi muutokset menetetään.",
|
"Any changes you've made will be lost.": "Kaikki tekemäsi muutokset menetetään.",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Haluatko varmasti poistaa kortin, joka päättyy numeroon {lastFourDigits} jäsenprofiilistasi?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Haluatko varmasti poistaa kortin, joka päättyy numeroon {lastFourDigits} jäsenprofiilistasi?",
|
||||||
"Arrival date": "Saapumispäivä",
|
"Arrival date": "Saapumispäivä",
|
||||||
"as of today": "tänään",
|
|
||||||
"As our": "{level}-etu",
|
"As our": "{level}-etu",
|
||||||
"As our Close Friend": "Läheisenä ystävänämme",
|
"As our Close Friend": "Läheisenä ystävänämme",
|
||||||
"At latest": "Viimeistään",
|
"At latest": "Viimeistään",
|
||||||
@@ -24,16 +23,13 @@
|
|||||||
"Bed type": "Vuodetyyppi",
|
"Bed type": "Vuodetyyppi",
|
||||||
"Book": "Varaa",
|
"Book": "Varaa",
|
||||||
"Book reward night": "Kirjapalkinto-ilta",
|
"Book reward night": "Kirjapalkinto-ilta",
|
||||||
"Booking codes and vouchers": "Varauskoodit ja kupongit",
|
|
||||||
"Booking number": "Varausnumero",
|
"Booking number": "Varausnumero",
|
||||||
"Breakfast": "Aamiainen",
|
"Breakfast": "Aamiainen",
|
||||||
"Breakfast excluded": "Aamiainen ei sisälly",
|
"Breakfast excluded": "Aamiainen ei sisälly",
|
||||||
"Breakfast included": "Aamiainen sisältyy",
|
"Breakfast included": "Aamiainen sisältyy",
|
||||||
"Bus terminal": "Bussiasema",
|
"Bus terminal": "Bussiasema",
|
||||||
"Business": "Business",
|
"Business": "Business",
|
||||||
"by": "mennessä",
|
|
||||||
"Cancel": "Peruuttaa",
|
"Cancel": "Peruuttaa",
|
||||||
"characters": "hahmoja",
|
|
||||||
"Check in": "Sisäänkirjautuminen",
|
"Check in": "Sisäänkirjautuminen",
|
||||||
"Check out": "Uloskirjautuminen",
|
"Check out": "Uloskirjautuminen",
|
||||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tarkista profiiliisi tallennetut luottokortit. Maksa tallennetulla kortilla kirjautuneena, jotta verkkokokemus on sujuvampi.",
|
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tarkista profiiliisi tallennetut luottokortit. Maksa tallennetulla kortilla kirjautuneena, jotta verkkokokemus on sujuvampi.",
|
||||||
@@ -48,6 +44,7 @@
|
|||||||
"Close menu": "Sulje valikko",
|
"Close menu": "Sulje valikko",
|
||||||
"Close my pages menu": "Sulje omat sivut -valikko",
|
"Close my pages menu": "Sulje omat sivut -valikko",
|
||||||
"Close the map": "Sulje kartta",
|
"Close the map": "Sulje kartta",
|
||||||
|
"Code / Voucher": "Varauskoodit / kupongit",
|
||||||
"Coming up": "Tulossa",
|
"Coming up": "Tulossa",
|
||||||
"Compare all levels": "Vertaa kaikkia tasoja",
|
"Compare all levels": "Vertaa kaikkia tasoja",
|
||||||
"Contact us": "Ota meihin yhteyttä",
|
"Contact us": "Ota meihin yhteyttä",
|
||||||
@@ -75,9 +72,9 @@
|
|||||||
"Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin",
|
"Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin",
|
||||||
"Explore nearby": "Tutustu lähialueeseen",
|
"Explore nearby": "Tutustu lähialueeseen",
|
||||||
"Extras to your booking": "Varauksessa lisäpalveluita",
|
"Extras to your booking": "Varauksessa lisäpalveluita",
|
||||||
|
"FAQ": "UKK",
|
||||||
"Failed to delete credit card, please try again later.": "Luottokortin poistaminen epäonnistui, yritä myöhemmin uudelleen.",
|
"Failed to delete credit card, please try again later.": "Luottokortin poistaminen epäonnistui, yritä myöhemmin uudelleen.",
|
||||||
"Fair": "Messukeskus",
|
"Fair": "Messukeskus",
|
||||||
"FAQ": "UKK",
|
|
||||||
"Find booking": "Etsi varaus",
|
"Find booking": "Etsi varaus",
|
||||||
"Find hotels": "Etsi hotelleja",
|
"Find hotels": "Etsi hotelleja",
|
||||||
"Flexibility": "Joustavuus",
|
"Flexibility": "Joustavuus",
|
||||||
@@ -88,21 +85,18 @@
|
|||||||
"Get inspired": "Inspiroidu",
|
"Get inspired": "Inspiroidu",
|
||||||
"Go back to edit": "Palaa muokkaamaan",
|
"Go back to edit": "Palaa muokkaamaan",
|
||||||
"Go back to overview": "Palaa yleiskatsaukseen",
|
"Go back to overview": "Palaa yleiskatsaukseen",
|
||||||
|
"Guests & Rooms": "Vieraat & Huoneet",
|
||||||
"Hi": "Hi",
|
"Hi": "Hi",
|
||||||
"Highest level": "Korkein taso",
|
"Highest level": "Korkein taso",
|
||||||
"Hospital": "Sairaala",
|
"Hospital": "Sairaala",
|
||||||
"Hotel": "Hotelli",
|
"Hotel": "Hotelli",
|
||||||
"Hotel facilities": "Hotellin palvelut",
|
"Hotel facilities": "Hotellin palvelut",
|
||||||
"Hotel surroundings": "Hotellin ympäristö",
|
"Hotel surroundings": "Hotellin ympäristö",
|
||||||
"hotelPages.rooms.roomCard.person": "henkilö",
|
|
||||||
"hotelPages.rooms.roomCard.persons": "Henkilöä",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Katso huoneen tiedot",
|
|
||||||
"Hotels": "Hotellit",
|
"Hotels": "Hotellit",
|
||||||
"How do you want to sleep?": "Kuinka haluat nukkua?",
|
"How do you want to sleep?": "Kuinka haluat nukkua?",
|
||||||
"How it works": "Kuinka se toimii",
|
"How it works": "Kuinka se toimii",
|
||||||
"Image gallery": "Kuvagalleria",
|
"Image gallery": "Kuvagalleria",
|
||||||
"Join Scandic Friends": "Liity jäseneksi",
|
"Join Scandic Friends": "Liity jäseneksi",
|
||||||
"km to city center": "km keskustaan",
|
|
||||||
"Language": "Kieli",
|
"Language": "Kieli",
|
||||||
"Latest searches": "Viimeisimmät haut",
|
"Latest searches": "Viimeisimmät haut",
|
||||||
"Level": "Level",
|
"Level": "Level",
|
||||||
@@ -129,9 +123,9 @@
|
|||||||
"Member price": "Jäsenhinta",
|
"Member price": "Jäsenhinta",
|
||||||
"Member price from": "Jäsenhinta alkaen",
|
"Member price from": "Jäsenhinta alkaen",
|
||||||
"Members": "Jäsenet",
|
"Members": "Jäsenet",
|
||||||
"Membership cards": "Jäsenkortit",
|
|
||||||
"Membership ID": "Jäsentunnus",
|
"Membership ID": "Jäsentunnus",
|
||||||
"Membership ID copied to clipboard": "Jäsenyystunnus kopioitu leikepöydälle",
|
"Membership ID copied to clipboard": "Jäsenyystunnus kopioitu leikepöydälle",
|
||||||
|
"Membership cards": "Jäsenkortit",
|
||||||
"Menu": "Valikko",
|
"Menu": "Valikko",
|
||||||
"Modify": "Muokkaa",
|
"Modify": "Muokkaa",
|
||||||
"Month": "Kuukausi",
|
"Month": "Kuukausi",
|
||||||
@@ -146,9 +140,6 @@
|
|||||||
"Nearby companies": "Läheiset yritykset",
|
"Nearby companies": "Läheiset yritykset",
|
||||||
"New password": "Uusi salasana",
|
"New password": "Uusi salasana",
|
||||||
"Next": "Seuraava",
|
"Next": "Seuraava",
|
||||||
"next level:": "pistettä tasolle:",
|
|
||||||
"night": "yö",
|
|
||||||
"nights": "yötä",
|
|
||||||
"Nights needed to level up": "Yöt, joita tarvitaan tasolle",
|
"Nights needed to level up": "Yöt, joita tarvitaan tasolle",
|
||||||
"No content published": "Ei julkaistua sisältöä",
|
"No content published": "Ei julkaistua sisältöä",
|
||||||
"No matching location found": "Vastaavaa sijaintia ei löytynyt",
|
"No matching location found": "Vastaavaa sijaintia ei löytynyt",
|
||||||
@@ -159,13 +150,11 @@
|
|||||||
"Non-refundable": "Ei palautettavissa",
|
"Non-refundable": "Ei palautettavissa",
|
||||||
"Not found": "Ei löydetty",
|
"Not found": "Ei löydetty",
|
||||||
"Nr night, nr adult": "{nights, number} yö, {adults, number} aikuinen",
|
"Nr night, nr adult": "{nights, number} yö, {adults, number} aikuinen",
|
||||||
"number": "määrä",
|
|
||||||
"On your journey": "Matkallasi",
|
"On your journey": "Matkallasi",
|
||||||
"Open": "Avata",
|
"Open": "Avata",
|
||||||
"Open language menu": "Avaa kielivalikko",
|
"Open language menu": "Avaa kielivalikko",
|
||||||
"Open menu": "Avaa valikko",
|
"Open menu": "Avaa valikko",
|
||||||
"Open my pages menu": "Avaa omat sivut -valikko",
|
"Open my pages menu": "Avaa omat sivut -valikko",
|
||||||
"or": "tai",
|
|
||||||
"Overview": "Yleiskatsaus",
|
"Overview": "Yleiskatsaus",
|
||||||
"Parking": "Pysäköinti",
|
"Parking": "Pysäköinti",
|
||||||
"Parking / Garage": "Pysäköinti / Autotalli",
|
"Parking / Garage": "Pysäköinti / Autotalli",
|
||||||
@@ -177,7 +166,6 @@
|
|||||||
"Phone is required": "Puhelin vaaditaan",
|
"Phone is required": "Puhelin vaaditaan",
|
||||||
"Phone number": "Puhelinnumero",
|
"Phone number": "Puhelinnumero",
|
||||||
"Please enter a valid phone number": "Ole hyvä ja näppäile voimassaoleva puhelinnumero",
|
"Please enter a valid phone number": "Ole hyvä ja näppäile voimassaoleva puhelinnumero",
|
||||||
"points": "pistettä",
|
|
||||||
"Points": "Pisteet",
|
"Points": "Pisteet",
|
||||||
"Points being calculated": "Pisteitä lasketaan",
|
"Points being calculated": "Pisteitä lasketaan",
|
||||||
"Points earned prior to May 1, 2021": "Pisteet, jotka ansaittu ennen 1.5.2021",
|
"Points earned prior to May 1, 2021": "Pisteet, jotka ansaittu ennen 1.5.2021",
|
||||||
@@ -196,11 +184,10 @@
|
|||||||
"Room & Terms": "Huone & Ehdot",
|
"Room & Terms": "Huone & Ehdot",
|
||||||
"Room facilities": "Huoneen varustelu",
|
"Room facilities": "Huoneen varustelu",
|
||||||
"Rooms": "Huoneet",
|
"Rooms": "Huoneet",
|
||||||
"Rooms & Guests": "Huoneet & Vieraat",
|
"Save": "Tallenna",
|
||||||
"Rooms & Guestss": "Huoneet & Vieraat",
|
|
||||||
"Save": "Tallentaa",
|
|
||||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||||
|
"Search": "Haku",
|
||||||
"See all photos": "Katso kaikki kuvat",
|
"See all photos": "Katso kaikki kuvat",
|
||||||
"See hotel details": "Katso hotellin tiedot",
|
"See hotel details": "Katso hotellin tiedot",
|
||||||
"See room details": "Katso huoneen tiedot",
|
"See room details": "Katso huoneen tiedot",
|
||||||
@@ -221,29 +208,25 @@
|
|||||||
"Something went wrong and we couldn't add your card. Please try again later.": "Jotain meni pieleen, emmekä voineet lisätä korttiasi. Yritä myöhemmin uudelleen.",
|
"Something went wrong and we couldn't add your card. Please try again later.": "Jotain meni pieleen, emmekä voineet lisätä korttiasi. Yritä myöhemmin uudelleen.",
|
||||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Jotain meni pieleen, emmekä voineet poistaa korttiasi. Yritä myöhemmin uudelleen.",
|
"Something went wrong and we couldn't remove your card. Please try again later.": "Jotain meni pieleen, emmekä voineet poistaa korttiasi. Yritä myöhemmin uudelleen.",
|
||||||
"Something went wrong!": "Jotain meni pieleen!",
|
"Something went wrong!": "Jotain meni pieleen!",
|
||||||
"special character": "erikoishahmo",
|
|
||||||
"spendable points expiring by": "{points} pistettä vanhenee {date} mennessä",
|
|
||||||
"Sports": "Urheilu",
|
"Sports": "Urheilu",
|
||||||
"Standard price": "Normaali hinta",
|
"Standard price": "Normaali hinta",
|
||||||
"Street": "Katu",
|
"Street": "Katu",
|
||||||
"Successfully updated profile!": "Profiilin päivitys onnistui!",
|
"Successfully updated profile!": "Profiilin päivitys onnistui!",
|
||||||
"Summary": "Yhteenveto",
|
"Summary": "Yhteenveto",
|
||||||
|
"TUI Points": "TUI Points",
|
||||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Kerro meille, mitä tietoja ja päivityksiä haluat saada ja miten, napsauttamalla alla olevaa linkkiä.",
|
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Kerro meille, mitä tietoja ja päivityksiä haluat saada ja miten, napsauttamalla alla olevaa linkkiä.",
|
||||||
"Thank you": "Kiitos",
|
"Thank you": "Kiitos",
|
||||||
"Theatre": "Teatteri",
|
"Theatre": "Teatteri",
|
||||||
"There are no transactions to display": "Näytettäviä tapahtumia ei ole",
|
"There are no transactions to display": "Näytettäviä tapahtumia ei ole",
|
||||||
"Things nearby HOTEL_NAME": "Lähellä olevia asioita {hotelName}",
|
"Things nearby HOTEL_NAME": "Lähellä olevia asioita {hotelName}",
|
||||||
"to": "to",
|
|
||||||
"Total Points": "Kokonaispisteet",
|
"Total Points": "Kokonaispisteet",
|
||||||
"Tourist": "Turisti",
|
"Tourist": "Turisti",
|
||||||
"Transaction date": "Tapahtuman päivämäärä",
|
"Transaction date": "Tapahtuman päivämäärä",
|
||||||
"Transactions": "Tapahtumat",
|
"Transactions": "Tapahtumat",
|
||||||
"Transportations": "Kuljetukset",
|
"Transportations": "Kuljetukset",
|
||||||
"Tripadvisor reviews": "{rating} ({count} arvostelua TripAdvisorissa)",
|
"Tripadvisor reviews": "{rating} ({count} arvostelua TripAdvisorissa)",
|
||||||
"TUI Points": "TUI Points",
|
|
||||||
"Type of bed": "Vuodetyyppi",
|
"Type of bed": "Vuodetyyppi",
|
||||||
"Type of room": "Huonetyyppi",
|
"Type of room": "Huonetyyppi",
|
||||||
"uppercase letter": "iso kirjain",
|
|
||||||
"Use bonus cheque": "Käytä bonussekkiä",
|
"Use bonus cheque": "Käytä bonussekkiä",
|
||||||
"User information": "Käyttäjän tiedot",
|
"User information": "Käyttäjän tiedot",
|
||||||
"View as list": "Näytä listana",
|
"View as list": "Näytä listana",
|
||||||
@@ -269,9 +252,9 @@
|
|||||||
"You canceled adding a new credit card.": "Peruutit uuden luottokortin lisäämisen.",
|
"You canceled adding a new credit card.": "Peruutit uuden luottokortin lisäämisen.",
|
||||||
"You have no previous stays.": "Sinulla ei ole aiempia majoituksia.",
|
"You have no previous stays.": "Sinulla ei ole aiempia majoituksia.",
|
||||||
"You have no upcoming stays.": "Sinulla ei ole tulevia majoituksia.",
|
"You have no upcoming stays.": "Sinulla ei ole tulevia majoituksia.",
|
||||||
|
"Your Challenges Conquer & Earn!": "Voita ja ansaitse haasteesi!",
|
||||||
"Your card was successfully removed!": "Korttisi poistettiin onnistuneesti!",
|
"Your card was successfully removed!": "Korttisi poistettiin onnistuneesti!",
|
||||||
"Your card was successfully saved!": "Korttisi tallennettu onnistuneesti!",
|
"Your card was successfully saved!": "Korttisi tallennettu onnistuneesti!",
|
||||||
"Your Challenges Conquer & Earn!": "Voita ja ansaitse haasteesi!",
|
|
||||||
"Your current level": "Nykyinen tasosi",
|
"Your current level": "Nykyinen tasosi",
|
||||||
"Your details": "Tietosi",
|
"Your details": "Tietosi",
|
||||||
"Your level": "Tasosi",
|
"Your level": "Tasosi",
|
||||||
@@ -279,5 +262,23 @@
|
|||||||
"Zip code": "Postinumero",
|
"Zip code": "Postinumero",
|
||||||
"Zoo": "Eläintarha",
|
"Zoo": "Eläintarha",
|
||||||
"Zoom in": "Lähennä",
|
"Zoom in": "Lähennä",
|
||||||
"Zoom out": "Loitonna"
|
"Zoom out": "Loitonna",
|
||||||
|
"as of today": "tänään",
|
||||||
|
"booking.nights": "{totalNights, plural, one {# yö} other {# yötä}}",
|
||||||
|
"by": "mennessä",
|
||||||
|
"characters": "hahmoja",
|
||||||
|
"hotelPages.rooms.roomCard.person": "henkilö",
|
||||||
|
"hotelPages.rooms.roomCard.persons": "Henkilöä",
|
||||||
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Katso huoneen tiedot",
|
||||||
|
"km to city center": "km keskustaan",
|
||||||
|
"next level:": "pistettä tasolle:",
|
||||||
|
"night": "yö",
|
||||||
|
"nights": "yötä",
|
||||||
|
"number": "määrä",
|
||||||
|
"or": "tai",
|
||||||
|
"points": "pistettä",
|
||||||
|
"special character": "erikoishahmo",
|
||||||
|
"spendable points expiring by": "{points} pistettä vanhenee {date} mennessä",
|
||||||
|
"to": "to",
|
||||||
|
"uppercase letter": "iso kirjain"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
"Any changes you've made will be lost.": "Eventuelle endringer du har gjort, går tapt.",
|
"Any changes you've made will be lost.": "Eventuelle endringer du har gjort, går tapt.",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på at du vil fjerne kortet som slutter på {lastFourDigits} fra medlemsprofilen din?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på at du vil fjerne kortet som slutter på {lastFourDigits} fra medlemsprofilen din?",
|
||||||
"Arrival date": "Ankomstdato",
|
"Arrival date": "Ankomstdato",
|
||||||
"as of today": "per idag",
|
|
||||||
"As our": "Som vår {level}",
|
"As our": "Som vår {level}",
|
||||||
"As our Close Friend": "Som vår nære venn",
|
"As our Close Friend": "Som vår nære venn",
|
||||||
"At latest": "Senest",
|
"At latest": "Senest",
|
||||||
@@ -24,16 +23,14 @@
|
|||||||
"Bed type": "Seng type",
|
"Bed type": "Seng type",
|
||||||
"Book": "Bestill",
|
"Book": "Bestill",
|
||||||
"Book reward night": "Bestill belønningskveld",
|
"Book reward night": "Bestill belønningskveld",
|
||||||
"Booking codes and vouchers": "Bestillingskoder og kuponger",
|
"Code / Voucher": "Bestillingskoder / kuponger",
|
||||||
"Booking number": "Bestillingsnummer",
|
"Booking number": "Bestillingsnummer",
|
||||||
"Breakfast": "Frokost",
|
"Breakfast": "Frokost",
|
||||||
"Breakfast excluded": "Frokost ekskludert",
|
"Breakfast excluded": "Frokost ekskludert",
|
||||||
"Breakfast included": "Frokost inkludert",
|
"Breakfast included": "Frokost inkludert",
|
||||||
"Bus terminal": "Bussterminal",
|
"Bus terminal": "Bussterminal",
|
||||||
"Business": "Forretnings",
|
"Business": "Forretnings",
|
||||||
"by": "innen",
|
|
||||||
"Cancel": "Avbryt",
|
"Cancel": "Avbryt",
|
||||||
"characters": "tegn",
|
|
||||||
"Check in": "Sjekk inn",
|
"Check in": "Sjekk inn",
|
||||||
"Check out": "Sjekk ut",
|
"Check out": "Sjekk ut",
|
||||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sjekk ut kredittkortene som er lagret på profilen din. Betal med et lagret kort når du er pålogget for en jevnere nettopplevelse.",
|
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sjekk ut kredittkortene som er lagret på profilen din. Betal med et lagret kort når du er pålogget for en jevnere nettopplevelse.",
|
||||||
@@ -75,9 +72,9 @@
|
|||||||
"Explore all levels and benefits": "Utforsk alle nivåer og fordeler",
|
"Explore all levels and benefits": "Utforsk alle nivåer og fordeler",
|
||||||
"Explore nearby": "Utforsk i nærheten",
|
"Explore nearby": "Utforsk i nærheten",
|
||||||
"Extras to your booking": "Tilvalg til bestillingen din",
|
"Extras to your booking": "Tilvalg til bestillingen din",
|
||||||
|
"FAQ": "FAQ",
|
||||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.",
|
"Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.",
|
||||||
"Fair": "Messe",
|
"Fair": "Messe",
|
||||||
"FAQ": "FAQ",
|
|
||||||
"Find booking": "Finn booking",
|
"Find booking": "Finn booking",
|
||||||
"Find hotels": "Finn hotell",
|
"Find hotels": "Finn hotell",
|
||||||
"Flexibility": "Fleksibilitet",
|
"Flexibility": "Fleksibilitet",
|
||||||
@@ -94,15 +91,11 @@
|
|||||||
"Hotel": "Hotel",
|
"Hotel": "Hotel",
|
||||||
"Hotel facilities": "Hotelfaciliteter",
|
"Hotel facilities": "Hotelfaciliteter",
|
||||||
"Hotel surroundings": "Hotellomgivelser",
|
"Hotel surroundings": "Hotellomgivelser",
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
|
||||||
"hotelPages.rooms.roomCard.persons": "personer",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se detaljer om rommet",
|
|
||||||
"Hotels": "Hoteller",
|
"Hotels": "Hoteller",
|
||||||
"How do you want to sleep?": "Hvordan vil du sove?",
|
"How do you want to sleep?": "Hvordan vil du sove?",
|
||||||
"How it works": "Hvordan det fungerer",
|
"How it works": "Hvordan det fungerer",
|
||||||
"Image gallery": "Bildegalleri",
|
"Image gallery": "Bildegalleri",
|
||||||
"Join Scandic Friends": "Bli med i Scandic Friends",
|
"Join Scandic Friends": "Bli med i Scandic Friends",
|
||||||
"km to city center": "km til sentrum",
|
|
||||||
"Language": "Språk",
|
"Language": "Språk",
|
||||||
"Latest searches": "Siste søk",
|
"Latest searches": "Siste søk",
|
||||||
"Level": "Nivå",
|
"Level": "Nivå",
|
||||||
@@ -129,9 +122,9 @@
|
|||||||
"Member price": "Medlemspris",
|
"Member price": "Medlemspris",
|
||||||
"Member price from": "Medlemspris fra",
|
"Member price from": "Medlemspris fra",
|
||||||
"Members": "Medlemmer",
|
"Members": "Medlemmer",
|
||||||
"Membership cards": "Medlemskort",
|
|
||||||
"Membership ID": "Medlems-ID",
|
"Membership ID": "Medlems-ID",
|
||||||
"Membership ID copied to clipboard": "Medlems-ID kopiert til utklippstavlen",
|
"Membership ID copied to clipboard": "Medlems-ID kopiert til utklippstavlen",
|
||||||
|
"Membership cards": "Medlemskort",
|
||||||
"Menu": "Menu",
|
"Menu": "Menu",
|
||||||
"Modify": "Endre",
|
"Modify": "Endre",
|
||||||
"Month": "Måned",
|
"Month": "Måned",
|
||||||
@@ -146,9 +139,6 @@
|
|||||||
"Nearby companies": "Nærliggende selskaper",
|
"Nearby companies": "Nærliggende selskaper",
|
||||||
"New password": "Nytt passord",
|
"New password": "Nytt passord",
|
||||||
"Next": "Neste",
|
"Next": "Neste",
|
||||||
"next level:": "Neste nivå:",
|
|
||||||
"night": "natt",
|
|
||||||
"nights": "netter",
|
|
||||||
"Nights needed to level up": "Netter som trengs for å komme opp i nivå",
|
"Nights needed to level up": "Netter som trengs for å komme opp i nivå",
|
||||||
"No content published": "Ingen innhold publisert",
|
"No content published": "Ingen innhold publisert",
|
||||||
"No matching location found": "Fant ingen samsvarende plassering",
|
"No matching location found": "Fant ingen samsvarende plassering",
|
||||||
@@ -159,13 +149,11 @@
|
|||||||
"Non-refundable": "Ikke-refunderbart",
|
"Non-refundable": "Ikke-refunderbart",
|
||||||
"Not found": "Ikke funnet",
|
"Not found": "Ikke funnet",
|
||||||
"Nr night, nr adult": "{nights, number} natt, {adults, number} voksen",
|
"Nr night, nr adult": "{nights, number} natt, {adults, number} voksen",
|
||||||
"number": "antall",
|
|
||||||
"On your journey": "På reisen din",
|
"On your journey": "På reisen din",
|
||||||
"Open": "Åpen",
|
"Open": "Åpen",
|
||||||
"Open language menu": "Åpne språkmenyen",
|
"Open language menu": "Åpne språkmenyen",
|
||||||
"Open menu": "Åpne menyen",
|
"Open menu": "Åpne menyen",
|
||||||
"Open my pages menu": "Åpne mine sider menyen",
|
"Open my pages menu": "Åpne mine sider menyen",
|
||||||
"or": "eller",
|
|
||||||
"Overview": "Oversikt",
|
"Overview": "Oversikt",
|
||||||
"Parking": "Parkering",
|
"Parking": "Parkering",
|
||||||
"Parking / Garage": "Parkering / Garasje",
|
"Parking / Garage": "Parkering / Garasje",
|
||||||
@@ -177,7 +165,6 @@
|
|||||||
"Phone is required": "Telefon kreves",
|
"Phone is required": "Telefon kreves",
|
||||||
"Phone number": "Telefonnummer",
|
"Phone number": "Telefonnummer",
|
||||||
"Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer",
|
"Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer",
|
||||||
"points": "poeng",
|
|
||||||
"Points": "Poeng",
|
"Points": "Poeng",
|
||||||
"Points being calculated": "Poeng beregnes",
|
"Points being calculated": "Poeng beregnes",
|
||||||
"Points earned prior to May 1, 2021": "Opptjente poeng før 1. mai 2021",
|
"Points earned prior to May 1, 2021": "Opptjente poeng før 1. mai 2021",
|
||||||
@@ -196,10 +183,11 @@
|
|||||||
"Room & Terms": "Rom & Vilkår",
|
"Room & Terms": "Rom & Vilkår",
|
||||||
"Room facilities": "Romfasiliteter",
|
"Room facilities": "Romfasiliteter",
|
||||||
"Rooms": "Rom",
|
"Rooms": "Rom",
|
||||||
"Rooms & Guests": "Rom og gjester",
|
"Guests & Rooms": "Gjester & rom",
|
||||||
"Save": "Lagre",
|
"Save": "Lagre",
|
||||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||||
|
"Search": "Søk",
|
||||||
"See all photos": "Se alle bilder",
|
"See all photos": "Se alle bilder",
|
||||||
"See hotel details": "Se hotellinformasjon",
|
"See hotel details": "Se hotellinformasjon",
|
||||||
"See room details": "Se detaljer om rommet",
|
"See room details": "Se detaljer om rommet",
|
||||||
@@ -220,29 +208,25 @@
|
|||||||
"Something went wrong and we couldn't add your card. Please try again later.": "Noe gikk galt, og vi kunne ikke legge til kortet ditt. Prøv igjen senere.",
|
"Something went wrong and we couldn't add your card. Please try again later.": "Noe gikk galt, og vi kunne ikke legge til kortet ditt. Prøv igjen senere.",
|
||||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Noe gikk galt, og vi kunne ikke fjerne kortet ditt. Vennligst prøv igjen senere.",
|
"Something went wrong and we couldn't remove your card. Please try again later.": "Noe gikk galt, og vi kunne ikke fjerne kortet ditt. Vennligst prøv igjen senere.",
|
||||||
"Something went wrong!": "Noe gikk galt!",
|
"Something went wrong!": "Noe gikk galt!",
|
||||||
"special character": "spesiell karakter",
|
|
||||||
"spendable points expiring by": "{points} Brukbare poeng utløper innen {date}",
|
|
||||||
"Sports": "Sport",
|
"Sports": "Sport",
|
||||||
"Standard price": "Standardpris",
|
"Standard price": "Standardpris",
|
||||||
"Street": "Gate",
|
"Street": "Gate",
|
||||||
"Successfully updated profile!": "Vellykket oppdatert profil!",
|
"Successfully updated profile!": "Vellykket oppdatert profil!",
|
||||||
"Summary": "Sammendrag",
|
"Summary": "Sammendrag",
|
||||||
|
"TUI Points": "TUI Points",
|
||||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Fortell oss hvilken informasjon og hvilke oppdateringer du ønsker å motta, og hvordan, ved å klikke på lenken nedenfor.",
|
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Fortell oss hvilken informasjon og hvilke oppdateringer du ønsker å motta, og hvordan, ved å klikke på lenken nedenfor.",
|
||||||
"Thank you": "Takk",
|
"Thank you": "Takk",
|
||||||
"Theatre": "Teater",
|
"Theatre": "Teater",
|
||||||
"There are no transactions to display": "Det er ingen transaksjoner å vise",
|
"There are no transactions to display": "Det er ingen transaksjoner å vise",
|
||||||
"Things nearby HOTEL_NAME": "Ting i nærheten av {hotelName}",
|
"Things nearby HOTEL_NAME": "Ting i nærheten av {hotelName}",
|
||||||
"to": "til",
|
|
||||||
"Total Points": "Totale poeng",
|
"Total Points": "Totale poeng",
|
||||||
"Tourist": "Turist",
|
"Tourist": "Turist",
|
||||||
"Transaction date": "Transaksjonsdato",
|
"Transaction date": "Transaksjonsdato",
|
||||||
"Transactions": "Transaksjoner",
|
"Transactions": "Transaksjoner",
|
||||||
"Transportations": "Transport",
|
"Transportations": "Transport",
|
||||||
"Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)",
|
"Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)",
|
||||||
"TUI Points": "TUI Points",
|
|
||||||
"Type of bed": "Sengtype",
|
"Type of bed": "Sengtype",
|
||||||
"Type of room": "Romtype",
|
"Type of room": "Romtype",
|
||||||
"uppercase letter": "stor bokstav",
|
|
||||||
"Use bonus cheque": "Bruk bonussjekk",
|
"Use bonus cheque": "Bruk bonussjekk",
|
||||||
"User information": "Brukerinformasjon",
|
"User information": "Brukerinformasjon",
|
||||||
"View as list": "Vis som liste",
|
"View as list": "Vis som liste",
|
||||||
@@ -268,9 +252,9 @@
|
|||||||
"You canceled adding a new credit card.": "Du kansellerte å legge til et nytt kredittkort.",
|
"You canceled adding a new credit card.": "Du kansellerte å legge til et nytt kredittkort.",
|
||||||
"You have no previous stays.": "Du har ingen tidligere opphold.",
|
"You have no previous stays.": "Du har ingen tidligere opphold.",
|
||||||
"You have no upcoming stays.": "Du har ingen kommende opphold.",
|
"You have no upcoming stays.": "Du har ingen kommende opphold.",
|
||||||
|
"Your Challenges Conquer & Earn!": "Dine utfordringer Erobre og tjen!",
|
||||||
"Your card was successfully removed!": "Kortet ditt ble fjernet!",
|
"Your card was successfully removed!": "Kortet ditt ble fjernet!",
|
||||||
"Your card was successfully saved!": "Kortet ditt ble lagret!",
|
"Your card was successfully saved!": "Kortet ditt ble lagret!",
|
||||||
"Your Challenges Conquer & Earn!": "Dine utfordringer Erobre og tjen!",
|
|
||||||
"Your current level": "Ditt nåværende nivå",
|
"Your current level": "Ditt nåværende nivå",
|
||||||
"Your details": "Dine detaljer",
|
"Your details": "Dine detaljer",
|
||||||
"Your level": "Ditt nivå",
|
"Your level": "Ditt nivå",
|
||||||
@@ -278,5 +262,23 @@
|
|||||||
"Zip code": "Post kode",
|
"Zip code": "Post kode",
|
||||||
"Zoo": "Dyrehage",
|
"Zoo": "Dyrehage",
|
||||||
"Zoom in": "Zoom inn",
|
"Zoom in": "Zoom inn",
|
||||||
"Zoom out": "Zoom ut"
|
"Zoom out": "Zoom ut",
|
||||||
|
"as of today": "per i dag",
|
||||||
|
"booking.nights": "{totalNights, plural, one {# natt} other {# netter}}",
|
||||||
|
"by": "innen",
|
||||||
|
"characters": "tegn",
|
||||||
|
"hotelPages.rooms.roomCard.person": "person",
|
||||||
|
"hotelPages.rooms.roomCard.persons": "personer",
|
||||||
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Se detaljer om rommet",
|
||||||
|
"km to city center": "km til sentrum",
|
||||||
|
"next level:": "Neste nivå:",
|
||||||
|
"night": "natt",
|
||||||
|
"nights": "netter",
|
||||||
|
"number": "antall",
|
||||||
|
"or": "eller",
|
||||||
|
"points": "poeng",
|
||||||
|
"special character": "spesiell karakter",
|
||||||
|
"spendable points expiring by": "{points} Brukbare poeng utløper innen {date}",
|
||||||
|
"to": "til",
|
||||||
|
"uppercase letter": "stor bokstav"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
"Any changes you've made will be lost.": "Alla ändringar du har gjort kommer att gå förlorade.",
|
"Any changes you've made will be lost.": "Alla ändringar du har gjort kommer att gå förlorade.",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Är du säker på att du vill ta bort kortet som slutar med {lastFourDigits} från din medlemsprofil?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Är du säker på att du vill ta bort kortet som slutar med {lastFourDigits} från din medlemsprofil?",
|
||||||
"Arrival date": "Ankomstdatum",
|
"Arrival date": "Ankomstdatum",
|
||||||
"as of today": "från och med idag",
|
|
||||||
"As our": "Som vår {level}",
|
"As our": "Som vår {level}",
|
||||||
"As our Close Friend": "Som vår nära vän",
|
"As our Close Friend": "Som vår nära vän",
|
||||||
"At latest": "Senast",
|
"At latest": "Senast",
|
||||||
@@ -24,16 +23,14 @@
|
|||||||
"Bed type": "Sängtyp",
|
"Bed type": "Sängtyp",
|
||||||
"Book": "Boka",
|
"Book": "Boka",
|
||||||
"Book reward night": "Boka frinatt",
|
"Book reward night": "Boka frinatt",
|
||||||
"Booking codes and vouchers": "Bokningskoder och kuponger",
|
"Code / Voucher": "Bokningskoder / kuponger",
|
||||||
"Booking number": "Bokningsnummer",
|
"Booking number": "Bokningsnummer",
|
||||||
"Breakfast": "Frukost",
|
"Breakfast": "Frukost",
|
||||||
"Breakfast excluded": "Frukost ingår ej",
|
"Breakfast excluded": "Frukost ingår ej",
|
||||||
"Breakfast included": "Frukost ingår",
|
"Breakfast included": "Frukost ingår",
|
||||||
"Bus terminal": "Bussterminal",
|
"Bus terminal": "Bussterminal",
|
||||||
"Business": "Business",
|
"Business": "Business",
|
||||||
"by": "innan",
|
|
||||||
"Cancel": "Avbryt",
|
"Cancel": "Avbryt",
|
||||||
"characters": "tecken",
|
|
||||||
"Check in": "Checka in",
|
"Check in": "Checka in",
|
||||||
"Check out": "Checka ut",
|
"Check out": "Checka ut",
|
||||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Kolla in kreditkorten som sparats i din profil. Betala med ett sparat kort när du är inloggad för en smidigare webbupplevelse.",
|
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Kolla in kreditkorten som sparats i din profil. Betala med ett sparat kort när du är inloggad för en smidigare webbupplevelse.",
|
||||||
@@ -75,9 +72,9 @@
|
|||||||
"Explore all levels and benefits": "Utforska alla nivåer och fördelar",
|
"Explore all levels and benefits": "Utforska alla nivåer och fördelar",
|
||||||
"Explore nearby": "Utforska i närheten",
|
"Explore nearby": "Utforska i närheten",
|
||||||
"Extras to your booking": "Extra tillval till din bokning",
|
"Extras to your booking": "Extra tillval till din bokning",
|
||||||
|
"FAQ": "FAQ",
|
||||||
"Failed to delete credit card, please try again later.": "Det gick inte att ta bort kreditkortet, försök igen senare.",
|
"Failed to delete credit card, please try again later.": "Det gick inte att ta bort kreditkortet, försök igen senare.",
|
||||||
"Fair": "Mässa",
|
"Fair": "Mässa",
|
||||||
"FAQ": "FAQ",
|
|
||||||
"Find booking": "Hitta bokning",
|
"Find booking": "Hitta bokning",
|
||||||
"Find hotels": "Hitta hotell",
|
"Find hotels": "Hitta hotell",
|
||||||
"Flexibility": "Flexibilitet",
|
"Flexibility": "Flexibilitet",
|
||||||
@@ -94,15 +91,11 @@
|
|||||||
"Hotel": "Hotell",
|
"Hotel": "Hotell",
|
||||||
"Hotel facilities": "Hotellfaciliteter",
|
"Hotel facilities": "Hotellfaciliteter",
|
||||||
"Hotel surroundings": "Hotellomgivning",
|
"Hotel surroundings": "Hotellomgivning",
|
||||||
"hotelPages.rooms.roomCard.person": "person",
|
|
||||||
"hotelPages.rooms.roomCard.persons": "personer",
|
|
||||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se information om rummet",
|
|
||||||
"Hotels": "Hotell",
|
"Hotels": "Hotell",
|
||||||
"How do you want to sleep?": "Hur vill du sova?",
|
"How do you want to sleep?": "Hur vill du sova?",
|
||||||
"How it works": "Hur det fungerar",
|
"How it works": "Hur det fungerar",
|
||||||
"Image gallery": "Bildgalleri",
|
"Image gallery": "Bildgalleri",
|
||||||
"Join Scandic Friends": "Gå med i Scandic Friends",
|
"Join Scandic Friends": "Gå med i Scandic Friends",
|
||||||
"km to city center": "km till stadens centrum",
|
|
||||||
"Language": "Språk",
|
"Language": "Språk",
|
||||||
"Latest searches": "Senaste sökningarna",
|
"Latest searches": "Senaste sökningarna",
|
||||||
"Level": "Nivå",
|
"Level": "Nivå",
|
||||||
@@ -129,9 +122,9 @@
|
|||||||
"Member price": "Medlemspris",
|
"Member price": "Medlemspris",
|
||||||
"Member price from": "Medlemspris från",
|
"Member price from": "Medlemspris från",
|
||||||
"Members": "Medlemmar",
|
"Members": "Medlemmar",
|
||||||
"Membership cards": "Medlemskort",
|
|
||||||
"Membership ID": "Medlems-ID",
|
"Membership ID": "Medlems-ID",
|
||||||
"Membership ID copied to clipboard": "Medlems-ID kopierat till urklipp",
|
"Membership ID copied to clipboard": "Medlems-ID kopierat till urklipp",
|
||||||
|
"Membership cards": "Medlemskort",
|
||||||
"Menu": "Meny",
|
"Menu": "Meny",
|
||||||
"Modify": "Ändra",
|
"Modify": "Ändra",
|
||||||
"Month": "Månad",
|
"Month": "Månad",
|
||||||
@@ -146,9 +139,6 @@
|
|||||||
"Nearby companies": "Närliggande företag",
|
"Nearby companies": "Närliggande företag",
|
||||||
"New password": "Nytt lösenord",
|
"New password": "Nytt lösenord",
|
||||||
"Next": "Nästa",
|
"Next": "Nästa",
|
||||||
"next level:": "Nästa nivå:",
|
|
||||||
"night": "natt",
|
|
||||||
"nights": "nätter",
|
|
||||||
"Nights needed to level up": "Nätter som behövs för att gå upp i nivå",
|
"Nights needed to level up": "Nätter som behövs för att gå upp i nivå",
|
||||||
"No content published": "Inget innehåll publicerat",
|
"No content published": "Inget innehåll publicerat",
|
||||||
"No matching location found": "Ingen matchande plats hittades",
|
"No matching location found": "Ingen matchande plats hittades",
|
||||||
@@ -159,13 +149,11 @@
|
|||||||
"Non-refundable": "Ej återbetalningsbar",
|
"Non-refundable": "Ej återbetalningsbar",
|
||||||
"Not found": "Hittades inte",
|
"Not found": "Hittades inte",
|
||||||
"Nr night, nr adult": "{nights, number} natt, {adults, number} vuxen",
|
"Nr night, nr adult": "{nights, number} natt, {adults, number} vuxen",
|
||||||
"number": "nummer",
|
|
||||||
"On your journey": "På din resa",
|
"On your journey": "På din resa",
|
||||||
"Open": "Öppna",
|
"Open": "Öppna",
|
||||||
"Open language menu": "Öppna språkmenyn",
|
"Open language menu": "Öppna språkmenyn",
|
||||||
"Open menu": "Öppna menyn",
|
"Open menu": "Öppna menyn",
|
||||||
"Open my pages menu": "Öppna mina sidor menyn",
|
"Open my pages menu": "Öppna mina sidor menyn",
|
||||||
"or": "eller",
|
|
||||||
"Overview": "Översikt",
|
"Overview": "Översikt",
|
||||||
"Parking": "Parkering",
|
"Parking": "Parkering",
|
||||||
"Parking / Garage": "Parkering / Garage",
|
"Parking / Garage": "Parkering / Garage",
|
||||||
@@ -177,7 +165,6 @@
|
|||||||
"Phone is required": "Telefonnummer är obligatorisk",
|
"Phone is required": "Telefonnummer är obligatorisk",
|
||||||
"Phone number": "Telefonnummer",
|
"Phone number": "Telefonnummer",
|
||||||
"Please enter a valid phone number": "Var vänlig och ange ett giltigt telefonnummer",
|
"Please enter a valid phone number": "Var vänlig och ange ett giltigt telefonnummer",
|
||||||
"points": "poäng",
|
|
||||||
"Points": "Poäng",
|
"Points": "Poäng",
|
||||||
"Points being calculated": "Poäng beräknas",
|
"Points being calculated": "Poäng beräknas",
|
||||||
"Points earned prior to May 1, 2021": "Intjänade poäng före den 1 maj 2021",
|
"Points earned prior to May 1, 2021": "Intjänade poäng före den 1 maj 2021",
|
||||||
@@ -196,10 +183,11 @@
|
|||||||
"Room & Terms": "Rum & Villkor",
|
"Room & Terms": "Rum & Villkor",
|
||||||
"Room facilities": "Rumfaciliteter",
|
"Room facilities": "Rumfaciliteter",
|
||||||
"Rooms": "Rum",
|
"Rooms": "Rum",
|
||||||
"Rooms & Guests": "Rum och gäster",
|
"Guests & Rooms": "Gäster & rum",
|
||||||
"Save": "Spara",
|
"Save": "Spara",
|
||||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||||
|
"Search": "Sök",
|
||||||
"See all photos": "Se alla foton",
|
"See all photos": "Se alla foton",
|
||||||
"See hotel details": "Se hotellinformation",
|
"See hotel details": "Se hotellinformation",
|
||||||
"See room details": "Se rumsdetaljer",
|
"See room details": "Se rumsdetaljer",
|
||||||
@@ -220,29 +208,25 @@
|
|||||||
"Something went wrong and we couldn't add your card. Please try again later.": "Något gick fel och vi kunde inte lägga till ditt kort. Försök igen senare.",
|
"Something went wrong and we couldn't add your card. Please try again later.": "Något gick fel och vi kunde inte lägga till ditt kort. Försök igen senare.",
|
||||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Något gick fel och vi kunde inte ta bort ditt kort. Försök igen senare.",
|
"Something went wrong and we couldn't remove your card. Please try again later.": "Något gick fel och vi kunde inte ta bort ditt kort. Försök igen senare.",
|
||||||
"Something went wrong!": "Något gick fel!",
|
"Something went wrong!": "Något gick fel!",
|
||||||
"special character": "speciell karaktär",
|
|
||||||
"spendable points expiring by": "{points} poäng förfaller {date}",
|
|
||||||
"Sports": "Sport",
|
"Sports": "Sport",
|
||||||
"Standard price": "Standardpris",
|
"Standard price": "Standardpris",
|
||||||
"Street": "Gata",
|
"Street": "Gata",
|
||||||
"Successfully updated profile!": "Profilen har uppdaterats framgångsrikt!",
|
"Successfully updated profile!": "Profilen har uppdaterats framgångsrikt!",
|
||||||
"Summary": "Sammanfattning",
|
"Summary": "Sammanfattning",
|
||||||
|
"TUI Points": "TUI Points",
|
||||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Berätta för oss vilken information och vilka uppdateringar du vill få och hur genom att klicka på länken nedan.",
|
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Berätta för oss vilken information och vilka uppdateringar du vill få och hur genom att klicka på länken nedan.",
|
||||||
"Thank you": "Tack",
|
"Thank you": "Tack",
|
||||||
"Theatre": "Teater",
|
"Theatre": "Teater",
|
||||||
"There are no transactions to display": "Det finns inga transaktioner att visa",
|
"There are no transactions to display": "Det finns inga transaktioner att visa",
|
||||||
"Things nearby HOTEL_NAME": "Saker i närheten av {hotelName}",
|
"Things nearby HOTEL_NAME": "Saker i närheten av {hotelName}",
|
||||||
"to": "till",
|
|
||||||
"Total Points": "Poäng totalt",
|
"Total Points": "Poäng totalt",
|
||||||
"Tourist": "Turist",
|
"Tourist": "Turist",
|
||||||
"Transaction date": "Transaktionsdatum",
|
"Transaction date": "Transaktionsdatum",
|
||||||
"Transactions": "Transaktioner",
|
"Transactions": "Transaktioner",
|
||||||
"Transportations": "Transport",
|
"Transportations": "Transport",
|
||||||
"Tripadvisor reviews": "{rating} ({count} recensioner på Tripadvisor)",
|
"Tripadvisor reviews": "{rating} ({count} recensioner på Tripadvisor)",
|
||||||
"TUI Points": "TUI Points",
|
|
||||||
"Type of bed": "Sängtyp",
|
"Type of bed": "Sängtyp",
|
||||||
"Type of room": "Rumstyp",
|
"Type of room": "Rumstyp",
|
||||||
"uppercase letter": "stor bokstav",
|
|
||||||
"Use bonus cheque": "Use bonus cheque",
|
"Use bonus cheque": "Use bonus cheque",
|
||||||
"User information": "Användarinformation",
|
"User information": "Användarinformation",
|
||||||
"View as list": "Visa som lista",
|
"View as list": "Visa som lista",
|
||||||
@@ -268,9 +252,9 @@
|
|||||||
"You canceled adding a new credit card.": "Du avbröt att lägga till ett nytt kreditkort.",
|
"You canceled adding a new credit card.": "Du avbröt att lägga till ett nytt kreditkort.",
|
||||||
"You have no previous stays.": "Du har inga tidigare vistelser.",
|
"You have no previous stays.": "Du har inga tidigare vistelser.",
|
||||||
"You have no upcoming stays.": "Du har inga planerade resor.",
|
"You have no upcoming stays.": "Du har inga planerade resor.",
|
||||||
|
"Your Challenges Conquer & Earn!": "Dina utmaningar Erövra och tjäna!",
|
||||||
"Your card was successfully removed!": "Ditt kort har tagits bort!",
|
"Your card was successfully removed!": "Ditt kort har tagits bort!",
|
||||||
"Your card was successfully saved!": "Ditt kort har sparats!",
|
"Your card was successfully saved!": "Ditt kort har sparats!",
|
||||||
"Your Challenges Conquer & Earn!": "Dina utmaningar Erövra och tjäna!",
|
|
||||||
"Your current level": "Din nuvarande nivå",
|
"Your current level": "Din nuvarande nivå",
|
||||||
"Your details": "Dina uppgifter",
|
"Your details": "Dina uppgifter",
|
||||||
"Your level": "Din nivå",
|
"Your level": "Din nivå",
|
||||||
@@ -278,5 +262,23 @@
|
|||||||
"Zip code": "Postnummer",
|
"Zip code": "Postnummer",
|
||||||
"Zoo": "Djurpark",
|
"Zoo": "Djurpark",
|
||||||
"Zoom in": "Zooma in",
|
"Zoom in": "Zooma in",
|
||||||
"Zoom out": "Zooma ut"
|
"Zoom out": "Zooma ut",
|
||||||
|
"as of today": "per idag",
|
||||||
|
"booking.nights": "{totalNights, plural, one {# natt} other {# nätter}}",
|
||||||
|
"by": "innan",
|
||||||
|
"characters": "tecken",
|
||||||
|
"hotelPages.rooms.roomCard.person": "person",
|
||||||
|
"hotelPages.rooms.roomCard.persons": "personer",
|
||||||
|
"hotelPages.rooms.roomCard.seeRoomDetails": "Se information om rummet",
|
||||||
|
"km to city center": "km till stadens centrum",
|
||||||
|
"next level:": "Nästa nivå:",
|
||||||
|
"night": "natt",
|
||||||
|
"nights": "nätter",
|
||||||
|
"number": "nummer",
|
||||||
|
"or": "eller",
|
||||||
|
"points": "poäng",
|
||||||
|
"special character": "speciell karaktär",
|
||||||
|
"spendable points expiring by": "{points} poäng förfaller {date}",
|
||||||
|
"to": "till",
|
||||||
|
"uppercase letter": "stor bokstav"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
export namespace endpoints {
|
export namespace endpoints {
|
||||||
export const enum v0 {
|
export const enum v0 {
|
||||||
profile = "profile/v0/Profile",
|
profile = "profile/v0/Profile",
|
||||||
availability = "availability/v0/availabilities/city",
|
|
||||||
}
|
}
|
||||||
export const enum v1 {
|
export const enum v1 {
|
||||||
|
availability = "availability/v1/availabilities/city",
|
||||||
profile = "profile/v1/Profile",
|
profile = "profile/v1/Profile",
|
||||||
booking = "booking/v1/Bookings",
|
booking = "booking/v1/Bookings",
|
||||||
creditCards = `${profile}/creditCards`,
|
creditCards = `${profile}/creditCards`,
|
||||||
|
|||||||
@@ -105,9 +105,14 @@ export const imageVaultAssetTransformedSchema = imageVaultAssetSchema.transform(
|
|||||||
const caption = rawData.Metadata?.find((meta) =>
|
const caption = rawData.Metadata?.find((meta) =>
|
||||||
meta.Name.includes("Title_")
|
meta.Name.includes("Title_")
|
||||||
)?.Value
|
)?.Value
|
||||||
|
const mediaConversion = rawData.MediaConversions[0]
|
||||||
|
const aspectRatio =
|
||||||
|
mediaConversion.FormatAspectRatio ||
|
||||||
|
mediaConversion.AspectRatio ||
|
||||||
|
mediaConversion.Width / mediaConversion.Height
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: rawData.MediaConversions[0].Url,
|
url: mediaConversion.Url,
|
||||||
id: rawData.Id,
|
id: rawData.Id,
|
||||||
meta: {
|
meta: {
|
||||||
alt,
|
alt,
|
||||||
@@ -115,9 +120,9 @@ export const imageVaultAssetTransformedSchema = imageVaultAssetSchema.transform(
|
|||||||
},
|
},
|
||||||
title: rawData.Name,
|
title: rawData.Name,
|
||||||
dimensions: {
|
dimensions: {
|
||||||
width: rawData.MediaConversions[0].Width,
|
width: mediaConversion.Width,
|
||||||
height: rawData.MediaConversions[0].Height,
|
height: mediaConversion.Height,
|
||||||
aspectRatio: rawData.MediaConversions[0].FormatAspectRatio,
|
aspectRatio,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ const parkingPricingSchema = z.object({
|
|||||||
.optional(),
|
.optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
const parkingSchema = z.object({
|
export const parkingSchema = z.object({
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
address: z.string().optional(),
|
address: z.string().optional(),
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ export const hotelQueryRouter = router({
|
|||||||
JSON.stringify({ query: { cityId, params } })
|
JSON.stringify({ query: { cityId, params } })
|
||||||
)
|
)
|
||||||
const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
`${api.endpoints.v0.availability}/${cityId}`,
|
`${api.endpoints.v1.availability}/${cityId}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||||
|
|||||||
@@ -9,19 +9,11 @@
|
|||||||
"cityName": "Stockholm",
|
"cityName": "Stockholm",
|
||||||
"ratings": {
|
"ratings": {
|
||||||
"tripAdvisor": {
|
"tripAdvisor": {
|
||||||
"numberOfReviews": 2776,
|
"numberOfReviews": 2793,
|
||||||
"rating": 4.0,
|
"rating": 4,
|
||||||
"ratingImageUrl": "https://www.tripadvisor.com/img/cdsi/img2/ratings/traveler/4.5-15458-5.svg",
|
"ratingImageUrl": "https://www.tripadvisor.com/img/cdsi/img2/ratings/traveler/4.0-15458-5.svg",
|
||||||
"webUrl": "https://www.tripadvisor.com/Hotel_Review-g189852-d229383-Reviews-Scandic_Continental-Stockholm.html",
|
"webUrl": "https://www.tripadvisor.com/Hotel_Review-g189852-d229383-Reviews-Scandic_Continental-Stockholm.html?m=15458",
|
||||||
"awards": [
|
"awards": [
|
||||||
{
|
|
||||||
"displayName": "Travelers Choice",
|
|
||||||
"images": {
|
|
||||||
"small": "https://static.tacdn.com/img2/travelers_choice/widgets/tchotel_2024_L.png",
|
|
||||||
"medium": "https://static.tacdn.com/img2/travelers_choice/widgets/tchotel_2024_L.png",
|
|
||||||
"large": "https://static.tacdn.com/img2/travelers_choice/widgets/tchotel_2024_L.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"displayName": "Travelers Choice",
|
"displayName": "Travelers Choice",
|
||||||
"images": {
|
"images": {
|
||||||
@@ -30,12 +22,7 @@
|
|||||||
"large": "https://static.tacdn.com/img2/travelers_choice/widgets/tchotel_2023_L.png"
|
"large": "https://static.tacdn.com/img2/travelers_choice/widgets/tchotel_2023_L.png"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"reviews": {
|
|
||||||
"widgetHtmlTagId": "TA_cdspropertydetail",
|
|
||||||
"widgetScriptEmbedUrlIframe": "//www.tripadvisor.com/WidgetEmbed-cdspropertydetail?locationId=12441627&partnerId=FDF3F5CC73C349C0A5AB94C0DD86BB76&lang=en&display=true",
|
|
||||||
"widgetScriptEmbedUrlJavaScript": "//www.tripadvisor.com/WidgetEmbed-cdspropertydetail?locationId=12441627&partnerId=FDF3F5CC73C349C0A5AB94C0DD86BB76&lang=en&display=false"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"address": {
|
"address": {
|
||||||
@@ -157,7 +144,7 @@
|
|||||||
"facilityInformation": "Relax with your favorite mixed drink in the bar on our roof terrace. Work out in our gym or relax in our sauna. At Scandic Continental we offer meeting rooms for up to 900 participants.",
|
"facilityInformation": "Relax with your favorite mixed drink in the bar on our roof terrace. Work out in our gym or relax in our sauna. At Scandic Continental we offer meeting rooms for up to 900 participants.",
|
||||||
"surroundingInformation": "Explore popular attractions such as the Old Town, the Opera, City Hall and Royal Palace. You are close to public transport, buses and trams and within walking distance of all that the city centre has to offer.",
|
"surroundingInformation": "Explore popular attractions such as the Old Town, the Opera, City Hall and Royal Palace. You are close to public transport, buses and trams and within walking distance of all that the city centre has to offer.",
|
||||||
"descriptions": {
|
"descriptions": {
|
||||||
"short": "Scandic Continental enjoys a fantastic location in Stockholm city centre. Close to shopping areas, culture, attractions and restaurants. ",
|
"short": "Scandic Continental enjoys a fantastic location in Stockholm city centre. Close to shopping areas, culture, attractions and restaurants.",
|
||||||
"medium": "Scandic Continental enjoys a fantastic location in Stockholm city centre. Close to shopping areas, culture, attractions and restaurants. Popular attractions such as the Old Town, the Opera, City Hall and Royal Palace are all close by."
|
"medium": "Scandic Continental enjoys a fantastic location in Stockholm city centre. Close to shopping areas, culture, attractions and restaurants. Popular attractions such as the Old Town, the Opera, City Hall and Royal Palace are all close by."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -172,190 +159,149 @@
|
|||||||
{
|
{
|
||||||
"id": 5550,
|
"id": 5550,
|
||||||
"name": "Bikes for loan",
|
"name": "Bikes for loan",
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "BikesForLoan",
|
"sortOrder": 700,
|
||||||
"sortOrder": 700
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1829,
|
"id": 1829,
|
||||||
"name": "Gym",
|
"name": "Gym",
|
||||||
"code": "HEA - TRAI",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "Gym",
|
"sortOrder": 1700,
|
||||||
"iconName": "Gym",
|
"filter": "Hotel facilities"
|
||||||
"sortOrder": 1700
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1833,
|
"id": 1833,
|
||||||
"name": "Free WiFi",
|
"name": "Free WiFi",
|
||||||
"code": "IHF",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "FreeWiFi",
|
"sortOrder": 1900,
|
||||||
"sortOrder": 1900
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1834,
|
"id": 1834,
|
||||||
"name": "Laundry service",
|
"name": "Laundry service",
|
||||||
"code": "LAU",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "LaundryService",
|
"sortOrder": 200,
|
||||||
"sortOrder": 200
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1406,
|
"id": 1406,
|
||||||
"name": "Parking - additional cost",
|
"name": "Parking - additional cost",
|
||||||
"code": "PAR",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "None",
|
"sortOrder": 0,
|
||||||
"sortOrder": 0
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2665,
|
"id": 2665,
|
||||||
"name": "Parking - garage",
|
"name": "Parking - garage",
|
||||||
"code": "GAR",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "Parking",
|
"sortOrder": 1400,
|
||||||
"iconName": "Garage",
|
"filter": "Hotel facilities"
|
||||||
"sortOrder": 1400
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1835,
|
"id": 1835,
|
||||||
"name": "Pet-friendly rooms",
|
"name": "Pet-friendly rooms",
|
||||||
"code": "PET",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "None",
|
"sortOrder": 0,
|
||||||
"sortOrder": 0
|
"filter": "Hotel facilities"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1379,
|
"id": 1379,
|
||||||
"name": "Sauna",
|
"name": "Sauna",
|
||||||
"code": "SAU - RELX",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "Sauna",
|
"sortOrder": 2000,
|
||||||
"sortOrder": 2000
|
"filter": "Hotel facilities"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1017,
|
"id": 1017,
|
||||||
"name": "Meeting rooms",
|
"name": "Meeting rooms",
|
||||||
"code": "MEE",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "Meeting",
|
"sortOrder": 9000,
|
||||||
"sortOrder": 9000
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1382,
|
"id": 1382,
|
||||||
"name": "Outdoor terrace",
|
"name": "Outdoor terrace",
|
||||||
"code": "-",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "OutdoorTerrace",
|
"sortOrder": 1000,
|
||||||
"sortOrder": 1000
|
"filter": "Hotel facilities"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1408,
|
"id": 1408,
|
||||||
"name": "Scandic Shop 24 hrs",
|
"name": "Scandic Shop 24 hrs",
|
||||||
"code": "SHOP",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "Shop",
|
"sortOrder": 100,
|
||||||
"sortOrder": 100
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1606,
|
"id": 1606,
|
||||||
"name": "Sky bar",
|
"name": "Sky bar",
|
||||||
"code": "-",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "Skybar",
|
"sortOrder": 1100,
|
||||||
"iconName": "Sky Bar / Rooftop Bar",
|
"filter": "Hotel facilities"
|
||||||
"sortOrder": 1100
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 5806,
|
"id": 5806,
|
||||||
"name": "Meeting / conference facilities",
|
"name": "Meeting / conference facilities",
|
||||||
"code": "MEE - MEETING ",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "None",
|
"sortOrder": 1500,
|
||||||
"sortOrder": 1500
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1607,
|
"id": 1607,
|
||||||
"name": "Golf course (0-30 km)",
|
"name": "Golf course (0-30 km)",
|
||||||
"code": "GOLF",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": false,
|
"public": false,
|
||||||
"icon": "None",
|
"sortOrder": 0,
|
||||||
"sortOrder": 0
|
"filter": "Hotel surroundings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 971,
|
"id": 971,
|
||||||
"name": "Shopping",
|
"name": "Shopping",
|
||||||
"code": "-",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": false,
|
"public": false,
|
||||||
"icon": "None",
|
"sortOrder": 0,
|
||||||
"sortOrder": 0
|
"filter": "Hotel surroundings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1911,
|
"id": 1911,
|
||||||
"name": "24 hours security",
|
"name": "24 hours security",
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "None",
|
"sortOrder": 0,
|
||||||
"sortOrder": 0
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1913,
|
"id": 1913,
|
||||||
"name": "Overnight security",
|
"name": "Overnight security",
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "None",
|
"sortOrder": 0,
|
||||||
"sortOrder": 0
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 162583,
|
"id": 162583,
|
||||||
"name": "Laundry service - express",
|
"name": "Laundry service - express",
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "ExpressLaundryService",
|
"sortOrder": 300,
|
||||||
"iconName": "Express dry cleaning",
|
"filter": "None"
|
||||||
"sortOrder": 300
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 229144,
|
"id": 229144,
|
||||||
"name": "TV with Chromecast",
|
"name": "TV with Chromecast",
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "None",
|
"sortOrder": 0,
|
||||||
"sortOrder": 0
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1407,
|
"id": 1407,
|
||||||
"name": "Serves breakfast (always included)",
|
"name": "Serves breakfast (always included)",
|
||||||
"code": "-",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "None",
|
"sortOrder": 0,
|
||||||
"sortOrder": 0
|
"filter": "None"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1378,
|
"id": 1378,
|
||||||
"name": "Room service",
|
"name": "Room service",
|
||||||
"code": "ROO - R/S",
|
|
||||||
"applyToAllHotels": false,
|
|
||||||
"public": true,
|
"public": true,
|
||||||
"icon": "RoomService",
|
"sortOrder": 400,
|
||||||
"sortOrder": 400
|
"filter": "None"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"healthFacilities": [
|
"healthFacilities": [
|
||||||
@@ -1067,7 +1013,617 @@
|
|||||||
"instagram": "https://www.instagram.com/scandiccontinental/",
|
"instagram": "https://www.instagram.com/scandiccontinental/",
|
||||||
"facebook": "https://www.facebook.com/scandiccontinental/"
|
"facebook": "https://www.facebook.com/scandiccontinental/"
|
||||||
},
|
},
|
||||||
"isActive": true
|
"isActive": true,
|
||||||
}
|
"gallery": {
|
||||||
|
"heroImages": [
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Superior plus, room",
|
||||||
|
"altText": "Superior plus, room",
|
||||||
|
"altText_En": "Superior plus, room",
|
||||||
|
"copyRight": "Elin Strömberg © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/5rrxa1aq23taddapu11q/scandic-continental-room-superiorplus.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/66hti5xhqzmr8jhab213/scandic-continental-room-superiorplus.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/9lplgtij1oyjwaz93lrs/scandic-continental-room-superiorplus.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/f5bdchr9zeat67jri8kw/scandic-continental-room-superiorplus.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Entrance, restaurant Market",
|
||||||
|
"altText": "Entrance, restaurant Market",
|
||||||
|
"altText_En": "Entrance, restaurant Market",
|
||||||
|
"copyRight": "Elin Sylwan © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/9qgydydjtavu128kv2xt/scandic-continental-entrance-themarket.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/0c0sz9g0r9hhejw62ez9/scandic-continental-entrance-themarket.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/ka3ku5zqp5uuqpfjddot/scandic-continental-entrance-themarket.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/ksa5jgagck9sbj2uarz5/scandic-continental-entrance-themarket.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Terrace",
|
||||||
|
"altText": "Terrace",
|
||||||
|
"altText_En": "Terrace",
|
||||||
|
"copyRight": "Björn Enström"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/m9yj4g160snutij4sivk/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/fqqakeunmamm3uetf47w/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/6fp0avu8yjrh38zv1as8/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/j8dveyubybb4f7or9qay/Scandic_Continental_Capitol_Terrace.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"smallerImages": [
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Entrance",
|
||||||
|
"altText": "Entrance from Vasagatan",
|
||||||
|
"altText_En": "Entrance from Vasagatan",
|
||||||
|
"copyRight": "Elin Sylwan © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/jlnmjycubt2ks3oiaoee/scandic-continental-entrance-vasagatan.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/izgmvkaesxw9jynunuyo/scandic-continental-entrance-vasagatan.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/sfasl29ijndz2ez6lywn/scandic-continental-entrance-vasagatan.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/9vcvf9x9exykkl2yf0bg/scandic-continental-entrance-vasagatan.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Standard room",
|
||||||
|
"altText": "Standard room",
|
||||||
|
"altText_En": "Standard room",
|
||||||
|
"copyRight": "Elin Strömberg © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/qj2en3cqvvadxe5y5b6y/scandic-continental-room-standard.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/16kz0cyxngrwe7880gjh/scandic-continental-room-standard.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/x8r0of336j2rsrhvwlqy/scandic-continental-room-standard.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/co3h6nr4p28j2tn641sz/scandic-continental-room-standard.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Junior suite",
|
||||||
|
"altText": "Junior suite, detail",
|
||||||
|
"altText_En": "Junior suite, detail",
|
||||||
|
"copyRight": "Elin Strömberg © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/tijveokv1qmnmy964iv9/scandic-continental-room-juniorsuite-detail-1.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/i7h3u2x6a9ta46jxb2tg/scandic-continental-room-juniorsuite-detail-1.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/hv4on6ec8qlcxmcd0enl/scandic-continental-room-juniorsuite-detail-1.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/3eakprn9n0zwyjpzpy6p/scandic-continental-room-juniorsuite-detail-1.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Junior Suite",
|
||||||
|
"altText": "Junior Suite",
|
||||||
|
"altText_En": "Junior Suite",
|
||||||
|
"copyRight": "Elin Strömberg © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/7h4bonsezjsjz1lpg6hd/scandic-continental-room-juniorsuite.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/gge47e5ach2e0kuakggx/scandic-continental-room-juniorsuite.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/cts4y9u9w02ockyyoze7/scandic-continental-room-juniorsuite.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/f8aojtx38vb7ywv33cf6/scandic-continental-room-juniorsuite.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Superior plus, room",
|
||||||
|
"altText": "Superior plus, room",
|
||||||
|
"altText_En": "Superior plus, room",
|
||||||
|
"copyRight": "Elin Strömberg © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/5rrxa1aq23taddapu11q/scandic-continental-room-superiorplus.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/66hti5xhqzmr8jhab213/scandic-continental-room-superiorplus.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/9lplgtij1oyjwaz93lrs/scandic-continental-room-superiorplus.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/f5bdchr9zeat67jri8kw/scandic-continental-room-superiorplus.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Superior room, detail",
|
||||||
|
"altText": "Superior room, detail",
|
||||||
|
"altText_En": "Superior room, detail",
|
||||||
|
"copyRight": "Elin Strömberg © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/6jjitns74k9nutn9v9tz/scandic-continental-room-superior-detail.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/ft3se6waag20lx6hxtn9/scandic-continental-room-superior-detail.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/wsfl5usd8qbdm929dvxu/scandic-continental-room-superior-detail.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/duedba0io5xew6almv7l/scandic-continental-room-superior-detail.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Junior suite, bathroom",
|
||||||
|
"altText": "Junior suite, bathroom",
|
||||||
|
"altText_En": "Junior suite, bathroom",
|
||||||
|
"copyRight": "Elin Strömberg © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/2cpdxrywepicqnp9eoob/scandic-continental-room-juniorsuite-bathroom-1.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/ynkzimfaldgef0ukj7q5/scandic-continental-room-juniorsuite-bathroom-1.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/jrhqe8vbm91vegd02bjh/scandic-continental-room-juniorsuite-bathroom-1.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/93ag11udgjefz6qwa8st/scandic-continental-room-juniorsuite-bathroom-1.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Entrance, restaurant Market",
|
||||||
|
"altText": "Entrance, restaurant Market",
|
||||||
|
"altText_En": "Entrance, restaurant Market",
|
||||||
|
"copyRight": "Elin Sylwan © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/9qgydydjtavu128kv2xt/scandic-continental-entrance-themarket.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/0c0sz9g0r9hhejw62ez9/scandic-continental-entrance-themarket.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/ka3ku5zqp5uuqpfjddot/scandic-continental-entrance-themarket.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/ksa5jgagck9sbj2uarz5/scandic-continental-entrance-themarket.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Dining room, the Market",
|
||||||
|
"altText": "Dining room, the Market",
|
||||||
|
"altText_En": "Dining room, the Market",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/bywlg4nwp11hmebfb1wg/scandic-continental-diningroom-themarket.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/fdvp429hm1rfpg2le6jo/scandic-continental-diningroom-themarket.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/wm24ma9ngk8gijotm7va/scandic-continental-diningroom-themarket.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/7xiz9p0f254yl9oef7bj/scandic-continental-diningroom-themarket.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Dining room, the Market",
|
||||||
|
"altText": "Dining room, the Market",
|
||||||
|
"altText_En": "Dining room, the Market",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/zd9rkkm383ctblciw0np/scandic-continental-diningroom-themarket-2.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/i9giiq3ijihm172b5bbw/scandic-continental-diningroom-themarket-2.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/9baixkat8wjdsjn1lo34/scandic-continental-diningroom-themarket-2.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/kwemrr8fohnnwo0uzd3s/scandic-continental-diningroom-themarket-2.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Restaurant, the Market, detail",
|
||||||
|
"altText": "Restaurant, the Market, detail",
|
||||||
|
"altText_En": "Restaurant, the Market, detail",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/uo7e32pi5cmf06as55ad/scandic-continental-themarket-detail.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/1qhpesv8d407j00b1y5x/scandic-continental-themarket-detail.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/ldenlssse8595b4iavjj/scandic-continental-themarket-detail.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/yhs5jgy8mgw9xvzazjze/scandic-continental-themarket-detail.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Breakfast, the Market",
|
||||||
|
"altText": "Breakfast, the Market",
|
||||||
|
"altText_En": "Breakfast, the Market",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/vsj1725jfae5eqx9urua/scandic-continental-breakfast-themarket-15.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/ajqxe5obci19sw4g08ub/scandic-continental-breakfast-themarket-15.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/wi3xa47bwtb5s5kbaxq8/scandic-continental-breakfast-themarket-15.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/1igemz65ffu4nw7bg64l/scandic-continental-breakfast-themarket-15.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Breakfast, the Market",
|
||||||
|
"altText": "Breakfast, the Market",
|
||||||
|
"altText_En": "Breakfast, the Market",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/1clcvo8e86itpus3xy99/scandic-continental-breakfast-themarket-9.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/on5ckcmwxvs47l11natn/scandic-continental-breakfast-themarket-9.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/l5g6dmumzh9duseaa9zw/scandic-continental-breakfast-themarket-9.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/pl45n94xmu19bo0fokwf/scandic-continental-breakfast-themarket-9.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Restaurant Caldo",
|
||||||
|
"altText": "Restaurant Caldo",
|
||||||
|
"altText_En": "Restaurant Caldo",
|
||||||
|
"copyRight": "Elin Sylwan © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/q5cj5qehiq03ey38ppen/scandic-continental-caldo.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/na08m2sippkfzd3yf9le/scandic-continental-caldo.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/wxh6w02ujd5wqyr9bvg8/scandic-continental-caldo.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/bem9c6dxappvlwxg7s1g/scandic-continental-caldo.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Roof top bar",
|
||||||
|
"altText": "Roof top bar at Scandic Continental",
|
||||||
|
"altText_En": "Roof top bar at Scandic Continental",
|
||||||
|
"copyRight": "Scandic"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/cxf1z9sgr9152ls1kdmg/Scandic_Continental_Capitol_The_View_13.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/pd43w13z5dx8l7kzhsyg/Scandic_Continental_Capitol_The_View_13.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/7ryqztmdqg0mam62oyf5/Scandic_Continental_Capitol_The_View_13.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/hwpsq70dbln0l052cjqa/Scandic_Continental_Capitol_The_View_13.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Roof top bar",
|
||||||
|
"altText": "Roof top bar at Scandic Continental",
|
||||||
|
"altText_En": "Roof top bar at Scandic Continental",
|
||||||
|
"copyRight": "Scandic"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/hd1wk1wvtwsz4fuvf0rb/Scandic_Continental_Capitol_The_View_6.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/ioi4ir6ijpy0f5dvu56n/Scandic_Continental_Capitol_The_View_6.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/ydbhbjj3ulju2bxh7ebj/Scandic_Continental_Capitol_The_View_6.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/kom0ttrq6xazxxuodrr1/Scandic_Continental_Capitol_The_View_6.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Terrace",
|
||||||
|
"altText": "Terrace",
|
||||||
|
"altText_En": "Terrace",
|
||||||
|
"copyRight": "Björn Enström"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/m9yj4g160snutij4sivk/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/fqqakeunmamm3uetf47w/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/6fp0avu8yjrh38zv1as8/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/j8dveyubybb4f7or9qay/Scandic_Continental_Capitol_Terrace.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Terrace",
|
||||||
|
"altText": "Terrace",
|
||||||
|
"altText_En": "Terrace",
|
||||||
|
"copyRight": "Björn Enström"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/dsm1ief68fx4b6j5673x/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/pm1lxtm8zf1ejd0qtko4/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/wwgkqa2fr5z223ybdemr/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/aztwaanmwiwk5s0tc36w/Scandic_Continental_Capitol_Terrace.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Rooftop terrace",
|
||||||
|
"altText": "Rooftop terrace",
|
||||||
|
"altText_En": "Rooftop terrace",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/m4mhmwyynuf65ls1cybu/scandic-continental-rooftop-terrace-capital-4.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/phsnpd122oq7e6hszsc7/scandic-continental-rooftop-terrace-capital-4.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/ty9gb8ogkq5iqszke7pe/scandic-continental-rooftop-terrace-capital-4.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/ug0co0ks7v25avze1vi3/scandic-continental-rooftop-terrace-capital-4.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "View",
|
||||||
|
"altText": "View",
|
||||||
|
"altText_En": "View",
|
||||||
|
"copyRight": "Elin Strömberg © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/gs1lqt13ptscg4audxgo/scandic-continental-theview-detail-1.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/otr9i3f4q8084ggfwnm1/scandic-continental-theview-detail-1.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/w8pkgf9igqibmlzp9egl/scandic-continental-theview-detail-1.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/9yujqdk34x6juk6fstks/scandic-continental-theview-detail-1.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "View",
|
||||||
|
"altText": "View",
|
||||||
|
"altText_En": "View",
|
||||||
|
"copyRight": "Elin Strömberg © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/autuf074xvc2xr3zmxyf/scandic-continental-theview-detail-2.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/hyf3fjkreqw2rlx4wm4e/scandic-continental-theview-detail-2.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/3wezw0ftkli48oloekdb/scandic-continental-theview-detail-2.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/ygxz2q45b4gcgscqdzaw/scandic-continental-theview-detail-2.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Work out equipment at the terrace",
|
||||||
|
"altText": "Work out equipment at the terrace",
|
||||||
|
"altText_En": "Work out equipment at the terrace",
|
||||||
|
"copyRight": "Scandic"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/oe32sgg1pudiwfd9hjpa/Scandic_Continental_Terrace_Workout_1.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/1g9d62gqkyvfky85kgag/Scandic_Continental_Terrace_Workout_1.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/eb9n7j8s9scwg89ac4lk/Scandic_Continental_Terrace_Workout_1.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/cyrtt6bmy11y1wti2d9h/Scandic_Continental_Terrace_Workout_1.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Meeting room",
|
||||||
|
"altText": "",
|
||||||
|
"altText_En": "",
|
||||||
|
"copyRight": "Elin Sylwan © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/zbc3bhdzs59sqsvm9v29/scandic-continental-conference-9.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/m2nh4f57ys3bcxuq27hb/scandic-continental-conference-9.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/wk39k84g3nrivntzuww2/scandic-continental-conference-9.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/yn1mzw6nbyl90dreiz6b/scandic-continental-conference-9.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Meeting room",
|
||||||
|
"altText": "",
|
||||||
|
"altText_En": "",
|
||||||
|
"copyRight": "Elin Sylwan © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/mfq1ayjplo7keqxm88ng/scandic-continental-conference-3.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/wzjl4n8qk4imfgla7r73/scandic-continental-conference-3.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/wf6lg2u4bz1f9ntkszuz/scandic-continental-conference-3.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/uh83sqrope55u6q4hbhl/scandic-continental-conference-3.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Meeting room ",
|
||||||
|
"altText": "Meeting room ",
|
||||||
|
"altText_En": "Meeting room ",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/z00ei7j974hx7c9vp8a6/Scandic_Continental_meetingroom_15-16-17-18-0357.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/v64n5ne3ayfo8v3q9x2s/Scandic_Continental_meetingroom_15-16-17-18-0357.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/mtx54l82hr3o3m7k0vqi/Scandic_Continental_meetingroom_15-16-17-18-0357.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/3xr7ovgdjxq5taz8nzma/Scandic_Continental_meetingroom_15-16-17-18-0357.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Breakout area ",
|
||||||
|
"altText": "Breakout area ",
|
||||||
|
"altText_En": "Breakout area ",
|
||||||
|
"copyRight": "Karl Gabor "
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/e84l4tyafvmx6epe0c3d/Scandic_Continental_Breakout_3-0432.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/60im9a4yre1kg5me9riz/Scandic_Continental_Breakout_3-0432.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/nsfh80un5l7qv9y9i1iw/Scandic_Continental_Breakout_3-0432.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/pxe0dozr1joaqig3cbf2/Scandic_Continental_Breakout_3-0432.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Breakout area ",
|
||||||
|
"altText": "Breakout area ",
|
||||||
|
"altText_En": "Breakout area ",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/fpyspgc9op7wb4ay3083/Scandic_Continental_breakout2-0391.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/2ueu1qe77bdqkj682xvq/Scandic_Continental_breakout2-0391.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/xmaaxzi1qrlwkm2gbl0z/Scandic_Continental_breakout2-0391.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/px12tarpgqq31cuz512z/Scandic_Continental_breakout2-0391.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Meeting room ",
|
||||||
|
"altText": "Meeting room ",
|
||||||
|
"altText_En": "Meeting room ",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/ssrltn03vl7layrr5wgk/Scandic_Continental_Meetingroom_15_16_17_18.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/jozqcjuo1dpd9gyqjcyo/Scandic_Continental_Meetingroom_15_16_17_18.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/vdinjqa1j09av33z6wss/Scandic_Continental_Meetingroom_15_16_17_18.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/gq5lhbwrjvvo6otkgi9z/Scandic_Continental_Meetingroom_15_16_17_18.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Gym",
|
||||||
|
"altText": "Gym of Scandic Continental in Stockholm",
|
||||||
|
"altText_En": "Gym of Scandic Continental in Stockholm",
|
||||||
|
"copyRight": "Elin Sylwan © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/ic6077yovsoxdmj9545r/scandic_continental_gym.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/uhhqrnb8wzy9xlr42zn8/scandic_continental_gym.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/iqor5zd7owsll42z59vh/scandic_continental_gym.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/5v5o0bbopf9t7r95sp2o/scandic_continental_gym.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"conferencesAndMeetings": {
|
||||||
|
"headingText": "Meetings, conferences & events",
|
||||||
|
"pageUrl": "https://test3.scandichotels.com/hotels/sweden/stockholm/scandic-continental/meetings-conferences-events",
|
||||||
|
"heroImages": [
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Roof top bar",
|
||||||
|
"altText": "Roof bar of Scandic Continental in Stockholm",
|
||||||
|
"altText_En": "Roof bar of Scandic Continental in Stockholm",
|
||||||
|
"copyRight": "Scandic"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/086xasvs4lq29x4oqvbq/Scandic_Continental_Capitol_The_View_2.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/rgrcj8ed9mhecp6hpcuq/Scandic_Continental_Capitol_The_View_2.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/gklysd2pkx9gi0nx1mqu/Scandic_Continental_Capitol_The_View_2.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/sjkd7r6mw6xxhwzkkqvk/Scandic_Continental_Capitol_The_View_2.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"healthAndWellness": {
|
||||||
|
"headingText": "Gym and health",
|
||||||
|
"heroImages": [
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Gym",
|
||||||
|
"altText": "Gym of Scandic Continental in Stockholm",
|
||||||
|
"altText_En": "Gym of Scandic Continental in Stockholm",
|
||||||
|
"copyRight": "Elin Sylwan © 2016"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/ic6077yovsoxdmj9545r/scandic_continental_gym.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/uhhqrnb8wzy9xlr42zn8/scandic_continental_gym.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/iqor5zd7owsll42z59vh/scandic_continental_gym.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/5v5o0bbopf9t7r95sp2o/scandic_continental_gym.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Work out equipment at the terrace",
|
||||||
|
"altText": "Work out equipment at the terrace",
|
||||||
|
"altText_En": "Work out equipment at the terrace",
|
||||||
|
"copyRight": "Scandic"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/oe32sgg1pudiwfd9hjpa/Scandic_Continental_Terrace_Workout_1.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/1g9d62gqkyvfky85kgag/Scandic_Continental_Terrace_Workout_1.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/eb9n7j8s9scwg89ac4lk/Scandic_Continental_Terrace_Workout_1.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/cyrtt6bmy11y1wti2d9h/Scandic_Continental_Terrace_Workout_1.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"accessibilityElevatorPitchText": "Find the information you might need, before visiting us. You are always welcome to our hotel - completely without barriers. Regardless of impairment, sight, hearing, allergies or wheelchair, we have made sure that you enjoy your stay.",
|
||||||
|
"merchantInformationData": {
|
||||||
|
"webMerchantId": "1110009031",
|
||||||
|
"cards": {
|
||||||
|
"americanExpress": true,
|
||||||
|
"dankort": false,
|
||||||
|
"dinersClub": true,
|
||||||
|
"jcb": true,
|
||||||
|
"masterCard": true,
|
||||||
|
"visa": true,
|
||||||
|
"maestro": false,
|
||||||
|
"chinaUnionPay": true,
|
||||||
|
"discover": true
|
||||||
|
},
|
||||||
|
"alternatePaymentOptions": {
|
||||||
|
"swish": true,
|
||||||
|
"vipps": false,
|
||||||
|
"mobilePay": true,
|
||||||
|
"applePay": true,
|
||||||
|
"alipayPlus": false,
|
||||||
|
"googlePay": true,
|
||||||
|
"klarna": false,
|
||||||
|
"payPal": false,
|
||||||
|
"weChatPay": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"restaurantImages": {
|
||||||
|
"headingText": "Bar and breakfast",
|
||||||
|
"heroImages": [
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Dining room, the Market",
|
||||||
|
"altText": "Dining room, the Market",
|
||||||
|
"altText_En": "Dining room, the Market",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/bywlg4nwp11hmebfb1wg/scandic-continental-diningroom-themarket.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/fdvp429hm1rfpg2le6jo/scandic-continental-diningroom-themarket.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/wm24ma9ngk8gijotm7va/scandic-continental-diningroom-themarket.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/7xiz9p0f254yl9oef7bj/scandic-continental-diningroom-themarket.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Rooftop bar",
|
||||||
|
"altText": "Rooftop bar",
|
||||||
|
"altText_En": "Rooftop bar",
|
||||||
|
"copyRight": "Karl Gabor"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/ntwor0b4i90sp9sh5qbj/scandic-continental-rooftopbar-capital.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/ivxowx9eunbyz68qkstd/scandic-continental-rooftopbar-capital.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/6pi2262z6lo0un7x7iik/scandic-continental-rooftopbar-capital.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/6rlwgo7lq198xg7ifii6/scandic-continental-rooftopbar-capital.jpg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metaData": {
|
||||||
|
"title": "Terrace",
|
||||||
|
"altText": "Bar of Scandic Continental in Stockholm",
|
||||||
|
"altText_En": "Bar of Scandic Continental in Stockholm",
|
||||||
|
"copyRight": "Björn Enström"
|
||||||
|
},
|
||||||
|
"imageSizes": {
|
||||||
|
"tiny": "https://test3.scandichotels.com/imagevault/publishedmedia/69jl573jtmw8g4jjtugo/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"small": "https://test3.scandichotels.com/imagevault/publishedmedia/td5tz4ld37kwws6rpnye/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"medium": "https://test3.scandichotels.com/imagevault/publishedmedia/mlrdylyxf3m9fr5r9l7x/Scandic_Continental_Capitol_Terrace.jpg",
|
||||||
|
"large": "https://test3.scandichotels.com/imagevault/publishedmedia/dywcmnaamp0altjge9qx/Scandic_Continental_Capitol_Terrace.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relationships": {
|
||||||
|
"restaurants": {
|
||||||
|
"links": {
|
||||||
|
"related": "http://tstapi.scandichotels.com/hotel/v1/Hotels/811/restaurants?language=En"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nearbyHotels": {
|
||||||
|
"links": {
|
||||||
|
"related": "http://tstapi.scandichotels.com/hotel/v1/Hotels/811/nearbyHotels?language=En"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"roomCategories": {
|
||||||
|
"links": {
|
||||||
|
"related": "http://tstapi.scandichotels.com/hotel/v1/Hotels/811/roomCategories?language=En"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meetingRooms": {
|
||||||
|
"links": {
|
||||||
|
"related": "http://tstapi.scandichotels.com/hotel/v1/Hotels/811/meetingRooms?language=En"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"merchantInformation": {
|
||||||
|
"links": {
|
||||||
|
"related": "http://tstapi.scandichotels.com/hotel/v1/Hotels/811/merchantInformation?language=En"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "811",
|
||||||
|
"language": "En",
|
||||||
|
"type": "hotels"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export async function fetchServiceToken(
|
|||||||
grant_type: "client_credentials",
|
grant_type: "client_credentials",
|
||||||
client_id: env.CURITY_CLIENT_ID_SERVICE,
|
client_id: env.CURITY_CLIENT_ID_SERVICE,
|
||||||
client_secret: env.CURITY_CLIENT_SECRET_SERVICE,
|
client_secret: env.CURITY_CLIENT_SECRET_SERVICE,
|
||||||
scope: scopes.join(","),
|
scope: scopes.join(" "),
|
||||||
}),
|
}),
|
||||||
next: {
|
next: {
|
||||||
revalidate: SERVICE_TOKEN_REVALIDATE_SECONDS,
|
revalidate: SERVICE_TOKEN_REVALIDATE_SECONDS,
|
||||||
|
|||||||
@@ -2,4 +2,14 @@ import { z } from "zod"
|
|||||||
|
|
||||||
import { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema"
|
import { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema"
|
||||||
|
|
||||||
|
import type { Locations } from "@/types/trpc/routers/hotel/locations"
|
||||||
|
|
||||||
export type BookingWidgetSchema = z.output<typeof bookingWidgetSchema>
|
export type BookingWidgetSchema = z.output<typeof bookingWidgetSchema>
|
||||||
|
|
||||||
|
export interface BookingWidgetClientProps {
|
||||||
|
locations: Locations
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BookingWidgetToggleButtonProps {
|
||||||
|
openMobileSearch: () => void
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
|
import type { Locale } from "date-fns"
|
||||||
|
import type { DateRange } from "react-day-picker"
|
||||||
|
|
||||||
export interface DatePickerFormProps {
|
export interface DatePickerFormProps {
|
||||||
name?: string
|
name?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LangWithoutEn = Lang.da | Lang.de | Lang.fi | Lang.no | Lang.sv
|
||||||
|
|
||||||
|
export interface DatePickerProps {
|
||||||
|
close: () => void
|
||||||
|
handleOnSelect: (selected: DateRange) => void
|
||||||
|
locales: Record<LangWithoutEn, Locale>
|
||||||
|
selectedDate: DateRange
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
import { ReactElement } from "react"
|
||||||
|
|
||||||
import type { Coordinates } from "@/types/components/maps/coordinates"
|
import type { Coordinates } from "@/types/components/maps/coordinates"
|
||||||
import type { PointOfInterest } from "@/types/hotel"
|
import type { PointOfInterest } from "@/types/hotel"
|
||||||
|
|
||||||
export interface MapContentProps {
|
export interface InteractiveMapProps {
|
||||||
coordinates: Coordinates
|
coordinates: Coordinates
|
||||||
pointsOfInterest: PointOfInterest[]
|
pointsOfInterest: PointOfInterest[]
|
||||||
activePoi: PointOfInterest["name"] | null
|
activePoi: PointOfInterest["name"] | null
|
||||||
mapId: string
|
mapId: string
|
||||||
onActivePoiChange: (poi: PointOfInterest["name"] | null) => void
|
onActivePoiChange: (poi: PointOfInterest["name"] | null) => void
|
||||||
|
closeButton: ReactElement
|
||||||
}
|
}
|
||||||
@@ -3,3 +3,11 @@ import { Hotel } from "@/types/hotel"
|
|||||||
export type HotelFiltersProps = {
|
export type HotelFiltersProps = {
|
||||||
filters: Hotel["detailedFacilities"]
|
filters: Hotel["detailedFacilities"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Filter = {
|
||||||
|
name: string
|
||||||
|
id: number
|
||||||
|
public: boolean
|
||||||
|
sortOrder: number
|
||||||
|
filter?: string
|
||||||
|
}
|
||||||
15
types/components/hotelReservation/selectHotel/map.ts
Normal file
15
types/components/hotelReservation/selectHotel/map.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Coordinates } from "@/types/components/maps/coordinates"
|
||||||
|
import type { PointOfInterest } from "@/types/hotel"
|
||||||
|
|
||||||
|
export interface HotelListingProps {
|
||||||
|
// pointsOfInterest: PointOfInterest[]
|
||||||
|
// activePoi: PointOfInterest["name"] | null
|
||||||
|
// onActivePoiChange: (poi: PointOfInterest["name"] | null) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectHotelMapProps {
|
||||||
|
apiKey: string
|
||||||
|
coordinates: Coordinates
|
||||||
|
pointsOfInterest: PointOfInterest[]
|
||||||
|
mapId: string
|
||||||
|
}
|
||||||
@@ -1,4 +1,25 @@
|
|||||||
|
import { Hotel, ParkingData } from "@/types/hotel"
|
||||||
|
|
||||||
export enum AvailabilityEnum {
|
export enum AvailabilityEnum {
|
||||||
Available = "Available",
|
Available = "Available",
|
||||||
NotAvailable = "NotAvailable",
|
NotAvailable = "NotAvailable",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DetailedAmenity {
|
||||||
|
name: string
|
||||||
|
heading: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReadMoreProps {
|
||||||
|
hotelId: string
|
||||||
|
hotel: Hotel
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContactProps {
|
||||||
|
hotel: Hotel
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ParkingProps {
|
||||||
|
parking: ParkingData
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Hotel } from "@/types/hotel"
|
||||||
|
|
||||||
|
export type HotelSelectionHeaderProps = {
|
||||||
|
hotel: Hotel
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ export interface LanguageSwitcherProps {
|
|||||||
|
|
||||||
export interface LanguageSwitcherContentProps {
|
export interface LanguageSwitcherContentProps {
|
||||||
urls: LanguageSwitcherData
|
urls: LanguageSwitcherData
|
||||||
|
onLanguageSwitch: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LanguageSwitcherContainerProps {
|
export interface LanguageSwitcherContainerProps {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { z } from "zod"
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
getHotelDataSchema,
|
getHotelDataSchema,
|
||||||
|
parkingSchema,
|
||||||
pointOfInterestSchema,
|
pointOfInterestSchema,
|
||||||
roomSchema,
|
roomSchema,
|
||||||
} from "@/server/routers/hotels/output"
|
} from "@/server/routers/hotels/output"
|
||||||
@@ -49,3 +50,5 @@ export enum PointOfInterestGroupEnum {
|
|||||||
PARKING = "Parking",
|
PARKING = "Parking",
|
||||||
SHOPPING_DINING = "Shopping & Dining",
|
SHOPPING_DINING = "Shopping & Dining",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ParkingData = z.infer<typeof parkingSchema>
|
||||||
|
|||||||
@@ -1,53 +1,21 @@
|
|||||||
import type { ButtonProps } from "@/components/TempDesignSystem/Button/button"
|
import type { ButtonProps } from "@/components/TempDesignSystem/Button/button"
|
||||||
import type { CardProps } from "@/components/TempDesignSystem/Card/card"
|
import type { CardProps } from "@/components/TempDesignSystem/Card/card"
|
||||||
import type { LinkProps } from "@/components/TempDesignSystem/Link/link"
|
|
||||||
|
|
||||||
export function getTheme(theme: CardProps["theme"]) {
|
|
||||||
let buttonTheme: ButtonProps["theme"] = "primaryLight"
|
|
||||||
let primaryLinkColor: LinkProps["color"] = "pale"
|
|
||||||
let secondaryLinkColor: LinkProps["color"] = "burgundy"
|
|
||||||
|
|
||||||
|
export function getTheme(theme: CardProps["theme"]): ButtonProps["theme"] {
|
||||||
switch (theme) {
|
switch (theme) {
|
||||||
case "one":
|
|
||||||
buttonTheme = "primaryLight"
|
|
||||||
primaryLinkColor = "pale"
|
|
||||||
secondaryLinkColor = "burgundy"
|
|
||||||
break
|
|
||||||
case "two":
|
case "two":
|
||||||
buttonTheme = "secondaryLight"
|
return "secondaryLight"
|
||||||
primaryLinkColor = "pale"
|
|
||||||
secondaryLinkColor = "burgundy"
|
|
||||||
break
|
|
||||||
case "three":
|
case "three":
|
||||||
buttonTheme = "tertiaryLight"
|
return "tertiaryLight"
|
||||||
primaryLinkColor = "pale"
|
|
||||||
secondaryLinkColor = "burgundy"
|
|
||||||
break
|
|
||||||
case "primaryDark":
|
case "primaryDark":
|
||||||
buttonTheme = "primaryDark"
|
return "primaryDark"
|
||||||
primaryLinkColor = "burgundy"
|
|
||||||
secondaryLinkColor = "pale"
|
|
||||||
break
|
|
||||||
case "primaryDim":
|
|
||||||
buttonTheme = "primaryLight"
|
|
||||||
primaryLinkColor = "pale"
|
|
||||||
secondaryLinkColor = "burgundy"
|
|
||||||
break
|
|
||||||
case "primaryInverted":
|
|
||||||
buttonTheme = "primaryLight"
|
|
||||||
primaryLinkColor = "pale"
|
|
||||||
secondaryLinkColor = "burgundy"
|
|
||||||
break
|
|
||||||
case "primaryStrong":
|
case "primaryStrong":
|
||||||
case "image":
|
case "image":
|
||||||
buttonTheme = "primaryStrong"
|
return "primaryStrong"
|
||||||
primaryLinkColor = "red"
|
case "one":
|
||||||
secondaryLinkColor = "white"
|
case "primaryDim":
|
||||||
}
|
case "primaryInverted":
|
||||||
|
default:
|
||||||
return {
|
return "primaryLight"
|
||||||
buttonTheme: buttonTheme,
|
|
||||||
primaryLinkColor: primaryLinkColor,
|
|
||||||
secondaryLinkColor: secondaryLinkColor,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
utils/debounce.ts
Normal file
10
utils/debounce.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export function debounce(func: Function, delay = 300) {
|
||||||
|
let debounceTimer: ReturnType<typeof setTimeout>
|
||||||
|
return function () {
|
||||||
|
// @ts-expect-error this in TypeScript
|
||||||
|
const context = this
|
||||||
|
const args = arguments
|
||||||
|
clearTimeout(debounceTimer)
|
||||||
|
debounceTimer = setTimeout(() => func.apply(context, args), delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,8 +14,14 @@ export function insertResponseToImageVaultAsset(
|
|||||||
meta.Name.includes("Title_")
|
meta.Name.includes("Title_")
|
||||||
)?.Value
|
)?.Value
|
||||||
|
|
||||||
|
const mediaConversion = response.MediaConversions[0]
|
||||||
|
const aspectRatio =
|
||||||
|
mediaConversion.FormatAspectRatio ||
|
||||||
|
mediaConversion.AspectRatio ||
|
||||||
|
mediaConversion.Width / mediaConversion.Height
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: response.MediaConversions[0].Url,
|
url: mediaConversion.Url,
|
||||||
id: response.Id,
|
id: response.Id,
|
||||||
meta: {
|
meta: {
|
||||||
alt,
|
alt,
|
||||||
@@ -23,9 +29,9 @@ export function insertResponseToImageVaultAsset(
|
|||||||
},
|
},
|
||||||
title: response.Name,
|
title: response.Name,
|
||||||
dimensions: {
|
dimensions: {
|
||||||
width: response.MediaConversions[0].Width,
|
width: mediaConversion.Width,
|
||||||
height: response.MediaConversions[0].Height,
|
height: mediaConversion.Height,
|
||||||
aspectRatio: response.MediaConversions[0].FormatAspectRatio,
|
aspectRatio,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user