feat(SW-664): Hotel listing component and queries for content pages
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { ScandicLogoIcon } from "@/components/Icons"
|
||||
import HotelLogo from "@/components/Icons/Logos"
|
||||
import Image from "@/components/Image"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
@@ -10,25 +10,27 @@ import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
import getSingleDecimal from "@/utils/numberFormatting"
|
||||
|
||||
import { getTypeSpecificInformation } from "./utils"
|
||||
|
||||
import styles from "./hotelListingItem.module.css"
|
||||
|
||||
import type { HotelListingItemProps } from "@/types/components/contentPage/hotelListingItem"
|
||||
|
||||
export default async function HotelListingItem({
|
||||
imageUrl,
|
||||
altText,
|
||||
name,
|
||||
address,
|
||||
distanceToCentre,
|
||||
description,
|
||||
link,
|
||||
hotel,
|
||||
contentType = "hotel",
|
||||
url,
|
||||
}: HotelListingItemProps) {
|
||||
const intl = await getIntl()
|
||||
const { description, imageSrc, altText } = getTypeSpecificInformation(
|
||||
contentType,
|
||||
hotel
|
||||
)
|
||||
|
||||
return (
|
||||
<article className={styles.container}>
|
||||
<Image
|
||||
src={imageUrl}
|
||||
src={imageSrc}
|
||||
alt={altText}
|
||||
width={300}
|
||||
height={200}
|
||||
@@ -36,35 +38,43 @@ export default async function HotelListingItem({
|
||||
/>
|
||||
<section className={styles.content}>
|
||||
<div className={styles.intro}>
|
||||
<ScandicLogoIcon color="red" />
|
||||
<HotelLogo hotelId={hotel.operaId} hotelType={hotel.hotelType} />
|
||||
<Subtitle asChild>
|
||||
<Title as="h3">{name}</Title>
|
||||
<Title as="h3">{hotel.name}</Title>
|
||||
</Subtitle>
|
||||
<div className={styles.captions}>
|
||||
<Caption color="uiTextPlaceholder">{address}</Caption>
|
||||
<Caption color="uiTextPlaceholder">
|
||||
{hotel.address.streetAddress}
|
||||
</Caption>
|
||||
<div className={styles.dividerContainer}>
|
||||
<Divider variant="vertical" color="beige" />
|
||||
</div>
|
||||
<Caption color="uiTextPlaceholder">
|
||||
{intl.formatMessage(
|
||||
{ id: "Distance in km to city centre" },
|
||||
{ number: getSingleDecimal(distanceToCentre / 1000) }
|
||||
{
|
||||
number: getSingleDecimal(
|
||||
hotel.location.distanceToCentre / 1000
|
||||
),
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
</div>
|
||||
</div>
|
||||
<Body>{description}</Body>
|
||||
<Button
|
||||
intent="primary"
|
||||
theme="base"
|
||||
size="small"
|
||||
className={styles.button}
|
||||
asChild
|
||||
>
|
||||
<Link href={link} color="white">
|
||||
{intl.formatMessage({ id: "See hotel details" })}
|
||||
</Link>
|
||||
</Button>
|
||||
{url && (
|
||||
<Button
|
||||
intent="primary"
|
||||
theme="base"
|
||||
size="small"
|
||||
className={styles.button}
|
||||
asChild
|
||||
>
|
||||
<Link href={url} color="white">
|
||||
{intl.formatMessage({ id: "See hotel details" })}
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</section>
|
||||
</article>
|
||||
)
|
||||
36
components/Blocks/HotelListing/HotelListingItem/utils.ts
Normal file
36
components/Blocks/HotelListing/HotelListingItem/utils.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { Hotel } from "@/types/hotel"
|
||||
import type { HotelListing } from "@/types/trpc/routers/contentstack/blocks"
|
||||
|
||||
export function getTypeSpecificInformation(
|
||||
contentType: HotelListing["contentType"],
|
||||
hotel: Hotel
|
||||
) {
|
||||
const { restaurantsOverviewPage, images } = hotel.hotelContent
|
||||
const { descriptions, meetingDescription } = hotel.hotelContent.texts
|
||||
const hotelData = {
|
||||
description: descriptions.short,
|
||||
imageSrc: images.imageSizes.small,
|
||||
altText: images.metaData.altText,
|
||||
}
|
||||
switch (contentType) {
|
||||
case "meeting":
|
||||
const meetingImage = hotel.conferencesAndMeetings?.heroImages[0]
|
||||
return {
|
||||
description: meetingDescription?.short || hotelData.description,
|
||||
imageSrc: meetingImage?.imageSizes.small || hotelData.imageSrc,
|
||||
altText: meetingImage?.metaData.altText || hotelData.altText,
|
||||
}
|
||||
case "restaurant":
|
||||
const restaurantImage = hotel.restaurantImages?.heroImages[0]
|
||||
return {
|
||||
description:
|
||||
restaurantsOverviewPage.restaurantsContentDescriptionShort ||
|
||||
hotelData.description,
|
||||
imageSrc: restaurantImage?.imageSizes.small || hotelData.imageSrc,
|
||||
altText: restaurantImage?.metaData.altText || hotelData.altText,
|
||||
}
|
||||
case "hotel":
|
||||
default:
|
||||
return hotelData
|
||||
}
|
||||
}
|
||||
40
components/Blocks/HotelListing/index.tsx
Normal file
40
components/Blocks/HotelListing/index.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { getHotels } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
|
||||
import HotelListingItem from "./HotelListingItem"
|
||||
|
||||
import type { HotelListingProps } from "@/types/components/blocks/hotelListing"
|
||||
|
||||
export default async function HotelListing({
|
||||
heading,
|
||||
locationFilter,
|
||||
hotelsToInclude,
|
||||
contentType,
|
||||
}: HotelListingProps) {
|
||||
const hotels = await getHotels({
|
||||
locationFilter,
|
||||
hotelsToInclude: hotelsToInclude,
|
||||
})
|
||||
|
||||
if (!hotels.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
<Title level="h4" as="h3" textTransform="capitalize">
|
||||
{heading}
|
||||
</Title>
|
||||
{hotels.map(({ data, url }) => (
|
||||
<HotelListingItem
|
||||
key={data.name}
|
||||
hotel={data}
|
||||
contentType={contentType}
|
||||
url={url}
|
||||
/>
|
||||
))}
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
@@ -6,13 +6,14 @@ import UspGrid from "@/components/Blocks/UspGrid"
|
||||
import JsonToHtml from "@/components/JsonToHtml"
|
||||
|
||||
import AccordionSection from "./Accordion"
|
||||
import HotelListing from "./HotelListing"
|
||||
import Table from "./Table"
|
||||
|
||||
import type { BlocksProps } from "@/types/components/blocks"
|
||||
import { BlocksEnums } from "@/types/enums/blocks"
|
||||
|
||||
export default function Blocks({ blocks }: BlocksProps) {
|
||||
return blocks.map((block, idx) => {
|
||||
return blocks.map(async (block, idx) => {
|
||||
const firstItem = idx === 0
|
||||
switch (block.typename) {
|
||||
case BlocksEnums.block.Accordion:
|
||||
@@ -48,6 +49,21 @@ export default function Blocks({ blocks }: BlocksProps) {
|
||||
key={`${block.dynamic_content.title}-${idx}`}
|
||||
/>
|
||||
)
|
||||
case BlocksEnums.block.HotelListing:
|
||||
const { heading, contentType, locationFilter, hotelsToInclude } =
|
||||
block.hotel_listing
|
||||
if (!locationFilter && !hotelsToInclude.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<HotelListing
|
||||
heading={heading}
|
||||
locationFilter={locationFilter}
|
||||
hotelsToInclude={hotelsToInclude}
|
||||
contentType={contentType}
|
||||
/>
|
||||
)
|
||||
case BlocksEnums.block.Shortcuts:
|
||||
return (
|
||||
<ShortcutsList
|
||||
|
||||
@@ -7,6 +7,7 @@ import { selectRate } from "@/constants/routes/hotelReservation"
|
||||
import { useHotelsMapStore } from "@/stores/hotels-map"
|
||||
|
||||
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
||||
import HotelLogo from "@/components/Icons/Logos"
|
||||
import ImageGallery from "@/components/ImageGallery"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
@@ -18,7 +19,6 @@ 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"
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
ScandicLogoIcon,
|
||||
} from "@/components/Icons"
|
||||
|
||||
import type { HotelLogoProps } from "@/types/components/hotelReservation/selectHotel/hotelLogoProps"
|
||||
import type { HotelLogoProps } from "@/types/components/hotelLogo"
|
||||
import { HotelTypeEnum } from "@/types/enums/hotelType"
|
||||
import { SignatureHotelEnum } from "@/types/enums/signatureHotel"
|
||||
|
||||
Reference in New Issue
Block a user