feat(SW-508): Added hotel alerts

This commit is contained in:
Erik Tiekstra
2024-10-22 10:49:01 +02:00
parent 0fe4a7c42c
commit a29657a6b2
8 changed files with 82 additions and 36 deletions

View File

@@ -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>
)
} }

View File

@@ -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;
}
} }

View File

@@ -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,7 +71,8 @@ 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}>
<div className={styles.introContainer}>
<IntroSection <IntroSection
hotelName={hotelName} hotelName={hotelName}
hotelDescription={hotelDescription} hotelDescription={hotelDescription}
@@ -80,6 +83,19 @@ export default async function HotelPage() {
<AmenitiesList detailedFacilities={hotelDetailedFacilities} /> <AmenitiesList detailedFacilities={hotelDetailedFacilities} />
</div> </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} /> <Rooms rooms={roomCategories} />
<Facilities facilities={facilities} activitiesCard={activitiesCard} /> <Facilities facilities={facilities} activitiesCard={activitiesCard} />
{faq && ( {faq && (

View File

@@ -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,7 +17,6 @@ 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"
@@ -32,6 +29,5 @@ export default async function SitewideAlert() {
text={sitewideAlert.text} text={sitewideAlert.text}
/> />
</div> </div>
</Suspense>
) )
} }

View File

@@ -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

View File

@@ -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}>

View File

@@ -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({

View File

@@ -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,
} }
}), }),