fix(BOOK-418): Refactored StandaloneHotelCardDialog and fixed pricing issue when using redemption or booking codes
Approved-by: Bianca Widstam
This commit is contained in:
25
packages/common/hooks/useUrlWithSearchParam.ts
Normal file
25
packages/common/hooks/useUrlWithSearchParam.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { usePathname, useSearchParams } from "next/navigation"
|
||||||
|
import { useMemo } from "react"
|
||||||
|
|
||||||
|
export function useUrlWithSearchParam(
|
||||||
|
searchParam: { key: string; value: string },
|
||||||
|
path?: string | null
|
||||||
|
) {
|
||||||
|
const { key, value } = searchParam
|
||||||
|
const pathname = path ?? usePathname()
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
const newUrl = new URL(pathname, window.location.origin)
|
||||||
|
|
||||||
|
// Preserve existing search params
|
||||||
|
searchParams.forEach((val, key) => {
|
||||||
|
newUrl.searchParams.set(key, val)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Set or override the specific search param
|
||||||
|
newUrl.searchParams.set(key, value)
|
||||||
|
|
||||||
|
return newUrl
|
||||||
|
}, [pathname, searchParams, key, value])
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ export function HotelCardDialogImage({
|
|||||||
src={imageSrc}
|
src={imageSrc}
|
||||||
alt={altText || ''}
|
alt={altText || ''}
|
||||||
fill
|
fill
|
||||||
|
sizes={position === 'top' ? '200px' : '450px'}
|
||||||
onError={() => setImageError(true)}
|
onError={() => setImageError(true)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
||||||
import { StandaloneHotelCardDialog } from './index'
|
import { StandaloneHotelCardDialog } from './index'
|
||||||
|
|
||||||
|
import { Lang } from '@scandic-hotels/common/constants/language'
|
||||||
import { fn } from 'storybook/test'
|
import { fn } from 'storybook/test'
|
||||||
|
import { hotelPins } from '../../../Map/InteractiveMap/storybookData'
|
||||||
|
|
||||||
const meta: Meta<typeof StandaloneHotelCardDialog> = {
|
const meta: Meta<typeof StandaloneHotelCardDialog> = {
|
||||||
title: 'Components/StandaloneHotelCardDialog',
|
title: 'Components/StandaloneHotelCardDialog',
|
||||||
@@ -15,29 +17,10 @@ type Story = StoryObj<typeof StandaloneHotelCardDialog>
|
|||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
args: {
|
args: {
|
||||||
data: {
|
lang: Lang.en,
|
||||||
name: 'Hotel Name',
|
isUserLoggedIn: false,
|
||||||
image: {
|
onClick: fn(),
|
||||||
url: 'img/img2.jpg',
|
data: hotelPins[0],
|
||||||
alt: 'Alt text',
|
|
||||||
},
|
|
||||||
coordinates: {
|
|
||||||
lat: 0,
|
|
||||||
lng: 0,
|
|
||||||
},
|
|
||||||
chequePrice: null,
|
|
||||||
publicPrice: 100,
|
|
||||||
memberPrice: 200,
|
|
||||||
redemptionPrice: null,
|
|
||||||
voucherPrice: null,
|
|
||||||
rateType: null,
|
|
||||||
currency: 'SEK',
|
|
||||||
amenities: [],
|
|
||||||
ratings: { tripAdvisor: 5 },
|
|
||||||
operaId: '123',
|
|
||||||
facilityIds: [],
|
|
||||||
hasEnoughPoints: false,
|
|
||||||
},
|
|
||||||
handleClose: fn(),
|
handleClose: fn(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,30 +3,27 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useIntl } from 'react-intl'
|
import { useIntl } from 'react-intl'
|
||||||
|
|
||||||
import { selectRate } from '@scandic-hotels/common/constants/routes/hotelReservation'
|
|
||||||
import Body from '../../../Body'
|
|
||||||
import Caption from '../../../Caption'
|
|
||||||
import Footnote from '../../../Footnote'
|
|
||||||
import { IconButton } from '../../../IconButton'
|
import { IconButton } from '../../../IconButton'
|
||||||
import { MaterialIcon } from '../../../Icons/MaterialIcon'
|
import { MaterialIcon } from '../../../Icons/MaterialIcon'
|
||||||
import Link from '../../../Link'
|
|
||||||
import { OldDSButton as Button } from '../../../OldDSButton'
|
|
||||||
import Subtitle from '../../../Subtitle'
|
|
||||||
import { Typography } from '../../../Typography'
|
import { Typography } from '../../../Typography'
|
||||||
|
|
||||||
import { HotelCardDialogImage } from '../../HotelCardDialogImage'
|
import { HotelCardDialogImage } from '../../HotelCardDialogImage'
|
||||||
import { NoPriceAvailableCard } from '../../NoPriceAvailableCard'
|
import { NoPriceAvailableCard } from '../../NoPriceAvailableCard'
|
||||||
|
|
||||||
|
import { CurrencyEnum } from '@scandic-hotels/common/constants/currency'
|
||||||
import { Lang } from '@scandic-hotels/common/constants/language'
|
import { Lang } from '@scandic-hotels/common/constants/language'
|
||||||
|
import { selectRate } from '@scandic-hotels/common/constants/routes/hotelReservation'
|
||||||
|
import { useUrlWithSearchParam } from '@scandic-hotels/common/hooks/useUrlWithSearchParam'
|
||||||
|
import ButtonLink from '../../../ButtonLink'
|
||||||
import { FacilityToIcon } from '../../../FacilityToIcon'
|
import { FacilityToIcon } from '../../../FacilityToIcon'
|
||||||
import { HotelPin } from '../../../Map/types'
|
import { HotelPin } from '../../../Map/types'
|
||||||
import { HotelPointsRow } from '../../HotelPointsRow'
|
import { HotelPointsRow } from '../../HotelPointsRow'
|
||||||
|
import { RoomPrice } from '../../RoomPrice'
|
||||||
import styles from './standaloneHotelCardDialog.module.css'
|
import styles from './standaloneHotelCardDialog.module.css'
|
||||||
import { CurrencyEnum } from '@scandic-hotels/common/constants/currency'
|
|
||||||
|
|
||||||
interface StandaloneHotelCardProps {
|
interface StandaloneHotelCardProps {
|
||||||
data: HotelPin
|
|
||||||
lang: Lang
|
lang: Lang
|
||||||
|
data: HotelPin
|
||||||
isUserLoggedIn: boolean
|
isUserLoggedIn: boolean
|
||||||
handleClose: () => void
|
handleClose: () => void
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
@@ -63,6 +60,17 @@ export function StandaloneHotelCardDialog({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const shouldShowNotEnoughPoints = redemptionPrice && !hasEnoughPoints
|
const shouldShowNotEnoughPoints = redemptionPrice && !hasEnoughPoints
|
||||||
|
const selectRateUrl = useUrlWithSearchParam(
|
||||||
|
{ key: 'hotel', value: operaId },
|
||||||
|
selectRate(lang)
|
||||||
|
)
|
||||||
|
const showPriceCard = !!(
|
||||||
|
publicPrice ||
|
||||||
|
memberPrice ||
|
||||||
|
redemptionPrice ||
|
||||||
|
voucherPrice ||
|
||||||
|
chequePrice
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
@@ -86,9 +94,9 @@ export function StandaloneHotelCardDialog({
|
|||||||
position="left"
|
position="left"
|
||||||
/>
|
/>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.name}>
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
<Body textTransform="bold">{name}</Body>
|
<h4 className={styles.name}>{name}</h4>
|
||||||
</div>
|
</Typography>
|
||||||
<div className={styles.facilities}>
|
<div className={styles.facilities}>
|
||||||
{amenities.slice(0, 3).map((facility) => {
|
{amenities.slice(0, 3).map((facility) => {
|
||||||
const Icon = (
|
const Icon = (
|
||||||
@@ -96,173 +104,93 @@ export function StandaloneHotelCardDialog({
|
|||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<div className={styles.facilitiesItem} key={facility.id}>
|
<div className={styles.facilitiesItem} key={facility.id}>
|
||||||
{Icon}
|
{Icon && Icon}
|
||||||
<Footnote color="uiTextMediumContrast">
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
{facility.name}
|
<span>{facility.name}</span>
|
||||||
</Footnote>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.pricesContainer}>
|
{showPriceCard ? (
|
||||||
{publicPrice ||
|
<>
|
||||||
memberPrice ||
|
<div className={styles.priceCard}>
|
||||||
redemptionPrice ||
|
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||||
voucherPrice ||
|
<p>
|
||||||
chequePrice ? (
|
{redemptionPrice
|
||||||
<>
|
? intl.formatMessage({ defaultMessage: 'Available rates' })
|
||||||
<div className={styles.priceCard}>
|
: intl.formatMessage({ defaultMessage: 'From' })}
|
||||||
{redemptionPrice ? (
|
</p>
|
||||||
<Caption>
|
</Typography>
|
||||||
{intl.formatMessage({
|
{chequePrice ? (
|
||||||
defaultMessage: 'Available rates',
|
<RoomPrice
|
||||||
})}
|
price={chequePrice.numberOfCheques}
|
||||||
</Caption>
|
currency="CC"
|
||||||
) : (
|
includePerNight={false}
|
||||||
<Caption type="bold">
|
|
||||||
{intl.formatMessage({
|
|
||||||
defaultMessage: 'From',
|
|
||||||
})}
|
|
||||||
</Caption>
|
|
||||||
)}
|
|
||||||
{chequePrice && (
|
|
||||||
<Subtitle type="two">
|
|
||||||
{intl.formatMessage(
|
|
||||||
{
|
|
||||||
defaultMessage: '{price} {currency}',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
price: chequePrice.numberOfCheques,
|
|
||||||
currency: 'CC',
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
{chequePrice.additionalPricePerStay > 0
|
|
||||||
? // eslint-disable-next-line formatjs/no-literal-string-in-jsx
|
|
||||||
' + ' +
|
|
||||||
intl.formatMessage(
|
|
||||||
{
|
|
||||||
defaultMessage: '{price} {currency}',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
price: chequePrice.additionalPricePerStay,
|
|
||||||
currency: chequePrice.currency,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
: null}
|
|
||||||
<Body asChild>
|
|
||||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
|
||||||
<span>
|
|
||||||
/
|
|
||||||
{intl.formatMessage({
|
|
||||||
defaultMessage: 'night',
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</Body>
|
|
||||||
</Subtitle>
|
|
||||||
)}
|
|
||||||
{voucherPrice && (
|
|
||||||
<Subtitle type="two">
|
|
||||||
{intl.formatMessage(
|
|
||||||
{
|
|
||||||
defaultMessage: '{price} {currency}',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
price: voucherPrice,
|
|
||||||
currency,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
<Body asChild>
|
|
||||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
|
||||||
<span>
|
|
||||||
/
|
|
||||||
{intl.formatMessage({
|
|
||||||
defaultMessage: 'night',
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</Body>
|
|
||||||
</Subtitle>
|
|
||||||
)}
|
|
||||||
{publicPrice && !isUserLoggedIn && (
|
|
||||||
<Subtitle type="two">
|
|
||||||
{intl.formatMessage(
|
|
||||||
{
|
|
||||||
defaultMessage: '{price} {currency}',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
price: publicPrice,
|
|
||||||
currency,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
<Body asChild>
|
|
||||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
|
||||||
<span>
|
|
||||||
/
|
|
||||||
{intl.formatMessage({
|
|
||||||
defaultMessage: 'night',
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</Body>
|
|
||||||
</Subtitle>
|
|
||||||
)}
|
|
||||||
{memberPrice && (
|
|
||||||
<Subtitle type="two" color="red">
|
|
||||||
{intl.formatMessage(
|
|
||||||
{
|
|
||||||
defaultMessage: '{price} {currency}',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
price: memberPrice,
|
|
||||||
currency,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
<Body asChild color="red">
|
|
||||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
|
||||||
<span>
|
|
||||||
/
|
|
||||||
{intl.formatMessage({
|
|
||||||
defaultMessage: 'night',
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</Body>
|
|
||||||
</Subtitle>
|
|
||||||
)}
|
|
||||||
{redemptionPrice && (
|
|
||||||
<HotelPointsRow
|
|
||||||
pointsPerStay={redemptionPrice}
|
|
||||||
pointsCurrency={pointsCurrency}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{shouldShowNotEnoughPoints ? (
|
|
||||||
<div className={styles.notEnoughPointsButton}>
|
|
||||||
<Typography variant="Body/Paragraph/mdBold">
|
|
||||||
<span>{notEnoughPointsLabel}</span>
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
asChild
|
|
||||||
theme="base"
|
|
||||||
size="small"
|
|
||||||
className={styles.button}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
>
|
||||||
<Link
|
{chequePrice.additionalPricePerStay > 0 ? (
|
||||||
href={`${selectRate(lang)}?hotel=${operaId}`}
|
<>
|
||||||
color="none"
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
keepSearchParams
|
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||||
>
|
<span> + </span>
|
||||||
{intl.formatMessage({
|
</Typography>
|
||||||
defaultMessage: 'See rooms',
|
<Typography variant="Title/Subtitle/md">
|
||||||
})}
|
<span>{chequePrice.additionalPricePerStay}</span>
|
||||||
</Link>
|
</Typography>
|
||||||
</Button>
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
)}
|
<span> {chequePrice.currency}</span>
|
||||||
</>
|
</Typography>
|
||||||
) : (
|
</>
|
||||||
<NoPriceAvailableCard />
|
) : null}
|
||||||
)}
|
</RoomPrice>
|
||||||
</div>
|
) : null}
|
||||||
|
{voucherPrice ? (
|
||||||
|
<RoomPrice price={voucherPrice} currency={currency} />
|
||||||
|
) : null}
|
||||||
|
{/* Show public price if:
|
||||||
|
1) user is not logged in, or
|
||||||
|
2) user is logged in but no member price is available (use of booking codes)
|
||||||
|
*/}
|
||||||
|
{publicPrice && (!isUserLoggedIn || !memberPrice) ? (
|
||||||
|
<RoomPrice price={publicPrice} currency={currency} />
|
||||||
|
) : null}
|
||||||
|
{memberPrice ? (
|
||||||
|
<RoomPrice
|
||||||
|
className={styles.memberPrice}
|
||||||
|
price={memberPrice}
|
||||||
|
currency={currency}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{redemptionPrice ? (
|
||||||
|
<HotelPointsRow
|
||||||
|
pointsPerStay={redemptionPrice}
|
||||||
|
pointsCurrency={pointsCurrency}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
{shouldShowNotEnoughPoints ? (
|
||||||
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
|
<div className={styles.notEnoughPointsButton}>
|
||||||
|
{notEnoughPointsLabel}
|
||||||
|
</div>
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<ButtonLink
|
||||||
|
href={selectRateUrl}
|
||||||
|
variant="Primary"
|
||||||
|
color="Primary"
|
||||||
|
size="Small"
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({
|
||||||
|
defaultMessage: 'See rooms',
|
||||||
|
})}
|
||||||
|
</ButtonLink>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<NoPriceAvailableCard />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,25 +1,30 @@
|
|||||||
.container {
|
.container {
|
||||||
flex-direction: row;
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
background: var(--Base-Surface-Primary-light-Normal);
|
display: flex;
|
||||||
|
background-color: var(--Surface-Primary-Default);
|
||||||
box-shadow: 0px 0px 8px 3px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 0px 8px 3px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--Space-x05);
|
||||||
|
right: var(--Space-x05);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
width: 100%;
|
width: 225px;
|
||||||
max-width: 220px;
|
|
||||||
padding: var(--Space-x15);
|
padding: var(--Space-x15);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: var(--Space-x1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
max-width: 180px;
|
width: 180px;
|
||||||
margin-bottom: var(--Space-x05);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-right: var(--Space-x1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.facilities {
|
.facilities {
|
||||||
@@ -32,40 +37,23 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--Space-x05);
|
gap: var(--Space-x05);
|
||||||
}
|
color: var(--Text-Secondary);
|
||||||
|
|
||||||
.prices {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--Space-x1);
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.priceCard {
|
.priceCard {
|
||||||
border-radius: var(--Corner-radius-md);
|
border-radius: var(--Corner-radius-md);
|
||||||
padding: var(--Space-x05) var(--Space-x1);
|
padding: var(--Space-x05) var(--Space-x1);
|
||||||
background: var(--Base-Surface-Secondary-light-Normal);
|
background: var(--Base-Surface-Secondary-light-Normal);
|
||||||
margin-top: var(--Space-x1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pricesContainer {
|
.memberPrice {
|
||||||
display: flex;
|
color: var(--Text-Accent-Primary);
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--Space-x1);
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content .button {
|
.content .seeRoomsButton {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.closeButton {
|
|
||||||
position: absolute;
|
|
||||||
top: 8px;
|
|
||||||
right: 8px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notEnoughPointsButton {
|
.notEnoughPointsButton {
|
||||||
border-radius: var(--Corner-radius-rounded);
|
border-radius: var(--Corner-radius-rounded);
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
.poinstRow {
|
.roomPrice {
|
||||||
display: flex;
|
|
||||||
gap: var(--Space-x1);
|
|
||||||
align-items: baseline;
|
|
||||||
color: var(--Text-Default);
|
color: var(--Text-Default);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useIntl } from 'react-intl'
|
import { useIntl } from 'react-intl'
|
||||||
|
|
||||||
import Caption from '../../Caption'
|
import { RoomPrice } from '../../HotelCard/RoomPrice'
|
||||||
import Subtitle from '../../Subtitle'
|
import { Typography } from '../../Typography'
|
||||||
|
|
||||||
import styles from './hotelPointsRow.module.css'
|
|
||||||
import { CurrencyEnum } from '@scandic-hotels/common/constants/currency'
|
import { CurrencyEnum } from '@scandic-hotels/common/constants/currency'
|
||||||
|
import styles from './hotelPointsRow.module.css'
|
||||||
|
|
||||||
export type PointsRowProps = {
|
export type PointsRowProps = {
|
||||||
pointsPerStay: number
|
pointsPerStay: number
|
||||||
@@ -21,27 +21,28 @@ export function HotelPointsRow({
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.poinstRow}>
|
<RoomPrice
|
||||||
<Subtitle type="two" color="uiTextHighContrast">
|
className={styles.roomPrice}
|
||||||
{pointsPerStay}
|
price={pointsPerStay}
|
||||||
</Subtitle>
|
currency={
|
||||||
<Caption color="uiTextHighContrast">
|
pointsCurrency ?? intl.formatMessage({ defaultMessage: 'Points' })
|
||||||
{pointsCurrency ??
|
}
|
||||||
intl.formatMessage({
|
includePerNight={false}
|
||||||
defaultMessage: 'Points',
|
>
|
||||||
})}
|
|
||||||
</Caption>
|
|
||||||
{additionalPricePerStay ? (
|
{additionalPricePerStay ? (
|
||||||
<>
|
<>
|
||||||
{'+'}
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
<Subtitle type="two" color="uiTextHighContrast">
|
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||||
{additionalPricePerStay}
|
<span> + </span>
|
||||||
</Subtitle>
|
</Typography>
|
||||||
<Caption color="uiTextHighContrast">
|
<Typography variant="Title/Subtitle/md">
|
||||||
{additionalPriceCurrency}
|
<span>{additionalPricePerStay}</span>
|
||||||
</Caption>
|
</Typography>
|
||||||
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
|
<span> {additionalPriceCurrency}</span>
|
||||||
|
</Typography>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</RoomPrice>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { useIntl } from 'react-intl'
|
||||||
|
import { Typography } from '../../Typography'
|
||||||
|
|
||||||
|
interface RoomPriceProps extends React.HTMLAttributes<HTMLParagraphElement> {
|
||||||
|
price: number
|
||||||
|
currency: string
|
||||||
|
includePerNight?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RoomPrice({
|
||||||
|
price,
|
||||||
|
currency,
|
||||||
|
children,
|
||||||
|
includePerNight = true,
|
||||||
|
...props
|
||||||
|
}: RoomPriceProps) {
|
||||||
|
const intl = useIntl()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p {...props}>
|
||||||
|
<Typography variant="Title/Subtitle/md">
|
||||||
|
<span>{price}</span>
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
|
<span> {currency}</span>
|
||||||
|
</Typography>
|
||||||
|
{children}
|
||||||
|
{includePerNight ? (
|
||||||
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
|
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||||
|
<span>/{intl.formatMessage({ defaultMessage: 'night' })}</span>
|
||||||
|
</Typography>
|
||||||
|
) : null}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,12 +5,13 @@ import {
|
|||||||
} from '@vis.gl/react-google-maps'
|
} from '@vis.gl/react-google-maps'
|
||||||
import { useMediaQuery } from 'usehooks-ts'
|
import { useMediaQuery } from 'usehooks-ts'
|
||||||
|
|
||||||
import { HotelPin } from './HotelPin'
|
import { CurrencyEnum } from '@scandic-hotels/common/constants/currency'
|
||||||
|
import { Lang } from '@scandic-hotels/common/constants/language'
|
||||||
|
import { useIntl } from 'react-intl'
|
||||||
|
import { StandaloneHotelCardDialog } from '../../../HotelCard/HotelDialogCard/StandaloneHotelCardDialog'
|
||||||
import type { HotelPin as HotelPinType } from '../../types'
|
import type { HotelPin as HotelPinType } from '../../types'
|
||||||
import styles from './hotelListingMapContent.module.css'
|
import styles from './hotelListingMapContent.module.css'
|
||||||
import { StandaloneHotelCardDialog } from '../../../HotelCard/HotelDialogCard/StandaloneHotelCardDialog'
|
import { HotelPin } from './HotelPin'
|
||||||
import { Lang } from '@scandic-hotels/common/constants/language'
|
|
||||||
import { CurrencyEnum } from '@scandic-hotels/common/constants/currency'
|
|
||||||
|
|
||||||
export type HotelListingMapContentProps = {
|
export type HotelListingMapContentProps = {
|
||||||
hotelPins: HotelPinType[]
|
hotelPins: HotelPinType[]
|
||||||
@@ -36,6 +37,7 @@ export function HotelListingMapContent({
|
|||||||
onClickHotel,
|
onClickHotel,
|
||||||
pointsCurrency,
|
pointsCurrency,
|
||||||
}: HotelListingMapContentProps) {
|
}: HotelListingMapContentProps) {
|
||||||
|
const intl = useIntl()
|
||||||
const isDesktop = useMediaQuery('(min-width: 900px)')
|
const isDesktop = useMediaQuery('(min-width: 900px)')
|
||||||
|
|
||||||
const toggleActiveHotelPin = (
|
const toggleActiveHotelPin = (
|
||||||
@@ -62,6 +64,10 @@ export function HotelListingMapContent({
|
|||||||
pin.chequePrice?.numberOfCheques ??
|
pin.chequePrice?.numberOfCheques ??
|
||||||
null
|
null
|
||||||
|
|
||||||
|
const pinCurrency = pin.redemptionPrice
|
||||||
|
? intl.formatMessage({ defaultMessage: 'Points' })
|
||||||
|
: pin.currency
|
||||||
|
|
||||||
const hotelAdditionalPrice = pin.chequePrice
|
const hotelAdditionalPrice = pin.chequePrice
|
||||||
? pin.chequePrice.additionalPricePerStay
|
? pin.chequePrice.additionalPricePerStay
|
||||||
: undefined
|
: undefined
|
||||||
@@ -114,7 +120,7 @@ export function HotelListingMapContent({
|
|||||||
<HotelPin
|
<HotelPin
|
||||||
isActive={isActiveOrHovered}
|
isActive={isActiveOrHovered}
|
||||||
hotelPrice={hotelPrice}
|
hotelPrice={hotelPrice}
|
||||||
currency={pin.currency}
|
currency={pinCurrency}
|
||||||
hotelAdditionalPrice={hotelAdditionalPrice}
|
hotelAdditionalPrice={hotelAdditionalPrice}
|
||||||
hotelAdditionalCurrency={hotelAdditionalCurrency}
|
hotelAdditionalCurrency={hotelAdditionalCurrency}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
||||||
|
|
||||||
// import { expect, fn } from 'storybook/test'
|
// import { expect, fn } from 'storybook/test'
|
||||||
import { InteractiveMap } from '.'
|
|
||||||
import { Lang } from '@scandic-hotels/common/constants/language'
|
import { Lang } from '@scandic-hotels/common/constants/language'
|
||||||
import { APIProvider } from '@vis.gl/react-google-maps'
|
import { APIProvider } from '@vis.gl/react-google-maps'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import { InteractiveMap } from '.'
|
||||||
|
import { hotelPins } from './storybookData'
|
||||||
|
|
||||||
const meta: Meta<typeof InteractiveMap> = {
|
const meta: Meta<typeof InteractiveMap> = {
|
||||||
title: 'Components/Map/Interactive Map',
|
title: 'Components/Map/Interactive Map',
|
||||||
@@ -19,159 +20,7 @@ type Story = StoryObj<typeof InteractiveMap>
|
|||||||
export const PrimaryDefault: Story = {
|
export const PrimaryDefault: Story = {
|
||||||
args: {
|
args: {
|
||||||
lang: Lang.en,
|
lang: Lang.en,
|
||||||
hotelPins: [
|
hotelPins,
|
||||||
{
|
|
||||||
coordinates: {
|
|
||||||
lat: 59.331303,
|
|
||||||
lng: 18.065542,
|
|
||||||
},
|
|
||||||
name: 'Downtown Camper by Scandic',
|
|
||||||
chequePrice: null,
|
|
||||||
publicPrice: 1100,
|
|
||||||
memberPrice: 1067,
|
|
||||||
redemptionPrice: null,
|
|
||||||
voucherPrice: null,
|
|
||||||
rateType: 'Regular',
|
|
||||||
currency: 'SEK',
|
|
||||||
|
|
||||||
amenities: [
|
|
||||||
{
|
|
||||||
filter: 'Hotel facilities',
|
|
||||||
icon: 'Pool',
|
|
||||||
id: 1831,
|
|
||||||
name: 'Pool',
|
|
||||||
public: true,
|
|
||||||
sortOrder: 7000,
|
|
||||||
slug: 'pool',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filter: 'Hotel facilities',
|
|
||||||
icon: 'Restaurant',
|
|
||||||
id: 1383,
|
|
||||||
name: 'Restaurant',
|
|
||||||
public: true,
|
|
||||||
sortOrder: 6000,
|
|
||||||
slug: 'restaurant',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filter: 'None',
|
|
||||||
icon: 'KayaksForLoan',
|
|
||||||
id: 162585,
|
|
||||||
name: 'Kayaks for loan',
|
|
||||||
public: true,
|
|
||||||
sortOrder: 5000,
|
|
||||||
slug: 'kayaks-for-loan',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filter: 'Hotel facilities',
|
|
||||||
icon: 'None',
|
|
||||||
id: 239348,
|
|
||||||
name: 'Rooftop bar',
|
|
||||||
public: false,
|
|
||||||
sortOrder: 4000,
|
|
||||||
slug: 'rooftop-bar',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filter: 'None',
|
|
||||||
icon: 'BikesForLoan',
|
|
||||||
id: 5550,
|
|
||||||
name: 'Bikes for loan',
|
|
||||||
public: true,
|
|
||||||
sortOrder: 3000,
|
|
||||||
slug: 'bikes-for-loan',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
ratings: {
|
|
||||||
tripAdvisor: 4.4,
|
|
||||||
},
|
|
||||||
operaId: '879',
|
|
||||||
facilityIds: [
|
|
||||||
1831, 1383, 162585, 239348, 5550, 162586, 5806, 1014, 1835, 1829,
|
|
||||||
1379, 1382, 162587, 1017, 1378, 1408, 1833, 971, 1834, 162584, 1381,
|
|
||||||
229144, 267806,
|
|
||||||
],
|
|
||||||
hasEnoughPoints: false,
|
|
||||||
image: {
|
|
||||||
alt: 'Bar of Downtown Camper by Scandic in Stockholm',
|
|
||||||
url: 'https://images-test.scandichotels.com/publishedmedia/z68596isempb61xm2ns9/Scandic_Downtown_Camper_spa_wellness_the_nest_swim.jpg',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
coordinates: {
|
|
||||||
lat: 59.33469,
|
|
||||||
lng: 18.061586,
|
|
||||||
},
|
|
||||||
name: 'Haymarket by Scandic',
|
|
||||||
chequePrice: null,
|
|
||||||
publicPrice: null,
|
|
||||||
memberPrice: 9999,
|
|
||||||
redemptionPrice: null,
|
|
||||||
voucherPrice: null,
|
|
||||||
rateType: 'Regular',
|
|
||||||
currency: 'SEK',
|
|
||||||
|
|
||||||
amenities: [
|
|
||||||
{
|
|
||||||
filter: 'Hotel facilities',
|
|
||||||
icon: 'Restaurant',
|
|
||||||
id: 1383,
|
|
||||||
name: 'Restaurant',
|
|
||||||
public: true,
|
|
||||||
sortOrder: 6000,
|
|
||||||
slug: 'restaurant',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filter: 'None',
|
|
||||||
icon: 'None',
|
|
||||||
id: 5806,
|
|
||||||
name: 'Meeting / conference facilities',
|
|
||||||
public: true,
|
|
||||||
sortOrder: 1500,
|
|
||||||
slug: 'meeting-conference-facilities',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filter: 'Hotel facilities',
|
|
||||||
icon: 'Bar',
|
|
||||||
id: 1014,
|
|
||||||
name: 'Bar',
|
|
||||||
public: true,
|
|
||||||
sortOrder: 1401,
|
|
||||||
slug: 'bar',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filter: 'Hotel facilities',
|
|
||||||
icon: 'PetFriendlyRooms',
|
|
||||||
id: 1835,
|
|
||||||
name: 'Pet-friendly rooms',
|
|
||||||
public: true,
|
|
||||||
sortOrder: 1201,
|
|
||||||
slug: 'pet-friendly-rooms',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filter: 'Hotel facilities',
|
|
||||||
icon: 'Gym',
|
|
||||||
id: 1829,
|
|
||||||
name: 'Gym',
|
|
||||||
public: true,
|
|
||||||
sortOrder: 1101,
|
|
||||||
slug: 'gym',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
ratings: {
|
|
||||||
tripAdvisor: 4.1,
|
|
||||||
},
|
|
||||||
operaId: '890',
|
|
||||||
facilityIds: [
|
|
||||||
1383, 5806, 1014, 1835, 1829, 1382, 162587, 1017, 1833, 971, 1834,
|
|
||||||
1381, 1406, 1913, 345180, 375885,
|
|
||||||
],
|
|
||||||
hasEnoughPoints: false,
|
|
||||||
image: {
|
|
||||||
alt: 'Bar',
|
|
||||||
url: 'https://images-test.scandichotels.com/publishedmedia/6wobp0j1ocvoopy1dmce/haymarket-by-scandic-bar-pauls_-3-.jpg',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
isUserLoggedIn: false,
|
isUserLoggedIn: false,
|
||||||
coordinates: {
|
coordinates: {
|
||||||
lat: 59.32644916839965,
|
lat: 59.32644916839965,
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ import PoiMapMarkers from './PoiMapMarkers'
|
|||||||
|
|
||||||
import styles from './interactiveMap.module.css'
|
import styles from './interactiveMap.module.css'
|
||||||
|
|
||||||
import { HotelPin, MarkerInfo, PointOfInterest } from '../types'
|
|
||||||
import { Lang } from '@scandic-hotels/common/constants/language'
|
|
||||||
import { CurrencyEnum } from '@scandic-hotels/common/constants/currency'
|
import { CurrencyEnum } from '@scandic-hotels/common/constants/currency'
|
||||||
|
import { Lang } from '@scandic-hotels/common/constants/language'
|
||||||
|
import { HotelPin, MarkerInfo, PointOfInterest } from '../types'
|
||||||
|
|
||||||
export type InteractiveMapProps = {
|
export type InteractiveMapProps = {
|
||||||
lang: Lang
|
lang: Lang
|
||||||
|
|||||||
@@ -0,0 +1,379 @@
|
|||||||
|
import { CurrencyEnum } from '@scandic-hotels/common/constants/currency'
|
||||||
|
import { HotelPin } from '../types'
|
||||||
|
|
||||||
|
export const hotelPins: HotelPin[] = [
|
||||||
|
{
|
||||||
|
coordinates: {
|
||||||
|
lat: 59.331303,
|
||||||
|
lng: 18.065542,
|
||||||
|
},
|
||||||
|
name: 'Downtown Camper by Scandic',
|
||||||
|
chequePrice: null,
|
||||||
|
publicPrice: 1100,
|
||||||
|
memberPrice: 1067,
|
||||||
|
redemptionPrice: null,
|
||||||
|
voucherPrice: null,
|
||||||
|
rateType: 'Regular',
|
||||||
|
currency: 'SEK',
|
||||||
|
amenities: [
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'Pool',
|
||||||
|
id: 1831,
|
||||||
|
name: 'Pool',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 7000,
|
||||||
|
slug: 'pool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'Restaurant',
|
||||||
|
id: 1383,
|
||||||
|
name: 'Restaurant',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 6000,
|
||||||
|
slug: 'restaurant',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'None',
|
||||||
|
icon: 'KayaksForLoan',
|
||||||
|
id: 162585,
|
||||||
|
name: 'Kayaks for loan',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 5000,
|
||||||
|
slug: 'kayaks-for-loan',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'None',
|
||||||
|
id: 239348,
|
||||||
|
name: 'Rooftop bar',
|
||||||
|
public: false,
|
||||||
|
sortOrder: 4000,
|
||||||
|
slug: 'rooftop-bar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'None',
|
||||||
|
icon: 'BikesForLoan',
|
||||||
|
id: 5550,
|
||||||
|
name: 'Bikes for loan',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 3000,
|
||||||
|
slug: 'bikes-for-loan',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ratings: {
|
||||||
|
tripAdvisor: 4.4,
|
||||||
|
},
|
||||||
|
operaId: '879',
|
||||||
|
facilityIds: [
|
||||||
|
1831, 1383, 162585, 239348, 5550, 162586, 5806, 1014, 1835, 1829, 1379,
|
||||||
|
1382, 162587, 1017, 1378, 1408, 1833, 971, 1834, 162584, 1381, 229144,
|
||||||
|
267806,
|
||||||
|
],
|
||||||
|
hasEnoughPoints: false,
|
||||||
|
image: {
|
||||||
|
alt: 'Bar of Downtown Camper by Scandic in Stockholm',
|
||||||
|
url: 'https://images-test.scandichotels.com/publishedmedia/p0ffjkfyfnx502knqlxo/Scandic_Downtown_Camper_spa_wellness_the_nest_swim.jpg',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
coordinates: {
|
||||||
|
lat: 59.33469,
|
||||||
|
lng: 18.061586,
|
||||||
|
},
|
||||||
|
name: 'Haymarket by Scandic',
|
||||||
|
chequePrice: null,
|
||||||
|
publicPrice: null,
|
||||||
|
memberPrice: 9999,
|
||||||
|
redemptionPrice: null,
|
||||||
|
voucherPrice: null,
|
||||||
|
rateType: 'Regular',
|
||||||
|
currency: 'SEK',
|
||||||
|
amenities: [
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'Restaurant',
|
||||||
|
id: 1383,
|
||||||
|
name: 'Restaurant',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 6000,
|
||||||
|
slug: 'restaurant',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'None',
|
||||||
|
icon: 'None',
|
||||||
|
id: 5806,
|
||||||
|
name: 'Meeting / conference facilities',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1500,
|
||||||
|
slug: 'meeting-conference-facilities',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'Bar',
|
||||||
|
id: 1014,
|
||||||
|
name: 'Bar',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1401,
|
||||||
|
slug: 'bar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'PetFriendlyRooms',
|
||||||
|
id: 1835,
|
||||||
|
name: 'Pet-friendly rooms',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1201,
|
||||||
|
slug: 'pet-friendly-rooms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'Gym',
|
||||||
|
id: 1829,
|
||||||
|
name: 'Gym',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1101,
|
||||||
|
slug: 'gym',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ratings: {
|
||||||
|
tripAdvisor: 4.1,
|
||||||
|
},
|
||||||
|
operaId: '890',
|
||||||
|
facilityIds: [
|
||||||
|
1383, 5806, 1014, 1835, 1829, 1382, 162587, 1017, 1833, 971, 1834, 1381,
|
||||||
|
1406, 1913, 345180, 375885,
|
||||||
|
],
|
||||||
|
hasEnoughPoints: false,
|
||||||
|
image: {
|
||||||
|
alt: 'Bar',
|
||||||
|
url: 'https://images-test.scandichotels.com/publishedmedia/lr2b7r655xl0sjcbgxt9/haymarket-by-scandic-bar-pauls_-3-.jpg',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
coordinates: {
|
||||||
|
lat: 59.337166,
|
||||||
|
lng: 18.072765,
|
||||||
|
},
|
||||||
|
name: 'Scandic Anglais',
|
||||||
|
chequePrice: {
|
||||||
|
numberOfCheques: 1,
|
||||||
|
additionalPricePerStay: 500,
|
||||||
|
currency: CurrencyEnum.SEK,
|
||||||
|
},
|
||||||
|
publicPrice: null,
|
||||||
|
memberPrice: null,
|
||||||
|
redemptionPrice: null,
|
||||||
|
voucherPrice: null,
|
||||||
|
rateType: 'Regular',
|
||||||
|
currency: 'CC',
|
||||||
|
amenities: [
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'Restaurant',
|
||||||
|
id: 1383,
|
||||||
|
name: 'Restaurant',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 6000,
|
||||||
|
slug: 'restaurant',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'None',
|
||||||
|
id: 239348,
|
||||||
|
name: 'Rooftop bar',
|
||||||
|
public: false,
|
||||||
|
sortOrder: 4000,
|
||||||
|
slug: 'rooftop-bar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'None',
|
||||||
|
icon: 'BikesForLoan',
|
||||||
|
id: 5550,
|
||||||
|
name: 'Bikes for loan',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 3000,
|
||||||
|
slug: 'bikes-for-loan',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'None',
|
||||||
|
icon: 'None',
|
||||||
|
id: 5806,
|
||||||
|
name: 'Meeting / conference facilities',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1500,
|
||||||
|
slug: 'meeting-conference-facilities',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'Bar',
|
||||||
|
id: 1014,
|
||||||
|
name: 'Bar',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1401,
|
||||||
|
slug: 'bar',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ratings: {
|
||||||
|
tripAdvisor: 3.6,
|
||||||
|
},
|
||||||
|
operaId: '810',
|
||||||
|
facilityIds: [
|
||||||
|
1383, 239348, 5550, 5806, 1014, 1835, 1829, 1379, 2665, 1382, 162587,
|
||||||
|
1017, 1408, 1833, 971, 1834, 1405, 1406, 956, 1913,
|
||||||
|
],
|
||||||
|
hasEnoughPoints: false,
|
||||||
|
image: {
|
||||||
|
alt: 'lobby at scandic anglais in stockholm',
|
||||||
|
url: 'https://images-test.scandichotels.com/publishedmedia/wi0lzgxhbm4vmfguf7wm/scandic-anglais-lobby6.jpg',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
coordinates: {
|
||||||
|
lat: 59.33099,
|
||||||
|
lng: 18.05926,
|
||||||
|
},
|
||||||
|
name: 'Scandic Continental',
|
||||||
|
chequePrice: null,
|
||||||
|
publicPrice: null,
|
||||||
|
memberPrice: null,
|
||||||
|
redemptionPrice: 65000,
|
||||||
|
voucherPrice: null,
|
||||||
|
rateType: 'Regular',
|
||||||
|
currency: 'Points',
|
||||||
|
amenities: [
|
||||||
|
{
|
||||||
|
filter: 'None',
|
||||||
|
icon: 'BikesForLoan',
|
||||||
|
id: 5550,
|
||||||
|
name: 'Bikes for loan',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 3000,
|
||||||
|
slug: 'bikes-for-loan',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'None',
|
||||||
|
icon: 'None',
|
||||||
|
id: 5806,
|
||||||
|
name: 'Meeting / conference facilities',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1500,
|
||||||
|
slug: 'meeting-conference-facilities',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'PetFriendlyRooms',
|
||||||
|
id: 1835,
|
||||||
|
name: 'Pet-friendly rooms',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1201,
|
||||||
|
slug: 'pet-friendly-rooms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'Gym',
|
||||||
|
id: 1829,
|
||||||
|
name: 'Gym',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1101,
|
||||||
|
slug: 'gym',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'Sauna',
|
||||||
|
id: 1379,
|
||||||
|
name: 'Sauna',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1001,
|
||||||
|
slug: 'sauna',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ratings: {
|
||||||
|
tripAdvisor: 4.2,
|
||||||
|
},
|
||||||
|
operaId: '811',
|
||||||
|
facilityIds: [
|
||||||
|
5550, 5806, 1835, 1829, 1379, 2665, 1606, 1382, 1017, 1378, 1408, 1833,
|
||||||
|
971, 1834, 162583, 1406, 1607, 1911, 1913, 229144,
|
||||||
|
],
|
||||||
|
hasEnoughPoints: false,
|
||||||
|
image: {
|
||||||
|
alt: 'Exterior',
|
||||||
|
url: 'https://images-test.scandichotels.com/publishedmedia/xnvuuzvogv7dzt3nmxit/Scandic_Continental_Exterior_Vasagatan_Day.jpg',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
coordinates: {
|
||||||
|
lat: 59.323063,
|
||||||
|
lng: 18.069921,
|
||||||
|
},
|
||||||
|
name: 'Scandic Gamla Stan',
|
||||||
|
chequePrice: null,
|
||||||
|
publicPrice: null,
|
||||||
|
memberPrice: null,
|
||||||
|
redemptionPrice: null,
|
||||||
|
voucherPrice: 1,
|
||||||
|
rateType: 'Regular',
|
||||||
|
currency: 'Voucher',
|
||||||
|
amenities: [
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'PetFriendlyRooms',
|
||||||
|
id: 1835,
|
||||||
|
name: 'Pet-friendly rooms',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 1201,
|
||||||
|
slug: 'pet-friendly-rooms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'Hotel facilities',
|
||||||
|
icon: 'OutdoorTerrace',
|
||||||
|
id: 1382,
|
||||||
|
name: 'Outdoor terrace',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 550,
|
||||||
|
slug: 'outdoor-terrace',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'None',
|
||||||
|
icon: 'Shop',
|
||||||
|
id: 1408,
|
||||||
|
name: 'Scandic Shop 24 hrs',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 301,
|
||||||
|
slug: 'scandic-shop-24-hrs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'None',
|
||||||
|
icon: 'FreeWiFi',
|
||||||
|
id: 1833,
|
||||||
|
name: 'Free WiFi',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 250,
|
||||||
|
slug: 'free-wifi',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filter: 'None',
|
||||||
|
icon: 'LaundryService',
|
||||||
|
id: 1834,
|
||||||
|
name: 'Laundry service',
|
||||||
|
public: true,
|
||||||
|
sortOrder: 199,
|
||||||
|
slug: 'laundry-service',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ratings: {
|
||||||
|
tripAdvisor: 4.2,
|
||||||
|
},
|
||||||
|
operaId: '821',
|
||||||
|
facilityIds: [
|
||||||
|
1835, 1382, 1408, 1833, 1834, 1405, 345180, 332224, 375885, 238849,
|
||||||
|
],
|
||||||
|
hasEnoughPoints: false,
|
||||||
|
image: {
|
||||||
|
alt: 'Facade Entrance Scandic Gamla Stan Stockholm',
|
||||||
|
url: 'https://images-test.scandichotels.com/publishedmedia/9attpqkkfy6uwc6keyvg/Scandic-Gamla-Stan-Exterior-facade.jpg',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user