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