feat(SW-508): Added hotel alerts
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { env } from "@/env/server"
|
||||
import { Suspense } from "react"
|
||||
|
||||
import SitewideAlert, { preload } from "@/components/SitewideAlert"
|
||||
import { setLang } from "@/i18n/serverContext"
|
||||
|
||||
import { LangParams, PageArgs } from "@/types/params"
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default function SitewideAlertPage({ params }: PageArgs<LangParams>) {
|
||||
if (env.HIDE_FOR_NEXT_RELEASE) {
|
||||
@@ -13,5 +14,9 @@ export default function SitewideAlertPage({ params }: PageArgs<LangParams>) {
|
||||
setLang(params.lang)
|
||||
preload()
|
||||
|
||||
return <SitewideAlert />
|
||||
return (
|
||||
<Suspense>
|
||||
<SitewideAlert />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.overview {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.introContainer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -37,6 +42,11 @@
|
||||
scroll-margin-top: var(--hotel-page-scroll-margin-top);
|
||||
}
|
||||
|
||||
.alertsContainer {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.pageContainer {
|
||||
grid-template-areas:
|
||||
@@ -76,10 +86,4 @@
|
||||
padding-left: var(--Spacing-x5);
|
||||
padding-right: var(--Spacing-x5);
|
||||
}
|
||||
.introContainer {
|
||||
grid-template-columns: 38rem minmax(max-content, 16rem);
|
||||
justify-content: space-between;
|
||||
gap: var(--Spacing-x2);
|
||||
align-items: end;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import AccordionSection from "@/components/Blocks/Accordion"
|
||||
import SidePeekProvider from "@/components/SidePeekProvider"
|
||||
import Alert from "@/components/TempDesignSystem/Alert"
|
||||
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
@@ -49,6 +50,7 @@ export default async function HotelPage() {
|
||||
pointsOfInterest,
|
||||
facilities,
|
||||
faq,
|
||||
alerts,
|
||||
} = hotelData
|
||||
|
||||
const topThreePois = pointsOfInterest.slice(0, 3)
|
||||
@@ -69,16 +71,30 @@ export default async function HotelPage() {
|
||||
hasFAQ={!!faq}
|
||||
/>
|
||||
<main className={styles.mainSection}>
|
||||
<div id={HotelHashValues.overview} className={styles.introContainer}>
|
||||
<IntroSection
|
||||
hotelName={hotelName}
|
||||
hotelDescription={hotelDescription}
|
||||
location={hotelLocation}
|
||||
address={hotelAddress}
|
||||
tripAdvisor={hotelRatings?.tripAdvisor}
|
||||
/>
|
||||
<div id={HotelHashValues.overview} className={styles.overview}>
|
||||
<div className={styles.introContainer}>
|
||||
<IntroSection
|
||||
hotelName={hotelName}
|
||||
hotelDescription={hotelDescription}
|
||||
location={hotelLocation}
|
||||
address={hotelAddress}
|
||||
tripAdvisor={hotelRatings?.tripAdvisor}
|
||||
/>
|
||||
|
||||
<AmenitiesList detailedFacilities={hotelDetailedFacilities} />
|
||||
<AmenitiesList detailedFacilities={hotelDetailedFacilities} />
|
||||
</div>
|
||||
{alerts.length ? (
|
||||
<div className={styles.alertsContainer}>
|
||||
{alerts.map((alert) => (
|
||||
<Alert
|
||||
key={alert.id}
|
||||
type={alert.type}
|
||||
heading={alert.heading}
|
||||
text={alert.text}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<Rooms rooms={roomCategories} />
|
||||
<Facilities facilities={facilities} activitiesCard={activitiesCard} />
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Suspense } from "react"
|
||||
|
||||
import { getSiteConfig } from "@/lib/trpc/memoizedRequests"
|
||||
|
||||
import Alert from "../TempDesignSystem/Alert"
|
||||
@@ -19,19 +17,17 @@ export default async function SitewideAlert() {
|
||||
|
||||
const { sitewideAlert } = siteConfig
|
||||
return (
|
||||
<Suspense>
|
||||
<div className={`${styles.sitewideAlert} ${styles[sitewideAlert.type]}`}>
|
||||
<Alert
|
||||
variant="banner"
|
||||
type={sitewideAlert.type}
|
||||
link={sitewideAlert.link}
|
||||
phoneContact={sitewideAlert.phoneContact}
|
||||
sidepeekCtaText={sitewideAlert.sidepeekButton?.cta_text}
|
||||
sidepeekContent={sitewideAlert.sidepeekContent}
|
||||
heading={sitewideAlert.heading}
|
||||
text={sitewideAlert.text}
|
||||
/>
|
||||
</div>
|
||||
</Suspense>
|
||||
<div className={`${styles.sitewideAlert} ${styles[sitewideAlert.type]}`}>
|
||||
<Alert
|
||||
variant="banner"
|
||||
type={sitewideAlert.type}
|
||||
link={sitewideAlert.link}
|
||||
phoneContact={sitewideAlert.phoneContact}
|
||||
sidepeekCtaText={sitewideAlert.sidepeekButton?.cta_text}
|
||||
sidepeekContent={sitewideAlert.sidepeekContent}
|
||||
heading={sitewideAlert.heading}
|
||||
text={sitewideAlert.text}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import type { SidepeekContent } from "@/types/trpc/routers/contentstack/siteConf
|
||||
export interface AlertProps extends VariantProps<typeof alertVariants> {
|
||||
className?: string
|
||||
type: AlertTypeEnum
|
||||
heading?: string
|
||||
text: string
|
||||
heading?: string | null
|
||||
text?: string | null
|
||||
phoneContact?: {
|
||||
displayText: string
|
||||
phoneNumber?: string
|
||||
|
||||
@@ -27,6 +27,10 @@ export default function Alert({
|
||||
})
|
||||
const Icon = getIconByAlertType(type)
|
||||
|
||||
if (!text && !heading) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<section className={classNames}>
|
||||
<div className={styles.content}>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
import { toLang } from "@/server/utils"
|
||||
|
||||
import { imageMetaDataSchema, imageSizesSchema } from "./schemas/image"
|
||||
import { roomSchema } from "./schemas/room"
|
||||
import { getPoiGroupByCategoryName } from "./utils"
|
||||
|
||||
import { AlertTypeEnum } from "@/types/enums/alert"
|
||||
import { FacilityEnum } from "@/types/enums/facilities"
|
||||
import { PointOfInterestCategoryNameEnum } from "@/types/hotel"
|
||||
|
||||
@@ -321,6 +323,7 @@ const socialMediaSchema = z.object({
|
||||
|
||||
const metaSpecialAlertSchema = z.object({
|
||||
type: z.string(),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
displayInBookingFlow: z.boolean(),
|
||||
startDate: z.string(),
|
||||
@@ -328,7 +331,23 @@ const metaSpecialAlertSchema = z.object({
|
||||
})
|
||||
|
||||
const metaSchema = z.object({
|
||||
specialAlerts: z.array(metaSpecialAlertSchema),
|
||||
specialAlerts: z
|
||||
.array(metaSpecialAlertSchema)
|
||||
.transform((data) => {
|
||||
const now = dt().utc().format("YYYY-MM-DD")
|
||||
const filteredAlerts = data.filter((alert) => {
|
||||
const shouldShowNow = alert.startDate <= now && alert.endDate >= now
|
||||
const hasText = alert.description || alert.title
|
||||
return shouldShowNow && hasText
|
||||
})
|
||||
return filteredAlerts.map((alert, idx) => ({
|
||||
id: `alert-${alert.type}-${idx}`,
|
||||
type: AlertTypeEnum.Info,
|
||||
heading: alert.title || null,
|
||||
text: alert.description || null,
|
||||
}))
|
||||
})
|
||||
.default([]),
|
||||
})
|
||||
|
||||
const relationshipsSchema = z.object({
|
||||
|
||||
@@ -219,6 +219,7 @@ export const hotelQueryRouter = router({
|
||||
|
||||
const hotelAttributes = validatedHotelData.data.data.attributes
|
||||
const images = extractHotelImages(hotelAttributes)
|
||||
const hotelAlerts = hotelAttributes.meta?.specialAlerts || []
|
||||
|
||||
const roomCategories = included
|
||||
? included.filter((item) => item.type === "roomcategories")
|
||||
@@ -262,6 +263,7 @@ export const hotelQueryRouter = router({
|
||||
roomCategories,
|
||||
activitiesCard: activities?.upcoming_activities_card,
|
||||
facilities,
|
||||
alerts: hotelAlerts,
|
||||
faq: contentstackData?.faq,
|
||||
}
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user