Merged in feat/SW-1368-1369-Guarantee-late-arrival (pull request #1512)

Feat/SW-1368 1369 Guarantee late arrival

* feat(SW-1368-SW-1369): guarantee late arrival for confirmation page and my stay

* feat(SW-1368-SW-1369): guarantee late arrival updated design

* feat(SW-1368-SW-1369): add translations

* feat(SW-1368-SW-1369): add translations

* feat(SW-1368-SW-1369): fix merge with master

* feat(SW-1368-SW-1369): add translations

* feat(SW-1368-SW-1369): add redirect with refId

* feat(SW-1368-SW-1369): if booking completed redirect to confirmation page

* feat(SW-1368-SW-1369): fix comments pr

* feat(SW-1368-SW-1369): fix comments pr

* feat(SW-1368-SW-1369): fix rebase master

* feat(SW-1368-SW-1369): fix duplicate flex rate check

* feat(SW-1368-SW-1369): if any room is flex, card must be used

* feat(SW-1368-SW-1369): move callback route

* feat(SW-1368-SW-1369): top align checkbox

* feat(SW-1368-SW-1369): top align checkbox


Approved-by: Tobias Johansson
Approved-by: Niclas Edenvin
This commit is contained in:
Bianca Widstam
2025-03-14 10:43:14 +00:00
parent 8ca862e32c
commit abd401c4f4
47 changed files with 1274 additions and 166 deletions

View File

@@ -0,0 +1,3 @@
.layout {
background-color: var(--Base-Background-Primary-Normal);
}

View File

@@ -0,0 +1,9 @@
import styles from "./layout.module.css"
import type { LangParams, LayoutArgs } from "@/types/params"
export default function GuaranteePaymentCallbackLayout({
children,
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
return <div className={styles.layout}>{children}</div>
}

View File

@@ -0,0 +1,77 @@
import { redirect } from "next/navigation"
import { BookingErrorCodeEnum } from "@/constants/booking"
import { hotelreservation } from "@/constants/routes/hotelReservation"
import { serverClient } from "@/lib/trpc/server"
import LoadingSpinner from "@/components/LoadingSpinner"
import type { LangParams, PageArgs } from "@/types/params"
export default async function GuaranteePaymentCallbackPage({
params,
searchParams,
}: PageArgs<
LangParams,
{
status: "error" | "success" | "cancel"
refId: string
confirmationNumber?: string
}
>) {
console.log(`[gla-payment-callback] callback started`)
const lang = params.lang
const status = searchParams.status
const confirmationNumber = searchParams.confirmationNumber
const refId = searchParams.refId
const myStayUrl = `${hotelreservation(lang)}/my-stay/${encodeURIComponent(refId)}`
if (status === "success" && confirmationNumber && refId) {
console.log(`[gla-payment-callback] redirecting to: ${myStayUrl}`)
redirect(myStayUrl)
}
const searchObject = new URLSearchParams()
let errorMessage = undefined
if (confirmationNumber) {
try {
const bookingStatus = await serverClient().booking.status({
confirmationNumber,
})
// TODO: how to handle errors for multiple rooms?
const error = bookingStatus.errors.find((e) => e.errorCode)
errorMessage =
error?.description ??
`No error message found for booking ${confirmationNumber}, status: ${status}`
searchObject.set(
"errorCode",
error
? error.errorCode.toString()
: BookingErrorCodeEnum.TransactionFailed
)
} catch {
console.error(
`[gla-payment-callback] failed to get booking status for ${confirmationNumber}, status: ${status}`
)
if (status === "cancel") {
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionCancelled)
}
if (status === "error") {
searchObject.set(
"errorCode",
BookingErrorCodeEnum.TransactionFailed.toString()
)
errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
}
}
console.log(errorMessage)
redirect(`${myStayUrl}?${searchObject.toString()}`)
}
return <LoadingSpinner />
}

View File

@@ -66,6 +66,7 @@ export default async function DetailsPage({
roomStayStartDate: booking.fromDate,
roomStayEndDate: booking.toDate,
roomTypeCode: room.roomTypeCode,
counterRateCode: room.counterRateCode,
bookingCode: booking.bookingCode,
}
@@ -89,13 +90,13 @@ export default async function DetailsPage({
// redirect back to select-rate if availability call fails
redirect(`${selectRate(lang)}?${selectRoomParams.toString()}`)
}
rooms.push({
bedTypes: roomAvailability.bedTypes,
breakfastIncluded: roomAvailability.breakfastIncluded,
cancellationRule: roomAvailability.cancellationRule,
cancellationText: roomAvailability.cancellationText,
isFlexRate: roomAvailability.isFlexRate,
mustBeGuaranteed: roomAvailability.mustBeGuaranteed,
memberMustBeGuaranteed: roomAvailability.memberMustBeGuaranteed,
packages,
rateTitle: roomAvailability.rateTitle,
rateDetails: roomAvailability.rateDetails ?? [],
@@ -109,8 +110,12 @@ export default async function DetailsPage({
roomAvailability.selectedRoom.status === AvailabilityEnum.Available,
})
}
const isCardOnlyPayment = rooms.some((room) => room?.mustBeGuaranteed)
const memberMustBeGuaranteed = rooms.some(
(room) => room?.memberMustBeGuaranteed
)
const isFlexRate = rooms.some((room) => room.isFlexRate)
const hotelData = await getHotel({
hotelId: booking.hotelId,
isCardOnlyPayment,
@@ -197,6 +202,9 @@ export default async function DetailsPage({
hotel.merchantInformationData.alternatePaymentOptions
}
supportedCards={hotel.merchantInformationData.cards}
mustBeGuaranteed={isCardOnlyPayment}
memberMustBeGuaranteed={memberMustBeGuaranteed}
isFlexRate={isFlexRate}
/>
</Suspense>
</div>

View File

@@ -54,6 +54,19 @@ export default function Room({ booking, img, roomName }: RoomProps) {
</>
)}
</div>
{booking.guaranteeInfo && (
<div className={styles.benefits}>
<CheckCircleIcon color="green" height={20} width={20} />
<Caption>
<strong>
{intl.formatMessage({ id: "Booking guaranteed." })}
</strong>{" "}
{intl.formatMessage({
id: "Your room will remain available for check-in even after 18:00.",
})}
</Caption>
</div>
)}
</header>
<div className={styles.booking}>
<Image

View File

@@ -5,10 +5,9 @@
}
.header {
align-items: flex-end;
display: grid;
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
grid-template-columns: 1fr;
}
.benefits {

View File

@@ -0,0 +1,64 @@
.container {
display: flex;
flex-direction: column;
gap: var(--Spacing-x3);
}
.title {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--Spacing-x-half);
}
.guaranteeContainer {
display: flex;
flex-direction: column;
gap: var(--Spacing-x-one-and-half);
background-color: var(--Base-Surface-Primary-light-Normal);
border-radius: var(--Corner-radius-Medium);
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
width: min(512px, 100%);
}
.checkbox {
display: flex;
gap: var(--Spacing-x-one-and-half);
}
.checkboxContainer {
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
padding: 0 var(--Spacing-x2);
width: min(800px, 100%);
}
.modalContainer {
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
align-items: center;
width: 100%;
}
.infoButton {
display: flex;
gap: var(--Space-x05);
outline: none;
}
.modalText {
text-align: center;
}
.closeButton {
margin-top: var(--Space-x15);
width: min(164px, 100%);
outline: none;
}
@media screen and (min-width: 768px) {
.modalContainer {
width: 552px;
}
}

View File

@@ -0,0 +1,180 @@
"use client"
import { useState } from "react"
import { useFormContext } from "react-hook-form"
import { useIntl } from "react-intl"
import { Button } from "@scandic-hotels/design-system/Button"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { PaymentMethodEnum } from "@/constants/booking"
import {
bookingTermsAndConditions,
privacyPolicy,
} from "@/constants/currentWebHrefs"
import { InfoCircleIcon } from "@/components/Icons"
import Modal from "@/components/Modal"
import Divider from "@/components/TempDesignSystem/Divider"
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
import Link from "@/components/TempDesignSystem/Link"
import useLang from "@/hooks/useLang"
import MySavedCards from "../Payment/MySavedCards"
import PaymentOption from "../Payment/PaymentOption"
import styles from "./confirm.module.css"
import type { CreditCard } from "@/types/user"
interface ConfirmBookingProps {
savedCreditCards: CreditCard[] | null
}
export default function ConfirmBooking({
savedCreditCards,
}: ConfirmBookingProps) {
const intl = useIntl()
const lang = useLang()
const [isModalOpen, setModalOpen] = useState(false)
const { watch } = useFormContext()
const guarantee = watch("guarantee")
return (
<div className={styles.container}>
<div className={styles.guaranteeContainer}>
<div className={styles.title}>
<div className={styles.checkbox}>
<Checkbox name="guarantee" />
<Typography variant="Body/Paragraph/mdBold">
<p>
{intl.formatMessage({
id: "Guarantee room for late arrival",
})}
</p>
</Typography>
</div>
<Button
variant="Text"
size="Small"
typography="Body/Supporting text (caption)/smBold"
className={styles.infoButton}
onPress={() => setModalOpen(true)}
>
<InfoCircleIcon
width={20}
height={20}
color="uiTextMediumContrast"
/>
{intl.formatMessage({ id: "How does it work" })}
</Button>
<Modal isOpen={isModalOpen} onToggle={() => setModalOpen(false)}>
<div className={styles.modalContainer}>
<Typography variant="Title/smRegular">
<h3>
{intl.formatMessage({ id: "Guarantee for late arrival" })}
</h3>
</Typography>
<Typography variant="Body/Lead text">
<p className={styles.modalText}>
{intl.formatMessage({
id: "When guaranteeing your booking with a credit card, we will hold the booking until 07:00 the day after check-in.",
})}
</p>
</Typography>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p className={styles.modalText}>
{intl.formatMessage({
id: "In case of a no-show, your credit card will be charged for the first night.",
})}
</p>
</Typography>
<Button
typography="Body/Paragraph/mdBold"
variant="Secondary"
size="Small"
onPress={() => setModalOpen(false)}
className={styles.closeButton}
>
{intl.formatMessage({ id: "Close" })}
</Button>
</div>
</Modal>
</div>
<Divider color="subtle" />
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{intl.formatMessage({
id: "I may arrive later than 18:00 and want to guarantee my booking with a credit card.",
})}
</p>
</Typography>
{savedCreditCards?.length && guarantee ? (
<MySavedCards savedCreditCards={savedCreditCards} />
) : null}
{guarantee && (
<>
{savedCreditCards?.length && (
<Typography variant="Title/Overline/sm">
<h4>{intl.formatMessage({ id: "OTHER" })}</h4>
</Typography>
)}
<PaymentOption
name="paymentMethod"
value={PaymentMethodEnum.card}
label={intl.formatMessage({ id: "Credit card" })}
/>
</>
)}
</div>
<div className={styles.checkboxContainer}>
<Checkbox name="smsConfirmation">
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{intl.formatMessage({
id: "I would like to get my booking confirmation via sms",
})}
</p>
</Typography>
</Checkbox>
<div className={styles.checkbox}>
<Checkbox name="termsAndConditions" topAlign>
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{intl.formatMessage(
{
id: "By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.",
},
{
termsAndConditionsLink: (str) => (
<Link
className={styles.link}
variant="underscored"
href={bookingTermsAndConditions[lang]}
target="_blank"
>
{str}
</Link>
),
privacyPolicyLink: (str) => (
<Link
className={styles.link}
variant="underscored"
href={privacyPolicy[lang]}
target="_blank"
>
{str}
</Link>
),
}
)}
</p>
</Typography>
</Checkbox>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,42 @@
import { useIntl } from "react-intl"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { PAYMENT_METHOD_TITLES ,type PaymentMethodEnum } from "@/constants/booking"
import PaymentOption from "../PaymentOption"
import styles from "./mySavedCards.module.css"
import type { CreditCard } from "@/types/user"
interface MySavedCardsProps {
savedCreditCards: CreditCard[] | null
}
export default function MySavedCards({ savedCreditCards }: MySavedCardsProps) {
const intl = useIntl()
return (
<section className={styles.section}>
<Typography variant="Title/Overline/sm">
<h4>{intl.formatMessage({ id: "MY SAVED CARDS" })}</h4>
</Typography>
<div className={styles.paymentOptionContainer}>
{savedCreditCards?.map((savedCreditCard) => (
<PaymentOption
key={savedCreditCard.id}
name="paymentMethod"
value={savedCreditCard.id}
label={
PAYMENT_METHOD_TITLES[
savedCreditCard.cardType as PaymentMethodEnum
]
}
cardNumber={savedCreditCard.truncatedNumber}
/>
))}
</div>
</section>
)
}

View File

@@ -0,0 +1,11 @@
.paymentOptionContainer {
display: flex;
flex-direction: column;
gap: var(--Spacing-x-one-and-half);
}
.section {
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
}

View File

@@ -7,6 +7,7 @@ import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
import {
BOOKING_CONFIRMATION_NUMBER,
BookingErrorCodeEnum,
BookingStatusEnum,
PAYMENT_METHOD_TITLES,
@@ -16,7 +17,10 @@ import {
bookingTermsAndConditions,
privacyPolicy,
} from "@/constants/currentWebHrefs"
import { selectRate } from "@/constants/routes/hotelReservation"
import {
bookingConfirmation,
selectRate,
} from "@/constants/routes/hotelReservation"
import { env } from "@/env/client"
import { trpc } from "@/lib/trpc/client"
import { useEnterDetailsStore } from "@/stores/enter-details"
@@ -36,10 +40,12 @@ import useLang from "@/hooks/useLang"
import { trackPaymentEvent } from "@/utils/tracking"
import { bedTypeMap } from "../../utils"
import ConfirmBooking from "../Confirm"
import PriceChangeDialog from "../PriceChangeDialog"
import GuaranteeDetails from "./GuaranteeDetails"
import { hasFlexibleRate, hasPrepaidRate, isPaymentMethodEnum } from "./helpers"
import MixedRatePaymentBreakdown from "./MixedRatePaymentBreakdown"
import MySavedCards from "./MySavedCards"
import PaymentOption from "./PaymentOption"
import { type PaymentFormData, paymentSchema } from "./schema"
@@ -56,6 +62,9 @@ export const formId = "submit-booking"
export default function PaymentClient({
otherPaymentOptions,
savedCreditCards,
mustBeGuaranteed,
memberMustBeGuaranteed,
isFlexRate,
}: PaymentClientProps) {
const router = useRouter()
const lang = useLang()
@@ -70,6 +79,14 @@ export default function PaymentClient({
totalPrice: state.totalPrice,
}))
const bookingMustBeGuaranteed = rooms.some(
({ room }, idx) =>
(room.guest.join || room.guest.membershipNo) &&
booking.rooms[idx].counterRateCode
)
? memberMustBeGuaranteed
: mustBeGuaranteed
const setIsSubmittingDisabled = useEnterDetailsStore(
(state) => state.actions.setIsSubmittingDisabled
)
@@ -87,7 +104,6 @@ export default function PaymentClient({
const { toDate, fromDate, hotelId } = booking
const mustBeGuaranteed = rooms.every((r) => r.room.mustBeGuaranteed)
const hasPrepaidRates = rooms.some(hasPrepaidRate)
const hasFlexRates = rooms.some(hasFlexibleRate)
const hasMixedRates = hasPrepaidRates && hasFlexRates
@@ -101,6 +117,7 @@ export default function PaymentClient({
: PaymentMethodEnum.card,
smsConfirmation: false,
termsAndConditions: false,
guarantee: false,
},
mode: "all",
reValidateMode: "onChange",
@@ -119,6 +136,11 @@ export default function PaymentClient({
return
}
if (result.reservationStatus == BookingStatusEnum.BookingCompleted) {
const confirmationUrl = `${bookingConfirmation(lang)}?${BOOKING_CONFIRMATION_NUMBER}=${result.id}`
router.push(confirmationUrl)
}
setBookingNumber(result.id)
const priceChange = result.rooms.find(
@@ -212,18 +234,49 @@ export default function PaymentClient({
setIsSubmittingDisabled,
])
const getPaymentMethod = (
isFlexRate: boolean,
paymentMethod: string | null | undefined
): PaymentMethodEnum => {
if (isFlexRate) {
return PaymentMethodEnum.card
}
return paymentMethod && isPaymentMethodEnum(paymentMethod)
? paymentMethod
: PaymentMethodEnum.card
}
const handleSubmit = useCallback(
(data: PaymentFormData) => {
// set payment method to card if saved card is submitted
const paymentMethod = isPaymentMethodEnum(data.paymentMethod)
? data.paymentMethod
: PaymentMethodEnum.card
const paymentMethod = getPaymentMethod(isFlexRate, data.paymentMethod)
const savedCreditCard = savedCreditCards?.find(
(card) => card.id === data.paymentMethod
)
const paymentRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}/${lang}/hotelreservation/payment-callback`
const guarantee = data.guarantee
const useSavedCard = savedCreditCard
? {
card: {
alias: savedCreditCard.alias,
expiryDate: savedCreditCard.expirationDate,
cardType: savedCreditCard.cardType,
},
}
: {}
const shouldUsePayment = !isFlexRate || guarantee
const payment = shouldUsePayment
? {
paymentMethod: paymentMethod,
...useSavedCard,
success: `${paymentRedirectUrl}/success`,
error: `${paymentRedirectUrl}/error`,
cancel: `${paymentRedirectUrl}/cancel`,
}
: undefined
trackPaymentEvent({
event: "paymentAttemptStart",
@@ -284,20 +337,7 @@ export default function PaymentClient({
publicPrice: room.roomRate.publicRate?.localPrice.pricePerStay,
},
})),
payment: {
paymentMethod,
card: savedCreditCard
? {
alias: savedCreditCard.alias,
expiryDate: savedCreditCard.expirationDate,
cardType: savedCreditCard.cardType,
}
: undefined,
success: `${paymentRedirectUrl}/success`,
error: `${paymentRedirectUrl}/error`,
cancel: `${paymentRedirectUrl}/cancel`,
},
payment,
})
},
[
@@ -309,6 +349,7 @@ export default function PaymentClient({
toDate,
rooms,
booking,
isFlexRate,
]
)
@@ -327,6 +368,9 @@ export default function PaymentClient({
const payment = intl.formatMessage({
id: "Payment",
})
const confirm = intl.formatMessage({
id: "Confirm booking",
})
return (
<section
@@ -334,7 +378,11 @@ export default function PaymentClient({
>
<header>
<Title level="h2" as="h4">
{mustBeGuaranteed ? paymentGuarantee : payment}
{bookingMustBeGuaranteed
? paymentGuarantee
: isFlexRate
? confirm
: payment}
</Title>
</header>
<FormProvider {...methods}>
@@ -343,127 +391,115 @@ export default function PaymentClient({
onSubmit={methods.handleSubmit(handleSubmit)}
id={formId}
>
{mustBeGuaranteed ? (
<section className={styles.section}>
<Body>
{intl.formatMessage({
id: "To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.",
})}
</Body>
<GuaranteeDetails />
</section>
) : null}
{isFlexRate && !bookingMustBeGuaranteed ? (
<ConfirmBooking savedCreditCards={savedCreditCards} />
) : (
<>
{bookingMustBeGuaranteed ? (
<section className={styles.section}>
<Body>
{intl.formatMessage({
id: "To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.",
})}
</Body>
<GuaranteeDetails />
</section>
) : null}
{hasMixedRates ? (
<Body>
{intl.formatMessage({
id: "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.",
})}
</Body>
) : null}
{hasMixedRates ? (
<Body>
{intl.formatMessage({
id: "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.",
})}
</Body>
) : null}
{savedCreditCards?.length ? (
<section className={styles.section}>
<Body color="uiTextHighContrast" textTransform="bold">
{intl.formatMessage({ id: "MY SAVED CARDS" })}
</Body>
<div className={styles.paymentOptionContainer}>
{savedCreditCards?.map((savedCreditCard) => (
{savedCreditCards?.length ? (
<section className={styles.section}>
<MySavedCards savedCreditCards={savedCreditCards} />
</section>
) : null}
<section className={styles.section}>
{savedCreditCards?.length ? (
<Body color="uiTextHighContrast" textTransform="bold">
{intl.formatMessage({ id: "OTHER PAYMENT METHODS" })}
</Body>
) : null}
<div className={styles.paymentOptionContainer}>
<PaymentOption
key={savedCreditCard.id}
name="paymentMethod"
value={savedCreditCard.id}
label={
PAYMENT_METHOD_TITLES[
savedCreditCard.cardType as PaymentMethodEnum
]
}
cardNumber={savedCreditCard.truncatedNumber}
hotelId={hotelId}
value={PaymentMethodEnum.card}
label={intl.formatMessage({ id: "Credit card" })}
/>
))}
</div>
</section>
) : null}
{availablePaymentOptions.map((paymentMethod) => (
<PaymentOption
key={paymentMethod}
name="paymentMethod"
value={paymentMethod}
label={
PAYMENT_METHOD_TITLES[
paymentMethod as PaymentMethodEnum
]
}
/>
))}
</div>
{hasMixedRates ? (
<MixedRatePaymentBreakdown
rooms={rooms}
currency={totalPrice.local.currency}
/>
) : null}
</section>
<section className={styles.section}>
{savedCreditCards?.length ? (
<Body color="uiTextHighContrast" textTransform="bold">
{intl.formatMessage({ id: "OTHER PAYMENT METHODS" })}
</Body>
) : null}
<div className={styles.paymentOptionContainer}>
<PaymentOption
name="paymentMethod"
value={PaymentMethodEnum.card}
label={intl.formatMessage({ id: "Credit card" })}
hotelId={hotelId}
/>
{availablePaymentOptions.map((paymentMethod) => (
<PaymentOption
key={paymentMethod}
name="paymentMethod"
value={paymentMethod}
label={
PAYMENT_METHOD_TITLES[paymentMethod as PaymentMethodEnum]
}
hotelId={hotelId}
/>
))}
</div>
{hasMixedRates ? (
<MixedRatePaymentBreakdown
rooms={rooms}
currency={totalPrice.local.currency}
/>
) : null}
</section>
<section className={styles.section}>
<Caption>
{intl.formatMessage(
{
id: "By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.",
},
{
termsAndConditionsLink: (str) => (
<Link
className={styles.link}
variant="underscored"
href={bookingTermsAndConditions[lang]}
target="_blank"
>
{str}
</Link>
),
privacyPolicyLink: (str) => (
<Link
className={styles.link}
variant="underscored"
href={privacyPolicy[lang]}
target="_blank"
>
{str}
</Link>
),
}
)}
</Caption>
<Checkbox name="termsAndConditions">
<Caption>
{intl.formatMessage({
id: "I accept the terms and conditions",
})}
</Caption>
</Checkbox>
<Checkbox name="smsConfirmation">
<Caption>
{intl.formatMessage({
id: "I would like to get my booking confirmation via sms",
})}
</Caption>
</Checkbox>
</section>
<section className={styles.section}>
<Caption>
{intl.formatMessage(
{
id: "By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.",
},
{
termsAndConditionsLink: (str) => (
<Link
className={styles.link}
variant="underscored"
href={bookingTermsAndConditions[lang]}
target="_blank"
>
{str}
</Link>
),
privacyPolicyLink: (str) => (
<Link
className={styles.link}
variant="underscored"
href={privacyPolicy[lang]}
target="_blank"
>
{str}
</Link>
),
}
)}
</Caption>
<Checkbox name="termsAndConditions">
<Caption>
{intl.formatMessage({
id: "I accept the terms and conditions",
})}
</Caption>
</Checkbox>
<Checkbox name="smsConfirmation">
<Caption>
{intl.formatMessage({
id: "I would like to get my booking confirmation via sms",
})}
</Caption>
</Checkbox>
</section>
</>
)}
<div className={styles.submitButton}>
<Button
intent="primary"

View File

@@ -7,5 +7,4 @@ export interface PaymentOptionProps {
cardNumber?: string
registerOptions?: RegisterOptions
onChange?: () => void
hotelId: string
}

View File

@@ -1,4 +1,4 @@
import { CancellationRuleEnum, PaymentMethodEnum } from "@/constants/booking"
import { PaymentMethodEnum } from "@/constants/booking"
import type { RoomState } from "@/types/stores/enter-details"
@@ -7,11 +7,11 @@ export function isPaymentMethodEnum(value: string): value is PaymentMethodEnum {
}
export function hasFlexibleRate({ room }: RoomState): boolean {
return room.cancellationRule === CancellationRuleEnum.CancellableBefore6PM
return room.isFlexRate
}
export function hasPrepaidRate({ room }: RoomState): boolean {
return room.cancellationRule !== CancellationRuleEnum.CancellableBefore6PM
return !room.isFlexRate
}
export function calculateTotalRoomPrice({ room }: RoomState) {

View File

@@ -6,7 +6,10 @@ import type { PaymentProps } from "@/types/components/hotelReservation/enterDeta
export default async function Payment({
otherPaymentOptions,
mustBeGuaranteed,
memberMustBeGuaranteed,
supportedCards,
isFlexRate,
}: PaymentProps) {
const savedCreditCards = await getSavedPaymentCardsSafely({
supportedCards,
@@ -16,6 +19,9 @@ export default async function Payment({
<PaymentClient
otherPaymentOptions={otherPaymentOptions}
savedCreditCards={savedCreditCards}
mustBeGuaranteed={mustBeGuaranteed}
memberMustBeGuaranteed={memberMustBeGuaranteed}
isFlexRate={isFlexRate}
/>
)
}

View File

@@ -1,11 +1,12 @@
import { z } from "zod"
export const paymentSchema = z.object({
paymentMethod: z.string(),
paymentMethod: z.string().nullish(),
smsConfirmation: z.boolean(),
termsAndConditions: z.boolean().refine((value) => value === true, {
message: "You must accept the terms and conditions",
}),
guarantee: z.boolean(),
})
export interface PaymentFormData extends z.output<typeof paymentSchema> {}

View File

@@ -66,6 +66,7 @@ const rooms: RoomState[] = [
roomTypeCode: "QS",
isAvailable: true,
mustBeGuaranteed: false,
isFlexRate: false,
},
steps: {
[StepEnum.selectBed]: {
@@ -94,7 +95,6 @@ const rooms: RoomState[] = [
bedTypes: [],
breakfast: undefined,
breakfastIncluded: false,
cancellationRule: "",
cancellationText: "Non-refundable",
childrenInRoom: [],
guest: guestDetailsMember,
@@ -106,6 +106,7 @@ const rooms: RoomState[] = [
roomTypeCode: "QS",
isAvailable: true,
mustBeGuaranteed: false,
isFlexRate: false,
},
steps: {
[StepEnum.selectBed]: {

View File

@@ -60,7 +60,11 @@ export default function ConfirmationStep() {
<Body textTransform="bold">{"MasterCard"}</Body>
<Body color="uiTextMediumContrast">{"**** 1234"}</Body>
</div>
<Checkbox name="termsAndConditions" registerOptions={{ required: true }}>
<Checkbox
name="termsAndConditions"
registerOptions={{ required: true }}
topAlign
>
<Caption>
{intl.formatMessage(
{

View File

@@ -0,0 +1,57 @@
.card {
display: flex;
align-items: center;
gap: var(--Spacing-x1);
padding: var(--Spacing-x2) var(--Spacing-x-one-and-half);
border-radius: var(--Corner-radius-Medium);
background-color: var(--Base-Surface-Subtle-Normal);
}
.addCreditCard {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
.guaranteeCost {
display: flex;
justify-content: flex-end;
padding: var(--Spacing-x2);
align-items: flex-end;
gap: var(--Spacing-x3);
border-radius: var(--Corner-radius-Medium);
background-color: var(--Base-Surface-Subtle-Normal);
}
.guaranteeCostText {
display: flex;
flex-direction: column;
}
.termsAndConditions {
display: flex;
gap: var(--Spacing-x2);
}
.section {
display: flex;
flex-direction: column;
gap: var(--Spacing-x2);
}
.paymentOptionContainer {
display: flex;
flex-direction: column;
gap: var(--Spacing-x-one-and-half);
}
.loading {
display: flex;
align-items: center;
justify-content: center;
width: 640px;
max-width: 100%;
height: 640px;
max-height: 100%;
}

View File

@@ -0,0 +1,252 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useRouter } from "next/navigation"
import { useCallback, useEffect, useState } from "react"
import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"
import { BookingStatusEnum, PaymentMethodEnum } from "@/constants/booking"
import {
bookingTermsAndConditions,
privacyPolicy,
} from "@/constants/currentWebHrefs"
import { guaranteeCallback } from "@/constants/routes/hotelReservation"
import { env } from "@/env/client"
import { trpc } from "@/lib/trpc/client"
import LoadingSpinner from "@/components/LoadingSpinner"
import { ModalContentWithActions } from "@/components/Modal/ModalContentWithActions"
import Divider from "@/components/TempDesignSystem/Divider"
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import { toast } from "@/components/TempDesignSystem/Toasts"
import { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus"
import useLang from "@/hooks/useLang"
import { formatPrice } from "@/utils/numberFormatting"
import MySavedCards from "../../EnterDetails/Payment/MySavedCards"
import PaymentOption from "../../EnterDetails/Payment/PaymentOption"
import { type GuaranteeFormData, paymentSchema } from "./schema"
import styles from "./guaranteeLateArrival.module.css"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
import type { CreditCard } from "@/types/user"
const maxRetries = 15
const retryInterval = 2000
export interface GuaranteeLateArrivalProps {
booking: BookingConfirmation["booking"]
handleCloseModal: () => void
handleBackToManageStay: () => void
savedCreditCards: CreditCard[] | null
refId: string
}
export default function GuaranteeLateArrival({
booking,
handleCloseModal,
handleBackToManageStay,
savedCreditCards,
refId,
}: GuaranteeLateArrivalProps) {
const intl = useIntl()
const lang = useLang()
const router = useRouter()
const methods = useForm<GuaranteeFormData>({
defaultValues: {
paymentMethod: savedCreditCards?.length
? savedCreditCards[0].id
: PaymentMethodEnum.card,
termsAndConditions: false,
},
mode: "all",
reValidateMode: "onChange",
resolver: zodResolver(paymentSchema),
})
const [isPollingForBookingStatus, setIsPollingForBookingStatus] =
useState(false)
const handlePaymentError = useCallback(() => {
toast.error(
intl.formatMessage({
id: "We had an issue guaranteeing your booking. Please try again.",
})
)
}, [intl])
const guaranteeBooking = trpc.booking.guarantee.useMutation({
onSuccess: (result) => {
if (result) {
setIsPollingForBookingStatus(true)
} else {
handlePaymentError()
}
},
onError: () => {
toast.error(
intl.formatMessage({
id: "Something went wrong!",
})
)
},
})
const bookingStatus = useHandleBookingStatus({
confirmationNumber: booking.confirmationNumber,
expectedStatus: BookingStatusEnum.BookingCompleted,
maxRetries,
retryInterval,
enabled: isPollingForBookingStatus,
})
useEffect(() => {
if (bookingStatus?.data?.paymentUrl) {
router.push(bookingStatus.data.paymentUrl)
} else if (bookingStatus.isTimeout) {
handlePaymentError()
}
}, [bookingStatus, router, intl, handlePaymentError])
if (
guaranteeBooking.isPending ||
(isPollingForBookingStatus &&
!bookingStatus.data?.paymentUrl &&
!bookingStatus.isTimeout)
) {
return (
<div className={styles.loading}>
<LoadingSpinner />
</div>
)
}
const handleGuaranteeLateArrival = (data: GuaranteeFormData) => {
const savedCreditCard = savedCreditCards?.find(
(card) => card.id === data.paymentMethod
)
const guaranteeRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}${guaranteeCallback(lang)}`
if (booking.confirmationNumber) {
const card = savedCreditCard
? {
alias: savedCreditCard.alias,
expiryDate: savedCreditCard.expirationDate,
cardType: savedCreditCard.cardType,
}
: undefined
guaranteeBooking.mutate({
confirmationNumber: booking.confirmationNumber,
language: lang,
...(card !== undefined && { card }),
success: `${guaranteeRedirectUrl}/success/${encodeURIComponent(refId)}`,
error: `${guaranteeRedirectUrl}/error/${encodeURIComponent(refId)}`,
cancel: `${guaranteeRedirectUrl}/cancel/${encodeURIComponent(refId)}`,
})
} else {
toast.error(
intl.formatMessage({
id: "Confirmation number is missing!",
})
)
}
}
return (
<FormProvider {...methods}>
<ModalContentWithActions
title={intl.formatMessage({ id: "Guarantee late arrival" })}
onClose={handleCloseModal}
content={
<>
<Caption>
{intl.formatMessage({
id: "Planning to arrive after 18.00? Secure your room by guaranteeing it with a credit card. Without the guarantee and in case of no-show, the room might be reallocated after 18:00.",
})}
</Caption>
<Caption type="bold">
{intl.formatMessage({
id: "In case of no-show you will be charged for the first night.",
})}
</Caption>
{savedCreditCards?.length ? (
<>
<MySavedCards savedCreditCards={savedCreditCards} />
<Body color="uiTextHighContrast" textTransform="bold">
{intl.formatMessage({ id: "OTHER" })}
</Body>
</>
) : null}
<PaymentOption
name="paymentMethod"
value={PaymentMethodEnum.card}
label={intl.formatMessage({ id: "Credit card" })}
/>
<div className={styles.termsAndConditions}>
<Checkbox topAlign name={"termsAndConditions"}>
<Caption>
{intl.formatMessage(
{
id: "By guaranteeing with any of the payment methods available, I accept the terms for this stay and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandics Privacy Policy</privacyPolicyLink>. I accept Scandic requiring a valid credit card during my visit in case anything is left unpaid.",
},
{
termsAndConditionsLink: (str) => (
<Link
className={styles.link}
variant="underscored"
href={bookingTermsAndConditions[lang]}
target="_blank"
>
{str}
</Link>
),
privacyPolicyLink: (str) => (
<Link
className={styles.link}
variant="underscored"
href={privacyPolicy[lang]}
target="_blank"
>
{str}
</Link>
),
}
)}
</Caption>
</Checkbox>
</div>
<div className={styles.guaranteeCost}>
<div className={styles.guaranteeCostText}>
<Caption type="bold">
{intl.formatMessage({ id: "Guarantee cost" })}
</Caption>
<Caption color="uiTextHighContrast">
{intl.formatMessage({
id: "Your card will only be used for authorisation",
})}
</Caption>
</div>
<Divider variant="vertical" color="subtle" />
<Body textTransform="bold">
{formatPrice(intl, 0, booking.currencyCode)}
</Body>
</div>
</>
}
primaryAction={{
label: intl.formatMessage({ id: "Guarantee" }),
onClick: methods.handleSubmit(handleGuaranteeLateArrival),
intent: "primary",
}}
secondaryAction={{
label: intl.formatMessage({ id: "Back" }),
onClick: handleBackToManageStay,
intent: "text",
}}
/>
</FormProvider>
)
}

View File

@@ -0,0 +1,10 @@
import { z } from "zod"
export const paymentSchema = z.object({
paymentMethod: z.string().nullable(),
termsAndConditions: z.boolean().refine((value) => value === true, {
message: "You must accept the terms and conditions",
}),
})
export interface GuaranteeFormData extends z.output<typeof paymentSchema> {}

View File

@@ -31,14 +31,18 @@ interface ActionPanelProps {
booking: BookingConfirmation["booking"]
hotel: Hotel
showCancelStayButton: boolean
showGuaranteeButton: boolean
onCancelClick: () => void
onGuaranteeClick: () => void
}
export default function ActionPanel({
booking,
hotel,
showCancelStayButton,
showGuaranteeButton,
onCancelClick,
onGuaranteeClick,
}: ActionPanelProps) {
const intl = useIntl()
const lang = useLang()
@@ -74,15 +78,17 @@ export default function ActionPanel({
{intl.formatMessage({ id: "Modify dates" })}
<CalendarIcon width={24} height={24} color="burgundy" />
</Button>
<Button
variant="icon"
onClick={() => {}}
intent="text"
className={styles.button}
>
{intl.formatMessage({ id: "Guarantee late arrival" })}
<CreditCard width={24} height={24} color="burgundy" />
</Button>
{showGuaranteeButton && (
<Button
variant="icon"
onClick={onGuaranteeClick}
intent="text"
className={styles.button}
>
{intl.formatMessage({ id: "Guarantee late arrival" })}
<CreditCard width={24} height={24} color="burgundy" />
</Button>
)}
<AddToCalendar
checkInDate={booking.checkInDate}
event={event}

View File

@@ -10,18 +10,22 @@ import Modal from "@/components/Modal"
import Button from "@/components/TempDesignSystem/Button"
import CancelStay from "../CancelStay"
import GuaranteeLateArrival from "../GuaranteeLateArrival"
import ActionPanel from "./ActionPanel"
import type { Hotel } from "@/types/hotel"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
import type { CreditCard } from "@/types/user"
type ActiveView = "actionPanel" | "cancelStay"
type ActiveView = "actionPanel" | "cancelStay" | "guaranteeLateArrival"
interface ManageStayProps {
booking: BookingConfirmation["booking"]
hotel: Hotel
setBookingStatus: (status: BookingStatusEnum) => void
bookingStatus: string | null
savedCreditCards: CreditCard[] | null
refId: string
}
export default function ManageStay({
@@ -29,6 +33,8 @@ export default function ManageStay({
hotel,
setBookingStatus,
bookingStatus,
savedCreditCards,
refId,
}: ManageStayProps) {
const [isOpen, setIsOpen] = useState(false)
@@ -39,6 +45,9 @@ export default function ManageStay({
const showCancelStayButton =
bookingStatus !== BookingStatusEnum.Cancelled && booking.isCancelable
const showGuaranteeButton =
bookingStatus !== BookingStatusEnum.Cancelled && !booking.guaranteeInfo
function handleClose() {
setIsOpen(false)
setActiveView("actionPanel")
@@ -61,13 +70,25 @@ export default function ManageStay({
handleBackToManageStay={handleBack}
/>
)
case "guaranteeLateArrival":
return (
<GuaranteeLateArrival
booking={booking}
handleCloseModal={handleClose}
handleBackToManageStay={handleBack}
savedCreditCards={savedCreditCards}
refId={refId}
/>
)
default:
return (
<ActionPanel
booking={booking}
hotel={hotel}
onCancelClick={() => setActiveView("cancelStay")}
onGuaranteeClick={() => setActiveView("guaranteeLateArrival")}
showCancelStayButton={showCancelStayButton}
showGuaranteeButton={showGuaranteeButton}
/>
)
}

View File

@@ -14,6 +14,7 @@ import IconChip from "@/components/TempDesignSystem/IconChip"
import Link from "@/components/TempDesignSystem/Link"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { useGuaranteePaymentFailedToast } from "@/hooks/booking/useGuaranteePaymentFailedToast"
import useLang from "@/hooks/useLang"
import { formatPrice } from "@/utils/numberFormatting"
@@ -24,13 +25,21 @@ import styles from "./referenceCard.module.css"
import type { Hotel } from "@/types/hotel"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
import type { CreditCard } from "@/types/user"
interface ReferenceCardProps {
booking: BookingConfirmation["booking"]
hotel: Hotel
savedCreditCards: CreditCard[] | null
refId: string
}
export function ReferenceCard({ booking, hotel }: ReferenceCardProps) {
export function ReferenceCard({
booking,
hotel,
savedCreditCards,
refId,
}: ReferenceCardProps) {
const [bookingStatus, setBookingStatus] = useState(booking.reservationStatus)
const intl = useIntl()
const lang = useLang()
@@ -40,6 +49,7 @@ export function ReferenceCard({ booking, hotel }: ReferenceCardProps) {
const toDate = dt(booking.checkOutDate).locale(lang)
const isCancelled = bookingStatus === BookingStatusEnum.Cancelled
useGuaranteePaymentFailedToast()
const directionsUrl = `https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}`
@@ -173,6 +183,8 @@ export function ReferenceCard({ booking, hotel }: ReferenceCardProps) {
hotel={hotel}
setBookingStatus={setBookingStatus}
bookingStatus={bookingStatus}
savedCreditCards={savedCreditCards}
refId={refId}
/>
<Button fullWidth intent="secondary" asChild>
<Link href={directionsUrl} target="_blank">

View File

@@ -9,6 +9,7 @@ import {
getAncillaryPackages,
getBookingConfirmation,
getProfileSafely,
getSavedPaymentCardsSafely,
} from "@/lib/trpc/memoizedRequests"
import { decrypt } from "@/server/routers/utils/encryption"
@@ -64,6 +65,10 @@ export async function MyStay({ refId }: { refId: string }) {
hotelId: hotel.operaId,
toDate: dt(booking.checkOutDate).format("YYYY-MM-DD"),
})
const supportedCards = hotel.merchantInformationData.cards
const savedCreditCards = await getSavedPaymentCardsSafely({
supportedCards,
})
return (
<main className={styles.main}>
@@ -84,7 +89,12 @@ export async function MyStay({ refId }: { refId: string }) {
<div className={styles.content}>
<div className={styles.headerContainer}>
<Header hotel={hotel} />
<ReferenceCard booking={booking} hotel={hotel} />
<ReferenceCard
booking={booking}
hotel={hotel}
savedCreditCards={savedCreditCards}
refId={refId}
/>
</div>
{booking.showAncillaries && (
<Ancillaries

View File

@@ -35,6 +35,10 @@
forced-color-adjust: none;
}
.topAlign {
align-items: flex-start;
}
.error {
align-items: center;
color: var(--Scandic-Red-60);

View File

@@ -17,6 +17,7 @@ export default function Checkbox({
children,
registerOptions,
hideError,
topAlign = false,
}: React.PropsWithChildren<CheckboxProps>) {
const { control } = useFormContext()
const { field, fieldState } = useController({
@@ -36,7 +37,9 @@ export default function Checkbox({
>
{({ isSelected }) => (
<>
<span className={styles.checkboxContainer}>
<span
className={`${styles.checkboxContainer} ${topAlign ? styles.topAlign : ""}`}
>
<span
className={styles.checkbox}
tabIndex={registerOptions?.disabled ? undefined : 0}

View File

@@ -57,3 +57,10 @@ export function alternativeHotels(lang) {
export function alternativeHotelsMap(lang) {
return `${hotelreservation(lang)}/alternative-hotels/map`
}
/**
* @param {Lang} lang
*/
export function guaranteeCallback(lang) {
return `${hotelreservation(lang)}/gla-payment-callback`
}

View File

@@ -0,0 +1,54 @@
"use client"
import { usePathname, useRouter, useSearchParams } from "next/navigation"
import { useCallback, useEffect } from "react"
import { useIntl } from "react-intl"
import { BookingErrorCodeEnum } from "@/constants/booking"
import { toast } from "@/components/TempDesignSystem/Toasts"
export function useGuaranteePaymentFailedToast() {
const intl = useIntl()
const searchParams = useSearchParams()
const pathname = usePathname()
const router = useRouter()
const getErrorMessage = useCallback(
(errorCode: string | null) => {
switch (errorCode) {
case BookingErrorCodeEnum.TransactionCancelled:
return intl.formatMessage({
id: "You have cancelled to process to guarantee your booking.",
})
default:
return intl.formatMessage({
id: "We had an issue guaranteeing your booking. Please try again.",
})
}
},
[intl]
)
const errorCode = searchParams.get("errorCode")
const errorMessage = getErrorMessage(errorCode)
useEffect(() => {
if (!errorCode) return
// setTimeout is needed to show toasts on page load: https://sonner.emilkowal.ski/toast#render-toast-on-page-load
setTimeout(() => {
const toastType =
errorCode === BookingErrorCodeEnum.TransactionCancelled
? "warning"
: "error"
toast[toastType](errorMessage)
})
const queryParams = new URLSearchParams(searchParams.toString())
queryParams.delete("errorCode")
router.push(`${pathname}?${queryParams.toString()}`)
}, [searchParams, pathname, errorCode, errorMessage, router])
}

View File

@@ -101,6 +101,7 @@
"Booking Code filter": "Booking Code filter",
"Booking code": "Bookingkode",
"Booking confirmation": "Booking bekræftelse",
"Booking guaranteed.": "Bestilling garanteret.",
"Booking number": "Bookingnummer",
"Booking number is required": "Bookingnummer er påkrævet",
"Booking number {value}": "Booking number {value}",
@@ -120,6 +121,7 @@
"Bus terminal": "Busstation",
"Business": "Forretning",
"By accepting the <termsAndConditionsLink>Terms and Conditions for Scandic Friends</termsAndConditionsLink> I understand that my personal data will be processed in accordance with <privacyPolicy>Scandic's Privacy Policy</privacyPolicy>.": "Ved at acceptere <termsAndConditionsLink>vilkårene og betingelserne for Scandic Friends</termsAndConditionsLink>, forstår jeg, at mine personlige oplysninger vil blive behandlet i overensstemmelse med <privacyPolicy>Scandics privatlivspolitik</privacyPolicy>.",
"By guaranteeing with any of the payment methods available, I accept the terms for this stay and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandics Privacy Policy</privacyPolicyLink>. I accept Scandic requiring a valid credit card during my visit in case anything is left unpaid.": "Ved at garantere med en af de tilgængelige betalingsmetoder, accepterer jeg vilkårene for dette ophold og de generelle <termsAndConditionsLink>Vilkår og betingelser</termsAndConditionsLink>, og forstår, at Scandic vil behandle min personlige data i forbindelse med dette ophold i henhold til <privacyPolicyLink>Scandics Privatlivspolitik</privacyPolicyLink>. Jeg accepterer, at Scandic kræver et gyldigt kreditkort under mit besøg i tilfælde af, at noget er tilbagebetalt.",
"By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.": "By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.",
"By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.": "Ved at betale med en af de tilgængelige betalingsmetoder, accepterer jeg vilkårene for denne booking og de generelle <termsAndConditionsLink>Vilkår og betingelser</termsAndConditionsLink>, og forstår, at Scandic vil behandle min personlige data i forbindelse med denne booking i henhold til <privacyPolicyLink>Scandics Privatlivspolitik</privacyPolicyLink>. Jeg accepterer, at Scandic kræver et gyldigt kreditkort under min besøg i tilfælde af, at noget er tilbagebetalt.",
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "Ved at tilmelde dig accepterer du Scandic Friends <termsAndConditionsLink>vilkår og betingelser</termsAndConditionsLink>. Dit medlemskab er gyldigt indtil videre, og du kan til enhver tid opsige dit medlemskab ved at sende en e-mail til Scandics kundeservice",
@@ -173,6 +175,7 @@
"Complete booking & go to payment": "Udfyld booking & gå til betaling",
"Complete the booking": "Fuldfør bookingen",
"Confirm": "Bekræft",
"Confirm booking": "Bekræft reservation",
"Confirm cancellation": "Bekræft annullerering",
"Contact information": "Kontaktoplysninger",
"Contact our memberservice": "Contact our memberservice",
@@ -294,8 +297,12 @@
"Go to My Benefits": "Gå til 'Mine fordele'",
"Go to profile": "Go to profile",
"Great minds meet here": "Great minds meet here",
"Guarantee": "Garanti",
"Guarantee booking with credit card": "Garantere booking med kreditkort",
"Guarantee cost": "Garantiomkostninger",
"Guarantee for late arrival": "Garanti for sen ankomst",
"Guarantee late arrival": "Garanter sen ankomst",
"Guarantee room for late arrival": "Garanti plads ved sen ankomst",
"Guest details updated": "Gæstdetaljer opdateret",
"Guest information": "Gæsteinformation",
"Guests": "Gæster",
@@ -324,11 +331,13 @@
"Hotels with {filter} in {location}": "Hoteller med {filter} i {location}",
"Hours": "Tider",
"How do you want to sleep?": "Hvordan vil du sove?",
"How does it work": "Hvordan virker det",
"How it works": "Hvordan det virker",
"How to use": "Sådan bruges",
"Hurry up and use them before they expire!": "Skynd dig og brug dem, før de udløber!",
"I accept": "Jeg accepterer",
"I accept the terms and conditions": "Jeg accepterer vilkårene",
"I may arrive later than 18:00 and want to guarantee my booking with a credit card.": "Jeg ankommer muligvis senere end kl. 18.00 og vil gerne garantere min reservation med et kreditkort.",
"I promise to join Scandic Friends before checking in": "Jeg lover at tilmelde mig Scandic Friends, før jeg tjekker ind",
"I would like to get my booking confirmation via sms": "Jeg vil gerne få min booking bekræftelse via SMS",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "Hvis ikke, så gå tilbage og gør det, før du lukker dette. Når du lukker dette, vil din fordel blive ugyldig og fjernet fra Mine fordele.",
@@ -336,6 +345,8 @@
"If you close this your benefit will be removed": "Hvis du lukker dette, vil din fordel blive fjernet",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "Hvis du bestiller et kampagnetilbud eller en firmaaftalepris, skal du bruge en bookingkode. Brug ikke specialtegn som (.) (,) (-) (:).Gem din bookingkode til næste gang du besøger siden ved at klikke “Husk bookingkode”. Vælg ikke dette, hvis du bruger en offentlig computer for at undgå uautoriseret anvendelse af din bookingkode.",
"In adults bed": "i de voksnes seng",
"In case of a no-show, your credit card will be charged for the first night.": "I tilfælde af udeblivelse vil dit kreditkort blive debiteret for den første nat.",
"In case of no-show you will be charged for the first night.": "I tilfælde af udeblivelse vil du blive opkrævet for den første nat.",
"In crib": "i tremmeseng",
"In extra bed": "i ekstra seng",
"In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.": "In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.",
@@ -525,6 +536,7 @@
"Phone": "Telefon",
"Phone is required": "Telefonnummer er påkrævet",
"Phone number": "Telefonnummer",
"Planning to arrive after 18.00? Secure your room by guaranteeing it with a credit card. Without the guarantee and in case of no-show, the room might be reallocated after 18:00.": "Planlægger du at ankomme efter kl. 18.00? Sikre dit værelse ved at garantere det med et kreditkort. Uden garantien og i tilfælde af udeblivelse kan værelset blive tildelt efter kl. 18.00.",
"Please contact <link>customer service</link>.": "Kontakt venligst <link>kundeservice</link>.",
"Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer",
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
@@ -735,6 +747,7 @@
"We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.": "We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.",
"We couldn't find a matching location for your search.": "Vi kunne ikke finde en matchende lokation til din søgning.",
"We found no available rooms using this booking code ({bookingCode}). See available rates below.": "Vi fandt desværre ingen ledige værelser. Vi fandt ingen ledige værelser med denne bookingkode ({bookingCode}). Se ledige værelser nedenfor.",
"We had an issue guaranteeing your booking. Please try again.": "Vi havde et problem med at garantere din reservation. Prøv venligst igen.",
"We had an issue processing your booking. Please try again. No charges have been made.": "Vi havde et problem med at behandle din booking. Prøv venligst igen. Ingen gebyrer er blevet opkrævet.",
"We have a special gift waiting for you!": "Vi har en speciel gave, der venter på dig!",
"We look forward to your visit!": "Vi ser frem til dit besøg!",
@@ -753,6 +766,7 @@
"Welcome to": "Velkommen til",
"What you have to do to guarantee booking:": "Hvad du skal gøre for at garantere booking:",
"When": "Hvornår",
"When guaranteeing your booking with a credit card, we will hold the booking until 07:00 the day after check-in.": "Når vi garanterer din reservation med et kreditkort, tilbageholder vi reservationen indtil kl. 07.00 dagen efter indtjekning.",
"When guaranteeing your booking, we will hold the booking until 07:00 until the day after check-in. This will provide you as a guest with added flexibility for check-in times.": "Når du garanterer din booking, vil vi holde bookingen indtil 07:00 til dagen efter check-in. Dette vil give dig som gæst tilføjet fleksibilitet til check-in-tider.",
"Where should you go next?": "Find inspiration til dit næste ophold",
"Where to?": "Hvor?",
@@ -784,6 +798,7 @@
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Din booking er bekræftet, men vi kunne ikke verificere dit medlemskab. Hvis du har booket med et medlemstilbud, skal du enten vise dit eksisterende medlemskab ved check-in, blive medlem eller betale prisdifferencen ved check-in. Tilmelding er foretrukket online før opholdet.",
"Your card was successfully removed!": "Dit kort blev fjernet!",
"Your card was successfully saved!": "Dit kort blev gemt!",
"Your card will only be used for authorisation": "Dit kort vil kun blive brugt til autorisation",
"Your current level": "Dit nuværende niveau",
"Your details": "Dine oplysninger",
"Your hotel": "Your hotel",
@@ -791,6 +806,7 @@
"Your member tier": "Dit medlemskabsniveau",
"Your points to spend": "Dine brugbare point",
"Your room": "Dit værelse",
"Your room will remain available for check-in even after 18:00.": "Dit værelse vil forblive tilgængeligt til check-in selv efter kl. 18.00.",
"Your selected bed type will be provided based on availability": "Din valgte sengtype vil blive stillet til rådighed baseret på tilgængelighed",
"Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Dit ophold blev annulleret. Annullereringspris: 0 {currency}. Vi beklager, at planerne ikke fungerede ud",
"Your stay was cancelled. Cancellation cost: 0 {currency}. Were sorry to see that the plans didnt work out": "Dit ophold blev annulleret. Annullereringspris: 0 {currency}. Vi beklager, at planerne ikke fungerede ud",

View File

@@ -102,6 +102,7 @@
"Booking Code filter": "Booking Code filter",
"Booking code": "Buchungscode",
"Booking confirmation": "Buchungsbestätigung",
"Booking guaranteed.": "Buchung garantiert.",
"Booking number": "Buchungsnummer",
"Booking number is required": "Buchungsnummer ist erforderlich",
"Booking number {value}": "Booking number {value}",
@@ -121,6 +122,7 @@
"Bus terminal": "Bus terminal",
"Business": "Geschäft",
"By accepting the <termsAndConditionsLink>Terms and Conditions for Scandic Friends</termsAndConditionsLink> I understand that my personal data will be processed in accordance with <privacyPolicy>Scandic's Privacy Policy</privacyPolicy>.": "Mit der Annahme der <termsAndConditionsLink>Allgemeinen Geschäftsbedingungen für Scandic Friends</termsAndConditionsLink> erkläre ich mich damit einverstanden, dass meine persönlichen Daten in Übereinstimmung mit der <privacyPolicy>Datenschutzrichtlinie von Scandic verarbeitet werden</privacyPolicy>.",
"By guaranteeing with any of the payment methods available, I accept the terms for this stay and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandics Privacy Policy</privacyPolicyLink>. I accept Scandic requiring a valid credit card during my visit in case anything is left unpaid.": "Mit der Garantie durch eine der verfügbaren Zahlungsmethoden akzeptiere ich die Bedingungen für diesen Aufenthalt und die allgemeinen <termsAndConditionsLink>Geschäftsbedingungen</termsAndConditionsLink> und verstehe, dass Scandic meine personenbezogenen Daten für diesen Aufenthalt gemäß der <privacyPolicyLink>Scandic-Datenschutzrichtlinie</privacyPolicyLink> verarbeitet. Ich akzeptiere, dass Scandic während meines Besuchs eine gültige Kreditkarte benötigt, falls etwas unbezahlt bleibt.",
"By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.": "By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.",
"By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.": "Mit der Zahlung über eine der verfügbaren Zahlungsmethoden akzeptiere ich die Buchungsbedingungen und die allgemeinen <termsAndConditionsLink>Geschäftsbedingungen</termsAndConditionsLink> und verstehe, dass Scandic meine personenbezogenen Daten im Zusammenhang mit dieser Buchung gemäß der <privacyPolicyLink>Scandic Datenschutzrichtlinie</privacyPolicyLink> verarbeitet. Ich akzeptiere, dass Scandic während meines Aufenthalts eine gültige Kreditkarte für eventuelle Rückerstattungen benötigt.",
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "Mit Ihrer Anmeldung akzeptieren Sie die <termsAndConditionsLink>Allgemeinen Geschäftsbedingungen</termsAndConditionsLink> von Scandic Friends. Ihre Mitgliedschaft ist bis auf Weiteres gültig und Sie können sie jederzeit kündigen, indem Sie eine E-Mail an den Kundenservice von Scandic senden.",
@@ -174,6 +176,7 @@
"Complete booking & go to payment": "Buchung abschließen & zur Bezahlung gehen",
"Complete the booking": "Buchung abschließen",
"Confirm": "Bestätigen",
"Confirm booking": "Buchung bestätigen",
"Confirm cancellation": "Stornierung bestätigen",
"Contact information": "Kontaktinformationen",
"Contact our memberservice": "Contact our memberservice",
@@ -295,8 +298,12 @@
"Go to My Benefits": "Gehen Sie zu „Meine Vorteile“",
"Go to profile": "Go to profile",
"Great minds meet here": "Great minds meet here",
"Guarantee": "Garantie",
"Guarantee booking with credit card": "Buchung mit Kreditkarte garantieren",
"Guarantee cost": "Garantiekosten",
"Guarantee for late arrival": "Garantie bei verspäteter Anreise",
"Guarantee late arrival": "Garantere sen ankomst",
"Guarantee room for late arrival": "Zimmergarantie bei Spätanreise",
"Guest details updated": "Gästedaten aktualisiert",
"Guest information": "Informationen für Gäste",
"Guests": "Gäste",
@@ -325,11 +332,13 @@
"Hotels with {filter} in {location}": "Hotels mit {filter} in {location}",
"Hours": "Zeiten",
"How do you want to sleep?": "Wie möchtest du schlafen?",
"How does it work": "Wie funktioniert es",
"How it works": "Wie es funktioniert",
"How to use": "Wie zu verwenden",
"Hurry up and use them before they expire!": "Beeilen Sie sich und nutzen Sie sie, bevor sie ablaufen!",
"I accept": "Ich akzeptiere",
"I accept the terms and conditions": "Ich akzeptiere die Geschäftsbedingungen",
"I may arrive later than 18:00 and want to guarantee my booking with a credit card.": "Ich komme voraussichtlich nach 18:00 Uhr an und möchte meine Buchung mit einer Kreditkarte garantieren.",
"I promise to join Scandic Friends before checking in": "Ich verspreche, Scandic Friends beizutreten, bevor ich einchecke",
"I would like to get my booking confirmation via sms": "Ich möchte meine Buchungsbestätigung per SMS erhalten",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "Wenn nicht, gehen Sie bitte zurück und tun Sie dies, bevor Sie dies schließen. Sobald Sie dies schließen, verfällt Ihr Vorteil und wird aus „Meine Vorteile“ entfernt.",
@@ -337,6 +346,8 @@
"If you close this your benefit will be removed": "Wenn Sie dies schließen, wird Ihr Vorteil entfernt",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "Um ein Aktionsangebot oder einen Unternehmenstarif in Anspruch zu nehmen, benötigen Sie einen speziellen Buchungscode. Verwenden Sie keine Sonderzeichen wie (.) (,) (-) (:).Wählen Sie das Kästchen aus, um beim nächsten Besuch der Seite Ihren Buchungscode zu speichern. “Buchungscode speichern”. Deaktivieren Sie das Feld, wenn Sie einen öffentlich zugänglichen Computer verwenden, um unautorisierte Zugriffe auf Ihren Buchungscode zu verhindern.",
"In adults bed": "Im Bett der Eltern",
"In case of a no-show, your credit card will be charged for the first night.": "Bei Nichtanreise wird Ihre Kreditkarte mit dem Betrag der ersten Übernachtung belastet.",
"In case of no-show you will be charged for the first night.": "Bei Nichterscheinen wird Ihnen die erste Nacht in Rechnung gestellt.",
"In crib": "im Kinderbett",
"In extra bed": "im zusätzlichen Bett",
"In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.": "In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.",
@@ -524,6 +535,7 @@
"Phone": "Telefon",
"Phone is required": "Telefon ist erforderlich",
"Phone number": "Telefonnummer",
"Planning to arrive after 18.00? Secure your room by guaranteeing it with a credit card. Without the guarantee and in case of no-show, the room might be reallocated after 18:00.": "Sie möchten nach 18:00 Uhr anreisen? Sichern Sie sich Ihr Zimmer, indem Sie es mit einer Kreditkarte garantieren. Ohne Garantie und bei Nichterscheinen kann das Zimmer nach 18:00 Uhr vergeben werden.",
"Please contact <link>customer service</link>.": "Bitte wenden Sie sich an den <link>Kundendienst</link>.",
"Please enter a valid phone number": "Bitte geben Sie eine gültige Telefonnummer ein",
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
@@ -733,6 +745,7 @@
"We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.": "We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.",
"We couldn't find a matching location for your search.": "Wir konnten keinen passenden Standort für Ihre Suche finden.",
"We found no available rooms using this booking code ({bookingCode}). See available rates below.": "Wir haben keine verfügbaren Zimmer zur Buchung mit dem Buchungscode ({bookingCode}) gefunden. Unten sehen Sie Preise, die ohne Buchungscode verfügbar sind.",
"We had an issue guaranteeing your booking. Please try again.": "Bei der Garantie Ihrer Buchung ist ein Problem aufgetreten. Bitte versuchen Sie es erneut.",
"We had an issue processing your booking. Please try again. No charges have been made.": "Wir hatten ein Problem beim Verarbeiten Ihrer Buchung. Bitte versuchen Sie es erneut. Es wurden keine Gebühren erhoben.",
"We have a special gift waiting for you!": "Wir haben ein besonderes Geschenk für Sie!",
"We look forward to your visit!": "Wir freuen uns auf Ihren Besuch!",
@@ -751,6 +764,7 @@
"Welcome to": "Willkommen zu",
"What you have to do to guarantee booking:": "Was Sie tun müssen, um eine Buchung zu garantieren:",
"When": "Wann",
"When guaranteeing your booking with a credit card, we will hold the booking until 07:00 the day after check-in.": "Wenn Sie Ihre Buchung mit einer Kreditkarte garantieren, halten wir die Buchung bis 07:00 Uhr am Tag nach dem Check-in aufrecht.",
"When guaranteeing your booking, we will hold the booking until 07:00 until the day after check-in. This will provide you as a guest with added flexibility for check-in times.": "Wenn Sie Ihre Buchung garantieren, halten wir die Buchung bis 07:00 am Tag nach dem Check-in. Dies wird Ihnen als Gast zusätzliche Flexibilität für die Check-in-Zeiten gewähren.",
"Where should you go next?": "Wo geht es als Nächstes hin?",
"Where to?": "Wohin?",
@@ -782,6 +796,7 @@
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Ihre Buchung ist bestätigt, aber wir konnten Ihr Mitglied nicht verifizieren. Wenn Sie mit einem Mitgliederrabatt gebucht haben, müssen Sie entweder Ihr vorhandenes Mitgliedschaftsnummer bei der Anreise präsentieren, ein Mitglied werden oder die Preisdifferenz bei der Anreise bezahlen. Die Anmeldung ist vorzugsweise online vor der Aufenthaltsdauer erfolgreich.",
"Your card was successfully removed!": "Ihre Karte wurde erfolgreich entfernt!",
"Your card was successfully saved!": "Ihre Karte wurde erfolgreich gespeichert!",
"Your card will only be used for authorisation": "Ihre Karte wird nur zur Autorisierung verwendet",
"Your current level": "Ihr aktuelles Level",
"Your details": "Ihre Angaben",
"Your hotel": "Your hotel",
@@ -789,6 +804,7 @@
"Your member tier": "Ihr Mitgliedsniveau",
"Your points to spend": "Meine Punkte",
"Your room": "Ihr Zimmer",
"Your room will remain available for check-in even after 18:00.": "Ihr Zimmer bleibt auch nach 18:00 Uhr zum Check-in verfügbar.",
"Your selected bed type will be provided based on availability": "Ihre ausgewählte Bettart wird basierend auf der Verfügbarkeit bereitgestellt",
"Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Ihr Aufenthalt wurde storniert. Stornierungskosten: 0 {currency}. Es tut uns leid, dass die Pläne nicht funktionierten",
"Your stay was cancelled. Cancellation cost: 0 {currency}. Were sorry to see that the plans didnt work out": "Ihr Aufenthalt wurde storniert. Stornierungskosten: 0 {currency}. Es tut uns leid, dass die Pläne nicht funktionierten",

View File

@@ -100,6 +100,7 @@
"Booking Code filter": "Booking Code filter",
"Booking code": "Booking code",
"Booking confirmation": "Booking confirmation",
"Booking guaranteed.": "Booking guaranteed.",
"Booking number": "Booking number",
"Booking number is required": "Booking number is required",
"Booking number {value}": "Booking number {value}",
@@ -119,6 +120,7 @@
"Bus terminal": "Bus terminal",
"Business": "Business",
"By accepting the <termsAndConditionsLink>Terms and Conditions for Scandic Friends</termsAndConditionsLink> I understand that my personal data will be processed in accordance with <privacyPolicy>Scandic's Privacy Policy</privacyPolicy>.": "By accepting the <termsAndConditionsLink>Terms and Conditions for Scandic Friends</termsAndConditionsLink> I understand that my personal data will be processed in accordance with <privacyPolicy>Scandic's Privacy Policy</privacyPolicy>.",
"By guaranteeing with any of the payment methods available, I accept the terms for this stay and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandics Privacy Policy</privacyPolicyLink>. I accept Scandic requiring a valid credit card during my visit in case anything is left unpaid.": "By guaranteeing with any of the payment methods available, I accept the terms for this stay and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandics Privacy Policy</privacyPolicyLink>. I accept Scandic requiring a valid credit card during my visit in case anything is left unpaid.",
"By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.": "By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.",
"By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.": "By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.",
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service",
@@ -172,6 +174,7 @@
"Complete booking & go to payment": "Complete booking & go to payment",
"Complete the booking": "Complete the booking",
"Confirm": "Confirm",
"Confirm booking": "Confirm booking",
"Confirm cancellation": "Confirm cancellation",
"Contact information": "Contact information",
"Contact our memberservice": "Contact our memberservice",
@@ -293,8 +296,12 @@
"Go to My Benefits": "Go to My Benefits",
"Go to profile": "Go to profile",
"Great minds meet here": "Great minds meet here",
"Guarantee": "Guarantee",
"Guarantee booking with credit card": "Guarantee booking with credit card",
"Guarantee cost": "Guarantee cost",
"Guarantee for late arrival": "Guarantee for late arrival",
"Guarantee late arrival": "Guarantee late arrival",
"Guarantee room for late arrival": "Guarantee room for late arrival",
"Guest details updated": "Guest details updated",
"Guest information": "Guest information",
"Guests": "Guests",
@@ -323,11 +330,13 @@
"Hotels with {filter} in {location}": "Hotels with {filter} in {location}",
"Hours": "Hours",
"How do you want to sleep?": "How do you want to sleep?",
"How does it work": "How does it work",
"How it works": "How it works",
"How to use": "How to use",
"Hurry up and use them before they expire!": "Hurry up and use them before they expire!",
"I accept": "I accept",
"I accept the terms and conditions": "I accept the terms and conditions",
"I may arrive later than 18:00 and want to guarantee my booking with a credit card.": "I may arrive later than 18:00 and want to guarantee my booking with a credit card.",
"I promise to join Scandic Friends before checking in": "I promise to join Scandic Friends before checking in",
"I would like to get my booking confirmation via sms": "I would like to get my booking confirmation via sms",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.",
@@ -335,6 +344,8 @@
"If you close this your benefit will be removed": "If you close this your benefit will be removed",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.",
"In adults bed": "In adults bed",
"In case of a no-show, your credit card will be charged for the first night.": "In case of a no-show, your credit card will be charged for the first night.",
"In case of no-show you will be charged for the first night.": "In case of no-show you will be charged for the first night.",
"In crib": "In crib",
"In extra bed": "In extra bed",
"In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.": "In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.",
@@ -479,6 +490,7 @@
"Number of parking spots: {number}": "Number of parking spots: {number}",
"Number: {membershipNumber}": "Number: {membershipNumber}",
"OK": "OK",
"OTHER": "OTHER",
"OTHER PAYMENT METHODS": "OTHER PAYMENT METHODS",
"On your journey": "On your journey",
"One last step": "One last step",
@@ -522,6 +534,7 @@
"Phone": "Phone",
"Phone is required": "Phone is required",
"Phone number": "Phone number",
"Planning to arrive after 18.00? Secure your room by guaranteeing it with a credit card. Without the guarantee and in case of no-show, the room might be reallocated after 18:00.": "Planning to arrive after 18.00? Secure your room by guaranteeing it with a credit card. Without the guarantee and in case of no-show, the room might be allocated after 18:00.",
"Please contact <link>customer service</link>.": "Please contact <link>customer service</link>.",
"Please enter a valid phone number": "Please enter a valid phone number",
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
@@ -730,6 +743,7 @@
"We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.": "We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.",
"We couldn't find a matching location for your search.": "We couldn't find a matching location for your search.",
"We found no available rooms using this booking code ({bookingCode}). See available rates below.": "We found no available rooms using this booking code ({bookingCode}). See available rates below.",
"We had an issue guaranteeing your booking. Please try again.": "We had an issue guaranteeing your booking. Please try again.",
"We had an issue processing your booking. Please try again. No charges have been made.": "We had an issue processing your booking. Please try again. No charges have been made.",
"We have a special gift waiting for you!": "We have a special gift waiting for you!",
"We look forward to your visit!": "We look forward to your visit!",
@@ -748,6 +762,7 @@
"Welcome to": "Welcome to",
"What you have to do to guarantee booking:": "What you have to do to guarantee booking:",
"When": "When",
"When guaranteeing your booking with a credit card, we will hold the booking until 07:00 the day after check-in.": "When guaranteeing your booking with a credit card, we will hold the booking until 07:00 the day after check-in.",
"When guaranteeing your booking, we will hold the booking until 07:00 until the day after check-in. This will provide you as a guest with added flexibility for check-in times.": "When guaranteeing your booking, we will hold the booking until 07:00 until the day after check-in. This will provide you as a guest with added flexibility for check-in times.",
"Where should you go next?": "Where should you go next?",
"Where to?": "Where to?",
@@ -779,6 +794,7 @@
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.",
"Your card was successfully removed!": "Your card was successfully removed!",
"Your card was successfully saved!": "Your card was successfully saved!",
"Your card will only be used for authorisation": "Your card will only be used for authorisation",
"Your current level": "Your current level",
"Your details": "Your details",
"Your hotel": "Your hotel",
@@ -786,6 +802,7 @@
"Your member tier": "Your member tier",
"Your points to spend": "Your points to spend",
"Your room": "Your room",
"Your room will remain available for check-in even after 18:00.": "Your room will remain available for check-in even after 18:00.",
"Your selected bed type will be provided based on availability": "Your selected bed type will be provided based on availability",
"Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out",
"Your stay was cancelled. Cancellation cost: 0 {currency}. Were sorry to see that the plans didnt work out": "Your stay was cancelled. Cancellation cost: 0 {currency}. Were sorry to see that the plans didnt work out",

View File

@@ -100,6 +100,7 @@
"Booking Code filter": "Booking Code filter",
"Booking code": "Varauskoodi",
"Booking confirmation": "Varausvahvistus",
"Booking guaranteed.": "Varaus taattu.",
"Booking number": "Varausnumero",
"Booking number is required": "Varausnumero vaaditaan",
"Booking number {value}": "Booking number {value}",
@@ -119,6 +120,7 @@
"Bus terminal": "Bussiasema",
"Business": "Business",
"By accepting the <termsAndConditionsLink>Terms and Conditions for Scandic Friends</termsAndConditionsLink> I understand that my personal data will be processed in accordance with <privacyPolicy>Scandic's Privacy Policy</privacyPolicy>.": "Kyllä, <termsAndConditionsLink>hyväksyn Scandic Friends -jäsenyyttä</termsAndConditionsLink> koskevat ehdot ja ymmärrän, että Scandic käsittelee henkilötietojani <privacyPolicy>Scandicin Tietosuojaselosteen mukaisesti</privacyPolicy>.",
"By guaranteeing with any of the payment methods available, I accept the terms for this stay and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandics Privacy Policy</privacyPolicyLink>. I accept Scandic requiring a valid credit card during my visit in case anything is left unpaid.": "Maksamalla minkä tahansa saatavilla olevan maksutavan avulla hyväksyn tämän majoituksen ehdot ja yleiset <termsAndConditionsLink>ehdot ja ehtoja</termsAndConditionsLink>, ja ymmärrän, että Scandic käsittelee minun henkilötietoni tässä majoituksessa mukaisesti <privacyPolicyLink>Scandicin tietosuojavaltuuden</privacyPolicyLink> mukaisesti. Hyväksyn myös, että Scandic vaatii validin luottokortin majoituksen ajan, jos jokin jää maksamatta.",
"By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.": "By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.",
"By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.": "Maksamalla minkä tahansa saatavilla olevan maksutavan avulla hyväksyn tämän varauksen ehdot ja yleiset <termsAndConditionsLink>ehdot ja ehtoja</termsAndConditionsLink>, ja ymmärrän, että Scandic käsittelee minun henkilötietoni tässä varauksessa mukaisesti <privacyPolicyLink>Scandicin tietosuojavaltuuden</privacyPolicyLink> mukaisesti. Hyväksyn myös, että Scandic vaatii validin luottokortin majoituksen ajan, jos jokin jää maksamatta.",
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "Rekisteröitymällä hyväksyt Scandic Friendsin <termsAndConditionsLink>käyttöehdot</termsAndConditionsLink>. Jäsenyytesi on voimassa toistaiseksi ja voit lopettaa jäsenyytesi milloin tahansa lähettämällä sähköpostia Scandicin asiakaspalveluun",
@@ -173,6 +175,7 @@
"Complete booking & go to payment": "Täydennä varaus & siirry maksamaan",
"Complete the booking": "Täydennä varaus",
"Confirm": "Vahvista",
"Confirm booking": "Vahvista varaus",
"Confirm cancellation": "Vahvista peruutus",
"Contact information": "Yhteystiedot",
"Contact our memberservice": "Contact our memberservice",
@@ -294,8 +297,12 @@
"Go to My Benefits": "Siirry kohtaan 'Omat edut'",
"Go to profile": "Go to profile",
"Great minds meet here": "Great minds meet here",
"Guarantee": "Takuu",
"Guarantee booking with credit card": "Varmista varaus luottokortilla",
"Guarantee cost": "Takuukulut",
"Guarantee for late arrival": "Takuu myöhäiselle saapumiselle",
"Guarantee late arrival": "Varmista myöhäisempi tulema",
"Guarantee room for late arrival": "Takuuhuone myöhään saapuville",
"Guest details updated": "Gästien tiedot päivitetty",
"Guest information": "Vieraan tiedot",
"Guests": "Vierailijat",
@@ -324,11 +331,13 @@
"Hotels with {filter} in {location}": "Hotellit, joissa on {filter} kaupungissa {location}",
"Hours": "Ajat",
"How do you want to sleep?": "Kuinka haluat nukkua?",
"How does it work": "Miten se toimii",
"How it works": "Kuinka se toimii",
"How to use": "Kuinka käyttää",
"Hurry up and use them before they expire!": "Ole nopea ja käytä ne ennen kuin ne vanhenevat!",
"I accept": "Hyväksyn",
"I accept the terms and conditions": "Hyväksyn käyttöehdot",
"I may arrive later than 18:00 and want to guarantee my booking with a credit card.": "Saatan saapua klo 18.00 jälkeen ja haluan taata varaukseni luottokortilla.",
"I promise to join Scandic Friends before checking in": "Lupaan liittyä Scandic Friends -ohjelmaan ennen sisäänkirjautumista",
"I would like to get my booking confirmation via sms": "Haluan saada varauksen vahvistuksen SMS-viestillä",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "Jos ei, palaa takaisin ja tee se ennen kuin suljet tämän. Kun suljet tämän, etusi mitätöidään ja poistetaan Omista eduista.",
@@ -336,6 +345,8 @@
"If you close this your benefit will be removed": "Jos suljet tämän, etusi poistetaan",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "Jos varaat tarjoushinnalla tai yrityksen sopimushinnalla, tarvitset varauskoodin. Älä käytä erikoismerkkejä, kuten (.) (,) (-) (:).Jos haluat tallentaa varauskoodisi seuraavaa varausta varten, rastita tämä ruutu. Älä rastita ruutua, mikäli käytät yleistä tietokonetta ja haluat välttää varauskoodin väärinkäyttöä.",
"In adults bed": "Aikuisten vuoteessa",
"In case of a no-show, your credit card will be charged for the first night.": "Jos et saavu paikalle, luottokortiltasi veloitetaan ensimmäinen yö.",
"In case of no-show you will be charged for the first night.": "Jos et saavu paikalle, sinulta veloitetaan ensimmäinen yö.",
"In crib": "Pinnasängyssä",
"In extra bed": "Oma vuodepaikka",
"In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.": "In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.",
@@ -523,6 +534,7 @@
"Phone": "Puhelin",
"Phone is required": "Puhelin vaaditaan",
"Phone number": "Puhelinnumero",
"Planning to arrive after 18.00? Secure your room by guaranteeing it with a credit card. Without the guarantee and in case of no-show, the room might be reallocated after 18:00.": "Aiotko saapua klo 18.00 jälkeen? Varmista huoneesi takaamalla se luottokortilla. Ilman takuuta ja saapumatta jättämisen yhteydessä huone voidaan luovuttaa klo 18.00 jälkeen.",
"Please contact <link>customer service</link>.": "Ota yhteyttä <link>asiakaspalveluun</link>.",
"Please enter a valid phone number": "Ole hyvä ja näppäile voimassaoleva puhelinnumero",
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
@@ -733,6 +745,7 @@
"We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.": "We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.",
"We couldn't find a matching location for your search.": "Emme löytäneet hakuasi vastaavaa sijaintia.",
"We found no available rooms using this booking code ({bookingCode}). See available rates below.": "Emme löytäneet huoneita varauskoodilla ({bookingCode}). Katso saatavilla olevat hinnat alla.",
"We had an issue guaranteeing your booking. Please try again.": "Meillä oli ongelma varauksen takaamisessa. Yritä uudelleen.",
"We had an issue processing your booking. Please try again. No charges have been made.": "Meillä oli ongelma varauksen käsittelyssä. Yritä uudelleen. Ei maksuja on tehty.",
"We have a special gift waiting for you!": "Meillä on erityinen lahja odottamassa sinua!",
"We look forward to your visit!": "Odotamme innolla vierailuasi!",
@@ -751,6 +764,7 @@
"Welcome to": "Tervetuloa",
"What you have to do to guarantee booking:": "Mitä sinun on tehtävä varmistaaksesi varauksen:",
"When": "Kun",
"When guaranteeing your booking with a credit card, we will hold the booking until 07:00 the day after check-in.": "Kun takaamme varauksesi luottokortilla, pidätämme varauksen sisäänkirjautumisen jälkeisenä päivänä klo 07.00 asti.",
"When guaranteeing your booking, we will hold the booking until 07:00 until the day after check-in. This will provide you as a guest with added flexibility for check-in times.": "Jos varaat varauksen, pidämme varauksen 07:00 päivän jälkeen tarkistuspäivän jälkeen. Tämä tarjoaa sinulle lisään tarkistuspäivän aikaan.",
"Where should you go next?": "Mihin menisit seuraavaksi?",
"Where to?": "Minne?",
@@ -782,6 +796,7 @@
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Varauksesi on vahvistettu, mutta jäsenyytesi ei voitu vahvistaa. Jos olet bookeutunut jäsenyysalennoilla, sinun on joko esitettävä olemassa olevan jäsenyysnumero tarkistukseen, tulla jäseneksi tai maksamaan hinnan eron hotellissa. Jäsenyyden tilittäminen on suositeltavampaa tehdä verkkoon ennen majoittumista.",
"Your card was successfully removed!": "Korttisi poistettiin onnistuneesti!",
"Your card was successfully saved!": "Korttisi tallennettu onnistuneesti!",
"Your card will only be used for authorisation": "Korttiasi käytetään vain valtuutukseen",
"Your current level": "Nykyinen tasosi",
"Your details": "Tietosi",
"Your hotel": "Your hotel",
@@ -789,6 +804,7 @@
"Your member tier": "Sinun jäsenyysluokkasi",
"Your points to spend": "Käytettävissä olevat pisteesi",
"Your room": "Sinun huoneesi",
"Your room will remain available for check-in even after 18:00.": "Huoneesi on käytettävissä sisäänkirjautumista varten myös klo 18.00 jälkeen.",
"Your selected bed type will be provided based on availability": "Valitun vuodetyypin toimitetaan saatavuuden mukaan",
"Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Majoituksesi peruutettiin. Peruutusmaksu: 0 {currency}. Emme voi käyttää sitä, että suunnitellut majoitukset eivät toiminneet",
"Your stay was cancelled. Cancellation cost: 0 {currency}. Were sorry to see that the plans didnt work out": "Majoituksesi peruutettiin. Peruutusmaksu: 0 {currency}. Emme voi käyttää sitä, että suunnitellut majoitukset eivät toiminneet",

View File

@@ -100,6 +100,7 @@
"Booking Code filter": "Booking Code filter",
"Booking code": "Bestillingskode",
"Booking confirmation": "Bestillingsbekreftelse",
"Booking guaranteed.": "Bestilling garantert.",
"Booking number": "Bestillingsnummer",
"Booking number is required": "Bookingnummer er påkrevd",
"Booking number {value}": "Booking number {value}",
@@ -119,6 +120,7 @@
"Bus terminal": "Bussterminal",
"Business": "Forretnings",
"By accepting the <termsAndConditionsLink>Terms and Conditions for Scandic Friends</termsAndConditionsLink> I understand that my personal data will be processed in accordance with <privacyPolicy>Scandic's Privacy Policy</privacyPolicy>.": "Ved å akseptere <termsAndConditionsLink>vilkårene og betingelsene for Scandic Friends</termsAndConditionsLink>, er jeg inneforstått med at mine personopplysninger vil bli behandlet i samsvar med <privacyPolicy>Scandics personvernpolicy</privacyPolicy>.",
"By guaranteeing with any of the payment methods available, I accept the terms for this stay and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandics Privacy Policy</privacyPolicyLink>. I accept Scandic requiring a valid credit card during my visit in case anything is left unpaid.": "Ved å garantere med en av de tilgjengelige betalingsmetodene, aksepterer jeg vilkårene for dette oppholdet og de generelle <termsAndConditionsLink>vilkårene og betingelsene</termsAndConditionsLink>, og forstår at Scandic vil behandle mine personopplysninger for dette oppholdet i samsvar med <privacyPolicyLink>Scandics personvernpolicy</privacyPolicyLink>. Jeg aksepterer at Scandic krever et gyldig kredittkort under besøket mitt i tilfelle noe står ubetalt.",
"By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.": "By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.",
"By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.": "Ved å betale med en av de tilgjengelige betalingsmetodene godtar jeg vilkårene og betingelsene for denne bestillingen og de generelle <termsAndConditionsLink>vilkårene</termsAndConditionsLink>, og forstår at Scandic vil behandle mine personopplysninger i forbindelse med denne bestillingen i henhold til <privacyPolicyLink> Scandics personvernpolicy</privacyPolicyLink>. Jeg aksepterer at Scandic krever et gyldig kredittkort under mitt besøk i tilfelle noe blir refundert.",
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "Ved å registrere deg godtar du Scandic Friends <termsAndConditionsLink>vilkår og betingelser</termsAndConditionsLink>. Medlemskapet ditt er gyldig inntil videre, og du kan si opp medlemskapet ditt når som helst ved å sende en e-post til Scandics kundeservice",
@@ -172,6 +174,7 @@
"Complete booking & go to payment": "Fullfør bestilling & gå til betaling",
"Complete the booking": "Fullfør reservasjonen",
"Confirm": "Bekreft",
"Confirm booking": "Bekreft bestilling",
"Confirm cancellation": "Bekræft annullerering",
"Contact information": "Kontaktinformasjon",
"Contact our memberservice": "Contact our memberservice",
@@ -293,8 +296,12 @@
"Go to My Benefits": "Gå til 'Mine fordeler'",
"Go to profile": "Go to profile",
"Great minds meet here": "Great minds meet here",
"Guarantee": "Garantere",
"Guarantee booking with credit card": "Garantere booking med kredittkort",
"Guarantee cost": "Garantikostnad",
"Guarantee for late arrival": "Garanti for sen ankomst",
"Guarantee late arrival": "Garantere sen ankomst",
"Guarantee room for late arrival": "Garantert rom for sen ankomst",
"Guest details updated": "Gjestens detaljer oppdatert",
"Guest information": "Informasjon til gjester",
"Guests": "Gjester",
@@ -323,11 +330,13 @@
"Hotels with {filter} in {location}": "Hoteller med {filter} i {location}",
"Hours": "Tider",
"How do you want to sleep?": "Hvordan vil du sove?",
"How does it work": "Hvordan fungerer det",
"How it works": "Hvordan det fungerer",
"How to use": "Hvordan bruke",
"Hurry up and use them before they expire!": "Skynd deg og bruk dem før de utløper!",
"I accept": "Jeg aksepterer",
"I accept the terms and conditions": "Jeg aksepterer vilkårene",
"I may arrive later than 18:00 and want to guarantee my booking with a credit card.": "Jeg kan ankomme senere enn kl. 18.00 og vil garantere bestillingen min med kredittkort.",
"I promise to join Scandic Friends before checking in": "Jeg lover å bli med i Scandic Friends før jeg sjekker inn",
"I would like to get my booking confirmation via sms": "Jeg vil gjerne motta bekreftelsen av bestillingen min via sms",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "Hvis ikke, gå tilbake og gjør det før du lukker dette. Når du lukker dette, vil fordelen din bli ugyldig og fjernet fra Mine fordeler.",
@@ -335,6 +344,8 @@
"If you close this your benefit will be removed": "Hvis du lukker dette, vil fordelen din bli fjernet",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "Om du bestiller et tilbud eller en avtalepris kreves en spesiell bookingkode. Ikke benytt spesialtegn som (.) (,) (-) (:). Lagre din bookingkode til neste gang du besøker siden ved å klikke på \"Husk\". Klikk ikke på \"Husk\" dersom du bruker en offentlig datamaskin, da uvedkommede kan få tilgang til din bookingkode.",
"In adults bed": "i voksnes seng",
"In case of a no-show, your credit card will be charged for the first night.": "Ved manglende oppmøte vil kredittkortet ditt bli belastet for den første natten.",
"In case of no-show you will be charged for the first night.": "Ved manglende oppmøte vil du bli belastet for den første natten.",
"In crib": "i sprinkelseng",
"In extra bed": "i ekstraseng",
"In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.": "In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.",
@@ -522,6 +533,7 @@
"Phone": "Telefon",
"Phone is required": "Telefon kreves",
"Phone number": "Telefonnummer",
"Planning to arrive after 18.00? Secure your room by guaranteeing it with a credit card. Without the guarantee and in case of no-show, the room might be reallocated after 18:00.": "Planlegger du å ankomme etter kl 18.00? Sikre rommet ditt ved å garantere det med et kredittkort. Uten garantien og ved manglende oppmøte, kan rommet bli tildelt etter kl. 18.00.",
"Please contact <link>customer service</link>.": "Vennligst kontakt <link>kundeservice</link>.",
"Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer",
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
@@ -729,6 +741,7 @@
"We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.": "We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.",
"We couldn't find a matching location for your search.": "Vi finner ikke et sted som samsvarer for søket ditt.",
"We found no available rooms using this booking code ({bookingCode}). See available rates below.": "Vi fant ingen tilgjengelige rom ved bruk av denne bookingkoden. Vennligst sjekk at bookingkoden du har skrevet inn er riktig. Se tilgjengelige priser nedenfor.",
"We had an issue guaranteeing your booking. Please try again.": "Vi hadde et problem med å garantere bestillingen din. Prøv igjen.",
"We had an issue processing your booking. Please try again. No charges have been made.": "Vi hadde et problem med å behandle din bestilling. Vær så snill å prøv igjen. Ingen gebyrer er blevet belastet.",
"We have a special gift waiting for you!": "Vi har en spesiell gave som venter på deg!",
"We look forward to your visit!": "Vi ser frem til ditt besøk!",
@@ -747,6 +760,7 @@
"Welcome to": "Velkommen til",
"What you have to do to guarantee booking:": "Hva du må gjøre for å garantere reservasjonen:",
"When": "Når",
"When guaranteeing your booking with a credit card, we will hold the booking until 07:00 the day after check-in.": "Når vi garanterer bestillingen med kredittkort, vil vi holde bestillingen til kl. 07.00 dagen etter innsjekking.",
"When guaranteeing your booking, we will hold the booking until 07:00 until the day after check-in. This will provide you as a guest with added flexibility for check-in times.": "Når du garanterer din reservasjon, vil vi holde reservasjonen til 07:00 til dagen etter check-in. Dette vil gi deg som gjest tilføjet fleksibilitet for check-in-tider.",
"Where should you go next?": "Hvor ønsker du å reise neste gang?",
"Where to?": "Hvor skal du?",
@@ -778,6 +792,7 @@
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Din bestilling er bekreftet, men vi kunne ikke verifisere medlemskapet ditt. Hvis du har booke ut med et medlemsrabatt, må du enten presentere eksisterende medlemsnummer ved check-in, bli medlem eller betale prisdifferansen ved hotellet. Registrering er foretrukket gjort online før oppholdet.",
"Your card was successfully removed!": "Kortet ditt ble fjernet!",
"Your card was successfully saved!": "Kortet ditt ble lagret!",
"Your card will only be used for authorisation": "Kortet ditt vil kun bli brukt til autorisasjon",
"Your current level": "Ditt nåværende nivå",
"Your details": "Dine detaljer",
"Your hotel": "Your hotel",
@@ -785,6 +800,7 @@
"Your member tier": "Ditt medlemskapsnivå",
"Your points to spend": "Dine brukbare poeng",
"Your room": "Rommet ditt",
"Your room will remain available for check-in even after 18:00.": "Rommet ditt vil være tilgjengelig for innsjekking selv etter kl. 18.00.",
"Your selected bed type will be provided based on availability": "Din valgte sengtype vil blive stillet til rådighed baseret på tilgængelighed",
"Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Ditt ophold ble annulleret. Annullereringspris: 0 {currency}. Vi beklager at planene ikke fungerte ut",
"Your stay was cancelled. Cancellation cost: 0 {currency}. Were sorry to see that the plans didnt work out": "Dit ophold blev annulleret. Annullereringspris: 0 {currency}. Vi beklager, at planerne ikke fungerede ud",

View File

@@ -100,6 +100,7 @@
"Booking Code filter": "Booking Code filter",
"Booking code": "Bokningskod",
"Booking confirmation": "Bokningsbekräftelse",
"Booking guaranteed.": "Bokning garanterad.",
"Booking number": "Bokningsnummer",
"Booking number is required": "Bokningsnummer krävs",
"Booking number {value}": "Booking number {value}",
@@ -119,6 +120,7 @@
"Bus terminal": "Bussterminal",
"Business": "Business",
"By accepting the <termsAndConditionsLink>Terms and Conditions for Scandic Friends</termsAndConditionsLink> I understand that my personal data will be processed in accordance with <privacyPolicy>Scandic's Privacy Policy</privacyPolicy>.": "Genom att acceptera <termsAndConditionsLink>villkoren för Scandic Friends</termsAndConditionsLink> förstår jag att mina personuppgifter kommer att behandlas i enlighet med <privacyPolicy>Scandics Integritetspolicy</privacyPolicy>.",
"By guaranteeing with any of the payment methods available, I accept the terms for this stay and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandics Privacy Policy</privacyPolicyLink>. I accept Scandic requiring a valid credit card during my visit in case anything is left unpaid.": "Genom att garantera med någon av de tillgängliga betalningsmetoderna accepterar jag villkoren för denna vistelse och de allmänna <termsAndConditionsLink>allmänna villkoren</termsAndConditionsLink>, och förstår att Scandic kommer att behandla mina personuppgifter för denna vistelse i enlighet med <privacyPolicyLink>Scandics Integritetspolicy</privacyPolicyLink>. Jag accepterar att Scandic kräver ett giltigt kreditkort under mitt besök om något lämnas obetalt.",
"By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.": "By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Scandic Friends & SAS Terms and Conditions</sasScandicTermsAndConditionsLink>. You will be connected throughout the duration of your employment or until further notice, and you can opt out at any time.",
"By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.": "Genom att betala med någon av de tillgängliga betalningsmetoderna accepterar jag villkoren för denna bokning och de generella <termsAndConditionsLink>Villkoren och villkoren</termsAndConditionsLink>, och förstår att Scandic kommer att behandla min personliga data i samband med denna bokning i enlighet med <privacyPolicyLink>Scandics integritetspolicy</privacyPolicyLink>. Jag accepterar att Scandic kräver ett giltigt kreditkort under min besök i fall att något är tillbaka betalt.",
"By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service": "Genom att registrera dig accepterar du Scandic Friends <termsAndConditionsLink>Användarvillkor</termsAndConditionsLink>. Ditt medlemskap gäller tills vidare och du kan när som helst säga upp ditt medlemskap genom att skicka ett mejl till Scandics kundtjänst",
@@ -172,6 +174,7 @@
"Complete booking & go to payment": "Fullför bokning & gå till betalning",
"Complete the booking": "Slutför bokningen",
"Confirm": "Bekräfta",
"Confirm booking": "Bekräfta bokning",
"Confirm cancellation": "Bekräfta avbokning",
"Contact information": "Kontaktinformation",
"Contact our memberservice": "Contact our memberservice",
@@ -293,8 +296,12 @@
"Go to My Benefits": "Gå till 'Mina förmåner'",
"Go to profile": "Go to profile",
"Great minds meet here": "Great minds meet here",
"Guarantee": "Garantera",
"Guarantee booking with credit card": "Garantera bokning med kreditkort",
"Guarantee cost": "Garantikostnad",
"Guarantee for late arrival": "Garantera för sen ankomst",
"Guarantee late arrival": "Garantera sen ankomst",
"Guarantee room for late arrival": "Garantera rum för sen ankomst",
"Guest details updated": "Gästdetaljer uppdaterade",
"Guest information": "Information till gästerna",
"Guests": "Gäster",
@@ -323,11 +330,13 @@
"Hotels with {filter} in {location}": "Hotell med {filter} i {location}",
"Hours": "Tider",
"How do you want to sleep?": "Hur vill du sova?",
"How does it work": "Hur fungerar det",
"How it works": "Hur det fungerar",
"How to use": "Hur man använder",
"Hurry up and use them before they expire!": "Skynda dig och använd dem innan de går ut!",
"I accept": "Jag accepterar",
"I accept the terms and conditions": "Jag accepterar villkoren",
"I may arrive later than 18:00 and want to guarantee my booking with a credit card.": "Jag kan komma senare än 18:00 och vill garantera min bokning med ett kreditkort.",
"I promise to join Scandic Friends before checking in": "Jag lovar att gå med i Scandic Friends innan jag checkar in",
"I would like to get my booking confirmation via sms": "Jag vill få min bokningsbekräftelse via sms",
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.": "Om inte, gå tillbaka och gör det innan du stänger detta. När du stänger detta kommer din förmån att ogiltigförklaras och tas bort från Mina förmåner.",
@@ -335,6 +344,8 @@
"If you close this your benefit will be removed": "Om du stänger detta kommer din förmån att tas bort",
"If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.": "Om du bokar ett erbjudande eller avtalspris krävs en särskild bokningskod. Använd inga specialtecken som (.) (,) (-) (:). För bokning med koden VOF ber vi dig ringa oss 08-517 517 00.Spara din bokningskod till nästa gång du besöker sidan, genom att klicka i rutan \"Kom ihåg\". Klicka inte i rutan om du använder en allmän dator, då kan obehöriga komma åt din bokningskod.",
"In adults bed": "I vuxens säng",
"In case of a no-show, your credit card will be charged for the first night.": "I händelse av utebliven ankomst kommer ditt kreditkort att debiteras för den första natten.",
"In case of no-show you will be charged for the first night.": "Vid utebliven ankomst kommer du att debiteras för den första natten.",
"In crib": "I spjälsäng",
"In extra bed": "Egen sängplats",
"In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.": "In order to verify your account linking we will ask you to sign in to your SAS EuroBonus account.",
@@ -522,6 +533,7 @@
"Phone": "Telefon",
"Phone is required": "Telefonnummer är obligatorisk",
"Phone number": "Telefonnummer",
"Planning to arrive after 18.00? Secure your room by guaranteeing it with a credit card. Without the guarantee and in case of no-show, the room might be reallocated after 18:00.": "Planerar du att anlända efter 18.00? Säkra ditt rum genom att garantera det med ett kreditkort. Utan garantin och vid utebliven ankomst kan rummet komma att omfördelas efter kl. 18.00.",
"Please contact <link>customer service</link>.": "Vänligen kontakta <link>kundtjänst</link>.",
"Please enter a valid phone number": "Var vänlig och ange ett giltigt telefonnummer",
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.": "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
@@ -731,6 +743,7 @@
"We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.": "We could not connect your accounts to give you access. Please contact us and well help you resolve this issue.",
"We couldn't find a matching location for your search.": "Vi kunde inte hitta en plats som matchade din sökning.",
"We found no available rooms using this booking code ({bookingCode}). See available rates below.": "Din bokningskod {bookingCode} finns inte tillgänglig. Se tillgängliga rum nedan.",
"We had an issue guaranteeing your booking. Please try again.": "Vi hade ett problem med att garantera din bokning. Försök igen.",
"We had an issue processing your booking. Please try again. No charges have been made.": "Vi hade ett problem med att bearbeta din bokning. Vänligen försök igen. Inga avgifter har debiterats.",
"We have a special gift waiting for you!": "Vi har en speciell present som väntar på dig!",
"We look forward to your visit!": "Vi ser fram emot ditt besök!",
@@ -749,6 +762,7 @@
"Welcome to": "Välkommen till",
"What you have to do to guarantee booking:": "Vad du måste göra för att garantera bokningen:",
"When": "När",
"When guaranteeing your booking with a credit card, we will hold the booking until 07:00 the day after check-in.": "När vi garanterar din bokning med kreditkort kommer vi att hålla bokningen till kl. 07.00 dagen efter incheckningen.",
"When guaranteeing your booking, we will hold the booking until 07:00 until the day after check-in. This will provide you as a guest with added flexibility for check-in times.": "När du garanterar din bokning kommer vi att hålla bokningen till 07:00 till dagen efter check-in. Detta ger dig som gäst extra flexibilitet för check-in-tider.",
"Where should you go next?": "Låter inte en spontanweekend härligt?",
"Where to?": "Vart?",
@@ -780,6 +794,7 @@
"Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.": "Din bokning är bekräftad, men vi kunde inte verifiera ditt medlemskap. Om du har bokat med ett medlemsrabatt måste du antingen presentera ditt befintliga medlemsnummer vid check-in, bli medlem eller betala prisdifferensen vid hotell. Registrering är föredragen gjord online före vistelsen.",
"Your card was successfully removed!": "Ditt kort har tagits bort!",
"Your card was successfully saved!": "Ditt kort har sparats!",
"Your card will only be used for authorisation": "Ditt kort kommer endast att användas för auktorisering",
"Your current level": "Din nuvarande nivå",
"Your details": "Dina uppgifter",
"Your hotel": "Your hotel",
@@ -787,6 +802,7 @@
"Your member tier": "Din medlemskapsnivå",
"Your points to spend": "Dina spenderbara poäng",
"Your room": "Ditt rum",
"Your room will remain available for check-in even after 18:00.": "Ditt rum kommer att förbli tillgängligt för incheckning även efter kl. 18.00.",
"Your selected bed type will be provided based on availability": "Din valda sängtyp kommer att tillhandahållas baserat på tillgänglighet",
"Your stay was cancelled. Cancellation cost: 0 {currency}. We're sorry to see that the plans didn't work out": "Din vistelse blev avbokad. Avbokningskostnad: 0 {currency}. Vi beklagar att planerna inte fungerade.",
"Your stay was cancelled. Cancellation cost: 0 {currency}. Were sorry to see that the plans didnt work out": "Din vistelse blev avbokad. Avbokningskostnad: 0 {currency}. Vi beklagar att planerna inte fungerade ut",

View File

@@ -68,6 +68,9 @@ export namespace endpoints {
export function packages(confirmationNumber: string) {
return `${bookings}/${confirmationNumber}/packages`
}
export function guarantee(confirmationNumber: string) {
return `${bookings}/${confirmationNumber}/guarantee`
}
export const enum Stays {
future = `${base.path.booking}/${version}/${base.enitity.Stays}/future`,

View File

@@ -278,6 +278,11 @@ const nextConfig = {
destination:
"/:lang/hotelreservation/payment-callback?status=:status",
},
{
source: "/:lang/hotelreservation/gla-payment-callback/:status/:refId",
destination:
"/:lang/hotelreservation/gla-payment-callback?status=:status&refId=:refId",
},
// Find my booking
{
source: findMyBooking.en,

View File

@@ -39,7 +39,6 @@ export default function EnterDetailsProvider({
.map((room) => ({
isAvailable: room.isAvailable,
breakfastIncluded: !!room.breakfastIncluded,
cancellationRule: room.cancellationRule,
cancellationText: room.cancellationText,
rateDetails: room.rateDetails,
rateTitle: room.rateTitle,
@@ -56,6 +55,7 @@ export default function EnterDetailsProvider({
}
: undefined,
mustBeGuaranteed: room.mustBeGuaranteed,
isFlexRate: room.isFlexRate,
})),
vat,
}

View File

@@ -78,7 +78,7 @@ export const createBookingInput = z.object({
checkInDate: z.string(),
checkOutDate: z.string(),
rooms: roomsSchema,
payment: paymentSchema,
payment: paymentSchema.optional(),
language: z.nativeEnum(Lang).transform((val) => langToApiLang[val]),
})
@@ -111,6 +111,21 @@ export const cancelBookingInput = z.object({
language: z.nativeEnum(Lang).transform((val) => langToApiLang[val]),
})
export const guaranteeBookingInput = z.object({
confirmationNumber: z.string(),
card: z
.object({
alias: z.string(),
expiryDate: z.string(),
cardType: z.string(),
})
.optional(),
language: z.nativeEnum(Lang).transform((val) => langToApiLang[val]),
success: z.string().nullable(),
error: z.string().nullable(),
cancel: z.string().nullable(),
})
export const createRefIdInput = z.object({
confirmationNumber: z
.string()

View File

@@ -11,6 +11,7 @@ import {
addPackageInput,
cancelBookingInput,
createBookingInput,
guaranteeBookingInput,
priceChangeInput,
removePackageInput,
updateBookingInput,
@@ -51,6 +52,14 @@ const addPackageFailCounter = meter.createCounter(
"trpc.bookings.add-package-fail"
)
const guaranteeBookingCounter = meter.createCounter("trpc.bookings.guarantee")
const guaranteeBookingSuccessCounter = meter.createCounter(
"trpc.bookings.guarantee-success"
)
const guaranteeBookingFailCounter = meter.createCounter(
"trpc.bookings.guarantee-fail"
)
const updateGuestCounter = meter.createCounter("trpc.bookings.update-guest")
const updateGuestSuccessCounter = meter.createCounter(
"trpc.bookings.update-guest-success"
@@ -403,6 +412,71 @@ export const bookingMutationRouter = router({
addPackageSuccessCounter.add(1, { confirmationNumber })
return verifiedData.data
}),
guarantee: safeProtectedServiceProcedure
.input(guaranteeBookingInput)
.mutation(async function ({ ctx, input }) {
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { confirmationNumber, language, ...body } = input
guaranteeBookingCounter.add(1, { confirmationNumber })
const headers = {
Authorization: `Bearer ${accessToken}`,
}
const apiResponse = await api.put(
api.endpoints.v1.Booking.guarantee(confirmationNumber),
{
headers,
body: body,
},
{ language }
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
guaranteeBookingFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.guarantee error",
JSON.stringify({
query: { confirmationNumber },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
error: text,
},
})
)
return null
}
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
guaranteeBookingFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
})
console.error(
"api.booking.guarantee validation error",
JSON.stringify({
query: { confirmationNumber },
error: verifiedData.error,
})
)
return null
}
guaranteeBookingSuccessCounter.add(1, { confirmationNumber })
return verifiedData.data
}),
update: safeProtectedServiceProcedure

View File

@@ -200,6 +200,14 @@ export const bookingConfirmationSchema = z
childrenAges: z.array(z.number().int()).default([]),
canChangeDate: z.boolean(),
bookingCode: z.string().nullable(),
guaranteeInfo: z
.object({
maskedCard: z.string(),
cardType: z.string(),
paymentMethod: z.string(),
paymentMethodDescription: z.string(),
})
.nullish(),
computedReservationStatus: z.string().nullable().default(""),
confirmationNumber: nullableStringValidator,
createDateTime: z.date({ coerce: true }),

View File

@@ -52,6 +52,7 @@ export const selectedRoomAvailabilityInputSchema = z.object({
bookingCode: z.string().optional(),
rateCode: z.string(),
roomTypeCode: z.string(),
counterRateCode: z.string().optional(),
packageCodes: z.array(z.nativeEnum(RoomPackageCodeEnum)).optional(),
})

View File

@@ -1,3 +1,4 @@
import { CancellationRuleEnum } from "@/constants/booking"
import { env } from "@/env/server"
import * as api from "@/lib/api"
import { dt } from "@/lib/dt"
@@ -594,6 +595,7 @@ export const hotelQueryRouter = router({
children,
bookingCode,
rateCode,
counterRateCode,
roomTypeCode,
} = input
@@ -742,6 +744,10 @@ export const hotelQueryRouter = router({
validateAvailabilityData.data.rateDefinitions.find(
(rate) => rate.rateCode === rateCode
)
const memberRateDefinition =
validateAvailabilityData.data.rateDefinitions.find(
(rate) => rate.rateCode === counterRateCode
)
const bedTypes = availableRoomsInCategory
.map((availRoom) => {
@@ -790,9 +796,12 @@ export const hotelQueryRouter = router({
return {
selectedRoom,
rateDetails: rateDefinition?.generalTerms,
cancellationRule: rateDefinition?.cancellationRule,
cancellationText: rateDefinition?.cancellationText ?? "",
isFlexRate:
rateDefinition?.cancellationRule ===
CancellationRuleEnum.CancellableBefore6PM,
mustBeGuaranteed: !!rateDefinition?.mustBeGuaranteed,
memberMustBeGuaranteed: !!memberRateDefinition?.mustBeGuaranteed,
breakfastIncluded: !!rateDefinition?.breakfastIncluded,
// Send rate Title when it is a booking code rate
rateTitle:

View File

@@ -5,4 +5,5 @@ export interface CheckboxProps
name: string
registerOptions?: RegisterOptions
hideError?: boolean
topAlign?: boolean
}

View File

@@ -3,7 +3,10 @@ import type { PaymentMethodEnum } from "@/constants/booking"
export interface PaymentProps {
otherPaymentOptions: PaymentMethodEnum[]
mustBeGuaranteed: boolean
memberMustBeGuaranteed: boolean
supportedCards: PaymentMethodEnum[]
isFlexRate: boolean
}
export interface PaymentClientProps

View File

@@ -124,7 +124,7 @@ export type TrackingSDKData = TrackingSDKPageData & {
type BasePaymentEvent = {
event: string
hotelId: string | undefined
method?: string
method?: string | null
isSavedCreditCard?: boolean
smsEnable?: boolean
status: "attempt" | "cancelled" | "failed"

View File

@@ -8,6 +8,7 @@ export interface Room {
cancellationRule?: string
cancellationText: string
mustBeGuaranteed: boolean
memberMustBeGuaranteed?: boolean
packages: Packages | null
rateDetails: string[]
rateTitle?: string
@@ -15,6 +16,7 @@ export interface Room {
roomType: string
roomTypeCode: string
isAvailable: boolean
isFlexRate: boolean
}
export interface RoomProviderProps extends React.PropsWithChildren {

View File

@@ -35,6 +35,7 @@ export interface InitialRoomData {
roomType: string
roomTypeCode: string
mustBeGuaranteed: boolean
isFlexRate: boolean
}
export type RoomStep = {