From 7a56d21a3ed500e551d68b7f416b67e5bdb62bce Mon Sep 17 00:00:00 2001 From: Linus Flood Date: Wed, 25 Jun 2025 07:44:33 +0000 Subject: [PATCH] Merged in feat/SW-3050-webviews (pull request #2429) Feat/SW-3050 webviews Approved-by: Anton Gunnarsson --- .../hotelreservation/my-stay/page.tsx | 313 +---------------- .../(views)/hotelreservation/README.md | 27 -- .../(views)/hotelreservation/layout.tsx | 23 -- .../hotelreservation/my-stay/loading.tsx | 1 - .../hotelreservation/my-stay/page.module.css | 75 ---- .../(views)/hotelreservation/my-stay/page.tsx | 317 +---------------- .../MyStay/GuestDetails/index.tsx | 222 ++++++------ .../Actions/AddToCalendar/index.tsx | 9 + .../HotelReservation/MyStay/index.module.css} | 0 .../HotelReservation/MyStay/index.tsx | 320 ++++++++++++++++++ apps/scandic-web/constants/routes/webviews.ts | 4 + 11 files changed, 461 insertions(+), 850 deletions(-) delete mode 100644 apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/README.md delete mode 100644 apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/layout.tsx delete mode 100644 apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/loading.tsx delete mode 100644 apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/page.module.css rename apps/scandic-web/{app/[lang]/(live)/(public)/hotelreservation/my-stay/page.module.css => components/HotelReservation/MyStay/index.module.css} (100%) create mode 100644 apps/scandic-web/components/HotelReservation/MyStay/index.tsx diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx index 1da0008ce..4eedbbc71 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx @@ -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 ) { 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 ( - - ) - } - } else { - return ( - - ) - } - - 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 ( - -
-
-
- {imageSrc && ( - {hotel.name} - )} -
-
-
-
- -
- {booking.showAncillaries && ancillaryPackagesPromise && ( - - )} - - - - - - -
-
- - -
- ) - } - - if (access === ERROR_BAD_REQUEST) { - return ( -
-
- -
-
- ) - } - - if (access === ERROR_UNAUTHORIZED) { - return ( -
-
- -

- {intl.formatMessage({ - defaultMessage: "You need to be logged in to view your booking", - })} -

-
- -

- {intl.formatMessage({ - defaultMessage: - "And you need to be logged in with the same member account that made the booking.", - })} -

-
-
-
- ) - } - - return notFound() -} - -function RenderAdditionalInfoForm({ - confirmationNumber, - lastName, -}: { - confirmationNumber: string - lastName: string -}) { return ( -
-
- -
-
+ <> + + + ) } diff --git a/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/README.md b/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/README.md deleted file mode 100644 index fdc0f1ad4..000000000 --- a/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Booking flow - -The booking flow is the user journey of booking one or more rooms at our -hotels. Everything from choosing the date to payment and confirmation is -part of the booking flow. - -## Booking widget - -On most of the pages on the website we have a booking widget. This is where -the user starts the booking flow, by filling the form and submit. If they -entered a city as the destination they will land on the select hotel page -and if they entered a specific hotel they will land on the select rate page. - -## Select hotel - -Lists available hotels based on the search criteria. When the user selects -a hotel they land on the select rate page. - -## Select rate, room, breakfast etc - -This is a page with an accordion like design, but every accordion is handled -as its own page with its own URL. - -## State management - -The state, like search parameters and selected alternatives, is kept -throughout the booking flow in the URL. diff --git a/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/layout.tsx b/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/layout.tsx deleted file mode 100644 index 9ec90c6f9..000000000 --- a/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/layout.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { env } from "@/env/server" - -import type { LangParams, PageArgs } from "@/types/params" - -export async function generateMetadata(props: PageArgs) { - const params = await props.params - return { - robots: { - index: env.isLangLive(params.lang), - follow: env.isLangLive(params.lang), - }, - } -} - -export default function HotelReservationLayout({ - children, -}: React.PropsWithChildren) { - if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") { - return null - } - - return <>{children} -} diff --git a/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/loading.tsx b/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/loading.tsx deleted file mode 100644 index bae8bb797..000000000 --- a/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/loading.tsx +++ /dev/null @@ -1 +0,0 @@ -export { MyStaySkeleton as default } from "@/components/HotelReservation/MyStay/myStaySkeleton" diff --git a/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/page.module.css b/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/page.module.css deleted file mode 100644 index 774114c7d..000000000 --- a/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/page.module.css +++ /dev/null @@ -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; - } -} diff --git a/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/page.tsx b/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/page.tsx index 702cba673..e535cea6c 100644 --- a/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/page.tsx +++ b/apps/scandic-web/app/[lang]/webview/(views)/hotelreservation/my-stay/page.tsx @@ -1,53 +1,14 @@ -import { cookies } from "next/headers" -import { notFound } from "next/navigation" +import { Suspense } from "react" -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 { MyStaySkeleton } from "@/components/HotelReservation/MyStay/myStaySkeleton" 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 MyStayWebviewPage( props: PageArgs ) { const searchParams = await props.searchParams @@ -55,272 +16,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 ( - - ) - } - } else { - return ( - - ) - } - 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 ancillariesInput = { - fromDate, - hotelId: hotel.operaId, - toDate, - } - 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) - } - if (booking.showAncillaries) { - void getAncillaryPackages(ancillariesInput) - } - - 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(ancillariesInput) - } - - 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(params.lang) - ? new URL(getCurrentWebUrl({ path: "/", lang })) - : new URL(`${baseUrl}/${lang}/`) - - 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 - - promoUrl.searchParams.set("hotel", hotel.operaId) - - return ( - -
-
-
- {imageSrc && ( - {hotel.name} - )} -
-
-
-
- -
- {booking.showAncillaries && ancillaryPackagesPromise && ( - - )} - - - - - - -
-
- - -
- ) - } - - if (access === ERROR_BAD_REQUEST) { - return ( -
-
- -
-
- ) - } - - if (access === ERROR_UNAUTHORIZED) { - return ( -
-
- -

- {intl.formatMessage({ - defaultMessage: "You need to be logged in to view your booking", - })} -

-
- -

- {intl.formatMessage({ - defaultMessage: - "And you need to be logged in with the same member account that made the booking.", - })} -

-
-
-
- ) - } - - return notFound() -} - -function RenderAdditionalInfoForm({ - confirmationNumber, - lastName, -}: { - confirmationNumber: string - lastName: string -}) { return ( -
-
- -
-
+ }> + + + ) } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/GuestDetails/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/GuestDetails/index.tsx index c6a87fb80..7aad76df2 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/GuestDetails/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/GuestDetails/index.tsx @@ -1,6 +1,6 @@ "use client" import { zodResolver } from "@hookform/resolvers/zod" -import { useRouter } from "next/navigation" +import { usePathname, useRouter } from "next/navigation" import { useState } from "react" import { Dialog } from "react-aria-components" import { FormProvider, useForm } from "react-hook-form" @@ -10,6 +10,7 @@ import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { Typography } from "@scandic-hotels/design-system/Typography" import { profileEdit } from "@/constants/routes/myPages" +import { isWebview } from "@/constants/routes/webviews" import { trpc } from "@/lib/trpc/client" import MembershipLevelIcon from "@/components/Levels/Icon" @@ -50,6 +51,7 @@ export default function GuestDetails({ const utils = trpc.useUtils() const [currentStep, setCurrentStep] = useState(MODAL_STEPS.INITIAL) const [isLoading, setIsLoading] = useState(false) + const pathname = usePathname() const [isModifyGuestDetailsOpen, setIsModifyGuestDetailsOpen] = useState(false) @@ -206,116 +208,120 @@ export default function GuestDetails({ - {isMemberBooking ? ( - - ) : ( + {!isWebview(pathname) && ( <> - - {isModifyGuestDetailsOpen && ( - - + + + {intl.formatMessage({ + defaultMessage: "Modify guest details", + })} + + + + ) : ( + <> + - + + + + {intl.formatMessage({ + defaultMessage: "Modify guest details", + })} + + + + {isModifyGuestDetailsOpen && ( + + + {({ close }) => ( + + setIsModifyGuestDetailsOpen(false)} + content={ + guest && ( + + ) + } + primaryAction={{ + label: isFirstStep + ? intl.formatMessage({ + defaultMessage: "Save updates", + }) + : intl.formatMessage({ + defaultMessage: "Confirm", + }), + onClick: isFirstStep + ? () => setCurrentStep(MODAL_STEPS.CONFIRMATION) + : form.handleSubmit(onSubmit), + disabled: !form.formState.isValid || isLoading, + intent: isFirstStep ? "secondary" : "primary", + }} + secondaryAction={{ + label: isFirstStep + ? intl.formatMessage({ + defaultMessage: "Back", + }) + : intl.formatMessage({ + defaultMessage: "Cancel", + }), + onClick: () => { + close() + setCurrentStep(MODAL_STEPS.INITIAL) + }, + }} + /> + + )} + + + )} + )} )} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/index.tsx index 1ea0e7e07..12145b734 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/AddToCalendar/index.tsx @@ -1,5 +1,8 @@ "use client" +import { usePathname } from "next/navigation" + +import { isWebview } from "@/constants/routes/webviews" import { useMyStayStore } from "@/stores/my-stay" import AddToCalendar from "@/components/HotelReservation/AddToCalendar" @@ -11,6 +14,8 @@ import AddToCalendarButton from "./AddToCalendarButton" import type { EventAttributes } from "ics" export default function AddToCalendarAction() { + const pathName = usePathname() + const { checkInDate, checkOutDate, createDateTime, hotel } = useMyStayStore( (state) => ({ checkInDate: state.bookedRoom.checkInDate, @@ -44,6 +49,10 @@ export default function AddToCalendarAction() { hotel.hotelFacts.checkin.checkInTime ) + if (isWebview(pathName)) { + return null + } + return ( + ) + } + } else { + return ( + + ) + } + + 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 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 ( + +
+
+
+ {imageSrc && ( + {hotel.name} + )} +
+
+
+
+ +
+ {booking.showAncillaries && ancillaryPackagesPromise && ( + + )} + + + + + + {!isWebview && ( + + )} +
+
+ +
+ ) + } + + if (access === ERROR_BAD_REQUEST) { + return ( +
+
+ +
+
+ ) + } + + if (access === ERROR_UNAUTHORIZED) { + return ( +
+
+ +

+ {intl.formatMessage({ + defaultMessage: "You need to be logged in to view your booking", + })} +

+
+ +

+ {intl.formatMessage({ + defaultMessage: + "And you need to be logged in with the same member account that made the booking.", + })} +

+
+
+
+ ) + } + + return notFound() +} + +function RenderAdditionalInfoForm({ + confirmationNumber, + lastName, +}: { + confirmationNumber: string + lastName: string +}) { + return ( +
+
+ +
+
+ ) +} diff --git a/apps/scandic-web/constants/routes/webviews.ts b/apps/scandic-web/constants/routes/webviews.ts index ca2f2c6bd..9391df443 100644 --- a/apps/scandic-web/constants/routes/webviews.ts +++ b/apps/scandic-web/constants/routes/webviews.ts @@ -93,3 +93,7 @@ export const myStayWebviews = [ ] export const refreshWebviews = [...Object.values(refreshUrl)] + +export function isWebview(path: string) { + return webviews.includes(path) +}