Merged in feat/booking-flow-performance (pull request #1282)
feat: booking flow performance * feat: booking flow performance * Cleanup Approved-by: Michael Zetterberg Approved-by: Pontus Dreij
This commit is contained in:
@@ -22,6 +22,7 @@ export default function FormContent({
|
|||||||
locations,
|
locations,
|
||||||
formId,
|
formId,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
isSearching,
|
||||||
}: BookingWidgetFormContentProps) {
|
}: BookingWidgetFormContentProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const selectedDate = useWatch({ name: "date" })
|
const selectedDate = useWatch({ name: "date" })
|
||||||
@@ -84,6 +85,7 @@ export default function FormContent({
|
|||||||
intent="primary"
|
intent="primary"
|
||||||
theme="base"
|
theme="base"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
disabled={isSearching}
|
||||||
>
|
>
|
||||||
<Caption
|
<Caption
|
||||||
color="white"
|
color="white"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
|
import { useEffect, useState, useTransition } from "react"
|
||||||
import { Form as FormRAC } from "react-aria-components"
|
import { Form as FormRAC } from "react-aria-components"
|
||||||
import { useFormContext } from "react-hook-form"
|
import { useFormContext } from "react-hook-form"
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ export default function Form({
|
|||||||
}: BookingWidgetFormProps) {
|
}: BookingWidgetFormProps) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
|
const [isPending, startTransition] = useTransition()
|
||||||
|
|
||||||
const classNames = bookingWidgetVariants({
|
const classNames = bookingWidgetVariants({
|
||||||
type,
|
type,
|
||||||
@@ -36,7 +38,6 @@ export default function Form({
|
|||||||
|
|
||||||
function onSubmit(data: BookingWidgetSchema) {
|
function onSubmit(data: BookingWidgetSchema) {
|
||||||
const locationData: Location = JSON.parse(decodeURIComponent(data.location))
|
const locationData: Location = JSON.parse(decodeURIComponent(data.location))
|
||||||
|
|
||||||
const bookingFlowPage =
|
const bookingFlowPage =
|
||||||
locationData.type == "cities" ? selectHotel(lang) : selectRate(lang)
|
locationData.type == "cities" ? selectHotel(lang) : selectRate(lang)
|
||||||
const bookingWidgetParams = convertObjToSearchParams({
|
const bookingWidgetParams = convertObjToSearchParams({
|
||||||
@@ -51,7 +52,9 @@ export default function Form({
|
|||||||
})
|
})
|
||||||
|
|
||||||
onClose()
|
onClose()
|
||||||
router.push(`${bookingFlowPage}?${bookingWidgetParams.toString()}`)
|
startTransition(() => {
|
||||||
|
router.push(`${bookingFlowPage}?${bookingWidgetParams.toString()}`)
|
||||||
|
})
|
||||||
if (!data.bookingCode?.value) {
|
if (!data.bookingCode?.value) {
|
||||||
setValue("bookingCode.remember", false)
|
setValue("bookingCode.remember", false)
|
||||||
localStorage.removeItem("bookingCode")
|
localStorage.removeItem("bookingCode")
|
||||||
@@ -72,6 +75,7 @@ export default function Form({
|
|||||||
locations={locations}
|
locations={locations}
|
||||||
formId={formId}
|
formId={formId}
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
isSearching={isPending}
|
||||||
/>
|
/>
|
||||||
</FormRAC>
|
</FormRAC>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {
|
import { getHeader, getLanguageSwitcher } from "@/lib/trpc/memoizedRequests"
|
||||||
getHeader,
|
|
||||||
getLanguageSwitcher,
|
import { auth } from "@/auth"
|
||||||
getName,
|
import { isValidSession } from "@/utils/session"
|
||||||
} from "@/lib/trpc/memoizedRequests"
|
|
||||||
|
|
||||||
import MobileMenu from "../MobileMenu"
|
import MobileMenu from "../MobileMenu"
|
||||||
|
|
||||||
@@ -12,7 +11,7 @@ export default async function MobileMenuWrapper({
|
|||||||
// preloaded
|
// preloaded
|
||||||
const languages = await getLanguageSwitcher()
|
const languages = await getLanguageSwitcher()
|
||||||
const header = await getHeader()
|
const header = await getHeader()
|
||||||
const user = await getName()
|
const session = await auth()
|
||||||
|
|
||||||
if (!languages || !header) {
|
if (!languages || !header) {
|
||||||
return null
|
return null
|
||||||
@@ -22,7 +21,7 @@ export default async function MobileMenuWrapper({
|
|||||||
<MobileMenu
|
<MobileMenu
|
||||||
languageUrls={languages.urls}
|
languageUrls={languages.urls}
|
||||||
topLink={header.data.topLink}
|
topLink={header.data.topLink}
|
||||||
isLoggedIn={!!user}
|
isLoggedIn={isValidSession(session)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</MobileMenu>
|
</MobileMenu>
|
||||||
|
|||||||
@@ -11,11 +11,10 @@ import TopMenu, { TopMenuSkeleton } from "./TopMenu"
|
|||||||
|
|
||||||
import styles from "./header.module.css"
|
import styles from "./header.module.css"
|
||||||
|
|
||||||
export default async function Header() {
|
export default function Header() {
|
||||||
void getHeader()
|
void getHeader()
|
||||||
void getLanguageSwitcher()
|
void getLanguageSwitcher()
|
||||||
void getName()
|
void getName()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
<Suspense fallback={<TopMenuSkeleton />}>
|
<Suspense fallback={<TopMenuSkeleton />}>
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
.hotelAlert {
|
|
||||||
max-width: var(--max-width-navigation);
|
|
||||||
margin: 0 auto;
|
|
||||||
padding-top: var(--Spacing-x-one-and-half);
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
import { dt } from "@/lib/dt"
|
|
||||||
import { getRoomsAvailability } from "@/lib/trpc/memoizedRequests"
|
|
||||||
|
|
||||||
import Alert from "@/components/TempDesignSystem/Alert"
|
|
||||||
import { getIntl } from "@/i18n"
|
|
||||||
import { safeTry } from "@/utils/safeTry"
|
|
||||||
|
|
||||||
import { generateChildrenString } from "../../utils"
|
|
||||||
import { combineRoomAvailabilities } from "../utils"
|
|
||||||
|
|
||||||
import styles from "./NoRoomsAlert.module.css"
|
|
||||||
|
|
||||||
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
|
|
||||||
import { AlertTypeEnum } from "@/types/enums/alert"
|
|
||||||
import type { Lang } from "@/constants/languages"
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
hotelId: number
|
|
||||||
lang: Lang
|
|
||||||
adultArray: number[]
|
|
||||||
childArray?: Child[]
|
|
||||||
fromDate: Date
|
|
||||||
toDate: Date
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function NoRoomsAlert({
|
|
||||||
hotelId,
|
|
||||||
fromDate,
|
|
||||||
toDate,
|
|
||||||
childArray,
|
|
||||||
adultArray,
|
|
||||||
lang,
|
|
||||||
}: Props) {
|
|
||||||
const fromDateString = dt(fromDate).format("YYYY-MM-DD")
|
|
||||||
const toDateString = dt(toDate).format("YYYY-MM-DD")
|
|
||||||
|
|
||||||
const uniqueAdultCounts = [...new Set(adultArray)]
|
|
||||||
const roomsAvailabilityPromises = uniqueAdultCounts.map((adultCount) => {
|
|
||||||
return safeTry(
|
|
||||||
getRoomsAvailability({
|
|
||||||
hotelId: hotelId,
|
|
||||||
roomStayStartDate: fromDateString,
|
|
||||||
roomStayEndDate: toDateString,
|
|
||||||
adults: adultCount,
|
|
||||||
children:
|
|
||||||
childArray && childArray.length > 0
|
|
||||||
? generateChildrenString(childArray)
|
|
||||||
: undefined,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const roomsAvailabilityResults = await Promise.all(roomsAvailabilityPromises)
|
|
||||||
|
|
||||||
const roomsAvailability = combineRoomAvailabilities({
|
|
||||||
availabilityResults: roomsAvailabilityResults,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!roomsAvailability) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const noRoomsAvailable = roomsAvailability.roomConfigurations.reduce(
|
|
||||||
(acc, room) => {
|
|
||||||
return acc && room.status === "NotAvailable"
|
|
||||||
},
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!noRoomsAvailable) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const intl = await getIntl(lang)
|
|
||||||
return (
|
|
||||||
<div className={styles.hotelAlert}>
|
|
||||||
<Alert
|
|
||||||
type={AlertTypeEnum.Info}
|
|
||||||
text={intl.formatMessage({
|
|
||||||
id: "There are no rooms available that match your request",
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import { Suspense } from "react"
|
|
||||||
|
|
||||||
import { getHotel } from "@/lib/trpc/memoizedRequests"
|
import { getHotel } from "@/lib/trpc/memoizedRequests"
|
||||||
|
|
||||||
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
||||||
@@ -15,7 +13,6 @@ import { getSingleDecimal } from "@/utils/numberFormatting"
|
|||||||
|
|
||||||
import ReadMore from "../../ReadMore"
|
import ReadMore from "../../ReadMore"
|
||||||
import TripAdvisorChip from "../../TripAdvisorChip"
|
import TripAdvisorChip from "../../TripAdvisorChip"
|
||||||
import { NoRoomsAlert } from "./NoRoomsAlert"
|
|
||||||
|
|
||||||
import styles from "./hotelInfoCard.module.css"
|
import styles from "./hotelInfoCard.module.css"
|
||||||
|
|
||||||
@@ -24,7 +21,6 @@ import type { HotelInfoCardProps } from "@/types/components/hotelReservation/sel
|
|||||||
export default async function HotelInfoCard({
|
export default async function HotelInfoCard({
|
||||||
hotelId,
|
hotelId,
|
||||||
lang,
|
lang,
|
||||||
...props
|
|
||||||
}: HotelInfoCardProps) {
|
}: HotelInfoCardProps) {
|
||||||
const hotelData = await getHotel({
|
const hotelData = await getHotel({
|
||||||
hotelId: hotelId.toString(),
|
hotelId: hotelId.toString(),
|
||||||
@@ -119,10 +115,6 @@ export default async function HotelInfoCard({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<Suspense fallback={null} key={hotelId}>
|
|
||||||
<NoRoomsAlert hotelId={hotelId} lang={lang} {...props} />
|
|
||||||
</Suspense>
|
|
||||||
</article>
|
</article>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
} from "@/lib/trpc/memoizedRequests"
|
} from "@/lib/trpc/memoizedRequests"
|
||||||
|
|
||||||
import { auth } from "@/auth"
|
import { auth } from "@/auth"
|
||||||
|
import Alert from "@/components/TempDesignSystem/Alert"
|
||||||
|
import { getIntl } from "@/i18n"
|
||||||
import { safeTry } from "@/utils/safeTry"
|
import { safeTry } from "@/utils/safeTry"
|
||||||
import { isValidSession } from "@/utils/session"
|
import { isValidSession } from "@/utils/session"
|
||||||
|
|
||||||
@@ -13,8 +15,11 @@ import { generateChildrenString } from "../../utils"
|
|||||||
import { combineRoomAvailabilities } from "../utils"
|
import { combineRoomAvailabilities } from "../utils"
|
||||||
import Rooms from "."
|
import Rooms from "."
|
||||||
|
|
||||||
|
import styles from "./rooms.module.css"
|
||||||
|
|
||||||
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
|
||||||
import type { RoomsContainerProps } from "@/types/components/hotelReservation/selectRate/roomsContainer"
|
import type { RoomsContainerProps } from "@/types/components/hotelReservation/selectRate/roomsContainer"
|
||||||
|
import { AlertTypeEnum } from "@/types/enums/alert"
|
||||||
|
|
||||||
export async function RoomsContainer({
|
export async function RoomsContainer({
|
||||||
adultArray,
|
adultArray,
|
||||||
@@ -77,14 +82,24 @@ export async function RoomsContainer({
|
|||||||
availabilityResults: roomsAvailabilityResults,
|
availabilityResults: roomsAvailabilityResults,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const intl = await getIntl(lang)
|
||||||
|
|
||||||
if (packagesError) {
|
if (packagesError) {
|
||||||
// TODO: Log packages error
|
// TODO: Log packages error
|
||||||
console.error("[RoomsContainer] unable to fetch packages")
|
console.error("[RoomsContainer] unable to fetch packages")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!roomsAvailability) {
|
if (!roomsAvailability) {
|
||||||
// HotelInfoCard has the logic for displaying when there are no rooms available
|
return (
|
||||||
return null
|
<div className={styles.hotelAlert}>
|
||||||
|
<Alert
|
||||||
|
type={AlertTypeEnum.Info}
|
||||||
|
text={intl.formatMessage({
|
||||||
|
id: "There are no rooms available that match your request",
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -48,3 +48,9 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hotelAlert {
|
||||||
|
max-width: var(--max-width-navigation);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: var(--Spacing-x-one-and-half);
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export interface BookingWidgetFormContentProps {
|
|||||||
locations: Locations
|
locations: Locations
|
||||||
formId: string
|
formId: string
|
||||||
onSubmit: () => void
|
onSubmit: () => void
|
||||||
|
isSearching: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ActionType {
|
export enum ActionType {
|
||||||
|
|||||||
Reference in New Issue
Block a user