fix(SW-978) Checks for null data on hotels
This commit is contained in:
@@ -14,6 +14,7 @@ import { setLang } from "@/i18n/serverContext"
|
|||||||
|
|
||||||
import { fetchAvailableHotels, getFiltersFromHotels } from "../../utils"
|
import { fetchAvailableHotels, getFiltersFromHotels } from "../../utils"
|
||||||
|
|
||||||
|
import { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||||
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
|
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
|
||||||
import type { LangParams, PageArgs } from "@/types/params"
|
import type { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
@@ -52,11 +53,15 @@ export default async function SelectHotelMapPage({
|
|||||||
children,
|
children,
|
||||||
})
|
})
|
||||||
|
|
||||||
const hotelPins = getHotelPins(hotels)
|
const validHotels = hotels.filter(
|
||||||
const filterList = getFiltersFromHotels(hotels)
|
(hotel): hotel is HotelData => hotel !== null
|
||||||
|
)
|
||||||
|
|
||||||
|
const hotelPins = getHotelPins(validHotels)
|
||||||
|
const filterList = getFiltersFromHotels(validHotels)
|
||||||
const cityCoordinates = await getCityCoordinates({
|
const cityCoordinates = await getCityCoordinates({
|
||||||
city: city.name,
|
city: city.name,
|
||||||
hotel: { address: hotels[0].hotelData.address.streetAddress },
|
hotel: { address: hotels?.[0]?.hotelData?.address.streetAddress },
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -65,7 +70,7 @@ export default async function SelectHotelMapPage({
|
|||||||
apiKey={googleMapsApiKey}
|
apiKey={googleMapsApiKey}
|
||||||
hotelPins={hotelPins}
|
hotelPins={hotelPins}
|
||||||
mapId={googleMapId}
|
mapId={googleMapId}
|
||||||
hotels={hotels}
|
hotels={validHotels}
|
||||||
filterList={filterList}
|
filterList={filterList}
|
||||||
cityCoordinates={cityCoordinates}
|
cityCoordinates={cityCoordinates}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { setLang } from "@/i18n/serverContext"
|
|||||||
|
|
||||||
import styles from "./page.module.css"
|
import styles from "./page.module.css"
|
||||||
|
|
||||||
|
import { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||||
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
|
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
|
||||||
import { AlertTypeEnum } from "@/types/enums/alert"
|
import { AlertTypeEnum } from "@/types/enums/alert"
|
||||||
import { LangParams, PageArgs } from "@/types/params"
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
@@ -82,7 +83,11 @@ export default async function SelectHotelPage({
|
|||||||
children,
|
children,
|
||||||
})
|
})
|
||||||
|
|
||||||
const filterList = getFiltersFromHotels(hotels)
|
const validHotels = hotels.filter(
|
||||||
|
(hotel): hotel is HotelData => hotel !== null
|
||||||
|
)
|
||||||
|
|
||||||
|
const filterList = getFiltersFromHotels(validHotels)
|
||||||
const breadcrumbs = [
|
const breadcrumbs = [
|
||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: "Home" }),
|
title: intl.formatMessage({ id: "Home" }),
|
||||||
@@ -177,7 +182,7 @@ export default async function SelectHotelPage({
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<HotelCardListing hotelData={hotels} />
|
<HotelCardListing hotelData={validHotels} />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import { getHotelData } from "@/lib/trpc/memoizedRequests"
|
import { getHotelData } from "@/lib/trpc/memoizedRequests"
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
import { badRequestError } from "@/server/errors/trpc"
|
||||||
|
|
||||||
import { getLang } from "@/i18n/serverContext"
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
import type { AvailabilityInput } from "@/types/components/hotelReservation/selectHotel/availabilityInput"
|
import type { AvailabilityInput } from "@/types/components/hotelReservation/selectHotel/availabilityInput"
|
||||||
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
import type {
|
||||||
|
HotelData,
|
||||||
|
NullableHotelData,
|
||||||
|
} from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||||
import type {
|
import type {
|
||||||
CategorizedFilters,
|
CategorizedFilters,
|
||||||
Filter,
|
Filter,
|
||||||
@@ -30,10 +34,10 @@ const hotelFacilitiesFilterNames = [
|
|||||||
|
|
||||||
export async function fetchAvailableHotels(
|
export async function fetchAvailableHotels(
|
||||||
input: AvailabilityInput
|
input: AvailabilityInput
|
||||||
): Promise<HotelData[]> {
|
): Promise<NullableHotelData[]> {
|
||||||
const availableHotels = await serverClient().hotel.availability.hotels(input)
|
const availableHotels = await serverClient().hotel.availability.hotels(input)
|
||||||
|
|
||||||
if (!availableHotels) throw new Error()
|
if (!availableHotels) return []
|
||||||
|
|
||||||
const language = getLang()
|
const language = getLang()
|
||||||
|
|
||||||
@@ -43,7 +47,7 @@ export async function fetchAvailableHotels(
|
|||||||
language,
|
language,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!hotelData) throw new Error()
|
if (!hotelData) return { hotelData: null, price: hotel.productType }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hotelData: hotelData.data.attributes,
|
hotelData: hotelData.data.attributes,
|
||||||
@@ -55,7 +59,13 @@ export async function fetchAvailableHotels(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getFiltersFromHotels(hotels: HotelData[]): CategorizedFilters {
|
export function getFiltersFromHotels(hotels: HotelData[]): CategorizedFilters {
|
||||||
const filters = hotels.flatMap((hotel) => hotel.hotelData.detailedFacilities)
|
if (hotels.length === 0)
|
||||||
|
return { facilityFilters: [], surroundingsFilters: [] }
|
||||||
|
|
||||||
|
const filters = hotels.flatMap((hotel) => {
|
||||||
|
if (!hotel.hotelData) return []
|
||||||
|
return hotel.hotelData.detailedFacilities
|
||||||
|
})
|
||||||
|
|
||||||
const uniqueFilterIds = [...new Set(filters.map((filter) => filter.id))]
|
const uniqueFilterIds = [...new Set(filters.map((filter) => filter.id))]
|
||||||
const filterList: Filter[] = uniqueFilterIds
|
const filterList: Filter[] = uniqueFilterIds
|
||||||
|
|||||||
@@ -41,18 +41,11 @@ function HotelCard({
|
|||||||
const { hotelData } = hotel
|
const { hotelData } = hotel
|
||||||
const { price } = hotel
|
const { price } = hotel
|
||||||
|
|
||||||
const amenities = hotelData.detailedFacilities.slice(0, 5)
|
|
||||||
|
|
||||||
const classNames = hotelCardVariants({
|
|
||||||
type,
|
|
||||||
state,
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleMouseEnter = useCallback(() => {
|
const handleMouseEnter = useCallback(() => {
|
||||||
if (onHotelCardHover) {
|
if (onHotelCardHover && hotelData) {
|
||||||
onHotelCardHover(hotelData.name)
|
onHotelCardHover(hotelData.name)
|
||||||
}
|
}
|
||||||
}, [onHotelCardHover, hotelData.name])
|
}, [onHotelCardHover, hotelData])
|
||||||
|
|
||||||
const handleMouseLeave = useCallback(() => {
|
const handleMouseLeave = useCallback(() => {
|
||||||
if (onHotelCardHover) {
|
if (onHotelCardHover) {
|
||||||
@@ -60,6 +53,15 @@ function HotelCard({
|
|||||||
}
|
}
|
||||||
}, [onHotelCardHover])
|
}, [onHotelCardHover])
|
||||||
|
|
||||||
|
if (!hotel || !hotelData) return null
|
||||||
|
|
||||||
|
const amenities = hotelData.detailedFacilities.slice(0, 5)
|
||||||
|
|
||||||
|
const classNames = hotelCardVariants({
|
||||||
|
type,
|
||||||
|
state,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article
|
<article
|
||||||
className={classNames}
|
className={classNames}
|
||||||
@@ -82,8 +84,8 @@ function HotelCard({
|
|||||||
<section className={styles.hotelInformation}>
|
<section className={styles.hotelInformation}>
|
||||||
<div className={styles.titleContainer}>
|
<div className={styles.titleContainer}>
|
||||||
<HotelLogo
|
<HotelLogo
|
||||||
hotelId={hotel.hotelData.operaId}
|
hotelId={hotelData.operaId}
|
||||||
hotelType={hotel.hotelData.hotelType}
|
hotelType={hotelData.hotelType}
|
||||||
/>
|
/>
|
||||||
<Subtitle textTransform="capitalize" color="uiTextHighContrast">
|
<Subtitle textTransform="capitalize" color="uiTextHighContrast">
|
||||||
{hotelData.name}
|
{hotelData.name}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default function HotelCardDialogListing({
|
|||||||
activeCard,
|
activeCard,
|
||||||
onActiveCardChange,
|
onActiveCardChange,
|
||||||
}: HotelCardDialogListingProps) {
|
}: HotelCardDialogListingProps) {
|
||||||
const hotelsPinData = getHotelPins(hotels)
|
const hotelsPinData = hotels ? getHotelPins(hotels) : []
|
||||||
const activeCardRef = useRef<HTMLDivElement | null>(null)
|
const activeCardRef = useRef<HTMLDivElement | null>(null)
|
||||||
const observerRef = useRef<IntersectionObserver | null>(null)
|
const observerRef = useRef<IntersectionObserver | null>(null)
|
||||||
const dialogRef = useRef<HTMLDivElement>(null)
|
const dialogRef = useRef<HTMLDivElement>(null)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import type { HotelData } from "@/types/components/hotelReservation/selectHotel/
|
|||||||
import type { HotelPin } from "@/types/components/hotelReservation/selectHotel/map"
|
import type { HotelPin } from "@/types/components/hotelReservation/selectHotel/map"
|
||||||
|
|
||||||
export function getHotelPins(hotels: HotelData[]): HotelPin[] {
|
export function getHotelPins(hotels: HotelData[]): HotelPin[] {
|
||||||
|
if (hotels.length === 0) return []
|
||||||
|
|
||||||
return hotels.map((hotel) => ({
|
return hotels.map((hotel) => ({
|
||||||
coordinates: {
|
coordinates: {
|
||||||
lat: hotel.hotelData.location.latitude,
|
lat: hotel.hotelData.location.latitude,
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export const getBookingConfirmation = cache(
|
|||||||
export const getCityCoordinates = cache(
|
export const getCityCoordinates = cache(
|
||||||
async function getMemoizedCityCoordinates(input: {
|
async function getMemoizedCityCoordinates(input: {
|
||||||
city: string
|
city: string
|
||||||
hotel: { address: string }
|
hotel: { address: string | undefined }
|
||||||
}) {
|
}) {
|
||||||
return serverClient().hotel.map.city(input)
|
return serverClient().hotel.map.city(input)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,6 @@ export const getRoomPackagesInputSchema = z.object({
|
|||||||
export const getCityCoordinatesInputSchema = z.object({
|
export const getCityCoordinatesInputSchema = z.object({
|
||||||
city: z.string(),
|
city: z.string(),
|
||||||
hotel: z.object({
|
hotel: z.object({
|
||||||
address: z.string(),
|
address: z.string().optional(),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -18,3 +18,7 @@ export type HotelData = {
|
|||||||
hotelData: Hotel
|
hotelData: Hotel
|
||||||
price: ProductType
|
price: ProductType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NullableHotelData extends Omit<HotelData, "hotelData"> {
|
||||||
|
hotelData: HotelData["hotelData"] | null
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export interface HotelCardDialogProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface HotelCardDialogListingProps {
|
export interface HotelCardDialogListingProps {
|
||||||
hotels: HotelData[]
|
hotels: HotelData[] | null
|
||||||
activeCard: string | null | undefined
|
activeCard: string | null | undefined
|
||||||
onActiveCardChange: (hotelName: string | null) => void
|
onActiveCardChange: (hotelName: string | null) => void
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user