Merged in feature/select-rate-vertical-data-flow (pull request #2535)
Feature/select rate vertical data flow * add fix from SW-2666 * use translations for room packages * move types to it's own file * Merge branch 'master' of bitbucket.org:scandic-swap/web into feature/select-rate-vertical-data-flow * merge * feature/select-rate: double rate for campaing rates * revert NODE_ENV check in Cookiebot script * revert testing values * fix(SW-3171): fix all filter selected in price details * fix(SW-3166): multiroom anchoring when changing filter * fix(SW-3172): check hotelType, show correct breakfast message * Merge branch 'feature/select-rate-vertical-data-flow' of bitbucket.org:scandic-swap/web into feature/select-rate-vertical-data-flow * fix: show special needs icons for subsequent roomTypes SW-3167 * fix: Display strike through text when logged in SW-3168 * fix: Reinstate the scrollToView behaviour when selecting a rate SW-3169 * merge * . * PR fixes * fix: don't return notFound() * . * always include defaults for room packages * merge * merge * merge * Remove floating h1 for new select-rate Approved-by: Anton Gunnarsson
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
import TripAdvisorChip from "@scandic-hotels/booking-flow/components/TripAdvisorChip"
|
||||
import { dt } from "@scandic-hotels/common/dt"
|
||||
import { getSingleDecimal } from "@scandic-hotels/common/utils/numberFormatting"
|
||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { FacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
||||
import ImageGallery from "@/components/ImageGallery"
|
||||
import Alert from "@/components/TempDesignSystem/Alert"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
||||
|
||||
import ReadMore from "../../ReadMore"
|
||||
import { getHotelAlertsForBookingDates } from "../../utils"
|
||||
import HotelDescription from "./HotelDescription"
|
||||
|
||||
import styles from "./hotelInfoCard.module.css"
|
||||
|
||||
import type { Hotel } from "@scandic-hotels/trpc/types/hotel"
|
||||
|
||||
import type { SelectRateBooking } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||
import { SidePeekEnum } from "@/types/components/hotelReservation/sidePeek"
|
||||
|
||||
export type HotelInfoCardProps = {
|
||||
booking: SelectRateBooking
|
||||
hotel: Hotel
|
||||
}
|
||||
|
||||
export async function HotelInfoCard({ booking, hotel }: 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}
|
||||
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>
|
||||
<ReadMore
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "See all amenities",
|
||||
})}
|
||||
hotelId={hotel.operaId}
|
||||
showCTA={false}
|
||||
sidePeekKey={SidePeekEnum.hotelDetails}
|
||||
/>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user