Merged in feat/SW-3050-webviews (pull request #2429)
Feat/SW-3050 webviews Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -1,75 +0,0 @@
|
||||
.main {
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 480px;
|
||||
}
|
||||
|
||||
.blurOverlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
backdrop-filter: blur(12px);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, transparent 100%);
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(0, 0, 0, 0.5) 0%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.image {
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.headerContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 80px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding-bottom: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.form {
|
||||
max-width: 640px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: var(--Spacing-x5) 0;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
padding: 0 var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.logIn {
|
||||
padding: var(--Spacing-x9) var(--Spacing-x2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
align-items: center;
|
||||
color: var(--Scandic-Grey-100);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.content {
|
||||
width: var(--max-width-content);
|
||||
padding-bottom: 160px;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,11 @@
|
||||
import { cookies } from "next/headers"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
import { dt } from "@/lib/dt"
|
||||
import {
|
||||
findBooking,
|
||||
getAncillaryPackages,
|
||||
getBookingConfirmation,
|
||||
getLinkedReservations,
|
||||
getPackages,
|
||||
getProfileSafely,
|
||||
getSavedPaymentCardsSafely,
|
||||
} from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import AdditionalInfoForm from "@/components/HotelReservation/FindMyBooking/AdditionalInfoForm"
|
||||
import accessBooking, {
|
||||
ACCESS_GRANTED,
|
||||
ERROR_BAD_REQUEST,
|
||||
ERROR_UNAUTHORIZED,
|
||||
} from "@/components/HotelReservation/MyStay/accessBooking"
|
||||
import { Ancillaries } from "@/components/HotelReservation/MyStay/Ancillaries"
|
||||
import BookingSummary from "@/components/HotelReservation/MyStay/BookingSummary"
|
||||
import { Header } from "@/components/HotelReservation/MyStay/Header"
|
||||
import Promo from "@/components/HotelReservation/MyStay/Promo"
|
||||
import { ReferenceCard } from "@/components/HotelReservation/MyStay/ReferenceCard"
|
||||
import MultiRoom from "@/components/HotelReservation/MyStay/Rooms/MultiRoom"
|
||||
import SingleRoom from "@/components/HotelReservation/MyStay/Rooms/SingleRoom"
|
||||
import SidePeek from "@/components/HotelReservation/SidePeek"
|
||||
import Image from "@/components/Image"
|
||||
import { getIntl } from "@/i18n"
|
||||
import MyStay from "@/components/HotelReservation/MyStay"
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
import MyStayProvider from "@/providers/MyStay"
|
||||
import { isLoggedInUser } from "@/utils/isLoggedInUser"
|
||||
import * as maskValue from "@/utils/maskValue"
|
||||
import { parseRefId } from "@/utils/refId"
|
||||
import { getCurrentWebUrl } from "@/utils/url"
|
||||
|
||||
import Tracking from "./tracking"
|
||||
|
||||
import styles from "./page.module.css"
|
||||
|
||||
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
|
||||
import type { SafeUser } from "@/types/user"
|
||||
|
||||
export default async function MyStay(
|
||||
export default async function MyStayPage(
|
||||
props: PageArgs<LangParams, { RefId?: string }>
|
||||
) {
|
||||
const searchParams = await props.searchParams
|
||||
@@ -55,269 +13,10 @@ export default async function MyStay(
|
||||
setLang(params.lang)
|
||||
const refId = searchParams.RefId
|
||||
|
||||
if (!refId) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
const { confirmationNumber, lastName } = parseRefId(refId)
|
||||
if (!confirmationNumber) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
const isLoggedIn = await isLoggedInUser()
|
||||
|
||||
const cookieStore = await cookies()
|
||||
const bv = cookieStore.get("bv")?.value
|
||||
let bookingConfirmation
|
||||
if (isLoggedIn) {
|
||||
bookingConfirmation = await getBookingConfirmation(refId)
|
||||
} else if (bv) {
|
||||
const params = new URLSearchParams(bv)
|
||||
const firstName = params.get("firstName")
|
||||
const email = params.get("email")
|
||||
|
||||
if (firstName && email) {
|
||||
bookingConfirmation = await findBooking(
|
||||
confirmationNumber,
|
||||
lastName,
|
||||
firstName,
|
||||
email
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<RenderAdditionalInfoForm
|
||||
confirmationNumber={confirmationNumber}
|
||||
lastName={lastName}
|
||||
/>
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
<RenderAdditionalInfoForm
|
||||
confirmationNumber={confirmationNumber}
|
||||
lastName={lastName}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (!bookingConfirmation) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
const { additionalData, booking, hotel, roomCategories } = bookingConfirmation
|
||||
|
||||
const user = await getProfileSafely()
|
||||
|
||||
const intl = await getIntl()
|
||||
|
||||
const access = accessBooking(booking.guest, lastName, user, bv)
|
||||
|
||||
if (access === ACCESS_GRANTED) {
|
||||
const lang = params.lang
|
||||
const fromDate = dt(booking.checkInDate).format("YYYY-MM-DD")
|
||||
const toDate = dt(booking.checkOutDate).format("YYYY-MM-DD")
|
||||
|
||||
const linkedReservationsPromise = getLinkedReservations(booking.refId)
|
||||
|
||||
const packagesInput = {
|
||||
adults: booking.adults,
|
||||
children: booking.childrenAges.length,
|
||||
endDate: toDate,
|
||||
hotelId: hotel.operaId,
|
||||
lang,
|
||||
startDate: fromDate,
|
||||
packageCodes: [
|
||||
BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST,
|
||||
BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST,
|
||||
BreakfastPackageEnum.FREE_CHILD_BREAKFAST,
|
||||
],
|
||||
}
|
||||
const supportedCards = hotel.merchantInformationData.cards
|
||||
const savedPaymentCardsInput = { supportedCards }
|
||||
|
||||
const hasBreakfastPackage = booking.packages.find(
|
||||
(pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST
|
||||
)
|
||||
const breakfastIncluded = booking.rateDefinition.breakfastIncluded
|
||||
const shouldFetchBreakfastPackages =
|
||||
!hasBreakfastPackage && !breakfastIncluded
|
||||
if (shouldFetchBreakfastPackages) {
|
||||
void getPackages(packagesInput)
|
||||
}
|
||||
if (user) {
|
||||
void getSavedPaymentCardsSafely(savedPaymentCardsInput)
|
||||
}
|
||||
|
||||
let breakfastPackages = null
|
||||
if (shouldFetchBreakfastPackages) {
|
||||
breakfastPackages = await getPackages(packagesInput)
|
||||
}
|
||||
let savedCreditCards = null
|
||||
if (user) {
|
||||
savedCreditCards = await getSavedPaymentCardsSafely(
|
||||
savedPaymentCardsInput
|
||||
)
|
||||
}
|
||||
let ancillaryPackagesPromise = null
|
||||
if (booking.showAncillaries) {
|
||||
ancillaryPackagesPromise = getAncillaryPackages({
|
||||
fromDate,
|
||||
hotelId: hotel.operaId,
|
||||
toDate,
|
||||
})
|
||||
}
|
||||
|
||||
const imageSrc =
|
||||
hotel.hotelContent.images.imageSizes.large ??
|
||||
additionalData.gallery?.heroImages[0]?.imageSizes.large ??
|
||||
hotel.galleryImages[0]?.imageSizes.large
|
||||
|
||||
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
|
||||
const promoUrl = !env.isLangLive(lang)
|
||||
? new URL(getCurrentWebUrl({ path: "/", lang }))
|
||||
: new URL(`${baseUrl}/${lang}/`)
|
||||
|
||||
promoUrl.searchParams.set("hotel", hotel.operaId)
|
||||
|
||||
const maskedBookingConfirmation = {
|
||||
...bookingConfirmation,
|
||||
booking: {
|
||||
...bookingConfirmation.booking,
|
||||
guest: {
|
||||
...bookingConfirmation.booking.guest,
|
||||
email: maskValue.email(bookingConfirmation.booking.guest.email),
|
||||
phoneNumber: maskValue.phone(
|
||||
bookingConfirmation.booking.guest.phoneNumber ?? ""
|
||||
),
|
||||
},
|
||||
},
|
||||
} satisfies BookingConfirmation
|
||||
|
||||
const maskedUser = user
|
||||
? ({
|
||||
...user,
|
||||
email: maskValue.email(user.email),
|
||||
phoneNumber: maskValue.phone(user.phoneNumber ?? ""),
|
||||
} satisfies SafeUser)
|
||||
: null
|
||||
|
||||
return (
|
||||
<MyStayProvider
|
||||
bookingConfirmation={maskedBookingConfirmation}
|
||||
breakfastPackages={breakfastPackages}
|
||||
lang={params.lang}
|
||||
linkedReservationsPromise={linkedReservationsPromise}
|
||||
refId={booking.refId}
|
||||
roomCategories={roomCategories}
|
||||
savedCreditCards={savedCreditCards}
|
||||
>
|
||||
<main className={styles.main}>
|
||||
<div className={styles.imageContainer}>
|
||||
<div className={styles.blurOverlay} />
|
||||
{imageSrc && (
|
||||
<Image
|
||||
className={styles.image}
|
||||
src={imageSrc}
|
||||
alt={hotel.name}
|
||||
fill
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.headerContainer}>
|
||||
<Header cityName={hotel.cityName} name={hotel.name} />
|
||||
<ReferenceCard />
|
||||
</div>
|
||||
{booking.showAncillaries && ancillaryPackagesPromise && (
|
||||
<Ancillaries
|
||||
ancillariesPromise={ancillaryPackagesPromise}
|
||||
packages={breakfastPackages}
|
||||
user={maskedUser}
|
||||
savedCreditCards={savedCreditCards}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SingleRoom user={maskedUser} />
|
||||
<MultiRoom user={maskedUser} />
|
||||
|
||||
<BookingSummary hotel={hotel} />
|
||||
<Promo
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Book your next stay",
|
||||
})}
|
||||
text={intl.formatMessage({
|
||||
defaultMessage:
|
||||
"Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.",
|
||||
})}
|
||||
buttonText={intl.formatMessage({
|
||||
defaultMessage: "Explore Scandic hotels",
|
||||
})}
|
||||
href={promoUrl.toString()}
|
||||
image={hotel.hotelContent.images}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
<SidePeek />
|
||||
<Tracking />
|
||||
</MyStayProvider>
|
||||
)
|
||||
}
|
||||
|
||||
if (access === ERROR_BAD_REQUEST) {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<div className={styles.form}>
|
||||
<AdditionalInfoForm
|
||||
confirmationNumber={confirmationNumber}
|
||||
lastName={lastName}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
if (access === ERROR_UNAUTHORIZED) {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<div className={styles.logIn}>
|
||||
<Typography variant="Title/md">
|
||||
<h1>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "You need to be logged in to view your booking",
|
||||
})}
|
||||
</h1>
|
||||
</Typography>
|
||||
<Typography variant="Body/Lead text">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage:
|
||||
"And you need to be logged in with the same member account that made the booking.",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
return notFound()
|
||||
}
|
||||
|
||||
function RenderAdditionalInfoForm({
|
||||
confirmationNumber,
|
||||
lastName,
|
||||
}: {
|
||||
confirmationNumber: string
|
||||
lastName: string
|
||||
}) {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<div className={styles.form}>
|
||||
<AdditionalInfoForm
|
||||
confirmationNumber={confirmationNumber}
|
||||
lastName={lastName}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
<>
|
||||
<MyStay refId={refId} lang={params.lang} />
|
||||
<Tracking />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user