fix: clean up hotel and its typings

This commit is contained in:
Simon Emanuelsson
2024-12-17 16:17:25 +01:00
parent ec74af8814
commit 13a164242f
110 changed files with 1931 additions and 1559 deletions
+2 -2
View File
@@ -29,8 +29,8 @@ export default async function HotelListing({
</Title>
{hotels.map(({ data, url }) => (
<HotelListingItem
key={data.name}
hotel={data}
key={data.hotel.name}
hotel={data.hotel}
contentType={contentType}
url={url}
/>
@@ -1,14 +1,15 @@
import type {
Hotel,
HotelAddress,
HotelData,
HotelContent,
HotelLocation,
HotelTripAdvisor,
} from "@/types/hotel"
export type IntroSectionProps = {
hotelName: HotelData["data"]["attributes"]["name"]
hotelDescription: HotelData["data"]["attributes"]["hotelContent"]["texts"]["descriptions"]["short"]
location: HotelLocation
address: HotelAddress
hotelDescription: HotelContent["texts"]["descriptions"]["short"]
hotelName: Hotel["name"]
location: HotelLocation
tripAdvisor: HotelTripAdvisor
}
@@ -10,9 +10,9 @@ import {
} from "@/types/components/hotelPage/sidepeek/parking"
export default async function ParkingPrices({
pricing,
currency,
freeParking,
pricing,
}: ParkingPricesProps) {
const intl = await getIntl()
const day = intl.formatMessage({ id: "Price per day" })
@@ -53,8 +53,8 @@ export default async function ParkingAmenity({
</Caption>
<Divider color="baseSurfaceSubtleHover" />
<ParkingPrices
pricing={data.pricing.localCurrency.ordinary}
currency={data.pricing.localCurrency.currency}
pricing={data.pricing.localCurrency?.ordinary}
currency={data.pricing.localCurrency?.currency}
freeParking={data.pricing.freeParking}
/>
</div>
@@ -64,8 +64,8 @@ export default async function ParkingAmenity({
</Caption>
<Divider color="baseSurfaceSubtleHover" />
<ParkingPrices
pricing={data.pricing.localCurrency.weekend}
currency={data.pricing.localCurrency.currency}
pricing={data.pricing.localCurrency?.weekend}
currency={data.pricing.localCurrency?.currency}
freeParking={data.pricing.freeParking}
/>
</div>
@@ -16,8 +16,8 @@ export default async function RestaurantBarItem({
restaurant,
}: RestaurantBarItemProps) {
const intl = await getIntl()
const { name, openingDetails, menus, content } = restaurant
const { bookTableUrl, images } = restaurant.content
const { bookTableUrl, name, openingDetails, content, menus } = restaurant
const { images } = restaurant.content
const visibleImages = restaurant.content.images.slice(0, 2)
const imageWidth = images.length === 2 ? 240 : 496
+13 -12
View File
@@ -2,7 +2,7 @@ import { notFound } from "next/navigation"
import { Suspense } from "react"
import { env } from "@/env/server"
import { getHotelData, getHotelPage } from "@/lib/trpc/memoizedRequests"
import { getHotel, getHotelPage } from "@/lib/trpc/memoizedRequests"
import AccordionSection from "@/components/Blocks/Accordion"
import Breadcrumbs from "@/components/Breadcrumbs"
@@ -41,23 +41,27 @@ import styles from "./hotelPage.module.css"
import { FacilityCardTypeEnum } from "@/types/components/hotelPage/facilities"
import type { HotelPageProps } from "@/types/components/hotelPage/hotelPage"
import { HotelHashValues } from "@/types/components/hotelPage/tabNavigation"
import type { AdditionalData, Facility } from "@/types/hotel"
import type { Facility } from "@/types/hotel"
import { PageContentTypeEnum } from "@/types/requests/contentType"
export default async function HotelPage({ hotelId }: HotelPageProps) {
const lang = getLang()
const [hotelPageData, hotelData] = await Promise.all([
getHotelPage(),
getHotelData({ hotelId, language: lang }),
getHotel({
hotelId,
isCardOnlyPayment: false,
language: lang,
}),
])
const googleMapsApiKey = env.GOOGLE_STATIC_MAP_KEY
const googleMapId = env.GOOGLE_DYNAMIC_MAP_ID
if (!hotelData?.data || !hotelPageData) {
if (!hotelData?.hotel || !hotelPageData) {
return notFound()
}
const jsonSchema = generateHotelSchema(hotelData.data.attributes)
const jsonSchema = generateHotelSchema(hotelData.hotel)
const { faq, content, tabValues } = hotelPageData
const {
name,
@@ -73,12 +77,9 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
location,
ratings,
parking,
} = hotelData.data.attributes
const roomCategories = hotelData.included.rooms || []
const restaurants = hotelData.included.restaurants || []
const additionalData =
hotelData.included.additionalData || ({} as AdditionalData)
} = hotelData.hotel
const restaurants = hotelData.restaurants
const roomCategories = hotelData.roomCategories
const {
healthAndWellness,
restaurantImages,
@@ -87,7 +88,7 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
gallery,
hotelParking,
displayWebPage,
} = additionalData
} = hotelData.additionalData
const images = gallery?.smallerImages
const description = hotelContent.texts.descriptions.medium
@@ -22,8 +22,9 @@ export default async function BookingConfirmation({
confirmationNumber,
}: BookingConfirmationProps) {
const lang = getLang()
const { booking, hotel, room } =
await getBookingConfirmation(confirmationNumber)
const { booking, hotel, room } = await getBookingConfirmation(
confirmationNumber
)
const arrivalDate = new Date(booking.checkInDate)
const departureDate = new Date(booking.checkOutDate)
@@ -1,6 +1,6 @@
import { z } from "zod"
import { breakfastPackageSchema } from "@/server/routers/hotels/output"
import { breakfastPackageSchema } from "@/server/routers/hotels/schemas/packages"
export const breakfastStoreSchema = z.object({
breakfast: breakfastPackageSchema.or(z.literal(false)),
@@ -10,10 +10,10 @@ import styles from "./header.module.css"
import type { HotelHeaderProps } from "@/types/components/hotelReservation/enterDetails/hotelHeader"
export default async function HotelHeader({ hotelData }: HotelHeaderProps) {
export default async function HotelHeader({
hotelData: { hotel },
}: HotelHeaderProps) {
const intl = await getIntl()
const hotel = hotelData.data.attributes
const image = hotel.hotelContent?.images
const addressStr = `${hotel.address.streetAddress}, ${hotel.address.city}`
@@ -32,7 +32,7 @@ import {
} from "@/types/components/tracking"
function isValidHotelData(hotel: NullableHotelData): hotel is HotelData {
return hotel !== null && hotel !== undefined
return hotel != null
}
export async function SelectHotelMapContainer({
@@ -1,6 +1,6 @@
import { Suspense } from "react"
import { getHotelData } from "@/lib/trpc/memoizedRequests"
import { getHotel } from "@/lib/trpc/memoizedRequests"
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
import ImageGallery from "@/components/ImageGallery"
@@ -18,55 +18,44 @@ import { NoRoomsAlert } from "./NoRoomsAlert"
import styles from "./hotelInfoCard.module.css"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Lang } from "@/constants/languages"
type Props = {
hotelId: number
lang: Lang
fromDate: Date
toDate: Date
adultCount: number
childArray?: Child[]
}
import type { HotelInfoCardProps } from "@/types/components/hotelReservation/selectRate/hotelInfoCard"
export default async function HotelInfoCard({
hotelId,
lang,
...props
}: Props) {
const hotelData = await getHotelData({
}: HotelInfoCardProps) {
const hotelData = await getHotel({
hotelId: hotelId.toString(),
isCardOnlyPayment: false,
language: lang,
})
const hotelAttributes = hotelData?.data.attributes
const hotel = hotelData?.hotel
const intl = await getIntl()
const sortedFacilities = hotelAttributes?.detailedFacilities
const sortedFacilities = hotel?.detailedFacilities
.sort((a, b) => b.sortOrder - a.sortOrder)
.slice(0, 5)
return (
<article className={styles.container}>
{hotelAttributes && (
{hotel && (
<section className={styles.wrapper}>
<div className={styles.imageWrapper}>
<ImageGallery
title={hotelAttributes.name}
images={hotelAttributes.galleryImages}
title={hotel.name}
images={hotel.galleryImages}
fill
/>
{hotelAttributes.ratings?.tripAdvisor && (
<TripAdvisorChip
rating={hotelAttributes.ratings.tripAdvisor.rating}
/>
{hotel.ratings?.tripAdvisor && (
<TripAdvisorChip rating={hotel.ratings.tripAdvisor.rating} />
)}
</div>
<div className={styles.hotelContent}>
<div className={styles.hotelInformation}>
<Title as="h2" textTransform="uppercase">
{hotelAttributes.name}
{hotel.name}
</Title>
<div className={styles.hotelAddressDescription}>
<Caption color="uiTextMediumContrast">
@@ -75,16 +64,16 @@ export default async function HotelInfoCard({
id: "{address}, {city} ∙ {distanceToCityCenterInKm} km to city center",
},
{
address: hotelAttributes.address.streetAddress,
city: hotelAttributes.address.city,
distanceToCityCenterInKm: getSingleDecimal(
hotelAttributes.location.distanceToCentre / 1000
address: hotel.address.streetAddress,
city: hotel.address.city,
distanceToCityCentreInKm: getSingleDecimal(
hotel.location.distanceToCentre / 1000
),
}
)}
</Caption>
<Body color="uiTextHighContrast">
{hotelAttributes.hotelContent.texts.descriptions.medium}
{hotel.hotelContent.texts.descriptions.medium}
</Body>
</div>
</div>
@@ -111,15 +100,15 @@ export default async function HotelInfoCard({
</div>
<ReadMore
label={intl.formatMessage({ id: "Show all amenities" })}
hotelId={hotelAttributes.operaId}
hotel={hotelAttributes}
hotelId={hotel.operaId}
hotel={hotel}
showCTA={false}
/>
</div>
</div>
</section>
)}
{hotelAttributes?.specialAlerts.map((alert) => {
{hotel?.specialAlerts.map((alert) => {
return (
<div className={styles.hotelAlert} key={`wrapper_${alert.id}`}>
<Alert
@@ -22,7 +22,7 @@ import styles from "./roomCard.module.css"
import type { RoomCardProps } from "@/types/components/hotelReservation/selectRate/roomCard"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { HotelTypeEnum } from "@/types/enums/hotelType"
import type { RateDefinition } from "@/server/routers/hotels/output"
import type { RateDefinition } from "@/types/trpc/routers/hotel/roomAvailability"
export default function RoomCard({
hotelId,
@@ -1,6 +1,6 @@
import { dt } from "@/lib/dt"
import {
getHotelData,
getHotel,
getPackages,
getRoomsAvailability,
} from "@/lib/trpc/memoizedRequests"
@@ -13,33 +13,28 @@ import { generateChildrenString } from "../../utils"
import Rooms from "."
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { Child } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { Lang } from "@/constants/languages"
export type Props = {
hotelId: number
fromDate: Date
toDate: Date
adultCount: number
childArray?: Child[]
lang: Lang
}
import type { RoomsContainerProps } from "@/types/components/hotelReservation/selectRate/roomsContainer"
export async function RoomsContainer({
hotelId,
fromDate,
toDate,
adultCount,
childArray,
fromDate,
hotelId,
lang,
}: Props) {
toDate,
}: RoomsContainerProps) {
const session = await auth()
const isUserLoggedIn = isValidSession(session)
const fromDateString = dt(fromDate).format("YYYY-MM-DD")
const toDateString = dt(toDate).format("YYYY-MM-DD")
const hotelDataPromise = safeTry(
getHotelData({ hotelId: hotelId.toString(), language: lang })
getHotel({
hotelId: hotelId.toString(),
isCardOnlyPayment: false,
language: lang,
})
)
const packagesPromise = safeTry(
@@ -94,10 +89,10 @@ export async function RoomsContainer({
return (
<Rooms
availablePackages={packages ?? []}
roomsAvailability={roomsAvailability}
roomCategories={hotelData?.included.rooms ?? []}
hotelType={hotelData?.data.attributes?.hotelType}
hotelType={hotelData?.hotel.hotelType}
isUserLoggedIn={isUserLoggedIn}
roomsAvailability={roomsAvailability}
roomCategories={hotelData?.roomCategories ?? []}
/>
)
}
@@ -25,14 +25,14 @@ import {
} from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { SelectRateProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { RoomConfiguration } from "@/server/routers/hotels/output"
import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
export default function Rooms({
roomsAvailability,
roomCategories = [],
availablePackages,
hotelType,
isUserLoggedIn,
roomsAvailability,
roomCategories = [],
}: SelectRateProps) {
const router = useRouter()
const pathname = usePathname()
@@ -1,5 +1,4 @@
import type { RoomParam } from "@/types/components/hotelReservation/selectRate/section"
import type { RoomConfiguration } from "@/server/routers/hotels/output"
import type { RoomConfiguration } from "@/types/trpc/routers/hotel/roomAvailability"
/**
* Get the lowest priced room for each room type that appears more than once.
@@ -64,16 +63,16 @@ export function filterDuplicateRoomTypesByLowestPrice(
if (
!previousLowest ||
currentRequestedPrice <
Math.min(
Number(
previousLowest.products[0].productType.public.requestedPrice
?.pricePerNight
) ?? Infinity,
Number(
previousLowest.products[0].productType.member?.requestedPrice
?.pricePerNight
) ?? Infinity
) ||
Math.min(
Number(
previousLowest.products[0].productType.public.requestedPrice
?.pricePerNight
) ?? Infinity,
Number(
previousLowest.products[0].productType.member?.requestedPrice
?.pricePerNight
) ?? Infinity
) ||
(currentRequestedPrice ===
Math.min(
Number(
@@ -86,16 +85,16 @@ export function filterDuplicateRoomTypesByLowestPrice(
) ?? Infinity
) &&
currentLocalPrice <
Math.min(
Number(
previousLowest.products[0].productType.public.localPrice
?.pricePerNight
) ?? Infinity,
Number(
previousLowest.products[0].productType.member?.localPrice
?.pricePerNight
) ?? Infinity
))
Math.min(
Number(
previousLowest.products[0].productType.public.localPrice
?.pricePerNight
) ?? Infinity,
Number(
previousLowest.products[0].productType.member?.localPrice
?.pricePerNight
) ?? Infinity
))
) {
roomMap.set(roomType, room)
}
@@ -7,13 +7,11 @@ import HotelSidePeek from "@/components/SidePeeks/HotelSidePeek"
import RoomSidePeek from "@/components/SidePeeks/RoomSidePeek"
import useLang from "@/hooks/useLang"
import type { HotelData } from "@/types/hotel"
import type { HotelReservationSidePeekProps } from "@/types/components/hotelReservation/sidePeek"
export default function HotelReservationSidePeek({
hotel,
}: {
hotel: HotelData | null
}) {
}: HotelReservationSidePeekProps) {
const activeSidePeek = useSidePeekStore((state) => state.activeSidePeek)
const hotelId = useSidePeekStore((state) => state.hotelId)
const roomTypeCode = useSidePeekStore((state) => state.roomTypeCode)
@@ -21,7 +19,7 @@ export default function HotelReservationSidePeek({
const close = useSidePeekStore((state) => state.closeSidePeek)
const lang = useLang()
const { data: hotelData } = trpc.hotel.hotelData.get.useQuery(
const { data: hotelData } = trpc.hotel.get.useQuery(
{
hotelId: hotelId ?? "",
language: lang,
@@ -32,7 +30,7 @@ export default function HotelReservationSidePeek({
}
)
const selectedRoom = hotelData?.included.rooms?.find((room) =>
const selectedRoom = hotelData?.roomCategories.find((room) =>
room.roomTypes.some((type) => type.code === roomTypeCode)
)
@@ -41,8 +39,8 @@ export default function HotelReservationSidePeek({
<>
{hotelData && (
<HotelSidePeek
hotel={hotelData.data?.attributes}
additionalHotelData={hotelData.included.additionalData}
additionalHotelData={hotelData.additionalData}
hotel={hotelData.hotel}
activeSidePeek={activeSidePeek}
close={close}
showCTA={showCTA}
+1 -1
View File
@@ -2,7 +2,7 @@ import { cva } from "class-variance-authority"
import styles from "./poi.module.css"
import { PointOfInterestGroupEnum } from "@/types/hotel"
import { PointOfInterestGroupEnum } from "@/types/enums/pointOfInterest"
export const poiVariants = cva(styles.icon, {
variants: {
+1 -1
View File
@@ -1,5 +1,5 @@
import { IconName } from "@/types/components/icon"
import { PointOfInterestGroupEnum } from "@/types/hotel"
import { PointOfInterestGroupEnum } from "@/types/enums/pointOfInterest"
export function getIconByPoiGroupAndCategory(
group: PointOfInterestGroupEnum,