fix(SW-978) Checks for null data on hotels

This commit is contained in:
Pontus Dreij
2024-11-26 13:55:44 +01:00
parent 4de3f4717c
commit 3d78bdd671
10 changed files with 54 additions and 26 deletions

View File

@@ -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}
/> />

View File

@@ -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>
</> </>

View File

@@ -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

View File

@@ -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}

View File

@@ -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)

View File

@@ -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,

View File

@@ -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)
} }

View File

@@ -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(),
}), }),
}) })

View File

@@ -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
}

View File

@@ -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
} }