diff --git a/apps/scandic-web/.env.local.example b/apps/scandic-web/.env.local.example index 6a4288baa..66a471991 100644 --- a/apps/scandic-web/.env.local.example +++ b/apps/scandic-web/.env.local.example @@ -72,3 +72,5 @@ SAS_OCP_APIM="" SAS_AUTH_CLIENTID="" LOKALISE_API_KEY="" + +CAMPAIGN_PAGES_ENABLED="0" # 0 - disabled, 1 - enabled diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_page/[uid]/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_page/[uid]/page.tsx index 51a61353e..54a81b00f 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_page/[uid]/page.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(public)/(contentTypes)/campaign_page/[uid]/page.tsx @@ -9,7 +9,7 @@ import CampaignPageSkeleton from "@/components/ContentType/CampaignPage/Campaign export { generateMetadata } from "@/utils/generateMetadata" export default async function CampaignPagePage() { - if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") { + if (!env.CAMPAIGN_PAGES_ENABLED) { return notFound() } diff --git a/apps/scandic-web/components/Blocks/Essentials/essentials.module.css b/apps/scandic-web/components/Blocks/Essentials/essentials.module.css new file mode 100644 index 000000000..59460f776 --- /dev/null +++ b/apps/scandic-web/components/Blocks/Essentials/essentials.module.css @@ -0,0 +1,108 @@ +.essentialsSection { + display: grid; + gap: var(--Space-x5); +} + +.header { + display: grid; + gap: var(--Space-x2); +} + +.heading { + color: var(--Text-Heading); +} + +.list { + list-style: none; + display: grid; + grid-auto-rows: auto; + row-gap: var(--Space-x15); +} + +.listItem { + position: relative; + display: grid; + gap: 19px; /* Special from Figma */ + color: var(--Text-Secondary); + justify-items: center; + padding: 0 var(--Space-x1); + + &::after { + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + content: ""; + width: 1px; + background-color: var(--Border-Default); + height: 82px; /* Special from Figma */ + } +} + +.text { + display: grid; + gap: var(--Space-x1); + justify-items: center; +} + +@media screen and (max-width: 949px) { + .list { + grid-template-columns: repeat(2, 1fr); + + .listItem:nth-child(2n)::after { + display: none; + } + } +} + +@media screen and (min-width: 950px) and (max-width: 1366px) { + .list { + grid-template-columns: repeat(4, 1fr); + + &:not(.count3, .count5, .count6) .listItem:nth-child(4n)::after { + display: none; + } + + &.count3, + &.count5, + &.count6 { + grid-template-columns: repeat(3, 1fr); + + .listItem:nth-child(3n)::after { + display: none; + } + } + } +} + +@media screen and (min-width: 1367px) { + .list { + grid-template-columns: repeat(4, 1fr); + + &:not(.count3, .count5, .count6) .listItem:nth-child(4n)::after { + display: none; + } + + &.count3 { + grid-template-columns: repeat(3, 1fr); + + .listItem:nth-child(3n)::after { + display: none; + } + } + &.count5 { + grid-template-columns: repeat(5, 1fr); + + .listItem:nth-child(5n)::after { + display: none; + } + } + &.count6 { + grid-template-columns: repeat(6, 1fr); + + .listItem:nth-child(6n)::after { + display: none; + } + } + } +} diff --git a/apps/scandic-web/components/Blocks/Essentials/index.tsx b/apps/scandic-web/components/Blocks/Essentials/index.tsx new file mode 100644 index 000000000..9aaeca310 --- /dev/null +++ b/apps/scandic-web/components/Blocks/Essentials/index.tsx @@ -0,0 +1,53 @@ +import { cx } from "class-variance-authority" + +import { Typography } from "@scandic-hotels/design-system/Typography" + +import IconByCSSelect from "@/components/Icons/IconByCSSelect" + +import styles from "./essentials.module.css" + +import type { EssentialsBlock } from "@/types/trpc/routers/contentstack/campaignPage" + +interface EssentialsProps { + content: EssentialsBlock +} + +export default async function Essentials({ content }: EssentialsProps) { + const { title, preamble, items } = content + + return ( +
+
+ +

{title}

+
+ {preamble ? ( + +

{preamble}

+
+ ) : null} +
+ +
+ ) +} diff --git a/apps/scandic-web/components/Blocks/index.tsx b/apps/scandic-web/components/Blocks/index.tsx index 60635cc3a..b78b834d9 100644 --- a/apps/scandic-web/components/Blocks/index.tsx +++ b/apps/scandic-web/components/Blocks/index.tsx @@ -10,6 +10,7 @@ import JsonToHtml from "@/components/JsonToHtml" import AccordionSection from "./Accordion" import CardGallery from "./CardGallery" +import Essentials from "./Essentials" import FullWidthCampaign from "./FullWidthCampaign" import HotelListing from "./HotelListing" import JoinScandicFriends from "./JoinScandicFriends" @@ -112,6 +113,8 @@ export default function Blocks({ blocks }: BlocksProps) { ) + case BlocksEnums.block.Essentials: + return default: return null } diff --git a/apps/scandic-web/components/ContentType/CampaignPage/Blocks/index.tsx b/apps/scandic-web/components/ContentType/CampaignPage/Blocks/index.tsx new file mode 100644 index 000000000..ca1147b66 --- /dev/null +++ b/apps/scandic-web/components/ContentType/CampaignPage/Blocks/index.tsx @@ -0,0 +1,17 @@ +import Essentials from "@/components/Blocks/Essentials" + +import type { BlocksProps } from "@/types/components/blocks" +import { BlocksEnums } from "@/types/enums/blocks" + +export default function Blocks({ blocks }: BlocksProps) { + return blocks.map(async (block) => { + switch (block.typename) { + case BlocksEnums.block.Essentials: + return ( + + ) + default: + return null + } + }) +} diff --git a/apps/scandic-web/components/ContentType/CampaignPage/index.tsx b/apps/scandic-web/components/ContentType/CampaignPage/index.tsx index 91705cc13..4bbed5f9e 100644 --- a/apps/scandic-web/components/ContentType/CampaignPage/index.tsx +++ b/apps/scandic-web/components/ContentType/CampaignPage/index.tsx @@ -6,6 +6,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography" import { getCampaignPage } from "@/lib/trpc/memoizedRequests" +import Blocks from "./Blocks" import CampaignPageSkeleton from "./CampaignPageSkeleton" import styles from "./campaignPage.module.css" @@ -18,7 +19,7 @@ export default async function CampaignPage() { } const { campaignPage } = pageData - const { heading, subheading, preamble } = campaignPage + const { heading, subheading, preamble, blocks } = campaignPage return ( <> @@ -52,6 +53,7 @@ export default async function CampaignPage() { + {blocks ? : null} {/* */} diff --git a/apps/scandic-web/components/TabFilters/FilterIcon/index.tsx b/apps/scandic-web/components/Icons/IconByCSSelect.tsx similarity index 73% rename from apps/scandic-web/components/TabFilters/FilterIcon/index.tsx rename to apps/scandic-web/components/Icons/IconByCSSelect.tsx index 97575bb74..1d2107e09 100644 --- a/apps/scandic-web/components/TabFilters/FilterIcon/index.tsx +++ b/apps/scandic-web/components/Icons/IconByCSSelect.tsx @@ -6,19 +6,26 @@ import { } from "@scandic-hotels/design-system/Icons/MaterialIcon" import PalmTreeIcon from "@scandic-hotels/design-system/Icons/PalmTreeIcon" -interface FilterIconProps { +import type { IconProps } from "@scandic-hotels/design-system/Icons" + +interface IconByCSSelectProps extends IconProps { identifier: string } -export default function FilterIcon({ identifier }: FilterIconProps) { +export default function IconByCSSelect({ + identifier, + color = "CurrentColor", + size = 24, + ...props +}: IconByCSSelectProps) { switch (identifier) { // These are custom icons case "discount-2-2": - return + return case "bouquet": - return + return case "palm_tree": - return + return // These are all Material Icons case "electric_car": @@ -72,10 +79,14 @@ export default function FilterIcon({ identifier }: FilterIconProps) { return ( ) default: - return + return ( + + ) } } diff --git a/apps/scandic-web/components/TabFilters/index.tsx b/apps/scandic-web/components/TabFilters/index.tsx index 8e70f74d1..f79d8d5fc 100644 --- a/apps/scandic-web/components/TabFilters/index.tsx +++ b/apps/scandic-web/components/TabFilters/index.tsx @@ -6,7 +6,7 @@ import { Typography } from "@scandic-hotels/design-system/Typography" import useScrollShadows from "@/hooks/useScrollShadows" -import FilterIcon from "./FilterIcon" +import IconByCSSelect from "../Icons/IconByCSSelect" import styles from "./tabFilters.module.css" @@ -51,7 +51,7 @@ export default function TabFilters({ })} type="button" > - + {category.label} diff --git a/apps/scandic-web/env/server.ts b/apps/scandic-web/env/server.ts index 63f24c264..d44bd6a8c 100644 --- a/apps/scandic-web/env/server.ts +++ b/apps/scandic-web/env/server.ts @@ -203,6 +203,11 @@ const _env = createEnv({ return val.split(",") }) .default(""), + CAMPAIGN_PAGES_ENABLED: z + .string() + .refine((s) => s === "1" || s === "0") + .transform((s) => s === "1") + .default("0"), }, emptyStringAsUndefined: true, runtimeEnv: { @@ -296,6 +301,7 @@ const _env = createEnv({ ENABLE_WARMUP_HOTEL: process.env.ENABLE_WARMUP_HOTEL, WARMUP_TOKEN: process.env.WARMUP_TOKEN, NEW_SITE_LIVE_FOR_LANGS: process.env.NEXT_PUBLIC_NEW_SITE_LIVE_FOR_LANGS, + CAMPAIGN_PAGES_ENABLED: process.env.CAMPAIGN_PAGES_ENABLED, }, }) diff --git a/apps/scandic-web/lib/graphql/Fragments/Blocks/Essentials.graphql b/apps/scandic-web/lib/graphql/Fragments/Blocks/Essentials.graphql new file mode 100644 index 000000000..9803aa4f7 --- /dev/null +++ b/apps/scandic-web/lib/graphql/Fragments/Blocks/Essentials.graphql @@ -0,0 +1,11 @@ +fragment Essentials_CampaignPage on CampaignPageBlocksEssentials { + essentials { + title + preamble + items { + label + icon_identifier + description + } + } +} diff --git a/apps/scandic-web/lib/graphql/Query/CampaignPage/CampaignPage.graphql b/apps/scandic-web/lib/graphql/Query/CampaignPage/CampaignPage.graphql index 0c1583063..09e39ed5d 100644 --- a/apps/scandic-web/lib/graphql/Query/CampaignPage/CampaignPage.graphql +++ b/apps/scandic-web/lib/graphql/Query/CampaignPage/CampaignPage.graphql @@ -1,4 +1,5 @@ #import "../../Fragments/System.graphql" +#import "../../Fragments/Blocks/Essentials.graphql" query GetCampaignPage($locale: String!, $uid: String!) { campaign_page(uid: $uid, locale: $locale) { @@ -11,6 +12,10 @@ query GetCampaignPage($locale: String!, $uid: String!) { first_column second_column } + blocks { + __typename + ...Essentials_CampaignPage + } system { ...System created_at diff --git a/apps/scandic-web/server/routers/contentstack/campaignPage/output.ts b/apps/scandic-web/server/routers/contentstack/campaignPage/output.ts index 1c64bb671..47723bbb1 100644 --- a/apps/scandic-web/server/routers/contentstack/campaignPage/output.ts +++ b/apps/scandic-web/server/routers/contentstack/campaignPage/output.ts @@ -1,7 +1,22 @@ import { z } from "zod" +import { discriminatedUnionArray } from "@/lib/discriminatedUnion" + +import { essentialsBlockSchema } from "../schemas/blocks/essentials" import { systemSchema } from "../schemas/system" +import { CampaignPageEnum } from "@/types/enums/campaignPage" + +const campaignPageEssentials = z + .object({ + __typename: z.literal(CampaignPageEnum.ContentStack.blocks.Essentials), + }) + .merge(essentialsBlockSchema) + +export const blocksSchema = z.discriminatedUnion("__typename", [ + campaignPageEssentials, +]) + export const campaignPageSchema = z.object({ campaign_page: z.object({ title: z.string(), @@ -13,6 +28,7 @@ export const campaignPageSchema = z.object({ first_column: z.string(), second_column: z.string(), }), + blocks: discriminatedUnionArray(blocksSchema.options), system: systemSchema.merge( z.object({ created_at: z.string(), diff --git a/apps/scandic-web/server/routers/contentstack/schemas/blocks/essentials.ts b/apps/scandic-web/server/routers/contentstack/schemas/blocks/essentials.ts new file mode 100644 index 000000000..35733fc2a --- /dev/null +++ b/apps/scandic-web/server/routers/contentstack/schemas/blocks/essentials.ts @@ -0,0 +1,26 @@ +import { z } from "zod" + +import { BlocksEnums } from "@/types/enums/blocks" + +export const essentialsSchema = z.object({ + essentials: z.object({ + title: z.string(), + preamble: z.string().nullish(), + items: z.array( + z.object({ + label: z.string(), + icon_identifier: z.string(), + description: z.string().nullish(), + }) + ), + }), +}) + +export const essentialsBlockSchema = z + .object({ + typename: z + .literal(BlocksEnums.block.Essentials) + .optional() + .default(BlocksEnums.block.Essentials), + }) + .merge(essentialsSchema) diff --git a/apps/scandic-web/types/components/blocks/index.ts b/apps/scandic-web/types/components/blocks/index.ts index 1c1e4ad17..0360c31b8 100644 --- a/apps/scandic-web/types/components/blocks/index.ts +++ b/apps/scandic-web/types/components/blocks/index.ts @@ -1,4 +1,5 @@ import type { Block as AccountPageBlock } from "@/types/trpc/routers/contentstack/accountPage" +import type { Block as CampaignPageBlock } from "@/types/trpc/routers/contentstack/campaignPage" import type { Block as CollectionPageBlock } from "@/types/trpc/routers/contentstack/collectionPage" import type { Block as ContentPageBlock } from "@/types/trpc/routers/contentstack/contentPage" import type { Block as DestinationCityPageBlock } from "@/types/trpc/routers/contentstack/destinationCityPage" @@ -9,6 +10,7 @@ import type { Block as StartPageBlock } from "@/types/trpc/routers/contentstack/ export type Blocks = | AccountPageBlock + | CampaignPageBlock | CollectionPageBlock | ContentPageBlock | DestinationCityPageBlock diff --git a/apps/scandic-web/types/enums/blocks.ts b/apps/scandic-web/types/enums/blocks.ts index 05f25d680..69d8f60cb 100644 --- a/apps/scandic-web/types/enums/blocks.ts +++ b/apps/scandic-web/types/enums/blocks.ts @@ -14,5 +14,6 @@ export namespace BlocksEnums { TextCols = "TextCols", TextContent = "TextContent", UspGrid = "UspGrid", + Essentials = "Essentials", } } diff --git a/apps/scandic-web/types/enums/campaignPage.ts b/apps/scandic-web/types/enums/campaignPage.ts new file mode 100644 index 000000000..062d56888 --- /dev/null +++ b/apps/scandic-web/types/enums/campaignPage.ts @@ -0,0 +1,7 @@ +export namespace CampaignPageEnum { + export namespace ContentStack { + export const enum blocks { + Essentials = "CampaignPageBlocksEssentials", + } + } +} diff --git a/apps/scandic-web/types/trpc/routers/contentstack/campaignPage.ts b/apps/scandic-web/types/trpc/routers/contentstack/campaignPage.ts index 901863d64..f747eb8eb 100644 --- a/apps/scandic-web/types/trpc/routers/contentstack/campaignPage.ts +++ b/apps/scandic-web/types/trpc/routers/contentstack/campaignPage.ts @@ -1,9 +1,11 @@ import type { z } from "zod" import type { + blocksSchema, campaignPageRefsSchema, campaignPageSchema, } from "@/server/routers/contentstack/campaignPage/output" +import type { essentialsSchema } from "@/server/routers/contentstack/schemas/blocks/essentials" export interface GetCampaignPageData extends z.input {} @@ -15,3 +17,7 @@ export interface GetCampaignPageRefsData export interface CampaignPageRefs extends z.output {} + +export type Block = z.output + +export type EssentialsBlock = z.output["essentials"]