Merged in feat/LOY-361-add-promo-campaign-page-type (pull request #2826)

Feat/LOY-361 add promo campaign page type

* feat(LOY-361): add Pomo Campaign page type

* chore(SW-361): remove campaign page flag

* fix(LOY-361): cleanup

* fix(LOY-361): add promo code


Approved-by: Erik Tiekstra
Approved-by: Chuma Mcphoy (We Ahead)
This commit is contained in:
Matilda Landström
2025-09-19 07:20:17 +00:00
parent c791fef2c6
commit 0e30a2d218
34 changed files with 613 additions and 19 deletions

View File

@@ -71,4 +71,4 @@ DTMC_ENTRA_ID_CLIENT=""
DTMC_ENTRA_ID_ISSUER=""
DTMC_ENTRA_ID_SECRET=""
CAMPAIGN_PAGES_ENABLED="0" # 0 - disabled, 1 - enabled
PROMO_CAMPAIGN_PAGES_ENABLED="0" # 0 - disabled, 1 - enabled

View File

@@ -0,0 +1,19 @@
import { Suspense } from "react"
import Breadcrumbs from "@/components/Breadcrumbs"
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
import type { BreadcrumbsProps } from "@/components/TempDesignSystem/Breadcrumbs/breadcrumbs"
export default function PromoCampaignPageBreadcrumbs() {
const variants: Pick<BreadcrumbsProps, "color" | "size"> = {
color: "Background/Primary",
size: "contentWidth",
}
return (
<Suspense fallback={<BreadcrumbsSkeleton {...variants} />}>
<Breadcrumbs {...variants} />
</Suspense>
)
}

View File

@@ -1,18 +1,11 @@
import { notFound } from "next/navigation"
import { Suspense } from "react"
import { env } from "@/env/server"
import CampaignOverviewPage from "@/components/ContentType/CampaignOverviewPage"
import CampaignOverviewPageSkeleton from "@/components/ContentType/CampaignOverviewPage/CampaignOverviewPageSkeleton"
import styles from "./page.module.css"
export default async function CampaignOverviewPagePage() {
if (!env.CAMPAIGN_PAGES_ENABLED) {
notFound()
}
return (
<div className={styles.page}>
<Suspense fallback={<CampaignOverviewPageSkeleton />}>

View File

@@ -1,8 +1,5 @@
import { notFound } from "next/navigation"
import { Suspense } from "react"
import { env } from "@/env/server"
import CampaignPage from "@/components/ContentType/CampaignPage"
import CampaignPageSkeleton from "@/components/ContentType/CampaignPage/CampaignPageSkeleton"
@@ -11,10 +8,6 @@ import styles from "./page.module.css"
export { generateMetadata } from "@/utils/metadata/generateMetadata"
export default async function CampaignPagePage() {
if (!env.CAMPAIGN_PAGES_ENABLED) {
return notFound()
}
return (
<div className={styles.page}>
<Suspense fallback={<CampaignPageSkeleton />}>

View File

@@ -0,0 +1,3 @@
.page {
background-color: var(--Background-Primary);
}

View File

@@ -0,0 +1,24 @@
import { notFound } from "next/navigation"
import { Suspense } from "react"
import { env } from "@/env/server"
import PromoCampaignPage from "@/components/ContentType/PromoCampaignPage"
import PromoCampaignPageSkeleton from "@/components/ContentType/PromoCampaignPage/PromoCampaignPageSkeleton"
import styles from "./page.module.css"
export { generateMetadata } from "@/utils/metadata/generateMetadata"
export default async function PromoCampaignPagePage() {
if (!env.PROMO_CAMPAIGN_PAGES_ENABLED) {
return notFound()
}
return (
<div className={styles.page}>
<Suspense fallback={<PromoCampaignPageSkeleton />}>
<PromoCampaignPage />
</Suspense>
</div>
)
}

View File

@@ -0,0 +1,31 @@
"use client"
import SkeletonShimmer from "@scandic-hotels/design-system/SkeletonShimmer"
import { Typography } from "@scandic-hotels/design-system/Typography"
import styles from "./promoCampaignPage.module.css"
export default function PromoCampaignPageSkeleton() {
return (
<div className={styles.pageContainer}>
<SkeletonShimmer width="100%" height="478px" />
<div className={styles.intro}>
<div className={styles.headingWrapper}>
<Typography variant="Title/lg">
<SkeletonShimmer width="20ch" />
</Typography>
<Typography variant="Title/Subtitle/lg">
<SkeletonShimmer width="50ch" />
</Typography>
</div>
</div>
<div>
<Typography variant="Title/smLowCase">
<SkeletonShimmer width="30ch" />
</Typography>
<SkeletonShimmer width="100%" height="110px" />
</div>
</div>
)
}

View File

@@ -0,0 +1,43 @@
import { notFound } from "next/navigation"
import { Suspense } from "react"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
import { getPromoCampaignPage } from "@/lib/trpc/memoizedRequests"
import PromoCampaignPageSkeleton from "./PromoCampaignPageSkeleton"
import styles from "./promoCampaignPage.module.css"
export default async function PromoCampaignPage() {
const pageData = await getPromoCampaignPage()
if (!pageData) {
notFound()
}
//const isUserLoggedIn = await isLoggedInUser()
const { promo_campaign_page, tracking } = pageData
const { heading, subheading } = promo_campaign_page
return (
<>
<Suspense fallback={<PromoCampaignPageSkeleton />}>
<div className={styles.pageContainer}>
<div className={styles.intro}>
<div className={styles.headingWrapper}>
<Typography variant="Title/lg">
<h1 className={styles.heading}>{heading}</h1>
</Typography>
{subheading ? (
<Typography variant="Title/Subtitle/lg">
<p>{subheading}</p>
</Typography>
) : null}
</div>
</div>
</div>
</Suspense>
<TrackingSDK pageData={tracking} />
</>
)
}

View File

@@ -0,0 +1,29 @@
.pageContainer {
display: grid;
gap: var(--Space-x6);
padding-bottom: var(--Space-x7);
width: var(--max-width-content);
margin: 0 auto;
}
.intro {
display: grid;
gap: var(--Space-x5);
}
.headingWrapper {
display: grid;
gap: var(--Space-x2);
}
.heading {
color: var(--Text-Heading);
text-wrap: balance;
hyphens: auto;
}
@media screen and (min-width: 1367px) {
.pageContainer {
gap: var(--Space-x9);
}
}

View File

@@ -149,7 +149,7 @@ export const env = createEnv({
* We currently have the secret in local and test environments.
*/
DTMC_ENTRA_ID_SECRET: z.string().optional(),
CAMPAIGN_PAGES_ENABLED: z
PROMO_CAMPAIGN_PAGES_ENABLED: z
.string()
.refine((s) => s === "1" || s === "0")
.transform((s) => s === "1")
@@ -247,7 +247,7 @@ export const env = createEnv({
DTMC_ENTRA_ID_CLIENT: process.env.DTMC_ENTRA_ID_CLIENT,
DTMC_ENTRA_ID_ISSUER: process.env.DTMC_ENTRA_ID_ISSUER,
DTMC_ENTRA_ID_SECRET: process.env.DTMC_ENTRA_ID_SECRET,
CAMPAIGN_PAGES_ENABLED: process.env.CAMPAIGN_PAGES_ENABLED,
PROMO_CAMPAIGN_PAGES_ENABLED: process.env.PROMO_CAMPAIGN_PAGES_ENABLED,
WEBVIEW_SHOW_OVERVIEW: process.env.WEBVIEW_SHOW_OVERVIEW,
ENABLE_NEW_OVERVIEW_SECTION: process.env.ENABLE_NEW_OVERVIEW_SECTION,
CHATBOT_LIVE_LANGS: process.env.CHATBOT_LIVE_LANGS,

View File

@@ -232,3 +232,10 @@ export const getCampaignOverviewPage = cache(
return caller.contentstack.campaignOverviewPage.get()
}
)
export const getPromoCampaignPage = cache(
async function getMemoizedPromoCampaignPage() {
const caller = await serverClient()
return caller.contentstack.promoCampaignPage.get()
}
)

View File

@@ -11,6 +11,7 @@ export enum TrackingChannelEnum {
"campaign" = "campaign",
"hotels" = "hotels",
"homepage" = "homepage",
"promo-campaign" = "promo-campaign",
}
export type TrackingChannel = keyof typeof TrackingChannelEnum

View File

@@ -11,4 +11,5 @@ export enum PageContentTypeEnum {
hotelPage = "hotel_page",
loyaltyPage = "loyalty_page",
startPage = "start_page",
promoCampaignPage = "promo_campaign_page",
}

View File

@@ -0,0 +1,24 @@
#import "../System.graphql"
fragment PromoCampaignPageBreadcrumb on PromoCampaignPage {
web {
breadcrumbs {
title
}
}
system {
...System
}
url
}
fragment PromoCampaignPageBreadcrumbRef on PromoCampaignPage {
web {
breadcrumbs {
title
}
}
system {
...System
}
}

View File

@@ -0,0 +1,9 @@
#import "../System.graphql"
fragment PromoCampaignPageLink on PromoCampaignPage {
title
url
system {
...System
}
}

View File

@@ -0,0 +1,7 @@
#import "../System.graphql"
fragment PromoCampaignPageRef on PromoCampaignPage {
system {
...System
}
}

View File

@@ -0,0 +1,29 @@
#import "../../Fragments/Breadcrumbs/Breadcrumbs.graphql"
#import "../../Fragments/System.graphql"
query GetPromoCampaignPageBreadcrumbs($locale: String!, $uid: String!) {
promo_campaign_page(locale: $locale, uid: $uid) {
url
web {
breadcrumbs {
...Breadcrumbs
}
}
system {
...System
}
}
}
query GetPromoCampaignPageBreadcrumbsRefs($locale: String!, $uid: String!) {
promo_campaign_page(locale: $locale, uid: $uid) {
web {
breadcrumbs {
...BreadcrumbsRefs
}
}
system {
...System
}
}
}

View File

@@ -95,3 +95,11 @@ query GetStartPageSettings($uid: String!, $locale: String!) {
}
}
}
query GetPromoCampaignPageSettings($uid: String!, $locale: String!) {
page: promo_campaign_page(uid: $uid, locale: $locale) {
settings: page_settings {
...PageSettings
}
}
}

View File

@@ -0,0 +1,19 @@
#import "../../Fragments/Metadata.graphql"
#import "../../Fragments/System.graphql"
query GetPromoCampaignPageMetadata($locale: String!, $uid: String!) {
promo_campaign_page(locale: $locale, uid: $uid) {
web {
breadcrumbs {
title
}
seo_metadata {
...Metadata
}
}
heading
system {
...System
}
}
}

View File

@@ -0,0 +1,64 @@
#import "../../Fragments/System.graphql"
#import "../../Fragments/CampaignPage/IncludedHotels.graphql"
#import "../../Fragments/CampaignPage/Hero.graphql"
#import "../../Fragments/Blocks/Accordion.graphql"
#import "../../Fragments/Blocks/Essentials.graphql"
#import "../../Fragments/Blocks/CarouselCards.graphql"
#import "../../Fragments/Blocks/HotelListing.graphql"
query GetPromoCampaignPage($locale: String!, $uid: String!) {
promo_campaign_page(uid: $uid, locale: $locale) {
title
heading
subheading
page_settings {
booking_code
}
campaign_type
promo_code
startdate
enddate
system {
...System
created_at
updated_at
}
}
trackingProps: promo_campaign_page(locale: "en", uid: $uid) {
url
}
}
query GetPromoCampaignPageRefs($locale: String!, $uid: String!) {
promo_campaign_page(locale: $locale, uid: $uid) {
system {
...System
}
}
}
query GetDaDeEnUrlsPromoCampaignPage($uid: String!) {
de: promo_campaign_page(locale: "de", uid: $uid) {
url
}
en: promo_campaign_page(locale: "en", uid: $uid) {
url
}
da: promo_campaign_page(locale: "da", uid: $uid) {
url
}
}
query GetFiNoSvUrlsPromoCampaignPage($uid: String!) {
fi: promo_campaign_page(locale: "fi", uid: $uid) {
url
}
no: promo_campaign_page(locale: "no", uid: $uid) {
url
}
sv: promo_campaign_page(locale: "sv", uid: $uid) {
url
}
}

View File

@@ -101,3 +101,14 @@ query EntryByUrlBatch2($locale: String!, $url: String!) {
total
}
}
query EntryByUrlBatch3($locale: String!, $url: String!) {
all_promo_campaign_page(where: { url: $url }, locale: $locale) {
items {
system {
...System
}
}
total
}
}

View File

@@ -45,6 +45,10 @@ import {
GetLoyaltyPageBreadcrumbs,
GetLoyaltyPageBreadcrumbsRefs,
} from "../../../graphql/Query/Breadcrumbs/LoyaltyPage.graphql"
import {
GetPromoCampaignPageBreadcrumbs,
GetPromoCampaignPageBreadcrumbsRefs,
} from "../../../graphql/Query/Breadcrumbs/PromoCampaignPage.graphql"
import { request } from "../../../graphql/request"
import { contentstackExtendedProcedureUID } from "../../../procedures"
import { generateRefsResponseTag } from "../../../utils/generateTag"
@@ -259,6 +263,17 @@ export const breadcrumbsQueryRouter = router({
},
variables
)
case PageContentTypeEnum.promoCampaignPage:
return await getBreadcrumbs<{
promo_campaign_page: RawBreadcrumbsSchema
}>(
{
dataKey: "promo_campaign_page",
refQuery: GetPromoCampaignPageBreadcrumbsRefs,
query: GetPromoCampaignPageBreadcrumbs,
},
variables
)
default:
return []
}

View File

@@ -16,6 +16,7 @@ import { loyaltyPageRouter } from "./loyaltyPage"
import { metadataRouter } from "./metadata"
import { pageSettingsRouter } from "./pageSettings"
import { partnerRouter } from "./partner"
import { promoCampaignPageRouter } from "./promoCampaignPage"
import { rewardRouter } from "./reward"
import { startPageRouter } from "./startPage"
@@ -39,4 +40,5 @@ export const contentstackRouter = router({
loyaltyLevels: loyaltyLevelRouter,
startPage: startPageRouter,
partner: partnerRouter,
promoCampaignPage: promoCampaignPageRouter,
})

View File

@@ -46,6 +46,10 @@ import {
GetDaDeEnUrlsLoyaltyPage,
GetFiNoSvUrlsLoyaltyPage,
} from "../../../graphql/Query/LoyaltyPage/LoyaltyPage.graphql"
import {
GetDaDeEnUrlsPromoCampaignPage,
GetFiNoSvUrlsPromoCampaignPage,
} from "../../../graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql"
import {
GetDaDeEnUrlsStartPage,
GetFiNoSvUrlsStartPage,
@@ -139,6 +143,10 @@ export async function getUrlsOfAllLanguages(
daDeEnDocument = GetDaDeEnUrlsStartPage
fiNoSvDocument = GetFiNoSvUrlsStartPage
break
case PageContentTypeEnum.promoCampaignPage:
daDeEnDocument = GetDaDeEnUrlsPromoCampaignPage
fiNoSvDocument = GetFiNoSvUrlsPromoCampaignPage
break
default:
languageSwitcherLogger.error(
`Trying to get a content type that is not supported, ${contentType}`

View File

@@ -15,6 +15,7 @@ import { GetDestinationCountryPageMetadata } from "../../../graphql/Query/Destin
import { GetDestinationOverviewPageMetadata } from "../../../graphql/Query/DestinationOverviewPage/Metadata.graphql"
import { GetHotelPageMetadata } from "../../../graphql/Query/HotelPage/Metadata.graphql"
import { GetLoyaltyPageMetadata } from "../../../graphql/Query/LoyaltyPage/Metadata.graphql"
import { GetPromoCampaignPageMetadata } from "../../../graphql/Query/PromoCampaignPage/Metadata.graphql"
import { GetStartPageMetadata } from "../../../graphql/Query/StartPage/Metadata.graphql"
import { request } from "../../../graphql/request"
import { contentStackUidWithServiceProcedure } from "../../../procedures"
@@ -202,6 +203,12 @@ export const metadataQueryRouter = router({
}>(GetStartPageMetadata, variables)
data = startPageResponse.start_page
break
case PageContentTypeEnum.promoCampaignPage:
const promoCampaignPageResponse = await fetchMetadata<{
promo_campaign_page: RawMetadataSchema
}>(GetPromoCampaignPageMetadata, variables)
data = promoCampaignPageResponse.promo_campaign_page
break
default:
break
}

View File

@@ -14,6 +14,7 @@ import {
GetDestinationOverviewPageSettings,
GetHotelPageSettings,
GetLoyaltyPageSettings,
GetPromoCampaignPageSettings,
GetStartPageSettings,
} from "../../../graphql/Query/PageSettings.graphql"
import { request } from "../../../graphql/request"
@@ -87,4 +88,5 @@ const graphqlQueriesForContentType: Record<PageContentTypeEnum, any> = {
[PageContentTypeEnum.hotelPage]: GetHotelPageSettings,
[PageContentTypeEnum.loyaltyPage]: GetLoyaltyPageSettings,
[PageContentTypeEnum.startPage]: GetStartPageSettings,
[PageContentTypeEnum.promoCampaignPage]: GetPromoCampaignPageSettings,
}

View File

@@ -0,0 +1,6 @@
import { mergeRouters } from "../../.."
import { promoCampaignPageQueryRouter } from "./query"
export const promoCampaignPageRouter = mergeRouters(
promoCampaignPageQueryRouter
)

View File

@@ -0,0 +1,56 @@
import { z } from "zod"
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
import { systemSchema } from "../schemas/system"
export const CAMPAIGN_TYPES = {
TIER: "TIER",
POINT: "POINT",
} as const
export const promoCampaignPageSchema = z
.object({
promo_campaign_page: z.object({
title: z.string(),
heading: z.string(),
subheading: z.string().nullish(),
page_settings: z
.object({
booking_code: z.string().nullish(),
})
.nullish(),
campaign_type: z.nativeEnum(CAMPAIGN_TYPES),
promo_code: z.string(),
startdate: nullableStringValidator,
enddate: nullableStringValidator,
system: systemSchema.merge(
z.object({
created_at: z.string(),
updated_at: z.string(),
})
),
}),
trackingProps: z.object({
url: z.string(),
}),
})
.transform(({ promo_campaign_page, ...data }) => {
const { page_settings, ...promoCampaignPageData } = promo_campaign_page
const bookingCode = page_settings?.booking_code || null
return {
...data,
promo_campaign_page: {
bookingCode,
...promoCampaignPageData,
},
}
})
/** REFS */
export const promoCampaignPageRefsSchema = z.object({
promo_campaign_page: z.object({
system: systemSchema,
}),
})

View File

@@ -0,0 +1,122 @@
import { createCounter } from "@scandic-hotels/common/telemetry"
import { router } from "../../.."
import { notFound } from "../../../errors"
import {
GetPromoCampaignPage,
GetPromoCampaignPageRefs,
} from "../../../graphql/Query/PromoCampaignPage/PromoCampaignPage.graphql"
import { request } from "../../../graphql/request"
import { contentStackUidWithServiceProcedure } from "../../../procedures"
import { generateRefsResponseTag } from "../../../utils/generateTag"
import { promoCampaignPageRefsSchema, promoCampaignPageSchema } from "./output"
import { generatePageTags } from "./utils"
import type {
GetPromoCampaignPageData,
GetPromoCampaignPageRefsData,
} from "../../../types/promoCampaignPage"
import type { TrackingPageData } from "../../types"
export const promoCampaignPageQueryRouter = router({
get: contentStackUidWithServiceProcedure.query(async ({ ctx }) => {
const { lang, uid } = ctx
const getPromoCampaignPageRefsCounter = createCounter(
"trpc.contentstack",
"promoCampaignPage.get.refs"
)
const metricsGetPromoCampaignPageRefs =
getPromoCampaignPageRefsCounter.init({
lang,
uid,
})
metricsGetPromoCampaignPageRefs.start()
const refsResponse = await request<GetPromoCampaignPageRefsData>(
GetPromoCampaignPageRefs,
{ locale: lang, uid },
{
key: generateRefsResponseTag(lang, uid),
ttl: "max",
}
)
if (!refsResponse.data) {
const notFoundError = notFound(refsResponse)
metricsGetPromoCampaignPageRefs.noDataError()
throw notFoundError
}
const validatedRefsData = promoCampaignPageRefsSchema.safeParse(
refsResponse.data
)
if (!validatedRefsData.success) {
metricsGetPromoCampaignPageRefs.validationError(validatedRefsData.error)
return null
}
metricsGetPromoCampaignPageRefs.success()
const tags = generatePageTags(validatedRefsData.data, lang)
const getPromoCampaignPageCounter = createCounter(
"trpc.contentstack",
"promoCampaignPage.get"
)
const metricsGetPromoCampaignPage = getPromoCampaignPageCounter.init({
lang,
uid,
})
metricsGetPromoCampaignPage.start()
const response = await request<GetPromoCampaignPageData>(
GetPromoCampaignPage,
{
locale: lang,
uid,
},
{
key: tags,
ttl: "max",
}
)
if (!response.data) {
const notFoundError = notFound(response)
metricsGetPromoCampaignPage.noDataError()
throw notFoundError
}
const validatedResponse = promoCampaignPageSchema.safeParse(response.data)
if (!validatedResponse.success) {
metricsGetPromoCampaignPage.validationError(validatedResponse.error)
return null
}
const { promo_campaign_page, trackingProps } = validatedResponse.data
metricsGetPromoCampaignPage.success()
const system = promo_campaign_page.system
const pageName = trackingProps.url
const tracking: TrackingPageData = {
pageId: system.uid,
domainLanguage: system.locale,
publishDate: system.updated_at,
createDate: system.created_at,
channel: "promo-campaign",
pageType: "promocampaignpage",
pageName,
siteSections: pageName,
siteVersion: "new-web",
}
return {
promo_campaign_page,
tracking,
}
}),
})

View File

@@ -0,0 +1,23 @@
import { generateTag, generateTagsFromSystem } from "../../../utils/generateTag"
import type { Lang } from "@scandic-hotels/common/constants/language"
import type { PromoCampaignPageRefs } from "../../../types/promoCampaignPage"
import type { System } from "../schemas/system"
export function generatePageTags(
validatedData: PromoCampaignPageRefs,
lang: Lang
): string[] {
const connections = getConnections(validatedData)
return [
generateTagsFromSystem(lang, connections),
generateTag(lang, validatedData.promo_campaign_page.system.uid),
].flat()
}
export function getConnections({ promo_campaign_page }: PromoCampaignPageRefs) {
const connections: System["system"][] = [promo_campaign_page.system]
return connections
}

View File

@@ -26,6 +26,7 @@ type TrackingSDKChannel =
| "campaign"
| "hotels"
| "homepage"
| "promo-campaign"
export type TrackingUserData =
| {

View File

@@ -25,4 +25,5 @@ export const validateEntryResolveSchema = z.object({
all_destination_country_page: entryResolveSchema,
all_destination_city_page: entryResolveSchema,
all_start_page: entryResolveSchema,
all_promo_campaign_page: entryResolveSchema,
})

View File

@@ -0,0 +1,29 @@
import type { z } from "zod"
import type {
promoCampaignPageRefsSchema,
promoCampaignPageSchema,
} from "../routers/contentstack/promoCampaignPage/output"
export namespace PromoCampaignPageEnum {
export namespace ContentStack {
export const enum blocks {
Accordion = "PromoCampaignPageBlocksAccordion",
}
}
}
export interface GetPromoCampaignPageData
extends z.input<typeof promoCampaignPageSchema> {}
export interface PromoCampaignPage
extends z.output<typeof promoCampaignPageSchema> {}
export type PromoCampaignPageData = PromoCampaignPage["promo_campaign_page"]
/* REFS */
export interface GetPromoCampaignPageRefsData
extends z.input<typeof promoCampaignPageRefsSchema> {}
export interface PromoCampaignPageRefs
extends z.output<typeof promoCampaignPageRefsSchema> {}

View File

@@ -4,6 +4,7 @@ import { batchRequest } from "../graphql/batchRequest"
import {
EntryByUrlBatch1,
EntryByUrlBatch2,
EntryByUrlBatch3,
} from "../graphql/Query/ResolveEntry.graphql"
import { validateEntryResolveSchema } from "../types/entry"
@@ -34,10 +35,16 @@ export async function resolve(url: string, lang = Lang.en) {
key: cacheKey,
},
},
{
document: EntryByUrlBatch3,
variables,
cacheOptions: {
ttl: "max",
key: cacheKey,
},
},
])
const validatedData = validateEntryResolveSchema.safeParse(response.data)
if (!validatedData.success) {
return {
error: validatedData.error,