Merged in feat/SW-1276-implement-design (pull request #1348)
Feat/SW-1276 implement design * feat(SW-1276) UI implementation Desktop part 1 for MyStay * feat(SW-1276) UI implementation Desktop part 2 for MyStay * feat(SW-1276) UI implementation Mobile part 1 for MyStay * refactor: move files from MyStay/MyStay to MyStay * feat(SW-1276) Sidepeek implementation * feat(SW-1276): Refactoring * feat(SW-1276) UI implementation Mobile part 2 for MyStay * feat(SW-1276): translations * feat(SW-1276) fixed skeleton * feat(SW-1276): Added missing translations * feat(SW-1276): Removed console log * feat(SW-1276) fixed translations * feat(SW-1276): Added translations * feat(SW-1276) fix dynamic ID:s * feat(SW-1276) removed createElement * feat(SW-1276): Fixed build errors * feat(SW-1276): Updated label * feat(SW-1276): Rewrite SummaryCard Approved-by: Niclas Edenvin
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { getHotel } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import SidePeek from "@/components/HotelReservation/SidePeek"
|
||||
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function HotelSidePeek({
|
||||
params,
|
||||
searchParams,
|
||||
}: PageArgs<LangParams, { hotel: string }>) {
|
||||
if (!searchParams.hotel) {
|
||||
return <SidePeek hotel={null} />
|
||||
}
|
||||
|
||||
const hotel = await getHotel({
|
||||
hotelId: searchParams.hotel,
|
||||
language: params.lang,
|
||||
isCardOnlyPayment: false,
|
||||
})
|
||||
|
||||
return <SidePeek hotel={hotel} />
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { LangParams, LayoutArgs } from "@/types/params"
|
||||
|
||||
export default function HotelReservationLayout({
|
||||
children,
|
||||
sidePeek,
|
||||
}: React.PropsWithChildren<LayoutArgs<LangParams>> & {
|
||||
sidePeek: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
{children}
|
||||
{sidePeek}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
|
||||
export default function Loading() {
|
||||
return <LoadingSpinner fullPage />
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Suspense } from "react"
|
||||
|
||||
import { MyStay } from "@/components/HotelReservation/MyStay/MyStay"
|
||||
import { MyStaySkeleton } from "@/components/HotelReservation/MyStay/MyStay/myStaySkeleton"
|
||||
import { MyStay } from "@/components/HotelReservation/MyStay"
|
||||
import { MyStaySkeleton } from "@/components/HotelReservation/MyStay/myStaySkeleton"
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
@@ -11,7 +11,10 @@ import styles from "./header.module.css"
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
import type { ToggleSidePeekProps } from "@/types/components/hotelReservation/toggleSidePeekProps"
|
||||
|
||||
export default function ToggleSidePeek({ hotelId }: ToggleSidePeekProps) {
|
||||
export default function ToggleSidePeek({
|
||||
hotelId,
|
||||
intent = "textInverted",
|
||||
}: ToggleSidePeekProps) {
|
||||
const intl = useIntl()
|
||||
const openSidePeek = useSidePeekStore((state) => state.openSidePeek)
|
||||
|
||||
@@ -21,7 +24,7 @@ export default function ToggleSidePeek({ hotelId }: ToggleSidePeekProps) {
|
||||
theme="base"
|
||||
size="small"
|
||||
variant="icon"
|
||||
intent="textInverted"
|
||||
intent={intent}
|
||||
wrapping
|
||||
className={styles.toggle}
|
||||
>
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { ToggleSidePeekProps } from "@/types/components/hotelReservation/to
|
||||
export default function ToggleSidePeek({
|
||||
hotelId,
|
||||
roomTypeCode,
|
||||
intent = "textInverted",
|
||||
}: ToggleSidePeekProps) {
|
||||
const intl = useIntl()
|
||||
const openSidePeek = useSidePeekStore((state) => state.openSidePeek)
|
||||
@@ -25,7 +26,7 @@ export default function ToggleSidePeek({
|
||||
theme="base"
|
||||
size="small"
|
||||
variant="icon"
|
||||
intent="text"
|
||||
intent={intent}
|
||||
wrapping
|
||||
>
|
||||
{intl.formatMessage({ id: "See room details" })}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import Image from "@/components/Image"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
|
||||
import styles from "./summaryCard.module.css"
|
||||
|
||||
interface SummaryCardProps {
|
||||
title: string
|
||||
image: {
|
||||
src: string
|
||||
alt: string
|
||||
}
|
||||
texts: string[]
|
||||
supportingText?: string
|
||||
links?: {
|
||||
href: string
|
||||
text: string
|
||||
icon: React.ReactNode
|
||||
}[]
|
||||
chip?: React.ReactNode
|
||||
}
|
||||
|
||||
export default function SummaryCard({
|
||||
title,
|
||||
texts,
|
||||
image,
|
||||
supportingText,
|
||||
links,
|
||||
chip,
|
||||
}: SummaryCardProps) {
|
||||
return (
|
||||
<div className={styles.card}>
|
||||
<div className={styles.image}>
|
||||
<Image src={image.src} alt={image.alt} width={152} height={152} />
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.topContent}>
|
||||
<Body textTransform="bold" color="uiTextHighContrast">
|
||||
{title}
|
||||
</Body>
|
||||
{texts.map((text) => (
|
||||
<Body color="uiTextHighContrast" key={text}>
|
||||
{text}
|
||||
</Body>
|
||||
))}
|
||||
</div>
|
||||
{supportingText && (
|
||||
<Caption color="uiTextPlaceholder">{supportingText}</Caption>
|
||||
)}
|
||||
<div className={styles.bottomContent}>
|
||||
{chip}
|
||||
{links && (
|
||||
<div className={styles.links}>
|
||||
{links.map((link) => (
|
||||
<Caption asChild type="bold" color="burgundy" key={link.href}>
|
||||
<Link
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
color="burgundy"
|
||||
className={styles.link}
|
||||
>
|
||||
{link.icon}
|
||||
{link.text}
|
||||
</Link>
|
||||
</Caption>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.card {
|
||||
align-items: flex-start;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 152px;
|
||||
height: 152px;
|
||||
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.image {
|
||||
background-color: var(--Base-Surface-Secondary-light-Normal);
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.topContent {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bottomContent {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x2);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
.bookingSummary {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x5);
|
||||
}
|
||||
|
||||
.bookingSummaryContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 80px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.bookingSummaryContent {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.toast {
|
||||
width: var(--max-width-content);
|
||||
margin: 0 auto;
|
||||
}
|
||||
130
components/HotelReservation/MyStay/BookingSummary/index.tsx
Normal file
130
components/HotelReservation/MyStay/BookingSummary/index.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import { dt } from "@/lib/dt"
|
||||
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
DirectionsIcon,
|
||||
EmailIcon,
|
||||
LinkIcon,
|
||||
} from "@/components/Icons"
|
||||
import CrossCircleIcon from "@/components/Icons/CrossCircle"
|
||||
import IconChip from "@/components/TempDesignSystem/IconChip"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { Toast } from "@/components/TempDesignSystem/Toasts"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import SummaryCard from "./SummaryCard"
|
||||
|
||||
import styles from "./bookingSummary.module.css"
|
||||
|
||||
import type { Hotel } from "@/types/hotel"
|
||||
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
||||
|
||||
interface BookingSummaryProps {
|
||||
booking: BookingConfirmation["booking"]
|
||||
hotel: Hotel
|
||||
}
|
||||
|
||||
export default async function BookingSummary({
|
||||
booking,
|
||||
hotel,
|
||||
}: BookingSummaryProps) {
|
||||
const intl = await getIntl()
|
||||
const lang = getLang()
|
||||
|
||||
const directionsUrl = `https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}`
|
||||
const isPaid =
|
||||
booking.rateDefinition.cancellationRule !== "CancellableBefore6PM"
|
||||
const bookingDate = dt(booking.createDateTime)
|
||||
.locale(lang)
|
||||
.format("D MMMM YYYY")
|
||||
|
||||
return (
|
||||
<div className={styles.bookingSummary}>
|
||||
<Subtitle textTransform="uppercase" color="burgundy">
|
||||
{intl.formatMessage({ id: "Booking summary" })}
|
||||
</Subtitle>
|
||||
<div className={styles.bookingSummaryContent}>
|
||||
<SummaryCard
|
||||
title={formatPrice(intl, booking.totalPrice, booking.currencyCode)}
|
||||
image={{
|
||||
src: "/_static/img/scandic-coin.svg",
|
||||
alt: "Scandic coin",
|
||||
}}
|
||||
texts={[`${intl.formatMessage({ id: "Payment" })}: N/A`]}
|
||||
supportingText={bookingDate}
|
||||
chip={
|
||||
<IconChip
|
||||
color={isPaid ? "green" : "red"}
|
||||
icon={
|
||||
isPaid ? (
|
||||
<CheckCircleIcon width={20} height={20} color="green" />
|
||||
) : (
|
||||
<CrossCircleIcon width={20} height={20} color="red" />
|
||||
)
|
||||
}
|
||||
>
|
||||
<Caption color={isPaid ? "green" : "red"}>
|
||||
<strong>{intl.formatMessage({ id: "Status" })}:</strong>{" "}
|
||||
{isPaid
|
||||
? intl.formatMessage({ id: "Paid" })
|
||||
: intl.formatMessage({ id: "Unpaid" })}
|
||||
</Caption>
|
||||
</IconChip>
|
||||
}
|
||||
/>
|
||||
<SummaryCard
|
||||
title={hotel.name}
|
||||
image={{
|
||||
src: "/_static/img/scandic-service.svg",
|
||||
alt: "Scandic service",
|
||||
}}
|
||||
texts={[
|
||||
hotel.address.streetAddress,
|
||||
`${hotel.address.zipCode} ${hotel.address.city}`,
|
||||
]}
|
||||
supportingText={intl.formatMessage(
|
||||
{ id: "Long {long} ∙ Lat {lat}" },
|
||||
{
|
||||
lat: hotel.location.latitude,
|
||||
long: hotel.location.longitude,
|
||||
}
|
||||
)}
|
||||
links={[
|
||||
{
|
||||
href: directionsUrl,
|
||||
text: intl.formatMessage({ id: "Directions" }),
|
||||
icon: <DirectionsIcon width={20} height={20} color="burgundy" />,
|
||||
},
|
||||
{
|
||||
href: `mailto:${hotel.contactInformation.email}`,
|
||||
text: intl.formatMessage({ id: "Email" }),
|
||||
icon: <EmailIcon width={20} height={20} color="burgundy" />,
|
||||
},
|
||||
{
|
||||
href: hotel.contactInformation.websiteUrl,
|
||||
text: intl.formatMessage({ id: "Homepage" }),
|
||||
icon: <LinkIcon width={20} height={20} color="burgundy" />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
{hotel.specialAlerts.length > 0 && (
|
||||
<div className={styles.toast}>
|
||||
<Toast variant="info">
|
||||
<ul className={styles.list}>
|
||||
{hotel.specialAlerts.map((alert) => (
|
||||
<li key={alert.id}>
|
||||
<Body color="uiTextHighContrast">{alert.text}</Body>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Toast>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
28
components/HotelReservation/MyStay/Header/header.module.css
Normal file
28
components/HotelReservation/MyStay/Header/header.module.css
Normal file
@@ -0,0 +1,28 @@
|
||||
header .title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
padding-top: var(--Spacing-x6);
|
||||
margin-top: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.title .hotelName {
|
||||
font-family: var(--typography-Title-3-fontFamily);
|
||||
font-size: var(--typography-Title-3-fontSize);
|
||||
font-weight: var(--typography-Title-3-fontWeight);
|
||||
letter-spacing: var(--typography-Title-3-letterSpacing);
|
||||
line-height: var(--typography-Title-3-lineHeight);
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.title .hotelName {
|
||||
font-family: var(--typography-Title-1-fontFamily);
|
||||
font-size: var(--typography-Title-1-fontSize);
|
||||
font-weight: var(--typography-Title-1-fontWeight);
|
||||
letter-spacing: var(--typography-Title-1-letterSpacing);
|
||||
line-height: var(--typography-Title-1-lineHeight);
|
||||
}
|
||||
}
|
||||
22
components/HotelReservation/MyStay/Header/index.tsx
Normal file
22
components/HotelReservation/MyStay/Header/index.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import styles from "./header.module.css"
|
||||
|
||||
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
||||
|
||||
export async function Header({ hotel }: Pick<BookingConfirmation, "hotel">) {
|
||||
const intl = await getIntl()
|
||||
return (
|
||||
<header>
|
||||
<Title as="h2" color="white" className={styles.title} textAlign="center">
|
||||
<BiroScript type="two" tilted="medium">
|
||||
{intl.formatMessage({ id: "My stay at" })}{" "}
|
||||
</BiroScript>
|
||||
<span className={styles.hotelName}>{hotel.name}</span>
|
||||
{hotel.cityName}
|
||||
</Title>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import styles from "./bookingActions.module.css"
|
||||
|
||||
export async function BookingActions() {
|
||||
const intl = await getIntl()
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: "Changes can be made until {time}, {date} pending availability.",
|
||||
},
|
||||
{ time: "15:00", date: "Mon 15 Aug" }
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.actions}>
|
||||
<Button>{intl.formatMessage({ id: "Modify dates" })}</Button>
|
||||
<Button>{intl.formatMessage({ id: "Cancel booking" })}</Button>
|
||||
<Button>{intl.formatMessage({ id: "Customer service" })}</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
||||
|
||||
export async function Header({
|
||||
booking,
|
||||
hotel,
|
||||
}: Pick<BookingConfirmation, "booking" | "hotel">) {
|
||||
const intl = await getIntl()
|
||||
return (
|
||||
<header>
|
||||
<Title as="h2" color="red">
|
||||
{intl.formatMessage(
|
||||
{ id: "My stay at {hotelName}" },
|
||||
{
|
||||
hotelName: hotel.name,
|
||||
}
|
||||
)}
|
||||
</Title>
|
||||
<Title as="h3" level="h2" textTransform="regular">
|
||||
{intl.formatMessage(
|
||||
{ id: "Reservation No. {reservationNumber}" },
|
||||
{
|
||||
reservationNumber: booking.confirmationNumber,
|
||||
}
|
||||
)}
|
||||
</Title>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import { dt } from "@/lib/dt"
|
||||
import {
|
||||
getAncillaryPackages,
|
||||
getBookingConfirmation,
|
||||
} from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
|
||||
import HotelDetails from "../../BookingConfirmation/HotelDetails"
|
||||
import PaymentDetails from "../../BookingConfirmation/PaymentDetails"
|
||||
import Promos from "../../BookingConfirmation/Promos"
|
||||
import Rooms from "../../BookingConfirmation/Rooms"
|
||||
import { Ancillaries } from "./Ancillaries"
|
||||
import { BookingActions } from "./BookingActions"
|
||||
import { Header } from "./Header"
|
||||
|
||||
import styles from "./myStay.module.css"
|
||||
|
||||
export async function MyStay({ reservationId }: { reservationId: string }) {
|
||||
const { booking, hotel, room } = await getBookingConfirmation(reservationId)
|
||||
const fromDate = dt(booking.checkInDate).format("YYYY-MM-DD")
|
||||
const toDate = dt(booking.checkOutDate).format("YYYY-MM-DD")
|
||||
const hotelId = hotel.operaId
|
||||
const ancillaryInput = { fromDate, hotelId, toDate }
|
||||
void getAncillaryPackages(ancillaryInput)
|
||||
const ancillaryPackages = await getAncillaryPackages(ancillaryInput)
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<Header booking={booking} hotel={hotel} />
|
||||
<BookingActions />
|
||||
{room && <Rooms booking={booking} mainRoom={room} />}
|
||||
{booking.showAncillaries && (
|
||||
<Ancillaries ancillaries={ancillaryPackages} />
|
||||
)}
|
||||
<Divider color="primaryLightSubtle" />
|
||||
<PaymentDetails booking={booking} />
|
||||
<Divider color="primaryLightSubtle" />
|
||||
<HotelDetails hotel={hotel} />
|
||||
<Promos
|
||||
hotelId={hotel.operaId}
|
||||
lastName={booking.guest.lastName}
|
||||
confirmationNumber={booking.confirmationNumber}
|
||||
/>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
.main {
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x5);
|
||||
margin: 0 auto;
|
||||
min-height: 100dvh;
|
||||
padding-top: var(--Spacing-x5);
|
||||
width: var(--max-width-page);
|
||||
}
|
||||
|
||||
.headerSkeleton {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.bookingActionsSkeleton {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--Spacing-x2);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bookingActionsSkeletonButtons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.ancillariesSkeleton {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.paymentDetailsSkeleton {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.hotelDetailsSkeleton {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
|
||||
import styles from "./myStay.module.css"
|
||||
|
||||
export async function MyStaySkeleton() {
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<div className={styles.headerSkeleton}>
|
||||
<SkeletonShimmer width={"100%"} height="40px" />
|
||||
<SkeletonShimmer width={"300px"} height="30px" />
|
||||
</div>
|
||||
<div className={styles.bookingActionsSkeleton}>
|
||||
<SkeletonShimmer width={"300px"} height="20px" />
|
||||
<div className={styles.bookingActionsSkeletonButtons}>
|
||||
<SkeletonShimmer width={"125px"} height="50px" />
|
||||
<SkeletonShimmer width={"125px"} height="50px" />
|
||||
<SkeletonShimmer width={"125px"} height="50px" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.roomSkeleton}>
|
||||
<SkeletonShimmer width={"100%"} height="290px" />
|
||||
</div>
|
||||
<div className={styles.ancillariesSkeleton}>
|
||||
<SkeletonShimmer width={"280px"} height="200px" />
|
||||
<SkeletonShimmer width={"280px"} height="200px" />
|
||||
<SkeletonShimmer width={"280px"} height="200px" />
|
||||
<SkeletonShimmer width={"280px"} height="200px" />
|
||||
<SkeletonShimmer width={"280px"} height="200px" />
|
||||
</div>
|
||||
<Divider color="primaryLightSubtle" />
|
||||
<div className={styles.paymentDetailsSkeleton}>
|
||||
<SkeletonShimmer width={"200px"} height="30px" />
|
||||
<SkeletonShimmer width={"180px"} height="20px" />
|
||||
<SkeletonShimmer width={"190px"} height="20px" />
|
||||
<SkeletonShimmer width={"170px"} height="20px" />
|
||||
</div>
|
||||
<Divider color="primaryLightSubtle" />
|
||||
<div className={styles.hotelDetailsSkeleton}>
|
||||
<SkeletonShimmer width={"200px"} height="30px" />
|
||||
<SkeletonShimmer width={"180px"} height="20px" />
|
||||
<SkeletonShimmer width={"190px"} height="20px" />
|
||||
<SkeletonShimmer width={"170px"} height="20px" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
26
components/HotelReservation/MyStay/Promo/index.tsx
Normal file
26
components/HotelReservation/MyStay/Promo/index.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
|
||||
import styles from "./promo.module.css"
|
||||
|
||||
import type { PromoProps } from "@/types/components/hotelReservation/bookingConfirmation/promo"
|
||||
|
||||
export default function Promo({ buttonText, href, text, title }: PromoProps) {
|
||||
return (
|
||||
<Link className={styles.link} color="none" href={href}>
|
||||
<article className={styles.promo}>
|
||||
<Title color="white" level="h4">
|
||||
{title}
|
||||
</Title>
|
||||
<Body className={styles.text} color="white" textAlign="center">
|
||||
{text}
|
||||
</Body>
|
||||
<Button asChild intent="primary" size="small" theme="primaryStrong">
|
||||
<div>{buttonText}</div>
|
||||
</Button>
|
||||
</article>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
33
components/HotelReservation/MyStay/Promo/promo.module.css
Normal file
33
components/HotelReservation/MyStay/Promo/promo.module.css
Normal file
@@ -0,0 +1,33 @@
|
||||
.promo {
|
||||
align-items: center;
|
||||
background-position: 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
flex: 1 0 480px;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
height: 480px;
|
||||
justify-content: center;
|
||||
padding: var(--Spacing-x4) var(--Spacing-x3);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.promo {
|
||||
border-radius: var(--Medium, 8px);
|
||||
}
|
||||
}
|
||||
|
||||
.link .promo {
|
||||
background-image: linear-gradient(
|
||||
180deg,
|
||||
rgba(0, 0, 0, 0) 0%,
|
||||
rgba(0, 0, 0, 0.36) 37.88%,
|
||||
rgba(0, 0, 0, 0.75) 100%
|
||||
),
|
||||
url("/_static/img/Scandic_Family_Breakfast.jpg");
|
||||
}
|
||||
|
||||
.text {
|
||||
max-width: 400px;
|
||||
}
|
||||
131
components/HotelReservation/MyStay/ReferenceCard/index.tsx
Normal file
131
components/HotelReservation/MyStay/ReferenceCard/index.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import { dt } from "@/lib/dt"
|
||||
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import styles from "./referenceCard.module.css"
|
||||
|
||||
import type { Hotel } from "@/types/hotel"
|
||||
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
||||
|
||||
export async function ReferenceCard({
|
||||
booking,
|
||||
hotel,
|
||||
}: {
|
||||
booking: BookingConfirmation["booking"]
|
||||
hotel: Hotel
|
||||
}) {
|
||||
const intl = await getIntl()
|
||||
const lang = getLang()
|
||||
|
||||
const fromDate = dt(booking.checkInDate).locale(lang)
|
||||
const toDate = dt(booking.checkOutDate).locale(lang)
|
||||
|
||||
const directionsUrl = `https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}`
|
||||
|
||||
return (
|
||||
<div className={styles.referenceCard}>
|
||||
<div className={styles.referenceRow}>
|
||||
<Subtitle color="uiTextHighContrast" className={styles.titleMobile}>
|
||||
{intl.formatMessage({ id: "Reference" })}
|
||||
</Subtitle>
|
||||
<Subtitle color="uiTextHighContrast" className={styles.titleDesktop}>
|
||||
{intl.formatMessage({ id: "Reference number" })}
|
||||
</Subtitle>
|
||||
<Subtitle color="uiTextHighContrast">
|
||||
{booking.confirmationNumber}
|
||||
</Subtitle>
|
||||
</div>
|
||||
<Divider color="primaryLightSubtle" className={styles.divider} />
|
||||
<div className={styles.referenceRow}>
|
||||
<Caption
|
||||
textTransform="uppercase"
|
||||
type="bold"
|
||||
color="uiTextHighContrast"
|
||||
>
|
||||
{intl.formatMessage({ id: "Guests" })}
|
||||
</Caption>
|
||||
<Caption type="bold" color="uiTextHighContrast">
|
||||
{booking.childrenAges.length > 0
|
||||
? intl.formatMessage(
|
||||
{ id: "{adults} adults, {children} children" },
|
||||
{
|
||||
adults: booking.adults,
|
||||
children: booking.childrenAges.length,
|
||||
}
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{ id: "{adults} adults" },
|
||||
{
|
||||
adults: booking.adults,
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
</div>
|
||||
<div className={styles.referenceRow}>
|
||||
<Caption
|
||||
textTransform="uppercase"
|
||||
type="bold"
|
||||
color="uiTextHighContrast"
|
||||
>
|
||||
{intl.formatMessage({ id: "Check-in" })}
|
||||
</Caption>
|
||||
<Caption type="bold" color="uiTextHighContrast">
|
||||
{`${fromDate.format("dddd, D MMMM")} ${intl.formatMessage({ id: "from" })} ${fromDate.format("HH:mm")}`}
|
||||
</Caption>
|
||||
</div>
|
||||
<div className={styles.referenceRow}>
|
||||
<Caption
|
||||
textTransform="uppercase"
|
||||
type="bold"
|
||||
color="uiTextHighContrast"
|
||||
>
|
||||
{intl.formatMessage({ id: "Check-out" })}
|
||||
</Caption>
|
||||
<Caption type="bold" color="uiTextHighContrast">
|
||||
{`${toDate.format("dddd, D MMMM")} ${intl.formatMessage({ id: "from" })} ${toDate.format("HH:mm")}`}
|
||||
</Caption>
|
||||
</div>
|
||||
<Divider color="primaryLightSubtle" className={styles.divider} />
|
||||
<div className={styles.referenceRow}>
|
||||
<Caption
|
||||
textTransform="uppercase"
|
||||
type="bold"
|
||||
color="uiTextHighContrast"
|
||||
>
|
||||
{intl.formatMessage({ id: "Total paid" })}
|
||||
</Caption>
|
||||
<Caption type="bold" color="uiTextHighContrast">
|
||||
{formatPrice(intl, booking.totalPrice, booking.currencyCode)}
|
||||
</Caption>
|
||||
</div>
|
||||
<div className={styles.actionArea}>
|
||||
<Button fullWidth>{intl.formatMessage({ id: "Manage stay" })}</Button>
|
||||
<Button fullWidth intent="secondary" asChild>
|
||||
<Link href={directionsUrl} target="_blank">
|
||||
{intl.formatMessage({ id: "Get directions" })}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
{booking.rateDefinition.cancellationRule !== "NotCancellable" && (
|
||||
<Caption className={styles.note} color="uiTextHighContrast">
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: "Changes can be made until {time} on {date}, subject to availability. Room rates may vary.",
|
||||
},
|
||||
{
|
||||
date: fromDate.format("D MMMM"),
|
||||
time: "18:00",
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
.referenceCard {
|
||||
width: var(--max-width-content);
|
||||
max-width: 588px;
|
||||
margin: 0 auto;
|
||||
padding: var(--Spacing-x3);
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
box-shadow: 0px 0px 14px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.referenceRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-bottom: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin-bottom: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.actionArea {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x3);
|
||||
margin: var(--Spacing-x4) 0 var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.referenceCard .note {
|
||||
text-align: center;
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.titleDesktop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.titleMobile {
|
||||
display: none;
|
||||
}
|
||||
.titleDesktop {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
97
components/HotelReservation/MyStay/Room/GuestDetails.tsx
Normal file
97
components/HotelReservation/MyStay/Room/GuestDetails.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { DiamondIcon, EditIcon } from "@/components/Icons"
|
||||
import MembershipLevelIcon from "@/components/Levels/Icon"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
|
||||
import styles from "./room.module.css"
|
||||
|
||||
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
||||
import type { User } from "@/types/user"
|
||||
|
||||
export default function GuestDetails({
|
||||
user,
|
||||
booking,
|
||||
isMobile = false,
|
||||
}: {
|
||||
user: User | null
|
||||
booking: BookingConfirmation["booking"]
|
||||
isMobile?: boolean
|
||||
}) {
|
||||
const intl = useIntl()
|
||||
const containerClass = isMobile
|
||||
? styles.guestDetailsMobile
|
||||
: styles.guestDetailsDesktop
|
||||
|
||||
return (
|
||||
<div className={containerClass}>
|
||||
{user?.membership && (
|
||||
<div className={styles.userDetails}>
|
||||
<div className={styles.row}>
|
||||
<div className={styles.rowTitle}>
|
||||
<Caption
|
||||
type="bold"
|
||||
color="burgundy"
|
||||
textTransform="uppercase"
|
||||
textAlign="center"
|
||||
>
|
||||
{intl.formatMessage({ id: "Your member tier" })}
|
||||
</Caption>
|
||||
</div>
|
||||
<MembershipLevelIcon
|
||||
level={user.membership.membershipLevel}
|
||||
color="red"
|
||||
height={isMobile ? "40" : "20"}
|
||||
width={isMobile ? "80" : "40"}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.totalPoints}>
|
||||
{isMobile && (
|
||||
<div className={styles.totalPointsIcon}>
|
||||
<DiamondIcon color="uiTextHighContrast" />
|
||||
</div>
|
||||
)}
|
||||
<Caption
|
||||
type="bold"
|
||||
color="uiTextHighContrast"
|
||||
textTransform="uppercase"
|
||||
>
|
||||
{intl.formatMessage({ id: "Total points" })}
|
||||
</Caption>
|
||||
|
||||
<Body color="uiTextHighContrast" className={styles.totalPointsText}>
|
||||
{user.membership.currentPoints}
|
||||
</Body>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.guest}>
|
||||
<Body textTransform="bold" color="uiTextHighContrast">
|
||||
{booking.guest.firstName} {booking.guest.lastName}
|
||||
</Body>
|
||||
{user?.membership && (
|
||||
<Body color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Member no." })}{" "}
|
||||
{user.membership.membershipNumber}
|
||||
</Body>
|
||||
)}
|
||||
<Caption color="uiTextHighContrast">{booking.guest.email}</Caption>
|
||||
<Caption color="uiTextHighContrast">
|
||||
{booking.guest.phoneNumber}
|
||||
</Caption>
|
||||
</div>
|
||||
<Button
|
||||
variant="icon"
|
||||
color="burgundy"
|
||||
intent={isMobile ? "secondary" : "text"}
|
||||
>
|
||||
<EditIcon color="burgundy" width={20} height={20} />
|
||||
<Caption color="burgundy">
|
||||
{intl.formatMessage({ id: "Modify guest details" })}
|
||||
</Caption>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
298
components/HotelReservation/MyStay/Room/index.tsx
Normal file
298
components/HotelReservation/MyStay/Room/index.tsx
Normal file
@@ -0,0 +1,298 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
|
||||
import { getIconForFeatureCode } from "@/components/HotelReservation/utils"
|
||||
import {
|
||||
BedDoubleIcon,
|
||||
CoffeeIcon,
|
||||
ContractIcon,
|
||||
DoorOpenIcon,
|
||||
PersonIcon,
|
||||
} from "@/components/Icons"
|
||||
import RocketLaunch from "@/components/Icons/Refresh"
|
||||
import Image from "@/components/Image"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { formatPrice } from "@/utils/numberFormatting"
|
||||
|
||||
import ToggleSidePeek from "../../EnterDetails/SelectedRoom/ToggleSidePeek"
|
||||
import PriceDetailsModal from "../../PriceDetailsModal"
|
||||
import GuestDetails from "./GuestDetails"
|
||||
|
||||
import styles from "./room.module.css"
|
||||
|
||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
import type { Hotel, Room } from "@/types/hotel"
|
||||
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
||||
import type { User } from "@/types/user"
|
||||
|
||||
interface RoomProps {
|
||||
booking: BookingConfirmation["booking"]
|
||||
room:
|
||||
| (Room & {
|
||||
bedType: Room["roomTypes"][number]
|
||||
})
|
||||
| null
|
||||
hotel: Hotel
|
||||
user: User | null
|
||||
}
|
||||
|
||||
function hasBreakfastPackage(
|
||||
packages: BookingConfirmation["booking"]["packages"]
|
||||
) {
|
||||
return packages.some(
|
||||
(p) =>
|
||||
p.code === BreakfastPackageEnum.REGULAR_BREAKFAST ||
|
||||
p.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST ||
|
||||
p.code === BreakfastPackageEnum.SPECIAL_PACKAGE_BREAKFAST
|
||||
)
|
||||
}
|
||||
|
||||
function RoomHeader({
|
||||
room,
|
||||
hotel,
|
||||
}: {
|
||||
room: RoomProps["room"]
|
||||
hotel: Hotel
|
||||
}) {
|
||||
if (!room) return null
|
||||
|
||||
return (
|
||||
<div className={styles.roomHeader}>
|
||||
<Subtitle
|
||||
textTransform="uppercase"
|
||||
color="burgundy"
|
||||
className={styles.roomName}
|
||||
>
|
||||
{room.name}
|
||||
</Subtitle>
|
||||
<ToggleSidePeek
|
||||
hotelId={hotel.operaId}
|
||||
roomTypeCode={room.roomTypes[0].code}
|
||||
intent="text"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Room({ booking, room, hotel, user }: RoomProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
|
||||
if (!room) return null
|
||||
|
||||
const fromDate = dt(booking.checkInDate).locale(lang)
|
||||
|
||||
return (
|
||||
<div className={styles.roomContainer}>
|
||||
<article className={styles.room}>
|
||||
<RoomHeader room={room} hotel={hotel} />
|
||||
<div className={styles.booking}>
|
||||
<div className={styles.chipContainer}>
|
||||
{booking.packages
|
||||
.filter((item) =>
|
||||
Object.values(RoomPackageCodeEnum).includes(
|
||||
item.code as RoomPackageCodeEnum
|
||||
)
|
||||
)
|
||||
.map((item) => {
|
||||
const Icon = getIconForFeatureCode(
|
||||
item.code as RoomPackageCodeEnum
|
||||
)
|
||||
return (
|
||||
<span className={styles.chip} key={item.code}>
|
||||
<Icon width={16} height={16} color="burgundy" />
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className={styles.images}>
|
||||
{room.images.slice(0, 2).map((image) => (
|
||||
<Image
|
||||
key={image.imageSizes.large}
|
||||
src={image.imageSizes.large}
|
||||
className={styles.image}
|
||||
alt={room?.name ?? hotel.name}
|
||||
width={700}
|
||||
height={450}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.roomDetails}>
|
||||
<div className={styles.bookingDetails}>
|
||||
<div className={styles.row}>
|
||||
<span className={styles.rowTitle}>
|
||||
<ContractIcon color="grey80" width={20} height={20} />
|
||||
<Body textTransform="bold" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Booking policy" })}
|
||||
</Body>
|
||||
</span>
|
||||
<div className={styles.rowContent}>
|
||||
<Body color="uiTextHighContrast">
|
||||
{booking.rateDefinition.title}
|
||||
</Body>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.row}>
|
||||
<span className={styles.rowTitle}>
|
||||
<RocketLaunch color="grey80" width={20} height={20} />
|
||||
<Body textTransform="bold" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Rebooking" })}
|
||||
</Body>
|
||||
</span>
|
||||
<div className={styles.rowContent}>
|
||||
<Body color="uiTextHighContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "Until {time}, {date}" },
|
||||
{ time: "18:00", date: fromDate.format("dddd D MMM") }
|
||||
)}
|
||||
</Body>
|
||||
</div>
|
||||
</div>
|
||||
{booking.packages.some((item) =>
|
||||
Object.values(RoomPackageCodeEnum).includes(
|
||||
item.code as RoomPackageCodeEnum
|
||||
)
|
||||
) && (
|
||||
<div className={styles.row}>
|
||||
<span className={styles.rowTitle}>
|
||||
<DoorOpenIcon color="grey80" width={20} height={20} />
|
||||
<Body textTransform="bold" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Room type" })}
|
||||
</Body>
|
||||
</span>
|
||||
<div className={styles.rowContent}>
|
||||
<Body color="uiTextHighContrast">
|
||||
{booking.packages
|
||||
.filter((item) =>
|
||||
Object.values(RoomPackageCodeEnum).includes(
|
||||
item.code as RoomPackageCodeEnum
|
||||
)
|
||||
)
|
||||
.map((item) => item.description)
|
||||
.join(", ")}
|
||||
</Body>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.row}>
|
||||
<span className={styles.rowTitle}>
|
||||
<PersonIcon color="grey80" width={20} height={20} />
|
||||
<Body textTransform="bold" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Guests" })}
|
||||
</Body>
|
||||
</span>
|
||||
<div className={styles.rowContent}>
|
||||
<Body color="uiTextHighContrast">
|
||||
{booking.childrenAges.length > 0
|
||||
? intl.formatMessage(
|
||||
{ id: "{adults} adults, {children} children" },
|
||||
{
|
||||
adults: booking.adults,
|
||||
children: booking.childrenAges.length,
|
||||
}
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{ id: "{adults} adults" },
|
||||
{
|
||||
adults: booking.adults,
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.row}>
|
||||
<span className={styles.rowTitle}>
|
||||
<CoffeeIcon color="grey80" width={20} height={20} />
|
||||
<Body textTransform="bold" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Breakfast" })}
|
||||
</Body>
|
||||
</span>
|
||||
<div className={styles.rowContent}>
|
||||
<Body color="uiTextHighContrast">
|
||||
{hasBreakfastPackage(booking.packages)
|
||||
? intl.formatMessage({ id: "Included" })
|
||||
: intl.formatMessage({ id: "Not included" })}
|
||||
</Body>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.row}>
|
||||
<span className={styles.rowTitle}>
|
||||
<BedDoubleIcon color="grey80" width={20} height={20} />
|
||||
<Body textTransform="bold" color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Bed preference" })}
|
||||
</Body>
|
||||
</span>
|
||||
<div className={styles.rowContent}>
|
||||
<Body color="uiTextHighContrast">
|
||||
{room.bedType.mainBed.description}
|
||||
{room.bedType.mainBed.widthRange.min ===
|
||||
room.bedType.mainBed.widthRange.max
|
||||
? ` (${room.bedType.mainBed.widthRange.min} ${intl.formatMessage({ id: "cm" })})`
|
||||
: ` (${room.bedType.mainBed.widthRange.min} - ${room.bedType.mainBed.widthRange.max} ${intl.formatMessage({ id: "cm" })})`}
|
||||
</Body>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<GuestDetails user={user} booking={booking} isMobile={false} />
|
||||
</div>
|
||||
<div className={styles.bookingInformation}>
|
||||
<div className={styles.bookingCode}></div>
|
||||
<div className={styles.priceDetails}>
|
||||
<div className={styles.price}>
|
||||
<Body color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "Room total" })}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast" textTransform="bold">
|
||||
{formatPrice(intl, booking.totalPrice, booking.currencyCode)}
|
||||
</Body>
|
||||
</div>
|
||||
|
||||
<PriceDetailsModal
|
||||
fromDate={dt(booking.checkInDate).format("YYYY-MM-DD")}
|
||||
toDate={dt(booking.checkOutDate).format("YYYY-MM-DD")}
|
||||
rooms={[
|
||||
{
|
||||
adults: booking.adults,
|
||||
childrenInRoom: undefined,
|
||||
roomPrice: {
|
||||
perNight: {
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: booking.currencyCode,
|
||||
price: booking.totalPrice,
|
||||
},
|
||||
},
|
||||
perStay: {
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: booking.currencyCode,
|
||||
price: booking.totalPrice,
|
||||
},
|
||||
},
|
||||
},
|
||||
roomType: room.name,
|
||||
},
|
||||
]}
|
||||
totalPrice={{
|
||||
requested: undefined,
|
||||
local: {
|
||||
currency: booking.currencyCode,
|
||||
price: booking.totalPrice,
|
||||
},
|
||||
}}
|
||||
vat={booking.vatPercentage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<GuestDetails user={user} booking={booking} isMobile={true} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
284
components/HotelReservation/MyStay/Room/room.module.css
Normal file
284
components/HotelReservation/MyStay/Room/room.module.css
Normal file
@@ -0,0 +1,284 @@
|
||||
.room {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
padding: var(--Spacing-x3) 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.room {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.roomHeader {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: var(--max-width-content);
|
||||
margin: 0 auto;
|
||||
align-items: flex-start;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.roomHeader {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.booking {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
position: relative;
|
||||
width: var(--max-width-content);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.booking {
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
padding: var(--Spacing-x2);
|
||||
}
|
||||
}
|
||||
|
||||
.chipContainer {
|
||||
position: absolute;
|
||||
top: 300px;
|
||||
left: 25px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.chip {
|
||||
background-color: var(--Main-Grey-White);
|
||||
padding: var(--Spacing-x-half) var(--Spacing-x1);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
}
|
||||
|
||||
.images {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
grid-template-columns: 1fr;
|
||||
height: 210px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.images {
|
||||
height: 320px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
width: 100%;
|
||||
height: 210px;
|
||||
aspect-ratio: 16/9;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.image:last-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.image {
|
||||
height: 100%;
|
||||
}
|
||||
.image:last-child {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.roomDetails {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x5);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.roomDetails {
|
||||
grid-template-columns: minmax(0, 700px) 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.bookingDetails {
|
||||
max-width: 100%;
|
||||
padding: 0 var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.bookingDetails {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--Spacing-x-one-and-half) 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.row {
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.rowTitle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.rowTitle svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.rowTitle svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.rowContent {
|
||||
padding-left: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.guestDetailsDesktop {
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.guestDetailsDesktop {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.guestDetailsMobile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: var(--Spacing-x2);
|
||||
background-color: var(--Main-Brand-PalePeach);
|
||||
padding: var(--Spacing-x3) 0;
|
||||
}
|
||||
|
||||
.guestDetailsMobile .row {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.guestDetailsMobile .rowTitle {
|
||||
margin-bottom: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.guestDetailsMobile .userDetails {
|
||||
width: calc(100% - var(--Spacing-x4) - var(--Spacing-x4));
|
||||
border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider);
|
||||
padding-bottom: var(--Spacing-x3);
|
||||
margin-bottom: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.guestDetailsMobile .totalPoints {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--Spacing-x1);
|
||||
padding-top: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.guestDetailsMobile .totalPointsText {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.guestDetailsMobile .guest {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.guestDetailsMobile {
|
||||
display: none;
|
||||
}
|
||||
.totalPoints {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--Spacing-x-one-and-half) 0;
|
||||
}
|
||||
}
|
||||
|
||||
.guest {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
margin-bottom: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.bookingInformation {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.bookingInformation {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.priceDetails {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
border-top: 1px solid var(--Base-Border-Subtle);
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
padding: var(--Spacing-x-one-and-half) 0;
|
||||
width: calc(100% - var(--Spacing-x4));
|
||||
justify-content: center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.priceDetails {
|
||||
border: none;
|
||||
margin: 0;
|
||||
width: auto;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.price {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
}
|
||||
|
||||
.userDetails {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
margin-bottom: var(--Spacing-x1);
|
||||
}
|
||||
71
components/HotelReservation/MyStay/index.tsx
Normal file
71
components/HotelReservation/MyStay/index.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { homeHrefs } from "@/constants/homeHrefs"
|
||||
import { env } from "@/env/server"
|
||||
import { dt } from "@/lib/dt"
|
||||
import {
|
||||
getAncillaryPackages,
|
||||
getBookingConfirmation,
|
||||
getProfileSafely,
|
||||
} from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import Image from "@/components/Image"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import { Ancillaries } from "./Ancillaries"
|
||||
import BookingSummary from "./BookingSummary"
|
||||
import { Header } from "./Header"
|
||||
import Promo from "./Promo"
|
||||
import { ReferenceCard } from "./ReferenceCard"
|
||||
import { Room } from "./Room"
|
||||
|
||||
import styles from "./myStay.module.css"
|
||||
|
||||
export async function MyStay({ reservationId }: { reservationId: string }) {
|
||||
const { booking, hotel, room } = await getBookingConfirmation(reservationId)
|
||||
const userResponse = await getProfileSafely()
|
||||
const user = userResponse && !("error" in userResponse) ? userResponse : null
|
||||
const intl = await getIntl()
|
||||
const lang = getLang()
|
||||
const homeUrl = homeHrefs[env.NODE_ENV][lang]
|
||||
const fromDate = dt(booking.checkInDate).format("YYYY-MM-DD")
|
||||
const toDate = dt(booking.checkOutDate).format("YYYY-MM-DD")
|
||||
const hotelId = hotel.operaId
|
||||
const ancillaryInput = { fromDate, hotelId, toDate }
|
||||
void getAncillaryPackages(ancillaryInput)
|
||||
const ancillaryPackages = await getAncillaryPackages(ancillaryInput)
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<div className={styles.imageContainer}>
|
||||
<div className={styles.blurOverlay} />
|
||||
{hotel.gallery?.heroImages[0].imageSizes.large && (
|
||||
<Image
|
||||
className={styles.image}
|
||||
src={hotel.gallery.heroImages[0].imageSizes.large}
|
||||
alt={hotel.name}
|
||||
fill
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.headerContainer}>
|
||||
<Header hotel={hotel} />
|
||||
<ReferenceCard booking={booking} hotel={hotel} />
|
||||
</div>
|
||||
{booking.showAncillaries && (
|
||||
<Ancillaries ancillaries={ancillaryPackages} />
|
||||
)}
|
||||
<Room booking={booking} room={room} hotel={hotel} user={user} />
|
||||
<BookingSummary booking={booking} hotel={hotel} />
|
||||
<Promo
|
||||
buttonText={intl.formatMessage({ id: "Book another stay" })}
|
||||
href={`${homeUrl}?hotel=${hotel.operaId}`}
|
||||
text={intl.formatMessage({
|
||||
id: "Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.",
|
||||
})}
|
||||
title={intl.formatMessage({ id: "Book your next stay" })}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
98
components/HotelReservation/MyStay/myStay.module.css
Normal file
98
components/HotelReservation/MyStay/myStay.module.css
Normal file
@@ -0,0 +1,98 @@
|
||||
.main {
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
min-height: 100dvh;
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 480px;
|
||||
}
|
||||
|
||||
.blurOverlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
backdrop-filter: blur(12px);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, transparent 100%);
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(0, 0, 0, 0.5) 0%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.image {
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.headerContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 80px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding-bottom: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.content {
|
||||
width: var(--max-width-content);
|
||||
padding-bottom: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
.headerSkeleton {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
align-items: center;
|
||||
padding: var(--Spacing-x6) var(--Spacing-x2) 0;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
padding: 0 var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.cardSkeleton {
|
||||
max-width: 100%;
|
||||
margin: -30px auto 0;
|
||||
padding: 0 var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.ancillariesSkeleton {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.ancillariesSkeleton {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.paymentDetailsSkeleton {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.hotelDetailsSkeleton {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
34
components/HotelReservation/MyStay/myStaySkeleton.tsx
Normal file
34
components/HotelReservation/MyStay/myStaySkeleton.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import SkeletonShimmer from "@/components/SkeletonShimmer"
|
||||
|
||||
import styles from "./myStay.module.css"
|
||||
|
||||
export async function MyStaySkeleton() {
|
||||
return (
|
||||
<div className={styles.content}>
|
||||
<div className={styles.headerSkeleton}>
|
||||
<SkeletonShimmer width={"100px"} height="20px" />
|
||||
<SkeletonShimmer width={"450px"} height="50px" />
|
||||
<SkeletonShimmer width={"200px"} height="30px" />
|
||||
</div>
|
||||
<div className={styles.cardSkeleton}>
|
||||
<SkeletonShimmer width="590px" height="380px" />
|
||||
</div>
|
||||
<div className={styles.section}>
|
||||
<SkeletonShimmer width={"200px"} height="30px" />
|
||||
<div className={styles.ancillariesSkeleton}>
|
||||
<SkeletonShimmer width="280px" height="200px" />
|
||||
<SkeletonShimmer width="280px" height="200px" />
|
||||
<SkeletonShimmer width="280px" height="200px" />
|
||||
<SkeletonShimmer width="280px" height="200px" />
|
||||
<SkeletonShimmer width="280px" height="200px" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.section}>
|
||||
<SkeletonShimmer width={"200px"} height="30px" />
|
||||
<div className={styles.roomSkeleton}>
|
||||
<SkeletonShimmer width="100%" height="700px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import ChevronRightSmallIcon from "@/components/Icons/ChevronRightSmall"
|
||||
|
||||
23
components/Icons/Diamond.tsx
Normal file
23
components/Icons/Diamond.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function DiamondIcon({ className, color, ...props }: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
fill="none"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M11.9993 20.17C11.7331 20.17 11.475 20.1117 11.225 19.995C10.975 19.8783 10.7542 19.7075 10.5625 19.4825L2.85 10.245C2.70834 10.0724 2.60209 9.88404 2.53125 9.68001C2.46042 9.47597 2.425 9.2641 2.425 9.04438C2.425 8.90313 2.4375 8.76167 2.4625 8.62001C2.4875 8.47834 2.5375 8.34084 2.6125 8.20751L4.525 4.42001C4.69167 4.10334 4.92018 3.85126 5.21053 3.66376C5.50088 3.47626 5.82654 3.38251 6.1875 3.38251H17.8125C18.1735 3.38251 18.4991 3.47626 18.7895 3.66376C19.0798 3.85126 19.3083 4.10334 19.475 4.42001L21.3875 8.20751C21.4625 8.34084 21.5125 8.47834 21.5375 8.62001C21.5625 8.76167 21.575 8.90313 21.575 9.04438C21.575 9.2641 21.5396 9.47597 21.4688 9.68001C21.3979 9.88404 21.2917 10.0724 21.15 10.245L13.4375 19.4825C13.2458 19.7075 13.0248 19.8783 12.7743 19.995C12.5237 20.1117 12.2654 20.17 11.9993 20.17ZM9.525 8.38251H14.475L12.9116 5.25751H11.0875L9.525 8.38251ZM11.0625 17.17V10.2575H5.3125L11.0625 17.17ZM12.9375 17.17L18.6875 10.2575H12.9375V17.17ZM16.5625 8.38251H19.35L17.7875 5.25751H15L16.5625 8.38251ZM4.65 8.38251H7.4375L9 5.25751H6.2125L4.65 8.38251Z"
|
||||
fill="#26201E"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
29
components/Icons/Directions.tsx
Normal file
29
components/Icons/Directions.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function DirectionsIcon({
|
||||
className,
|
||||
color,
|
||||
width = "20",
|
||||
height = "20",
|
||||
...props
|
||||
}: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
fill="none"
|
||||
height={height}
|
||||
viewBox="0 0 20 20"
|
||||
width={width}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M8.28158 9.94792H11.3024V11.0104C11.3024 11.191 11.3823 11.3142 11.542 11.3802C11.7017 11.4462 11.8441 11.4167 11.9691 11.2917L13.542 9.70834C13.7017 9.54862 13.7816 9.36632 13.7816 9.16146C13.7816 8.9566 13.7017 8.77431 13.542 8.61459L11.9691 7.04167C11.8441 6.91667 11.7017 6.88542 11.542 6.94792C11.3823 7.01042 11.3024 7.13195 11.3024 7.31251V8.38542H7.50033C7.28505 8.38542 7.10102 8.46181 6.94824 8.61459C6.79546 8.76737 6.71908 8.95139 6.71908 9.16667V11.6667C6.71908 11.882 6.79546 12.066 6.94824 12.2188C7.10102 12.3715 7.28505 12.4479 7.50033 12.4479C7.7156 12.4479 7.89963 12.3715 8.05241 12.2188C8.20519 12.066 8.28158 11.882 8.28158 11.6667V9.94792ZM10.0003 18.1146C9.79894 18.1146 9.60623 18.0764 9.4222 18C9.23817 17.9236 9.0663 17.809 8.90658 17.6563L2.34408 11.0938C2.1913 10.934 2.07671 10.7622 2.00033 10.5781C1.92394 10.3941 1.88574 10.2014 1.88574 10C1.88574 9.79862 1.92394 9.60417 2.00033 9.41667C2.07671 9.22917 2.1913 9.05903 2.34408 8.90626L8.90658 2.34376C9.0663 2.18403 9.23817 2.06771 9.4222 1.9948C9.60623 1.92188 9.79894 1.88542 10.0003 1.88542C10.2017 1.88542 10.3962 1.92188 10.5837 1.9948C10.7712 2.06771 10.9413 2.18403 11.0941 2.34376L17.6566 8.90626C17.8163 9.05903 17.9326 9.22917 18.0055 9.41667C18.0785 9.60417 18.1149 9.79862 18.1149 10C18.1149 10.2014 18.0785 10.3941 18.0055 10.5781C17.9326 10.7622 17.8163 10.934 17.6566 11.0938L11.0941 17.6563C10.9413 17.809 10.7712 17.9236 10.5837 18C10.3962 18.0764 10.2017 18.1146 10.0003 18.1146ZM10.0003 16.5625L16.5628 10L10.0003 3.43751L3.43783 10L10.0003 16.5625Z"
|
||||
fill="#4D001B"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
29
components/Icons/Link.tsx
Normal file
29
components/Icons/Link.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function LinkIcon({
|
||||
className,
|
||||
color,
|
||||
width = "20",
|
||||
height = "20",
|
||||
...props
|
||||
}: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
fill="none"
|
||||
height={height}
|
||||
viewBox="0 0 20 20"
|
||||
width={width}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M5.89583 14.0208C4.77778 14.0208 3.82812 13.6302 3.04688 12.8489C2.26562 12.0677 1.875 11.118 1.875 9.99999C1.875 8.88193 2.26562 7.93228 3.04688 7.15103C3.82812 6.36978 4.77778 5.97916 5.89583 5.97916H8.36458C8.57986 5.97916 8.76389 6.05555 8.91667 6.20832C9.06944 6.3611 9.14583 6.54513 9.14583 6.76041C9.14583 6.97568 9.06944 7.15971 8.91667 7.31249C8.76389 7.46527 8.57986 7.54166 8.36458 7.54166H5.89583C5.21528 7.54166 4.63542 7.78124 4.15625 8.26041C3.67708 8.73957 3.4375 9.31943 3.4375 9.99999C3.4375 10.6805 3.67708 11.2604 4.15625 11.7396C4.63542 12.2187 5.21528 12.4583 5.89583 12.4583H8.36458C8.57986 12.4583 8.76389 12.5347 8.91667 12.6875C9.06944 12.8403 9.14583 13.0243 9.14583 13.2396C9.14583 13.4549 9.06944 13.6389 8.91667 13.7917C8.76389 13.9444 8.57986 14.0208 8.36458 14.0208H5.89583ZM7.53125 10.7708C7.31597 10.7708 7.13194 10.6944 6.97917 10.5417C6.82639 10.3889 6.75 10.2049 6.75 9.98957C6.75 9.7743 6.82639 9.59027 6.97917 9.43749C7.13194 9.28471 7.31597 9.20832 7.53125 9.20832H12.4688C12.684 9.20832 12.8681 9.28471 13.0208 9.43749C13.1736 9.59027 13.25 9.7743 13.25 9.98957C13.25 10.2049 13.1736 10.3889 13.0208 10.5417C12.8681 10.6944 12.684 10.7708 12.4688 10.7708H7.53125ZM11.6354 14.0208C11.4201 14.0208 11.2361 13.9444 11.0833 13.7917C10.9306 13.6389 10.8542 13.4549 10.8542 13.2396C10.8542 13.0243 10.9306 12.8403 11.0833 12.6875C11.2361 12.5347 11.4201 12.4583 11.6354 12.4583H14.1042C14.7847 12.4583 15.3646 12.2187 15.8438 11.7396C16.3229 11.2604 16.5625 10.6805 16.5625 9.99999C16.5625 9.31943 16.3229 8.73957 15.8438 8.26041C15.3646 7.78124 14.7847 7.54166 14.1042 7.54166H11.6354C11.4201 7.54166 11.2361 7.46527 11.0833 7.31249C10.9306 7.15971 10.8542 6.97568 10.8542 6.76041C10.8542 6.54513 10.9306 6.3611 11.0833 6.20832C11.2361 6.05555 11.4201 5.97916 11.6354 5.97916H14.1042C15.2222 5.97916 16.1719 6.36978 16.9531 7.15103C17.7344 7.93228 18.125 8.88193 18.125 9.99999C18.125 11.118 17.7344 12.0677 16.9531 12.8489C16.1719 13.6302 15.2222 14.0208 14.1042 14.0208H11.6354Z"
|
||||
fill="#4D001B"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -34,6 +34,8 @@ import {
|
||||
CulturalIcon,
|
||||
CutleryOneIcon,
|
||||
CutleryTwoIcon,
|
||||
DiamondIcon,
|
||||
DirectionsIcon,
|
||||
DoorOpenIcon,
|
||||
DresserIcon,
|
||||
ElectricBikeIcon,
|
||||
@@ -67,6 +69,7 @@ import {
|
||||
KidsMocktailIcon,
|
||||
LampIcon,
|
||||
LaundryMachineIcon,
|
||||
LinkIcon,
|
||||
LocalBarIcon,
|
||||
LocationIcon,
|
||||
LockIcon,
|
||||
@@ -191,6 +194,10 @@ export function getIconByIconName(
|
||||
return CutleryOneIcon
|
||||
case IconName.CutleryTwo:
|
||||
return CutleryTwoIcon
|
||||
case IconName.Diamond:
|
||||
return DiamondIcon
|
||||
case IconName.Directions:
|
||||
return DirectionsIcon
|
||||
case IconName.DoorOpen:
|
||||
return DoorOpenIcon
|
||||
case IconName.Dresser:
|
||||
@@ -257,6 +264,8 @@ export function getIconByIconName(
|
||||
return LampIcon
|
||||
case IconName.LaundryMachine:
|
||||
return LaundryMachineIcon
|
||||
case IconName.Link:
|
||||
return LinkIcon
|
||||
case IconName.LocalBar:
|
||||
return LocalBarIcon
|
||||
case IconName.Location:
|
||||
|
||||
@@ -65,7 +65,9 @@ export { default as CutleryOneIcon } from "./CutleryOne"
|
||||
export { default as CutleryTwoIcon } from "./CutleryTwo"
|
||||
export { default as DeleteIcon } from "./Delete"
|
||||
export { default as DeskIcon } from "./Desk"
|
||||
export { default as DiamondIcon } from "./Diamond"
|
||||
export { default as DiningIcon } from "./Dining"
|
||||
export { default as DirectionsIcon } from "./Directions"
|
||||
export { default as DiscountIcon } from "./Discount"
|
||||
export { default as DoorClosedIcon } from "./DoorClosed"
|
||||
export { default as DoorOpenIcon } from "./DoorOpen"
|
||||
@@ -112,6 +114,7 @@ export { default as KidsMocktailIcon } from "./KidsMocktail"
|
||||
export { default as LampIcon } from "./Lamp"
|
||||
export { default as LaptopIcon } from "./Laptop"
|
||||
export { default as LaundryMachineIcon } from "./LaundryMachine"
|
||||
export { default as LinkIcon } from "./Link"
|
||||
export { default as LocalBarIcon } from "./LocalBar"
|
||||
export { default as LocationIcon } from "./Location"
|
||||
export { default as LockIcon } from "./Lock"
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function SkeletonShimmer({
|
||||
style={{
|
||||
height: height,
|
||||
width: width,
|
||||
maxWidth: "100%",
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
22
components/TempDesignSystem/IconChip/iconChip.module.css
Normal file
22
components/TempDesignSystem/IconChip/iconChip.module.css
Normal file
@@ -0,0 +1,22 @@
|
||||
.chip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
padding: var(--Spacing-x1) var(--Spacing-x-one-and-half);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
}
|
||||
|
||||
.blue {
|
||||
background-color: var(--Scandic-Blue-00);
|
||||
color: var(--UI-Semantic-Information);
|
||||
}
|
||||
|
||||
.green {
|
||||
background-color: var(--Scandic-Green-00);
|
||||
color: var(--UI-Semantic-Success);
|
||||
}
|
||||
|
||||
.red {
|
||||
background-color: var(--Scandic-Red-00);
|
||||
color: var(--UI-Semantic-Error);
|
||||
}
|
||||
19
components/TempDesignSystem/IconChip/index.tsx
Normal file
19
components/TempDesignSystem/IconChip/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { iconChipVariants } from "./variants"
|
||||
|
||||
interface IconChipProps {
|
||||
color: "blue" | "green" | "red" | null | undefined
|
||||
icon: React.ReactNode
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default function IconChip({ color, icon, children }: IconChipProps) {
|
||||
const classNames = iconChipVariants({
|
||||
color: color,
|
||||
})
|
||||
return (
|
||||
<div className={classNames}>
|
||||
{icon}
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
13
components/TempDesignSystem/IconChip/variants.ts
Normal file
13
components/TempDesignSystem/IconChip/variants.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
import styles from "./iconChip.module.css"
|
||||
|
||||
export const iconChipVariants = cva(styles.chip, {
|
||||
variants: {
|
||||
color: {
|
||||
blue: styles.blue,
|
||||
green: styles.green,
|
||||
red: styles.red,
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -143,3 +143,7 @@
|
||||
.baseText {
|
||||
color: var(--Base-Text-Inverted);
|
||||
}
|
||||
|
||||
.success {
|
||||
color: var(--UI-Semantic-Success);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ const config = {
|
||||
primaryDim: styles.primaryDim,
|
||||
primaryStrong: styles.primaryStrong,
|
||||
baseText: styles.baseText,
|
||||
success: styles.success,
|
||||
},
|
||||
textAlign: {
|
||||
center: styles.textAlignCenter,
|
||||
|
||||
@@ -110,3 +110,10 @@ p.caption {
|
||||
.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.green {
|
||||
color: var(--UI-Semantic-Success);
|
||||
}
|
||||
.blue {
|
||||
color: var(--UI-Semantic-Information);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ const config = {
|
||||
textMediumContrast: styles.textMediumContrast,
|
||||
red: styles.red,
|
||||
white: styles.white,
|
||||
green: styles.green,
|
||||
blue: styles.blue,
|
||||
uiTextHighContrast: styles.uiTextHighContrast,
|
||||
uiTextActive: styles.uiTextActive,
|
||||
uiTextMediumContrast: styles.uiTextMediumContrast,
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: var(--Spacing-x-one-and-half) var(--Spacing-x3)
|
||||
var(--Spacing-x-one-and-half) var(--Spacing-x2);
|
||||
padding: var(--Spacing-x-one-and-half) var(--Spacing-x3);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"Based on availability": "Baseret på tilgængelighed",
|
||||
"Bed": "Seng type",
|
||||
"Bed options": "Sengemuligheder",
|
||||
"Bed preference": "Sengvalg",
|
||||
"Bed type": "Seng type",
|
||||
"Bike friendly": "Cykelvenlig",
|
||||
"Birth date": "Fødselsdato",
|
||||
@@ -78,6 +79,7 @@
|
||||
"Booking": "Booking",
|
||||
"Booking confirmation": "Booking bekræftelse",
|
||||
"Booking number": "Bookingnummer",
|
||||
"Booking policy": "Booking politik",
|
||||
"Booking summary": "Opsummering",
|
||||
"Breakfast": "Morgenmad",
|
||||
"Breakfast ({totalAdults, plural, one {# adult} other {# adults}}) x {totalBreakfasts}": "Morgenmad ({totalAdults, plural, one {# voksen} other {# voksne}}) x {totalBreakfasts}",
|
||||
@@ -101,6 +103,7 @@
|
||||
"Cancel": "Afbestille",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Skift værelse",
|
||||
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Ændringer kan gøres indtil {time} på {date}, under forudsætning af tilgængelighed. Priserne for værelserne kan variere.",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Check ind",
|
||||
"Check in from: {checkInTime}": "Indtjekning fra: {checkInTime}",
|
||||
@@ -162,6 +165,7 @@
|
||||
"Dialog": "Dialog",
|
||||
"Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>": "Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>",
|
||||
"Dimensions": "Dimensioner",
|
||||
"Directions": "Retninger",
|
||||
"Discard changes": "Kassér ændringer",
|
||||
"Discard unsaved changes?": "Slette ændringer, der ikke er gemt?",
|
||||
"Discounted rooms": "Værelser med rabat",
|
||||
@@ -229,6 +233,8 @@
|
||||
"Full circle": "Full circle",
|
||||
"Full price rooms": "Fuld pris værelser",
|
||||
"Garage": "Garage",
|
||||
"Get directions": "Få retninger",
|
||||
"Get hotel directions": "Få hotel retninger",
|
||||
"Get inspired": "Bliv inspireret",
|
||||
"Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.": "Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.",
|
||||
"Get the member price: {amount}": "Betal kun {amount}",
|
||||
@@ -239,6 +245,7 @@
|
||||
"Go to profile": "Go to profile",
|
||||
"Guarantee booking with credit card": "Garantere booking med kreditkort",
|
||||
"Guest information": "Gæsteinformation",
|
||||
"Guests": "Gæster",
|
||||
"Guests & Rooms": "Gæster & værelser",
|
||||
"Gym": "Fitnesscenter",
|
||||
"Half circle": "Half circle",
|
||||
@@ -248,6 +255,7 @@
|
||||
"Highest level": "Højeste niveau",
|
||||
"Hiking": "Vandring",
|
||||
"Home": "Hjem",
|
||||
"Homepage": "Hjemmeside",
|
||||
"Hospital": "Hospital",
|
||||
"Hotel": "Hotel",
|
||||
"Hotel details": "Hoteloplysninger",
|
||||
@@ -320,6 +328,7 @@
|
||||
"Main menu": "Hovedmenu",
|
||||
"Manage booking": "Manage booking",
|
||||
"Manage preferences": "Administrer præferencer",
|
||||
"Manage stay": "Administrer ophold",
|
||||
"Map": "Kort",
|
||||
"Map of the city center": "Kort over byens centrum",
|
||||
"Map of the country": "Kort over landet",
|
||||
@@ -330,6 +339,7 @@
|
||||
"Meetings & Conferences": "Møder & Konferencer",
|
||||
"Member Since: {value}": "Member Since: {value}",
|
||||
"Member discount": "Member discount",
|
||||
"Member no.": "Medlemsnummer",
|
||||
"Member price": "Medlemspris",
|
||||
"Member price activated": "Medlemspris aktiveret",
|
||||
"Member price from": "Medlemspris fra",
|
||||
@@ -344,6 +354,7 @@
|
||||
"Menu": "Menu",
|
||||
"Menus": "Menukort",
|
||||
"Modify": "Ændre",
|
||||
"Modify guest details": "Ændre gæstdetaljer",
|
||||
"Mon-Fri Always open": "Man-Fre Altid åben",
|
||||
"Mon-Fri {openingTime}-{closingTime}": "Man-Fre {openingTime}-{closingTime}",
|
||||
"Monday": "Mandag",
|
||||
@@ -360,7 +371,7 @@
|
||||
"My pages": "Mine sider",
|
||||
"My pages menu": "Mine sider menu",
|
||||
"My payment cards": "Mine betalingskort",
|
||||
"My stay at {hotelName}": "My stay at {hotelName}",
|
||||
"My stay at": "My stay at",
|
||||
"My wishes": "Mine ønsker",
|
||||
"N/A": "N/A",
|
||||
"Name": "Navn",
|
||||
@@ -408,6 +419,7 @@
|
||||
"Outdoor pool": "Udendørs pool",
|
||||
"Overview": "Oversigt",
|
||||
"PETR": "Kæledyr",
|
||||
"Paid": "Betalt",
|
||||
"Parking": "Parkering",
|
||||
"Parking / Garage": "Parkering / Garage",
|
||||
"Parking can be reserved in advance": "Parkering kan reserveres på forhånd",
|
||||
@@ -470,6 +482,7 @@
|
||||
"Redeemed & valid through:": "Redeemed & valid through:",
|
||||
"Redirecting you to SAS": "Redirecting you to SAS",
|
||||
"Redirecting you to my pages.": "Redirecting you to my pages.",
|
||||
"Reference": "Reference",
|
||||
"Reference #{bookingNr}": "Reference #{bookingNr}",
|
||||
"Relax": "Slap af",
|
||||
"Remember code": "Husk kode",
|
||||
@@ -485,6 +498,7 @@
|
||||
"Room & Terms": "Værelse & Vilkår",
|
||||
"Room charge": "Værelsesafgift",
|
||||
"Room facilities": "Værelsesfaciliteter",
|
||||
"Room total": "Værelse total",
|
||||
"Room {roomIndex}": "Værelse {roomIndex}",
|
||||
"Rooms": "Værelser",
|
||||
"Rooms & Guests": "Værelser & gæster",
|
||||
@@ -544,6 +558,7 @@
|
||||
"Sports": "Sport",
|
||||
"Standard price": "Standardpris",
|
||||
"Standing table": "Standing table",
|
||||
"Status": "Status",
|
||||
"Stay at {hotelName} | Hotel in {destination}": "Bo på {hotelName} | Hotel i {destination}",
|
||||
"Street": "Gade",
|
||||
"Submit": "Submit",
|
||||
@@ -573,8 +588,9 @@
|
||||
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "For at få medlemsprisen <span>{price}</span>, log ind eller tilmeld dig, når du udfylder bookingen.",
|
||||
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "For at sikre din reservation, beder vi om at du giver os dine betalingsoplysninger. Du kan så være sikker på, at ingen gebyrer vil blive opkrævet på dette tidspunkt.",
|
||||
"Total": "Total",
|
||||
"Total Points": "Samlet antal point",
|
||||
"Total cost": "Total cost",
|
||||
"Total paid": "Total betalt",
|
||||
"Total points": "Samlet antal point",
|
||||
"Total price": "Samlet pris",
|
||||
"Total price (incl VAT)": "Samlet pris (inkl. moms)",
|
||||
"Tourist": "Turist",
|
||||
@@ -587,6 +603,8 @@
|
||||
"Type of room": "Værelsestype",
|
||||
"U-shape": "U-form",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Unpaid": "Ikke betalt",
|
||||
"Until {time}, {date}": "Indtil {time} den {date}",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Upgrade your stay": "Opgrader dit ophold",
|
||||
"Use Bonus Cheque": "Brug Bonus Cheque",
|
||||
@@ -662,6 +680,7 @@
|
||||
"Your details": "Dine oplysninger",
|
||||
"Your hotel": "Your hotel",
|
||||
"Your level": "Dit niveau",
|
||||
"Your member tier": "Dit medlemskabsniveau",
|
||||
"Your points to spend": "Dine brugbare point",
|
||||
"Your room": "Dit værelse",
|
||||
"Your selected bed type will be provided based on availability": "Din valgte sengtype vil blive stillet til rådighed baseret på tilgængelighed",
|
||||
@@ -676,6 +695,7 @@
|
||||
"booking.selectRoom": "Zimmer auswählen",
|
||||
"booking.thisRoomIsEquippedWith": "Dette værelse er udstyret med",
|
||||
"friday": "fredag",
|
||||
"from": "fra",
|
||||
"max {seatings} pers": "max {seatings} pers",
|
||||
"monday": "mandag",
|
||||
"next level: {nextLevel}": "next level: {nextLevel}",
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"Based on availability": "Je nach Verfügbarkeit",
|
||||
"Bed": "Bettentyp",
|
||||
"Bed options": "Bettoptionen",
|
||||
"Bed preference": "Betttyp",
|
||||
"Bed type": "Bettentyp",
|
||||
"Bike friendly": "Fahrradfreundlich",
|
||||
"Birth date": "Geburtsdatum",
|
||||
@@ -79,6 +80,7 @@
|
||||
"Booking": "Booking",
|
||||
"Booking confirmation": "Buchungsbestätigung",
|
||||
"Booking number": "Buchungsnummer",
|
||||
"Booking policy": "Buchungsbedingungen",
|
||||
"Booking summary": "Zusammenfassung",
|
||||
"Breakfast": "Frühstück",
|
||||
"Breakfast ({totalAdults, plural, one {# adult} other {# adults}}) x {totalBreakfasts}": "Frühstück ({totalAdults, plural, one {# erwachsene} other {# erwachsene}}) x {totalBreakfasts}",
|
||||
@@ -102,6 +104,7 @@
|
||||
"Cancel": "Stornieren",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Zimmer ändern",
|
||||
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Änderungen können bis {time} am {date} vorgenommen werden, vorausgesetzt, dass die Zimmer noch verfügbar sind. Die Zimmerpreise können variieren.",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Einchecken",
|
||||
"Check in from: {checkInTime}": "Check-in ab: {checkInTime}",
|
||||
@@ -163,6 +166,7 @@
|
||||
"Dialog": "Dialog",
|
||||
"Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>": "Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>",
|
||||
"Dimensions": "Abmessungen",
|
||||
"Directions": "Richtungen",
|
||||
"Discard changes": "Änderungen verwerfen",
|
||||
"Discard unsaved changes?": "Nicht gespeicherte Änderungen verwerfen?",
|
||||
"Discounted rooms": "Zimmer mit Rabatt",
|
||||
@@ -230,6 +234,8 @@
|
||||
"Full circle": "Full circle",
|
||||
"Full price rooms": "Zimmer zum vollen Preis",
|
||||
"Garage": "Garage",
|
||||
"Get directions": "Richtungen erhalten",
|
||||
"Get hotel directions": "Hotel Richtungen",
|
||||
"Get inspired": "Lassen Sie sich inspieren",
|
||||
"Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.": "Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.",
|
||||
"Get the member price: {amount}": "Nur bezahlen {amount}",
|
||||
@@ -240,6 +246,7 @@
|
||||
"Go to profile": "Go to profile",
|
||||
"Guarantee booking with credit card": "Buchung mit Kreditkarte garantieren",
|
||||
"Guest information": "Informationen für Gäste",
|
||||
"Guests": "Gäste",
|
||||
"Guests & Rooms": "Gäste & Zimmer",
|
||||
"Gym": "Fitnessstudio",
|
||||
"Half circle": "Half circle",
|
||||
@@ -249,6 +256,7 @@
|
||||
"Highest level": "Höchstes Level",
|
||||
"Hiking": "Wandern",
|
||||
"Home": "Heim",
|
||||
"Homepage": "Startseite",
|
||||
"Hospital": "Krankenhaus",
|
||||
"Hotel": "Hotel",
|
||||
"Hotel details": "Hotelinformationen",
|
||||
@@ -321,6 +329,7 @@
|
||||
"Main menu": "Hauptmenü",
|
||||
"Manage booking": "Manage booking",
|
||||
"Manage preferences": "Verwalten von Voreinstellungen",
|
||||
"Manage stay": "Aufenthalt verwalten",
|
||||
"Map": "Karte",
|
||||
"Map of the city center": "Karte des Stadtzentrums",
|
||||
"Map of the country": "Karte des Landes",
|
||||
@@ -331,6 +340,7 @@
|
||||
"Meetings & Conferences": "Tagungen & Konferenzen",
|
||||
"Member Since: {value}": "Member Since: {value}",
|
||||
"Member discount": "Member discount",
|
||||
"Member no.": "Mitgliedsnummer",
|
||||
"Member price": "Mitgliederpreis",
|
||||
"Member price activated": "Mitgliederpreis aktiviert",
|
||||
"Member price from": "Mitgliederpreis ab",
|
||||
@@ -345,6 +355,7 @@
|
||||
"Menu": "Menü",
|
||||
"Menus": "Menüs",
|
||||
"Modify": "Ändern",
|
||||
"Modify guest details": "Gastdetails ändern",
|
||||
"Mon-Fri Always open": "Mo-Fr Immer geöffnet",
|
||||
"Mon-Fri {openingTime}-{closingTime}": "Mo-Fr {openingTime}-{closingTime}",
|
||||
"Monday": "Montag",
|
||||
@@ -361,7 +372,7 @@
|
||||
"My pages": "Meine Seiten",
|
||||
"My pages menu": "Meine Seite Menü",
|
||||
"My payment cards": "Meine Zahlungskarten",
|
||||
"My stay at {hotelName}": "My stay at {hotelName}",
|
||||
"My stay at": "My stay at",
|
||||
"My wishes": "Meine Wünsche",
|
||||
"N/A": "N/A",
|
||||
"Name": "Name",
|
||||
@@ -409,6 +420,7 @@
|
||||
"Outdoor pool": "Außenpool",
|
||||
"Overview": "Übersicht",
|
||||
"PETR": "Haustier",
|
||||
"Paid": "Bezahlt",
|
||||
"Parking": "Parken",
|
||||
"Parking / Garage": "Parken / Garage",
|
||||
"Parking can be reserved in advance": "Parkplätze können im Voraus reserviert werden",
|
||||
@@ -471,6 +483,7 @@
|
||||
"Redeemed & valid through:": "Redeemed & valid through:",
|
||||
"Redirecting you to SAS": "Redirecting you to SAS",
|
||||
"Redirecting you to my pages.": "Redirecting you to my pages.",
|
||||
"Reference": "Referenz",
|
||||
"Reference #{bookingNr}": "Referenz #{bookingNr}",
|
||||
"Relax": "Entspannen",
|
||||
"Remember code": "Code merken",
|
||||
@@ -486,6 +499,7 @@
|
||||
"Room & Terms": "Zimmer & Bedingungen",
|
||||
"Room charge": "Zimmerpreis",
|
||||
"Room facilities": "Zimmerausstattung",
|
||||
"Room total": "Zimmer total",
|
||||
"Room {roomIndex}": "Zimmer {roomIndex}",
|
||||
"Rooms": "Räume",
|
||||
"Rooms & Guests": "Zimmer & Gäste",
|
||||
@@ -545,6 +559,7 @@
|
||||
"Sports": "Sport",
|
||||
"Standard price": "Standardpreis",
|
||||
"Standing table": "Standing table",
|
||||
"Status": "Status",
|
||||
"Stay at {hotelName} | Hotel in {destination}": "Übernachten Sie im {hotelName} | Hotel in {destination}",
|
||||
"Street": "Straße",
|
||||
"Submit": "Submit",
|
||||
@@ -573,8 +588,9 @@
|
||||
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "Um den Mitgliederpreis von <span>{price}</span> zu erhalten, loggen Sie sich ein oder treten Sie Scandic Friends bei, wenn Sie die Buchung abschließen.",
|
||||
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "Um Ihre Reservierung zu sichern, bitten wir Sie, Ihre Zahlungskarteninformationen zu geben. Sie können sicher sein, dass keine Gebühren zu diesem Zeitpunkt erhoben werden.",
|
||||
"Total": "Gesamt",
|
||||
"Total Points": "Gesamtpunktzahl",
|
||||
"Total cost": "Total cost",
|
||||
"Total paid": "Gesamt bezahlt",
|
||||
"Total points": "Gesamtpunktzahl",
|
||||
"Total price": "Gesamtpreis",
|
||||
"Total price (incl VAT)": "Gesamtpreis (inkl. MwSt.)",
|
||||
"Tourist": "Tourist",
|
||||
@@ -587,6 +603,8 @@
|
||||
"Type of room": "Zimmerart",
|
||||
"U-shape": "U-shape",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Unpaid": "Nicht bezahlt",
|
||||
"Until {time}, {date}": "Bis {time} am {date}",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Upgrade your stay": "Werten Sie Ihren Aufenthalt auf",
|
||||
"Use Bonus Cheque": "Bonusscheck nutzen",
|
||||
@@ -662,6 +680,7 @@
|
||||
"Your details": "Ihre Angaben",
|
||||
"Your hotel": "Your hotel",
|
||||
"Your level": "Dein level",
|
||||
"Your member tier": "Ihr Mitgliedsniveau",
|
||||
"Your points to spend": "Meine Punkte",
|
||||
"Your room": "Ihr Zimmer",
|
||||
"Your selected bed type will be provided based on availability": "Ihre ausgewählte Bettart wird basierend auf der Verfügbarkeit bereitgestellt",
|
||||
@@ -676,6 +695,7 @@
|
||||
"booking.selectRoom": "Vælg værelse",
|
||||
"booking.thisRoomIsEquippedWith": "Dieses Zimmer ist ausgestattet mit",
|
||||
"friday": "freitag",
|
||||
"from": "von",
|
||||
"max {seatings} pers": "max {seatings} pers",
|
||||
"monday": "montag",
|
||||
"next level: {nextLevel}": "next level: {nextLevel}",
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"Based on availability": "Based on availability",
|
||||
"Bed": "Bed",
|
||||
"Bed options": "Bed options",
|
||||
"Bed preference": "Bed preference",
|
||||
"Bed type": "Bed type",
|
||||
"Bike friendly": "Bike friendly",
|
||||
"Birth date": "Birth date",
|
||||
@@ -79,6 +80,7 @@
|
||||
"Booking": "Booking",
|
||||
"Booking confirmation": "Booking confirmation",
|
||||
"Booking number": "Booking number",
|
||||
"Booking policy": "Booking policy",
|
||||
"Booking summary": "Booking summary",
|
||||
"Breakfast": "Breakfast",
|
||||
"Breakfast ({totalAdults, plural, one {# adult} other {# adults}}) x {totalBreakfasts}": "Breakfast ({totalAdults, plural, one {# adult} other {# adults}}) x {totalBreakfasts}",
|
||||
@@ -103,7 +105,7 @@
|
||||
"Cancel booking": "Cancel booking",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Change room",
|
||||
"Changes can be made until {time}, {date} pending availability.": "Changes can be made until {time}, {date} pending availability.",
|
||||
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Changes can be made until {time} on {date}, subject to availability. Room rates may vary.",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Check in",
|
||||
"Check in from: {checkInTime}": "Check in from: {checkInTime}",
|
||||
@@ -166,6 +168,7 @@
|
||||
"Dialog": "Dialog",
|
||||
"Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>": "Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>",
|
||||
"Dimensions": "Dimensions",
|
||||
"Directions": "Directions",
|
||||
"Discard changes": "Discard changes",
|
||||
"Discard unsaved changes?": "Discard unsaved changes?",
|
||||
"Discounted rooms": "Discounted rooms",
|
||||
@@ -233,6 +236,8 @@
|
||||
"Full circle": "Full circle",
|
||||
"Full price rooms": "Full price rooms",
|
||||
"Garage": "Garage",
|
||||
"Get directions": "Get directions",
|
||||
"Get hotel directions": "Get hotel directions",
|
||||
"Get inspired": "Get inspired",
|
||||
"Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.": "Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.",
|
||||
"Get the member price: {amount}": "Get the member price: {amount}",
|
||||
@@ -243,6 +248,7 @@
|
||||
"Go to profile": "Go to profile",
|
||||
"Guarantee booking with credit card": "Guarantee booking with credit card",
|
||||
"Guest information": "Guest information",
|
||||
"Guests": "Guests",
|
||||
"Guests & Rooms": "Guests & Rooms",
|
||||
"Gym": "Gym",
|
||||
"Half circle": "Half circle",
|
||||
@@ -252,6 +258,7 @@
|
||||
"Highest level": "Highest level",
|
||||
"Hiking": "Hiking",
|
||||
"Home": "Home",
|
||||
"Homepage": "Homepage",
|
||||
"Hospital": "Hospital",
|
||||
"Hotel": "Hotel",
|
||||
"Hotel details": "Hotel details",
|
||||
@@ -325,6 +332,7 @@
|
||||
"Main menu": "Main menu",
|
||||
"Manage booking": "Manage booking",
|
||||
"Manage preferences": "Manage preferences",
|
||||
"Manage stay": "Manage stay",
|
||||
"Map": "Map",
|
||||
"Map of the city center": "Map of the city center",
|
||||
"Map of the country": "Map of the country",
|
||||
@@ -335,6 +343,7 @@
|
||||
"Meetings & Conferences": "Meetings & Conferences",
|
||||
"Member Since: {value}": "Member Since: {value}",
|
||||
"Member discount": "Member discount",
|
||||
"Member no.": "Member no.",
|
||||
"Member price": "Member price",
|
||||
"Member price activated": "Member price activated",
|
||||
"Member price from": "Member price from",
|
||||
@@ -350,6 +359,7 @@
|
||||
"Menus": "Menus",
|
||||
"Modify": "Modify",
|
||||
"Modify dates": "Modify dates",
|
||||
"Modify guest details": "Modify guest details",
|
||||
"Mon-Fri Always open": "Mon-Fri Always open",
|
||||
"Mon-Fri {openingTime}-{closingTime}": "Mon-Fri {openingTime}-{closingTime}",
|
||||
"Monday": "Monday",
|
||||
@@ -366,7 +376,7 @@
|
||||
"My pages": "My pages",
|
||||
"My pages menu": "My pages menu",
|
||||
"My payment cards": "My payment cards",
|
||||
"My stay at {hotelName}": "My stay at {hotelName}",
|
||||
"My stay at": "My stay at",
|
||||
"My wishes": "My wishes",
|
||||
"N/A": "N/A",
|
||||
"Name": "Name",
|
||||
@@ -414,6 +424,7 @@
|
||||
"Outdoor pool": "Outdoor pool",
|
||||
"Overview": "Overview",
|
||||
"PETR": "Pet",
|
||||
"Paid": "Paid",
|
||||
"Parking": "Parking",
|
||||
"Parking / Garage": "Parking / Garage",
|
||||
"Parking can be reserved in advance": "Parking can be reserved in advance",
|
||||
@@ -476,7 +487,9 @@
|
||||
"Redeemed & valid through:": "Redeemed & valid through:",
|
||||
"Redirecting you to SAS": "Redirecting you to SAS",
|
||||
"Redirecting you to my pages.": "Redirecting you to my pages.",
|
||||
"Reference": "Reference",
|
||||
"Reference #{bookingNr}": "Reference #{bookingNr}",
|
||||
"Reference number": "Reference number",
|
||||
"Relax": "Relax",
|
||||
"Remember code": "Remember code",
|
||||
"Remove card from member profile": "Remove card from member profile",
|
||||
@@ -492,6 +505,7 @@
|
||||
"Room & Terms": "Room & Terms",
|
||||
"Room charge": "Room charge",
|
||||
"Room facilities": "Room facilities",
|
||||
"Room total": "Room total",
|
||||
"Room {roomIndex}": "Room {roomIndex}",
|
||||
"Rooms": "Rooms",
|
||||
"Rooms & Guests": "Rooms & Guests",
|
||||
@@ -551,6 +565,7 @@
|
||||
"Sports": "Sports",
|
||||
"Standard price": "Standard price",
|
||||
"Standing table": "Standing table",
|
||||
"Status": "Status",
|
||||
"Stay at {hotelName} | Hotel in {destination}": "Stay at {hotelName} | Hotel in {destination}",
|
||||
"Street": "Street",
|
||||
"Submit": "Submit",
|
||||
@@ -580,8 +595,9 @@
|
||||
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "To get the member price <span>{price}</span>, log in or join when completing the booking.",
|
||||
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.",
|
||||
"Total": "Total",
|
||||
"Total Points": "Total Points",
|
||||
"Total cost": "Total cost",
|
||||
"Total paid": "Total paid",
|
||||
"Total points": "Total points",
|
||||
"Total price": "Total price",
|
||||
"Tourist": "Tourist",
|
||||
"Transaction date": "Transaction date",
|
||||
@@ -593,6 +609,8 @@
|
||||
"Type of room": "Type of room",
|
||||
"U-shape": "U-shape",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Unpaid": "Unpaid",
|
||||
"Until {time}, {date}": "Until {time}, {date}",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Upgrade your stay": "Upgrade your stay",
|
||||
"Use Bonus Cheque": "Use Bonus Cheque",
|
||||
@@ -668,6 +686,7 @@
|
||||
"Your details": "Your details",
|
||||
"Your hotel": "Your hotel",
|
||||
"Your level": "Your level",
|
||||
"Your member tier": "Your member tier",
|
||||
"Your points to spend": "Your points to spend",
|
||||
"Your room": "Your room",
|
||||
"Your selected bed type will be provided based on availability": "Your selected bed type will be provided based on availability",
|
||||
@@ -678,7 +697,9 @@
|
||||
"as of today": "as of today",
|
||||
"booking.selectRoom": "Select room",
|
||||
"booking.thisRoomIsEquippedWith": "This room is equipped with",
|
||||
"cm": "cm",
|
||||
"friday": "friday",
|
||||
"from": "from",
|
||||
"max {seatings} pers": "max {seatings} pers",
|
||||
"monday": "monday",
|
||||
"next level: {nextLevel}": "next level: {nextLevel}",
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"Based on availability": "Saatavuuden mukaan",
|
||||
"Bed": "Vuodetyyppi",
|
||||
"Bed options": "Vuodevaihtoehdot",
|
||||
"Bed preference": "Vuodetyyppi",
|
||||
"Bed type": "Vuodetyyppi",
|
||||
"Bike friendly": "Pyöräystävällinen",
|
||||
"Birth date": "Syntymäaika",
|
||||
@@ -77,6 +78,7 @@
|
||||
"Booking": "Booking",
|
||||
"Booking confirmation": "Varausvahvistus",
|
||||
"Booking number": "Varausnumero",
|
||||
"Booking policy": "Varauskäytäntö",
|
||||
"Booking summary": "Yhteenveto",
|
||||
"Breakfast": "Aamiainen",
|
||||
"Breakfast ({totalAdults, plural, one {# adult} other {# adults}}) x {totalBreakfasts}": "Aamiainen ({totalAdults, plural, one {# aikuinen} other {# aikuiset}}) x {totalBreakfasts}",
|
||||
@@ -100,6 +102,7 @@
|
||||
"Cancel": "Peruuttaa",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Vaihda huonetta",
|
||||
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Muutoksia voi tehdä {time} päivänä {date}, olettaen saatavuuden olemassaolon. Huonehinnat voivat muuttua.",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Sisäänkirjautuminen",
|
||||
"Check in from: {checkInTime}": "Sisäänkirjautuminen alkaen: {checkInTime}",
|
||||
@@ -162,6 +165,7 @@
|
||||
"Dialog": "Dialog",
|
||||
"Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>": "Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>",
|
||||
"Dimensions": "Mitat",
|
||||
"Directions": "Suunnat",
|
||||
"Discard changes": "Hylkää muutokset",
|
||||
"Discard unsaved changes?": "Hylkäätkö tallentamattomat muutokset?",
|
||||
"Discounted rooms": "Alennetut huoneet",
|
||||
@@ -229,6 +233,8 @@
|
||||
"Full circle": "Full circle",
|
||||
"Full price rooms": "Täyshintaiset huoneet",
|
||||
"Garage": "Autotalli",
|
||||
"Get directions": "Hae suunnat",
|
||||
"Get hotel directions": "Hae hotellin suunnat",
|
||||
"Get inspired": "Inspiroidu",
|
||||
"Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.": "Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.",
|
||||
"Get the member price: {amount}": "Vain maksaa {amount}",
|
||||
@@ -239,6 +245,7 @@
|
||||
"Go to profile": "Go to profile",
|
||||
"Guarantee booking with credit card": "Varmista varaus luottokortilla",
|
||||
"Guest information": "Vieraan tiedot",
|
||||
"Guests": "Vierailijat",
|
||||
"Guests & Rooms": "Vieraat & Huoneet",
|
||||
"Gym": "Kuntosali",
|
||||
"Half circle": "Half circle",
|
||||
@@ -248,6 +255,7 @@
|
||||
"Highest level": "Korkein taso",
|
||||
"Hiking": "Hiking",
|
||||
"Home": "Kotiin",
|
||||
"Homepage": "Kotisivu",
|
||||
"Hospital": "Sairaala",
|
||||
"Hotel": "Hotelli",
|
||||
"Hotel details": "Hotellin tiedot",
|
||||
@@ -320,6 +328,7 @@
|
||||
"Main menu": "Päävalikko",
|
||||
"Manage booking": "Manage booking",
|
||||
"Manage preferences": "Asetusten hallinta",
|
||||
"Manage stay": "Hallitse majoituksesi",
|
||||
"Map": "Kartta",
|
||||
"Map of the city center": "Kartta kaupungin keskustasta",
|
||||
"Map of the country": "Kartta maasta",
|
||||
@@ -330,6 +339,7 @@
|
||||
"Meetings & Conferences": "Kokoukset & Konferenssit",
|
||||
"Member Since: {value}": "Member Since: {value}",
|
||||
"Member discount": "Member discount",
|
||||
"Member no.": "Jäsenyysnumero",
|
||||
"Member price": "Jäsenhinta",
|
||||
"Member price activated": "Jäsenhinta aktivoitu",
|
||||
"Member price from": "Jäsenhinta alkaen",
|
||||
@@ -344,6 +354,7 @@
|
||||
"Menu": "Valikko",
|
||||
"Menus": "Valikot",
|
||||
"Modify": "Muokkaa",
|
||||
"Modify guest details": "Muuta vierailijoiden tietoja",
|
||||
"Mon-Fri Always open": "Ma-Pe Aina auki",
|
||||
"Mon-Fri {openingTime}-{closingTime}": "Ma-Pe {openingTime}-{closingTime}",
|
||||
"Monday": "Maanantai",
|
||||
@@ -360,7 +371,7 @@
|
||||
"My pages": "Omat sivut",
|
||||
"My pages menu": "Omat sivut -valikko",
|
||||
"My payment cards": "Minun maksukortit",
|
||||
"My stay at {hotelName}": "My stay at {hotelName}",
|
||||
"My stay at": "My stay at",
|
||||
"My wishes": "Toiveeni",
|
||||
"N/A": "N/A",
|
||||
"Name": "Nimi",
|
||||
@@ -408,6 +419,7 @@
|
||||
"Outdoor pool": "Ulkouima-allas",
|
||||
"Overview": "Yleiskatsaus",
|
||||
"PETR": "Lemmikki",
|
||||
"Paid": "Maksettu",
|
||||
"Parking": "Pysäköinti",
|
||||
"Parking / Garage": "Pysäköinti / Autotalli",
|
||||
"Parking can be reserved in advance": "Pysäköintipaikan voi varata etukäteen",
|
||||
@@ -470,6 +482,7 @@
|
||||
"Redeemed & valid through:": "Redeemed & valid through:",
|
||||
"Redirecting you to SAS": "Redirecting you to SAS",
|
||||
"Redirecting you to my pages.": "Redirecting you to my pages.",
|
||||
"Reference": "Viite",
|
||||
"Reference #{bookingNr}": "Referenssi #{bookingNr}",
|
||||
"Relax": "Rentoutua",
|
||||
"Remember code": "Muista koodi",
|
||||
@@ -485,6 +498,7 @@
|
||||
"Room & Terms": "Huone & Ehdot",
|
||||
"Room charge": "Huonemaksu",
|
||||
"Room facilities": "Huoneen varustelu",
|
||||
"Room total": "Huoneen kokonaishinta",
|
||||
"Room {roomIndex}": "Huone {roomIndex}",
|
||||
"Rooms": "Huoneet",
|
||||
"Rooms & Guests": "Huoneet & Vieraat",
|
||||
@@ -545,6 +559,7 @@
|
||||
"Sports": "Urheilu",
|
||||
"Standard price": "Normaali hinta",
|
||||
"Standing table": "Standing table",
|
||||
"Status": "Status",
|
||||
"Stay at {hotelName} | Hotel in {destination}": "Majoitu kohteessa {hotelName} | Hotelli kohteessa {destination}",
|
||||
"Street": "Katu",
|
||||
"Submit": "Submit",
|
||||
@@ -573,8 +588,9 @@
|
||||
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "Jäsenhintaan saavat sisäänkirjautuneet tai liittyneet jäsenet.",
|
||||
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "Varmistaaksesi varauksen, pyydämme sinua antamaan meille maksukortin tiedot. Varmista, että ei veloiteta maksusi tällä hetkellä.",
|
||||
"Total": "Kokonais",
|
||||
"Total Points": "Kokonaispisteet",
|
||||
"Total cost": "Total cost",
|
||||
"Total paid": "Kokonais maksamasi",
|
||||
"Total points": "Kokonaispisteet",
|
||||
"Total price": "Kokonaishinta",
|
||||
"Total price (incl VAT)": "Kokonaishinta (sis. ALV)",
|
||||
"Tourist": "Turisti",
|
||||
@@ -587,6 +603,8 @@
|
||||
"Type of room": "Huonetyyppi",
|
||||
"U-shape": "U-muoto",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Unpaid": "Maksettaa",
|
||||
"Until {time}, {date}": "Asti {time}, {date}",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Upgrade your stay": "Päivitä oleskelusi",
|
||||
"Use Bonus Cheque": "Käytä bonussekkiä",
|
||||
@@ -662,6 +680,7 @@
|
||||
"Your details": "Tietosi",
|
||||
"Your hotel": "Your hotel",
|
||||
"Your level": "Tasosi",
|
||||
"Your member tier": "Sinun jäsenyysluokkasi",
|
||||
"Your points to spend": "Käytettävissä olevat pisteesi",
|
||||
"Your room": "Sinun huoneesi",
|
||||
"Your selected bed type will be provided based on availability": "Valitun vuodetyypin toimitetaan saatavuuden mukaan",
|
||||
@@ -676,6 +695,7 @@
|
||||
"booking.selectRoom": "Valitse huone",
|
||||
"booking.thisRoomIsEquippedWith": "Tämä huone on varustettu",
|
||||
"friday": "perjantai",
|
||||
"from": "alkaa",
|
||||
"max {seatings} pers": "max {seatings} pers",
|
||||
"monday": "maanantai",
|
||||
"next level: {nextLevel}": "next level: {nextLevel}",
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"Based on availability": "Basert på tilgjengelighet",
|
||||
"Bed": "Seng type",
|
||||
"Bed options": "Sengemuligheter",
|
||||
"Bed preference": "Sengvalg",
|
||||
"Bed type": "Seng type",
|
||||
"Bike friendly": "Sykkelvennlig",
|
||||
"Birth date": "Fødselsdato",
|
||||
@@ -77,6 +78,7 @@
|
||||
"Booking": "Booking",
|
||||
"Booking confirmation": "Bestillingsbekreftelse",
|
||||
"Booking number": "Bestillingsnummer",
|
||||
"Booking policy": "Bestillingsbetingelser",
|
||||
"Booking summary": "Sammendrag",
|
||||
"Breakfast": "Frokost",
|
||||
"Breakfast ({totalAdults, plural, one {# adult} other {# adults}}) x {totalBreakfasts}": "Frokost ({totalAdults, plural, one {# voksen} other {# voksne}}) x {totalBreakfasts}",
|
||||
@@ -100,6 +102,7 @@
|
||||
"Cancel": "Avbryt",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Endre rom",
|
||||
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Endringer kan gjøres til {time} på {date}, under forutsetning av tilgjengelighet. Rompriser kan variere.",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Sjekk inn",
|
||||
"Check in from: {checkInTime}": "Innsjekking fra: {checkInTime}",
|
||||
@@ -161,6 +164,7 @@
|
||||
"Dialog": "Dialog",
|
||||
"Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>": "Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>",
|
||||
"Dimensions": "Dimensjoner",
|
||||
"Directions": "Retninger",
|
||||
"Discard changes": "Forkaste endringer",
|
||||
"Discard unsaved changes?": "Forkaste endringer som ikke er lagret?",
|
||||
"Discounted rooms": "Rabatterte rom",
|
||||
@@ -228,6 +232,8 @@
|
||||
"Full circle": "Full circle",
|
||||
"Full price rooms": "Full pris rom",
|
||||
"Garage": "Garasje",
|
||||
"Get directions": "Få retninger",
|
||||
"Get hotel directions": "Få hotel retninger",
|
||||
"Get inspired": "Bli inspirert",
|
||||
"Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.": "Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.",
|
||||
"Get the member price: {amount}": "Bare betal {amount}",
|
||||
@@ -238,6 +244,7 @@
|
||||
"Go to profile": "Go to profile",
|
||||
"Guarantee booking with credit card": "Garantere booking med kredittkort",
|
||||
"Guest information": "Informasjon til gjester",
|
||||
"Guests": "Gjester",
|
||||
"Guests & Rooms": "Gjester & rom",
|
||||
"Gym": "Treningsstudio",
|
||||
"Half circle": "Half circle",
|
||||
@@ -247,6 +254,7 @@
|
||||
"Highest level": "Høyeste nivå",
|
||||
"Hiking": "Fotturer",
|
||||
"Home": "Hjem",
|
||||
"Homepage": "Hjemmeside",
|
||||
"Hospital": "Sykehus",
|
||||
"Hotel": "Hotel",
|
||||
"Hotel details": "Detaljer om hotellet",
|
||||
@@ -319,6 +327,7 @@
|
||||
"Main menu": "Hovedmeny",
|
||||
"Manage booking": "Manage booking",
|
||||
"Manage preferences": "Administrer preferanser",
|
||||
"Manage stay": "Administrer ophold",
|
||||
"Map": "Kart",
|
||||
"Map of the city center": "Kart over sentrum",
|
||||
"Map of the country": "Kart over landet",
|
||||
@@ -329,6 +338,7 @@
|
||||
"Meetings & Conferences": "Møter & Konferanser",
|
||||
"Member Since: {value}": "Member Since: {value}",
|
||||
"Member discount": "Member discount",
|
||||
"Member no.": "Medlemsnummer",
|
||||
"Member price": "Medlemspris",
|
||||
"Member price activated": "Medlemspris aktivert",
|
||||
"Member price from": "Medlemspris fra",
|
||||
@@ -343,6 +353,7 @@
|
||||
"Menu": "Menu",
|
||||
"Menus": "Menyer",
|
||||
"Modify": "Endre",
|
||||
"Modify guest details": "Endre gjestdetaljer",
|
||||
"Mon-Fri Always open": "Man-Fre Alltid åpen",
|
||||
"Mon-Fri {openingTime}-{closingTime}": "Man-Fre {openingTime}-{closingTime}",
|
||||
"Monday": "Mandag",
|
||||
@@ -359,7 +370,7 @@
|
||||
"My pages": "Mine sider",
|
||||
"My pages menu": "Mine sider-menyen",
|
||||
"My payment cards": "Mine betalingskort",
|
||||
"My stay at {hotelName}": "My stay at {hotelName}",
|
||||
"My stay at": "My stay at",
|
||||
"My wishes": "Mine ønsker",
|
||||
"N/A": "N/A",
|
||||
"Name": "Navn",
|
||||
@@ -407,6 +418,7 @@
|
||||
"Outdoor pool": "Utendørs basseng",
|
||||
"Overview": "Oversikt",
|
||||
"PETR": "Kjæledyr",
|
||||
"Paid": "Betalt",
|
||||
"Parking": "Parkering",
|
||||
"Parking / Garage": "Parkering / Garasje",
|
||||
"Parking can be reserved in advance": "Parkering kan reserveres på forhånd",
|
||||
@@ -469,6 +481,7 @@
|
||||
"Redeemed & valid through:": "Redeemed & valid through:",
|
||||
"Redirecting you to SAS": "Redirecting you to SAS",
|
||||
"Redirecting you to my pages.": "Redirecting you to my pages.",
|
||||
"Reference": "Referanse",
|
||||
"Reference #{bookingNr}": "Referanse #{bookingNr}",
|
||||
"Relax": "Slappe av",
|
||||
"Remember code": "Husk kode",
|
||||
@@ -484,6 +497,7 @@
|
||||
"Room & Terms": "Rom & Vilkår",
|
||||
"Room charge": "Pris for rom",
|
||||
"Room facilities": "Romfasiliteter",
|
||||
"Room total": "Rom total",
|
||||
"Room {roomIndex}": "Rom {roomIndex}",
|
||||
"Rooms": "Rom",
|
||||
"Rooms & Guests": "Rom og gjester",
|
||||
@@ -543,6 +557,7 @@
|
||||
"Sports": "Sport",
|
||||
"Standard price": "Standardpris",
|
||||
"Standing table": "Standing table",
|
||||
"Status": "Status",
|
||||
"Stay at {hotelName} | Hotel in {destination}": "Bo på {hotelName} | Hotell i {destination}",
|
||||
"Street": "Gate",
|
||||
"Submit": "Submit",
|
||||
@@ -571,9 +586,10 @@
|
||||
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "For å få medlemsprisen <span>{price}</span>, logg inn eller bli med når du fullfører bestillingen.",
|
||||
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "For å sikre din reservasjon, ber vi om at du gir oss dine betalingskortdetaljer. Vær sikker på at ingen gebyrer vil bli belastet på dette tidspunktet.",
|
||||
"Total": "Total",
|
||||
"Total Points": "Totale poeng",
|
||||
"Total cost": "Total cost",
|
||||
"Total incl VAT": "Sum inkl mva",
|
||||
"Total paid": "Total betalt",
|
||||
"Total points": "Totale poeng",
|
||||
"Total price": "Totalpris",
|
||||
"Tourist": "Turist",
|
||||
"Transaction date": "Transaksjonsdato",
|
||||
@@ -585,6 +601,8 @@
|
||||
"Type of room": "Romtype",
|
||||
"U-shape": "U-form",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Unpaid": "Ikke betalt",
|
||||
"Until {time}, {date}": "Til {time}, {date}",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Upgrade your stay": "Oppgrader oppholdet ditt",
|
||||
"Use Bonus Cheque": "Bruk bonussjekk",
|
||||
@@ -660,6 +678,7 @@
|
||||
"Your details": "Dine detaljer",
|
||||
"Your hotel": "Your hotel",
|
||||
"Your level": "Ditt nivå",
|
||||
"Your member tier": "Ditt medlemskapsnivå",
|
||||
"Your points to spend": "Dine brukbare poeng",
|
||||
"Your room": "Rommet ditt",
|
||||
"Your selected bed type will be provided based on availability": "Din valgte sengtype vil blive stillet til rådighed baseret på tilgængelighed",
|
||||
@@ -674,6 +693,7 @@
|
||||
"booking.selectRoom": "Velg rom",
|
||||
"booking.thisRoomIsEquippedWith": "Dette rommet er utstyrt med",
|
||||
"friday": "fredag",
|
||||
"from": "fra",
|
||||
"max {seatings} pers": "max {seatings} pers",
|
||||
"monday": "mandag",
|
||||
"next level: {nextLevel}": "next level: {nextLevel}",
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"Based on availability": "Baserat på tillgänglighet",
|
||||
"Bed": "Sängtyp",
|
||||
"Bed options": "Sängalternativ",
|
||||
"Bed preference": "Sängval",
|
||||
"Bed type": "Sängtyp",
|
||||
"Bike friendly": "Cykelvänligt",
|
||||
"Birth date": "Födelsedatum",
|
||||
@@ -77,6 +78,7 @@
|
||||
"Booking": "Booking",
|
||||
"Booking confirmation": "Bokningsbekräftelse",
|
||||
"Booking number": "Bokningsnummer",
|
||||
"Booking policy": "Bokningsvillkor",
|
||||
"Booking summary": "Sammanfattning",
|
||||
"Breakfast": "Frukost",
|
||||
"Breakfast ({totalAdults, plural, one {# adult} other {# adults}}) x {totalBreakfasts}": "Frukost ({totalAdults, plural, one {# vuxen} other {# vuxna}}) x {totalBreakfasts}",
|
||||
@@ -100,6 +102,7 @@
|
||||
"Cancel": "Avbryt",
|
||||
"Cancellation policy": "Cancellation policy",
|
||||
"Change room": "Ändra rum",
|
||||
"Changes can be made until {time} on {date}, subject to availability. Room rates may vary.": "Ändringar kan göras tills {time} den {date}, under förutsättning av tillgänglighet. Priserna för rummen kan variera.",
|
||||
"Check for level upgrade": "Check for level upgrade",
|
||||
"Check in": "Checka in",
|
||||
"Check in from: {checkInTime}": "Incheckning från: {checkInTime}",
|
||||
@@ -161,6 +164,7 @@
|
||||
"Dialog": "Dialog",
|
||||
"Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>": "Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>",
|
||||
"Dimensions": "Dimensioner",
|
||||
"Directions": "Vägbeskrivning",
|
||||
"Discard changes": "Ignorera ändringar",
|
||||
"Discard unsaved changes?": "Vill du ignorera ändringar som inte har sparats?",
|
||||
"Discounted rooms": "Rabatterade rum",
|
||||
@@ -228,6 +232,8 @@
|
||||
"Full circle": "Full circle",
|
||||
"Full price rooms": "Fullpris rum",
|
||||
"Garage": "Garage",
|
||||
"Get directions": "Hämta vägbeskrivning",
|
||||
"Get hotel directions": "Hämta vägbeskrivning till hotellet",
|
||||
"Get inspired": "Bli inspirerad",
|
||||
"Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.": "Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.",
|
||||
"Get the member price: {amount}": "Betala endast {amount}",
|
||||
@@ -238,6 +244,7 @@
|
||||
"Go to profile": "Go to profile",
|
||||
"Guarantee booking with credit card": "Garantera bokning med kreditkort",
|
||||
"Guest information": "Information till gästerna",
|
||||
"Guests": "Gäster",
|
||||
"Guests & Rooms": "Gäster & rum",
|
||||
"Gym": "Gym",
|
||||
"Half circle": "Half circle",
|
||||
@@ -247,6 +254,7 @@
|
||||
"Highest level": "Högsta nivå",
|
||||
"Hiking": "Vandring",
|
||||
"Home": "Hem",
|
||||
"Homepage": "Hemsida",
|
||||
"Hospital": "Sjukhus",
|
||||
"Hotel": "Hotell",
|
||||
"Hotel details": "Detaljer om hotellet",
|
||||
@@ -319,6 +327,7 @@
|
||||
"Main menu": "Huvudmeny",
|
||||
"Manage booking": "Manage booking",
|
||||
"Manage preferences": "Hantera inställningar",
|
||||
"Manage stay": "Hantera vistelse",
|
||||
"Map": "Karta",
|
||||
"Map of the city center": "Karta över stadskärnan",
|
||||
"Map of the country": "Karta över landet",
|
||||
@@ -329,6 +338,7 @@
|
||||
"Meetings & Conferences": "Möten & Konferenser",
|
||||
"Member Since: {value}": "Member Since: {value}",
|
||||
"Member discount": "Member discount",
|
||||
"Member no.": "Medlemsnummer",
|
||||
"Member price": "Medlemspris",
|
||||
"Member price activated": "Medlemspris aktiverat",
|
||||
"Member price from": "Medlemspris från",
|
||||
@@ -343,6 +353,7 @@
|
||||
"Menu": "Meny",
|
||||
"Menus": "Menyer",
|
||||
"Modify": "Ändra",
|
||||
"Modify guest details": "Ändra gästinformation",
|
||||
"Mon-Fri Always open": "Mån-Fre Alltid öppet",
|
||||
"Mon-Fri {openingTime}-{closingTime}": "Mån-Fre {openingTime}-{closingTime}",
|
||||
"Monday": "Måndag",
|
||||
@@ -359,7 +370,7 @@
|
||||
"My pages": "Mina sidor",
|
||||
"My pages menu": "Mina sidor meny",
|
||||
"My payment cards": "Mina betalningskort",
|
||||
"My stay at {hotelName}": "My stay at {hotelName}",
|
||||
"My stay at": "My stay at",
|
||||
"My wishes": "Mina önskningar",
|
||||
"N/A": "N/A",
|
||||
"Name": "Namn",
|
||||
@@ -407,6 +418,7 @@
|
||||
"Outdoor pool": "Utomhuspool",
|
||||
"Overview": "Översikt",
|
||||
"PETR": "Husdjur",
|
||||
"Paid": "Betalt",
|
||||
"Parking": "Parkering",
|
||||
"Parking / Garage": "Parkering / Garage",
|
||||
"Parking can be reserved in advance": "Parkering kan reserveras i förväg",
|
||||
@@ -469,6 +481,7 @@
|
||||
"Redeemed & valid through:": "Redeemed & valid through:",
|
||||
"Redirecting you to SAS": "Redirecting you to SAS",
|
||||
"Redirecting you to my pages.": "Redirecting you to my pages.",
|
||||
"Reference": "Referens",
|
||||
"Reference #{bookingNr}": "Referens #{bookingNr}",
|
||||
"Relax": "Koppla av",
|
||||
"Remember code": "Kom ihåg kod",
|
||||
@@ -484,6 +497,7 @@
|
||||
"Room & Terms": "Rum & Villkor",
|
||||
"Room charge": "Rumspris",
|
||||
"Room facilities": "Rumfaciliteter",
|
||||
"Room total": "Rum total",
|
||||
"Room {roomIndex}": "Rum {roomIndex}",
|
||||
"Rooms": "Rum",
|
||||
"Rooms & Guests": "Rum och gäster",
|
||||
@@ -543,6 +557,7 @@
|
||||
"Sports": "Sport",
|
||||
"Standard price": "Standardpris",
|
||||
"Standing table": "Ståbord",
|
||||
"Status": "Status",
|
||||
"Stay at {hotelName} | Hotel in {destination}": "Bo på {hotelName} | Hotell i {destination}",
|
||||
"Street": "Gata",
|
||||
"Submit": "Submit",
|
||||
@@ -571,9 +586,10 @@
|
||||
"To get the member price <span>{price}</span>, log in or join when completing the booking.": "För att få medlemsprisen <span>{price}</span>, logga in eller bli medlem när du slutför bokningen.",
|
||||
"To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.": "För att säkra din bokning ber vi om att du ger oss dina betalkortdetaljer. Välj säker på att ingen avgifter kommer att debiteras just nu.",
|
||||
"Total": "Totalt",
|
||||
"Total Points": "Poäng totalt",
|
||||
"Total cost": "Total cost",
|
||||
"Total incl VAT": "Totalt inkl moms",
|
||||
"Total paid": "Total betalt",
|
||||
"Total points": "Poäng totalt",
|
||||
"Total price": "Totalpris",
|
||||
"Tourist": "Turist",
|
||||
"Transaction date": "Transaktionsdatum",
|
||||
@@ -585,6 +601,8 @@
|
||||
"Type of room": "Rumstyp",
|
||||
"U-shape": "U-form",
|
||||
"Unlink accounts": "Unlink accounts",
|
||||
"Unpaid": "Ej betalt",
|
||||
"Until {time}, {date}": "Tills {time} den {date}",
|
||||
"Upgrade expires {upgradeExpires, date, short}": "Upgrade expires {upgradeExpires, date, short}",
|
||||
"Upgrade your stay": "Uppgradera din vistelse",
|
||||
"Use Bonus Cheque": "Använd bonuscheck",
|
||||
@@ -660,6 +678,7 @@
|
||||
"Your details": "Dina uppgifter",
|
||||
"Your hotel": "Your hotel",
|
||||
"Your level": "Din nivå",
|
||||
"Your member tier": "Din medlemskapsnivå",
|
||||
"Your points to spend": "Dina spenderbara poäng",
|
||||
"Your room": "Ditt rum",
|
||||
"Your selected bed type will be provided based on availability": "Din valda sängtyp kommer att tillhandahållas baserat på tillgänglighet",
|
||||
@@ -674,6 +693,7 @@
|
||||
"booking.selectRoom": "Välj rum",
|
||||
"booking.thisRoomIsEquippedWith": "Detta rum är utrustat med",
|
||||
"friday": "fredag",
|
||||
"from": "från",
|
||||
"max {seatings} pers": "max {seatings} pers",
|
||||
"monday": "måndag",
|
||||
"next level: {nextLevel}": "next level: {nextLevel}",
|
||||
|
||||
1407
public/_static/img/scandic-coin.svg
Normal file
1407
public/_static/img/scandic-coin.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 361 KiB |
958
public/_static/img/scandic-service.svg
Normal file
958
public/_static/img/scandic-service.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 195 KiB |
@@ -1,4 +1,5 @@
|
||||
export type ToggleSidePeekProps = {
|
||||
hotelId: string
|
||||
roomTypeCode?: string
|
||||
intent?: "text" | "textInverted"
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ export enum IconName {
|
||||
Cultural = "Cultural",
|
||||
CutleryOne = "CutleryOne",
|
||||
CutleryTwo = "CutleryTwo",
|
||||
Diamond = "Diamond",
|
||||
Directions = "Directions",
|
||||
DoorOpen = "DoorOpen",
|
||||
Dresser = "Dresser",
|
||||
ElectricBike = "ElectricBike",
|
||||
@@ -76,6 +78,7 @@ export enum IconName {
|
||||
Kids = "Kids",
|
||||
Lamp = "Lamp",
|
||||
LaundryMachine = "LaundryMachine",
|
||||
Link = "Link",
|
||||
LocalBar = "LocalBar",
|
||||
Location = "Location",
|
||||
Lock = "Lock",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export enum BreakfastPackageEnum {
|
||||
FREE_MEMBER_BREAKFAST = "BRF0",
|
||||
REGULAR_BREAKFAST = "BRF1",
|
||||
SPECIAL_PACKAGE_BREAKFAST = "F01S",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user