Merged in feat/SW-431-payment-flow (pull request #635)
Feat/SW-431 payment flow * feat(SW-431): Update mock hotel data * feat(SW-431): Added route handler and trpc routes * feat(SW-431): List payment methods and handle booking status and redirection * feat(SW-431): Updated booking page to poll for booking status * feat(SW-431): Updated create booking contract * feat(SW-431): small fix * fix(SW-431): Added intl string and sorted dictionaries * fix(SW-431): Changes from PR * fix(SW-431): fixes from PR * fix(SW-431): add todo comments * fix(SW-431): update schema prop Approved-by: Simon.Emanuelsson
This commit is contained in:
committed by
Pontus Dreij
parent
c7e829cd02
commit
d44451d2dc
@@ -1,6 +1,6 @@
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
import { getHotelDataSchema } from "@/server/routers/hotels/output"
|
||||
import tempHotelData from "@/server/routers/hotels/tempHotelData.json"
|
||||
|
||||
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
|
||||
import BedSelection from "@/components/HotelReservation/SelectRate/BedSelection"
|
||||
@@ -80,12 +80,19 @@ export default async function SectionsPage({
|
||||
}: PageArgs<LangParams & { section: string }, SectionPageProps>) {
|
||||
setLang(params.lang)
|
||||
|
||||
// TODO: Use real endpoint.
|
||||
const hotel = getHotelDataSchema.parse(tempHotelData)
|
||||
const hotel = await serverClient().hotel.hotelData.get({
|
||||
hotelId: "811",
|
||||
language: params.lang,
|
||||
})
|
||||
|
||||
if (!hotel) {
|
||||
// TODO: handle case with hotel missing
|
||||
return notFound()
|
||||
}
|
||||
|
||||
const rooms = await serverClient().hotel.rates.get({
|
||||
// TODO: pass the correct hotel ID and all other parameters that should be included in the search
|
||||
hotelId: "1",
|
||||
hotelId: hotel.data.id,
|
||||
})
|
||||
const intl = await getIntl()
|
||||
|
||||
@@ -170,7 +177,9 @@ export default async function SectionsPage({
|
||||
header={intl.formatMessage({ id: "Payment info" })}
|
||||
path={`payment?${currentSearchParams}`}
|
||||
>
|
||||
{params.section === "payment" && <Payment />}
|
||||
{params.section === "payment" && (
|
||||
<Payment hotel={hotel.data.attributes} />
|
||||
)}
|
||||
</SectionAccordion>
|
||||
</div>
|
||||
<div className={styles.summary}>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
|
||||
export default function Loading() {
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
@@ -1,20 +1,67 @@
|
||||
"use client"
|
||||
|
||||
import { useMemo } from "react"
|
||||
|
||||
import {
|
||||
BOOKING_CONFIRMATION_NUMBER,
|
||||
BookingStatusEnum,
|
||||
} from "@/constants/booking"
|
||||
|
||||
import IntroSection from "@/components/HotelReservation/BookingConfirmation/IntroSection"
|
||||
import StaySection from "@/components/HotelReservation/BookingConfirmation/StaySection"
|
||||
import SummarySection from "@/components/HotelReservation/BookingConfirmation/SummarySection"
|
||||
import { tempConfirmationData } from "@/components/HotelReservation/BookingConfirmation/tempConfirmationData"
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
import { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus"
|
||||
|
||||
import styles from "./page.module.css"
|
||||
|
||||
const maxRetries = 10
|
||||
const retryInterval = 2000
|
||||
|
||||
export default function BookingConfirmationPage() {
|
||||
const { email, hotel, stay, summary } = tempConfirmationData
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<section className={styles.section}>
|
||||
<IntroSection email={email} />
|
||||
<StaySection hotel={hotel} stay={stay} />
|
||||
<SummarySection summary={summary} />
|
||||
</section>
|
||||
</main>
|
||||
const confirmationNumber = useMemo(() => {
|
||||
if (typeof window === "undefined") return ""
|
||||
|
||||
const storedConfirmationNumber = sessionStorage.getItem(
|
||||
BOOKING_CONFIRMATION_NUMBER
|
||||
)
|
||||
// TODO: cleanup stored values
|
||||
// sessionStorage.removeItem(BOOKING_CONFIRMATION_NUMBER)
|
||||
return storedConfirmationNumber
|
||||
}, [])
|
||||
|
||||
const bookingStatus = useHandleBookingStatus(
|
||||
confirmationNumber,
|
||||
BookingStatusEnum.BookingCompleted,
|
||||
maxRetries,
|
||||
retryInterval
|
||||
)
|
||||
|
||||
if (
|
||||
confirmationNumber === null ||
|
||||
bookingStatus.isError ||
|
||||
(bookingStatus.isFetched && !bookingStatus.data)
|
||||
) {
|
||||
// TODO: handle error
|
||||
throw new Error("Error fetching booking status")
|
||||
}
|
||||
|
||||
if (
|
||||
bookingStatus.data?.reservationStatus === BookingStatusEnum.BookingCompleted
|
||||
) {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<section className={styles.section}>
|
||||
<IntroSection email={email} />
|
||||
<StaySection hotel={hotel} stay={stay} />
|
||||
<SummarySection summary={summary} />
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
|
||||
37
app/api/web/payment-callback/[lang]/[status]/route.ts
Normal file
37
app/api/web/payment-callback/[lang]/[status]/route.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { NextRequest, NextResponse } from "next/server"
|
||||
import { env } from "process"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
import {
|
||||
bookingConfirmation,
|
||||
payment,
|
||||
} from "@/constants/routes/hotelReservation"
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { lang: string; status: string } }
|
||||
): Promise<NextResponse> {
|
||||
console.log(`[payment-callback] callback started`)
|
||||
const lang = params.lang as Lang
|
||||
const status = params.status
|
||||
const returnUrl = new URL(`${env.PUBLIC_URL}/${payment[lang]}`)
|
||||
|
||||
if (status === "success") {
|
||||
const confirmationUrl = new URL(
|
||||
`${env.PUBLIC_URL}/${bookingConfirmation[lang]}`
|
||||
)
|
||||
console.log(`[payment-callback] redirecting to: ${confirmationUrl}`)
|
||||
return NextResponse.redirect(confirmationUrl)
|
||||
}
|
||||
|
||||
if (status === "cancel") {
|
||||
returnUrl.searchParams.set("cancel", "true")
|
||||
}
|
||||
|
||||
if (status === "error") {
|
||||
returnUrl.searchParams.set("error", "true")
|
||||
}
|
||||
|
||||
console.log(`[payment-callback] redirecting to: ${returnUrl}`)
|
||||
return NextResponse.redirect(returnUrl)
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import styles from "./introSection.module.css"
|
||||
|
||||
import { IntroSectionProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation"
|
||||
|
||||
export default async function IntroSection({ email }: IntroSectionProps) {
|
||||
const intl = await getIntl()
|
||||
export default function IntroSection({ email }: IntroSectionProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
return (
|
||||
<section className={styles.section}>
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { ArrowRightIcon, ScandicLogoIcon } from "@/components/Icons"
|
||||
import Image from "@/components/Image"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import styles from "./staySection.module.css"
|
||||
|
||||
import { StaySectionProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation"
|
||||
|
||||
export default async function StaySection({ hotel, stay }: StaySectionProps) {
|
||||
const intl = await getIntl()
|
||||
export default function StaySection({ hotel, stay }: StaySectionProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const nightsText =
|
||||
stay.nights > 1
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import styles from "./summarySection.module.css"
|
||||
|
||||
import { SummarySectionProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation"
|
||||
|
||||
export default async function SummarySection({ summary }: SummarySectionProps) {
|
||||
const intl = await getIntl()
|
||||
export default function SummarySection({ summary }: SummarySectionProps) {
|
||||
const intl = useIntl()
|
||||
const roomType = `${intl.formatMessage({ id: "Type of room" })}: ${summary.roomType}`
|
||||
const bedType = `${intl.formatMessage({ id: "Type of bed" })}: ${summary.bedType}`
|
||||
const breakfast = `${intl.formatMessage({ id: "Breakfast" })}: ${summary.breakfast}`
|
||||
|
||||
@@ -1,62 +1,161 @@
|
||||
"use client"
|
||||
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import {
|
||||
BOOKING_CONFIRMATION_NUMBER,
|
||||
BookingStatusEnum,
|
||||
} from "@/constants/booking"
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import { toast } from "@/components/TempDesignSystem/Toasts"
|
||||
import { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import styles from "./payment.module.css"
|
||||
|
||||
import { PaymentProps } from "@/types/components/hotelReservation/selectRate/section"
|
||||
|
||||
const maxRetries = 40
|
||||
const retryInterval = 2000
|
||||
|
||||
export default function Payment({ hotel }: PaymentProps) {
|
||||
const router = useRouter()
|
||||
const lang = useLang()
|
||||
const intl = useIntl()
|
||||
const [confirmationNumber, setConfirmationNumber] = useState<string>("")
|
||||
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<string>("")
|
||||
|
||||
export default function Payment() {
|
||||
const initiateBooking = trpc.booking.booking.create.useMutation({
|
||||
onSuccess: (result) => {
|
||||
// TODO: Handle success, poll for payment link and redirect the user to the payment
|
||||
console.log("Res", result)
|
||||
if (result?.confirmationNumber) {
|
||||
// Planet doesn't support query params so we have to store values in session storage
|
||||
sessionStorage.setItem(
|
||||
BOOKING_CONFIRMATION_NUMBER,
|
||||
result.confirmationNumber
|
||||
)
|
||||
|
||||
setConfirmationNumber(result.confirmationNumber)
|
||||
} else {
|
||||
// TODO: add proper error message
|
||||
toast.error("Failed to create booking")
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
// TODO: Handle error
|
||||
console.log("Error")
|
||||
onError: (error) => {
|
||||
console.error("Error", error)
|
||||
// TODO: add proper error message
|
||||
toast.error("Failed to create booking")
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() =>
|
||||
// TODO: Use real values
|
||||
initiateBooking.mutate({
|
||||
hotelId: "811",
|
||||
checkInDate: "2024-12-10",
|
||||
checkOutDate: "2024-12-11",
|
||||
rooms: [
|
||||
{
|
||||
adults: 1,
|
||||
children: 0,
|
||||
rateCode: "SAVEEU",
|
||||
roomTypeCode: "QC",
|
||||
guest: {
|
||||
title: "Mr",
|
||||
firstName: "Test",
|
||||
lastName: "User",
|
||||
email: "test.user@scandichotels.com",
|
||||
phoneCountryCodePrefix: "string",
|
||||
phoneNumber: "string",
|
||||
countryCode: "string",
|
||||
},
|
||||
smsConfirmationRequested: true,
|
||||
},
|
||||
],
|
||||
payment: {
|
||||
cardHolder: {
|
||||
Email: "test.user@scandichotels.com",
|
||||
Name: "Test User",
|
||||
PhoneCountryCode: "",
|
||||
PhoneSubscriber: "",
|
||||
},
|
||||
success: "success/handle",
|
||||
error: "error/handle",
|
||||
cancel: "cancel/handle",
|
||||
const bookingStatus = useHandleBookingStatus(
|
||||
confirmationNumber,
|
||||
BookingStatusEnum.PaymentRegistered,
|
||||
maxRetries,
|
||||
retryInterval
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (bookingStatus?.data?.paymentUrl) {
|
||||
router.push(bookingStatus.data.paymentUrl)
|
||||
}
|
||||
}, [bookingStatus, router])
|
||||
|
||||
function handleSubmit() {
|
||||
initiateBooking.mutate({
|
||||
hotelId: hotel.operaId,
|
||||
checkInDate: "2024-12-10",
|
||||
checkOutDate: "2024-12-11",
|
||||
rooms: [
|
||||
{
|
||||
adults: 1,
|
||||
childrenAges: [],
|
||||
rateCode: "SAVEEU",
|
||||
roomTypeCode: "QC",
|
||||
guest: {
|
||||
title: "Mr",
|
||||
firstName: "Test",
|
||||
lastName: "User",
|
||||
email: "test.user@scandichotels.com",
|
||||
phoneCountryCodePrefix: "string",
|
||||
phoneNumber: "string",
|
||||
countryCode: "string",
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
Create booking
|
||||
</Button>
|
||||
packages: {
|
||||
breakfast: true,
|
||||
allergyFriendly: true,
|
||||
petFriendly: true,
|
||||
accessibility: true,
|
||||
},
|
||||
smsConfirmationRequested: true,
|
||||
},
|
||||
],
|
||||
payment: {
|
||||
paymentMethod: selectedPaymentMethod,
|
||||
cardHolder: {
|
||||
email: "test.user@scandichotels.com",
|
||||
name: "Test User",
|
||||
phoneCountryCode: "",
|
||||
phoneSubscriber: "",
|
||||
},
|
||||
success: `api/web/payment-callback/${lang}/success`,
|
||||
error: `api/web/payment-callback/${lang}/error`,
|
||||
cancel: `api/web/payment-callback/${lang}/cancel`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
initiateBooking.isPending ||
|
||||
(confirmationNumber && !bookingStatus.data?.paymentUrl)
|
||||
) {
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<div className={styles.paymentItemContainer}>
|
||||
<button
|
||||
className={styles.paymentItem}
|
||||
onClick={() => setSelectedPaymentMethod("card")}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="payment-method"
|
||||
id="card"
|
||||
value="card"
|
||||
checked={selectedPaymentMethod === "card"}
|
||||
/>
|
||||
<label htmlFor="card">card</label>
|
||||
</button>
|
||||
{hotel.merchantInformationData.alternatePaymentOptions.map(
|
||||
(paymentOption) => (
|
||||
<button
|
||||
key={paymentOption}
|
||||
className={styles.paymentItem}
|
||||
onClick={() => setSelectedPaymentMethod(paymentOption)}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="payment-method"
|
||||
id={paymentOption}
|
||||
value={paymentOption}
|
||||
checked={selectedPaymentMethod === paymentOption}
|
||||
/>
|
||||
<label htmlFor={paymentOption}>{paymentOption}</label>
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Button disabled={!selectedPaymentMethod} onClick={handleSubmit}>
|
||||
{intl.formatMessage({ id: "Complete booking & go to payment" })}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
.paymentItemContainer {
|
||||
max-width: 480px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x1);
|
||||
padding-bottom: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.paymentItem {
|
||||
background-color: var(--Base-Background-Normal);
|
||||
padding: var(--Spacing-x3);
|
||||
border: 1px solid var(--Base-Border-Normal);
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x2);
|
||||
cursor: pointer;
|
||||
}
|
||||
7
constants/booking.ts
Normal file
7
constants/booking.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum BookingStatusEnum {
|
||||
CreatedInOhip = "CreatedInOhip",
|
||||
PaymentRegistered = "PaymentRegistered",
|
||||
BookingCompleted = "BookingCompleted",
|
||||
}
|
||||
|
||||
export const BOOKING_CONFIRMATION_NUMBER = "bookingConfirmationNumber"
|
||||
@@ -28,4 +28,24 @@ export const selectHotelMap = {
|
||||
de: `${selectHotel.de}/map`,
|
||||
}
|
||||
|
||||
/** @type {import('@/types/routes').LangRoute} */
|
||||
export const payment = {
|
||||
en: `${hotelReservation.en}/payment`,
|
||||
sv: `${hotelReservation.sv}/betalning`,
|
||||
no: `${hotelReservation.no}/betaling`,
|
||||
fi: `${hotelReservation.fi}/maksu`,
|
||||
da: `${hotelReservation.da}/payment`,
|
||||
de: `${hotelReservation.de}/bezahlung`,
|
||||
}
|
||||
|
||||
/** @type {import('@/types/routes').LangRoute} */
|
||||
export const bookingConfirmation = {
|
||||
en: `${hotelReservation.en}/booking-confirmation`,
|
||||
sv: `${hotelReservation.sv}/bokningsbekraftelse`,
|
||||
no: `${hotelReservation.no}/booking-confirmation`,
|
||||
fi: `${hotelReservation.fi}/varausvahvistus`,
|
||||
da: `${hotelReservation.da}/booking-confirmation`,
|
||||
de: `${hotelReservation.de}/buchungsbesttigung`,
|
||||
}
|
||||
|
||||
export const bookingFlow = [...Object.values(hotelReservation)]
|
||||
|
||||
35
hooks/booking/useHandleBookingStatus.ts
Normal file
35
hooks/booking/useHandleBookingStatus.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
"use client"
|
||||
|
||||
import { BookingStatusEnum } from "@/constants/booking"
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
|
||||
export function useHandleBookingStatus(
|
||||
confirmationNumber: string | null,
|
||||
expectedStatus: BookingStatusEnum,
|
||||
maxRetries: number,
|
||||
retryInterval: number
|
||||
) {
|
||||
const query = trpc.booking.status.useQuery(
|
||||
{ confirmationNumber: confirmationNumber ?? "" },
|
||||
{
|
||||
enabled: !!confirmationNumber,
|
||||
refetchInterval: (query) => {
|
||||
if (query.state.error || query.state.dataUpdateCount >= maxRetries) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (query.state.data?.reservationStatus === expectedStatus) {
|
||||
return false
|
||||
}
|
||||
|
||||
return retryInterval
|
||||
},
|
||||
refetchIntervalInBackground: true,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
retry: false,
|
||||
}
|
||||
)
|
||||
|
||||
return query
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
"Any changes you've made will be lost.": "Alle ændringer, du har foretaget, går tabt.",
|
||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på, at du vil fjerne kortet, der slutter me {lastFourDigits} fra din medlemsprofil?",
|
||||
"Arrival date": "Ankomstdato",
|
||||
"as of today": "fra idag",
|
||||
"As our": "Som vores {level}",
|
||||
"As our Close Friend": "Som vores nære ven",
|
||||
"At latest": "Senest",
|
||||
@@ -23,14 +24,16 @@
|
||||
"Bed type": "Seng type",
|
||||
"Book": "Book",
|
||||
"Book reward night": "Book bonusnat",
|
||||
"Code / Voucher": "Bookingkoder / voucher",
|
||||
"Booking number": "Bookingnummer",
|
||||
"booking.nights": "{totalNights, plural, one {# nat} other {# nætter}}",
|
||||
"Breakfast": "Morgenmad",
|
||||
"Breakfast excluded": "Morgenmad ikke inkluderet",
|
||||
"Breakfast included": "Morgenmad inkluderet",
|
||||
"Bus terminal": "Busstation",
|
||||
"Business": "Forretning",
|
||||
"by": "inden",
|
||||
"Cancel": "Afbestille",
|
||||
"characters": "tegn",
|
||||
"Check in": "Check ind",
|
||||
"Check out": "Check ud",
|
||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tjek de kreditkort, der er gemt på din profil. Betal med et gemt kort, når du er logget ind for en mere jævn weboplevelse.",
|
||||
@@ -45,8 +48,10 @@
|
||||
"Close menu": "Luk menu",
|
||||
"Close my pages menu": "Luk mine sider menu",
|
||||
"Close the map": "Luk kortet",
|
||||
"Code / Voucher": "Bookingkoder / voucher",
|
||||
"Coming up": "Er lige om hjørnet",
|
||||
"Compare all levels": "Sammenlign alle niveauer",
|
||||
"Complete booking & go to payment": "Udfyld booking & gå til betaling",
|
||||
"Contact us": "Kontakt os",
|
||||
"Continue": "Blive ved",
|
||||
"Copyright all rights reserved": "Scandic AB Alle rettigheder forbeholdes",
|
||||
@@ -74,9 +79,9 @@
|
||||
"Explore all levels and benefits": "Udforsk alle niveauer og fordele",
|
||||
"Explore nearby": "Udforsk i nærheden",
|
||||
"Extras to your booking": "Tillæg til din booking",
|
||||
"FAQ": "Ofte stillede spørgsmål",
|
||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kreditkort. Prøv venligst igen senere.",
|
||||
"Fair": "Messe",
|
||||
"FAQ": "Ofte stillede spørgsmål",
|
||||
"Find booking": "Find booking",
|
||||
"Find hotels": "Find hotel",
|
||||
"Flexibility": "Fleksibilitet",
|
||||
@@ -87,17 +92,22 @@
|
||||
"Get inspired": "Bliv inspireret",
|
||||
"Go back to edit": "Gå tilbage til redigering",
|
||||
"Go back to overview": "Gå tilbage til oversigten",
|
||||
"Guests & Rooms": "Gæster & værelser",
|
||||
"Hi": "Hei",
|
||||
"Highest level": "Højeste niveau",
|
||||
"Hospital": "Hospital",
|
||||
"Hotel": "Hotel",
|
||||
"Hotel facilities": "Hotel faciliteter",
|
||||
"Hotel surroundings": "Hotel omgivelser",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "personer",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se værelsesdetaljer",
|
||||
"Hotels": "Hoteller",
|
||||
"How do you want to sleep?": "Hvordan vil du sove?",
|
||||
"How it works": "Hvordan det virker",
|
||||
"Image gallery": "Billedgalleri",
|
||||
"Join Scandic Friends": "Tilmeld dig Scandic Friends",
|
||||
"km to city center": "km til byens centrum",
|
||||
"Language": "Sprog",
|
||||
"Latest searches": "Seneste søgninger",
|
||||
"Level": "Niveau",
|
||||
@@ -124,9 +134,9 @@
|
||||
"Member price": "Medlemspris",
|
||||
"Member price from": "Medlemspris fra",
|
||||
"Members": "Medlemmer",
|
||||
"Membership cards": "Medlemskort",
|
||||
"Membership ID": "Medlems-id",
|
||||
"Membership ID copied to clipboard": "Medlems-ID kopieret til udklipsholder",
|
||||
"Membership cards": "Medlemskort",
|
||||
"Menu": "Menu",
|
||||
"Modify": "Ændre",
|
||||
"Month": "Måned",
|
||||
@@ -141,6 +151,9 @@
|
||||
"Nearby companies": "Nærliggende virksomheder",
|
||||
"New password": "Nyt kodeord",
|
||||
"Next": "Næste",
|
||||
"next level:": "Næste niveau:",
|
||||
"night": "nat",
|
||||
"nights": "nætter",
|
||||
"Nights needed to level up": "Nætter nødvendige for at komme i niveau",
|
||||
"No content published": "Intet indhold offentliggjort",
|
||||
"No matching location found": "Der blev ikke fundet nogen matchende placering",
|
||||
@@ -151,11 +164,13 @@
|
||||
"Non-refundable": "Ikke-refunderbart",
|
||||
"Not found": "Ikke fundet",
|
||||
"Nr night, nr adult": "{nights, number} nat, {adults, number} voksen",
|
||||
"number": "nummer",
|
||||
"On your journey": "På din rejse",
|
||||
"Open": "Åben",
|
||||
"Open language menu": "Åbn sprogmenuen",
|
||||
"Open menu": "Åbn menuen",
|
||||
"Open my pages menu": "Åbn mine sider menuen",
|
||||
"or": "eller",
|
||||
"Overview": "Oversigt",
|
||||
"Parking": "Parkering",
|
||||
"Parking / Garage": "Parkering / Garage",
|
||||
@@ -167,6 +182,7 @@
|
||||
"Phone is required": "Telefonnummer er påkrævet",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please enter a valid phone number": "Indtast venligst et gyldigt telefonnummer",
|
||||
"points": "Point",
|
||||
"Points": "Point",
|
||||
"Points being calculated": "Point udregnes",
|
||||
"Points earned prior to May 1, 2021": "Point optjent inden 1. maj 2021",
|
||||
@@ -185,7 +201,6 @@
|
||||
"Room & Terms": "Værelse & Vilkår",
|
||||
"Room facilities": "Værelsesfaciliteter",
|
||||
"Rooms": "Værelser",
|
||||
"Guests & Rooms": "Gæster & værelser",
|
||||
"Save": "Gemme",
|
||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||
@@ -210,25 +225,29 @@
|
||||
"Something went wrong and we couldn't add your card. Please try again later.": "Noget gik galt, og vi kunne ikke tilføje dit kort. Prøv venligst igen senere.",
|
||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Noget gik galt, og vi kunne ikke fjerne dit kort. Prøv venligst igen senere.",
|
||||
"Something went wrong!": "Noget gik galt!",
|
||||
"special character": "speciel karakter",
|
||||
"spendable points expiring by": "{points} Brugbare point udløber den {date}",
|
||||
"Sports": "Sport",
|
||||
"Standard price": "Standardpris",
|
||||
"Street": "Gade",
|
||||
"Successfully updated profile!": "Profilen er opdateret med succes!",
|
||||
"Summary": "Opsummering",
|
||||
"TUI Points": "TUI Points",
|
||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Fortæl os, hvilke oplysninger og opdateringer du gerne vil modtage, og hvordan, ved at klikke på linket nedenfor.",
|
||||
"Thank you": "Tak",
|
||||
"Theatre": "Teater",
|
||||
"There are no transactions to display": "Der er ingen transaktioner at vise",
|
||||
"Things nearby HOTEL_NAME": "Ting i nærheden af {hotelName}",
|
||||
"to": "til",
|
||||
"Total Points": "Samlet antal point",
|
||||
"Tourist": "Turist",
|
||||
"Transaction date": "Overførselsdato",
|
||||
"Transactions": "Transaktioner",
|
||||
"Transportations": "Transport",
|
||||
"Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)",
|
||||
"TUI Points": "TUI Points",
|
||||
"Type of bed": "Sengtype",
|
||||
"Type of room": "Værelsestype",
|
||||
"uppercase letter": "stort bogstav",
|
||||
"Use bonus cheque": "Brug Bonus Cheque",
|
||||
"Use code/voucher": "Brug kode/voucher",
|
||||
"User information": "Brugeroplysninger",
|
||||
@@ -255,9 +274,9 @@
|
||||
"You canceled adding a new credit card.": "Du har annulleret tilføjelsen af et nyt kreditkort.",
|
||||
"You have no previous stays.": "Du har ingen tidligere ophold.",
|
||||
"You have no upcoming stays.": "Du har ingen kommende ophold.",
|
||||
"Your Challenges Conquer & Earn!": "Dine udfordringer Overvind og tjen!",
|
||||
"Your card was successfully removed!": "Dit kort blev fjernet!",
|
||||
"Your card was successfully saved!": "Dit kort blev gemt!",
|
||||
"Your Challenges Conquer & Earn!": "Dine udfordringer Overvind og tjen!",
|
||||
"Your current level": "Dit nuværende niveau",
|
||||
"Your details": "Dine oplysninger",
|
||||
"Your level": "Dit niveau",
|
||||
@@ -265,23 +284,5 @@
|
||||
"Zip code": "Postnummer",
|
||||
"Zoo": "Zoo",
|
||||
"Zoom in": "Zoom ind",
|
||||
"Zoom out": "Zoom ud",
|
||||
"as of today": "pr. dags dato",
|
||||
"booking.nights": "{totalNights, plural, one {# nat} other {# nætter}}",
|
||||
"by": "inden",
|
||||
"characters": "tegn",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "personer",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se værelsesdetaljer",
|
||||
"km to city center": "km til byens centrum",
|
||||
"next level:": "Næste niveau:",
|
||||
"night": "nat",
|
||||
"nights": "nætter",
|
||||
"number": "nummer",
|
||||
"or": "eller",
|
||||
"points": "Point",
|
||||
"special character": "speciel karakter",
|
||||
"spendable points expiring by": "{points} Brugbare point udløber den {date}",
|
||||
"to": "til",
|
||||
"uppercase letter": "stort bogstav"
|
||||
"Zoom out": "Zoom ud"
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Any changes you've made will be lost.": "Alle Änderungen, die Sie vorgenommen haben, gehen verloren.",
|
||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Möchten Sie die Karte mit der Endung {lastFourDigits} wirklich aus Ihrem Mitgliedsprofil entfernen?",
|
||||
"Arrival date": "Ankunftsdatum",
|
||||
"as of today": "Stand heute",
|
||||
"As our": "Als unser {level}",
|
||||
"As our Close Friend": "Als unser enger Freund",
|
||||
"At latest": "Spätestens",
|
||||
@@ -23,14 +24,16 @@
|
||||
"Bed type": "Bettentyp",
|
||||
"Book": "Buchen",
|
||||
"Book reward night": "Bonusnacht buchen",
|
||||
"Code / Voucher": "Buchungscodes / Gutscheine",
|
||||
"Booking number": "Buchungsnummer",
|
||||
"booking.nights": "{totalNights, plural, one {# nacht} other {# Nächte}}",
|
||||
"Breakfast": "Frühstück",
|
||||
"Breakfast excluded": "Frühstück nicht inbegriffen",
|
||||
"Breakfast included": "Frühstück inbegriffen",
|
||||
"Bus terminal": "Busbahnhof",
|
||||
"Business": "Geschäft",
|
||||
"by": "bis",
|
||||
"Cancel": "Stornieren",
|
||||
"characters": "figuren",
|
||||
"Check in": "Einchecken",
|
||||
"Check out": "Auschecken",
|
||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sehen Sie sich die in Ihrem Profil gespeicherten Kreditkarten an. Bezahlen Sie mit einer gespeicherten Karte, wenn Sie angemeldet sind, für ein reibungsloseres Web-Erlebnis.",
|
||||
@@ -45,8 +48,10 @@
|
||||
"Close menu": "Menü schließen",
|
||||
"Close my pages menu": "Meine Seiten Menü schließen",
|
||||
"Close the map": "Karte schließen",
|
||||
"Code / Voucher": "Buchungscodes / Gutscheine",
|
||||
"Coming up": "Demnächst",
|
||||
"Compare all levels": "Vergleichen Sie alle Levels",
|
||||
"Complete booking & go to payment": "Buchung abschließen & zur Bezahlung gehen",
|
||||
"Contact us": "Kontaktieren Sie uns",
|
||||
"Continue": "Weitermachen",
|
||||
"Copyright all rights reserved": "Scandic AB Alle Rechte vorbehalten",
|
||||
@@ -74,9 +79,9 @@
|
||||
"Explore all levels and benefits": "Entdecken Sie alle Levels und Vorteile",
|
||||
"Explore nearby": "Erkunden Sie die Umgebung",
|
||||
"Extras to your booking": "Extras zu Ihrer Buchung",
|
||||
"FAQ": "Häufig gestellte Fragen",
|
||||
"Failed to delete credit card, please try again later.": "Kreditkarte konnte nicht gelöscht werden. Bitte versuchen Sie es später noch einmal.",
|
||||
"Fair": "Messe",
|
||||
"FAQ": "Häufig gestellte Fragen",
|
||||
"Find booking": "Buchung finden",
|
||||
"Find hotels": "Hotels finden",
|
||||
"Flexibility": "Flexibilität",
|
||||
@@ -87,17 +92,22 @@
|
||||
"Get inspired": "Lassen Sie sich inspieren",
|
||||
"Go back to edit": "Zurück zum Bearbeiten",
|
||||
"Go back to overview": "Zurück zur Übersicht",
|
||||
"Guests & Rooms": "Gäste & Zimmer",
|
||||
"Hi": "Hallo",
|
||||
"Highest level": "Höchstes Level",
|
||||
"Hospital": "Krankenhaus",
|
||||
"Hotel": "Hotel",
|
||||
"Hotel facilities": "Hotel-Infos",
|
||||
"Hotel surroundings": "Umgebung des Hotels",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "personen",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Zimmerdetails ansehen",
|
||||
"Hotels": "Hotels",
|
||||
"How do you want to sleep?": "Wie möchtest du schlafen?",
|
||||
"How it works": "Wie es funktioniert",
|
||||
"Image gallery": "Bildergalerie",
|
||||
"Join Scandic Friends": "Treten Sie Scandic Friends bei",
|
||||
"km to city center": "km bis zum Stadtzentrum",
|
||||
"Language": "Sprache",
|
||||
"Latest searches": "Letzte Suchanfragen",
|
||||
"Level": "Level",
|
||||
@@ -124,9 +134,9 @@
|
||||
"Member price": "Mitgliederpreis",
|
||||
"Member price from": "Mitgliederpreis ab",
|
||||
"Members": "Mitglieder",
|
||||
"Membership cards": "Mitgliedskarten",
|
||||
"Membership ID": "Mitglieds-ID",
|
||||
"Membership ID copied to clipboard": "Mitglieds-ID in die Zwischenablage kopiert",
|
||||
"Membership cards": "Mitgliedskarten",
|
||||
"Menu": "Menu",
|
||||
"Modify": "Ändern",
|
||||
"Month": "Monat",
|
||||
@@ -141,6 +151,9 @@
|
||||
"Nearby companies": "Nahe gelegene Unternehmen",
|
||||
"New password": "Neues Kennwort",
|
||||
"Next": "Nächste",
|
||||
"next level:": "Nächstes Level:",
|
||||
"night": "nacht",
|
||||
"nights": "Nächte",
|
||||
"Nights needed to level up": "Nächte, die zum Levelaufstieg benötigt werden",
|
||||
"No content published": "Kein Inhalt veröffentlicht",
|
||||
"No matching location found": "Kein passender Standort gefunden",
|
||||
@@ -151,11 +164,13 @@
|
||||
"Non-refundable": "Nicht erstattungsfähig",
|
||||
"Not found": "Nicht gefunden",
|
||||
"Nr night, nr adult": "{nights, number} Nacht, {adults, number} Erwachsener",
|
||||
"number": "nummer",
|
||||
"On your journey": "Auf deiner Reise",
|
||||
"Open": "Offen",
|
||||
"Open language menu": "Sprachmenü öffnen",
|
||||
"Open menu": "Menü öffnen",
|
||||
"Open my pages menu": "Meine Seiten Menü öffnen",
|
||||
"or": "oder",
|
||||
"Overview": "Übersicht",
|
||||
"Parking": "Parken",
|
||||
"Parking / Garage": "Parken / Garage",
|
||||
@@ -166,6 +181,7 @@
|
||||
"Phone is required": "Telefon ist erforderlich",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please enter a valid phone number": "Bitte geben Sie eine gültige Telefonnummer ein",
|
||||
"points": "Punkte",
|
||||
"Points": "Punkte",
|
||||
"Points being calculated": "Punkte werden berechnet",
|
||||
"Points earned prior to May 1, 2021": "Zusammengeführte Punkte vor dem 1. Mai 2021",
|
||||
@@ -184,7 +200,6 @@
|
||||
"Room & Terms": "Zimmer & Bedingungen",
|
||||
"Room facilities": "Zimmerausstattung",
|
||||
"Rooms": "Räume",
|
||||
"Guests & Rooms": "Gäste & Zimmer",
|
||||
"Save": "Speichern",
|
||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||
@@ -209,25 +224,29 @@
|
||||
"Something went wrong and we couldn't add your card. Please try again later.": "Ein Fehler ist aufgetreten und wir konnten Ihre Karte nicht hinzufügen. Bitte versuchen Sie es später erneut.",
|
||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Ein Fehler ist aufgetreten und wir konnten Ihre Karte nicht entfernen. Bitte versuchen Sie es später noch einmal.",
|
||||
"Something went wrong!": "Etwas ist schief gelaufen!",
|
||||
"special character": "sonderzeichen",
|
||||
"spendable points expiring by": "{points} Einlösbare punkte verfallen bis zum {date}",
|
||||
"Sports": "Sport",
|
||||
"Standard price": "Standardpreis",
|
||||
"Street": "Straße",
|
||||
"Successfully updated profile!": "Profil erfolgreich aktualisiert!",
|
||||
"Summary": "Zusammenfassung",
|
||||
"TUI Points": "TUI Points",
|
||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Teilen Sie uns mit, welche Informationen und Updates Sie wie erhalten möchten, indem Sie auf den unten stehenden Link klicken.",
|
||||
"Thank you": "Danke",
|
||||
"Theatre": "Theater",
|
||||
"There are no transactions to display": "Es sind keine Transaktionen zum Anzeigen vorhanden",
|
||||
"Things nearby HOTEL_NAME": "Dinge in der Nähe von {hotelName}",
|
||||
"to": "zu",
|
||||
"Total Points": "Gesamtpunktzahl",
|
||||
"Tourist": "Tourist",
|
||||
"Transaction date": "Transaktionsdatum",
|
||||
"Transactions": "Transaktionen",
|
||||
"Transportations": "Transportmittel",
|
||||
"Tripadvisor reviews": "{rating} ({count} Bewertungen auf Tripadvisor)",
|
||||
"TUI Points": "TUI Points",
|
||||
"Type of bed": "Bettentyp",
|
||||
"Type of room": "Zimmerart",
|
||||
"uppercase letter": "großbuchstabe",
|
||||
"Use bonus cheque": "Bonusscheck nutzen",
|
||||
"Use code/voucher": "Code/Gutschein nutzen",
|
||||
"User information": "Nutzerinformation",
|
||||
@@ -254,9 +273,9 @@
|
||||
"You canceled adding a new credit card.": "Sie haben das Hinzufügen einer neuen Kreditkarte abgebrochen.",
|
||||
"You have no previous stays.": "Sie haben keine vorherigen Aufenthalte.",
|
||||
"You have no upcoming stays.": "Sie haben keine bevorstehenden Aufenthalte.",
|
||||
"Your Challenges Conquer & Earn!": "Meistern Sie Ihre Herausforderungen und verdienen Sie Geld!",
|
||||
"Your card was successfully removed!": "Ihre Karte wurde erfolgreich entfernt!",
|
||||
"Your card was successfully saved!": "Ihre Karte wurde erfolgreich gespeichert!",
|
||||
"Your Challenges Conquer & Earn!": "Meistern Sie Ihre Herausforderungen und verdienen Sie Geld!",
|
||||
"Your current level": "Ihr aktuelles Level",
|
||||
"Your details": "Ihre Angaben",
|
||||
"Your level": "Dein level",
|
||||
@@ -264,23 +283,5 @@
|
||||
"Zip code": "PLZ",
|
||||
"Zoo": "Zoo",
|
||||
"Zoom in": "Vergrößern",
|
||||
"Zoom out": "Verkleinern",
|
||||
"as of today": "Stand heute",
|
||||
"booking.nights": "{totalNights, plural, one {# nacht} other {# Nächte}}",
|
||||
"by": "bis",
|
||||
"characters": "figuren",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "personen",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Zimmerdetails ansehen",
|
||||
"km to city center": "km bis zum Stadtzentrum",
|
||||
"next level:": "Nächstes Level:",
|
||||
"night": "nacht",
|
||||
"nights": "Nächte",
|
||||
"number": "nummer",
|
||||
"or": "oder",
|
||||
"points": "Punkte",
|
||||
"special character": "sonderzeichen",
|
||||
"spendable points expiring by": "{points} Einlösbare punkte verfallen bis zum {date}",
|
||||
"to": "zu",
|
||||
"uppercase letter": "großbuchstabe"
|
||||
"Zoom out": "Verkleinern"
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Any changes you've made will be lost.": "Any changes you've made will be lost.",
|
||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?",
|
||||
"Arrival date": "Arrival date",
|
||||
"as of today": "as of today",
|
||||
"As our": "As our {level}",
|
||||
"As our Close Friend": "As our Close Friend",
|
||||
"At latest": "At latest",
|
||||
@@ -23,14 +24,18 @@
|
||||
"Bed type": "Bed type",
|
||||
"Book": "Book",
|
||||
"Book reward night": "Book reward night",
|
||||
"Code / Voucher": "Code / Voucher",
|
||||
"Booking number": "Booking number",
|
||||
"booking.adults": "{totalAdults, plural, one {# adult} other {# adults}}",
|
||||
"booking.nights": "{totalNights, plural, one {# night} other {# nights}}",
|
||||
"booking.rooms": "{totalRooms, plural, one {# room} other {# rooms}}",
|
||||
"Breakfast": "Breakfast",
|
||||
"Breakfast excluded": "Breakfast excluded",
|
||||
"Breakfast included": "Breakfast included",
|
||||
"Bus terminal": "Bus terminal",
|
||||
"Business": "Business",
|
||||
"by": "by",
|
||||
"Cancel": "Cancel",
|
||||
"characters": "characters",
|
||||
"Check in": "Check in",
|
||||
"Check out": "Check out",
|
||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.",
|
||||
@@ -45,8 +50,10 @@
|
||||
"Close menu": "Close menu",
|
||||
"Close my pages menu": "Close my pages menu",
|
||||
"Close the map": "Close the map",
|
||||
"Code / Voucher": "Code / Voucher",
|
||||
"Coming up": "Coming up",
|
||||
"Compare all levels": "Compare all levels",
|
||||
"Complete booking & go to payment": "Complete booking & go to payment",
|
||||
"Contact us": "Contact us",
|
||||
"Continue": "Continue",
|
||||
"Copyright all rights reserved": "Scandic AB All rights reserved",
|
||||
@@ -59,6 +66,7 @@
|
||||
"Date of Birth": "Date of Birth",
|
||||
"Day": "Day",
|
||||
"Description": "Description",
|
||||
"Destination": "Destination",
|
||||
"Destinations & hotels": "Destinations & hotels",
|
||||
"Destination": "Destination",
|
||||
"Disabled booking options header": "We're sorry",
|
||||
@@ -75,9 +83,9 @@
|
||||
"Explore all levels and benefits": "Explore all levels and benefits",
|
||||
"Explore nearby": "Explore nearby",
|
||||
"Extras to your booking": "Extras to your booking",
|
||||
"FAQ": "FAQ",
|
||||
"Failed to delete credit card, please try again later.": "Failed to delete credit card, please try again later.",
|
||||
"Fair": "Fair",
|
||||
"FAQ": "FAQ",
|
||||
"Find booking": "Find booking",
|
||||
"Find hotels": "Find hotels",
|
||||
"Flexibility": "Flexibility",
|
||||
@@ -88,17 +96,22 @@
|
||||
"Get inspired": "Get inspired",
|
||||
"Go back to edit": "Go back to edit",
|
||||
"Go back to overview": "Go back to overview",
|
||||
"Guests & Rooms": "Guests & Rooms",
|
||||
"Hi": "Hi",
|
||||
"Highest level": "Highest level",
|
||||
"Hospital": "Hospital",
|
||||
"Hotel": "Hotel",
|
||||
"Hotel facilities": "Hotel facilities",
|
||||
"Hotel surroundings": "Hotel surroundings",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "persons",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
|
||||
"Hotels": "Hotels",
|
||||
"How do you want to sleep?": "How do you want to sleep?",
|
||||
"How it works": "How it works",
|
||||
"Image gallery": "Image gallery",
|
||||
"Join Scandic Friends": "Join Scandic Friends",
|
||||
"km to city center": "km to city center",
|
||||
"Language": "Language",
|
||||
"Latest searches": "Latest searches",
|
||||
"Level": "Level",
|
||||
@@ -125,9 +138,9 @@
|
||||
"Member price": "Member price",
|
||||
"Member price from": "Member price from",
|
||||
"Members": "Members",
|
||||
"Membership cards": "Membership cards",
|
||||
"Membership ID": "Membership ID",
|
||||
"Membership ID copied to clipboard": "Membership ID copied to clipboard",
|
||||
"Membership cards": "Membership cards",
|
||||
"Menu": "Menu",
|
||||
"Modify": "Modify",
|
||||
"Month": "Month",
|
||||
@@ -142,6 +155,9 @@
|
||||
"Nearby companies": "Nearby companies",
|
||||
"New password": "New password",
|
||||
"Next": "Next",
|
||||
"next level:": "next level:",
|
||||
"night": "night",
|
||||
"nights": "nights",
|
||||
"Nights needed to level up": "Nights needed to level up",
|
||||
"No content published": "No content published",
|
||||
"No matching location found": "No matching location found",
|
||||
@@ -152,11 +168,13 @@
|
||||
"Non-refundable": "Non-refundable",
|
||||
"Not found": "Not found",
|
||||
"Nr night, nr adult": "{nights, number} night, {adults, number} adult",
|
||||
"number": "number",
|
||||
"On your journey": "On your journey",
|
||||
"Open": "Open",
|
||||
"Open language menu": "Open language menu",
|
||||
"Open menu": "Open menu",
|
||||
"Open my pages menu": "Open my pages menu",
|
||||
"or": "or",
|
||||
"Overview": "Overview",
|
||||
"Parking": "Parking",
|
||||
"Parking / Garage": "Parking / Garage",
|
||||
@@ -168,6 +186,7 @@
|
||||
"Phone is required": "Phone is required",
|
||||
"Phone number": "Phone number",
|
||||
"Please enter a valid phone number": "Please enter a valid phone number",
|
||||
"points": "Points",
|
||||
"Points": "Points",
|
||||
"Points being calculated": "Points being calculated",
|
||||
"Points earned prior to May 1, 2021": "Points earned prior to May 1, 2021",
|
||||
@@ -186,12 +205,11 @@
|
||||
"Room & Terms": "Room & Terms",
|
||||
"Room facilities": "Room facilities",
|
||||
"Rooms": "Rooms",
|
||||
"Guests & Rooms": "Guests & Rooms",
|
||||
"Save": "Save",
|
||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||
"See all photos": "See all photos",
|
||||
"Search": "Search",
|
||||
"See all photos": "See all photos",
|
||||
"See hotel details": "See hotel details",
|
||||
"See room details": "See room details",
|
||||
"See rooms": "See rooms",
|
||||
@@ -212,25 +230,29 @@
|
||||
"Something went wrong and we couldn't add your card. Please try again later.": "Something went wrong and we couldn't add your card. Please try again later.",
|
||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Something went wrong and we couldn't remove your card. Please try again later.",
|
||||
"Something went wrong!": "Something went wrong!",
|
||||
"special character": "special character",
|
||||
"spendable points expiring by": "{points} spendable points expiring by {date}",
|
||||
"Sports": "Sports",
|
||||
"Standard price": "Standard price",
|
||||
"Street": "Street",
|
||||
"Successfully updated profile!": "Successfully updated profile!",
|
||||
"Summary": "Summary",
|
||||
"TUI Points": "TUI Points",
|
||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Tell us what information and updates you'd like to receive, and how, by clicking the link below.",
|
||||
"Thank you": "Thank you",
|
||||
"Theatre": "Theatre",
|
||||
"There are no transactions to display": "There are no transactions to display",
|
||||
"Things nearby HOTEL_NAME": "Things nearby {hotelName}",
|
||||
"to": "to",
|
||||
"Total Points": "Total Points",
|
||||
"Tourist": "Tourist",
|
||||
"Transaction date": "Transaction date",
|
||||
"Transactions": "Transactions",
|
||||
"Transportations": "Transportations",
|
||||
"Tripadvisor reviews": "{rating} ({count} reviews on Tripadvisor)",
|
||||
"TUI Points": "TUI Points",
|
||||
"Type of bed": "Type of bed",
|
||||
"Type of room": "Type of room",
|
||||
"uppercase letter": "uppercase letter",
|
||||
"Use bonus cheque": "Use bonus cheque",
|
||||
"Use code/voucher": "Use code/voucher",
|
||||
"User information": "User information",
|
||||
@@ -257,9 +279,9 @@
|
||||
"You canceled adding a new credit card.": "You canceled adding a new credit card.",
|
||||
"You have no previous stays.": "You have no previous stays.",
|
||||
"You have no upcoming stays.": "You have no upcoming stays.",
|
||||
"Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!",
|
||||
"Your card was successfully removed!": "Your card was successfully removed!",
|
||||
"Your card was successfully saved!": "Your card was successfully saved!",
|
||||
"Your Challenges Conquer & Earn!": "Your Challenges Conquer & Earn!",
|
||||
"Your current level": "Your current level",
|
||||
"Your details": "Your details",
|
||||
"Your level": "Your level",
|
||||
@@ -267,25 +289,5 @@
|
||||
"Zip code": "Zip code",
|
||||
"Zoo": "Zoo",
|
||||
"Zoom in": "Zoom in",
|
||||
"Zoom out": "Zoom out",
|
||||
"as of today": "as of today",
|
||||
"booking.adults": "{totalAdults, plural, one {# adult} other {# adults}}",
|
||||
"booking.nights": "{totalNights, plural, one {# night} other {# nights}}",
|
||||
"booking.rooms": "{totalRooms, plural, one {# room} other {# rooms}}",
|
||||
"by": "by",
|
||||
"characters": "characters",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "persons",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
|
||||
"km to city center": "km to city center",
|
||||
"next level:": "next level:",
|
||||
"night": "night",
|
||||
"nights": "nights",
|
||||
"number": "number",
|
||||
"or": "or",
|
||||
"points": "Points",
|
||||
"special character": "special character",
|
||||
"spendable points expiring by": "{points} spendable points expiring by {date}",
|
||||
"to": "to",
|
||||
"uppercase letter": "uppercase letter"
|
||||
"Zoom out": "Zoom out"
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Any changes you've made will be lost.": "Kaikki tekemäsi muutokset menetetään.",
|
||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Haluatko varmasti poistaa kortin, joka päättyy numeroon {lastFourDigits} jäsenprofiilistasi?",
|
||||
"Arrival date": "Saapumispäivä",
|
||||
"as of today": "tänään",
|
||||
"As our": "{level}-etu",
|
||||
"As our Close Friend": "Läheisenä ystävänämme",
|
||||
"At latest": "Viimeistään",
|
||||
@@ -24,12 +25,15 @@
|
||||
"Book": "Varaa",
|
||||
"Book reward night": "Kirjapalkinto-ilta",
|
||||
"Booking number": "Varausnumero",
|
||||
"booking.nights": "{totalNights, plural, one {# yö} other {# yötä}}",
|
||||
"Breakfast": "Aamiainen",
|
||||
"Breakfast excluded": "Aamiainen ei sisälly",
|
||||
"Breakfast included": "Aamiainen sisältyy",
|
||||
"Bus terminal": "Bussiasema",
|
||||
"Business": "Business",
|
||||
"by": "mennessä",
|
||||
"Cancel": "Peruuttaa",
|
||||
"characters": "hahmoja",
|
||||
"Check in": "Sisäänkirjautuminen",
|
||||
"Check out": "Uloskirjautuminen",
|
||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Tarkista profiiliisi tallennetut luottokortit. Maksa tallennetulla kortilla kirjautuneena, jotta verkkokokemus on sujuvampi.",
|
||||
@@ -47,6 +51,7 @@
|
||||
"Code / Voucher": "Varauskoodit / kupongit",
|
||||
"Coming up": "Tulossa",
|
||||
"Compare all levels": "Vertaa kaikkia tasoja",
|
||||
"Complete booking & go to payment": "Täydennä varaus & siirry maksamaan",
|
||||
"Contact us": "Ota meihin yhteyttä",
|
||||
"Continue": "Jatkaa",
|
||||
"Copyright all rights reserved": "Scandic AB Kaikki oikeudet pidätetään",
|
||||
@@ -74,9 +79,9 @@
|
||||
"Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin",
|
||||
"Explore nearby": "Tutustu lähialueeseen",
|
||||
"Extras to your booking": "Varauksessa lisäpalveluita",
|
||||
"FAQ": "UKK",
|
||||
"Failed to delete credit card, please try again later.": "Luottokortin poistaminen epäonnistui, yritä myöhemmin uudelleen.",
|
||||
"Fair": "Messukeskus",
|
||||
"FAQ": "UKK",
|
||||
"Find booking": "Etsi varaus",
|
||||
"Find hotels": "Etsi hotelleja",
|
||||
"Flexibility": "Joustavuus",
|
||||
@@ -94,11 +99,15 @@
|
||||
"Hotel": "Hotelli",
|
||||
"Hotel facilities": "Hotellin palvelut",
|
||||
"Hotel surroundings": "Hotellin ympäristö",
|
||||
"hotelPages.rooms.roomCard.person": "henkilö",
|
||||
"hotelPages.rooms.roomCard.persons": "Henkilöä",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Katso huoneen tiedot",
|
||||
"Hotels": "Hotellit",
|
||||
"How do you want to sleep?": "Kuinka haluat nukkua?",
|
||||
"How it works": "Kuinka se toimii",
|
||||
"Image gallery": "Kuvagalleria",
|
||||
"Join Scandic Friends": "Liity jäseneksi",
|
||||
"km to city center": "km keskustaan",
|
||||
"Language": "Kieli",
|
||||
"Latest searches": "Viimeisimmät haut",
|
||||
"Level": "Level",
|
||||
@@ -125,9 +134,9 @@
|
||||
"Member price": "Jäsenhinta",
|
||||
"Member price from": "Jäsenhinta alkaen",
|
||||
"Members": "Jäsenet",
|
||||
"Membership cards": "Jäsenkortit",
|
||||
"Membership ID": "Jäsentunnus",
|
||||
"Membership ID copied to clipboard": "Jäsenyystunnus kopioitu leikepöydälle",
|
||||
"Membership cards": "Jäsenkortit",
|
||||
"Menu": "Valikko",
|
||||
"Modify": "Muokkaa",
|
||||
"Month": "Kuukausi",
|
||||
@@ -142,6 +151,9 @@
|
||||
"Nearby companies": "Läheiset yritykset",
|
||||
"New password": "Uusi salasana",
|
||||
"Next": "Seuraava",
|
||||
"next level:": "pistettä tasolle:",
|
||||
"night": "yö",
|
||||
"nights": "yötä",
|
||||
"Nights needed to level up": "Yöt, joita tarvitaan tasolle",
|
||||
"No content published": "Ei julkaistua sisältöä",
|
||||
"No matching location found": "Vastaavaa sijaintia ei löytynyt",
|
||||
@@ -152,11 +164,13 @@
|
||||
"Non-refundable": "Ei palautettavissa",
|
||||
"Not found": "Ei löydetty",
|
||||
"Nr night, nr adult": "{nights, number} yö, {adults, number} aikuinen",
|
||||
"number": "määrä",
|
||||
"On your journey": "Matkallasi",
|
||||
"Open": "Avata",
|
||||
"Open language menu": "Avaa kielivalikko",
|
||||
"Open menu": "Avaa valikko",
|
||||
"Open my pages menu": "Avaa omat sivut -valikko",
|
||||
"or": "tai",
|
||||
"Overview": "Yleiskatsaus",
|
||||
"Parking": "Pysäköinti",
|
||||
"Parking / Garage": "Pysäköinti / Autotalli",
|
||||
@@ -168,6 +182,7 @@
|
||||
"Phone is required": "Puhelin vaaditaan",
|
||||
"Phone number": "Puhelinnumero",
|
||||
"Please enter a valid phone number": "Ole hyvä ja näppäile voimassaoleva puhelinnumero",
|
||||
"points": "pistettä",
|
||||
"Points": "Pisteet",
|
||||
"Points being calculated": "Pisteitä lasketaan",
|
||||
"Points earned prior to May 1, 2021": "Pisteet, jotka ansaittu ennen 1.5.2021",
|
||||
@@ -210,25 +225,29 @@
|
||||
"Something went wrong and we couldn't add your card. Please try again later.": "Jotain meni pieleen, emmekä voineet lisätä korttiasi. Yritä myöhemmin uudelleen.",
|
||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Jotain meni pieleen, emmekä voineet poistaa korttiasi. Yritä myöhemmin uudelleen.",
|
||||
"Something went wrong!": "Jotain meni pieleen!",
|
||||
"special character": "erikoishahmo",
|
||||
"spendable points expiring by": "{points} pistettä vanhenee {date} mennessä",
|
||||
"Sports": "Urheilu",
|
||||
"Standard price": "Normaali hinta",
|
||||
"Street": "Katu",
|
||||
"Successfully updated profile!": "Profiilin päivitys onnistui!",
|
||||
"Summary": "Yhteenveto",
|
||||
"TUI Points": "TUI Points",
|
||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Kerro meille, mitä tietoja ja päivityksiä haluat saada ja miten, napsauttamalla alla olevaa linkkiä.",
|
||||
"Thank you": "Kiitos",
|
||||
"Theatre": "Teatteri",
|
||||
"There are no transactions to display": "Näytettäviä tapahtumia ei ole",
|
||||
"Things nearby HOTEL_NAME": "Lähellä olevia asioita {hotelName}",
|
||||
"to": "to",
|
||||
"Total Points": "Kokonaispisteet",
|
||||
"Tourist": "Turisti",
|
||||
"Transaction date": "Tapahtuman päivämäärä",
|
||||
"Transactions": "Tapahtumat",
|
||||
"Transportations": "Kuljetukset",
|
||||
"Tripadvisor reviews": "{rating} ({count} arvostelua TripAdvisorissa)",
|
||||
"TUI Points": "TUI Points",
|
||||
"Type of bed": "Vuodetyyppi",
|
||||
"Type of room": "Huonetyyppi",
|
||||
"uppercase letter": "iso kirjain",
|
||||
"Use bonus cheque": "Käytä bonussekkiä",
|
||||
"Use code/voucher": "Käytä koodia/voucheria",
|
||||
"User information": "Käyttäjän tiedot",
|
||||
@@ -255,9 +274,9 @@
|
||||
"You canceled adding a new credit card.": "Peruutit uuden luottokortin lisäämisen.",
|
||||
"You have no previous stays.": "Sinulla ei ole aiempia majoituksia.",
|
||||
"You have no upcoming stays.": "Sinulla ei ole tulevia majoituksia.",
|
||||
"Your Challenges Conquer & Earn!": "Voita ja ansaitse haasteesi!",
|
||||
"Your card was successfully removed!": "Korttisi poistettiin onnistuneesti!",
|
||||
"Your card was successfully saved!": "Korttisi tallennettu onnistuneesti!",
|
||||
"Your Challenges Conquer & Earn!": "Voita ja ansaitse haasteesi!",
|
||||
"Your current level": "Nykyinen tasosi",
|
||||
"Your details": "Tietosi",
|
||||
"Your level": "Tasosi",
|
||||
@@ -265,23 +284,5 @@
|
||||
"Zip code": "Postinumero",
|
||||
"Zoo": "Eläintarha",
|
||||
"Zoom in": "Lähennä",
|
||||
"Zoom out": "Loitonna",
|
||||
"as of today": "tänään",
|
||||
"booking.nights": "{totalNights, plural, one {# yö} other {# yötä}}",
|
||||
"by": "mennessä",
|
||||
"characters": "hahmoja",
|
||||
"hotelPages.rooms.roomCard.person": "henkilö",
|
||||
"hotelPages.rooms.roomCard.persons": "Henkilöä",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Katso huoneen tiedot",
|
||||
"km to city center": "km keskustaan",
|
||||
"next level:": "pistettä tasolle:",
|
||||
"night": "yö",
|
||||
"nights": "yötä",
|
||||
"number": "määrä",
|
||||
"or": "tai",
|
||||
"points": "pistettä",
|
||||
"special character": "erikoishahmo",
|
||||
"spendable points expiring by": "{points} pistettä vanhenee {date} mennessä",
|
||||
"to": "to",
|
||||
"uppercase letter": "iso kirjain"
|
||||
"Zoom out": "Loitonna"
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Any changes you've made will be lost.": "Eventuelle endringer du har gjort, går tapt.",
|
||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på at du vil fjerne kortet som slutter på {lastFourDigits} fra medlemsprofilen din?",
|
||||
"Arrival date": "Ankomstdato",
|
||||
"as of today": "per idag",
|
||||
"As our": "Som vår {level}",
|
||||
"As our Close Friend": "Som vår nære venn",
|
||||
"At latest": "Senest",
|
||||
@@ -23,14 +24,16 @@
|
||||
"Bed type": "Seng type",
|
||||
"Book": "Bestill",
|
||||
"Book reward night": "Bestill belønningskveld",
|
||||
"Code / Voucher": "Bestillingskoder / kuponger",
|
||||
"Booking number": "Bestillingsnummer",
|
||||
"booking.nights": "{totalNights, plural, one {# natt} other {# netter}}",
|
||||
"Breakfast": "Frokost",
|
||||
"Breakfast excluded": "Frokost ekskludert",
|
||||
"Breakfast included": "Frokost inkludert",
|
||||
"Bus terminal": "Bussterminal",
|
||||
"Business": "Forretnings",
|
||||
"by": "innen",
|
||||
"Cancel": "Avbryt",
|
||||
"characters": "tegn",
|
||||
"Check in": "Sjekk inn",
|
||||
"Check out": "Sjekk ut",
|
||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Sjekk ut kredittkortene som er lagret på profilen din. Betal med et lagret kort når du er pålogget for en jevnere nettopplevelse.",
|
||||
@@ -45,8 +48,10 @@
|
||||
"Close menu": "Lukk meny",
|
||||
"Close my pages menu": "Lukk mine sidermenyn",
|
||||
"Close the map": "Lukk kartet",
|
||||
"Code / Voucher": "Bestillingskoder / kuponger",
|
||||
"Coming up": "Kommer opp",
|
||||
"Compare all levels": "Sammenlign alle nivåer",
|
||||
"Complete booking & go to payment": "Fullfør bestilling & gå til betaling",
|
||||
"Contact us": "Kontakt oss",
|
||||
"Continue": "Fortsette",
|
||||
"Copyright all rights reserved": "Scandic AB Alle rettigheter forbeholdt",
|
||||
@@ -74,9 +79,9 @@
|
||||
"Explore all levels and benefits": "Utforsk alle nivåer og fordeler",
|
||||
"Explore nearby": "Utforsk i nærheten",
|
||||
"Extras to your booking": "Tilvalg til bestillingen din",
|
||||
"FAQ": "FAQ",
|
||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.",
|
||||
"Fair": "Messe",
|
||||
"FAQ": "FAQ",
|
||||
"Find booking": "Finn booking",
|
||||
"Find hotels": "Finn hotell",
|
||||
"Flexibility": "Fleksibilitet",
|
||||
@@ -87,17 +92,22 @@
|
||||
"Get inspired": "Bli inspirert",
|
||||
"Go back to edit": "Gå tilbake til redigering",
|
||||
"Go back to overview": "Gå tilbake til oversikten",
|
||||
"Guests & Rooms": "Gjester & rom",
|
||||
"Hi": "Hei",
|
||||
"Highest level": "Høyeste nivå",
|
||||
"Hospital": "Sykehus",
|
||||
"Hotel": "Hotel",
|
||||
"Hotel facilities": "Hotelfaciliteter",
|
||||
"Hotel surroundings": "Hotellomgivelser",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "personer",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se detaljer om rommet",
|
||||
"Hotels": "Hoteller",
|
||||
"How do you want to sleep?": "Hvordan vil du sove?",
|
||||
"How it works": "Hvordan det fungerer",
|
||||
"Image gallery": "Bildegalleri",
|
||||
"Join Scandic Friends": "Bli med i Scandic Friends",
|
||||
"km to city center": "km til sentrum",
|
||||
"Language": "Språk",
|
||||
"Latest searches": "Siste søk",
|
||||
"Level": "Nivå",
|
||||
@@ -124,9 +134,9 @@
|
||||
"Member price": "Medlemspris",
|
||||
"Member price from": "Medlemspris fra",
|
||||
"Members": "Medlemmer",
|
||||
"Membership cards": "Medlemskort",
|
||||
"Membership ID": "Medlems-ID",
|
||||
"Membership ID copied to clipboard": "Medlems-ID kopiert til utklippstavlen",
|
||||
"Membership cards": "Medlemskort",
|
||||
"Menu": "Menu",
|
||||
"Modify": "Endre",
|
||||
"Month": "Måned",
|
||||
@@ -141,6 +151,9 @@
|
||||
"Nearby companies": "Nærliggende selskaper",
|
||||
"New password": "Nytt passord",
|
||||
"Next": "Neste",
|
||||
"next level:": "Neste nivå:",
|
||||
"night": "natt",
|
||||
"nights": "netter",
|
||||
"Nights needed to level up": "Netter som trengs for å komme opp i nivå",
|
||||
"No content published": "Ingen innhold publisert",
|
||||
"No matching location found": "Fant ingen samsvarende plassering",
|
||||
@@ -151,11 +164,13 @@
|
||||
"Non-refundable": "Ikke-refunderbart",
|
||||
"Not found": "Ikke funnet",
|
||||
"Nr night, nr adult": "{nights, number} natt, {adults, number} voksen",
|
||||
"number": "antall",
|
||||
"On your journey": "På reisen din",
|
||||
"Open": "Åpen",
|
||||
"Open language menu": "Åpne språkmenyen",
|
||||
"Open menu": "Åpne menyen",
|
||||
"Open my pages menu": "Åpne mine sider menyen",
|
||||
"or": "eller",
|
||||
"Overview": "Oversikt",
|
||||
"Parking": "Parkering",
|
||||
"Parking / Garage": "Parkering / Garasje",
|
||||
@@ -167,6 +182,7 @@
|
||||
"Phone is required": "Telefon kreves",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please enter a valid phone number": "Vennligst oppgi et gyldig telefonnummer",
|
||||
"points": "poeng",
|
||||
"Points": "Poeng",
|
||||
"Points being calculated": "Poeng beregnes",
|
||||
"Points earned prior to May 1, 2021": "Opptjente poeng før 1. mai 2021",
|
||||
@@ -185,7 +201,6 @@
|
||||
"Room & Terms": "Rom & Vilkår",
|
||||
"Room facilities": "Romfasiliteter",
|
||||
"Rooms": "Rom",
|
||||
"Guests & Rooms": "Gjester & rom",
|
||||
"Save": "Lagre",
|
||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||
@@ -210,25 +225,29 @@
|
||||
"Something went wrong and we couldn't add your card. Please try again later.": "Noe gikk galt, og vi kunne ikke legge til kortet ditt. Prøv igjen senere.",
|
||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Noe gikk galt, og vi kunne ikke fjerne kortet ditt. Vennligst prøv igjen senere.",
|
||||
"Something went wrong!": "Noe gikk galt!",
|
||||
"special character": "spesiell karakter",
|
||||
"spendable points expiring by": "{points} Brukbare poeng utløper innen {date}",
|
||||
"Sports": "Sport",
|
||||
"Standard price": "Standardpris",
|
||||
"Street": "Gate",
|
||||
"Successfully updated profile!": "Vellykket oppdatert profil!",
|
||||
"Summary": "Sammendrag",
|
||||
"TUI Points": "TUI Points",
|
||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Fortell oss hvilken informasjon og hvilke oppdateringer du ønsker å motta, og hvordan, ved å klikke på lenken nedenfor.",
|
||||
"Thank you": "Takk",
|
||||
"Theatre": "Teater",
|
||||
"There are no transactions to display": "Det er ingen transaksjoner å vise",
|
||||
"Things nearby HOTEL_NAME": "Ting i nærheten av {hotelName}",
|
||||
"to": "til",
|
||||
"Total Points": "Totale poeng",
|
||||
"Tourist": "Turist",
|
||||
"Transaction date": "Transaksjonsdato",
|
||||
"Transactions": "Transaksjoner",
|
||||
"Transportations": "Transport",
|
||||
"Tripadvisor reviews": "{rating} ({count} anmeldelser på Tripadvisor)",
|
||||
"TUI Points": "TUI Points",
|
||||
"Type of bed": "Sengtype",
|
||||
"Type of room": "Romtype",
|
||||
"uppercase letter": "stor bokstav",
|
||||
"Use bonus cheque": "Bruk bonussjekk",
|
||||
"Use code/voucher": "Bruk kode/voucher",
|
||||
"User information": "Brukerinformasjon",
|
||||
@@ -255,9 +274,9 @@
|
||||
"You canceled adding a new credit card.": "Du kansellerte å legge til et nytt kredittkort.",
|
||||
"You have no previous stays.": "Du har ingen tidligere opphold.",
|
||||
"You have no upcoming stays.": "Du har ingen kommende opphold.",
|
||||
"Your Challenges Conquer & Earn!": "Dine utfordringer Erobre og tjen!",
|
||||
"Your card was successfully removed!": "Kortet ditt ble fjernet!",
|
||||
"Your card was successfully saved!": "Kortet ditt ble lagret!",
|
||||
"Your Challenges Conquer & Earn!": "Dine utfordringer Erobre og tjen!",
|
||||
"Your current level": "Ditt nåværende nivå",
|
||||
"Your details": "Dine detaljer",
|
||||
"Your level": "Ditt nivå",
|
||||
@@ -265,23 +284,5 @@
|
||||
"Zip code": "Post kode",
|
||||
"Zoo": "Dyrehage",
|
||||
"Zoom in": "Zoom inn",
|
||||
"Zoom out": "Zoom ut",
|
||||
"as of today": "per i dag",
|
||||
"booking.nights": "{totalNights, plural, one {# natt} other {# netter}}",
|
||||
"by": "innen",
|
||||
"characters": "tegn",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "personer",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se detaljer om rommet",
|
||||
"km to city center": "km til sentrum",
|
||||
"next level:": "Neste nivå:",
|
||||
"night": "natt",
|
||||
"nights": "netter",
|
||||
"number": "antall",
|
||||
"or": "eller",
|
||||
"points": "poeng",
|
||||
"special character": "spesiell karakter",
|
||||
"spendable points expiring by": "{points} Brukbare poeng utløper innen {date}",
|
||||
"to": "til",
|
||||
"uppercase letter": "stor bokstav"
|
||||
"Zoom out": "Zoom ut"
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Any changes you've made will be lost.": "Alla ändringar du har gjort kommer att gå förlorade.",
|
||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Är du säker på att du vill ta bort kortet som slutar med {lastFourDigits} från din medlemsprofil?",
|
||||
"Arrival date": "Ankomstdatum",
|
||||
"as of today": "från och med idag",
|
||||
"As our": "Som vår {level}",
|
||||
"As our Close Friend": "Som vår nära vän",
|
||||
"At latest": "Senast",
|
||||
@@ -23,14 +24,16 @@
|
||||
"Bed type": "Sängtyp",
|
||||
"Book": "Boka",
|
||||
"Book reward night": "Boka frinatt",
|
||||
"Code / Voucher": "Bokningskoder / kuponger",
|
||||
"Booking number": "Bokningsnummer",
|
||||
"booking.nights": "{totalNights, plural, one {# natt} other {# nätter}}",
|
||||
"Breakfast": "Frukost",
|
||||
"Breakfast excluded": "Frukost ingår ej",
|
||||
"Breakfast included": "Frukost ingår",
|
||||
"Bus terminal": "Bussterminal",
|
||||
"Business": "Business",
|
||||
"by": "innan",
|
||||
"Cancel": "Avbryt",
|
||||
"characters": "tecken",
|
||||
"Check in": "Checka in",
|
||||
"Check out": "Checka ut",
|
||||
"Check out the credit cards saved to your profile. Pay with a saved card when signed in for a smoother web experience.": "Kolla in kreditkorten som sparats i din profil. Betala med ett sparat kort när du är inloggad för en smidigare webbupplevelse.",
|
||||
@@ -45,8 +48,10 @@
|
||||
"Close menu": "Stäng menyn",
|
||||
"Close my pages menu": "Stäng mina sidor menyn",
|
||||
"Close the map": "Stäng kartan",
|
||||
"Code / Voucher": "Bokningskoder / kuponger",
|
||||
"Coming up": "Kommer härnäst",
|
||||
"Compare all levels": "Jämför alla nivåer",
|
||||
"Complete booking & go to payment": "Fullför bokning & gå till betalning",
|
||||
"Contact us": "Kontakta oss",
|
||||
"Continue": "Fortsätt",
|
||||
"Copyright all rights reserved": "Scandic AB Alla rättigheter förbehålls",
|
||||
@@ -74,9 +79,9 @@
|
||||
"Explore all levels and benefits": "Utforska alla nivåer och fördelar",
|
||||
"Explore nearby": "Utforska i närheten",
|
||||
"Extras to your booking": "Extra tillval till din bokning",
|
||||
"FAQ": "FAQ",
|
||||
"Failed to delete credit card, please try again later.": "Det gick inte att ta bort kreditkortet, försök igen senare.",
|
||||
"Fair": "Mässa",
|
||||
"FAQ": "FAQ",
|
||||
"Find booking": "Hitta bokning",
|
||||
"Find hotels": "Hitta hotell",
|
||||
"Flexibility": "Flexibilitet",
|
||||
@@ -87,17 +92,22 @@
|
||||
"Get inspired": "Bli inspirerad",
|
||||
"Go back to edit": "Gå tillbaka till redigeringen",
|
||||
"Go back to overview": "Gå tillbaka till översikten",
|
||||
"Guests & Rooms": "Gäster & rum",
|
||||
"Hi": "Hej",
|
||||
"Highest level": "Högsta nivå",
|
||||
"Hospital": "Sjukhus",
|
||||
"Hotel": "Hotell",
|
||||
"Hotel facilities": "Hotellfaciliteter",
|
||||
"Hotel surroundings": "Hotellomgivning",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "personer",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se information om rummet",
|
||||
"Hotels": "Hotell",
|
||||
"How do you want to sleep?": "Hur vill du sova?",
|
||||
"How it works": "Hur det fungerar",
|
||||
"Image gallery": "Bildgalleri",
|
||||
"Join Scandic Friends": "Gå med i Scandic Friends",
|
||||
"km to city center": "km till stadens centrum",
|
||||
"Language": "Språk",
|
||||
"Latest searches": "Senaste sökningarna",
|
||||
"Level": "Nivå",
|
||||
@@ -124,9 +134,9 @@
|
||||
"Member price": "Medlemspris",
|
||||
"Member price from": "Medlemspris från",
|
||||
"Members": "Medlemmar",
|
||||
"Membership cards": "Medlemskort",
|
||||
"Membership ID": "Medlems-ID",
|
||||
"Membership ID copied to clipboard": "Medlems-ID kopierat till urklipp",
|
||||
"Membership cards": "Medlemskort",
|
||||
"Menu": "Meny",
|
||||
"Modify": "Ändra",
|
||||
"Month": "Månad",
|
||||
@@ -141,6 +151,9 @@
|
||||
"Nearby companies": "Närliggande företag",
|
||||
"New password": "Nytt lösenord",
|
||||
"Next": "Nästa",
|
||||
"next level:": "Nästa nivå:",
|
||||
"night": "natt",
|
||||
"nights": "nätter",
|
||||
"Nights needed to level up": "Nätter som behövs för att gå upp i nivå",
|
||||
"No content published": "Inget innehåll publicerat",
|
||||
"No matching location found": "Ingen matchande plats hittades",
|
||||
@@ -151,11 +164,13 @@
|
||||
"Non-refundable": "Ej återbetalningsbar",
|
||||
"Not found": "Hittades inte",
|
||||
"Nr night, nr adult": "{nights, number} natt, {adults, number} vuxen",
|
||||
"number": "nummer",
|
||||
"On your journey": "På din resa",
|
||||
"Open": "Öppna",
|
||||
"Open language menu": "Öppna språkmenyn",
|
||||
"Open menu": "Öppna menyn",
|
||||
"Open my pages menu": "Öppna mina sidor menyn",
|
||||
"or": "eller",
|
||||
"Overview": "Översikt",
|
||||
"Parking": "Parkering",
|
||||
"Parking / Garage": "Parkering / Garage",
|
||||
@@ -167,6 +182,7 @@
|
||||
"Phone is required": "Telefonnummer är obligatorisk",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Please enter a valid phone number": "Var vänlig och ange ett giltigt telefonnummer",
|
||||
"points": "poäng",
|
||||
"Points": "Poäng",
|
||||
"Points being calculated": "Poäng beräknas",
|
||||
"Points earned prior to May 1, 2021": "Intjänade poäng före den 1 maj 2021",
|
||||
@@ -185,7 +201,6 @@
|
||||
"Room & Terms": "Rum & Villkor",
|
||||
"Room facilities": "Rumfaciliteter",
|
||||
"Rooms": "Rum",
|
||||
"Guests & Rooms": "Gäster & rum",
|
||||
"Save": "Spara",
|
||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||
"Scandic Friends Point Shop": "Scandic Friends Point Shop",
|
||||
@@ -210,25 +225,29 @@
|
||||
"Something went wrong and we couldn't add your card. Please try again later.": "Något gick fel och vi kunde inte lägga till ditt kort. Försök igen senare.",
|
||||
"Something went wrong and we couldn't remove your card. Please try again later.": "Något gick fel och vi kunde inte ta bort ditt kort. Försök igen senare.",
|
||||
"Something went wrong!": "Något gick fel!",
|
||||
"special character": "speciell karaktär",
|
||||
"spendable points expiring by": "{points} poäng förfaller {date}",
|
||||
"Sports": "Sport",
|
||||
"Standard price": "Standardpris",
|
||||
"Street": "Gata",
|
||||
"Successfully updated profile!": "Profilen har uppdaterats framgångsrikt!",
|
||||
"Summary": "Sammanfattning",
|
||||
"TUI Points": "TUI Points",
|
||||
"Tell us what information and updates you'd like to receive, and how, by clicking the link below.": "Berätta för oss vilken information och vilka uppdateringar du vill få och hur genom att klicka på länken nedan.",
|
||||
"Thank you": "Tack",
|
||||
"Theatre": "Teater",
|
||||
"There are no transactions to display": "Det finns inga transaktioner att visa",
|
||||
"Things nearby HOTEL_NAME": "Saker i närheten av {hotelName}",
|
||||
"to": "till",
|
||||
"Total Points": "Poäng totalt",
|
||||
"Tourist": "Turist",
|
||||
"Transaction date": "Transaktionsdatum",
|
||||
"Transactions": "Transaktioner",
|
||||
"Transportations": "Transport",
|
||||
"Tripadvisor reviews": "{rating} ({count} recensioner på Tripadvisor)",
|
||||
"TUI Points": "TUI Points",
|
||||
"Type of bed": "Sängtyp",
|
||||
"Type of room": "Rumstyp",
|
||||
"uppercase letter": "stor bokstav",
|
||||
"Use bonus cheque": "Använd bonuscheck",
|
||||
"Use code/voucher": "Använd kod/voucher",
|
||||
"User information": "Användarinformation",
|
||||
@@ -255,9 +274,9 @@
|
||||
"You canceled adding a new credit card.": "Du avbröt att lägga till ett nytt kreditkort.",
|
||||
"You have no previous stays.": "Du har inga tidigare vistelser.",
|
||||
"You have no upcoming stays.": "Du har inga planerade resor.",
|
||||
"Your Challenges Conquer & Earn!": "Dina utmaningar Erövra och tjäna!",
|
||||
"Your card was successfully removed!": "Ditt kort har tagits bort!",
|
||||
"Your card was successfully saved!": "Ditt kort har sparats!",
|
||||
"Your Challenges Conquer & Earn!": "Dina utmaningar Erövra och tjäna!",
|
||||
"Your current level": "Din nuvarande nivå",
|
||||
"Your details": "Dina uppgifter",
|
||||
"Your level": "Din nivå",
|
||||
@@ -265,23 +284,5 @@
|
||||
"Zip code": "Postnummer",
|
||||
"Zoo": "Djurpark",
|
||||
"Zoom in": "Zooma in",
|
||||
"Zoom out": "Zooma ut",
|
||||
"as of today": "per idag",
|
||||
"booking.nights": "{totalNights, plural, one {# natt} other {# nätter}}",
|
||||
"by": "innan",
|
||||
"characters": "tecken",
|
||||
"hotelPages.rooms.roomCard.person": "person",
|
||||
"hotelPages.rooms.roomCard.persons": "personer",
|
||||
"hotelPages.rooms.roomCard.seeRoomDetails": "Se information om rummet",
|
||||
"km to city center": "km till stadens centrum",
|
||||
"next level:": "Nästa nivå:",
|
||||
"night": "natt",
|
||||
"nights": "nätter",
|
||||
"number": "nummer",
|
||||
"or": "eller",
|
||||
"points": "poäng",
|
||||
"special character": "speciell karaktär",
|
||||
"spendable points expiring by": "{points} poäng förfaller {date}",
|
||||
"to": "till",
|
||||
"uppercase letter": "stor bokstav"
|
||||
"Zoom out": "Zooma ut"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { mergeRouters } from "@/server/trpc"
|
||||
|
||||
import { bookingMutationRouter } from "./mutation"
|
||||
import { bookingQueryRouter } from "./query"
|
||||
|
||||
export const bookingRouter = mergeRouters(bookingMutationRouter)
|
||||
export const bookingRouter = mergeRouters(
|
||||
bookingMutationRouter,
|
||||
bookingQueryRouter
|
||||
)
|
||||
|
||||
@@ -1,38 +1,68 @@
|
||||
import { z } from "zod"
|
||||
|
||||
// Query
|
||||
const roomsSchema = z.array(
|
||||
z.object({
|
||||
adults: z.number().int().nonnegative(),
|
||||
childrenAges: z
|
||||
.array(
|
||||
z.object({
|
||||
age: z.number().int().nonnegative(),
|
||||
bedType: z.string(),
|
||||
})
|
||||
)
|
||||
.default([]),
|
||||
rateCode: z.string(),
|
||||
roomTypeCode: z.string(),
|
||||
guest: z.object({
|
||||
title: z.string(),
|
||||
firstName: z.string(),
|
||||
lastName: z.string(),
|
||||
email: z.string().email(),
|
||||
phoneCountryCodePrefix: z.string(),
|
||||
phoneNumber: z.string(),
|
||||
countryCode: z.string(),
|
||||
membershipNumber: z.string().optional(),
|
||||
}),
|
||||
smsConfirmationRequested: z.boolean(),
|
||||
packages: z.object({
|
||||
breakfast: z.boolean(),
|
||||
allergyFriendly: z.boolean(),
|
||||
petFriendly: z.boolean(),
|
||||
accessibility: z.boolean(),
|
||||
}),
|
||||
})
|
||||
)
|
||||
|
||||
const paymentSchema = z.object({
|
||||
paymentMethod: z.string(),
|
||||
card: z
|
||||
.object({
|
||||
alias: z.string(),
|
||||
expiryDate: z.string(),
|
||||
cardType: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
cardHolder: z.object({
|
||||
email: z.string().email(),
|
||||
name: z.string(),
|
||||
phoneCountryCode: z.string(),
|
||||
phoneSubscriber: z.string(),
|
||||
}),
|
||||
success: z.string(),
|
||||
error: z.string(),
|
||||
cancel: z.string(),
|
||||
})
|
||||
|
||||
// Mutation
|
||||
export const createBookingInput = z.object({
|
||||
hotelId: z.string(),
|
||||
checkInDate: z.string(),
|
||||
checkOutDate: z.string(),
|
||||
rooms: z.array(
|
||||
z.object({
|
||||
adults: z.number().int().nonnegative(),
|
||||
children: z.number().int().nonnegative(),
|
||||
rateCode: z.string(),
|
||||
roomTypeCode: z.string(),
|
||||
guest: z.object({
|
||||
title: z.string(),
|
||||
firstName: z.string(),
|
||||
lastName: z.string(),
|
||||
email: z.string().email(),
|
||||
phoneCountryCodePrefix: z.string(),
|
||||
phoneNumber: z.string(),
|
||||
countryCode: z.string(),
|
||||
}),
|
||||
smsConfirmationRequested: z.boolean(),
|
||||
})
|
||||
),
|
||||
payment: z.object({
|
||||
cardHolder: z.object({
|
||||
Email: z.string().email(),
|
||||
Name: z.string(),
|
||||
PhoneCountryCode: z.string(),
|
||||
PhoneSubscriber: z.string(),
|
||||
}),
|
||||
success: z.string(),
|
||||
error: z.string(),
|
||||
cancel: z.string(),
|
||||
}),
|
||||
rooms: roomsSchema,
|
||||
payment: paymentSchema,
|
||||
})
|
||||
|
||||
// Query
|
||||
export const getBookingStatusInput = z.object({
|
||||
confirmationNumber: z.string(),
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import * as api from "@/lib/api"
|
||||
import { getVerifiedUser } from "@/server/routers/user/query"
|
||||
import { router, safeProtectedProcedure } from "@/server/trpc"
|
||||
import { bookingServiceProcedure, router } from "@/server/trpc"
|
||||
|
||||
import { getMembership } from "@/utils/user"
|
||||
|
||||
@@ -36,13 +36,15 @@ async function getMembershipNumber(
|
||||
|
||||
export const bookingMutationRouter = router({
|
||||
booking: router({
|
||||
create: safeProtectedProcedure
|
||||
create: bookingServiceProcedure
|
||||
.input(createBookingInput)
|
||||
.mutation(async function ({ ctx, input }) {
|
||||
const { checkInDate, checkOutDate, hotelId } = input
|
||||
|
||||
// TODO: add support for user token OR service token in procedure
|
||||
// then we can fetch membership number if user token exists
|
||||
const loggingAttributes = {
|
||||
membershipNumber: await getMembershipNumber(ctx.session),
|
||||
// membershipNumber: await getMembershipNumber(ctx.session),
|
||||
checkInDate,
|
||||
checkOutDate,
|
||||
hotelId,
|
||||
@@ -56,11 +58,10 @@ export const bookingMutationRouter = router({
|
||||
query: loggingAttributes,
|
||||
})
|
||||
)
|
||||
const headers = ctx.session
|
||||
? {
|
||||
Authorization: `Bearer ${ctx.session?.token.access_token}`,
|
||||
}
|
||||
: undefined
|
||||
const headers = {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
}
|
||||
|
||||
const apiResponse = await api.post(api.endpoints.v1.booking, {
|
||||
headers,
|
||||
body: input,
|
||||
|
||||
@@ -5,9 +5,9 @@ export const createBookingSchema = z
|
||||
data: z.object({
|
||||
attributes: z.object({
|
||||
confirmationNumber: z.string(),
|
||||
cancellationNumber: z.string().nullable(),
|
||||
cancellationNumber: z.string().optional(),
|
||||
reservationStatus: z.string(),
|
||||
paymentUrl: z.string().nullable(),
|
||||
paymentUrl: z.string().optional(),
|
||||
}),
|
||||
type: z.string(),
|
||||
id: z.string(),
|
||||
|
||||
85
server/routers/booking/query.ts
Normal file
85
server/routers/booking/query.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import * as api from "@/lib/api"
|
||||
import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc"
|
||||
import { bookingServiceProcedure, router } from "@/server/trpc"
|
||||
|
||||
import { getBookingStatusInput } from "./input"
|
||||
import { createBookingSchema } from "./output"
|
||||
|
||||
const meter = metrics.getMeter("trpc.booking")
|
||||
const getBookingStatusCounter = meter.createCounter("trpc.booking.status")
|
||||
const getBookingStatusSuccessCounter = meter.createCounter(
|
||||
"trpc.booking.status-success"
|
||||
)
|
||||
const getBookingStatusFailCounter = meter.createCounter(
|
||||
"trpc.booking.status-fail"
|
||||
)
|
||||
|
||||
export const bookingQueryRouter = router({
|
||||
status: bookingServiceProcedure
|
||||
.input(getBookingStatusInput)
|
||||
.query(async function ({ ctx, input }) {
|
||||
const { confirmationNumber } = input
|
||||
getBookingStatusCounter.add(1, { confirmationNumber })
|
||||
|
||||
const apiResponse = await api.get(
|
||||
`${api.endpoints.v1.booking}/${confirmationNumber}/status`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!apiResponse.ok) {
|
||||
const responseMessage = await apiResponse.text()
|
||||
getBookingStatusFailCounter.add(1, {
|
||||
confirmationNumber,
|
||||
error_type: "http_error",
|
||||
error: responseMessage,
|
||||
})
|
||||
console.error(
|
||||
"api.booking.status error",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
error: {
|
||||
status: apiResponse.status,
|
||||
statusText: apiResponse.statusText,
|
||||
text: responseMessage,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
throw serverErrorByStatus(apiResponse.status, apiResponse)
|
||||
}
|
||||
|
||||
const apiJson = await apiResponse.json()
|
||||
const verifiedData = createBookingSchema.safeParse(apiJson)
|
||||
if (!verifiedData.success) {
|
||||
getBookingStatusFailCounter.add(1, {
|
||||
confirmationNumber,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(verifiedData.error),
|
||||
})
|
||||
console.error(
|
||||
"api.booking.status validation error",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
error: verifiedData.error,
|
||||
})
|
||||
)
|
||||
throw badRequestError()
|
||||
}
|
||||
|
||||
getBookingStatusSuccessCounter.add(1, { confirmationNumber })
|
||||
console.info(
|
||||
"api.booking.status success",
|
||||
JSON.stringify({
|
||||
query: { confirmationNumber },
|
||||
})
|
||||
)
|
||||
|
||||
return verifiedData.data
|
||||
}),
|
||||
})
|
||||
@@ -436,6 +436,22 @@ export const roomSchema = z.object({
|
||||
type: z.enum(["roomcategories"]),
|
||||
})
|
||||
|
||||
const merchantInformationSchema = z.object({
|
||||
webMerchantId: z.string(),
|
||||
cards: z.record(z.string(), z.boolean()).transform((val) => {
|
||||
return Object.entries(val)
|
||||
.filter(([_, enabled]) => enabled)
|
||||
.map(([key]) => key)
|
||||
}),
|
||||
alternatePaymentOptions: z
|
||||
.record(z.string(), z.boolean())
|
||||
.transform((val) => {
|
||||
return Object.entries(val)
|
||||
.filter(([_, enabled]) => enabled)
|
||||
.map(([key]) => key)
|
||||
}),
|
||||
})
|
||||
|
||||
// NOTE: Find schema at: https://aks-test.scandichotels.com/hotel/swagger/v1/index.html
|
||||
export const getHotelDataSchema = z.object({
|
||||
data: z.object({
|
||||
@@ -471,6 +487,7 @@ export const getHotelDataSchema = z.object({
|
||||
hotelContent: hotelContentSchema,
|
||||
detailedFacilities: z.array(detailedFacilitySchema),
|
||||
healthFacilities: z.array(healthFacilitySchema),
|
||||
merchantInformationData: merchantInformationSchema,
|
||||
rewardNight: rewardNightSchema,
|
||||
pointsOfInterest: z
|
||||
.array(pointOfInterestSchema)
|
||||
|
||||
@@ -121,29 +121,24 @@ export const safeProtectedProcedure = t.procedure.use(async function (opts) {
|
||||
})
|
||||
})
|
||||
|
||||
export const profileServiceProcedure = t.procedure.use(async (opts) => {
|
||||
const { access_token } = await fetchServiceToken(["profile"])
|
||||
if (!access_token) {
|
||||
throw internalServerError("Failed to obtain profile service token")
|
||||
}
|
||||
return opts.next({
|
||||
ctx: {
|
||||
serviceToken: access_token,
|
||||
},
|
||||
function createServiceProcedure(serviceName: string) {
|
||||
return t.procedure.use(async (opts) => {
|
||||
const { access_token } = await fetchServiceToken([serviceName])
|
||||
if (!access_token) {
|
||||
throw internalServerError(`Failed to obtain ${serviceName} service token`)
|
||||
}
|
||||
return opts.next({
|
||||
ctx: {
|
||||
serviceToken: access_token,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const bookingServiceProcedure = createServiceProcedure("booking")
|
||||
export const hotelServiceProcedure = createServiceProcedure("hotel")
|
||||
export const profileServiceProcedure = createServiceProcedure("profile")
|
||||
|
||||
export const hotelServiceProcedure = t.procedure.use(async (opts) => {
|
||||
const { access_token } = await fetchServiceToken(["hotel"])
|
||||
if (!access_token) {
|
||||
throw internalServerError("Failed to obtain hotel service token")
|
||||
}
|
||||
return opts.next({
|
||||
ctx: {
|
||||
serviceToken: access_token,
|
||||
},
|
||||
})
|
||||
})
|
||||
export const serverActionProcedure = t.procedure.experimental_caller(
|
||||
experimental_nextAppDirCaller({
|
||||
createContext,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Rate } from "@/server/routers/hotels/output"
|
||||
|
||||
import { Hotel } from "@/types/hotel"
|
||||
|
||||
export interface SectionProps {
|
||||
nextPath: string
|
||||
}
|
||||
@@ -33,6 +35,10 @@ export interface RoomSelectionProps extends SectionProps {
|
||||
|
||||
export interface DetailsProps extends SectionProps {}
|
||||
|
||||
export interface PaymentProps {
|
||||
hotel: Hotel
|
||||
}
|
||||
|
||||
export interface SectionPageProps {
|
||||
breakfast?: string
|
||||
bed?: string
|
||||
|
||||
Reference in New Issue
Block a user