191 lines
6.4 KiB
TypeScript
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)
|