chore(SW-3246): Moved Alert component into design system * chore(SW-3246): Moved Alert component into design system * chore(SW-3246): Optimsed code and imports * chore(SW-3246): Moved type AlertTypeEnum and other to common package Approved-by: Anton Gunnarsson
197 lines
6.8 KiB
TypeScript
197 lines
6.8 KiB
TypeScript
import { dt } from "@scandic-hotels/common/dt"
|
|
import { getSingleDecimal } from "@scandic-hotels/common/utils/numberFormatting"
|
|
import { Alert } from "@scandic-hotels/design-system/Alert"
|
|
import { Divider } from "@scandic-hotels/design-system/Divider"
|
|
import { FacilityToIcon } from "@scandic-hotels/design-system/FacilityToIcon"
|
|
import ImageGallery from "@scandic-hotels/design-system/ImageGallery"
|
|
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
|
|
import { TripAdvisorChip } from "@scandic-hotels/design-system/TripAdvisorChip"
|
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
|
|
import HotelDetailsSidePeek from "@/components/SidePeeks/HotelDetailsSidePeek"
|
|
import { getIntl } from "@/i18n"
|
|
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
|
|
|
import { getHotelAlertsForBookingDates } from "../../utils"
|
|
import HotelDescription from "./HotelDescription"
|
|
|
|
import styles from "./hotelInfoCard.module.css"
|
|
|
|
import type {
|
|
AdditionalData,
|
|
Hotel,
|
|
Restaurant,
|
|
} from "@scandic-hotels/trpc/types/hotel"
|
|
|
|
import type { SelectRateBooking } from "@/types/components/hotelReservation/selectRate/selectRate"
|
|
|
|
export type HotelInfoCardProps = {
|
|
booking: SelectRateBooking
|
|
hotel: Hotel & { url: string | null }
|
|
restaurants: Restaurant[]
|
|
additionalData: AdditionalData | undefined
|
|
}
|
|
|
|
export async function HotelInfoCard({
|
|
booking,
|
|
hotel,
|
|
restaurants,
|
|
additionalData,
|
|
}: HotelInfoCardProps) {
|
|
const intl = await getIntl()
|
|
|
|
const sortedFacilities = hotel.detailedFacilities
|
|
.sort((a, b) => b.sortOrder - a.sortOrder)
|
|
.slice(0, 5)
|
|
|
|
const galleryImages = mapApiImagesToGalleryImages(hotel.galleryImages || [])
|
|
|
|
const bookingFromDate = dt(booking.fromDate)
|
|
const bookingToDate = dt(booking.toDate)
|
|
const specialAlerts = getHotelAlertsForBookingDates(
|
|
hotel.specialAlerts,
|
|
bookingFromDate,
|
|
bookingToDate
|
|
)
|
|
|
|
return (
|
|
<article className={styles.container}>
|
|
<section className={styles.wrapper}>
|
|
<div className={styles.imageWrapper}>
|
|
<ImageGallery title={hotel.name} images={galleryImages} fill />
|
|
{hotel.ratings?.tripAdvisor && (
|
|
<TripAdvisorChip rating={hotel.ratings.tripAdvisor.rating} />
|
|
)}
|
|
</div>
|
|
<div className={styles.hotelContent}>
|
|
<div className={styles.hotelInformation}>
|
|
<Typography variant="Title/md">
|
|
<h1 className={styles.hotelName}>{hotel.name}</h1>
|
|
</Typography>
|
|
<div className={styles.hotelAddressDescription}>
|
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
|
<p className={styles.hotelAddress}>
|
|
{intl.formatMessage(
|
|
{
|
|
defaultMessage:
|
|
"{address}, {city} ∙ {distanceToCityCenterInKm} km to city center",
|
|
},
|
|
{
|
|
address: hotel.address.streetAddress,
|
|
city: hotel.address.city,
|
|
distanceToCityCenterInKm: getSingleDecimal(
|
|
hotel.location.distanceToCentre / 1000
|
|
),
|
|
}
|
|
)}
|
|
</p>
|
|
</Typography>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p className={styles.hotelDescription}>
|
|
{hotel.hotelContent.texts.descriptions?.medium}
|
|
</p>
|
|
</Typography>
|
|
<HotelDescription
|
|
key={hotel.operaId}
|
|
description={hotel.hotelContent.texts.descriptions?.medium}
|
|
hotel={hotel}
|
|
restaurants={restaurants}
|
|
additionalData={additionalData}
|
|
sortedFacilities={sortedFacilities}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<Divider variant="vertical" />
|
|
<div className={styles.facilities}>
|
|
<div className={styles.facilityList}>
|
|
{sortedFacilities?.map((facility) => (
|
|
<div className={styles.facilitiesItem} key={facility.id}>
|
|
<FacilityToIcon id={facility.id} color="Icon/Default" />
|
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
|
<p>{facility.name}</p>
|
|
</Typography>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<HotelDetailsSidePeek
|
|
hotel={hotel}
|
|
restaurants={restaurants}
|
|
additionalHotelData={additionalData}
|
|
triggerLabel={intl.formatMessage({
|
|
defaultMessage: "See all amenities",
|
|
})}
|
|
buttonVariant="primary"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{specialAlerts.map((alert) => (
|
|
<SpecialAlert key={alert.id} alert={alert} />
|
|
))}
|
|
</article>
|
|
)
|
|
}
|
|
|
|
function SpecialAlert({ alert }: { alert: Hotel["specialAlerts"][number] }) {
|
|
return (
|
|
<div className={styles.hotelAlert} key={`wrapper_${alert.id}`}>
|
|
<Alert
|
|
key={alert.id}
|
|
type={alert.type}
|
|
heading={alert.heading}
|
|
text={alert.text}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function HotelInfoCardSkeleton() {
|
|
return (
|
|
<article className={styles.container}>
|
|
<section className={styles.wrapper}>
|
|
<div className={styles.imageWrapper}>
|
|
<SkeletonShimmer height="100%" width="100%" />
|
|
</div>
|
|
<div className={styles.hotelContent}>
|
|
<div className={styles.hotelInformation}>
|
|
<SkeletonShimmer width="60ch" height="40px" />
|
|
<div className={styles.hotelAddressDescription}>
|
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
|
<SkeletonShimmer width="40ch" />
|
|
</Typography>
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p>
|
|
<SkeletonShimmer width="60ch" />
|
|
<SkeletonShimmer width="58ch" />
|
|
<SkeletonShimmer width="45ch" />
|
|
</p>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
<Divider variant="vertical" />
|
|
<div className={styles.facilities}>
|
|
<div className={styles.facilityList}>
|
|
<Typography
|
|
variant="Body/Paragraph/mdBold"
|
|
className={styles.facilityTitle}
|
|
>
|
|
<SkeletonShimmer width="20ch" />
|
|
</Typography>
|
|
{[1, 2, 3, 4, 5]?.map((id) => {
|
|
return (
|
|
<div className={styles.facilitiesItem} key={id}>
|
|
<SkeletonShimmer width="10ch" />
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
<div className={styles.hotelAlert}>
|
|
<SkeletonShimmer width="18ch" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</article>
|
|
)
|
|
}
|