Merged in feature/hardcoded-mypages-links (pull request #1325)
Feature/hardcoded mypages links * feat: wip use hardcoded links * Merge branch 'master' of bitbucket.org:scandic-swap/web into feature/hardcoded-mypages-links * feat: use hardcoded links for my pages to support dynamic links * cleanup * code fixes * refactor: restructure MyPagesMobileDropdown component for improved readability * use util timeout function Approved-by: Christian Andolf Approved-by: Linus Flood
This commit is contained in:
@@ -14,7 +14,6 @@ import { languageSwitcherRouter } from "./languageSwitcher"
|
||||
import { loyaltyLevelRouter } from "./loyaltyLevel"
|
||||
import { loyaltyPageRouter } from "./loyaltyPage"
|
||||
import { metadataRouter } from "./metadata"
|
||||
import { myPagesRouter } from "./myPages"
|
||||
import { partnerRouter } from "./partner"
|
||||
import { rewardRouter } from "./reward"
|
||||
import { startPageRouter } from "./startPage"
|
||||
@@ -32,7 +31,6 @@ export const contentstackRouter = router({
|
||||
destinationOverviewPage: destinationOverviewPageRouter,
|
||||
destinationCountryPage: destinationCountryPageRouter,
|
||||
destinationCityPage: destinationCityPageRouter,
|
||||
myPages: myPagesRouter,
|
||||
metadata: metadataRouter,
|
||||
rewards: rewardRouter,
|
||||
loyaltyLevels: loyaltyLevelRouter,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { router } from "@/server/trpc"
|
||||
|
||||
import { navigationRouter } from "./navigation"
|
||||
|
||||
export const myPagesRouter = router({
|
||||
navigation: navigationRouter,
|
||||
})
|
||||
@@ -1,5 +0,0 @@
|
||||
import { mergeRouters } from "@/server/trpc"
|
||||
|
||||
import { navigationQueryRouter } from "./query"
|
||||
|
||||
export const navigationRouter = mergeRouters(navigationQueryRouter)
|
||||
@@ -1,90 +0,0 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
|
||||
import { linkRefsUnionSchema, linkUnionSchema } from "../../schemas/pageLinks"
|
||||
import { systemSchema } from "../../schemas/system"
|
||||
|
||||
const pageConnection = z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: linkUnionSchema,
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
const pageConnectionRefs = z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: linkRefsUnionSchema,
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const navigationRefsPayloadSchema = z.object({
|
||||
all_navigation_my_pages: z.object({
|
||||
items: z.array(
|
||||
z.object({
|
||||
menu_items: z.array(
|
||||
z.object({
|
||||
links: z.array(
|
||||
z.object({
|
||||
page: pageConnectionRefs,
|
||||
})
|
||||
),
|
||||
})
|
||||
),
|
||||
system: systemSchema,
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
export type GetNavigationMyPagesRefsData = z.infer<
|
||||
typeof navigationRefsPayloadSchema
|
||||
>
|
||||
|
||||
const menuItems = z.array(
|
||||
z.object({
|
||||
display_sign_out_link: z.boolean(),
|
||||
links: z.array(
|
||||
z.object({
|
||||
link_text: z.string().default(""),
|
||||
page: pageConnection,
|
||||
})
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
export type MenuItems = z.infer<typeof menuItems>
|
||||
|
||||
export const navigationPayloadSchema = z.object({
|
||||
all_navigation_my_pages: z.object({
|
||||
items: z.array(
|
||||
z.object({
|
||||
menu_items: menuItems,
|
||||
title: z.string(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
export type GetNavigationMyPagesData = z.infer<typeof navigationPayloadSchema>
|
||||
|
||||
const link = z.object({
|
||||
lang: z.nativeEnum(Lang),
|
||||
linkText: z.string(),
|
||||
uid: z.string(),
|
||||
url: z.string(),
|
||||
originalUrl: z.string().optional(),
|
||||
})
|
||||
|
||||
export const getNavigationSchema = z.object({
|
||||
menuItems: z.array(
|
||||
z.object({
|
||||
display_sign_out_link: z.boolean(),
|
||||
links: z.array(link),
|
||||
})
|
||||
),
|
||||
title: z.string(),
|
||||
})
|
||||
@@ -1,200 +0,0 @@
|
||||
import { metrics } from "@opentelemetry/api"
|
||||
|
||||
import {
|
||||
GetNavigationMyPages,
|
||||
GetNavigationMyPagesRefs,
|
||||
} from "@/lib/graphql/Query/AccountPage/Navigation.graphql"
|
||||
import { request } from "@/lib/graphql/request"
|
||||
import { notFound } from "@/server/errors/trpc"
|
||||
import { contentstackBaseProcedure, router } from "@/server/trpc"
|
||||
|
||||
import {
|
||||
generateRefsResponseTag,
|
||||
generateTag,
|
||||
generateTags,
|
||||
} from "@/utils/generateTag"
|
||||
|
||||
import {
|
||||
type GetNavigationMyPagesData,
|
||||
type GetNavigationMyPagesRefsData,
|
||||
getNavigationSchema,
|
||||
navigationPayloadSchema,
|
||||
navigationRefsPayloadSchema,
|
||||
} from "./output"
|
||||
import { getConnections, mapMenuItems } from "./utils"
|
||||
|
||||
const meter = metrics.getMeter("trpc.navigationMyPages")
|
||||
const getNavigationMyPagesRefsCounter = meter.createCounter(
|
||||
"trpc.contentstack.navigationMyPages.refs.get"
|
||||
)
|
||||
const getNavigationMyPagesRefsSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.navigationMyPages.refs.get-success"
|
||||
)
|
||||
const getNavigationMyPagesRefsFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.navigationMyPages.refs.get-fail"
|
||||
)
|
||||
const getNavigationMyPagesCounter = meter.createCounter(
|
||||
"trpc.contentstack.navigationMyPages.get"
|
||||
)
|
||||
const getNavigationMyPagesSuccessCounter = meter.createCounter(
|
||||
"trpc.contentstack.navigationMyPages.get-success"
|
||||
)
|
||||
const getNavigationMyPagesFailCounter = meter.createCounter(
|
||||
"trpc.contentstack.navigationMyPages.get-fail"
|
||||
)
|
||||
|
||||
export const navigationQueryRouter = router({
|
||||
get: contentstackBaseProcedure.query(async function ({ ctx }) {
|
||||
const { lang } = ctx
|
||||
getNavigationMyPagesRefsCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.myPages.navigation.refs start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
const refsResponse = await request<GetNavigationMyPagesRefsData>(
|
||||
GetNavigationMyPagesRefs,
|
||||
{ locale: lang },
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: {
|
||||
tags: [generateRefsResponseTag(lang, "navigation_my_pages")],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!refsResponse.data) {
|
||||
const notFoundError = notFound(refsResponse)
|
||||
getNavigationMyPagesRefsFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.myPages.navigation.refs not found error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
)
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedMyPagesNavigationRefs =
|
||||
navigationRefsPayloadSchema.safeParse(refsResponse.data)
|
||||
if (!validatedMyPagesNavigationRefs.success) {
|
||||
getNavigationMyPagesRefsFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedMyPagesNavigationRefs.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.myPages.navigation.refs validation error",
|
||||
JSON.stringify({
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: validatedMyPagesNavigationRefs.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
getNavigationMyPagesRefsSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.myPages.navigation.refs success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
const connections = getConnections(validatedMyPagesNavigationRefs.data)
|
||||
|
||||
const tags = [
|
||||
generateTags(lang, connections),
|
||||
generateTag(
|
||||
lang,
|
||||
validatedMyPagesNavigationRefs.data.all_navigation_my_pages.items[0]
|
||||
.system.uid
|
||||
),
|
||||
].flat()
|
||||
getNavigationMyPagesCounter.add(1)
|
||||
console.info(
|
||||
"contentstack.myPages.navigation start",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
const response = await request<GetNavigationMyPagesData>(
|
||||
GetNavigationMyPages,
|
||||
{ locale: lang },
|
||||
{
|
||||
cache: "force-cache",
|
||||
next: { tags },
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.data) {
|
||||
const notFoundError = notFound(response)
|
||||
getNavigationMyPagesFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "not_found",
|
||||
error: JSON.stringify({ code: notFoundError.code }),
|
||||
})
|
||||
console.error("contentstack.myPages.navigation not found error", {
|
||||
query: {
|
||||
lang,
|
||||
},
|
||||
error: { code: notFoundError.code },
|
||||
})
|
||||
throw notFoundError
|
||||
}
|
||||
|
||||
const validatedMyPagesNavigation = navigationPayloadSchema.safeParse(
|
||||
response.data
|
||||
)
|
||||
if (!validatedMyPagesNavigation.success) {
|
||||
getNavigationMyPagesFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedMyPagesNavigation.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.myPages.navigation.payload validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedMyPagesNavigation.error,
|
||||
})
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
const menuItem =
|
||||
validatedMyPagesNavigation.data.all_navigation_my_pages.items[0]
|
||||
|
||||
const nav = {
|
||||
menuItems: mapMenuItems(menuItem.menu_items),
|
||||
title: menuItem.title,
|
||||
}
|
||||
|
||||
const validatedNav = getNavigationSchema.safeParse(nav)
|
||||
if (!validatedNav.success) {
|
||||
getNavigationMyPagesFailCounter.add(1, {
|
||||
lang,
|
||||
error_type: "validation_error",
|
||||
error: JSON.stringify(validatedNav.error),
|
||||
})
|
||||
console.error(
|
||||
"contentstack.myPages.navigation validation error",
|
||||
JSON.stringify({
|
||||
query: { lang },
|
||||
error: validatedNav.error,
|
||||
})
|
||||
)
|
||||
|
||||
console.error(validatedNav.error)
|
||||
return null
|
||||
}
|
||||
getNavigationMyPagesSuccessCounter.add(1, { lang })
|
||||
console.info(
|
||||
"contentstack.myPages.navigation success",
|
||||
JSON.stringify({ query: { lang } })
|
||||
)
|
||||
return validatedNav.data
|
||||
}),
|
||||
})
|
||||
@@ -1,52 +0,0 @@
|
||||
import { removeMultipleSlashes } from "@/utils/url"
|
||||
|
||||
import { ContentEnum } from "@/types/enums/content"
|
||||
import type { Edges } from "@/types/requests/utils/edges"
|
||||
import type { NodeRefs } from "@/types/requests/utils/refs"
|
||||
import type { GetNavigationMyPagesRefsData, MenuItems } from "./output"
|
||||
|
||||
export function getConnections(refs: GetNavigationMyPagesRefsData) {
|
||||
const connections: Edges<NodeRefs>[] = []
|
||||
refs.all_navigation_my_pages.items.forEach((ref) => {
|
||||
ref.menu_items.forEach((menuItem) => {
|
||||
menuItem.links.map((link) => {
|
||||
connections.push(link.page)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return connections
|
||||
}
|
||||
|
||||
export function mapMenuItems(menuItems: MenuItems) {
|
||||
return menuItems.map((menuItem) => {
|
||||
return {
|
||||
...menuItem,
|
||||
links: menuItem.links
|
||||
.filter((link) => {
|
||||
// If content is unpublished or in other way inaccessible in Contentstack
|
||||
// there will be no edges, filter out those links.
|
||||
return !!link.page.edges[0]
|
||||
})
|
||||
.map((link) => {
|
||||
const page = link.page.edges[0].node
|
||||
let originalUrl = undefined
|
||||
if (
|
||||
page.__typename === ContentEnum.blocks.ContentPage ||
|
||||
page.__typename === ContentEnum.blocks.LoyaltyPage
|
||||
) {
|
||||
if (page.web.original_url) {
|
||||
originalUrl = page.web.original_url
|
||||
}
|
||||
}
|
||||
return {
|
||||
lang: page.system.locale,
|
||||
linkText: link.link_text ? link.link_text : page.title,
|
||||
uid: page.system.uid,
|
||||
url: removeMultipleSlashes(`/${page.system.locale}/${page.url}`),
|
||||
originalUrl,
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { setTimeout } from "timers/promises"
|
||||
import { z } from "zod"
|
||||
|
||||
import { protectedProcedure } from "@/server/trpc"
|
||||
|
||||
import { timeout } from "@/utils/timeout"
|
||||
|
||||
import { getSasToken } from "./getSasToken"
|
||||
|
||||
const outputSchema = z.object({
|
||||
@@ -15,7 +16,7 @@ export const linkAccount = protectedProcedure
|
||||
const sasAuthToken = getSasToken()
|
||||
|
||||
console.log("[SAS] link account")
|
||||
await setTimeout(1000)
|
||||
await timeout(1000)
|
||||
//TODO: Call actual API here
|
||||
console.log("[SAS] link account done")
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { TRPCError } from "@trpc/server"
|
||||
import { setTimeout } from "timers/promises"
|
||||
import { z } from "zod"
|
||||
|
||||
import { protectedProcedure } from "@/server/trpc"
|
||||
|
||||
import { timeout } from "@/utils/timeout"
|
||||
|
||||
const outputSchema = z.object({})
|
||||
|
||||
export const performLevelUpgrade = protectedProcedure
|
||||
.output(outputSchema)
|
||||
.mutation(async function ({ ctx, input }) {
|
||||
console.log("[SAS] perform upgrade")
|
||||
await setTimeout(1000)
|
||||
await timeout(1000)
|
||||
//TODO: Call actual API here
|
||||
|
||||
throw new TRPCError({
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { TRPCError } from "@trpc/server"
|
||||
import { setTimeout } from "timers/promises"
|
||||
import { z } from "zod"
|
||||
|
||||
import { protectedProcedure } from "@/server/trpc"
|
||||
|
||||
import { timeout } from "@/utils/timeout"
|
||||
|
||||
const outputSchema = z.object({
|
||||
// unlinked: z.boolean(),
|
||||
})
|
||||
@@ -12,7 +13,7 @@ export const unlinkAccount = protectedProcedure
|
||||
.output(outputSchema)
|
||||
.mutation(async function ({ ctx, input }) {
|
||||
console.log("[SAS] unlink account")
|
||||
await setTimeout(1000)
|
||||
await timeout(1000)
|
||||
//TODO: Call actual API here
|
||||
|
||||
throw new TRPCError({
|
||||
|
||||
Reference in New Issue
Block a user