Merged in feat/sw-2873-move-selecthotel-to-booking-flow (pull request #2727)
feat(SW-2873): Move select-hotel to booking flow * crude setup of select-hotel in partner-sas * wip * Fix linting * restructure tracking files * Remove dependency on trpc in tracking hooks * Move pageview tracking to common * Fix some lint and import issues * Add AlternativeHotelsPage * Add SelectHotelMapPage * Add AlternativeHotelsMapPage * remove next dependency in tracking store * Remove dependency on react in tracking hooks * move isSameBooking to booking-flow * Inject searchParamsComparator into tracking store * Move useTrackHardNavigation to common * Move useTrackSoftNavigation to common * Add TrackingSDK to partner-sas * call serverclient in layout * Remove unused css * Update types * Move HotelPin type * Fix todos * Merge branch 'master' into feat/sw-2873-move-selecthotel-to-booking-flow * Merge branch 'master' into feat/sw-2873-move-selecthotel-to-booking-flow * Fix component Approved-by: Joakim Jäderberg
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { SelectHotelSkeleton } from "@/components/HotelReservation/SelectHotel/SelectHotelSkeleton"
|
||||
import { SelectHotelSkeleton } from "@scandic-hotels/booking-flow/components/SelectHotel"
|
||||
|
||||
export default function AlternativeHotelsLoading() {
|
||||
return <SelectHotelSkeleton />
|
||||
|
||||
@@ -1,35 +1,30 @@
|
||||
import { notFound } from "next/navigation"
|
||||
import { Suspense } from "react"
|
||||
import { AlternativeHotelsMapPage as AlternativeHotelsMapPagePrimitive } from "@scandic-hotels/booking-flow/pages/AlternativeHotelsMapPage"
|
||||
|
||||
import { parseSelectHotelSearchParams } from "@scandic-hotels/booking-flow/utils/url"
|
||||
|
||||
import { SelectHotelMapContainer } from "@/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContainer"
|
||||
import { SelectHotelMapContainerSkeleton } from "@/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContainerSkeleton"
|
||||
import { MapContainer } from "@/components/MapContainer"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import styles from "./page.module.css"
|
||||
|
||||
import type { LangParams, NextSearchParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function SelectHotelMapPage(
|
||||
export default async function AlternativeHotelsMapPage(
|
||||
props: PageArgs<LangParams, NextSearchParams>
|
||||
) {
|
||||
const searchParams = await props.searchParams
|
||||
|
||||
const booking = parseSelectHotelSearchParams(searchParams)
|
||||
|
||||
if (!booking) return notFound()
|
||||
const lang = await getLang()
|
||||
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<MapContainer>
|
||||
<Suspense
|
||||
key={searchParams.hotel}
|
||||
fallback={<SelectHotelMapContainerSkeleton />}
|
||||
>
|
||||
<SelectHotelMapContainer booking={booking} isAlternativeHotels />
|
||||
</Suspense>
|
||||
</MapContainer>
|
||||
<AlternativeHotelsMapPagePrimitive
|
||||
lang={lang}
|
||||
searchParams={searchParams}
|
||||
renderTracking={(props) => (
|
||||
<TrackingSDK
|
||||
hotelInfo={props.hotelsTrackingData}
|
||||
pageData={props.pageTrackingData}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
import stringify from "json-stable-stringify-without-jsonify"
|
||||
import { cookies } from "next/headers"
|
||||
import { notFound } from "next/navigation"
|
||||
import { Suspense } from "react"
|
||||
import { AlternativeHotelsPage as AlternativeHotelsPagePrimitive } from "@scandic-hotels/booking-flow/pages/AlternativeHotelsPage"
|
||||
|
||||
import { parseSelectHotelSearchParams } from "@scandic-hotels/booking-flow/utils/url"
|
||||
import { alternativeHotelsMap } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
||||
|
||||
import { FamilyAndFriendsCodes } from "@/constants/booking"
|
||||
|
||||
import FnFNotAllowedAlert from "@/components/HotelReservation/FnFNotAllowedAlert/FnFNotAllowedAlert"
|
||||
import SelectHotel from "@/components/HotelReservation/SelectHotel"
|
||||
import { getHotels } from "@/components/HotelReservation/SelectHotel/helpers"
|
||||
import { getSelectHotelTracking } from "@/components/HotelReservation/SelectHotel/tracking"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getHotelSearchDetails } from "@/utils/hotelSearchDetails"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import {
|
||||
type LangParams,
|
||||
@@ -26,112 +13,18 @@ export default async function AlternativeHotelsPage(
|
||||
props: PageArgs<LangParams, NextSearchParams>
|
||||
) {
|
||||
const searchParams = await props.searchParams
|
||||
const params = await props.params
|
||||
const lang = await getLang()
|
||||
|
||||
const booking = parseSelectHotelSearchParams(searchParams)
|
||||
|
||||
if (!booking) return notFound()
|
||||
|
||||
const searchDetails = await getHotelSearchDetails(booking, true)
|
||||
|
||||
if (!searchDetails || !searchDetails.hotel || !searchDetails.city) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
if (
|
||||
booking.bookingCode &&
|
||||
FamilyAndFriendsCodes.includes(booking.bookingCode)
|
||||
) {
|
||||
const cookieStore = await cookies()
|
||||
const isInvalidFNF = cookieStore.get("sc")?.value !== "1"
|
||||
|
||||
if (isInvalidFNF) {
|
||||
return <FnFNotAllowedAlert />
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This needs to be refactored into its
|
||||
// own functions
|
||||
const hotels = await getHotels({
|
||||
fromDate: booking.fromDate,
|
||||
toDate: booking.toDate,
|
||||
rooms: booking.rooms,
|
||||
isAlternativeFor: searchDetails.hotel,
|
||||
bookingCode: booking.bookingCode,
|
||||
city: searchDetails.city,
|
||||
redemption: !!searchDetails.redemption,
|
||||
})
|
||||
|
||||
const arrivalDate = new Date(booking.fromDate)
|
||||
const departureDate = new Date(booking.toDate)
|
||||
|
||||
const isRedemptionAvailability = searchDetails.redemption
|
||||
? hotels.some(
|
||||
(hotel) => hotel.availability.productType?.redemptions?.length
|
||||
)
|
||||
: false
|
||||
|
||||
const isBookingCodeRateAvailable = booking.bookingCode
|
||||
? hotels.some(
|
||||
(hotel) =>
|
||||
hotel.availability.bookingCode &&
|
||||
hotel.availability.status === "Available"
|
||||
)
|
||||
: false
|
||||
|
||||
const { hotelsTrackingData, pageTrackingData } = getSelectHotelTracking({
|
||||
lang: params.lang,
|
||||
pageId: searchDetails.hotel ? "alternative-hotels" : "select-hotel",
|
||||
pageName: searchDetails.hotel
|
||||
? "hotelreservation|alternative-hotels"
|
||||
: "hotelreservation|select-hotel",
|
||||
siteSections: searchDetails.hotel
|
||||
? "hotelreservation|alternative-hotels"
|
||||
: "hotelreservation|select-hotel",
|
||||
arrivalDate,
|
||||
departureDate,
|
||||
rooms: booking.rooms,
|
||||
hotelsResult: hotels?.length ?? 0,
|
||||
searchTerm: searchDetails.hotel
|
||||
? booking.hotelId
|
||||
: searchDetails.cityIdentifier,
|
||||
country: hotels?.[0]?.hotel.address.country,
|
||||
hotelCity: hotels?.[0]?.hotel.address.city,
|
||||
bookingCode: booking.bookingCode,
|
||||
isBookingCodeRateAvailable,
|
||||
isRedemption: searchDetails.redemption,
|
||||
isRedemptionAvailable: isRedemptionAvailability,
|
||||
})
|
||||
|
||||
const mapHref = alternativeHotelsMap(params.lang)
|
||||
|
||||
const intl = await getIntl()
|
||||
const title = intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "Alternatives for {value}",
|
||||
},
|
||||
{
|
||||
value: searchDetails.hotel.name,
|
||||
}
|
||||
)
|
||||
const suspenseKey = stringify(searchParams)
|
||||
return (
|
||||
<>
|
||||
<SelectHotel
|
||||
bookingCode={booking.bookingCode}
|
||||
city={searchDetails.city}
|
||||
hotels={hotels}
|
||||
isAlternative={!!searchDetails.hotel}
|
||||
isBookingCodeRateAvailable={isBookingCodeRateAvailable}
|
||||
mapHref={mapHref}
|
||||
title={title}
|
||||
/>
|
||||
<Suspense key={`${suspenseKey}-tracking`} fallback={null}>
|
||||
<AlternativeHotelsPagePrimitive
|
||||
lang={lang}
|
||||
searchParams={searchParams}
|
||||
renderTracking={(props) => (
|
||||
<TrackingSDK
|
||||
pageData={pageTrackingData}
|
||||
hotelInfo={hotelsTrackingData}
|
||||
hotelInfo={props.hotelsTrackingData}
|
||||
pageData={props.pageTrackingData}
|
||||
/>
|
||||
</Suspense>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ import { cookies } from "next/headers"
|
||||
import { notFound } from "next/navigation"
|
||||
import { Suspense } from "react"
|
||||
|
||||
import FnFNotAllowedAlert from "@scandic-hotels/booking-flow/components/FnFNotAllowedAlert"
|
||||
import { parseDetailsSearchParams } from "@scandic-hotels/booking-flow/utils/url"
|
||||
import { FamilyAndFriendsCodes } from "@scandic-hotels/common/constants/familyAndFriends"
|
||||
|
||||
import { FamilyAndFriendsCodes } from "@/constants/booking"
|
||||
import {
|
||||
getBreakfastPackages,
|
||||
getHotel,
|
||||
@@ -19,7 +20,6 @@ import RoomOne from "@/components/HotelReservation/EnterDetails/Room/One"
|
||||
import DesktopSummary from "@/components/HotelReservation/EnterDetails/Summary/Desktop"
|
||||
import MobileSummary from "@/components/HotelReservation/EnterDetails/Summary/Mobile"
|
||||
import EnterDetailsTrackingWrapper from "@/components/HotelReservation/EnterDetails/Tracking"
|
||||
import FnFNotAllowedAlert from "@/components/HotelReservation/FnFNotAllowedAlert/FnFNotAllowedAlert"
|
||||
import RoomProvider from "@/providers/Details/RoomProvider"
|
||||
import EnterDetailsProvider from "@/providers/EnterDetailsProvider"
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKPageData,
|
||||
} from "@scandic-hotels/common/tracking/types"
|
||||
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
|
||||
import styles from "./page.module.css"
|
||||
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKPageData,
|
||||
} from "@/types/components/tracking"
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function HotelReservationPage(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SelectHotelSkeleton } from "@/components/HotelReservation/SelectHotel/SelectHotelSkeleton"
|
||||
import { SelectHotelSkeleton } from "@scandic-hotels/booking-flow/components/SelectHotel"
|
||||
|
||||
export default function SelectHotelLoading() {
|
||||
return <SelectHotelSkeleton />
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import stringify from "json-stable-stringify-without-jsonify"
|
||||
import { notFound } from "next/navigation"
|
||||
import { Suspense } from "react"
|
||||
import { SelectHotelMapPage as SelectHotelMapPagePrimitive } from "@scandic-hotels/booking-flow/pages/SelectHotelMapPage"
|
||||
|
||||
import { parseSelectHotelSearchParams } from "@scandic-hotels/booking-flow/utils/url"
|
||||
|
||||
import { SelectHotelMapContainer } from "@/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContainer"
|
||||
import { SelectHotelMapContainerSkeleton } from "@/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContainerSkeleton"
|
||||
import { MapContainer } from "@/components/MapContainer"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import styles from "./page.module.css"
|
||||
|
||||
@@ -16,22 +11,20 @@ export default async function SelectHotelMapPage(
|
||||
props: PageArgs<LangParams, NextSearchParams>
|
||||
) {
|
||||
const searchParams = await props.searchParams
|
||||
const suspenseKey = stringify(searchParams)
|
||||
|
||||
const booking = parseSelectHotelSearchParams(searchParams)
|
||||
|
||||
if (!booking) return notFound()
|
||||
const lang = await getLang()
|
||||
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<MapContainer>
|
||||
<Suspense
|
||||
key={suspenseKey}
|
||||
fallback={<SelectHotelMapContainerSkeleton />}
|
||||
>
|
||||
<SelectHotelMapContainer booking={booking} />
|
||||
</Suspense>
|
||||
</MapContainer>
|
||||
<SelectHotelMapPagePrimitive
|
||||
lang={lang}
|
||||
searchParams={searchParams}
|
||||
renderTracking={(props) => (
|
||||
<TrackingSDK
|
||||
hotelInfo={props.hotelsTrackingData}
|
||||
pageData={props.pageTrackingData}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,7 @@
|
||||
import stringify from "json-stable-stringify-without-jsonify"
|
||||
import { cookies } from "next/headers"
|
||||
import { notFound } from "next/navigation"
|
||||
import { Suspense } from "react"
|
||||
import { SelectHotelPage as SelectHotelPagePrimitive } from "@scandic-hotels/booking-flow/pages/SelectHotelPage"
|
||||
|
||||
import { parseSelectHotelSearchParams } from "@scandic-hotels/booking-flow/utils/url"
|
||||
import { selectHotelMap } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
||||
|
||||
import { FamilyAndFriendsCodes } from "@/constants/booking"
|
||||
|
||||
import FnFNotAllowedAlert from "@/components/HotelReservation/FnFNotAllowedAlert/FnFNotAllowedAlert"
|
||||
import SelectHotel from "@/components/HotelReservation/SelectHotel"
|
||||
import { getHotels } from "@/components/HotelReservation/SelectHotel/helpers"
|
||||
import { getSelectHotelTracking } from "@/components/HotelReservation/SelectHotel/tracking"
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getHotelSearchDetails } from "@/utils/hotelSearchDetails"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import type { LangParams, NextSearchParams, PageArgs } from "@/types/params"
|
||||
|
||||
@@ -21,93 +9,18 @@ export default async function SelectHotelPage(
|
||||
props: PageArgs<LangParams, NextSearchParams>
|
||||
) {
|
||||
const searchParams = await props.searchParams
|
||||
const params = await props.params
|
||||
const lang = await getLang()
|
||||
|
||||
const booking = parseSelectHotelSearchParams(searchParams)
|
||||
|
||||
if (!booking) return notFound()
|
||||
|
||||
const searchDetails = await getHotelSearchDetails(booking)
|
||||
|
||||
if (!searchDetails || !searchDetails.city) return notFound()
|
||||
|
||||
if (
|
||||
booking.bookingCode &&
|
||||
FamilyAndFriendsCodes.includes(booking.bookingCode)
|
||||
) {
|
||||
const cookieStore = await cookies()
|
||||
const isInvalidFNF = cookieStore.get("sc")?.value !== "1"
|
||||
|
||||
if (isInvalidFNF) {
|
||||
return <FnFNotAllowedAlert />
|
||||
}
|
||||
}
|
||||
|
||||
const { city, redemption } = searchDetails
|
||||
|
||||
const hotels = await getHotels({
|
||||
fromDate: booking.fromDate,
|
||||
toDate: booking.toDate,
|
||||
rooms: booking.rooms,
|
||||
isAlternativeFor: null,
|
||||
bookingCode: booking.bookingCode,
|
||||
city: city,
|
||||
redemption: !!redemption,
|
||||
})
|
||||
|
||||
const isRedemptionAvailability = redemption
|
||||
? hotels.some(
|
||||
(hotel) => hotel.availability.productType?.redemptions?.length
|
||||
)
|
||||
: false
|
||||
|
||||
const isBookingCodeRateAvailable = booking.bookingCode
|
||||
? hotels.some(
|
||||
(hotel) =>
|
||||
hotel.availability.bookingCode &&
|
||||
hotel.availability.status === "Available"
|
||||
)
|
||||
: false
|
||||
|
||||
const arrivalDate = new Date(booking.fromDate)
|
||||
const departureDate = new Date(booking.toDate)
|
||||
|
||||
const { hotelsTrackingData, pageTrackingData } = getSelectHotelTracking({
|
||||
rooms: booking.rooms,
|
||||
lang: params.lang,
|
||||
pageId: "select-hotel",
|
||||
pageName: "hotelreservation|select-hotel",
|
||||
siteSections: "hotelreservation|select-hotel",
|
||||
arrivalDate,
|
||||
departureDate,
|
||||
hotelsResult: hotels?.length ?? 0,
|
||||
searchTerm: booking.hotelId,
|
||||
country: hotels?.[0]?.hotel.address.country,
|
||||
hotelCity: hotels?.[0]?.hotel.address.city,
|
||||
bookingCode: booking.bookingCode,
|
||||
isBookingCodeRateAvailable,
|
||||
isRedemption: redemption,
|
||||
isRedemptionAvailable: isRedemptionAvailability,
|
||||
})
|
||||
|
||||
const mapHref = selectHotelMap(params.lang)
|
||||
const suspenseKey = stringify(searchParams)
|
||||
return (
|
||||
<>
|
||||
<SelectHotel
|
||||
bookingCode={booking.bookingCode}
|
||||
isBookingCodeRateAvailable={isBookingCodeRateAvailable}
|
||||
city={city}
|
||||
hotels={hotels}
|
||||
mapHref={mapHref}
|
||||
title={city.name}
|
||||
/>
|
||||
<Suspense key={`${suspenseKey}-tracking`} fallback={null}>
|
||||
<SelectHotelPagePrimitive
|
||||
lang={lang}
|
||||
searchParams={searchParams}
|
||||
renderTracking={(props) => (
|
||||
<TrackingSDK
|
||||
pageData={pageTrackingData}
|
||||
hotelInfo={hotelsTrackingData}
|
||||
hotelInfo={props.hotelsTrackingData}
|
||||
pageData={props.pageTrackingData}
|
||||
/>
|
||||
</Suspense>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import {
|
||||
TrackingChannelEnum,
|
||||
type TrackingSDKPageData,
|
||||
} from "@/types/components/tracking"
|
||||
} from "@scandic-hotels/common/tracking/types"
|
||||
|
||||
import TrackingSDK from "@/components/TrackingSDK"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
export default async function Tracking() {
|
||||
const lang = await getLang()
|
||||
|
||||
@@ -9,13 +9,13 @@ import Script from "next/script"
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
import { NuqsAdapter } from "nuqs/adapters/next/app"
|
||||
|
||||
import { BookingFlowTrackingProvider } from "@scandic-hotels/booking-flow/BookingFlowTrackingProvider"
|
||||
import { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import { ToastHandler } from "@scandic-hotels/design-system/ToastHandler"
|
||||
|
||||
import TrpcProvider from "@/lib/trpc/Provider"
|
||||
|
||||
import { SessionRefresher } from "@/components/Auth/TokenRefresher"
|
||||
import { BookingFlowProviders } from "@/components/BookingFlowProviders"
|
||||
import CookieBotConsent from "@/components/CookieBot"
|
||||
import Footer from "@/components/Footer"
|
||||
import Header from "@/components/Header"
|
||||
@@ -30,8 +30,6 @@ import { FontPreload } from "@/fonts/font-preloading"
|
||||
import { getMessages } from "@/i18n"
|
||||
import ClientIntlProvider from "@/i18n/Provider"
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
import { trackAccordionClick, trackOpenSidePeekEvent } from "@/utils/tracking"
|
||||
import { trackBookingSearchClick } from "@/utils/tracking/booking"
|
||||
|
||||
import type { LangParams, LayoutArgs } from "@/types/params"
|
||||
|
||||
@@ -72,13 +70,7 @@ export default async function RootLayout(
|
||||
<NuqsAdapter>
|
||||
<TrpcProvider>
|
||||
<RACRouterProvider>
|
||||
<BookingFlowTrackingProvider
|
||||
trackingFunctions={{
|
||||
trackBookingSearchClick,
|
||||
trackAccordionItemOpen: trackAccordionClick,
|
||||
trackOpenSidePeek: trackOpenSidePeekEvent,
|
||||
}}
|
||||
>
|
||||
<BookingFlowProviders>
|
||||
<RouteChange />
|
||||
<SitewideAlert />
|
||||
<Header />
|
||||
@@ -91,7 +83,7 @@ export default async function RootLayout(
|
||||
<CookieBotConsent />
|
||||
<UserExists />
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</BookingFlowTrackingProvider>
|
||||
</BookingFlowProviders>
|
||||
</RACRouterProvider>
|
||||
</TrpcProvider>
|
||||
</NuqsAdapter>
|
||||
|
||||
Reference in New Issue
Block a user