feat(SW-189): added translations and some minor changes

This commit is contained in:
Erik Tiekstra
2024-09-11 14:40:48 +02:00
parent 789133af11
commit 21d8a5835a
32 changed files with 271 additions and 151 deletions

View File

@@ -42,6 +42,7 @@ export default async function SelectHotelPage({
height={180}
zoomLevel={11}
mapType="roadmap"
altText={`Map of ${tempSearchTerm} city center`}
/>
<Link className={styles.link} color="burgundy" href="#">
{intl.formatMessage({ id: "Show map" })}

View File

@@ -1,6 +1,12 @@
"use client"
import { AdvancedMarker, Map, useMap } from "@vis.gl/react-google-maps"
import {
AdvancedMarker,
Map,
type MapProps,
useMap,
} from "@vis.gl/react-google-maps"
import { useState } from "react"
import { useIntl } from "react-intl"
import useHotelPageStore from "@/stores/hotel-page"
@@ -9,28 +15,31 @@ import Button from "@/components/TempDesignSystem/Button"
import Divider from "@/components/TempDesignSystem/Divider"
import Title from "@/components/TempDesignSystem/Text/Title"
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
import useLang from "@/hooks/useLang"
import ScandicMarker from "../Markers/Scandic"
import styles from "./dynamicMap.module.css"
import type { DynamicMapContentProps } from "@/types/components/hotelPage/map/dynamicMapContent"
export default function DynamicMapContent({
hotelName,
coordinates,
}: {
hotelName: string
coordinates: { lat: number; lng: number }
}) {
}: DynamicMapContentProps) {
const intl = useIntl()
const lang = useLang()
const { isDynamicMapOpen, closeDynamicMap } = useHotelPageStore()
const [isFullScreenSidebar, setIsFullScreenSidebar] = useState(false)
const map = useMap()
const mapOptions = {
const mapOptions: MapProps = {
defaultZoom: 15,
defaultCenter: coordinates,
disableDefaultUI: true,
clickableIcons: false,
mapId: `${hotelName}-map`,
mapId: `${hotelName}-${lang}-map`,
// As reference for future styles when adding POIs
// styles: [
// {
// featureType: "poi",
@@ -75,7 +84,7 @@ export default function DynamicMapContent({
onClick={closeDynamicMap}
>
<CloseIcon color="burgundy" width={24} height={24} />
<span>Close the map</span>
<span>{intl.formatMessage({ id: "Close the map" })}</span>
</Button>
<div className={styles.zoomButtons}>
<Button
@@ -85,7 +94,7 @@ export default function DynamicMapContent({
size="small"
className={styles.zoomButton}
onClick={zoomOut}
aria-label="Zoom in"
aria-label={intl.formatMessage({ id: "Zoom in" })}
>
<MinusIcon color="burgundy" width={20} height={20} />
</Button>
@@ -96,7 +105,7 @@ export default function DynamicMapContent({
size="small"
className={styles.zoomButton}
onClick={zoomIn}
aria-label="Zoom in"
aria-label={intl.formatMessage({ id: "Zoom out" })}
>
<PlusIcon color="burgundy" width={20} height={20} />
</Button>
@@ -111,12 +120,17 @@ export default function DynamicMapContent({
className={styles.toggleButton}
onClick={toggleFullScreenSidebar}
>
{isFullScreenSidebar ? "View as map" : "View as list"}
{intl.formatMessage({
id: isFullScreenSidebar ? "View as map" : "View as list",
})}
</button>
</div>
<div className={styles.sidebarContent}>
<Title as="h4" level="h2" textTransform="regular">
Things nearby {hotelName}
{intl.formatMessage(
{ id: "Things nearby HOTEL_NAME" },
{ hotelName }
)}
</Title>
<Divider color="subtle" />
</div>

View File

@@ -7,6 +7,7 @@
left: 0;
z-index: var(--dialog-z-index);
display: flex;
background-color: var(--Base-Surface-Primary-light-Normal);
}
.sidebar {
@@ -44,7 +45,7 @@
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
font-family: var(--typography-Body-Bold-fontFamily);
font-size: var(--typography-Body-Bold-fontSize);
font-weight: 500;
font-weight: var(--typography-Body-Bold-fontWeight);
color: var(--UI-Text-Medium-contrast);
width: 100%;
}
@@ -62,7 +63,7 @@
.sidebarContent {
display: grid;
gap: var(--Spacing-x3);
align-items: start;
align-content: start;
padding: var(--Spacing-x3) var(--Spacing-x2);
height: 100%;
overflow-y: auto;
@@ -112,18 +113,13 @@
}
.zoomButton {
width: 40px;
width: var(--Spacing-x5);
height: var(--Spacing-x5);
padding: 0;
pointer-events: initial;
box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.1);
}
/* @media screen and (max-width: 767px) {
.sidebar:not(.fullscreen) .sidebarContent {
display: none;
}
} */
@media screen and (min-width: 768px) {
.dynamicMap {
top: var(--main-menu-desktop-height);

View File

@@ -1,6 +1,8 @@
"use client"
import { APIProvider } from "@vis.gl/react-google-maps"
import { useEffect, useRef, useState } from "react"
import { Dialog, Modal } from "react-aria-components"
import { useIntl } from "react-intl"
import useHotelPageStore from "@/stores/hotel-page"
@@ -10,16 +12,17 @@ import DynamicMapContent from "./Content"
import styles from "./dynamicMap.module.css"
import type { DynamicMapProps } from "@/types/components/hotelPage/map/dynamicMap"
export default function DynamicMap({
apiKey,
hotelName,
coordinates,
}: {
apiKey: string
hotelName: string
coordinates: { lat: number; lng: number }
}) {
}: DynamicMapProps) {
const intl = useIntl()
const { isDynamicMapOpen, closeDynamicMap } = useHotelPageStore()
const [scrollHeightWhenOpened, setScrollHeightWhenOpened] = useState(0)
const hasMounted = useRef(false)
useHandleKeyUp((event: KeyboardEvent) => {
if (event.key === "Escape" && isDynamicMapOpen) {
@@ -27,12 +30,33 @@ export default function DynamicMap({
}
})
// Making sure the map is always opened at the top of the page, just below the header.
// When closing, the page should scroll back to the position it was before opening the map.
useEffect(() => {
// Skip the first render
if (!hasMounted.current) {
hasMounted.current = true
return
}
if (isDynamicMapOpen && scrollHeightWhenOpened === 0) {
setScrollHeightWhenOpened(window.scrollY)
window.scrollTo({ top: 0, behavior: "instant" })
} else if (!isDynamicMapOpen && scrollHeightWhenOpened !== 0) {
window.scrollTo({ top: scrollHeightWhenOpened, behavior: "instant" })
setScrollHeightWhenOpened(0)
}
}, [isDynamicMapOpen, scrollHeightWhenOpened])
return (
<APIProvider apiKey={apiKey}>
<Modal isOpen={isDynamicMapOpen}>
<Dialog
className={styles.dynamicMap}
aria-label={`Things nearby ${hotelName}`}
aria-label={intl.formatMessage(
{ id: "Things nearby HOTEL_NAME" },
{ hotelName }
)}
>
<DynamicMapContent hotelName={hotelName} coordinates={coordinates} />
</Dialog>

View File

@@ -1,5 +1,7 @@
"use client"
import { useIntl } from "react-intl"
import useHotelPageStore from "@/stores/hotel-page"
import Button from "@/components/TempDesignSystem/Button"
@@ -8,9 +10,10 @@ import Title from "@/components/TempDesignSystem/Text/Title"
import styles from "./mapCard.module.css"
import { MapCardProps } from "@/types/components/hotelPage/mapCard"
import type { MapCardProps } from "@/types/components/hotelPage/map/mapCard"
export default function MapCard({ hotelName }: MapCardProps) {
const intl = useIntl()
const { openDynamicMap } = useHotelPageStore()
return (
@@ -20,7 +23,7 @@ export default function MapCard({ hotelName }: MapCardProps) {
textTransform="uppercase"
textAlign="center"
>
Nearby
{intl.formatMessage({ id: "Nearby" })}
</Caption>
<Title
color="burgundy"
@@ -39,7 +42,7 @@ export default function MapCard({ hotelName }: MapCardProps) {
className={styles.ctaButton}
onClick={openDynamicMap}
>
Explore nearby
{intl.formatMessage({ id: "Explore nearby" })}
</Button>
</div>
)

View File

@@ -1,12 +1,15 @@
"use client"
import { useIntl } from "react-intl"
import useHotelPageStore from "@/stores/hotel-page"
import { HouseIcon, LocationIcon } from "@/components/Icons"
import { HouseIcon, MapIcon } from "@/components/Icons"
import styles from "./mobileToggle.module.css"
export default function MobileMapToggle() {
const intl = useIntl()
const { isDynamicMapOpen, openDynamicMap, closeDynamicMap } =
useHotelPageStore()
@@ -23,20 +26,20 @@ export default function MobileMapToggle() {
height={24}
width={24}
/>
<span>Hotel</span>
<span>{intl.formatMessage({ id: "Hotel" })}</span>
</button>
<button
type="button"
className={`${styles.button} ${isDynamicMapOpen ? styles.active : ""}`}
onClick={openDynamicMap}
>
<LocationIcon
<MapIcon
className={styles.icon}
color={isDynamicMapOpen ? "white" : "red"}
height={24}
width={24}
/>
<span>Nearby</span>
<span>{intl.formatMessage({ id: "Map" })}</span>
</button>
</div>
)

View File

@@ -19,15 +19,15 @@
align-items: center;
justify-content: center;
gap: var(--Spacing-x-half);
padding: var(--Spacing-x1);
padding: var(--Spacing-x1) var(--Spacing-x2);
background-color: var(--Base-Surface-Primary-light-Normal);
border-width: 0;
cursor: pointer;
border-radius: 2.5rem;
color: var(--Base-Text-Accent);
font-family: var(--typography-Body-Bold-fontFamily);
font-size: var(--typography-Body-Bold-fontSize);
font-weight: 500;
font-family: var(--typography-Caption-Bold-Desktop-fontFamily);
font-size: var(--typography-Caption-Bold-Desktop-fontSize);
font-weight: var(--typography-Caption-Bold-Desktop-fontWeight);
}
.button:hover {
background-color: var(--Base-Surface-Primary-light-Hover);

View File

@@ -1,30 +0,0 @@
# Google Map Static API
### About
The Google Maps Static API lets you embed a Google Maps image on your web page. The Google Maps Static API service creates your map based on URL parameters sent through a standard HTTP request and returns the map as an image you can display on your web page. Due to regulations from Google we are not allowed to store and serve copies of images generated using the Google Maps Static API. All web pages that require static images must link the src attribute of an HTML img tag or as a background-image using CSS.
### API Key Restrictions
You can restrict your API key on websites, IP addresses, Android apps and iOS apps. For now (12/8-24) the API key is restricted by IP address, hence the request will only work at Scandic HQ network. This will be changed to a referrer website in the future.
[Read more about API key restrictions](https://developers.google.com/maps/api-security-best-practices#restricting-api-keys)
### Digital Signature
Requests exceeding 25,000 requests per day require an API key and a digital signature. However, it is strongly recommended by Google to use both an API key and digital signature, regardless of the usage.
The digital signature is set on the Google Maps Platform. For dynamically generated requests, the signature is handled through server side signing appending the signature as base64 on the request url.
[Read more about digital signature](https://developers.google.com/maps/documentation/maps-static/digital-signature)
### Generating new API keys
Regenerating an API key creates a new key that has all the old key's restrictions. This process also starts a 24-hour timer after which the old API key is deleted. During this time window, both the old and new key are accepted, giving you a chance to migrate your apps to use the new key. However, after this time period elapses, any apps still using the old API key stop working.
**Caution:** Only regenerate an API key if you absolutely must to avoid unauthorized use. This process can shut down legitimate traffic and prevent your apps from functioning properly.
[Read more about regenerating keys](https://developers.google.com/maps/api-security-best-practices#regenerate-apikey)
### Version history
| Description | Version | Date | Author |
| ------------------- | ------- | ------- | ---------------- |
| Create ReadMe file. | 1.0.0 | 12/8-24 | Fredrik Thorsson |

View File

@@ -1,15 +1,14 @@
/* eslint-disable @next/next/no-img-element */
import { env } from "@/env/server"
import StaticMapComp from "@/components/Maps/StaticMap"
import { getIntl } from "@/i18n"
import { calculateLatWithOffset } from "@/utils/map"
import ScandicMarker from "../Markers/Scandic"
import { calculateLatWithOffset, getUrlWithSignature } from "./util"
import styles from "./staticMap.module.css"
import { StaticMapProps } from "@/types/components/hotelPage/staticMap"
import type { StaticMapProps } from "@/types/components/hotelPage/map/staticMap"
export default async function StaticMap({
coordinates,
@@ -17,34 +16,23 @@ export default async function StaticMap({
zoomLevel = 14,
}: StaticMapProps) {
const intl = await getIntl()
const key = env.GOOGLE_STATIC_MAP_KEY
const secret = env.GOOGLE_STATIC_MAP_SIGNATURE_SECRET
const baseUrl = "https://maps.googleapis.com/maps/api/staticmap"
const { lng, lat } = coordinates
const mapHeight = 785
const markerHeight = 100
const mapLatitudeInPx = mapHeight * 0.2
const size = `380x${mapHeight}`
const mapLatitude = calculateLatWithOffset(lat, mapLatitudeInPx, zoomLevel)
// Custom Icon should be available from a public URL accessible by Google Static Maps API. At the moment, we don't have a public URL for the custom icon.
// const marker = `icon:https://IMAGE_URL|${lat},${lng}`
const marker = `${lat},${lng}`
// Google Maps Static API only supports images smaller than 640x640px. Read: https://developers.google.com/maps/documentation/maps-static/start#Largerimagesizes
const alt = intl.formatMessage({ id: "Map of HOTEL_NAME" }, { hotelName })
const url = new URL(
`${baseUrl}?zoom=${zoomLevel}&center=${mapLatitude},${lng}&size=${size}&key=${key}`
)
// const url = new URL(
// `${baseUrl}?zoom=${zoomLevel}&center=${mapLatitude},${lng}&size=${size}&markers=${marker}&key=${key}`
// )
const src = getUrlWithSignature(url, secret)
const mapCoordinates = {
lat: calculateLatWithOffset(coordinates.lat, mapLatitudeInPx, zoomLevel),
lng: coordinates.lng,
}
return (
<div className={styles.staticMap}>
<img src={src} alt={alt} />
<StaticMapComp
coordinates={mapCoordinates}
width={380}
height={mapHeight}
zoomLevel={zoomLevel}
altText={intl.formatMessage({ id: "Map of HOTEL_NAME" }, { hotelName })}
/>
<ScandicMarker
className={styles.mapMarker}
height={markerHeight}

View File

@@ -40,7 +40,7 @@
}
.mainSection {
grid-area: mainSection;
padding: var(--Spacing-x6) 0;
padding: var(--Spacing-x6) var(--Spacing-x4) 0;
}
.mapContainer {
display: flex;

View File

@@ -1,15 +1,15 @@
import { env } from "@/env/server"
import { serverClient } from "@/lib/trpc/server"
import AmenitiesList from "./AmenitiesList"
import Facilities from "./Facilities"
import { MOCK_FACILITIES } from "./Facilities/mockData"
import { setActivityCard } from "./Facilities/utils"
import IntroSection from "./IntroSection"
import DynamicMap from "./Map/DynamicMap"
import MapCard from "./Map/MapCard"
import MobileMapToggle from "./Map/MobileMapToggle"
import StaticMap from "./Map/StaticMap"
import AmenitiesList from "./AmenitiesList"
import Facilities from "./Facilities"
import IntroSection from "./IntroSection"
import PreviewImages from "./PreviewImages"
import { Rooms } from "./Rooms"
import SidePeeks from "./SidePeeks"
@@ -69,7 +69,6 @@ export default async function HotelPage() {
{googleMapsApiKey ? (
<>
<aside className={styles.mapContainer}>
{/* <Map coordinates={coordinates} hotelName={hotelName} /> */}
<StaticMap coordinates={coordinates} hotelName={hotelName} />
<MapCard hotelName={hotelName} />
</aside>

View File

@@ -1,5 +1,6 @@
"use client"
import { useEffect } from "react"
import { Dialog, Modal } from "react-aria-components"
import { useIntl } from "react-intl"
@@ -38,6 +39,13 @@ export default function MobileMenu({
}
})
// Making sure the menu is always opened at the top of the page, just below the header.
useEffect(() => {
if (isHamburgerMenuOpen) {
window.scrollTo({ top: 0, behavior: "instant" })
}
}, [isHamburgerMenuOpen])
return (
<>
<button

View File

@@ -1,5 +1,6 @@
"use client"
import { useEffect } from "react"
import { Dialog, Modal } from "react-aria-components"
import { useIntl } from "react-intl"
@@ -31,6 +32,13 @@ export default function MyPagesMobileMenu({
}
})
// Making sure the menu is always opened at the top of the page, just below the header.
useEffect(() => {
if (isMyPagesMobileMenuOpen) {
window.scrollTo({ top: 0, behavior: "instant" })
}
}, [isMyPagesMobileMenuOpen])
return (
<div className={styles.myPagesMobileMenu}>
<MainMenuButton

36
components/Icons/Map.tsx Normal file
View File

@@ -0,0 +1,36 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function MapIcon({ className, color, ...props }: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="25"
height="25"
viewBox="0 0 25 25"
fill="none"
{...props}
>
<mask
id="mask0_4031_935"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="25"
height="25"
>
<rect x="0.5" y="0.194336" width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_4031_935)">
<path
d="M15.5 21.1943L9.5 19.0943L4.85 20.8943C4.51667 21.0277 4.20833 20.9902 3.925 20.7818C3.64167 20.5735 3.5 20.2943 3.5 19.9443V5.94434C3.5 5.72767 3.5625 5.536 3.6875 5.36934C3.8125 5.20267 3.98333 5.07767 4.2 4.99434L9.5 3.19434L15.5 5.29434L20.15 3.49434C20.4833 3.361 20.7917 3.3985 21.075 3.60684C21.3583 3.81517 21.5 4.09434 21.5 4.44434V18.4443C21.5 18.661 21.4375 18.8527 21.3125 19.0193C21.1875 19.186 21.0167 19.311 20.8 19.3943L15.5 21.1943ZM14.5 18.7443V7.04434L10.5 5.64434V17.3443L14.5 18.7443ZM16.5 18.7443L19.5 17.7443V5.89434L16.5 7.04434V18.7443ZM5.5 18.4943L8.5 17.3443V5.64434L5.5 6.64434V18.4943Z"
fill="white"
/>
</g>
</svg>
)
}

View File

@@ -33,6 +33,7 @@ import {
InfoCircleIcon,
LocationIcon,
LockIcon,
MapIcon,
MinusIcon,
ParkingIcon,
People2Icon,
@@ -116,6 +117,8 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
return LocationIcon
case IconName.Lock:
return LockIcon
case IconName.Map:
return MapIcon
case IconName.Minus:
return MinusIcon
case IconName.Parking:

View File

@@ -29,6 +29,7 @@ export { default as ImageIcon } from "./Image"
export { default as InfoCircleIcon } from "./InfoCircle"
export { default as LocationIcon } from "./Location"
export { default as LockIcon } from "./Lock"
export { default as MapIcon } from "./Map"
export { default as MinusIcon } from "./Minus"
export { default as ParkingIcon } from "./Parking"
export { default as People2Icon } from "./People2"

View File

@@ -1,46 +1,33 @@
/* eslint-disable @next/next/no-img-element */
import crypto from "node:crypto"
import { env } from "@/env/server"
import { StaticMapProps } from "@/types/components/maps/staticMap/staticMap"
import { getUrlWithSignature } from "@/utils/map"
function removeWebSafe(safeEncodedString: string) {
return safeEncodedString.replace(/-/g, "+").replace(/_/g, "/")
}
function makeWebSafe(encodedString: string) {
return encodedString.replace(/\+/g, "-").replace(/\//g, "_")
}
function decodeBase64Hash(code: string) {
return Buffer.from(code, "base64")
}
function encodeBase64Hash(key: Buffer, data: string) {
return crypto.createHmac("sha1", key).update(data).digest("base64")
}
import { StaticMapProps } from "@/types/components/maps/staticMap"
export default function StaticMap({
city,
coordinates,
width,
height,
zoomLevel,
mapType,
zoomLevel = 14,
mapType = "roadmap",
altText,
}: StaticMapProps) {
const key = env.GOOGLE_STATIC_MAP_KEY
const secret = env.GOOGLE_STATIC_MAP_SIGNATURE_SECRET
const safeSecret = decodeBase64Hash(removeWebSafe(secret ?? ""))
const baseUrl = "https://maps.googleapis.com/maps/api/staticmap"
const center = coordinates ? `${coordinates.lat},${coordinates.lng}` : city
if (!center) {
return null
}
// Google Maps Static API only supports images smaller than 640x640px. Read: https://developers.google.com/maps/documentation/maps-static/start#Largerimagesizes
const url = new URL(
`https://maps.googleapis.com/maps/api/staticmap?center=${city}&zoom=${zoomLevel}&size=${width}x${height}&maptype=${mapType}&key=${key}`
`${baseUrl}?center=${center}&zoom=${zoomLevel}&size=${width}x${height}&maptype=${mapType}&key=${key}`
)
const src = getUrlWithSignature(url, secret)
const hashedSignature = makeWebSafe(
encodeBase64Hash(safeSecret, url.pathname + url.search)
)
const src = url.toString() + "&signature=" + hashedSignature
return <img src={src} alt={`Map of ${city} city center`} />
return <img src={src} alt={altText} />
}

View File

@@ -40,6 +40,7 @@
"Close language menu": "Luk sprogmenu",
"Close menu": "Luk menu",
"Close my pages menu": "Luk mine sider menu",
"Close the map": "Luk kortet",
"Coming up": "Er lige om hjørnet",
"Compare all levels": "Sammenlign alle niveauer",
"Contact us": "Kontakt os",
@@ -62,6 +63,7 @@
"Edit profile": "Rediger profil",
"Email": "E-mail",
"Explore all levels and benefits": "Udforsk alle niveauer og fordele",
"Explore nearby": "Udforsk i nærheden",
"Extras to your booking": "Tillæg til din booking",
"Failed to delete credit card, please try again later.": "Kunne ikke slette kreditkort. Prøv venligst igen senere.",
"Find booking": "Find booking",
@@ -76,6 +78,7 @@
"Go back to overview": "Gå tilbage til oversigten",
"Hi": "Hei",
"Highest level": "Højeste niveau",
"Hotel": "Hotel",
"Hotel facilities": "Hotel faciliteter",
"Hotel surroundings": "Hotel omgivelser",
"How do you want to sleep?": "Hvordan vil du sove?",
@@ -98,6 +101,7 @@
"Log in/Join": "Log på/Tilmeld dig",
"Log out": "Log ud",
"Manage preferences": "Administrer præferencer",
"Map": "Kort",
"Map of HOTEL_NAME": "Map of {hotelName}",
"Meetings & Conferences": "Møder & Konferencer",
"Member price": "Medlemspris",
@@ -115,6 +119,7 @@
"My pages menu": "Mine sider menu",
"My payment cards": "Mine betalingskort",
"My wishes": "Mine ønsker",
"Nearby": "I nærheden",
"New password": "Nyt kodeord",
"Next": "Næste",
"next level:": "Næste niveau:",
@@ -144,8 +149,8 @@
"Phone is required": "Telefonnummer er påkrævet",
"Phone number": "Telefonnummer",
"Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer",
"Points": "Point",
"points": "Point",
"Points": "Point",
"Points being calculated": "Point udregnes",
"Points earned prior to May 1, 2021": "Point optjent inden 1. maj 2021",
"Points may take up to 10 days to be displayed.": "Det kan tage op til 10 dage at få vist point.",
@@ -191,6 +196,7 @@
"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",
"There are no transactions to display": "Der er ingen transaktioner at vise",
"Things nearby HOTEL_NAME": "Ting i nærheden af {hotelName}",
"to": "til",
"Total Points": "Samlet antal point",
"Transaction date": "Overførselsdato",
@@ -202,6 +208,8 @@
"uppercase letter": "stort bogstav",
"Use bonus cheque": "Brug Bonus Cheque",
"User information": "Brugeroplysninger",
"View as list": "Vis som liste",
"View as map": "Vis som kort",
"View your booking": "Se din booking",
"Visiting address": "Besøgsadresse",
"We could not add a card right now, please try again later.": "Vi kunne ikke tilføje et kort lige nu. Prøv venligst igen senere.",
@@ -229,5 +237,7 @@
"Your details": "Dine oplysninger",
"Your level": "Dit niveau",
"Your points to spend": "Dine brugbare point",
"Zip code": "Postnummer"
"Zip code": "Postnummer",
"Zoom in": "Zoom ind",
"Zoom out": "Zoom ud"
}

View File

@@ -38,6 +38,7 @@
"Close language menu": "Sprachmenü schließen",
"Close menu": "Menü schließen",
"Close my pages menu": "Meine Seiten Menü schließen",
"Close the map": "Karte schließen",
"Coming up": "Demnächst",
"Compare all levels": "Vergleichen Sie alle Levels",
"Contact us": "Kontaktieren Sie uns",
@@ -60,6 +61,7 @@
"Edit profile": "Profil bearbeiten",
"Email": "Email",
"Explore all levels and benefits": "Entdecken Sie alle Levels und Vorteile",
"Explore nearby": "Erkunden Sie die Umgebung",
"Extras to your booking": "Extras zu Ihrer Buchung",
"Failed to delete credit card, please try again later.": "Kreditkarte konnte nicht gelöscht werden. Bitte versuchen Sie es später noch einmal.",
"Find booking": "Buchung finden",
@@ -74,6 +76,7 @@
"Go back to overview": "Zurück zur Übersicht",
"Hi": "Hallo",
"Highest level": "Höchstes Level",
"Hotel": "Hotel",
"Hotel facilities": "Hotel-Infos",
"Hotel surroundings": "Umgebung des Hotels",
"How do you want to sleep?": "Wie möchtest du schlafen?",
@@ -96,6 +99,7 @@
"Log in/Join": "Log in/Anmelden",
"Log out": "Ausloggen",
"Manage preferences": "Verwalten von Voreinstellungen",
"Map": "Karte",
"Map of HOTEL_NAME": "Map of {hotelName}",
"Member price": "Mitgliederpreis",
"Member price from": "Mitgliederpreis ab",
@@ -112,6 +116,7 @@
"My pages menu": "Meine Seite Menü",
"My payment cards": "Meine Zahlungskarten",
"My wishes": "Meine Wünsche",
"Nearby": "In der Nähe",
"New password": "Neues Kennwort",
"Next": "Nächste",
"next level:": "Nächstes Level:",
@@ -140,8 +145,8 @@
"Phone is required": "Telefon ist erforderlich",
"Phone number": "Telefonnummer",
"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 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.",
@@ -185,6 +190,7 @@
"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",
"There are no transactions to display": "Es sind keine Transaktionen zum Anzeigen vorhanden",
"Things nearby HOTEL_NAME": "Dinge in der Nähe von {hotelName}",
"to": "zu",
"Total Points": "Gesamtpunktzahl",
"Transaction date": "Transaktionsdatum",
@@ -196,6 +202,8 @@
"uppercase letter": "großbuchstabe",
"Use bonus cheque": "Bonusscheck nutzen",
"User information": "Nutzerinformation",
"View as list": "Als Liste anzeigen",
"View as map": "Als Karte anzeigen",
"View your booking": "Ihre Buchung ansehen",
"Visiting address": "Besuchsadresse",
"We could not add a card right now, please try again later.": "Wir konnten momentan keine Karte hinzufügen. Bitte versuchen Sie es später noch einmal.",
@@ -222,5 +230,7 @@
"Your details": "Ihre Angaben",
"Your level": "Dein level",
"Your points to spend": "Meine Punkte",
"Zip code": "PLZ"
"Zip code": "PLZ",
"Zoom in": "Vergrößern",
"Zoom out": "Verkleinern"
}

View File

@@ -39,6 +39,7 @@
"Close language menu": "Close language menu",
"Close menu": "Close menu",
"Close my pages menu": "Close my pages menu",
"Close the map": "Close the map",
"Coming up": "Coming up",
"Compare all levels": "Compare all levels",
"Contact us": "Contact us",
@@ -62,6 +63,7 @@
"Edit profile": "Edit profile",
"Email": "Email",
"Explore all levels and benefits": "Explore all levels and benefits",
"Explore nearby": "Explore nearby",
"Extras to your booking": "Extras to your booking",
"Failed to delete credit card, please try again later.": "Failed to delete credit card, please try again later.",
"FAQ": "FAQ",
@@ -77,6 +79,7 @@
"Go back to overview": "Go back to overview",
"Hi": "Hi",
"Highest level": "Highest level",
"Hotel": "Hotel",
"Hotel facilities": "Hotel facilities",
"Hotel surroundings": "Hotel surroundings",
"hotelPages.rooms.roomCard.person": "person",
@@ -102,6 +105,7 @@
"Log in/Join": "Log in/Join",
"Log out": "Log out",
"Manage preferences": "Manage preferences",
"Map": "Map",
"Map of HOTEL_NAME": "Map of {hotelName}",
"Meetings & Conferences": "Meetings & Conferences",
"Member price": "Member price",
@@ -119,6 +123,7 @@
"My pages menu": "My pages menu",
"My payment cards": "My payment cards",
"My wishes": "My wishes",
"Nearby": "Nearby",
"New password": "New password",
"Next": "Next",
"next level:": "next level:",
@@ -148,8 +153,8 @@
"Phone is required": "Phone is required",
"Phone number": "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 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.",
@@ -196,6 +201,7 @@
"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",
"There are no transactions to display": "There are no transactions to display",
"Things nearby HOTEL_NAME": "Things nearby {hotelName}",
"to": "to",
"Total Points": "Total Points",
"Transaction date": "Transaction date",
@@ -207,6 +213,8 @@
"uppercase letter": "uppercase letter",
"Use bonus cheque": "Use bonus cheque",
"User information": "User information",
"View as list": "View as list",
"View as map": "View as map",
"View your booking": "View your booking",
"Visiting address": "Visiting address",
"We could not add a card right now, please try again later.": "We could not add a card right now, please try again later.",
@@ -234,5 +242,7 @@
"Your details": "Your details",
"Your level": "Your level",
"Your points to spend": "Your points to spend",
"Zip code": "Zip code"
"Zip code": "Zip code",
"Zoom in": "Zoom in",
"Zoom out": "Zoom out"
}

View File

@@ -39,6 +39,7 @@
"Close language menu": "Sulje kielivalikko",
"Close menu": "Sulje valikko",
"Close my pages menu": "Sulje omat sivut -valikko",
"Close the map": "Sulje kartta",
"Coming up": "Tulossa",
"Compare all levels": "Vertaa kaikkia tasoja",
"Contact us": "Ota meihin yhteyttä",
@@ -61,6 +62,7 @@
"Edit profile": "Muokkaa profiilia",
"Email": "Sähköposti",
"Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin",
"Explore nearby": "Tutustu lähialueeseen",
"Extras to your booking": "Varauksessa lisäpalveluita",
"Failed to delete credit card, please try again later.": "Luottokortin poistaminen epäonnistui, yritä myöhemmin uudelleen.",
"Find booking": "Etsi varaus",
@@ -75,6 +77,7 @@
"Go back to overview": "Palaa yleiskatsaukseen",
"Hi": "Hi",
"Highest level": "Korkein taso",
"Hotel": "Hotelli",
"Hotel facilities": "Hotellin palvelut",
"Hotel surroundings": "Hotellin ympäristö",
"How do you want to sleep?": "Kuinka haluat nukkua?",
@@ -97,6 +100,7 @@
"Log in/Join": "Kirjaudu sisään/Liittyä",
"Log out": "Kirjaudu ulos",
"Manage preferences": "Asetusten hallinta",
"Map": "Kartta",
"Map of HOTEL_NAME": "Map of {hotelName}",
"Meetings & Conferences": "Kokoukset & Konferenssit",
"Member price": "Jäsenhinta",
@@ -114,6 +118,7 @@
"My pages menu": "Omat sivut -valikko",
"My payment cards": "Minun maksukortit",
"My wishes": "Toiveeni",
"Nearby": "Lähistöllä",
"New password": "Uusi salasana",
"Next": "Seuraava",
"next level:": "pistettä tasolle:",
@@ -143,8 +148,8 @@
"Phone is required": "Puhelin vaaditaan",
"Phone number": "Puhelinnumero",
"Please enter a valid phone number": "Ole hyvä ja näppäile voimassaoleva puhelinnumero",
"Points": "Pisteet",
"points": "pistettä",
"Points": "Pisteet",
"Points being calculated": "Pisteitä lasketaan",
"Points earned prior to May 1, 2021": "Pisteet, jotka ansaittu ennen 1.5.2021",
"Points may take up to 10 days to be displayed.": "Pisteiden näyttäminen voi kestää jopa 10 päivää.",
@@ -190,6 +195,7 @@
"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",
"There are no transactions to display": "Näytettäviä tapahtumia ei ole",
"Things nearby HOTEL_NAME": "Lähellä olevia asioita {hotelName}",
"to": "to",
"Total Points": "Kokonaispisteet",
"Transaction date": "Tapahtuman päivämäärä",
@@ -201,6 +207,8 @@
"uppercase letter": "iso kirjain",
"Use bonus cheque": "Käytä bonussekkiä",
"User information": "Käyttäjän tiedot",
"View as list": "Näytä listana",
"View as map": "Näytä kartalla",
"View your booking": "Näytä varauksesi",
"Visiting address": "Käyntiosoite",
"We could not add a card right now, please try again later.": "Emme voineet lisätä korttia juuri nyt. Yritä myöhemmin uudelleen.",
@@ -228,5 +236,7 @@
"Your details": "Tietosi",
"Your level": "Tasosi",
"Your points to spend": "Käytettävissä olevat pisteesi",
"Zip code": "Postinumero"
"Zip code": "Postinumero",
"Zoom in": "Lähennä",
"Zoom out": "Loitonna"
}

View File

@@ -39,6 +39,7 @@
"Close language menu": "Lukk språkmeny",
"Close menu": "Lukk meny",
"Close my pages menu": "Lukk mine sidermenyn",
"Close the map": "Lukk kartet",
"Coming up": "Kommer opp",
"Compare all levels": "Sammenlign alle nivåer",
"Contact us": "Kontakt oss",
@@ -61,6 +62,7 @@
"Edit profile": "Rediger profil",
"Email": "E-post",
"Explore all levels and benefits": "Utforsk alle nivåer og fordeler",
"Explore nearby": "Utforsk i nærheten",
"Extras to your booking": "Tilvalg til bestillingen din",
"Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.",
"Find booking": "Finn booking",
@@ -75,6 +77,7 @@
"Go back to overview": "Gå tilbake til oversikten",
"Hi": "Hei",
"Highest level": "Høyeste nivå",
"Hotel": "Hotel",
"Hotel facilities": "Hotelfaciliteter",
"Hotel surroundings": "Hotellomgivelser",
"How do you want to sleep?": "Hvordan vil du sove?",
@@ -97,6 +100,7 @@
"Log in/Join": "Logg på/Bli med",
"Log out": "Logg ut",
"Manage preferences": "Administrer preferanser",
"Map": "Kart",
"Map of HOTEL_NAME": "Map of {hotelName}",
"Meetings & Conferences": "Møter & Konferanser",
"Member price": "Medlemspris",
@@ -114,6 +118,7 @@
"My pages menu": "Mine sider-menyen",
"My payment cards": "Mine betalingskort",
"My wishes": "Mine ønsker",
"Nearby": "I nærheten",
"New password": "Nytt passord",
"Next": "Neste",
"next level:": "Neste nivå:",
@@ -143,8 +148,8 @@
"Phone is required": "Telefon kreves",
"Phone number": "Telefonnummer",
"Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer",
"points": "poeng",
"Points": "Poeng",
"points": "poeng",
"Points being calculated": "Poeng beregnes",
"Points earned prior to May 1, 2021": "Opptjente poeng før 1. mai 2021",
"Points may take up to 10 days to be displayed.": "Det kan ta opptil 10 dager før poeng vises.",
@@ -190,6 +195,7 @@
"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",
"There are no transactions to display": "Det er ingen transaksjoner å vise",
"Things nearby HOTEL_NAME": "Ting i nærheten av {hotelName}",
"to": "til",
"Total Points": "Totale poeng",
"Transaction date": "Transaksjonsdato",
@@ -201,6 +207,8 @@
"uppercase letter": "stor bokstav",
"Use bonus cheque": "Bruk bonussjekk",
"User information": "Brukerinformasjon",
"View as list": "Vis som liste",
"View as map": "Vis som kart",
"View your booking": "Se din bestilling",
"Visiting address": "Besøksadresse",
"We could not add a card right now, please try again later.": "Vi kunne ikke legge til et kort akkurat nå. Prøv igjen senere.",
@@ -228,5 +236,7 @@
"Your details": "Dine detaljer",
"Your level": "Ditt nivå",
"Your points to spend": "Dine brukbare poeng",
"Zip code": "Post kode"
"Zip code": "Post kode",
"Zoom in": "Zoom inn",
"Zoom out": "Zoom ut"
}

View File

@@ -39,6 +39,7 @@
"Close language menu": "Stäng språkmenyn",
"Close menu": "Stäng menyn",
"Close my pages menu": "Stäng mina sidor menyn",
"Close the map": "Stäng kartan",
"Coming up": "Kommer härnäst",
"Compare all levels": "Jämför alla nivåer",
"Contact us": "Kontakta oss",
@@ -61,6 +62,7 @@
"Edit profile": "Redigera profil",
"Email": "E-post",
"Explore all levels and benefits": "Utforska alla nivåer och fördelar",
"Explore nearby": "Utforska i närheten",
"Extras to your booking": "Extra tillval till din bokning",
"Failed to delete credit card, please try again later.": "Det gick inte att ta bort kreditkortet, försök igen senare.",
"Find booking": "Hitta bokning",
@@ -75,6 +77,7 @@
"Go back to overview": "Gå tillbaka till översikten",
"Hi": "Hej",
"Highest level": "Högsta nivå",
"Hotel": "Hotell",
"Hotel facilities": "Hotellfaciliteter",
"Hotel surroundings": "Hotellomgivning",
"hotelPages.rooms.roomCard.person": "person",
@@ -99,6 +102,7 @@
"Log in/Join": "Logga in/Gå med",
"Log out": "Logga ut",
"Manage preferences": "Hantera inställningar",
"Map": "Karta",
"Map of HOTEL_NAME": "Map of {hotelName}",
"Meetings & Conferences": "Möten & Konferenser",
"Member price": "Medlemspris",
@@ -116,6 +120,7 @@
"My pages menu": "Mina sidor meny",
"My payment cards": "Mina betalningskort",
"My wishes": "Mina önskningar",
"Nearby": "I närheten",
"New password": "Nytt lösenord",
"Next": "Nästa",
"next level:": "Nästa nivå:",
@@ -145,8 +150,8 @@
"Phone is required": "Telefonnummer är obligatorisk",
"Phone number": "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 earned prior to May 1, 2021": "Intjänade poäng före den 1 maj 2021",
"Points may take up to 10 days to be displayed.": "Det kan ta upp till 10 dagar innan poäng visas.",
@@ -193,6 +198,7 @@
"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",
"There are no transactions to display": "Det finns inga transaktioner att visa",
"Things nearby HOTEL_NAME": "Saker i närheten av {hotelName}",
"to": "till",
"Total Points": "Poäng totalt",
"Transaction date": "Transaktionsdatum",
@@ -204,6 +210,8 @@
"uppercase letter": "stor bokstav",
"Use bonus cheque": "Use bonus cheque",
"User information": "Användarinformation",
"View as list": "Visa som lista",
"View as map": "Visa som karta",
"View your booking": "Visa din bokning",
"Visiting address": "Besöksadress",
"We could not add a card right now, please try again later.": "Vi kunde inte lägga till ett kort just nu, vänligen försök igen senare.",
@@ -230,5 +238,7 @@
"Your details": "Dina uppgifter",
"Your level": "Din nivå",
"Your points to spend": "Dina spenderbara poäng",
"Zip code": "Postnummer"
"Zip code": "Postnummer",
"Zoom in": "Zooma in",
"Zoom out": "Zooma ut"
}

View File

@@ -0,0 +1,7 @@
import type { Coordinates } from "../../maps/coordinates"
export interface DynamicMapProps {
apiKey: string
hotelName: string
coordinates: Coordinates
}

View File

@@ -0,0 +1,6 @@
import type { Coordinates } from "../../maps/coordinates"
export interface DynamicMapContentProps {
hotelName: string
coordinates: Coordinates
}

View File

@@ -1,7 +1,4 @@
type Coordinates = {
lat: number
lng: number
}
import type { Coordinates } from "../../maps/coordinates"
export type StaticMapProps = {
coordinates: Coordinates

View File

@@ -38,6 +38,7 @@ export enum IconName {
Instagram = "Instagram",
Location = "Location",
Lock = "Lock",
Map = "Map",
Minus = "Minus",
Parking = "Parking",
Person = "Person",

View File

@@ -0,0 +1,4 @@
export interface Coordinates {
lat: number
lng: number
}

View File

@@ -0,0 +1,11 @@
import { Coordinates } from "./coordinates"
export type StaticMapProps = {
city?: string
coordinates?: Coordinates
width: number
height: number
zoomLevel?: number
mapType?: "roadmap" | "satellite" | "terrain" | "hybrid"
altText: string
}

View File

@@ -1,7 +0,0 @@
export type StaticMapProps = {
city: string
width: number
height: number
zoomLevel: number
mapType: "roadmap" | "satellite" | "terrain" | "hybrid"
}