From 3d78bdd671a369f2b0bba3d03a0adf431fd2cd86 Mon Sep 17 00:00:00 2001 From: Pontus Dreij Date: Tue, 26 Nov 2024 13:55:44 +0100 Subject: [PATCH] fix(SW-978) Checks for null data on hotels --- .../select-hotel/@modal/(.)map/page.tsx | 13 ++++++---- .../(standard)/select-hotel/page.tsx | 9 +++++-- .../(standard)/select-hotel/utils.ts | 20 ++++++++++++---- .../HotelReservation/HotelCard/index.tsx | 24 ++++++++++--------- .../HotelCardDialogListing/index.tsx | 2 +- .../HotelCardDialogListing/utils.ts | 2 ++ lib/trpc/memoizedRequests/index.ts | 2 +- server/routers/hotels/input.ts | 2 +- .../selectHotel/hotelCardListingProps.ts | 4 ++++ .../hotelReservation/selectHotel/map.ts | 2 +- 10 files changed, 54 insertions(+), 26 deletions(-) diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/@modal/(.)map/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/@modal/(.)map/page.tsx index 2579fd597..40ce894bc 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/@modal/(.)map/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/@modal/(.)map/page.tsx @@ -14,6 +14,7 @@ import { setLang } from "@/i18n/serverContext" import { fetchAvailableHotels, getFiltersFromHotels } from "../../utils" +import { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps" import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams" import type { LangParams, PageArgs } from "@/types/params" @@ -52,11 +53,15 @@ export default async function SelectHotelMapPage({ children, }) - const hotelPins = getHotelPins(hotels) - const filterList = getFiltersFromHotels(hotels) + const validHotels = hotels.filter( + (hotel): hotel is HotelData => hotel !== null + ) + + const hotelPins = getHotelPins(validHotels) + const filterList = getFiltersFromHotels(validHotels) const cityCoordinates = await getCityCoordinates({ city: city.name, - hotel: { address: hotels[0].hotelData.address.streetAddress }, + hotel: { address: hotels?.[0]?.hotelData?.address.streetAddress }, }) return ( @@ -65,7 +70,7 @@ export default async function SelectHotelMapPage({ apiKey={googleMapsApiKey} hotelPins={hotelPins} mapId={googleMapId} - hotels={hotels} + hotels={validHotels} filterList={filterList} cityCoordinates={cityCoordinates} /> diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/page.tsx b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/page.tsx index 1d3ea38f6..ec281141b 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/page.tsx +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/page.tsx @@ -33,6 +33,7 @@ import { setLang } from "@/i18n/serverContext" import styles from "./page.module.css" +import { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps" import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams" import { AlertTypeEnum } from "@/types/enums/alert" import { LangParams, PageArgs } from "@/types/params" @@ -82,7 +83,11 @@ export default async function SelectHotelPage({ children, }) - const filterList = getFiltersFromHotels(hotels) + const validHotels = hotels.filter( + (hotel): hotel is HotelData => hotel !== null + ) + + const filterList = getFiltersFromHotels(validHotels) const breadcrumbs = [ { title: intl.formatMessage({ id: "Home" }), @@ -177,7 +182,7 @@ export default async function SelectHotelPage({ })} /> )} - + diff --git a/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils.ts b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils.ts index bd41510ce..fe9f0ab84 100644 --- a/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils.ts +++ b/app/[lang]/(live)/(public)/hotelreservation/(standard)/select-hotel/utils.ts @@ -1,10 +1,14 @@ import { getHotelData } from "@/lib/trpc/memoizedRequests" import { serverClient } from "@/lib/trpc/server" +import { badRequestError } from "@/server/errors/trpc" import { getLang } from "@/i18n/serverContext" 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 { CategorizedFilters, Filter, @@ -30,10 +34,10 @@ const hotelFacilitiesFilterNames = [ export async function fetchAvailableHotels( input: AvailabilityInput -): Promise { +): Promise { const availableHotels = await serverClient().hotel.availability.hotels(input) - if (!availableHotels) throw new Error() + if (!availableHotels) return [] const language = getLang() @@ -43,7 +47,7 @@ export async function fetchAvailableHotels( language, }) - if (!hotelData) throw new Error() + if (!hotelData) return { hotelData: null, price: hotel.productType } return { hotelData: hotelData.data.attributes, @@ -55,7 +59,13 @@ export async function fetchAvailableHotels( } 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 filterList: Filter[] = uniqueFilterIds diff --git a/components/HotelReservation/HotelCard/index.tsx b/components/HotelReservation/HotelCard/index.tsx index f6fbbcf12..5c2f788ee 100644 --- a/components/HotelReservation/HotelCard/index.tsx +++ b/components/HotelReservation/HotelCard/index.tsx @@ -41,18 +41,11 @@ function HotelCard({ const { hotelData } = hotel const { price } = hotel - const amenities = hotelData.detailedFacilities.slice(0, 5) - - const classNames = hotelCardVariants({ - type, - state, - }) - const handleMouseEnter = useCallback(() => { - if (onHotelCardHover) { + if (onHotelCardHover && hotelData) { onHotelCardHover(hotelData.name) } - }, [onHotelCardHover, hotelData.name]) + }, [onHotelCardHover, hotelData]) const handleMouseLeave = useCallback(() => { if (onHotelCardHover) { @@ -60,6 +53,15 @@ function HotelCard({ } }, [onHotelCardHover]) + if (!hotel || !hotelData) return null + + const amenities = hotelData.detailedFacilities.slice(0, 5) + + const classNames = hotelCardVariants({ + type, + state, + }) + return (
{hotelData.name} diff --git a/components/HotelReservation/HotelCardDialogListing/index.tsx b/components/HotelReservation/HotelCardDialogListing/index.tsx index 123cc4ced..7e8750ec2 100644 --- a/components/HotelReservation/HotelCardDialogListing/index.tsx +++ b/components/HotelReservation/HotelCardDialogListing/index.tsx @@ -17,7 +17,7 @@ export default function HotelCardDialogListing({ activeCard, onActiveCardChange, }: HotelCardDialogListingProps) { - const hotelsPinData = getHotelPins(hotels) + const hotelsPinData = hotels ? getHotelPins(hotels) : [] const activeCardRef = useRef(null) const observerRef = useRef(null) const dialogRef = useRef(null) diff --git a/components/HotelReservation/HotelCardDialogListing/utils.ts b/components/HotelReservation/HotelCardDialogListing/utils.ts index 2299b3f35..1a0e05ad8 100644 --- a/components/HotelReservation/HotelCardDialogListing/utils.ts +++ b/components/HotelReservation/HotelCardDialogListing/utils.ts @@ -2,6 +2,8 @@ import type { HotelData } from "@/types/components/hotelReservation/selectHotel/ import type { HotelPin } from "@/types/components/hotelReservation/selectHotel/map" export function getHotelPins(hotels: HotelData[]): HotelPin[] { + if (hotels.length === 0) return [] + return hotels.map((hotel) => ({ coordinates: { lat: hotel.hotelData.location.latitude, diff --git a/lib/trpc/memoizedRequests/index.ts b/lib/trpc/memoizedRequests/index.ts index 7db2e66da..2bf35c264 100644 --- a/lib/trpc/memoizedRequests/index.ts +++ b/lib/trpc/memoizedRequests/index.ts @@ -144,7 +144,7 @@ export const getBookingConfirmation = cache( export const getCityCoordinates = cache( async function getMemoizedCityCoordinates(input: { city: string - hotel: { address: string } + hotel: { address: string | undefined } }) { return serverClient().hotel.map.city(input) } diff --git a/server/routers/hotels/input.ts b/server/routers/hotels/input.ts index a4e47caf3..4a66009b3 100644 --- a/server/routers/hotels/input.ts +++ b/server/routers/hotels/input.ts @@ -77,6 +77,6 @@ export const getRoomPackagesInputSchema = z.object({ export const getCityCoordinatesInputSchema = z.object({ city: z.string(), hotel: z.object({ - address: z.string(), + address: z.string().optional(), }), }) diff --git a/types/components/hotelReservation/selectHotel/hotelCardListingProps.ts b/types/components/hotelReservation/selectHotel/hotelCardListingProps.ts index 68a6174ed..eb84b6977 100644 --- a/types/components/hotelReservation/selectHotel/hotelCardListingProps.ts +++ b/types/components/hotelReservation/selectHotel/hotelCardListingProps.ts @@ -18,3 +18,7 @@ export type HotelData = { hotelData: Hotel price: ProductType } + +export interface NullableHotelData extends Omit { + hotelData: HotelData["hotelData"] | null +} diff --git a/types/components/hotelReservation/selectHotel/map.ts b/types/components/hotelReservation/selectHotel/map.ts index 4ec21f86f..19490464d 100644 --- a/types/components/hotelReservation/selectHotel/map.ts +++ b/types/components/hotelReservation/selectHotel/map.ts @@ -56,7 +56,7 @@ export interface HotelCardDialogProps { } export interface HotelCardDialogListingProps { - hotels: HotelData[] + hotels: HotelData[] | null activeCard: string | null | undefined onActiveCardChange: (hotelName: string | null) => void }