Merged in feature/bookingwidget-client-side (pull request #1481)
Move more of BookingWidget to client SW-1639 * feat: move getLocations in booking widget to client side so that it's also cached on the client reducing the blinking when switching urls (and reducing duplicate calls) Approved-by: Linus Flood
This commit is contained in:
@@ -3,6 +3,7 @@ import { notFound } from "next/navigation"
|
||||
import { getLocations } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import { generateChildrenString } from "@/components/HotelReservation/utils"
|
||||
import { safeTry } from "@/utils/safeTry"
|
||||
import { convertSearchParamsToObj, type SelectHotelParams } from "@/utils/url"
|
||||
|
||||
import type {
|
||||
@@ -46,13 +47,14 @@ export async function getHotelSearchDetails<
|
||||
): Promise<HotelSearchDetails<T> | null> {
|
||||
const selectHotelParams = convertSearchParamsToObj<T>(searchParams)
|
||||
|
||||
const locations = await getLocations()
|
||||
|
||||
if (!locations || "error" in locations) return null
|
||||
const [locations, error] = await safeTry(getLocations())
|
||||
if (!locations || error) {
|
||||
return null
|
||||
}
|
||||
|
||||
const hotel =
|
||||
("hotelId" in selectHotelParams &&
|
||||
(locations.data.find(
|
||||
(locations.find(
|
||||
(location) =>
|
||||
isHotelLocation(location) &&
|
||||
"operaId" in location &&
|
||||
@@ -72,7 +74,7 @@ export async function getHotelSearchDetails<
|
||||
|
||||
const city =
|
||||
(typeof cityName === "string" &&
|
||||
locations.data.find(
|
||||
locations.find(
|
||||
(location) => location.name.toLowerCase() === cityName.toLowerCase()
|
||||
)) ||
|
||||
null
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { env } from "@/env/server"
|
||||
import { getHotel, getHotelPage } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import BookingWidget, { preload } from "@/components/BookingWidget"
|
||||
import BookingWidget from "@/components/BookingWidget"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
|
||||
@@ -16,8 +16,6 @@ export default async function BookingWidgetPage({
|
||||
return null
|
||||
}
|
||||
|
||||
preload()
|
||||
|
||||
if (params.contentType === PageContentTypeEnum.hotelPage) {
|
||||
const hotelPageData = await getHotelPage()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { env } from "@/env/server"
|
||||
|
||||
import BookingWidget, { preload } from "@/components/BookingWidget"
|
||||
import BookingWidget from "@/components/BookingWidget"
|
||||
|
||||
import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
@@ -12,7 +12,5 @@ export default async function BookingWidgetPage({
|
||||
return null
|
||||
}
|
||||
|
||||
preload()
|
||||
|
||||
return <BookingWidget bookingWidgetSearchParams={searchParams} />
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { env } from "@/env/server"
|
||||
|
||||
import BookingWidget, { preload } from "@/components/BookingWidget"
|
||||
import BookingWidget from "@/components/BookingWidget"
|
||||
|
||||
import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
|
||||
import type { PageArgs } from "@/types/params"
|
||||
@@ -12,7 +12,5 @@ export default async function BookingWidgetPage({
|
||||
return null
|
||||
}
|
||||
|
||||
preload()
|
||||
|
||||
return <BookingWidget bookingWidgetSearchParams={searchParams} />
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from "react"
|
||||
import { FormProvider, useForm } from "react-hook-form"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
import { trpc } from "@/lib/trpc/client"
|
||||
import { StickyElementNameEnum } from "@/stores/sticky-position"
|
||||
|
||||
import Form, {
|
||||
@@ -12,6 +13,7 @@ import Form, {
|
||||
} from "@/components/Forms/BookingWidget"
|
||||
import { bookingWidgetSchema } from "@/components/Forms/BookingWidget/schema"
|
||||
import { CloseLargeIcon } from "@/components/Icons"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import useStickyPosition from "@/hooks/useStickyPosition"
|
||||
import { debounce } from "@/utils/debounce"
|
||||
import isValidJson from "@/utils/isValidJson"
|
||||
@@ -31,32 +33,14 @@ import type {
|
||||
} from "@/types/components/bookingWidget"
|
||||
import type { Location } from "@/types/trpc/routers/hotel/locations"
|
||||
|
||||
function getLocationObj(locations: Location[], destination: string) {
|
||||
try {
|
||||
const location = locations.find((location) => {
|
||||
if (location.type === "hotels") {
|
||||
return location.operaId === destination
|
||||
} else if (location.type === "cities") {
|
||||
return location.name.toLowerCase() === destination.toLowerCase()
|
||||
}
|
||||
})
|
||||
|
||||
if (location) {
|
||||
return location
|
||||
}
|
||||
} catch (_) {
|
||||
// ignore any errors
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export default function BookingWidgetClient({
|
||||
locations,
|
||||
type,
|
||||
bookingWidgetSearchParams,
|
||||
}: BookingWidgetClientProps) {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const bookingWidgetRef = useRef(null)
|
||||
const lang = useLang()
|
||||
const [locations] = trpc.hotel.locations.get.useSuspenseQuery({ lang })
|
||||
|
||||
useStickyPosition({
|
||||
ref: bookingWidgetRef,
|
||||
@@ -225,3 +209,22 @@ export function BookingWidgetSkeleton() {
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function getLocationObj(locations: Location[], destination: string) {
|
||||
try {
|
||||
const location = locations.find((location) => {
|
||||
if (location.type === "hotels") {
|
||||
return location.operaId === destination
|
||||
} else if (location.type === "cities") {
|
||||
return location.name.toLowerCase() === destination.toLowerCase()
|
||||
}
|
||||
})
|
||||
|
||||
if (location) {
|
||||
return location
|
||||
}
|
||||
} catch (_) {
|
||||
// ignore any errors
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
import {
|
||||
getLocations,
|
||||
isBookingWidgetHidden,
|
||||
} from "@/lib/trpc/memoizedRequests"
|
||||
import { isBookingWidgetHidden } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import BookingWidgetClient from "./Client"
|
||||
|
||||
import type { BookingWidgetProps } from "@/types/components/bookingWidget"
|
||||
|
||||
export function preload() {
|
||||
void getLocations()
|
||||
}
|
||||
|
||||
export default async function BookingWidget({
|
||||
type,
|
||||
bookingWidgetSearchParams,
|
||||
@@ -21,15 +14,8 @@ export default async function BookingWidget({
|
||||
return null
|
||||
}
|
||||
|
||||
const locations = await getLocations()
|
||||
|
||||
if (!locations || "error" in locations) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<BookingWidgetClient
|
||||
locations={locations.data}
|
||||
type={type}
|
||||
bookingWidgetSearchParams={bookingWidgetSearchParams}
|
||||
/>
|
||||
|
||||
@@ -21,7 +21,8 @@ import type {
|
||||
import type { GetSavedPaymentCardsInput } from "@/server/routers/user/input"
|
||||
|
||||
export const getLocations = cache(async function getMemoizedLocations() {
|
||||
return serverClient().hotel.locations.get()
|
||||
const lang = getLang()
|
||||
return serverClient().hotel.locations.get({ lang })
|
||||
})
|
||||
|
||||
export const getProfile = cache(async function getMemoizedProfile() {
|
||||
|
||||
@@ -140,3 +140,7 @@ export const getHotelsByCountryInput = z.object({
|
||||
export const getHotelsByCityIdentifierInput = z.object({
|
||||
cityIdentifier: z.string(),
|
||||
})
|
||||
|
||||
export const getLocationsInput = z.object({
|
||||
lang: z.nativeEnum(Lang),
|
||||
})
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
getHotelsByCountryInput,
|
||||
getHotelsByCSFilterInput,
|
||||
getHotelsByHotelIdsAvailabilityInputSchema,
|
||||
getLocationsInput,
|
||||
getMeetingRoomsInputSchema,
|
||||
hotelInputSchema,
|
||||
hotelsAvailabilityInputSchema,
|
||||
@@ -1272,9 +1273,14 @@ export const hotelQueryRouter = router({
|
||||
return validateHotelData.data.map((id: string) => parseInt(id, 10))
|
||||
}),
|
||||
locations: router({
|
||||
get: serviceProcedure.query(async function ({ ctx }) {
|
||||
get: serviceProcedure.input(getLocationsInput).query(async function ({
|
||||
input,
|
||||
ctx,
|
||||
}) {
|
||||
const lang = input.lang ?? ctx.lang
|
||||
|
||||
const searchParams = new URLSearchParams()
|
||||
searchParams.set("language", toApiLang(ctx.lang))
|
||||
searchParams.set("language", toApiLang(lang))
|
||||
|
||||
const options: RequestOptionsWithOutBody = {
|
||||
// needs to clear default option as only
|
||||
@@ -1288,29 +1294,27 @@ export const hotelQueryRouter = router({
|
||||
},
|
||||
}
|
||||
|
||||
const countries = await getCountries(options, searchParams, ctx.lang)
|
||||
const countries = await getCountries(options, searchParams, lang)
|
||||
if (!countries) {
|
||||
return null
|
||||
throw new Error("Unable to fetch countries")
|
||||
}
|
||||
const countryNames = countries.data.map((country) => country.name)
|
||||
const citiesByCountry = await getCitiesByCountry(
|
||||
countryNames,
|
||||
options,
|
||||
searchParams,
|
||||
ctx.lang
|
||||
lang
|
||||
)
|
||||
|
||||
const locations = await getLocations(
|
||||
ctx.lang,
|
||||
lang,
|
||||
options,
|
||||
searchParams,
|
||||
citiesByCountry
|
||||
)
|
||||
|
||||
if (Array.isArray(locations)) {
|
||||
return {
|
||||
data: locations,
|
||||
}
|
||||
if (!locations || "error" in locations) {
|
||||
throw new Error("Unable to fetch locations")
|
||||
}
|
||||
|
||||
return locations
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { VariantProps } from "class-variance-authority"
|
||||
import type { z } from "zod"
|
||||
|
||||
import type { SearchParams } from "@/types/params"
|
||||
import type { Locations } from "@/types/trpc/routers/hotel/locations"
|
||||
import type {
|
||||
bookingCodeSchema,
|
||||
bookingWidgetSchema,
|
||||
@@ -32,7 +31,6 @@ export interface BookingWidgetProps {
|
||||
}
|
||||
|
||||
export interface BookingWidgetClientProps {
|
||||
locations: Locations
|
||||
type?: BookingWidgetType
|
||||
bookingWidgetSearchParams: SearchParams<BookingWidgetSearchData>["searchParams"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user