diff --git a/apps/scandic-web/components/Blocks/Alert/index.tsx b/apps/scandic-web/components/Blocks/Alert/index.tsx new file mode 100644 index 000000000..a5561b08e --- /dev/null +++ b/apps/scandic-web/components/Blocks/Alert/index.tsx @@ -0,0 +1,13 @@ +import { Alert } from "@scandic-hotels/design-system/Alert" + +import type { AlertBlock } from "@scandic-hotels/trpc/types/blocks" + +interface AlertBlockProps extends Pick {} + +export function AlertBlock({ alert }: AlertBlockProps) { + if (!alert) { + return null + } + + return +} diff --git a/apps/scandic-web/components/Blocks/index.tsx b/apps/scandic-web/components/Blocks/index.tsx index 2038a2ee3..a5a564ea2 100644 --- a/apps/scandic-web/components/Blocks/index.tsx +++ b/apps/scandic-web/components/Blocks/index.tsx @@ -1,6 +1,7 @@ import { JsonToHtml } from "@scandic-hotels/design-system/JsonToHtml" import { BlocksEnums } from "@scandic-hotels/trpc/types/blocksEnum" +import { AlertBlock } from "@/components/Blocks/Alert" import CardsGrid from "@/components/Blocks/CardsGrid" import CarouselCards from "@/components/Blocks/CarouselCards" import DynamicContent from "@/components/Blocks/DynamicContent" @@ -30,6 +31,10 @@ export default function Blocks({ blocks }: BlocksProps) { key={`${block.typename}-${idx}`} /> ) + case BlocksEnums.block.Alert: + return ( + + ) case BlocksEnums.block.CardsGrid: return ( = { export type Embeds = | { - __typename: Exclude + __typename: Exclude system?: { uid: string } | null url?: string | null permanent_url?: string | null @@ -29,6 +31,25 @@ export type Embeds = image_left?: ImageVaultAsset image_right?: ImageVaultAsset } + | { + __typename: "Alert" + system?: { uid: string } | null + type: AlertTypeEnum + heading: string | null + text: string + phoneContact?: { + displayText: string + phoneNumber: string + footnote?: string | null + } | null + sidepeekContent?: AlertSidepeekContent | null + sidepeekCtaText?: string | null + link?: { + url: string + title: string + keepSearchParams?: boolean + } | null + } export type EmbedByUid = Record> diff --git a/packages/design-system/lib/components/JsonToHtml/renderOptions.tsx b/packages/design-system/lib/components/JsonToHtml/renderOptions.tsx index d56c103fb..94a764116 100644 --- a/packages/design-system/lib/components/JsonToHtml/renderOptions.tsx +++ b/packages/design-system/lib/components/JsonToHtml/renderOptions.tsx @@ -20,6 +20,7 @@ import { mapImageVaultAssetResponseToImageVaultAsset, mapInsertResponseToImageVaultAsset, } from "@scandic-hotels/common/utils/imageVault" +import { Alert } from "../Alert" import { TextLink } from "../TextLink" import type { EmbedByUid } from "./JsonToHtml" import type { Attributes } from "./types/rte/attrs" @@ -458,6 +459,8 @@ export const renderOptions: RenderOptions = { ) } return null + } else if (entry?.node.__typename === "Alert") { + return } else if ( entry?.node.__typename === "AccountPage" || entry?.node.__typename === "CampaignOverviewPage" || diff --git a/packages/trpc/lib/graphql/Fragments/Alert.graphql.ts b/packages/trpc/lib/graphql/Fragments/Alert.graphql.ts index 43191b2b7..702e540d1 100644 --- a/packages/trpc/lib/graphql/Fragments/Alert.graphql.ts +++ b/packages/trpc/lib/graphql/Fragments/Alert.graphql.ts @@ -12,9 +12,11 @@ import { HotelPageLink } from "./PageLink/HotelPageLink.graphql" import { LoyaltyPageLink } from "./PageLink/LoyaltyPageLink.graphql" import { PromoCampaignPageLink } from "./PageLink/PromoCampaignPageLink.graphql" import { StartPageLink } from "./PageLink/StartPageLink.graphql" +import { System } from "./System.graphql" export const Alert = gql` fragment Alert on Alert { + __typename type heading text @@ -76,6 +78,9 @@ export const Alert = gql` } } visible_on + system { + ...System + } } ${AccountPageLink} @@ -90,4 +95,5 @@ export const Alert = gql` ${LoyaltyPageLink} ${StartPageLink} ${PromoCampaignPageLink} + ${System} ` diff --git a/packages/trpc/lib/graphql/Fragments/Blocks/Alert.graphql.ts b/packages/trpc/lib/graphql/Fragments/Blocks/Alert.graphql.ts new file mode 100644 index 000000000..14dee717b --- /dev/null +++ b/packages/trpc/lib/graphql/Fragments/Blocks/Alert.graphql.ts @@ -0,0 +1,35 @@ +import { gql } from "graphql-tag" + +import { Alert } from "../Alert.graphql" + +export const Alert_ContentPage = gql` + fragment Alert_ContentPage on ContentPageBlocksAlert { + __typename + alert { + alertConnection { + edges { + node { + ...Alert + } + } + } + } + } + ${Alert} +` + +export const Alert_CollectionPage = gql` + fragment Alert_CollectionPage on CollectionPageBlocksAlert { + __typename + alert { + alertConnection { + edges { + node { + ...Alert + } + } + } + } + } + ${Alert} +` diff --git a/packages/trpc/lib/graphql/Fragments/Blocks/Content.graphql.ts b/packages/trpc/lib/graphql/Fragments/Blocks/Content.graphql.ts index d2e99bf39..d5420b1ce 100644 --- a/packages/trpc/lib/graphql/Fragments/Blocks/Content.graphql.ts +++ b/packages/trpc/lib/graphql/Fragments/Blocks/Content.graphql.ts @@ -1,5 +1,6 @@ import { gql } from "graphql-tag" +import { Alert } from "../Alert.graphql" import { ImageContainer } from "../ImageContainer.graphql" import { AccountPageLink } from "../PageLink/AccountPageLink.graphql" import { CampaignOverviewPageLink } from "../PageLink/CampaignOverviewPageLink.graphql" @@ -24,6 +25,7 @@ export const Content_ContentPage = gql` node { __typename ...SysAsset + ...Alert ...ImageContainer ...AccountPageLink ...CampaignOverviewPageLink @@ -45,6 +47,7 @@ export const Content_ContentPage = gql` } } ${SysAsset} + ${Alert} ${ImageContainer} ${AccountPageLink} ${CampaignOverviewPageLink} diff --git a/packages/trpc/lib/graphql/Query/CollectionPage/CollectionPage.graphql.ts b/packages/trpc/lib/graphql/Query/CollectionPage/CollectionPage.graphql.ts index c40746c40..dfb4ad150 100644 --- a/packages/trpc/lib/graphql/Query/CollectionPage/CollectionPage.graphql.ts +++ b/packages/trpc/lib/graphql/Query/CollectionPage/CollectionPage.graphql.ts @@ -1,5 +1,6 @@ import { gql } from "graphql-tag" +import { Alert_CollectionPage } from "../../Fragments/Blocks/Alert.graphql" import { CardsGrid_CollectionPage } from "../../Fragments/Blocks/CardsGrid.graphql" import { DynamicContent_CollectionPage } from "../../Fragments/Blocks/DynamicContent.graphql" import { Shortcuts_CollectionPage } from "../../Fragments/Blocks/Shortcuts.graphql" @@ -35,6 +36,7 @@ export const GetCollectionPage = gql` ...UspGrid_CollectionPage ...DynamicContent_CollectionPage ...VideoCard_CollectionPage + ...Alert_CollectionPage } system { ...System @@ -55,6 +57,7 @@ export const GetCollectionPage = gql` ${DynamicContent_CollectionPage} ${VideoCard_CollectionPage} ${Video} + ${Alert_CollectionPage} ` export const GetDaDeEnUrlsCollectionPage = gql` diff --git a/packages/trpc/lib/graphql/Query/ContentPage/ContentPage.graphql.ts b/packages/trpc/lib/graphql/Query/ContentPage/ContentPage.graphql.ts index 08badfc04..ba0136e11 100644 --- a/packages/trpc/lib/graphql/Query/ContentPage/ContentPage.graphql.ts +++ b/packages/trpc/lib/graphql/Query/ContentPage/ContentPage.graphql.ts @@ -1,6 +1,7 @@ import { gql } from "graphql-tag" import { Accordion_ContentPage } from "../../Fragments/Blocks/Accordion.graphql" +import { Alert_ContentPage } from "../../Fragments/Blocks/Alert.graphql" import { CardsGrid_ContentPage } from "../../Fragments/Blocks/CardsGrid.graphql" import { Content_ContentPage } from "../../Fragments/Blocks/Content.graphql" import { DynamicContent_ContentPage } from "../../Fragments/Blocks/DynamicContent.graphql" @@ -112,6 +113,7 @@ export const GetContentPageBlocksBatch2 = gql` ...Table_ContentPage ...TextCols_ContentPage ...UspGrid_ContentPage + ...Alert_ContentPage } } } @@ -120,6 +122,7 @@ export const GetContentPageBlocksBatch2 = gql` ${Table_ContentPage} ${TextCols_ContentPage} ${UspGrid_ContentPage} + ${Alert_ContentPage} ` export const GetDaDeEnUrlsContentPage = gql` diff --git a/packages/trpc/lib/routers/contentstack/base/output.ts b/packages/trpc/lib/routers/contentstack/base/output.ts index c05b5ea63..933d2c970 100644 --- a/packages/trpc/lib/routers/contentstack/base/output.ts +++ b/packages/trpc/lib/routers/contentstack/base/output.ts @@ -1,15 +1,13 @@ import { z, ZodError, ZodIssueCode } from "zod" -import { - AlertTypeEnum, - AlertVisibleOnEnum, -} from "@scandic-hotels/common/constants/alert" +import { AlertVisibleOnEnum } from "@scandic-hotels/common/constants/alert" import { Lang } from "@scandic-hotels/common/constants/language" import { logger } from "@scandic-hotels/common/logger" import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url" import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator" import { discriminatedUnion } from "../../../utils/discriminatedUnion" +import { transformedAlertSchema } from "../schemas/alert" import { infoCardBlockSchema, transformInfoCardBlock, @@ -410,85 +408,6 @@ export const headerSchema = z } }) -export const alertSchema = z - .object({ - type: z.nativeEnum(AlertTypeEnum), - text: z.string(), - heading: z.string(), - phone_contact: z.object({ - display_text: z.string(), - phone_number: z.string().nullable(), - footnote: z.string().nullable(), - }), - has_link: z.boolean(), - link: linkAndTitleSchema, - has_sidepeek_button: z.boolean(), - sidepeek_button: z.object({ - cta_text: z.string(), - }), - sidepeek_content: z.object({ - heading: z.string(), - content: z.object({ - json: z.any(), - embedded_itemsConnection: z.object({ - edges: z.array( - z.object({ - node: linkUnionSchema.transform((data) => { - const link = transformPageLink(data) - if (link) { - return link - } - return data - }), - }) - ), - }), - }), - }), - visible_on: z.array(z.string()).nullish().default([]), - }) - .transform( - ({ - type, - heading, - text, - phone_contact, - has_link, - link, - has_sidepeek_button, - sidepeek_button, - sidepeek_content, - visible_on, - }) => { - const hasLink = has_link && link.link - return { - type, - text, - heading, - visible_on, - phoneContact: - phone_contact.display_text && phone_contact.phone_number - ? { - displayText: phone_contact.display_text, - phoneNumber: phone_contact.phone_number, - footnote: phone_contact.footnote, - } - : null, - hasSidepeekButton: !!has_sidepeek_button, - link: hasLink - ? { - url: link.link.url, - title: link.title, - } - : null, - sidepeekButton: - !hasLink && has_sidepeek_button ? sidepeek_button : null, - sidepeekContent: - !hasLink && has_sidepeek_button ? sidepeek_content : null, - } - } - ) - export const siteConfigSchema = z .object({ all_site_config: z.object({ @@ -503,7 +422,7 @@ export const siteConfigSchema = z alertConnection: z.object({ edges: z.array( z.object({ - node: alertSchema, + node: transformedAlertSchema, }) ), }), diff --git a/packages/trpc/lib/routers/contentstack/base/utils.ts b/packages/trpc/lib/routers/contentstack/base/utils.ts index e385c69de..eb4b76a06 100644 --- a/packages/trpc/lib/routers/contentstack/base/utils.ts +++ b/packages/trpc/lib/routers/contentstack/base/utils.ts @@ -5,11 +5,11 @@ import { logger } from "@scandic-hotels/common/logger" import { getValueFromContactConfig } from "../../../utils/contactConfig" -import type { AlertOutput } from "../../../types/siteConfig" +import type { Alert } from "../schemas/alert" import type { ContactConfig } from "./output" export function getAlertPhoneContactData( - alert: AlertOutput, + alert: Alert, contactConfig: ContactConfig ) { if (alert.phoneContact) { diff --git a/packages/trpc/lib/routers/contentstack/collectionPage/output.ts b/packages/trpc/lib/routers/contentstack/collectionPage/output.ts index 2270511b8..c15e86c56 100644 --- a/packages/trpc/lib/routers/contentstack/collectionPage/output.ts +++ b/packages/trpc/lib/routers/contentstack/collectionPage/output.ts @@ -4,6 +4,7 @@ import { transformedImageVaultAssetSchema } from "@scandic-hotels/common/utils/i import { CollectionPageEnum } from "../../../types/collectionPage" import { discriminatedUnionArray } from "../../../utils/discriminatedUnion" +import { alertBlockSchema } from "../schemas/blocks/alert" import { cardsGridSchema } from "../schemas/blocks/cardsGrid" import { dynamicContentSchema as blockDynamicContentSchema } from "../schemas/blocks/dynamicContent" import { shortcutsSchema } from "../schemas/blocks/shortcuts" @@ -47,12 +48,19 @@ export const collectionPageVideoCard = z }) .merge(videoCardSchema) +export const collectionPageAlert = z + .object({ + __typename: z.literal(CollectionPageEnum.ContentStack.blocks.Alert), + }) + .merge(alertBlockSchema) + export const blocksSchema = z.discriminatedUnion("__typename", [ collectionPageCards, collectionPageDynamicContent, collectionPageShortcuts, collectionPageUspGrid, collectionPageVideoCard, + collectionPageAlert, ]) const navigationLinksSchema = z diff --git a/packages/trpc/lib/routers/contentstack/contentPage/output.ts b/packages/trpc/lib/routers/contentstack/contentPage/output.ts index 25d4bc559..fdd31b8ad 100644 --- a/packages/trpc/lib/routers/contentstack/contentPage/output.ts +++ b/packages/trpc/lib/routers/contentstack/contentPage/output.ts @@ -5,6 +5,7 @@ import { transformedImageVaultAssetSchema } from "@scandic-hotels/common/utils/i import { ContentPageEnum } from "../../../types/contentPage" import { discriminatedUnionArray } from "../../../utils/discriminatedUnion" import { accordionSchema } from "../schemas/blocks/accordion" +import { alertBlockSchema } from "../schemas/blocks/alert" import { cardsGridSchema } from "../schemas/blocks/cardsGrid" import { contentSchema as blockContentSchema } from "../schemas/blocks/content" import { dynamicContentSchema as blockDynamicContentSchema } from "../schemas/blocks/dynamicContent" @@ -101,6 +102,12 @@ export const contentPageVideo = z }) .merge(videoBlockSchema) +export const contentPageAlert = z + .object({ + __typename: z.literal(ContentPageEnum.ContentStack.blocks.Alert), + }) + .merge(alertBlockSchema) + export const blocksSchema = z.discriminatedUnion("__typename", [ contentPageAccordion, contentPageCards, @@ -114,6 +121,7 @@ export const blocksSchema = z.discriminatedUnion("__typename", [ contentPageHotelListing, contentPageVideoCard, contentPageVideo, + contentPageAlert, ]) export const contentPageSidebarContent = z diff --git a/packages/trpc/lib/routers/contentstack/schemas/alert.ts b/packages/trpc/lib/routers/contentstack/schemas/alert.ts new file mode 100644 index 000000000..ebe083549 --- /dev/null +++ b/packages/trpc/lib/routers/contentstack/schemas/alert.ts @@ -0,0 +1,92 @@ +import z from "zod" + +import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert" + +import { linkAndTitleSchema } from "./linkConnection" +import { linkUnionSchema, transformPageLink } from "./pageLinks" +import { systemSchema } from "./system" + +export const alertSchema = z.object({ + type: z.nativeEnum(AlertTypeEnum), + text: z.string(), + heading: z.string(), + phone_contact: z.object({ + display_text: z.string(), + phone_number: z.string().nullish(), + footnote: z.string().nullish(), + }), + has_link: z.boolean(), + link: linkAndTitleSchema, + has_sidepeek_button: z.boolean(), + sidepeek_button: z.object({ + cta_text: z.string(), + }), + sidepeek_content: z.object({ + heading: z.string(), + content: z.object({ + json: z.any(), + embedded_itemsConnection: z.object({ + edges: z.array( + z.object({ + node: linkUnionSchema.transform((data) => { + const link = transformPageLink(data) + if (link) { + return link + } + return data + }), + }) + ), + }), + }), + }), + visible_on: z.array(z.string()).nullish().default([]), + system: systemSchema, +}) + +export const transformedAlertSchema = + alertSchema.transform(transformAlertSchema) + +export function transformAlertSchema(data: typeof alertSchema._type) { + const { + type, + heading, + text, + phone_contact, + has_link, + link, + has_sidepeek_button, + sidepeek_button, + sidepeek_content, + visible_on, + system, + } = data + + const hasLink = has_link && link.link + return { + type, + text, + heading, + visible_on, + phoneContact: + phone_contact.display_text && phone_contact.phone_number + ? { + displayText: phone_contact.display_text, + phoneNumber: phone_contact.phone_number, + footnote: phone_contact.footnote, + } + : null, + hasSidepeekButton: !!has_sidepeek_button, + link: hasLink + ? { + url: link.link.url, + title: link.title, + } + : null, + sidepeekButton: !hasLink && has_sidepeek_button ? sidepeek_button : null, + sidepeekContent: !hasLink && has_sidepeek_button ? sidepeek_content : null, + system, + } +} + +export type Alert = z.output diff --git a/packages/trpc/lib/routers/contentstack/schemas/blocks/alert.ts b/packages/trpc/lib/routers/contentstack/schemas/blocks/alert.ts new file mode 100644 index 000000000..893886af3 --- /dev/null +++ b/packages/trpc/lib/routers/contentstack/schemas/blocks/alert.ts @@ -0,0 +1,25 @@ +import { z } from "zod" + +import { BlocksEnums } from "../../../../types/blocksEnum" +import { transformedAlertSchema } from "../alert" + +export const alertBlockSchema = z.object({ + typename: z.literal(BlocksEnums.block.Alert).default(BlocksEnums.block.Alert), + alert: z + .object({ + alertConnection: z.object({ + edges: z.array( + z.object({ + node: transformedAlertSchema, + }) + ), + }), + }) + .transform((data) => { + const alert = data.alertConnection.edges[0]?.node + if (!alert) { + return null + } + return alert + }), +}) diff --git a/packages/trpc/lib/routers/contentstack/schemas/blocks/content.ts b/packages/trpc/lib/routers/contentstack/schemas/blocks/content.ts index 51f09afed..bb455d98f 100644 --- a/packages/trpc/lib/routers/contentstack/schemas/blocks/content.ts +++ b/packages/trpc/lib/routers/contentstack/schemas/blocks/content.ts @@ -1,6 +1,8 @@ import { z } from "zod" import { BlocksEnums } from "../../../../types/blocksEnum" +import { ContentEnum } from "../../../../types/content" +import { alertSchema, transformAlertSchema } from "../alert" import { rawLinkUnionSchema, transformPageLink } from "../pageLinks" import { imageContainerSchema } from "./imageContainer" import { sysAssetSchema } from "./sysAsset" @@ -22,6 +24,11 @@ export const contentSchema = z.object({ .discriminatedUnion("__typename", [ imageContainerSchema, sysAssetSchema, + alertSchema.merge( + z.object({ + __typename: z.literal(ContentEnum.blocks.Alert), + }) + ), ...rawLinkUnionSchema.options, ]) .transform((data) => { @@ -29,6 +36,12 @@ export const contentSchema = z.object({ if (link) { return link } + if (data.__typename === ContentEnum.blocks.Alert) { + return { + __typename: data.__typename, + ...transformAlertSchema(data), + } + } return data }), }) diff --git a/packages/trpc/lib/types/blocks.ts b/packages/trpc/lib/types/blocks.ts index 3a3089e8d..1f1bc6395 100644 --- a/packages/trpc/lib/types/blocks.ts +++ b/packages/trpc/lib/types/blocks.ts @@ -12,6 +12,7 @@ import type { uspGridSchema } from "@scandic-hotels/trpc/routers/contentstack/sc import type { videoCardSchema } from "@scandic-hotels/trpc/routers/contentstack/schemas/blocks/videoCard" import type { z } from "zod" +import type { alertBlockSchema } from "../routers/contentstack/schemas/blocks/alert" import type { videoBlockSchema } from "../routers/contentstack/schemas/blocks/video" export interface TeaserCard extends z.output {} @@ -32,3 +33,4 @@ export interface CarouselCards extends z.output {} export interface CardGallery extends z.output {} export interface VideoCard extends z.output {} export interface VideoBlock extends z.output {} +export interface AlertBlock extends z.output {} diff --git a/packages/trpc/lib/types/blocksEnum.ts b/packages/trpc/lib/types/blocksEnum.ts index 93b35e5d9..7edcc31b5 100644 --- a/packages/trpc/lib/types/blocksEnum.ts +++ b/packages/trpc/lib/types/blocksEnum.ts @@ -21,5 +21,6 @@ export namespace BlocksEnums { Essentials = "Essentials", VideoCard = "VideoCard", Video = "Video", + Alert = "Alert", } } diff --git a/packages/trpc/lib/types/collectionPage.ts b/packages/trpc/lib/types/collectionPage.ts index 2adfcc4b2..5186ba6ce 100644 --- a/packages/trpc/lib/types/collectionPage.ts +++ b/packages/trpc/lib/types/collectionPage.ts @@ -21,6 +21,7 @@ export namespace CollectionPageEnum { Shortcuts = "CollectionPageBlocksShortcuts", UspGrid = "CollectionPageBlocksUspGrid", VideoCard = "CollectionPageBlocksVideoCard", + Alert = "CollectionPageBlocksAlert", } } } diff --git a/packages/trpc/lib/types/content.ts b/packages/trpc/lib/types/content.ts index c78ddd992..23421fdb7 100644 --- a/packages/trpc/lib/types/content.ts +++ b/packages/trpc/lib/types/content.ts @@ -14,5 +14,6 @@ export namespace ContentEnum { StartPage = "StartPage", PromoCampaignPage = "PromoCampaignPage", SysAsset = "SysAsset", + Alert = "Alert", } } diff --git a/packages/trpc/lib/types/contentPage.ts b/packages/trpc/lib/types/contentPage.ts index 767a2c7c5..0cf092a58 100644 --- a/packages/trpc/lib/types/contentPage.ts +++ b/packages/trpc/lib/types/contentPage.ts @@ -22,6 +22,7 @@ export namespace ContentPageEnum { export namespace ContentStack { export const enum blocks { Accordion = "ContentPageBlocksAccordion", + Alert = "ContentPageBlocksAlert", CardsGrid = "ContentPageBlocksCardsGrid", Content = "ContentPageBlocksContent", DynamicContent = "ContentPageBlocksDynamicContent", diff --git a/packages/trpc/lib/types/siteConfig.ts b/packages/trpc/lib/types/siteConfig.ts index acc843166..f6b400cb9 100644 --- a/packages/trpc/lib/types/siteConfig.ts +++ b/packages/trpc/lib/types/siteConfig.ts @@ -1,7 +1,6 @@ import type { z } from "zod" import type { - alertSchema, siteConfigSchema, sitewideCampaignBannerSchema, } from "../routers/contentstack/base/output" @@ -9,19 +8,6 @@ import type { export type GetSiteConfigData = z.input export type SiteConfig = z.output -export type AlertOutput = z.output -export type SidepeekContent = AlertOutput["sidepeekContent"] - -export type AlertPhoneContact = { - displayText: string - phoneNumber?: string - footnote?: string | null -} - -export type Alert = Omit & { - phoneContact: AlertPhoneContact | null -} - export type GetSitewideCampaignBannerData = z.output< typeof sitewideCampaignBannerSchema >