Files
web/components/HotelReservation/HotelCard/index.tsx
2024-12-12 11:47:44 +01:00

191 lines
6.4 KiB
TypeScript

"use client"
import { useParams } from "next/dist/client/components/navigation"
import { memo, useCallback } from "react"
import { useIntl } from "react-intl"
import { selectRate } from "@/constants/routes/hotelReservation"
import { useHotelsMapStore } from "@/stores/hotels-map"
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
import ImageGallery from "@/components/ImageGallery"
import Button from "@/components/TempDesignSystem/Button"
import Divider from "@/components/TempDesignSystem/Divider"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import getSingleDecimal from "@/utils/numberFormatting"
import ReadMore from "../ReadMore"
import TripAdvisorChip from "../TripAdvisorChip"
import HotelLogo from "./HotelLogo"
import HotelPriceCard from "./HotelPriceCard"
import NoPriceAvailableCard from "./NoPriceAvailableCard"
import { hotelCardVariants } from "./variants"
import styles from "./hotelCard.module.css"
import { HotelCardListingTypeEnum } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
import type { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps"
import type { Lang } from "@/constants/languages"
function HotelCard({
hotel,
type = HotelCardListingTypeEnum.PageListing,
state = "default",
}: HotelCardProps) {
const params = useParams()
const lang = params.lang as Lang
const intl = useIntl()
const { setActiveHotelPin, setActiveHotelCard } = useHotelsMapStore()
const { hotelData } = hotel
const { price } = hotel
const handleMouseEnter = useCallback(() => {
if (hotelData) {
setActiveHotelPin(hotelData.name)
}
}, [setActiveHotelPin, hotelData])
const handleMouseLeave = useCallback(() => {
if (hotelData) {
setActiveHotelPin(null)
setActiveHotelCard(null)
}
}, [setActiveHotelPin, hotelData, setActiveHotelCard])
if (!hotel || !hotelData) return null
const amenities = hotelData.detailedFacilities.slice(0, 5)
const classNames = hotelCardVariants({
type,
state,
})
return (
<article
className={classNames}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<div>
<div className={styles.imageContainer}>
<ImageGallery
title={hotelData.name}
images={hotelData.galleryImages}
fill
/>
{hotelData.ratings?.tripAdvisor && (
<TripAdvisorChip rating={hotelData.ratings.tripAdvisor.rating} />
)}
</div>
</div>
<div className={styles.hotelContent}>
<section className={styles.hotelInformation}>
<div className={styles.titleContainer}>
<HotelLogo
hotelId={hotelData.operaId}
hotelType={hotelData.hotelType}
/>
<Subtitle textTransform="capitalize" color="uiTextHighContrast">
{hotelData.name}
</Subtitle>
<div className={styles.addressContainer}>
<address className={styles.address}>
<Caption color="uiTextPlaceholder">
{hotelData.address.streetAddress}, {hotelData.address.city}
</Caption>
</address>
<Caption color="baseTextMediumContrast" type="underline" asChild>
<Link
href={`https://www.google.com/maps/dir/?api=1&destination=${hotelData.location.latitude},${hotelData.location.longitude}`}
target="_blank"
aria-label={intl.formatMessage({
id: "Driving directions",
})}
title={intl.formatMessage({
id: "Driving directions",
})}
>
{hotelData.address.streetAddress}, {hotelData.address.city}
</Link>
</Caption>
<div>
<Divider variant="vertical" color="subtle" />
</div>
<Caption color="uiTextPlaceholder">
{intl.formatMessage(
{ id: "Distance in km to city centre" },
{
number: getSingleDecimal(
hotelData.location.distanceToCentre / 1000
),
}
)}
</Caption>
</div>
</div>
<Body className={styles.hotelDescription}>
{hotelData.hotelContent.texts.descriptions.short}
</Body>
<div className={styles.facilities}>
{amenities.map((facility) => {
const IconComponent = mapFacilityToIcon(facility.id)
return (
<div className={styles.facilitiesItem} key={facility.id}>
{IconComponent && <IconComponent color="grey80" />}
<Caption color="uiTextMediumContrast">
{facility.name}
</Caption>
</div>
)
})}
</div>
<ReadMore
label={intl.formatMessage({ id: "See hotel details" })}
hotelId={hotelData.operaId}
hotel={hotelData}
showCTA={true}
/>
</section>
<div className={styles.prices}>
{!price ? (
<NoPriceAvailableCard />
) : (
<>
{price.public && (
<HotelPriceCard productTypePrices={price.public} />
)}
{price.member && (
<HotelPriceCard
productTypePrices={price.member}
isMemberPrice
/>
)}
<Button
asChild
theme="base"
intent="primary"
size="small"
className={styles.button}
>
<Link
href={`${selectRate(lang)}?hotel=${hotel.hotelData.operaId}`}
color="none"
keepSearchParams
>
{intl.formatMessage({ id: "See rooms" })}
</Link>
</Button>
</>
)}
</div>
</div>
</article>
)
}
export default memo(HotelCard)