feat: improve structure and error handling
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
export default function Default() {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { benefits } from "@/constants/routes/myPages"
|
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
|
||||||
|
|
||||||
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
|
|
||||||
|
|
||||||
import type { LangParams, PageArgs } from "@/types/params"
|
|
||||||
|
|
||||||
export default async function BenefitsBreadcrumbs({
|
|
||||||
params,
|
|
||||||
}: PageArgs<LangParams>) {
|
|
||||||
const href = benefits[params.lang].replace(`/${params.lang}`, "")
|
|
||||||
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
|
|
||||||
href,
|
|
||||||
locale: params.lang,
|
|
||||||
})
|
|
||||||
return <Breadcrumbs breadcrumbs={breadcrumbs} />
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function Default() {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function Default() {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { overview } from "@/constants/routes/myPages"
|
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
|
||||||
|
|
||||||
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
|
|
||||||
|
|
||||||
import type { LangParams, PageArgs } from "@/types/params"
|
|
||||||
|
|
||||||
export default async function OverviewBreadcrumbs({
|
|
||||||
params,
|
|
||||||
}: PageArgs<LangParams>) {
|
|
||||||
const href = overview[params.lang].replace(`/${params.lang}`, "")
|
|
||||||
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
|
|
||||||
href,
|
|
||||||
locale: params.lang,
|
|
||||||
})
|
|
||||||
return <Breadcrumbs breadcrumbs={breadcrumbs} />
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { profile } from "@/constants/routes/myPages"
|
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
|
||||||
|
|
||||||
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
|
|
||||||
|
|
||||||
import type { LangParams, PageArgs } from "@/types/params"
|
|
||||||
|
|
||||||
export default async function ProfileBreadcrumbs({
|
|
||||||
params,
|
|
||||||
}: PageArgs<LangParams>) {
|
|
||||||
const href = profile[params.lang].replace(`/${params.lang}`, "")
|
|
||||||
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
|
|
||||||
href,
|
|
||||||
locale: params.lang,
|
|
||||||
})
|
|
||||||
return <Breadcrumbs breadcrumbs={breadcrumbs} />
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { profile } from "@/constants/routes/myPages"
|
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
|
||||||
|
|
||||||
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
|
|
||||||
|
|
||||||
import type { LangParams, PageArgs } from "@/types/params"
|
|
||||||
|
|
||||||
export default async function ProfileBreadcrumbs({
|
|
||||||
params,
|
|
||||||
}: PageArgs<LangParams>) {
|
|
||||||
const href = profile[params.lang].replace(`/${params.lang}`, "")
|
|
||||||
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
|
|
||||||
href,
|
|
||||||
locale: params.lang,
|
|
||||||
})
|
|
||||||
return <Breadcrumbs breadcrumbs={breadcrumbs} />
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function Default() {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { stays } from "@/constants/routes/myPages"
|
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
|
||||||
|
|
||||||
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
|
|
||||||
|
|
||||||
import type { LangParams, PageArgs } from "@/types/params"
|
|
||||||
|
|
||||||
export default async function StaysBreadcrumbs({
|
|
||||||
params,
|
|
||||||
}: PageArgs<LangParams>) {
|
|
||||||
const href = stays[params.lang].replace(`/${params.lang}`, "")
|
|
||||||
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
|
|
||||||
href,
|
|
||||||
locale: params.lang,
|
|
||||||
})
|
|
||||||
return <Breadcrumbs breadcrumbs={breadcrumbs} />
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts"
|
|
||||||
import Header from "@/components/MyPages/Header"
|
|
||||||
import Sidebar from "@/components/MyPages/Sidebar"
|
|
||||||
|
|
||||||
import styles from "./layout.module.css"
|
|
||||||
|
|
||||||
import type { MyPagesLayoutProps } from "@/types/components/myPages/layout"
|
|
||||||
|
|
||||||
export default async function MyPagesLayout({
|
|
||||||
breadcrumbs,
|
|
||||||
children,
|
|
||||||
params,
|
|
||||||
}: React.PropsWithChildren<MyPagesLayoutProps>) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`${firaMono.variable} ${firaSans.variable} ${styles.layout}`}
|
|
||||||
>
|
|
||||||
<Header lang={params.lang} />
|
|
||||||
{breadcrumbs}
|
|
||||||
<div className={styles.content}>
|
|
||||||
<Sidebar lang={params.lang} />
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import { auth } from "@/auth"
|
|
||||||
|
|
||||||
export default async function ByPass() {
|
|
||||||
const session = await auth()
|
|
||||||
|
|
||||||
return <pre>{JSON.stringify(session, null, 2)}</pre>
|
|
||||||
}
|
|
||||||
@@ -17,5 +17,5 @@ export default async function ProtectedLayout({
|
|||||||
redirect(`/${params.lang}/login`)
|
redirect(`/${params.lang}/login`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>{children}</>
|
return children
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ export async function GET(request: NextRequest) {
|
|||||||
// If all else fails, always redirect to startpage
|
// If all else fails, always redirect to startpage
|
||||||
const redirectTo =
|
const redirectTo =
|
||||||
returnUrl ||
|
returnUrl ||
|
||||||
request.headers.get("x-redirect-to") ||
|
|
||||||
request.nextUrl.searchParams.get("redirectTo") ||
|
request.nextUrl.searchParams.get("redirectTo") ||
|
||||||
"/"
|
"/"
|
||||||
|
|
||||||
|
|||||||
@@ -13,25 +13,14 @@ import type { LangParams, PageArgs } from "@/types/params"
|
|||||||
export default async function MyPages({
|
export default async function MyPages({
|
||||||
params,
|
params,
|
||||||
}: PageArgs<LangParams & { path: string[] }>) {
|
}: PageArgs<LangParams & { path: string[] }>) {
|
||||||
const baseUrl = myPages[params.lang].replace(`/${params.lang}`, "")
|
const accountPage = await serverClient().contentstack.accountPage.get()
|
||||||
const pathUrl = params.path.join("/")
|
|
||||||
|
|
||||||
const accountPage = await serverClient().contentstack.accountPage.get({
|
|
||||||
url: `${baseUrl}/${pathUrl}`,
|
|
||||||
lang: params.lang,
|
|
||||||
})
|
|
||||||
|
|
||||||
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
|
|
||||||
href: `${baseUrl}/${pathUrl}`,
|
|
||||||
locale: params.lang,
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Breadcrumbs breadcrumbs={breadcrumbs} />
|
<Breadcrumbs />
|
||||||
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<Sidebar lang={params.lang} />
|
<Sidebar />
|
||||||
<MaxWidth className={styles.blocks} tag="main">
|
<MaxWidth className={styles.blocks} tag="main">
|
||||||
{accountPage.content.length ? (
|
{accountPage.content.length ? (
|
||||||
<Content lang={params.lang} content={accountPage.content} />
|
<Content lang={params.lang} content={accountPage.content} />
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
.content {
|
||||||
|
display: grid;
|
||||||
|
padding-bottom: 7.7rem;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 950px) {
|
||||||
|
.content {
|
||||||
|
gap: 10rem;
|
||||||
|
grid-template-columns: 25rem 1fr;
|
||||||
|
padding-bottom: 17.5rem;
|
||||||
|
padding-left: 2.4rem;
|
||||||
|
padding-right: 2.4rem;
|
||||||
|
padding-top: 5.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/[lang]/(live)/(protected)/my-pages/[[...uri]]/layout.tsx
Normal file
21
app/[lang]/(live)/(protected)/my-pages/[[...uri]]/layout.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
|
||||||
|
import Sidebar from "@/components/MyPages/Sidebar"
|
||||||
|
|
||||||
|
import styles from "./layout.module.css"
|
||||||
|
|
||||||
|
import { LangParams, LayoutArgs, UriParams } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function MyPagesURILayout({
|
||||||
|
children,
|
||||||
|
}: React.PropsWithChildren<LayoutArgs<LangParams & UriParams>>) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Breadcrumbs />
|
||||||
|
|
||||||
|
<div className={styles.content}>
|
||||||
|
<Sidebar />
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
export const breadcrumbs = {
|
|
||||||
"/my-pages": [
|
|
||||||
{
|
|
||||||
title: "My Pages",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"/my-pages/profile": [
|
|
||||||
{
|
|
||||||
href: "/my-pages",
|
|
||||||
title: "My Pages",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "My Profile",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts"
|
import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts"
|
||||||
import Header from "@/components/MyPages/Header"
|
|
||||||
|
|
||||||
import styles from "./layout.module.css"
|
import styles from "./layout.module.css"
|
||||||
|
|
||||||
import type { MyPagesLayoutProps } from "@/types/components/myPages/layout"
|
import { LangParams, LayoutArgs } from "@/types/params"
|
||||||
|
|
||||||
export default async function MyPagesLayout({
|
export default async function MyPagesLayout({
|
||||||
children,
|
children,
|
||||||
params,
|
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
|
||||||
}: React.PropsWithChildren<MyPagesLayoutProps>) {
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${firaMono.variable} ${firaSans.variable} ${styles.layout}`}
|
className={`${firaMono.variable} ${firaSans.variable} ${styles.layout}`}
|
||||||
>
|
>
|
||||||
<Header lang={params.lang} />
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,9 +2,19 @@ import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts"
|
|||||||
|
|
||||||
import styles from "./layout.module.css"
|
import styles from "./layout.module.css"
|
||||||
|
|
||||||
export default function LoyaltyPagesLayout({
|
import {
|
||||||
|
ContentTypeParams,
|
||||||
|
LangParams,
|
||||||
|
LayoutArgs,
|
||||||
|
UIDParams,
|
||||||
|
} from "@/types/params"
|
||||||
|
|
||||||
|
export default function ContentTypeLayout({
|
||||||
children,
|
children,
|
||||||
}: React.PropsWithChildren) {
|
params,
|
||||||
|
}: React.PropsWithChildren<
|
||||||
|
LayoutArgs<LangParams & ContentTypeParams & UIDParams>
|
||||||
|
>) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${firaMono.variable} ${firaSans.variable} ${styles.layout}`}
|
className={`${firaMono.variable} ${firaSans.variable} ${styles.layout}`}
|
||||||
26
app/[lang]/(live)/(public)/[contentType]/[uid]/page.tsx
Normal file
26
app/[lang]/(live)/(public)/[contentType]/[uid]/page.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { notFound } from "next/navigation"
|
||||||
|
|
||||||
|
import ContentPage from "@/components/ContentType/ContentPage"
|
||||||
|
import LoyaltyPage from "@/components/ContentType/LoyaltyPage"
|
||||||
|
|
||||||
|
import {
|
||||||
|
ContentTypeParams,
|
||||||
|
LangParams,
|
||||||
|
PageArgs,
|
||||||
|
UIDParams,
|
||||||
|
} from "@/types/params"
|
||||||
|
|
||||||
|
export default async function ContentTypePage({
|
||||||
|
params,
|
||||||
|
}: PageArgs<LangParams & ContentTypeParams & UIDParams, {}>) {
|
||||||
|
switch (params.contentType) {
|
||||||
|
case "loyalty-page":
|
||||||
|
return <LoyaltyPage />
|
||||||
|
case "content-page":
|
||||||
|
return <ContentPage />
|
||||||
|
default:
|
||||||
|
const type: never = params.contentType
|
||||||
|
console.error(`Unsupported content type given: ${type}`)
|
||||||
|
notFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { AuthError } from "next-auth"
|
|||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
import { Lang } from "@/constants/languages"
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
import { badRequest, internalServerError } from "@/server/errors/next"
|
import { internalServerError } from "@/server/errors/next"
|
||||||
|
|
||||||
import { signIn } from "@/auth"
|
import { signIn } from "@/auth"
|
||||||
|
|
||||||
@@ -22,17 +22,15 @@ export async function GET(
|
|||||||
// Normal login request from New web
|
// Normal login request from New web
|
||||||
redirectTo =
|
redirectTo =
|
||||||
request.cookies.get("redirectTo")?.value || // Cookie gets set by authRequired middleware
|
request.cookies.get("redirectTo")?.value || // Cookie gets set by authRequired middleware
|
||||||
request.headers.get("x-redirect-to") ||
|
|
||||||
request.nextUrl.searchParams.get("redirectTo") ||
|
request.nextUrl.searchParams.get("redirectTo") ||
|
||||||
""
|
"/"
|
||||||
|
|
||||||
// If above fails, always redirect to startpage
|
// Make relative URL to absolute URL
|
||||||
if (!redirectTo) {
|
if (redirectTo.startsWith("/")) {
|
||||||
if (!env.PUBLIC_URL) {
|
if (!env.PUBLIC_URL) {
|
||||||
throw internalServerError("No value for env.PUBLIC_URL")
|
throw internalServerError("No value for env.PUBLIC_URL")
|
||||||
}
|
}
|
||||||
redirectTo = env.PUBLIC_URL
|
redirectTo = new URL(redirectTo, env.PUBLIC_URL).href
|
||||||
console.log({ login_fallback: redirectTo })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up cookie from authRequired middleware
|
// Clean up cookie from authRequired middleware
|
||||||
@@ -104,11 +102,11 @@ export async function GET(
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof AuthError) {
|
if (error instanceof AuthError) {
|
||||||
console.log({ signInAuthError: error })
|
console.error({ signInAuthError: error })
|
||||||
} else {
|
} else {
|
||||||
console.log({ signInError: error })
|
console.error({ signInError: error })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return badRequest()
|
return internalServerError()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
import { notFound } from "next/navigation"
|
|
||||||
|
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
|
||||||
|
|
||||||
import { Blocks } from "@/components/Loyalty/Blocks"
|
|
||||||
import Sidebar from "@/components/Loyalty/Sidebar"
|
|
||||||
import MaxWidth from "@/components/MaxWidth"
|
|
||||||
|
|
||||||
import styles from "./page.module.css"
|
|
||||||
|
|
||||||
import type { LangParams, PageArgs, UriParams } from "@/types/params"
|
|
||||||
|
|
||||||
export default async function LoyaltyPage({
|
|
||||||
params,
|
|
||||||
searchParams,
|
|
||||||
}: PageArgs<LangParams, UriParams>) {
|
|
||||||
try {
|
|
||||||
if (!searchParams.uri) {
|
|
||||||
throw new Error("Bad URI")
|
|
||||||
}
|
|
||||||
|
|
||||||
const loyaltyPage = await serverClient().contentstack.loyaltyPage.get({
|
|
||||||
href: searchParams.uri,
|
|
||||||
locale: params.lang,
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className={styles.content}>
|
|
||||||
{loyaltyPage.sidebar ? <Sidebar blocks={loyaltyPage.sidebar} /> : null}
|
|
||||||
|
|
||||||
<MaxWidth className={styles.blocks} tag="main">
|
|
||||||
{loyaltyPage.blocks ? <Blocks blocks={loyaltyPage.blocks} /> : null}
|
|
||||||
</MaxWidth>
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
} catch (err) {
|
|
||||||
return notFound()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
.layout {
|
.layout {
|
||||||
--header-height: 4.5rem;
|
--header-height: 4.5rem;
|
||||||
|
|
||||||
background-color: var(--Brand-Coffee-Subtle);
|
|
||||||
display: grid;
|
display: grid;
|
||||||
font-family: var(--ff-fira-sans);
|
font-family: var(--ff-fira-sans);
|
||||||
grid-template-rows: var(--header-height) auto 1fr;
|
grid-template-rows: var(--header-height) auto 1fr;
|
||||||
32
app/[lang]/(live)/error.tsx
Normal file
32
app/[lang]/(live)/error.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
"use client" // Error components must be Client Components
|
||||||
|
|
||||||
|
import { usePathname } from "next/navigation"
|
||||||
|
import { useEffect } from "react"
|
||||||
|
|
||||||
|
import { findLang } from "@/constants/languages"
|
||||||
|
|
||||||
|
import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts"
|
||||||
|
|
||||||
|
import styles from "./error.module.css"
|
||||||
|
|
||||||
|
export default function Error({
|
||||||
|
error,
|
||||||
|
}: {
|
||||||
|
error: Error & { digest?: string }
|
||||||
|
}) {
|
||||||
|
useEffect(() => {
|
||||||
|
// Log the error to an error reporting service
|
||||||
|
console.error(error)
|
||||||
|
}, [error])
|
||||||
|
|
||||||
|
const pathname = usePathname()
|
||||||
|
const lang = findLang(pathname)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`${firaMono.variable} ${firaSans.variable} ${styles.layout}`}
|
||||||
|
>
|
||||||
|
<div className={styles.content}>{lang}: Something went wrong!</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,11 +2,9 @@ import "@/app/globals.css"
|
|||||||
import "@scandic-hotels/design-system/style.css"
|
import "@scandic-hotels/design-system/style.css"
|
||||||
|
|
||||||
import Script from "next/script"
|
import Script from "next/script"
|
||||||
import { SessionProvider } from "next-auth/react"
|
|
||||||
|
|
||||||
import TrpcProvider from "@/lib/trpc/Provider"
|
import TrpcProvider from "@/lib/trpc/Provider"
|
||||||
|
|
||||||
import { auth } from "@/auth"
|
|
||||||
import AdobeScript from "@/components/Current/AdobeScript"
|
import AdobeScript from "@/components/Current/AdobeScript"
|
||||||
import VwoScript from "@/components/Current/VwoScript"
|
import VwoScript from "@/components/Current/VwoScript"
|
||||||
|
|
||||||
@@ -23,7 +21,6 @@ export default async function RootLayout({
|
|||||||
children,
|
children,
|
||||||
params,
|
params,
|
||||||
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
|
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
|
||||||
const session = await auth()
|
|
||||||
return (
|
return (
|
||||||
<html lang={params.lang}>
|
<html lang={params.lang}>
|
||||||
<head>
|
<head>
|
||||||
@@ -46,9 +43,7 @@ export default async function RootLayout({
|
|||||||
<VwoScript />
|
<VwoScript />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<SessionProvider basePath="/api/web/auth" session={session}>
|
<TrpcProvider>{children}</TrpcProvider>
|
||||||
<TrpcProvider>{children}</TrpcProvider>
|
|
||||||
</SessionProvider>
|
|
||||||
<Script id="page-tracking">{`
|
<Script id="page-tracking">{`
|
||||||
typeof _satellite !== "undefined" && _satellite.pageBottom();
|
typeof _satellite !== "undefined" && _satellite.pageBottom();
|
||||||
`}</Script>
|
`}</Script>
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.layout {
|
||||||
|
font-family: var(--ff-fira-sans);
|
||||||
|
}
|
||||||
17
app/[lang]/(live)/middleware-error/[status]/page.tsx
Normal file
17
app/[lang]/(live)/middleware-error/[status]/page.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts"
|
||||||
|
|
||||||
|
import styles from "./page.module.css"
|
||||||
|
|
||||||
|
import { LangParams, LayoutArgs, StatusParams } from "@/types/params"
|
||||||
|
|
||||||
|
export default function MiddlewareError({
|
||||||
|
params,
|
||||||
|
}: LayoutArgs<LangParams & StatusParams>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`${firaMono.variable} ${firaSans.variable} ${styles.layout}`}
|
||||||
|
>
|
||||||
|
Middleware Error {params.lang}: {params.status}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<h2>Not Found</h2>
|
<h1>Not found</h1>
|
||||||
<p>Could not find requested resource</p>
|
<p>Could not find requested resource</p>
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<h2>Not Found</h2>
|
<h1>Not found</h1>
|
||||||
<p>Could not find requested resource</p>
|
<p>Could not find requested resource</p>
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<h2>Not Found</h2>
|
<h1>Not found</h1>
|
||||||
<p>Could not find requested resource</p>
|
<p>Could not find requested resource</p>
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
|
|||||||
20
app/[lang]/(preview)/preview/[contentType]/[uid]/page.tsx
Normal file
20
app/[lang]/(preview)/preview/[contentType]/[uid]/page.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import {
|
||||||
|
ContentTypeParams,
|
||||||
|
LangParams,
|
||||||
|
PageArgs,
|
||||||
|
UIDParams,
|
||||||
|
} from "@/types/params"
|
||||||
|
|
||||||
|
export default async function PreviewPage({
|
||||||
|
params,
|
||||||
|
searchParams,
|
||||||
|
}: PageArgs<LangParams & ContentTypeParams & UIDParams, {}>) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Preview for {params.contentType}:{params.uid} in {params.lang} with
|
||||||
|
params <pre>{JSON.stringify(searchParams, null, 2)}</pre> goes here
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export default async function PreviewPage() {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<h2>Not Found</h2>
|
<h1>Not found</h1>
|
||||||
<p>Could not find requested resource</p>
|
<p>Could not find requested resource</p>
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,15 +5,15 @@ import type { NextRequest } from "next/server"
|
|||||||
export const runtime = "edge"
|
export const runtime = "edge"
|
||||||
|
|
||||||
export async function GET(req: NextRequest) {
|
export async function GET(req: NextRequest) {
|
||||||
console.log({ GET_REQ: req })
|
// console.log({ GET_REQ: req })
|
||||||
console.log({ GET_AUTH_URL: process.env.AUTH_URL })
|
// console.log({ GET_AUTH_URL: process.env.AUTH_URL })
|
||||||
console.log({ GET_NEXTAUTH_URL: process.env.NEXTAUTH_URL })
|
// console.log({ GET_NEXTAUTH_URL: process.env.NEXTAUTH_URL })
|
||||||
return _GET(req)
|
return _GET(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function POST(req: NextRequest) {
|
export async function POST(req: NextRequest) {
|
||||||
console.log({ POST_REQ: req })
|
// console.log({ POST_REQ: req })
|
||||||
console.log({ POST_AUTH_URL: process.env.AUTH_URL })
|
// console.log({ POST_AUTH_URL: process.env.AUTH_URL })
|
||||||
console.log({ POST_NEXTAUTH_URL: process.env.NEXTAUTH_URL })
|
// console.log({ POST_NEXTAUTH_URL: process.env.NEXTAUTH_URL })
|
||||||
return _POST(req)
|
return _POST(req)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,8 +64,7 @@ export async function POST(request: NextRequest) {
|
|||||||
data: { content_type, entry },
|
data: { content_type, entry },
|
||||||
},
|
},
|
||||||
} = validatedData
|
} = validatedData
|
||||||
const identifier = entry.url ?? content_type.uid
|
const refsTag = generateRefsResponseTag(entry.locale, entry.uid)
|
||||||
const refsTag = generateRefsResponseTag(entry.locale, identifier)
|
|
||||||
const refTag = generateRefTag(entry.locale, content_type.uid, entry.uid)
|
const refTag = generateRefTag(entry.locale, content_type.uid, entry.uid)
|
||||||
const tag = generateTag(entry.locale, entry.uid)
|
const tag = generateTag(entry.locale, entry.uid)
|
||||||
|
|
||||||
@@ -81,7 +80,7 @@ export async function POST(request: NextRequest) {
|
|||||||
if (entry.breadcrumbs) {
|
if (entry.breadcrumbs) {
|
||||||
const breadcrumbsRefsTag = generateRefsResponseTag(
|
const breadcrumbsRefsTag = generateRefsResponseTag(
|
||||||
entry.locale,
|
entry.locale,
|
||||||
identifier,
|
entry.uid,
|
||||||
breadcrumbsAffix
|
breadcrumbsAffix
|
||||||
)
|
)
|
||||||
const breadcrumbsTag = generateTag(
|
const breadcrumbsTag = generateTag(
|
||||||
|
|||||||
3
app/global-error.module.css
Normal file
3
app/global-error.module.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.layout {
|
||||||
|
font-family: var(--ff-fira-sans);
|
||||||
|
}
|
||||||
25
app/global-error.tsx
Normal file
25
app/global-error.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts"
|
||||||
|
|
||||||
|
import styles from "./global-error.module.css"
|
||||||
|
|
||||||
|
export default function GlobalError({
|
||||||
|
error,
|
||||||
|
}: {
|
||||||
|
error: Error & { digest?: string }
|
||||||
|
}) {
|
||||||
|
console.log({ global_error: error })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<div
|
||||||
|
className={`${firaMono.variable} ${firaSans.variable} ${styles.layout}`}
|
||||||
|
>
|
||||||
|
<h1>Something went really wrong!</h1>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)
|
||||||
|
}
|
||||||
3
app/not-found.module.css
Normal file
3
app/not-found.module.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.layout {
|
||||||
|
font-family: var(--ff-fira-sans);
|
||||||
|
}
|
||||||
69
auth.ts
69
auth.ts
@@ -35,7 +35,6 @@ const customProvider = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
profile(profile) {
|
profile(profile) {
|
||||||
console.log({ profile })
|
|
||||||
return {
|
return {
|
||||||
id: profile.id,
|
id: profile.id,
|
||||||
sub: profile.sub,
|
sub: profile.sub,
|
||||||
@@ -54,17 +53,9 @@ export const config = {
|
|||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
async signIn() {
|
async signIn() {
|
||||||
console.log("****** SIGN IN *******")
|
|
||||||
console.log(arguments)
|
|
||||||
console.log("****** END - SIGN IN *******")
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
async session({ session, token, user }) {
|
async session({ session, token, user }) {
|
||||||
console.log("****** SESSION *******")
|
|
||||||
console.log({ session })
|
|
||||||
console.log({ token })
|
|
||||||
console.log({ user })
|
|
||||||
console.log("****** END - SESSION *******")
|
|
||||||
if (session.user) {
|
if (session.user) {
|
||||||
return {
|
return {
|
||||||
...session,
|
...session,
|
||||||
@@ -79,7 +70,6 @@ export const config = {
|
|||||||
return session
|
return session
|
||||||
},
|
},
|
||||||
async redirect({ baseUrl, url }) {
|
async redirect({ baseUrl, url }) {
|
||||||
console.log({ redirect_baseUrl: baseUrl, redirect_url: url })
|
|
||||||
if (url.startsWith("/")) {
|
if (url.startsWith("/")) {
|
||||||
// Allows relative callback URLs
|
// Allows relative callback URLs
|
||||||
return `${baseUrl}${url}`
|
return `${baseUrl}${url}`
|
||||||
@@ -103,16 +93,9 @@ export const config = {
|
|||||||
return baseUrl
|
return baseUrl
|
||||||
},
|
},
|
||||||
async authorized({ auth, request }) {
|
async authorized({ auth, request }) {
|
||||||
console.log("****** AUTHORIZED *******")
|
|
||||||
console.log({ auth })
|
|
||||||
console.log({ request })
|
|
||||||
console.log("****** END - AUTHORIZED *******")
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
async jwt({ session, token, trigger, account }) {
|
async jwt({ session, token, trigger, account }) {
|
||||||
console.log("****** JWT *******")
|
|
||||||
console.log({ session, token, trigger, account })
|
|
||||||
console.log("****** END - JWT *******")
|
|
||||||
if (account) {
|
if (account) {
|
||||||
return {
|
return {
|
||||||
access_token: account.access_token,
|
access_token: account.access_token,
|
||||||
@@ -121,32 +104,32 @@ export const config = {
|
|||||||
return token
|
return token
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
events: {
|
// events: {
|
||||||
async signIn() {
|
// async signIn() {
|
||||||
console.log("#### SIGNIN EVENT ARGS ######")
|
// console.log("#### SIGNIN EVENT ARGS ######")
|
||||||
console.log(arguments)
|
// console.log(arguments)
|
||||||
console.log("#### END - SIGNIN EVENT ARGS ######")
|
// console.log("#### END - SIGNIN EVENT ARGS ######")
|
||||||
},
|
// },
|
||||||
async session() {
|
// async session() {
|
||||||
console.log("#### SESSION EVENT ARGS ######")
|
// console.log("#### SESSION EVENT ARGS ######")
|
||||||
console.log(arguments)
|
// console.log(arguments)
|
||||||
console.log("#### END - SESSION EVENT ARGS ######")
|
// console.log("#### END - SESSION EVENT ARGS ######")
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
logger: {
|
// logger: {
|
||||||
error(code, ...message) {
|
// error(code, ...message) {
|
||||||
console.info("ERROR LOGGER")
|
// console.info("ERROR LOGGER")
|
||||||
console.error(code, message)
|
// console.error(code, message)
|
||||||
},
|
// },
|
||||||
warn(code, ...message) {
|
// warn(code, ...message) {
|
||||||
console.info("WARN LOGGER")
|
// console.info("WARN LOGGER")
|
||||||
console.warn(code, message)
|
// console.warn(code, message)
|
||||||
},
|
// },
|
||||||
debug(code, ...message) {
|
// debug(code, ...message) {
|
||||||
console.info("DEBUG LOGGER")
|
// console.info("DEBUG LOGGER")
|
||||||
console.debug(code, message)
|
// console.debug(code, message)
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
} satisfies NextAuthConfig
|
} satisfies NextAuthConfig
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
|
|||||||
21
components/ContentType/LoyaltyPage.tsx
Normal file
21
components/ContentType/LoyaltyPage.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import { Blocks } from "@/components/Loyalty/Blocks"
|
||||||
|
import Sidebar from "@/components/Loyalty/Sidebar"
|
||||||
|
import MaxWidth from "@/components/MaxWidth"
|
||||||
|
|
||||||
|
import styles from "./loyaltyPage.module.css"
|
||||||
|
|
||||||
|
export default async function LoyaltyPage() {
|
||||||
|
const loyaltyPage = await serverClient().contentstack.loyaltyPage.get()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.content}>
|
||||||
|
{loyaltyPage.sidebar ? <Sidebar blocks={loyaltyPage.sidebar} /> : null}
|
||||||
|
|
||||||
|
<MaxWidth className={styles.blocks} tag="main">
|
||||||
|
{loyaltyPage.blocks ? <Blocks blocks={loyaltyPage.blocks} /> : null}
|
||||||
|
</MaxWidth>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
15
components/Header/Logo/index.tsx
Normal file
15
components/Header/Logo/index.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import Image from "@/components/Image"
|
||||||
|
|
||||||
|
import styles from "./logo.module.css"
|
||||||
|
|
||||||
|
import { LogoProps } from "@/types/components/header/logo"
|
||||||
|
|
||||||
|
export default async function Logo({ title, height, width, src }: LogoProps) {
|
||||||
|
return (
|
||||||
|
<Link className={styles.link} href="#">
|
||||||
|
<Image alt={title} height={height} src={src} width={width} />
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
35
components/Header/index.tsx
Normal file
35
components/Header/index.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { GetHeader } from "@/lib/graphql/Query/Header.graphql"
|
||||||
|
import { request } from "@/lib/graphql/request"
|
||||||
|
|
||||||
|
import Logo from "./Logo"
|
||||||
|
|
||||||
|
import styles from "./header.module.css"
|
||||||
|
|
||||||
|
import type { LangParams } from "@/types/params"
|
||||||
|
import { HeaderQueryData } from "@/types/requests/header"
|
||||||
|
|
||||||
|
export default async function Header({ lang }: LangParams) {
|
||||||
|
const { data } = await request<HeaderQueryData>(GetHeader, {
|
||||||
|
locale: lang,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (
|
||||||
|
!data.all_header.items.length ||
|
||||||
|
!data.all_header.items?.[0].logoConnection.totalCount
|
||||||
|
) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const logo = data.all_header.items[0].logoConnection.edges[0]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className={styles.header}>
|
||||||
|
<Logo
|
||||||
|
title={logo.node.title}
|
||||||
|
height={logo.node.dimension.height}
|
||||||
|
width={logo.node.dimension.width}
|
||||||
|
src={logo.node.url}
|
||||||
|
/>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,9 +8,7 @@ import styles from "./contactRow.module.css"
|
|||||||
import type { ContactRowProps } from "@/types/components/loyalty/sidebar"
|
import type { ContactRowProps } from "@/types/components/loyalty/sidebar"
|
||||||
|
|
||||||
export default async function ContactRow({ contact }: ContactRowProps) {
|
export default async function ContactRow({ contact }: ContactRowProps) {
|
||||||
const data = await serverClient().contentstack.contactConfig.get({
|
const data = await serverClient().contentstack.contactConfig.get()
|
||||||
lang: Lang.en,
|
|
||||||
})
|
|
||||||
|
|
||||||
const val = getValueFromContactConfig(contact.contact_field, data)
|
const val = getValueFromContactConfig(contact.contact_field, data)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { cva } from "class-variance-authority"
|
import { cva } from "class-variance-authority"
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
|
||||||
import { trpc } from "@/lib/trpc/client"
|
|
||||||
|
|
||||||
import styles from "./maxWidth.module.css"
|
import styles from "./maxWidth.module.css"
|
||||||
|
|
||||||
import type { MaxWidthProps } from "@/types/components/max-width"
|
import type { MaxWidthProps } from "@/types/components/max-width"
|
||||||
@@ -15,10 +12,6 @@ export default function MaxWidth({
|
|||||||
tag = "section",
|
tag = "section",
|
||||||
...props
|
...props
|
||||||
}: MaxWidthProps) {
|
}: MaxWidthProps) {
|
||||||
const d = trpc.contentstack.breadcrumbs.get.useQuery({
|
|
||||||
locale: Lang.en,
|
|
||||||
href: "/my-pages/overview",
|
|
||||||
})
|
|
||||||
const Cmp = tag
|
const Cmp = tag
|
||||||
return <Cmp className={maxWidthVariants({ className })} {...props} />
|
return <Cmp className={maxWidthVariants({ className })} {...props} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { _ } from "@/lib/translation"
|
import { _ } from "@/lib/translation"
|
||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
import Breadcrumb from "./Breadcrumb"
|
import Breadcrumb from "./Breadcrumb"
|
||||||
import BreadcrumbsWithLink from "./BreadcrumbWithLink"
|
import BreadcrumbsWithLink from "./BreadcrumbWithLink"
|
||||||
|
|
||||||
import styles from "./breadcrumbs.module.css"
|
import styles from "./breadcrumbs.module.css"
|
||||||
|
|
||||||
import type { BreadcrumbsProps } from "@/types/components/myPages/breadcrumbs"
|
export default async function Breadcrumbs() {
|
||||||
|
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get()
|
||||||
|
|
||||||
export default function Breadcrumbs({ breadcrumbs }: BreadcrumbsProps) {
|
|
||||||
return (
|
return (
|
||||||
<nav className={styles.breadcrumbs}>
|
<nav className={styles.breadcrumbs}>
|
||||||
<ul className={styles.list}>
|
<ul className={styles.list}>
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import Link from "next/link"
|
|
||||||
|
|
||||||
import { GetMyPagesLogo } from "@/lib/graphql/Query/Logo.graphql"
|
|
||||||
import { request } from "@/lib/graphql/request"
|
|
||||||
|
|
||||||
import Image from "@/components/Image"
|
|
||||||
|
|
||||||
import styles from "./logo.module.css"
|
|
||||||
|
|
||||||
import type { LangParams } from "@/types/params"
|
|
||||||
import type { LogoQueryData } from "@/types/requests/myPages/logo"
|
|
||||||
|
|
||||||
export default async function Logo({ lang }: LangParams) {
|
|
||||||
const { data } = await request<LogoQueryData>(GetMyPagesLogo, {
|
|
||||||
locale: lang,
|
|
||||||
})
|
|
||||||
if (
|
|
||||||
!data.all_header.items.length ||
|
|
||||||
!data.all_header.items?.[0].logoConnection.totalCount
|
|
||||||
) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const logo = data.all_header.items[0].logoConnection.edges[0]
|
|
||||||
return (
|
|
||||||
<Link className={styles.link} href="#">
|
|
||||||
<Image
|
|
||||||
alt={logo.node.title}
|
|
||||||
height={logo.node.dimension.height}
|
|
||||||
src={logo.node.url}
|
|
||||||
width={logo.node.dimension.width}
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import Hamburger from "./Hamburger"
|
|
||||||
import LanguageSwitcher from "./LanguageSwitcher"
|
|
||||||
import Logo from "./Logo"
|
|
||||||
import User from "./User"
|
|
||||||
|
|
||||||
import styles from "./header.module.css"
|
|
||||||
|
|
||||||
import type { LangParams } from "@/types/params"
|
|
||||||
|
|
||||||
export default function Header({ lang }: LangParams) {
|
|
||||||
return (
|
|
||||||
<header className={styles.header}>
|
|
||||||
<Logo lang={lang} />
|
|
||||||
<LanguageSwitcher />
|
|
||||||
<User />
|
|
||||||
<Hamburger />
|
|
||||||
</header>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -8,11 +8,9 @@ import Title from "@/components/Title"
|
|||||||
|
|
||||||
import styles from "./sidebar.module.css"
|
import styles from "./sidebar.module.css"
|
||||||
|
|
||||||
import type { SidebarProps } from "@/types/requests/myPages/navigation"
|
export default async function Sidebar() {
|
||||||
|
const navigation = await serverClient().contentstack.myPages.navigation.get()
|
||||||
|
|
||||||
export default async function Sidebar({ lang }: SidebarProps) {
|
|
||||||
const navigation =
|
|
||||||
await serverClient().contentstack.myPages.navigation.get(lang)
|
|
||||||
return (
|
return (
|
||||||
<aside className={styles.sidebar}>
|
<aside className={styles.sidebar}>
|
||||||
<nav className={styles.nav}>
|
<nav className={styles.nav}>
|
||||||
|
|||||||
@@ -7,63 +7,58 @@
|
|||||||
#import "../Fragments/Refs/LoyaltyPage.graphql"
|
#import "../Fragments/Refs/LoyaltyPage.graphql"
|
||||||
#import "../Fragments/Refs/System.graphql"
|
#import "../Fragments/Refs/System.graphql"
|
||||||
|
|
||||||
query GetAccountPage($locale: String!, $url: String!) {
|
query GetAccountPage($locale: String!, $uid: String!) {
|
||||||
all_account_page(limit: 1, locale: $locale, where: { url: $url }) {
|
account_page(locale: $locale, uid: $uid) {
|
||||||
items {
|
url
|
||||||
url
|
title
|
||||||
title
|
content {
|
||||||
content {
|
__typename
|
||||||
__typename
|
...AccountPageContentDynamicContent
|
||||||
...AccountPageContentDynamicContent
|
...AccountPageContentShortcuts
|
||||||
...AccountPageContentShortcuts
|
...AccountPageContentTextContent
|
||||||
...AccountPageContentTextContent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
total
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query GetAccountPageRefs($locale: String!, $url: String!) {
|
query GetAccountPageRefs($locale: String!, $uid: String!) {
|
||||||
all_account_page(limit: 1, locale: $locale, where: { url: $url }) {
|
account_page(locale: $locale, uid: $uid) {
|
||||||
items {
|
content {
|
||||||
content {
|
... on AccountPageContentDynamicContent {
|
||||||
... on AccountPageContentDynamicContent {
|
__typename
|
||||||
__typename
|
dynamic_content {
|
||||||
dynamic_content {
|
link {
|
||||||
link {
|
linkConnection {
|
||||||
linkConnection {
|
edges {
|
||||||
edges {
|
node {
|
||||||
node {
|
__typename
|
||||||
__typename
|
...AccountPageRef
|
||||||
...AccountPageRef
|
...LoyaltyPageRef
|
||||||
...LoyaltyPageRef
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
... on AccountPageContentShortcuts {
|
}
|
||||||
__typename
|
... on AccountPageContentShortcuts {
|
||||||
|
__typename
|
||||||
|
shortcuts {
|
||||||
shortcuts {
|
shortcuts {
|
||||||
shortcuts {
|
linkConnection {
|
||||||
linkConnection {
|
edges {
|
||||||
edges {
|
node {
|
||||||
node {
|
__typename
|
||||||
__typename
|
...AccountPageRef
|
||||||
...AccountPageRef
|
...ContentPageRef
|
||||||
...ContentPageRef
|
...LoyaltyPageRef
|
||||||
...LoyaltyPageRef
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
system {
|
}
|
||||||
...System
|
system {
|
||||||
}
|
...System
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
query GetContentTypeUid($locale: String!, $url: String!) {
|
|
||||||
all_content_page(where: { url: $url }, locale: $locale) {
|
|
||||||
total
|
|
||||||
}
|
|
||||||
all_current_blocks_page(where: { url: $url }, locale: $locale) {
|
|
||||||
total
|
|
||||||
}
|
|
||||||
all_loyalty_page(where: { url: $url }, locale: $locale) {
|
|
||||||
total
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#import "../Fragments/Image.graphql"
|
|
||||||
|
|
||||||
query GetMyPagesLogo($locale: String!) {
|
|
||||||
all_header(limit: 1, locale: $locale) {
|
|
||||||
items {
|
|
||||||
logoConnection {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
...Image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,222 +8,218 @@
|
|||||||
#import "../Fragments/Refs/LoyaltyPage.graphql"
|
#import "../Fragments/Refs/LoyaltyPage.graphql"
|
||||||
#import "../Fragments/Refs/System.graphql"
|
#import "../Fragments/Refs/System.graphql"
|
||||||
|
|
||||||
query GetLoyaltyPage($locale: String!, $url: String!) {
|
query GetLoyaltyPage($locale: String!, $uid: String!) {
|
||||||
all_loyalty_page(where: { url: $url }, locale: $locale) {
|
loyalty_page(uid: $uid, locale: $locale) {
|
||||||
items {
|
blocks {
|
||||||
blocks {
|
__typename
|
||||||
__typename
|
... on LoyaltyPageBlocksShortcuts {
|
||||||
... on LoyaltyPageBlocksShortcuts {
|
shortcuts {
|
||||||
shortcuts {
|
|
||||||
title
|
|
||||||
preamble
|
|
||||||
shortcuts {
|
|
||||||
text
|
|
||||||
open_in_new_tab
|
|
||||||
linkConnection {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
...AccountPageLink
|
|
||||||
...LoyaltyPageLink
|
|
||||||
...ContentPageLink
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
... on LoyaltyPageBlocksDynamicContent {
|
|
||||||
dynamic_content {
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
component
|
|
||||||
link {
|
|
||||||
text
|
|
||||||
pageConnection {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
...ContentPageLink
|
|
||||||
...LoyaltyPageLink
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
... on LoyaltyPageBlocksCardGrid {
|
|
||||||
card_grid {
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
cards {
|
|
||||||
referenceConnection {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
...LoyaltyPageLink
|
|
||||||
...ContentPageLink
|
|
||||||
...AccountPageLink
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
open_in_new_tab
|
|
||||||
cta_text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
... on LoyaltyPageBlocksContent {
|
|
||||||
content {
|
|
||||||
content {
|
|
||||||
json
|
|
||||||
embedded_itemsConnection {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
...Image
|
|
||||||
...LoyaltyPageLink
|
|
||||||
...ContentPageLink
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
title
|
|
||||||
heading
|
|
||||||
sidebar {
|
|
||||||
__typename
|
|
||||||
... on LoyaltyPageSidebarJoinLoyaltyContact {
|
|
||||||
join_loyalty_contact {
|
|
||||||
title
|
|
||||||
preamble
|
|
||||||
contact {
|
|
||||||
... on LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact {
|
|
||||||
__typename
|
|
||||||
contact {
|
|
||||||
display_text
|
|
||||||
contact_field
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
... on LoyaltyPageSidebarContent {
|
|
||||||
content {
|
|
||||||
content {
|
|
||||||
json
|
|
||||||
embedded_itemsConnection {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
...Image
|
|
||||||
...LoyaltyPageLink
|
|
||||||
...ContentPageLink
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
web {
|
|
||||||
breadcrumbs {
|
|
||||||
title
|
title
|
||||||
parents {
|
preamble
|
||||||
href
|
shortcuts {
|
||||||
title
|
text
|
||||||
|
open_in_new_tab
|
||||||
|
linkConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
__typename
|
||||||
|
...AccountPageLink
|
||||||
|
...LoyaltyPageLink
|
||||||
|
...ContentPageLink
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
system {
|
... on LoyaltyPageBlocksDynamicContent {
|
||||||
uid
|
dynamic_content {
|
||||||
created_at
|
title
|
||||||
updated_at
|
subtitle
|
||||||
|
component
|
||||||
|
link {
|
||||||
|
text
|
||||||
|
pageConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...ContentPageLink
|
||||||
|
...LoyaltyPageLink
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
... on LoyaltyPageBlocksCardGrid {
|
||||||
|
card_grid {
|
||||||
|
title
|
||||||
|
subtitle
|
||||||
|
cards {
|
||||||
|
referenceConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
__typename
|
||||||
|
...LoyaltyPageLink
|
||||||
|
...ContentPageLink
|
||||||
|
...AccountPageLink
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
title
|
||||||
|
subtitle
|
||||||
|
open_in_new_tab
|
||||||
|
cta_text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on LoyaltyPageBlocksContent {
|
||||||
|
content {
|
||||||
|
content {
|
||||||
|
json
|
||||||
|
embedded_itemsConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
__typename
|
||||||
|
...Image
|
||||||
|
...LoyaltyPageLink
|
||||||
|
...ContentPageLink
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
title
|
||||||
|
heading
|
||||||
|
sidebar {
|
||||||
|
__typename
|
||||||
|
... on LoyaltyPageSidebarJoinLoyaltyContact {
|
||||||
|
join_loyalty_contact {
|
||||||
|
title
|
||||||
|
preamble
|
||||||
|
contact {
|
||||||
|
... on LoyaltyPageSidebarJoinLoyaltyContactBlockContactContact {
|
||||||
|
__typename
|
||||||
|
contact {
|
||||||
|
display_text
|
||||||
|
contact_field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on LoyaltyPageSidebarContent {
|
||||||
|
content {
|
||||||
|
content {
|
||||||
|
json
|
||||||
|
embedded_itemsConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
__typename
|
||||||
|
...Image
|
||||||
|
...LoyaltyPageLink
|
||||||
|
...ContentPageLink
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
web {
|
||||||
|
breadcrumbs {
|
||||||
|
title
|
||||||
|
parents {
|
||||||
|
href
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
system {
|
||||||
|
uid
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query GetLoyaltyPageRefs($locale: String!, $url: String!) {
|
query GetLoyaltyPageRefs($locale: String!, $uid: String!) {
|
||||||
all_loyalty_page(where: { url: $url }, locale: $locale) {
|
loyalty_page(locale: $locale, uid: $uid) {
|
||||||
items {
|
blocks {
|
||||||
blocks {
|
... on LoyaltyPageBlocksShortcuts {
|
||||||
... on LoyaltyPageBlocksShortcuts {
|
__typename
|
||||||
__typename
|
shortcuts {
|
||||||
shortcuts {
|
shortcuts {
|
||||||
shortcuts {
|
linkConnection {
|
||||||
linkConnection {
|
edges {
|
||||||
edges {
|
node {
|
||||||
node {
|
__typename
|
||||||
__typename
|
...AccountPageRef
|
||||||
...AccountPageRef
|
...ContentPageRef
|
||||||
...ContentPageRef
|
...LoyaltyPageRef
|
||||||
...LoyaltyPageRef
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
... on LoyaltyPageBlocksDynamicContent {
|
}
|
||||||
__typename
|
... on LoyaltyPageBlocksDynamicContent {
|
||||||
dynamic_content {
|
__typename
|
||||||
link {
|
dynamic_content {
|
||||||
pageConnection {
|
link {
|
||||||
edges {
|
pageConnection {
|
||||||
node {
|
edges {
|
||||||
__typename
|
node {
|
||||||
...ContentPageRef
|
__typename
|
||||||
...LoyaltyPageRef
|
...ContentPageRef
|
||||||
}
|
...LoyaltyPageRef
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
... on LoyaltyPageBlocksCardGrid {
|
}
|
||||||
__typename
|
... on LoyaltyPageBlocksCardGrid {
|
||||||
card_grid {
|
__typename
|
||||||
cards {
|
card_grid {
|
||||||
referenceConnection {
|
cards {
|
||||||
edges {
|
referenceConnection {
|
||||||
node {
|
edges {
|
||||||
__typename
|
node {
|
||||||
...AccountPageRef
|
__typename
|
||||||
...ContentPageRef
|
...AccountPageRef
|
||||||
...LoyaltyPageRef
|
...ContentPageRef
|
||||||
}
|
...LoyaltyPageRef
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
... on LoyaltyPageBlocksContent {
|
}
|
||||||
__typename
|
... on LoyaltyPageBlocksContent {
|
||||||
|
__typename
|
||||||
|
content {
|
||||||
content {
|
content {
|
||||||
content {
|
embedded_itemsConnection {
|
||||||
embedded_itemsConnection {
|
edges {
|
||||||
edges {
|
node {
|
||||||
node {
|
# No fragments used since we want to include __typename for each type to avoid fetching SystemAsset
|
||||||
# No fragments used since we want to include __typename for each type to avoid fetching SystemAsset
|
... on ContentPage {
|
||||||
... on ContentPage {
|
__typename
|
||||||
__typename
|
system {
|
||||||
system {
|
...System
|
||||||
...System
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
... on LoyaltyPage {
|
}
|
||||||
__typename
|
... on LoyaltyPage {
|
||||||
system {
|
__typename
|
||||||
...System
|
system {
|
||||||
}
|
...System
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,26 +228,26 @@ query GetLoyaltyPageRefs($locale: String!, $url: String!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sidebar {
|
}
|
||||||
... on LoyaltyPageSidebarContent {
|
sidebar {
|
||||||
__typename
|
... on LoyaltyPageSidebarContent {
|
||||||
|
__typename
|
||||||
|
content {
|
||||||
content {
|
content {
|
||||||
content {
|
embedded_itemsConnection {
|
||||||
embedded_itemsConnection {
|
edges {
|
||||||
edges {
|
node {
|
||||||
node {
|
# No fragments used since we want to include __typename for each type to avoid fetching SystemAsset
|
||||||
# No fragments used since we want to include __typename for each type to avoid fetching SystemAsset
|
... on ContentPage {
|
||||||
... on ContentPage {
|
__typename
|
||||||
__typename
|
system {
|
||||||
system {
|
...System
|
||||||
...System
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
... on LoyaltyPage {
|
}
|
||||||
__typename
|
... on LoyaltyPage {
|
||||||
system {
|
__typename
|
||||||
...System
|
system {
|
||||||
}
|
...System
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,9 +256,9 @@ query GetLoyaltyPageRefs($locale: String!, $url: String!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
system {
|
}
|
||||||
...System
|
system {
|
||||||
}
|
...System
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
lib/graphql/Query/ResolveEntry.graphql
Normal file
28
lib/graphql/Query/ResolveEntry.graphql
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#import "../Fragments/Refs/System.graphql"
|
||||||
|
|
||||||
|
query ResolveEntryByUrl($locale: String!, $url: String!) {
|
||||||
|
all_content_page(where: { url: $url }, locale: $locale) {
|
||||||
|
items {
|
||||||
|
system {
|
||||||
|
...System
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total
|
||||||
|
}
|
||||||
|
all_current_blocks_page(where: { url: $url }, locale: $locale) {
|
||||||
|
items {
|
||||||
|
system {
|
||||||
|
...System
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total
|
||||||
|
}
|
||||||
|
all_loyalty_page(where: { url: $url }, locale: $locale) {
|
||||||
|
items {
|
||||||
|
system {
|
||||||
|
...System
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,54 +24,48 @@ export async function request<T>(
|
|||||||
variables?: {},
|
variables?: {},
|
||||||
options?: Pick<RequestInit, "cache" | "next">
|
options?: Pick<RequestInit, "cache" | "next">
|
||||||
): Promise<Data<T>> {
|
): Promise<Data<T>> {
|
||||||
try {
|
if (options?.cache) {
|
||||||
if (options?.cache) {
|
client.requestConfig.cache = options.cache
|
||||||
client.requestConfig.cache = options.cache
|
}
|
||||||
}
|
if (options?.next) {
|
||||||
if (options?.next) {
|
client.requestConfig.next = options.next
|
||||||
client.requestConfig.next = options.next
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (env.PRINT_QUERY) {
|
if (env.PRINT_QUERY) {
|
||||||
const print = (await import("graphql/language/printer")).print
|
const { print } = await import("graphql")
|
||||||
const rawResponse = await client.rawRequest<T>(
|
const rawResponse = await client.rawRequest<T>(
|
||||||
print(query as DocumentNode),
|
print(query as DocumentNode),
|
||||||
variables,
|
variables,
|
||||||
{
|
{
|
||||||
access_token: env.CMS_ACCESS_TOKEN,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Send to Monitoring (Logging and Metrics)
|
|
||||||
*/
|
|
||||||
console.log({
|
|
||||||
complexityLimit: rawResponse.headers.get("x-query-complexity"),
|
|
||||||
})
|
|
||||||
console.log({
|
|
||||||
referenceDepth: rawResponse.headers.get("x-reference-depth"),
|
|
||||||
})
|
|
||||||
console.log({ resolverCost: rawResponse.headers.get("x-resolver-cost") })
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: rawResponse.data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await client.request<T>({
|
|
||||||
document: query,
|
|
||||||
requestHeaders: {
|
|
||||||
access_token: env.CMS_ACCESS_TOKEN,
|
access_token: env.CMS_ACCESS_TOKEN,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
}
|
||||||
variables,
|
)
|
||||||
})
|
|
||||||
|
|
||||||
return { data: response }
|
/**
|
||||||
} catch (error) {
|
* TODO: Send to Monitoring (Logging and Metrics)
|
||||||
console.info(`GraphQL Request Error`)
|
*/
|
||||||
console.error(error)
|
console.log({
|
||||||
throw new Error("Something went wrong")
|
complexityLimit: rawResponse.headers.get("x-query-complexity"),
|
||||||
|
})
|
||||||
|
console.log({
|
||||||
|
referenceDepth: rawResponse.headers.get("x-reference-depth"),
|
||||||
|
})
|
||||||
|
console.log({ resolverCost: rawResponse.headers.get("x-resolver-cost") })
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: rawResponse.data,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const response = await client.request<T>({
|
||||||
|
document: query,
|
||||||
|
requestHeaders: {
|
||||||
|
access_token: env.CMS_ACCESS_TOKEN,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
variables,
|
||||||
|
})
|
||||||
|
|
||||||
|
return { data: response }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ import { createTRPCReact } from "@trpc/react-query"
|
|||||||
|
|
||||||
import type { AppRouter } from "@/server"
|
import type { AppRouter } from "@/server"
|
||||||
|
|
||||||
export const trpc = createTRPCReact<AppRouter>({})
|
export const trpc = createTRPCReact<AppRouter>()
|
||||||
|
|||||||
@@ -1,9 +1,33 @@
|
|||||||
|
import { TRPCError } from "@trpc/server"
|
||||||
|
import { redirect } from "next/navigation"
|
||||||
|
|
||||||
|
import { Lang } from "@/constants/languages"
|
||||||
import { appRouter } from "@/server"
|
import { appRouter } from "@/server"
|
||||||
import { createContext } from "@/server/context"
|
import { createContext } from "@/server/context"
|
||||||
|
import { internalServerError } from "@/server/errors/next"
|
||||||
import { createCallerFactory } from "@/server/trpc"
|
import { createCallerFactory } from "@/server/trpc"
|
||||||
|
|
||||||
const createCaller = createCallerFactory(appRouter)
|
const createCaller = createCallerFactory(appRouter)
|
||||||
|
|
||||||
export function serverClient() {
|
export function serverClient() {
|
||||||
return createCaller(createContext())
|
return createCaller(createContext(), {
|
||||||
|
onError: ({ ctx, error, input, path, type }) => {
|
||||||
|
console.error(`Server Client error for ${type}: ${path}`)
|
||||||
|
if (input) {
|
||||||
|
console.error(`Received input:`)
|
||||||
|
console.error(input)
|
||||||
|
}
|
||||||
|
console.error(error)
|
||||||
|
|
||||||
|
if (error instanceof TRPCError) {
|
||||||
|
if (error.code === "UNAUTHORIZED") {
|
||||||
|
const lang = ctx?.lang || Lang.en
|
||||||
|
const pathname = ctx?.pathname || "/"
|
||||||
|
redirect(`/${lang}/login?redirectTo=${encodeURIComponent(pathname)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw internalServerError()
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,30 @@
|
|||||||
import { NextMiddleware } from "next/server"
|
import { NextMiddleware, NextResponse } from "next/server"
|
||||||
|
|
||||||
|
import { findLang } from "./constants/languages"
|
||||||
import * as authRequired from "./middlewares/authRequired"
|
import * as authRequired from "./middlewares/authRequired"
|
||||||
import * as cmsContent from "./middlewares/cmsContent"
|
import * as cmsContent from "./middlewares/cmsContent"
|
||||||
import * as currentWebLogin from "./middlewares/currentWebLogin"
|
import * as currentWebLogin from "./middlewares/currentWebLogin"
|
||||||
import * as ensureLang from "./middlewares/ensureLang"
|
|
||||||
import * as handleAuth from "./middlewares/handleAuth"
|
import * as handleAuth from "./middlewares/handleAuth"
|
||||||
import * as myPages from "./middlewares/myPages"
|
import * as myPages from "./middlewares/myPages"
|
||||||
import * as webView from "./middlewares/webView"
|
import * as webView from "./middlewares/webView"
|
||||||
|
|
||||||
export const middleware: NextMiddleware = async (request, event) => {
|
export const middleware: NextMiddleware = async (request, event) => {
|
||||||
|
const lang = findLang(request.nextUrl.pathname)
|
||||||
|
if (!lang) {
|
||||||
|
// Lang is required for all our middleware.
|
||||||
|
// Without it we shortcircuit early.
|
||||||
|
// We use middleware-error route because notFound() requires a root layout
|
||||||
|
// which we do not want. We can move to that once all Current stuff is gone.
|
||||||
|
return NextResponse.rewrite(
|
||||||
|
new URL(`/${lang}/middleware-error/404`, request.nextUrl),
|
||||||
|
{
|
||||||
|
status: 404,
|
||||||
|
statusText: "Not found",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const middlewares = [
|
const middlewares = [
|
||||||
ensureLang,
|
|
||||||
currentWebLogin,
|
currentWebLogin,
|
||||||
authRequired,
|
authRequired,
|
||||||
handleAuth,
|
handleAuth,
|
||||||
@@ -19,13 +33,48 @@ export const middleware: NextMiddleware = async (request, event) => {
|
|||||||
cmsContent,
|
cmsContent,
|
||||||
]
|
]
|
||||||
|
|
||||||
for (let i = 0; i < middlewares.length; ++i) {
|
try {
|
||||||
const middleware = middlewares[i]
|
for (let i = 0; i < middlewares.length; ++i) {
|
||||||
|
const middleware = middlewares[i]
|
||||||
|
|
||||||
if (middleware.matcher(request)) {
|
if (middleware.matcher(request)) {
|
||||||
return await middleware.middleware(request, event)
|
const result = await middleware.middleware(request, event)
|
||||||
|
const _continue = result?.headers.get("x-continue")
|
||||||
|
if (_continue) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use x-lang as either Akamai or Netlify use x-language as the users preferred language
|
||||||
|
result?.headers.set("x-lang", lang)
|
||||||
|
result?.headers.set("x-pathname", request.nextUrl.pathname)
|
||||||
|
result?.headers.set("x-url", request.nextUrl.href)
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NextResponse && e.status) {
|
||||||
|
return NextResponse.rewrite(
|
||||||
|
new URL(`/${lang}/middleware-error/${e.status}`, request.nextUrl),
|
||||||
|
{
|
||||||
|
status: e.status,
|
||||||
|
statusText: e.statusText,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(`Error in middleware`)
|
||||||
|
console.error(e)
|
||||||
|
return NextResponse.rewrite(
|
||||||
|
new URL(`/${lang}/middleware-error/500`, request.nextUrl),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
statusText: "Internal Server Error",
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Follow through with normal App router rules.
|
||||||
|
return NextResponse.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
// See "Matching Paths" below to learn more
|
// See "Matching Paths" below to learn more
|
||||||
|
|||||||
@@ -44,7 +44,11 @@ export const middleware = auth(async (request) => {
|
|||||||
const isLoggedIn = !!request.auth
|
const isLoggedIn = !!request.auth
|
||||||
|
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
return NextResponse.next()
|
const headers = new Headers(request.headers)
|
||||||
|
headers.set("x-continue", "1")
|
||||||
|
return NextResponse.next({
|
||||||
|
headers,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!env.PUBLIC_URL) {
|
if (!env.PUBLIC_URL) {
|
||||||
@@ -61,6 +65,7 @@ export const middleware = auth(async (request) => {
|
|||||||
"set-cookie",
|
"set-cookie",
|
||||||
`redirectTo=${encodeURIComponent(nextUrlClone.href)}; Path=/; HttpOnly; SameSite=Lax`
|
`redirectTo=${encodeURIComponent(nextUrlClone.href)}; Path=/; HttpOnly; SameSite=Lax`
|
||||||
)
|
)
|
||||||
|
|
||||||
const loginUrl = login[lang]
|
const loginUrl = login[lang]
|
||||||
return NextResponse.redirect(new URL(loginUrl, nextUrlClone), {
|
return NextResponse.redirect(new URL(loginUrl, nextUrlClone), {
|
||||||
headers,
|
headers,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { NextResponse } from "next/server"
|
import { NextResponse } from "next/server"
|
||||||
|
|
||||||
import { findLang } from "@/constants/languages"
|
import { findLang } from "@/constants/languages"
|
||||||
|
import { notFound } from "@/server/errors/next"
|
||||||
|
|
||||||
import { getContentTypeByPathName, PageTypeEnum } from "@/utils/contentType"
|
import { resolve as resolveEntry } from "@/utils/entry"
|
||||||
|
|
||||||
import type { NextMiddleware } from "next/server"
|
import type { NextMiddleware } from "next/server"
|
||||||
|
|
||||||
@@ -15,37 +16,65 @@ export const middleware: NextMiddleware = async (request) => {
|
|||||||
const pathNameWithoutLang = nextUrl.pathname.replace(`/${lang}`, "")
|
const pathNameWithoutLang = nextUrl.pathname.replace(`/${lang}`, "")
|
||||||
const searchParams = new URLSearchParams(request.nextUrl.searchParams)
|
const searchParams = new URLSearchParams(request.nextUrl.searchParams)
|
||||||
|
|
||||||
const contentType = await getContentTypeByPathName(pathNameWithoutLang, lang)
|
const { contentType, uid } = await resolveEntry(pathNameWithoutLang, lang)
|
||||||
|
|
||||||
|
if (!contentType || !uid) {
|
||||||
|
throw notFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCurrent = contentType ? contentType.indexOf("current") >= 0 : false
|
||||||
|
|
||||||
|
if (request.nextUrl.pathname.includes("/preview")) {
|
||||||
|
if (isCurrent) {
|
||||||
|
searchParams.set("uri", pathNameWithoutLang.replace("/preview", ""))
|
||||||
|
return NextResponse.rewrite(
|
||||||
|
new URL(`/${lang}/preview-current?${searchParams.toString()}`, nextUrl)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (request.nextUrl.pathname.includes("preview")) {
|
|
||||||
searchParams.set("uri", pathNameWithoutLang.replace("/preview", ""))
|
|
||||||
return NextResponse.rewrite(
|
return NextResponse.rewrite(
|
||||||
new URL(`/${lang}/preview-current?${searchParams.toString()}`, nextUrl)
|
new URL(
|
||||||
|
`/${lang}/preview/${contentType}/${uid}?${searchParams.toString()}`,
|
||||||
|
nextUrl
|
||||||
|
),
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
headers: new Headers({
|
||||||
|
"x-uid": uid,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
searchParams.set("uri", pathNameWithoutLang)
|
if (isCurrent) {
|
||||||
switch (contentType) {
|
return NextResponse.rewrite(
|
||||||
case PageTypeEnum.CurrentBlocksPage:
|
new URL(
|
||||||
return NextResponse.rewrite(
|
`/${lang}/current-content-page?${searchParams.toString()}`,
|
||||||
new URL(
|
nextUrl
|
||||||
`/${lang}/current-content-page?${searchParams.toString()}`,
|
|
||||||
nextUrl
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
case PageTypeEnum.LoyaltyPage:
|
)
|
||||||
return NextResponse.rewrite(
|
|
||||||
new URL(`/${lang}/loyalty-page?${searchParams.toString()}`, nextUrl)
|
|
||||||
)
|
|
||||||
// case PageTypeEnum.ContentPage:
|
|
||||||
// return NextResponse.rewrite(
|
|
||||||
// new URL(`/${lang}/content-page?${searchParams.toString()}`, nextUrl)
|
|
||||||
// )
|
|
||||||
default:
|
|
||||||
return NextResponse.next()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NextResponse.rewrite(
|
||||||
|
new URL(
|
||||||
|
`/${lang}/${contentType}/${uid}?${searchParams.toString()}`,
|
||||||
|
nextUrl
|
||||||
|
),
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
headers: new Headers({
|
||||||
|
"x-uid": uid,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const matcher: MiddlewareMatcher = (request) => {
|
export const matcher: MiddlewareMatcher = (request) => {
|
||||||
return true
|
// Do not process paths with file extension.
|
||||||
|
// Only looking for dot might be too brute force/give false positives.
|
||||||
|
// It works right now but adjust accordingly when new use cases/data emerges.
|
||||||
|
const lastPathnameSegment = request.nextUrl.pathname.split("/").pop()
|
||||||
|
return lastPathnameSegment?.indexOf(".") === -1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { findLang } from "@/constants/languages"
|
|
||||||
|
|
||||||
import type { NextMiddleware } from "next/server"
|
|
||||||
|
|
||||||
import type { MiddlewareMatcher } from "@/types/middleware"
|
|
||||||
|
|
||||||
export const middleware: NextMiddleware = () => {
|
|
||||||
return new NextResponse("Not found", { status: 404 })
|
|
||||||
}
|
|
||||||
|
|
||||||
export const matcher: MiddlewareMatcher = (request) => {
|
|
||||||
return !findLang(request.nextUrl.pathname)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import createJiti from "jiti"
|
import createJiti from "jiti"
|
||||||
|
|
||||||
import { login } from "./constants/routes/handleAuth.js"
|
import { login } from "./constants/routes/handleAuth.js"
|
||||||
import { myPages, overview } from "./constants/routes/myPages.js"
|
import { myPages } from "./constants/routes/myPages.js"
|
||||||
|
|
||||||
const jiti = createJiti(new URL(import.meta.url).pathname)
|
const jiti = createJiti(new URL(import.meta.url).pathname)
|
||||||
jiti("./env/server")
|
jiti("./env/server")
|
||||||
|
|||||||
88
package-lock.json
generated
88
package-lock.json
generated
@@ -28,7 +28,7 @@
|
|||||||
"graphql-request": "^6.1.0",
|
"graphql-request": "^6.1.0",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
"libphonenumber-js": "^1.10.60",
|
"libphonenumber-js": "^1.10.60",
|
||||||
"next": "^14.2.0",
|
"next": "^14.2.3",
|
||||||
"next-auth": "^5.0.0-beta.15",
|
"next-auth": "^5.0.0-beta.15",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
@@ -479,9 +479,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/env": {
|
"node_modules/@next/env": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz",
|
||||||
"integrity": "sha512-sk72qRfM1Q90XZWYRoJKu/UWlTgihrASiYw/scb15u+tyzcze3bOuJ/UV6TBOQEeUaxOkRqGeuGUdiiuxc5oqw=="
|
"integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA=="
|
||||||
},
|
},
|
||||||
"node_modules/@next/eslint-plugin-next": {
|
"node_modules/@next/eslint-plugin-next": {
|
||||||
"version": "14.1.4",
|
"version": "14.1.4",
|
||||||
@@ -493,9 +493,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-arm64": {
|
"node_modules/@next/swc-darwin-arm64": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz",
|
||||||
"integrity": "sha512-3iPgMhzbalizGwHNFUcGnDhFPSgVBHQ8aqSTAMxB5BvJG0oYrDf1WOJZlbXBgunOEj/8KMVbejEur/FpvFsgFQ==",
|
"integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -508,9 +508,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-x64": {
|
"node_modules/@next/swc-darwin-x64": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz",
|
||||||
"integrity": "sha512-x7Afi/jt0ZBRUZHTi49yyej4o8znfIMHO4RvThuoc0P+uli8Jd99y5GKjxoYunPKsXL09xBXEM1+OQy2xEL0Ag==",
|
"integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -523,9 +523,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz",
|
||||||
"integrity": "sha512-zbfPtkk7L41ODMJwSp5VbmPozPmMMQrzAc0HAUomVeVIIwlDGs/UCqLJvLNDt4jpWgc21SjjyIn762lNGrMaUA==",
|
"integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -538,9 +538,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-arm64-musl": {
|
"node_modules/@next/swc-linux-arm64-musl": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz",
|
||||||
"integrity": "sha512-wPbS3pI/JU16rm3XdLvvTmlsmm1nd+sBa2ohXgBZcShX4TgOjD4R+RqHKlI1cjo/jDZKXt6OxmcU0Iys0OC/yg==",
|
"integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -553,9 +553,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-x64-gnu": {
|
"node_modules/@next/swc-linux-x64-gnu": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz",
|
||||||
"integrity": "sha512-NqWOHqqq8iC9tuHvZxjQ2tX+jWy2X9y8NX2mcB4sj2bIccuCxbIZrU/ThFPZZPauygajZuVQ6zediejQHwZHwQ==",
|
"integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -568,9 +568,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-x64-musl": {
|
"node_modules/@next/swc-linux-x64-musl": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz",
|
||||||
"integrity": "sha512-lGepHhwb9sGhCcU7999+iK1ZZT+6rrIoVg40MP7DZski9GIZP80wORSbt5kJzh9v2x2ev2lxC6VgwMQT0PcgTA==",
|
"integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -583,9 +583,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz",
|
||||||
"integrity": "sha512-TZSh/48SfcLEQ4rD25VVn2kdIgUWmMflRX3OiyPwGNXn3NiyPqhqei/BaqCYXViIQ+6QsG9R0C8LftMqy8JPMA==",
|
"integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -598,9 +598,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz",
|
||||||
"integrity": "sha512-M0tBVNMEBJN2ZNQWlcekMn6pvLria7Sa2Fai5znm7CCJz4pP3lrvlSxhKdkCerk0D9E0bqx5yAo3o2Q7RrD4gA==",
|
"integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -613,9 +613,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-x64-msvc": {
|
"node_modules/@next/swc-win32-x64-msvc": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz",
|
||||||
"integrity": "sha512-a/20E/wtTJZ3Ykv3f/8F0l7TtgQa2LWHU2oNB9bsu0VjqGuGGHmm/q6waoUNQYTVPYrrlxxaHjJcDV6aiSTt/w==",
|
"integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -8132,11 +8132,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/next": {
|
"node_modules/next": {
|
||||||
"version": "14.2.2",
|
"version": "14.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/next/-/next-14.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz",
|
||||||
"integrity": "sha512-oGwUaa2bCs47FbuxWMpOoXtBMPYpvTPgdZr3UAo+pu7Ns00z9otmYpoeV1HEiYL06AlRQQIA/ypK526KjJfaxg==",
|
"integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/env": "14.2.2",
|
"@next/env": "14.2.3",
|
||||||
"@swc/helpers": "0.5.5",
|
"@swc/helpers": "0.5.5",
|
||||||
"busboy": "1.6.0",
|
"busboy": "1.6.0",
|
||||||
"caniuse-lite": "^1.0.30001579",
|
"caniuse-lite": "^1.0.30001579",
|
||||||
@@ -8151,15 +8151,15 @@
|
|||||||
"node": ">=18.17.0"
|
"node": ">=18.17.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@next/swc-darwin-arm64": "14.2.2",
|
"@next/swc-darwin-arm64": "14.2.3",
|
||||||
"@next/swc-darwin-x64": "14.2.2",
|
"@next/swc-darwin-x64": "14.2.3",
|
||||||
"@next/swc-linux-arm64-gnu": "14.2.2",
|
"@next/swc-linux-arm64-gnu": "14.2.3",
|
||||||
"@next/swc-linux-arm64-musl": "14.2.2",
|
"@next/swc-linux-arm64-musl": "14.2.3",
|
||||||
"@next/swc-linux-x64-gnu": "14.2.2",
|
"@next/swc-linux-x64-gnu": "14.2.3",
|
||||||
"@next/swc-linux-x64-musl": "14.2.2",
|
"@next/swc-linux-x64-musl": "14.2.3",
|
||||||
"@next/swc-win32-arm64-msvc": "14.2.2",
|
"@next/swc-win32-arm64-msvc": "14.2.3",
|
||||||
"@next/swc-win32-ia32-msvc": "14.2.2",
|
"@next/swc-win32-ia32-msvc": "14.2.3",
|
||||||
"@next/swc-win32-x64-msvc": "14.2.2"
|
"@next/swc-win32-x64-msvc": "14.2.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@opentelemetry/api": "^1.1.0",
|
"@opentelemetry/api": "^1.1.0",
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
"graphql-request": "^6.1.0",
|
"graphql-request": "^6.1.0",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
"libphonenumber-js": "^1.10.60",
|
"libphonenumber-js": "^1.10.60",
|
||||||
"next": "^14.2.0",
|
"next": "^14.2.3",
|
||||||
"next-auth": "^5.0.0-beta.15",
|
"next-auth": "^5.0.0-beta.15",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
|
import { headers } from "next/headers"
|
||||||
|
|
||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
import { auth } from "@/auth"
|
import { auth } from "@/auth"
|
||||||
|
|
||||||
type CreateContextOptions = {
|
type CreateContextOptions = {
|
||||||
auth: typeof auth
|
auth: typeof auth
|
||||||
|
lang: Lang
|
||||||
|
pathname: string
|
||||||
|
uid?: string | null
|
||||||
|
url: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Use this helper for:
|
/** Use this helper for:
|
||||||
@@ -11,6 +19,10 @@ type CreateContextOptions = {
|
|||||||
export function createContextInner(opts: CreateContextOptions) {
|
export function createContextInner(opts: CreateContextOptions) {
|
||||||
return {
|
return {
|
||||||
auth: opts.auth,
|
auth: opts.auth,
|
||||||
|
lang: opts.lang,
|
||||||
|
pathname: opts.pathname,
|
||||||
|
uid: opts.uid,
|
||||||
|
url: opts.url,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,8 +31,14 @@ export function createContextInner(opts: CreateContextOptions) {
|
|||||||
* @link https://trpc.io/docs/context
|
* @link https://trpc.io/docs/context
|
||||||
**/
|
**/
|
||||||
export function createContext() {
|
export function createContext() {
|
||||||
|
const h = headers()
|
||||||
|
|
||||||
return createContextInner({
|
return createContextInner({
|
||||||
auth,
|
auth,
|
||||||
|
lang: h.get("x-lang") as Lang,
|
||||||
|
pathname: h.get("x-pathname")!,
|
||||||
|
uid: h.get("x-uid"),
|
||||||
|
url: h.get("x-url")!,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,20 @@ import { NextResponse } from "next/server"
|
|||||||
export function badRequest(body: unknown | string = "Bad request") {
|
export function badRequest(body: unknown | string = "Bad request") {
|
||||||
const resInit = {
|
const resInit = {
|
||||||
status: 400,
|
status: 400,
|
||||||
|
statusText: "Bad request",
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof body === "string") {
|
||||||
|
return new NextResponse(body, resInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json(body, resInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notFound(body: unknown | string = "Not found") {
|
||||||
|
const resInit = {
|
||||||
|
status: 404,
|
||||||
|
statusText: "Not found",
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof body === "string") {
|
if (typeof body === "string") {
|
||||||
@@ -17,6 +31,7 @@ export function internalServerError(
|
|||||||
) {
|
) {
|
||||||
const resInit = {
|
const resInit = {
|
||||||
status: 500,
|
status: 500,
|
||||||
|
statusText: "Internal Server Error",
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof body === "string") {
|
if (typeof body === "string") {
|
||||||
|
|||||||
@@ -1,35 +1,41 @@
|
|||||||
import { TRPCError } from "@trpc/server"
|
import { TRPCError } from "@trpc/server"
|
||||||
import {
|
|
||||||
TRPC_ERROR_CODES_BY_KEY,
|
|
||||||
TRPC_ERROR_CODES_BY_NUMBER,
|
|
||||||
} from "@trpc/server/rpc"
|
|
||||||
|
|
||||||
export function unauthorizedError() {
|
export function unauthorizedError(cause?: unknown) {
|
||||||
return new TRPCError({
|
return new TRPCError({
|
||||||
code: TRPC_ERROR_CODES_BY_NUMBER[TRPC_ERROR_CODES_BY_KEY.UNAUTHORIZED],
|
code: "UNAUTHORIZED",
|
||||||
message: `Authorization required!`,
|
message: `Unauthorized`,
|
||||||
|
cause,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function forbiddenError() {
|
export function forbiddenError(cause?: unknown) {
|
||||||
return new TRPCError({
|
return new TRPCError({
|
||||||
code: TRPC_ERROR_CODES_BY_NUMBER[TRPC_ERROR_CODES_BY_KEY.FORBIDDEN],
|
code: "FORBIDDEN",
|
||||||
message: `You do not have permission!`,
|
message: `Forbidden`,
|
||||||
|
cause,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function badRequestError(msg = "Bad request!") {
|
export function badRequestError(cause?: unknown) {
|
||||||
return new TRPCError({
|
return new TRPCError({
|
||||||
code: TRPC_ERROR_CODES_BY_NUMBER[TRPC_ERROR_CODES_BY_KEY.BAD_REQUEST],
|
code: "BAD_REQUEST",
|
||||||
message: msg,
|
message: `Bad request`,
|
||||||
|
cause,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function internalServerError() {
|
export function notFound(cause?: unknown) {
|
||||||
return new TRPCError({
|
return new TRPCError({
|
||||||
code: TRPC_ERROR_CODES_BY_NUMBER[
|
code: "NOT_FOUND",
|
||||||
TRPC_ERROR_CODES_BY_KEY.INTERNAL_SERVER_ERROR
|
message: `Not found`,
|
||||||
],
|
cause,
|
||||||
message: `Internal Server Error!`,
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function internalServerError(cause?: unknown) {
|
||||||
|
return new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: `Internal Server Error`,
|
||||||
|
cause,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { router } from "./trpc"
|
|
||||||
|
|
||||||
/** Routers */
|
/** Routers */
|
||||||
import { contentstackRouter } from "./routers/contentstack"
|
import { contentstackRouter } from "./routers/contentstack"
|
||||||
import { userRouter } from "./routers/user"
|
|
||||||
import { loyaltyRouter } from "./routers/loyalty"
|
import { loyaltyRouter } from "./routers/loyalty"
|
||||||
|
import { userRouter } from "./routers/user"
|
||||||
|
import { router } from "./trpc"
|
||||||
|
|
||||||
export const appRouter = router({
|
export const appRouter = router({
|
||||||
contentstack: contentstackRouter,
|
contentstack: contentstackRouter,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
import { mergeRouters } from "@/server/trpc"
|
import { mergeRouters } from "@/server/trpc"
|
||||||
|
|
||||||
import { accountPageQueryRouter } from "./query"
|
import { accountPageQueryRouter } from "./query"
|
||||||
|
|
||||||
export const accountPageRouter = mergeRouters(accountPageQueryRouter)
|
export const accountPageRouter = mergeRouters(accountPageQueryRouter)
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import { z } from "zod"
|
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
|
||||||
|
|
||||||
export const getAccountPageInput = z.object({
|
|
||||||
url: z.string(),
|
|
||||||
lang: z.nativeEnum(Lang),
|
|
||||||
})
|
|
||||||
@@ -120,24 +120,18 @@ const accountPageContentItem = z.discriminatedUnion("__typename", [
|
|||||||
])
|
])
|
||||||
|
|
||||||
export const validateAccountPageSchema = z.object({
|
export const validateAccountPageSchema = z.object({
|
||||||
all_account_page: z.object({
|
account_page: z.object({
|
||||||
items: z.array(
|
url: z.string(),
|
||||||
z.object({
|
title: z.string(),
|
||||||
url: z.string(),
|
content: z.array(accountPageContentItem),
|
||||||
title: z.string(),
|
|
||||||
content: z.array(accountPageContentItem),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
type AccountPageDataRaw = z.infer<typeof validateAccountPageSchema>
|
export type AccountPageDataRaw = z.infer<typeof validateAccountPageSchema>
|
||||||
|
|
||||||
type AccountPageRaw = AccountPageDataRaw["all_account_page"]["items"][0]
|
type AccountPageRaw = AccountPageDataRaw["account_page"]
|
||||||
|
|
||||||
export type AccountPage = Omit<AccountPageRaw, "content"> & {
|
export type AccountPage = Omit<AccountPageRaw, "content"> & {
|
||||||
url: string
|
|
||||||
title: string
|
|
||||||
content: AccountPageContentItem[]
|
content: AccountPageContentItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,16 +176,12 @@ const accountPageContentItemRefs = z.discriminatedUnion("__typename", [
|
|||||||
])
|
])
|
||||||
|
|
||||||
export const validateAccountPageRefsSchema = z.object({
|
export const validateAccountPageRefsSchema = z.object({
|
||||||
all_account_page: z.object({
|
account_page: z.object({
|
||||||
items: z.array(
|
content: z.array(accountPageContentItemRefs),
|
||||||
z.object({
|
system: z.object({
|
||||||
content: z.array(accountPageContentItemRefs),
|
content_type_uid: z.string(),
|
||||||
system: z.object({
|
uid: z.string(),
|
||||||
content_type_uid: z.string(),
|
}),
|
||||||
uid: z.string(),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
GetAccountPageRefs,
|
GetAccountPageRefs,
|
||||||
} from "@/lib/graphql/Query/AccountPage.graphql"
|
} from "@/lib/graphql/Query/AccountPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { badRequestError, internalServerError } from "@/server/errors/trpc"
|
import { internalServerError, notFound } from "@/server/errors/trpc"
|
||||||
import { publicProcedure, router } from "@/server/trpc"
|
import { contentstackProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
generateRefsResponseTag,
|
generateRefsResponseTag,
|
||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
} from "@/utils/generateTag"
|
} from "@/utils/generateTag"
|
||||||
|
|
||||||
import { removeEmptyObjects } from "../../utils"
|
import { removeEmptyObjects } from "../../utils"
|
||||||
import { getAccountPageInput } from "./input"
|
|
||||||
import {
|
import {
|
||||||
type AccountPage,
|
type AccountPage,
|
||||||
AccountPageRefsDataRaw,
|
AccountPageRefsDataRaw,
|
||||||
@@ -28,122 +27,108 @@ import { Edges } from "@/types/requests/utils/edges"
|
|||||||
import { RTEDocument } from "@/types/rte/node"
|
import { RTEDocument } from "@/types/rte/node"
|
||||||
|
|
||||||
export const accountPageQueryRouter = router({
|
export const accountPageQueryRouter = router({
|
||||||
get: publicProcedure.input(getAccountPageInput).query(async ({ input }) => {
|
get: contentstackProcedure.query(async ({ ctx }) => {
|
||||||
try {
|
const { lang, uid } = ctx
|
||||||
const { lang, url } = input
|
|
||||||
|
|
||||||
const refsResponse = await request<AccountPageRefsDataRaw>(
|
const refsResponse = await request<AccountPageRefsDataRaw>(
|
||||||
GetAccountPageRefs,
|
GetAccountPageRefs,
|
||||||
{
|
{
|
||||||
locale: lang,
|
locale: lang,
|
||||||
url,
|
uid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
next: {
|
||||||
|
tags: [generateRefsResponseTag(lang, uid)],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
next: {
|
|
||||||
tags: [generateRefsResponseTag(lang, "account_page")],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!refsResponse.data) {
|
|
||||||
console.error("Bad response for `GetAccountPageRefs`")
|
|
||||||
console.error({ refsResponse })
|
|
||||||
throw internalServerError()
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Remove empty objects from a fetched content type. Needed since
|
if (!refsResponse.data) {
|
||||||
// Contentstack returns empty objects for all non queried blocks in modular blocks.
|
throw notFound(refsResponse)
|
||||||
// This is an ongoing support case in Contentstack, ticker number #00031579
|
|
||||||
const cleanedData = removeEmptyObjects(refsResponse.data)
|
|
||||||
|
|
||||||
const validatedAccountPageRefs =
|
|
||||||
validateAccountPageRefsSchema.safeParse(cleanedData)
|
|
||||||
if (!validatedAccountPageRefs.success) {
|
|
||||||
console.error("Bad validation for `GetAccountPageRefs`")
|
|
||||||
console.error(validatedAccountPageRefs.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const connections = getConnections(validatedAccountPageRefs.data)
|
|
||||||
|
|
||||||
const tags = generateTags(lang, connections)
|
|
||||||
|
|
||||||
tags.push(
|
|
||||||
generateTag(
|
|
||||||
lang,
|
|
||||||
validatedAccountPageRefs.data.all_account_page.items[0].system.uid
|
|
||||||
)
|
|
||||||
)
|
|
||||||
const response = await request<AccountPageRefsDataRaw>(
|
|
||||||
GetAccountPage,
|
|
||||||
{
|
|
||||||
locale: lang,
|
|
||||||
url,
|
|
||||||
},
|
|
||||||
{ next: { tags } }
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!response.data) {
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const validatedAccountPage = validateAccountPageSchema.safeParse(
|
|
||||||
response.data
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!validatedAccountPage.success) {
|
|
||||||
console.info(`Get Account Page Validation Error`)
|
|
||||||
console.error(validatedAccountPage.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
// TODO: Make returned data nicer
|
|
||||||
const content =
|
|
||||||
validatedAccountPage.data.all_account_page.items[0].content.map(
|
|
||||||
(block) => {
|
|
||||||
switch (block.__typename) {
|
|
||||||
case ContentEntries.AccountPageContentDynamicContent:
|
|
||||||
return block
|
|
||||||
case ContentEntries.AccountPageContentShortcuts:
|
|
||||||
return {
|
|
||||||
...block,
|
|
||||||
shortcuts: {
|
|
||||||
...block.shortcuts,
|
|
||||||
shortcuts: block.shortcuts.shortcuts.map((shortcut) => ({
|
|
||||||
text: shortcut.text,
|
|
||||||
openInNewTab: shortcut.open_in_new_tab,
|
|
||||||
...shortcut.linkConnection.edges[0].node,
|
|
||||||
url:
|
|
||||||
shortcut.linkConnection.edges[0].node.original_url ||
|
|
||||||
`/${shortcut.linkConnection.edges[0].node.system.locale}${shortcut.linkConnection.edges[0].node.url}`,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
case ContentEntries.AccountPageContentTextContent:
|
|
||||||
return {
|
|
||||||
...block,
|
|
||||||
text_content: {
|
|
||||||
content: {
|
|
||||||
json: block.text_content.content.json as RTEDocument,
|
|
||||||
embedded_itemsConnection: block.text_content.content
|
|
||||||
.embedded_itemsConnection as Edges<Embeds>,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const accountPage = {
|
|
||||||
...validatedAccountPage.data.all_account_page.items[0],
|
|
||||||
content,
|
|
||||||
} as AccountPage
|
|
||||||
return accountPage
|
|
||||||
} catch (error) {
|
|
||||||
console.info(`Get Account Page Overview Error`)
|
|
||||||
console.error(error)
|
|
||||||
throw internalServerError()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove empty objects from a fetched content type. Needed since
|
||||||
|
// Contentstack returns empty objects for all non queried blocks in modular blocks.
|
||||||
|
// This is an ongoing support case in Contentstack, ticker number #00031579
|
||||||
|
const cleanedData = removeEmptyObjects(refsResponse.data)
|
||||||
|
|
||||||
|
const validatedAccountPageRefs =
|
||||||
|
validateAccountPageRefsSchema.safeParse(cleanedData)
|
||||||
|
if (!validatedAccountPageRefs.success) {
|
||||||
|
throw internalServerError(validatedAccountPageRefs.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const connections = getConnections(validatedAccountPageRefs.data)
|
||||||
|
|
||||||
|
const tags = [
|
||||||
|
generateTags(lang, connections),
|
||||||
|
generateTag(lang, validatedAccountPageRefs.data.account_page.system.uid),
|
||||||
|
].flat()
|
||||||
|
|
||||||
|
const response = await request<AccountPageRefsDataRaw>(
|
||||||
|
GetAccountPage,
|
||||||
|
{
|
||||||
|
locale: lang,
|
||||||
|
uid,
|
||||||
|
},
|
||||||
|
{ next: { tags } }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!response.data) {
|
||||||
|
throw notFound(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatedAccountPage = validateAccountPageSchema.safeParse(
|
||||||
|
response.data
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!validatedAccountPage.success) {
|
||||||
|
throw internalServerError(validatedAccountPage.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make returned data nicer
|
||||||
|
const content = validatedAccountPage.data.account_page.content.map(
|
||||||
|
(block) => {
|
||||||
|
switch (block.__typename) {
|
||||||
|
case ContentEntries.AccountPageContentDynamicContent:
|
||||||
|
return block
|
||||||
|
case ContentEntries.AccountPageContentShortcuts:
|
||||||
|
return {
|
||||||
|
...block,
|
||||||
|
shortcuts: {
|
||||||
|
...block.shortcuts,
|
||||||
|
shortcuts: block.shortcuts.shortcuts.map((shortcut) => ({
|
||||||
|
text: shortcut.text,
|
||||||
|
openInNewTab: shortcut.open_in_new_tab,
|
||||||
|
...shortcut.linkConnection.edges[0].node,
|
||||||
|
url:
|
||||||
|
shortcut.linkConnection.edges[0].node.original_url ||
|
||||||
|
`/${shortcut.linkConnection.edges[0].node.system.locale}${shortcut.linkConnection.edges[0].node.url}`,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case ContentEntries.AccountPageContentTextContent:
|
||||||
|
return {
|
||||||
|
...block,
|
||||||
|
text_content: {
|
||||||
|
content: {
|
||||||
|
json: block.text_content.content.json as RTEDocument,
|
||||||
|
embedded_itemsConnection: block.text_content.content
|
||||||
|
.embedded_itemsConnection as Edges<Embeds>,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const accountPage = {
|
||||||
|
...validatedAccountPage.data.account_page,
|
||||||
|
content,
|
||||||
|
} as AccountPage
|
||||||
|
|
||||||
|
return accountPage
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,28 +6,26 @@ import type { NodeRefs } from "@/types/requests/utils/refs"
|
|||||||
|
|
||||||
export function getConnections(refs: AccountPageRefsDataRaw) {
|
export function getConnections(refs: AccountPageRefsDataRaw) {
|
||||||
const connections: Edges<NodeRefs>[] = []
|
const connections: Edges<NodeRefs>[] = []
|
||||||
refs.all_account_page.items.forEach((ref) => {
|
if (refs.account_page.content) {
|
||||||
if (ref.content) {
|
refs.account_page.content.forEach((item) => {
|
||||||
ref.content.forEach((item) => {
|
switch (item.__typename) {
|
||||||
switch (item.__typename) {
|
case ContentEntries.AccountPageContentShortcuts: {
|
||||||
case ContentEntries.AccountPageContentShortcuts: {
|
item.shortcuts.shortcuts.forEach((shortcut) => {
|
||||||
item.shortcuts.shortcuts.forEach((shortcut) => {
|
if (shortcut.linkConnection.edges.length) {
|
||||||
if (shortcut.linkConnection.edges.length) {
|
connections.push(shortcut.linkConnection)
|
||||||
connections.push(shortcut.linkConnection)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case ContentEntries.AccountPageContentDynamicContent: {
|
|
||||||
if (item.dynamic_content.link.linkConnection.edges.length) {
|
|
||||||
connections.push(item.dynamic_content.link.linkConnection)
|
|
||||||
}
|
}
|
||||||
break
|
})
|
||||||
}
|
break
|
||||||
}
|
}
|
||||||
})
|
case ContentEntries.AccountPageContentDynamicContent: {
|
||||||
}
|
if (item.dynamic_content.link.linkConnection.edges.length) {
|
||||||
})
|
connections.push(item.dynamic_content.link.linkConnection)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return connections
|
return connections
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import { z } from "zod"
|
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
|
||||||
|
|
||||||
export const getBreadcrumbsInput = z.object({
|
|
||||||
href: z.string().min(1, { message: "href is required" }),
|
|
||||||
locale: z.nativeEnum(Lang),
|
|
||||||
})
|
|
||||||
@@ -3,8 +3,12 @@ import {
|
|||||||
GetMyPagesBreadcrumbsRefs,
|
GetMyPagesBreadcrumbsRefs,
|
||||||
} from "@/lib/graphql/Query/BreadcrumbsMyPages.graphql"
|
} from "@/lib/graphql/Query/BreadcrumbsMyPages.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { badRequestError, internalServerError } from "@/server/errors/trpc"
|
import {
|
||||||
import { publicProcedure, router } from "@/server/trpc"
|
badRequestError,
|
||||||
|
internalServerError,
|
||||||
|
notFound,
|
||||||
|
} from "@/server/errors/trpc"
|
||||||
|
import { contentstackProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
generateRefsResponseTag,
|
generateRefsResponseTag,
|
||||||
@@ -13,7 +17,6 @@ import {
|
|||||||
} from "@/utils/generateTag"
|
} from "@/utils/generateTag"
|
||||||
import { removeMultipleSlashes } from "@/utils/url"
|
import { removeMultipleSlashes } from "@/utils/url"
|
||||||
|
|
||||||
import { getBreadcrumbsInput } from "./input"
|
|
||||||
import {
|
import {
|
||||||
getBreadcrumbsSchema,
|
getBreadcrumbsSchema,
|
||||||
validateBreadcrumbsConstenstackSchema,
|
validateBreadcrumbsConstenstackSchema,
|
||||||
@@ -27,98 +30,81 @@ import type {
|
|||||||
} from "@/types/requests/myPages/breadcrumbs"
|
} from "@/types/requests/myPages/breadcrumbs"
|
||||||
|
|
||||||
export const breadcrumbsQueryRouter = router({
|
export const breadcrumbsQueryRouter = router({
|
||||||
get: publicProcedure.input(getBreadcrumbsInput).query(async ({ input }) => {
|
get: contentstackProcedure.query(async ({ ctx }) => {
|
||||||
try {
|
const refsResponse = await request<GetMyPagesBreadcrumbsRefsData>(
|
||||||
const refsResponse = await request<GetMyPagesBreadcrumbsRefsData>(
|
GetMyPagesBreadcrumbsRefs,
|
||||||
GetMyPagesBreadcrumbsRefs,
|
{ locale: ctx.lang, url: ctx.pathname },
|
||||||
{ locale: input.locale, url: input.href },
|
{
|
||||||
{
|
next: {
|
||||||
next: {
|
tags: [generateRefsResponseTag(ctx.lang, ctx.pathname, affix)],
|
||||||
tags: [generateRefsResponseTag(input.locale, input.href, affix)],
|
},
|
||||||
},
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!refsResponse.data) {
|
||||||
|
throw notFound(refsResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatedRefsData =
|
||||||
|
validateBreadcrumbsRefsConstenstackSchema.safeParse(refsResponse.data)
|
||||||
|
if (!validatedRefsData.success) {
|
||||||
|
throw internalServerError(validatedRefsData.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const connections = getConnections(validatedRefsData.data)
|
||||||
|
const tags = generateTags(ctx.lang, connections)
|
||||||
|
const page = validatedRefsData.data.all_account_page.items[0]
|
||||||
|
tags.push(generateTag(ctx.lang, page.system.uid, affix))
|
||||||
|
|
||||||
|
const response = await request<GetMyPagesBreadcrumbsData>(
|
||||||
|
GetMyPagesBreadcrumbs,
|
||||||
|
{ locale: ctx.lang, url: ctx.pathname },
|
||||||
|
{ next: { tags } }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!response.data) {
|
||||||
|
throw notFound(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatedBreadcrumbsData =
|
||||||
|
validateBreadcrumbsConstenstackSchema.safeParse(response.data)
|
||||||
|
|
||||||
|
if (!validatedBreadcrumbsData.success) {
|
||||||
|
throw internalServerError(validatedBreadcrumbsData.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentBreadcrumbs =
|
||||||
|
validatedBreadcrumbsData.data.all_account_page.items[0].breadcrumbs.parentsConnection.edges.map(
|
||||||
|
(breadcrumb) => {
|
||||||
|
return {
|
||||||
|
href: removeMultipleSlashes(
|
||||||
|
`/${breadcrumb.node.system.locale}/${breadcrumb.node.url}`
|
||||||
|
),
|
||||||
|
title: breadcrumb.node.breadcrumbs.title,
|
||||||
|
uid: breadcrumb.node.system.uid,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!refsResponse.data) {
|
const pageBreadcrumb =
|
||||||
console.error("Bad response for `GetMyPagesBreadcrumbsRefs`")
|
validatedBreadcrumbsData.data.all_account_page.items.map((breadcrumb) => {
|
||||||
console.error({ refsResponse })
|
return {
|
||||||
throw internalServerError()
|
title: breadcrumb.breadcrumbs.title,
|
||||||
}
|
uid: breadcrumb.system.uid,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const validatedRefsData =
|
const breadcrumbs = [
|
||||||
validateBreadcrumbsRefsConstenstackSchema.safeParse(refsResponse.data)
|
homeBreadcrumbs[ctx.lang],
|
||||||
if (!validatedRefsData.success) {
|
parentBreadcrumbs,
|
||||||
console.info("Bad validation for `GetMyPagesBreadcrumbsRefs`")
|
pageBreadcrumb,
|
||||||
console.error(validatedRefsData.error)
|
].flat()
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const connections = getConnections(validatedRefsData.data)
|
const validatedBreadcrumbs = getBreadcrumbsSchema.safeParse(breadcrumbs)
|
||||||
const tags = generateTags(input.locale, connections)
|
if (!validatedBreadcrumbs.success) {
|
||||||
const page = validatedRefsData.data.all_account_page.items[0]
|
throw internalServerError(validatedBreadcrumbs.error)
|
||||||
tags.push(generateTag(input.locale, page.system.uid, affix))
|
|
||||||
|
|
||||||
const response = await request<GetMyPagesBreadcrumbsData>(
|
|
||||||
GetMyPagesBreadcrumbs,
|
|
||||||
{ locale: input.locale, url: input.href },
|
|
||||||
{ next: { tags } }
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!response.data) {
|
|
||||||
console.error("Bad response for `GetMyPagesBreadcrumbs`")
|
|
||||||
console.error({ input })
|
|
||||||
console.error({ response })
|
|
||||||
throw internalServerError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const validatedBreadcrumbsData =
|
|
||||||
validateBreadcrumbsConstenstackSchema.safeParse(response.data)
|
|
||||||
if (!validatedBreadcrumbsData.success) {
|
|
||||||
console.error("Bad validation for `GetMyPagesBreadcrumbs`")
|
|
||||||
console.error(validatedBreadcrumbsData.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentBreadcrumbs =
|
|
||||||
validatedBreadcrumbsData.data.all_account_page.items[0].breadcrumbs.parentsConnection.edges.map(
|
|
||||||
(breadcrumb) => {
|
|
||||||
return {
|
|
||||||
href: removeMultipleSlashes(
|
|
||||||
`/${breadcrumb.node.system.locale}/${breadcrumb.node.url}`
|
|
||||||
),
|
|
||||||
title: breadcrumb.node.breadcrumbs.title,
|
|
||||||
uid: breadcrumb.node.system.uid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const pageBreadcrumb =
|
|
||||||
validatedBreadcrumbsData.data.all_account_page.items.map(
|
|
||||||
(breadcrumb) => {
|
|
||||||
return {
|
|
||||||
title: breadcrumb.breadcrumbs.title,
|
|
||||||
uid: breadcrumb.system.uid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const breadcrumbs = [
|
|
||||||
homeBreadcrumbs[input.locale],
|
|
||||||
parentBreadcrumbs,
|
|
||||||
pageBreadcrumb,
|
|
||||||
].flat()
|
|
||||||
const validatedBreadcrumbs = getBreadcrumbsSchema.safeParse(breadcrumbs)
|
|
||||||
if (!validatedBreadcrumbs.success) {
|
|
||||||
console.info("Bad validation for `validatedBreadcrumbs`")
|
|
||||||
console.error(validatedBreadcrumbs.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
return validatedBreadcrumbs.data
|
|
||||||
} catch (error) {
|
|
||||||
console.info(`Get My Pages Breadcrumbs Error`)
|
|
||||||
console.error(error)
|
|
||||||
throw internalServerError()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return validatedBreadcrumbs.data
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import { z } from "zod"
|
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
|
||||||
|
|
||||||
export const getConfigInput = z.object({ lang: z.nativeEnum(Lang) })
|
|
||||||
@@ -1,34 +1,30 @@
|
|||||||
import { GetContactConfig } from "@/lib/graphql/Query/ContactConfig.graphql"
|
import { GetContactConfig } from "@/lib/graphql/Query/ContactConfig.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { badRequestError } from "@/server/errors/trpc"
|
import { internalServerError, notFound } from "@/server/errors/trpc"
|
||||||
import { publicProcedure, router } from "@/server/trpc"
|
import { contentstackProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import { getConfigInput } from "./input"
|
|
||||||
import { type ContactConfigData, validateContactConfigSchema } from "./output"
|
import { type ContactConfigData, validateContactConfigSchema } from "./output"
|
||||||
|
|
||||||
export const contactConfigQueryRouter = router({
|
export const contactConfigQueryRouter = router({
|
||||||
get: publicProcedure.input(getConfigInput).query(async ({ input }) => {
|
get: contentstackProcedure.query(async ({ ctx }) => {
|
||||||
try {
|
const { lang } = ctx
|
||||||
const contactConfig = await request<ContactConfigData>(GetContactConfig, {
|
|
||||||
locale: input.lang,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!contactConfig.data) {
|
const response = await request<ContactConfigData>(GetContactConfig, {
|
||||||
throw badRequestError()
|
locale: lang,
|
||||||
}
|
})
|
||||||
|
|
||||||
const validatedContactConfigConfig =
|
if (!response.data) {
|
||||||
validateContactConfigSchema.safeParse(contactConfig.data)
|
throw notFound(response)
|
||||||
|
|
||||||
if (!validatedContactConfigConfig.success) {
|
|
||||||
console.error(validatedContactConfigConfig.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
return validatedContactConfigConfig.data.all_contact_config.items[0]
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validatedContactConfigConfig = validateContactConfigSchema.safeParse(
|
||||||
|
response.data
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!validatedContactConfigConfig.success) {
|
||||||
|
throw internalServerError(validatedContactConfigConfig.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return validatedContactConfigConfig.data.all_contact_config.items[0]
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { z } from "zod"
|
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
|
||||||
|
|
||||||
const langs = Object.keys(Lang) as [keyof typeof Lang]
|
|
||||||
|
|
||||||
export const getLoyaltyPageInput = z.object({
|
|
||||||
href: z.string().min(1, { message: "href is required" }),
|
|
||||||
locale: z.nativeEnum(Lang),
|
|
||||||
})
|
|
||||||
@@ -168,16 +168,12 @@ const loyaltyPageSidebarItem = z.discriminatedUnion("__typename", [
|
|||||||
])
|
])
|
||||||
|
|
||||||
export const validateLoyaltyPageSchema = z.object({
|
export const validateLoyaltyPageSchema = z.object({
|
||||||
all_loyalty_page: z.object({
|
loyalty_page: z.object({
|
||||||
items: z.array(
|
title: z.string(),
|
||||||
z.object({
|
heading: z.string().nullable(),
|
||||||
title: z.string(),
|
blocks: z.array(loyaltyPageBlockItem).nullable(),
|
||||||
heading: z.string().nullable(),
|
sidebar: z.array(loyaltyPageSidebarItem).nullable(),
|
||||||
blocks: z.array(loyaltyPageBlockItem).nullable(),
|
system: z.object({ uid: z.string() }),
|
||||||
sidebar: z.array(loyaltyPageSidebarItem).nullable(),
|
|
||||||
system: z.object({ uid: z.string() }),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -257,7 +253,7 @@ export type Sidebar = JoinLoyaltyContact | RteSidebarContent
|
|||||||
|
|
||||||
export type LoyaltyPageDataRaw = z.infer<typeof validateLoyaltyPageSchema>
|
export type LoyaltyPageDataRaw = z.infer<typeof validateLoyaltyPageSchema>
|
||||||
|
|
||||||
type LoyaltyPageRaw = LoyaltyPageDataRaw["all_loyalty_page"]["items"][0]
|
type LoyaltyPageRaw = LoyaltyPageDataRaw["loyalty_page"]
|
||||||
|
|
||||||
export type LoyaltyPage = Omit<LoyaltyPageRaw, "blocks" | "sidebar"> & {
|
export type LoyaltyPage = Omit<LoyaltyPageRaw, "blocks" | "sidebar"> & {
|
||||||
blocks: Block[]
|
blocks: Block[]
|
||||||
@@ -342,17 +338,13 @@ const loyaltyPageSidebarRefsItem = z.discriminatedUnion("__typename", [
|
|||||||
])
|
])
|
||||||
|
|
||||||
export const validateLoyaltyPageRefsSchema = z.object({
|
export const validateLoyaltyPageRefsSchema = z.object({
|
||||||
all_loyalty_page: z.object({
|
loyalty_page: z.object({
|
||||||
items: z.array(
|
blocks: z.array(loyaltyPageBlocRefsItem).nullable(),
|
||||||
z.object({
|
sidebar: z.array(loyaltyPageSidebarRefsItem).nullable(),
|
||||||
blocks: z.array(loyaltyPageBlocRefsItem).nullable(),
|
system: z.object({
|
||||||
sidebar: z.array(loyaltyPageSidebarRefsItem).nullable(),
|
content_type_uid: z.string(),
|
||||||
system: z.object({
|
uid: z.string(),
|
||||||
content_type_uid: z.string(),
|
}),
|
||||||
uid: z.string(),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import {
|
|||||||
} from "@/lib/graphql/Query/LoyaltyPage.graphql"
|
} from "@/lib/graphql/Query/LoyaltyPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { _ } from "@/lib/translation"
|
import { _ } from "@/lib/translation"
|
||||||
import { badRequestError, internalServerError } from "@/server/errors/trpc"
|
import { internalServerError, notFound } from "@/server/errors/trpc"
|
||||||
import { publicProcedure, router } from "@/server/trpc"
|
import { contentstackProcedure, publicProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
generateRefsResponseTag,
|
generateRefsResponseTag,
|
||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
} from "@/utils/generateTag"
|
} from "@/utils/generateTag"
|
||||||
|
|
||||||
import { removeEmptyObjects } from "../../utils"
|
import { removeEmptyObjects } from "../../utils"
|
||||||
import { getLoyaltyPageInput } from "./input"
|
|
||||||
import {
|
import {
|
||||||
type LoyaltyPage,
|
type LoyaltyPage,
|
||||||
type LoyaltyPageDataRaw,
|
type LoyaltyPageDataRaw,
|
||||||
@@ -33,184 +32,160 @@ import { Edges } from "@/types/requests/utils/edges"
|
|||||||
import { RTEDocument } from "@/types/rte/node"
|
import { RTEDocument } from "@/types/rte/node"
|
||||||
|
|
||||||
export const loyaltyPageQueryRouter = router({
|
export const loyaltyPageQueryRouter = router({
|
||||||
get: publicProcedure.input(getLoyaltyPageInput).query(async ({ input }) => {
|
get: contentstackProcedure.query(async ({ ctx }) => {
|
||||||
try {
|
const { lang, uid } = ctx
|
||||||
const { locale } = input
|
|
||||||
|
|
||||||
const refsResponse = await request<LoyaltyPageRefsDataRaw>(
|
const refsResponse = await request<LoyaltyPageRefsDataRaw>(
|
||||||
GetLoyaltyPageRefs,
|
GetLoyaltyPageRefs,
|
||||||
{
|
{
|
||||||
locale,
|
locale: lang,
|
||||||
url: input.href,
|
uid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
next: {
|
||||||
|
tags: [generateRefsResponseTag(lang, uid)],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
next: {
|
|
||||||
tags: [generateRefsResponseTag(locale, "loyalty_page")],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!refsResponse.data) {
|
|
||||||
console.error("Bad response for `GetLoyaltyPageRefs`")
|
|
||||||
console.error({ refsResponse })
|
|
||||||
throw internalServerError()
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Remove empty objects from a fetched content type. Needed since
|
if (!refsResponse.data) {
|
||||||
// Contentstack returns empty objects for all non queried blocks in modular blocks.
|
throw notFound(refsResponse)
|
||||||
// This is an ongoing support case in Contentstack, ticker number #00031579
|
}
|
||||||
const cleanedData = removeEmptyObjects(refsResponse.data)
|
|
||||||
|
|
||||||
const validatedLoyaltyPageRefs =
|
// Remove empty objects from a fetched content type. Needed since
|
||||||
validateLoyaltyPageRefsSchema.safeParse(cleanedData)
|
// Contentstack returns empty objects for all non queried blocks in modular blocks.
|
||||||
if (!validatedLoyaltyPageRefs.success) {
|
// This is an ongoing support case in Contentstack, ticker number #00031579
|
||||||
console.error("Bad validation for `GetLoyaltyPageRefs`")
|
const cleanedData = removeEmptyObjects(refsResponse.data)
|
||||||
console.error(validatedLoyaltyPageRefs.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const connections = getConnections(validatedLoyaltyPageRefs.data)
|
const validatedLoyaltyPageRefs =
|
||||||
|
validateLoyaltyPageRefsSchema.safeParse(cleanedData)
|
||||||
|
if (!validatedLoyaltyPageRefs.success) {
|
||||||
|
throw internalServerError(validatedLoyaltyPageRefs.error)
|
||||||
|
}
|
||||||
|
|
||||||
const tags = generateTags(locale, connections)
|
const connections = getConnections(validatedLoyaltyPageRefs.data)
|
||||||
|
|
||||||
tags.push(
|
const tags = [
|
||||||
generateTag(
|
generateTags(lang, connections),
|
||||||
locale,
|
generateTag(lang, validatedLoyaltyPageRefs.data.loyalty_page.system.uid),
|
||||||
validatedLoyaltyPageRefs.data.all_loyalty_page.items[0].system.uid
|
].flat()
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
const loyaltyPageRes = await request<LoyaltyPageDataRaw>(
|
const response = await request<LoyaltyPageDataRaw>(
|
||||||
GetLoyaltyPage,
|
GetLoyaltyPage,
|
||||||
{
|
{
|
||||||
locale,
|
locale: lang,
|
||||||
url: input.href,
|
uid,
|
||||||
},
|
},
|
||||||
{ next: { tags } }
|
{ next: { tags } }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!loyaltyPageRes.data) {
|
if (!response.data) {
|
||||||
throw badRequestError()
|
throw notFound(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedLoyaltyPage = validateLoyaltyPageSchema.safeParse(
|
const validatedLoyaltyPage = validateLoyaltyPageSchema.safeParse(
|
||||||
loyaltyPageRes.data
|
response.data
|
||||||
)
|
)
|
||||||
|
if (!validatedLoyaltyPage.success) {
|
||||||
|
throw internalServerError(validatedLoyaltyPage.error)
|
||||||
|
}
|
||||||
|
|
||||||
if (!validatedLoyaltyPage.success) {
|
const sidebar = validatedLoyaltyPage.data.loyalty_page.sidebar
|
||||||
console.error("Bad validation for `validatedLoyaltyPage`")
|
? validatedLoyaltyPage.data.loyalty_page.sidebar.map((block) => {
|
||||||
console.error(validatedLoyaltyPage.error)
|
if (
|
||||||
throw badRequestError()
|
block.__typename == SidebarTypenameEnum.LoyaltyPageSidebarContent
|
||||||
}
|
) {
|
||||||
|
return {
|
||||||
const sidebar = validatedLoyaltyPage.data.all_loyalty_page.items[0]
|
...block,
|
||||||
.sidebar
|
content: {
|
||||||
? validatedLoyaltyPage.data.all_loyalty_page.items[0].sidebar.map(
|
content: {
|
||||||
(block) => {
|
json: block.content.content.json as RTEDocument,
|
||||||
if (
|
embedded_itemsConnection: block.content.content
|
||||||
block.__typename ==
|
.embedded_itemsConnection as Edges<Embeds>,
|
||||||
SidebarTypenameEnum.LoyaltyPageSidebarContent
|
},
|
||||||
) {
|
},
|
||||||
return {
|
|
||||||
...block,
|
|
||||||
content: {
|
|
||||||
content: {
|
|
||||||
json: block.content.content.json as RTEDocument,
|
|
||||||
embedded_itemsConnection: block.content.content
|
|
||||||
.embedded_itemsConnection as Edges<Embeds>,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return block
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
} else {
|
||||||
: null
|
return block
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: null
|
||||||
|
|
||||||
const blocks = validatedLoyaltyPage.data.all_loyalty_page.items[0].blocks
|
const blocks = validatedLoyaltyPage.data.loyalty_page.blocks
|
||||||
? validatedLoyaltyPage.data.all_loyalty_page.items[0].blocks.map(
|
? validatedLoyaltyPage.data.loyalty_page.blocks.map((block) => {
|
||||||
(block) => {
|
switch (block.__typename) {
|
||||||
switch (block.__typename) {
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid:
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid:
|
return {
|
||||||
return {
|
...block,
|
||||||
...block,
|
card_grid: {
|
||||||
card_grid: {
|
...block.card_grid,
|
||||||
...block.card_grid,
|
cards: block.card_grid.cards.map((card) => {
|
||||||
cards: block.card_grid.cards.map((card) => {
|
return {
|
||||||
return {
|
...card,
|
||||||
...card,
|
link: card.referenceConnection.totalCount
|
||||||
link: card.referenceConnection.totalCount
|
|
||||||
? {
|
|
||||||
href: `/${card.referenceConnection.edges[0].node.system.locale}${card.referenceConnection.edges[0].node.url}`,
|
|
||||||
title: card.cta_text || _("Read more"),
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent:
|
|
||||||
return {
|
|
||||||
...block,
|
|
||||||
dynamic_content: {
|
|
||||||
...block.dynamic_content,
|
|
||||||
link: block.dynamic_content.link.pageConnection.totalCount
|
|
||||||
? {
|
? {
|
||||||
text: block.dynamic_content.link.text,
|
href: `/${card.referenceConnection.edges[0].node.system.locale}${card.referenceConnection.edges[0].node.url}`,
|
||||||
href: `/${block.dynamic_content.link.pageConnection.edges[0].node.system.locale}${block.dynamic_content.link.pageConnection.edges[0].node.url}`,
|
title: card.cta_text || _("Read more"),
|
||||||
title:
|
|
||||||
block.dynamic_content.link.pageConnection.edges[0]
|
|
||||||
.node.title,
|
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
}
|
||||||
}
|
}),
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent:
|
},
|
||||||
return {
|
|
||||||
...block,
|
|
||||||
content: {
|
|
||||||
content: {
|
|
||||||
json: block.content.content.json as RTEDocument,
|
|
||||||
embedded_itemsConnection: block.content.content
|
|
||||||
.embedded_itemsConnection as Edges<Embeds>,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
|
|
||||||
return {
|
|
||||||
...block,
|
|
||||||
shortcuts: {
|
|
||||||
...block.shortcuts,
|
|
||||||
shortcuts: block.shortcuts.shortcuts.map((shortcut) => ({
|
|
||||||
text: shortcut.text,
|
|
||||||
openInNewTab: shortcut.open_in_new_tab,
|
|
||||||
...shortcut.linkConnection.edges[0].node,
|
|
||||||
url:
|
|
||||||
shortcut.linkConnection.edges[0].node.web
|
|
||||||
?.original_url ||
|
|
||||||
`/${shortcut.linkConnection.edges[0].node.system.locale}${shortcut.linkConnection.edges[0].node.url}`,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return block
|
|
||||||
}
|
}
|
||||||
}
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent:
|
||||||
)
|
return {
|
||||||
: null
|
...block,
|
||||||
|
dynamic_content: {
|
||||||
|
...block.dynamic_content,
|
||||||
|
link: block.dynamic_content.link.pageConnection.totalCount
|
||||||
|
? {
|
||||||
|
text: block.dynamic_content.link.text,
|
||||||
|
href: `/${block.dynamic_content.link.pageConnection.edges[0].node.system.locale}${block.dynamic_content.link.pageConnection.edges[0].node.url}`,
|
||||||
|
title:
|
||||||
|
block.dynamic_content.link.pageConnection.edges[0]
|
||||||
|
.node.title,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent:
|
||||||
|
return {
|
||||||
|
...block,
|
||||||
|
content: {
|
||||||
|
content: {
|
||||||
|
json: block.content.content.json as RTEDocument,
|
||||||
|
embedded_itemsConnection: block.content.content
|
||||||
|
.embedded_itemsConnection as Edges<Embeds>,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
|
||||||
|
return {
|
||||||
|
...block,
|
||||||
|
shortcuts: {
|
||||||
|
...block.shortcuts,
|
||||||
|
shortcuts: block.shortcuts.shortcuts.map((shortcut) => ({
|
||||||
|
text: shortcut.text,
|
||||||
|
openInNewTab: shortcut.open_in_new_tab,
|
||||||
|
...shortcut.linkConnection.edges[0].node,
|
||||||
|
url:
|
||||||
|
shortcut.linkConnection.edges[0].node.web?.original_url ||
|
||||||
|
`/${shortcut.linkConnection.edges[0].node.system.locale}${shortcut.linkConnection.edges[0].node.url}`,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: null
|
||||||
|
|
||||||
const loyaltyPage = {
|
const loyaltyPage = {
|
||||||
...validatedLoyaltyPage.data.all_loyalty_page.items[0],
|
...validatedLoyaltyPage.data.loyalty_page,
|
||||||
blocks,
|
blocks,
|
||||||
sidebar,
|
sidebar,
|
||||||
} as LoyaltyPage
|
} as LoyaltyPage
|
||||||
|
|
||||||
return loyaltyPage
|
return loyaltyPage
|
||||||
} catch (error) {
|
|
||||||
console.info(`Get Loyalty Page Error`)
|
|
||||||
console.error(error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,49 +6,47 @@ import type { NodeRefs } from "@/types/requests/utils/refs"
|
|||||||
|
|
||||||
export function getConnections(refs: LoyaltyPageRefsDataRaw) {
|
export function getConnections(refs: LoyaltyPageRefsDataRaw) {
|
||||||
const connections: Edges<NodeRefs>[] = []
|
const connections: Edges<NodeRefs>[] = []
|
||||||
refs.all_loyalty_page.items.forEach((ref) => {
|
if (refs.loyalty_page.blocks) {
|
||||||
if (ref.blocks) {
|
refs.loyalty_page.blocks.forEach((item) => {
|
||||||
ref.blocks.forEach((item) => {
|
switch (item.__typename) {
|
||||||
switch (item.__typename) {
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: {
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: {
|
if (item.content.content.embedded_itemsConnection.edges.length) {
|
||||||
if (item.content.content.embedded_itemsConnection.edges.length) {
|
connections.push(item.content.content.embedded_itemsConnection)
|
||||||
connections.push(item.content.content.embedded_itemsConnection)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid: {
|
|
||||||
item.card_grid.cards.forEach((card) => {
|
|
||||||
if (card.referenceConnection.edges.length) {
|
|
||||||
connections.push(card.referenceConnection)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: {
|
|
||||||
item.shortcuts.shortcuts.forEach((shortcut) => {
|
|
||||||
if (shortcut.linkConnection.edges.length) {
|
|
||||||
connections.push(shortcut.linkConnection)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: {
|
|
||||||
if (item.dynamic_content.link.pageConnection.edges.length) {
|
|
||||||
connections.push(item.dynamic_content.link.pageConnection)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid: {
|
||||||
}
|
item.card_grid.cards.forEach((card) => {
|
||||||
if (ref.sidebar) {
|
if (card.referenceConnection.edges.length) {
|
||||||
ref.sidebar?.forEach((item) => {
|
connections.push(card.referenceConnection)
|
||||||
if (item.content.content.embedded_itemsConnection.edges.length) {
|
}
|
||||||
connections.push(item.content.content.embedded_itemsConnection)
|
})
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: {
|
||||||
}
|
item.shortcuts.shortcuts.forEach((shortcut) => {
|
||||||
})
|
if (shortcut.linkConnection.edges.length) {
|
||||||
|
connections.push(shortcut.linkConnection)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: {
|
||||||
|
if (item.dynamic_content.link.pageConnection.edges.length) {
|
||||||
|
connections.push(item.dynamic_content.link.pageConnection)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (refs.loyalty_page.sidebar) {
|
||||||
|
refs.loyalty_page.sidebar?.forEach((item) => {
|
||||||
|
if (item.content.content.embedded_itemsConnection.edges.length) {
|
||||||
|
connections.push(item.content.content.embedded_itemsConnection)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return connections
|
return connections
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages";
|
|
||||||
|
|
||||||
export const getNavigationInputSchema = z.nativeEnum(Lang)
|
|
||||||
@@ -66,7 +66,7 @@ export const navigationRefsPayloadSchema = z.object({
|
|||||||
return input.length === 1
|
return input.length === 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message: `Expected navigationRefsPayloadSchema 1 all_navigation_my_pages item`,
|
message: `Expected all_navigation_my_pages items to only contain 1 in navigationRefsPayloadSchema`,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
@@ -101,7 +101,7 @@ export const navigationPayloadSchema = z.object({
|
|||||||
return input.length === 1
|
return input.length === 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message: `Expected navigationPayloadSchema to containt 1 all_navigation_my_pages item`,
|
message: `Expected all_navigation_my_pages items to only contain 1 in navigationPayloadSchema`,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
GetNavigationMyPagesRefs,
|
GetNavigationMyPagesRefs,
|
||||||
} from "@/lib/graphql/Query/NavigationMyPages.graphql"
|
} from "@/lib/graphql/Query/NavigationMyPages.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { badRequestError, internalServerError } from "@/server/errors/trpc"
|
import { internalServerError, notFound } from "@/server/errors/trpc"
|
||||||
import { publicProcedure, router } from "@/server/trpc"
|
import { contentstackProcedure, publicProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
generateRefsResponseTag,
|
generateRefsResponseTag,
|
||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
} from "@/utils/generateTag"
|
} from "@/utils/generateTag"
|
||||||
import { removeMultipleSlashes } from "@/utils/url"
|
import { removeMultipleSlashes } from "@/utils/url"
|
||||||
|
|
||||||
import { getNavigationInputSchema } from "./input"
|
|
||||||
import {
|
import {
|
||||||
getNavigationSchema,
|
getNavigationSchema,
|
||||||
navigationPayloadSchema,
|
navigationPayloadSchema,
|
||||||
@@ -48,82 +47,70 @@ export function mapMenuItems(navigationItems: NavigationItem[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const navigationQueryRouter = router({
|
export const navigationQueryRouter = router({
|
||||||
get: publicProcedure.input(getNavigationInputSchema).query(async function ({
|
get: contentstackProcedure.query(async function ({ ctx }) {
|
||||||
input: lang,
|
const { lang } = ctx
|
||||||
}) {
|
|
||||||
try {
|
|
||||||
const refsResponse = await request<GetNavigationMyPagesRefsData>(
|
|
||||||
GetNavigationMyPagesRefs,
|
|
||||||
{ locale: lang },
|
|
||||||
{
|
|
||||||
next: {
|
|
||||||
tags: [generateRefsResponseTag(lang, "navigation_my_pages")],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!refsResponse.data) {
|
const refsResponse = await request<GetNavigationMyPagesRefsData>(
|
||||||
console.error("Bad response for `GetNavigationMyPagesRefs`")
|
GetNavigationMyPagesRefs,
|
||||||
console.error({ refsResponse })
|
{ locale: lang },
|
||||||
throw internalServerError()
|
{
|
||||||
|
next: {
|
||||||
|
tags: [generateRefsResponseTag(lang, "navigation_my_pages")],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const validatedMyPagesNavigationRefs =
|
if (!refsResponse.data) {
|
||||||
navigationRefsPayloadSchema.safeParse(refsResponse.data)
|
throw notFound(refsResponse)
|
||||||
if (!validatedMyPagesNavigationRefs.success) {
|
|
||||||
console.error("Bad validation for `GetNavigationMyPagesRefs`")
|
|
||||||
console.error(validatedMyPagesNavigationRefs.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const connections = getConnections(validatedMyPagesNavigationRefs.data)
|
|
||||||
const tags = generateTags(lang, connections)
|
|
||||||
const navigation =
|
|
||||||
validatedMyPagesNavigationRefs.data.all_navigation_my_pages.items[0]
|
|
||||||
tags.push(generateTag(lang, navigation.system.uid))
|
|
||||||
|
|
||||||
const response = await request<GetNavigationMyPagesData>(
|
|
||||||
GetNavigationMyPages,
|
|
||||||
{ locale: lang },
|
|
||||||
{ next: { tags } }
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!response.data) {
|
|
||||||
console.error("Bad response for `GetNavigationMyPages`")
|
|
||||||
console.error({ input: lang })
|
|
||||||
console.error({ response })
|
|
||||||
throw internalServerError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const validatedMyPagesNavigation = navigationPayloadSchema.safeParse(
|
|
||||||
response.data
|
|
||||||
)
|
|
||||||
if (!validatedMyPagesNavigation.success) {
|
|
||||||
console.error("Bad validation for `GetNavigationMyPages`")
|
|
||||||
console.error(validatedMyPagesNavigation.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const menuItem =
|
|
||||||
validatedMyPagesNavigation.data.all_navigation_my_pages.items[0]
|
|
||||||
|
|
||||||
const nav = {
|
|
||||||
items: mapMenuItems(menuItem.items),
|
|
||||||
title: menuItem.title,
|
|
||||||
}
|
|
||||||
|
|
||||||
const validatedNav = getNavigationSchema.safeParse(nav)
|
|
||||||
if (!validatedNav.success) {
|
|
||||||
console.error("Bad validation for `getNavigationSchema`")
|
|
||||||
console.error(validatedNav.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
return validatedNav.data
|
|
||||||
} catch (error) {
|
|
||||||
console.info(`Get My Pages Navigation Error`)
|
|
||||||
console.error(error)
|
|
||||||
throw internalServerError()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validatedMyPagesNavigationRefs =
|
||||||
|
navigationRefsPayloadSchema.safeParse(refsResponse.data)
|
||||||
|
if (!validatedMyPagesNavigationRefs.success) {
|
||||||
|
throw internalServerError(validatedMyPagesNavigationRefs.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const connections = getConnections(validatedMyPagesNavigationRefs.data)
|
||||||
|
|
||||||
|
const tags = [
|
||||||
|
generateTags(lang, connections),
|
||||||
|
generateTag(
|
||||||
|
lang,
|
||||||
|
validatedMyPagesNavigationRefs.data.all_navigation_my_pages.items[0]
|
||||||
|
.system.uid
|
||||||
|
),
|
||||||
|
].flat()
|
||||||
|
|
||||||
|
const response = await request<GetNavigationMyPagesData>(
|
||||||
|
GetNavigationMyPages,
|
||||||
|
{ locale: lang },
|
||||||
|
{ next: { tags } }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!response.data) {
|
||||||
|
throw notFound(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatedMyPagesNavigation = navigationPayloadSchema.safeParse(
|
||||||
|
response.data
|
||||||
|
)
|
||||||
|
if (!validatedMyPagesNavigation.success) {
|
||||||
|
throw internalServerError(validatedMyPagesNavigation.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuItem =
|
||||||
|
validatedMyPagesNavigation.data.all_navigation_my_pages.items[0]
|
||||||
|
|
||||||
|
const nav = {
|
||||||
|
items: mapMenuItems(menuItem.items),
|
||||||
|
title: menuItem.title,
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatedNav = getNavigationSchema.safeParse(nav)
|
||||||
|
if (!validatedNav.success) {
|
||||||
|
throw internalServerError(validatedNav.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return validatedNav.data
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
badRequestError,
|
badRequestError,
|
||||||
forbiddenError,
|
forbiddenError,
|
||||||
internalServerError,
|
internalServerError,
|
||||||
|
notFound,
|
||||||
unauthorizedError,
|
unauthorizedError,
|
||||||
} from "@/server/errors/trpc"
|
} from "@/server/errors/trpc"
|
||||||
import { protectedProcedure, router } from "@/server/trpc"
|
import { protectedProcedure, router } from "@/server/trpc"
|
||||||
@@ -21,50 +22,43 @@ function fakingRequest<T>(payload: T): Promise<T> {
|
|||||||
|
|
||||||
export const userQueryRouter = router({
|
export const userQueryRouter = router({
|
||||||
get: protectedProcedure.query(async function ({ ctx }) {
|
get: protectedProcedure.query(async function ({ ctx }) {
|
||||||
try {
|
const apiResponse = await api.get(api.endpoints.v0.profile, {
|
||||||
const apiResponse = await api.get(api.endpoints.v0.profile, {
|
headers: {
|
||||||
headers: {
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
if (!apiResponse.ok) {
|
|
||||||
switch (apiResponse.status) {
|
|
||||||
case 400:
|
|
||||||
throw badRequestError()
|
|
||||||
case 401:
|
|
||||||
throw unauthorizedError()
|
|
||||||
case 403:
|
|
||||||
throw forbiddenError()
|
|
||||||
default:
|
|
||||||
throw internalServerError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
if (!apiResponse.ok) {
|
||||||
|
switch (apiResponse.status) {
|
||||||
if (!apiJson.data?.length) {
|
case 400:
|
||||||
throw internalServerError()
|
throw badRequestError(apiResponse)
|
||||||
|
case 401:
|
||||||
|
throw unauthorizedError(apiResponse)
|
||||||
|
case 403:
|
||||||
|
throw forbiddenError(apiResponse)
|
||||||
|
default:
|
||||||
|
throw internalServerError(apiResponse)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const verifiedData = getUserSchema.safeParse(apiJson.data[0].attributes)
|
const apiJson = await apiResponse.json()
|
||||||
if (!verifiedData.success) {
|
if (!apiJson.data?.length) {
|
||||||
console.info(`Get User - Verified Data Error`)
|
throw notFound(apiJson)
|
||||||
console.error(verifiedData.error)
|
}
|
||||||
throw badRequestError()
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
const verifiedData = getUserSchema.safeParse(apiJson.data[0].attributes)
|
||||||
...extendedUser,
|
if (!verifiedData.success) {
|
||||||
...verifiedData.data,
|
throw internalServerError(verifiedData.error)
|
||||||
firstName: verifiedData.data.name,
|
}
|
||||||
name: `${verifiedData.data.name} ${verifiedData.data.lastName}`,
|
|
||||||
}
|
return {
|
||||||
} catch (error) {
|
...extendedUser,
|
||||||
console.info(`Get User Error`)
|
...verifiedData.data,
|
||||||
console.error(error)
|
firstName: verifiedData.data.name,
|
||||||
throw internalServerError()
|
name: `${verifiedData.data.name} ${verifiedData.data.lastName}`,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
benefits: router({
|
benefits: router({
|
||||||
current: protectedProcedure.query(async function (opts) {
|
current: protectedProcedure.query(async function (opts) {
|
||||||
// TODO: Make request to get user data from Scandic API
|
// TODO: Make request to get user data from Scandic API
|
||||||
@@ -77,9 +71,10 @@ export const userQueryRouter = router({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
stays: router({
|
stays: router({
|
||||||
previous: protectedProcedure.input(staysInput).query(async (opts) => {
|
previous: protectedProcedure
|
||||||
try {
|
.input(staysInput)
|
||||||
const { limit, cursor } = opts.input
|
.query(async ({ ctx, input }) => {
|
||||||
|
const { limit, cursor } = input
|
||||||
|
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
params.set("limit", limit.toString())
|
params.set("limit", limit.toString())
|
||||||
@@ -92,7 +87,7 @@ export const userQueryRouter = router({
|
|||||||
api.endpoints.v1.previousStays,
|
api.endpoints.v1.previousStays,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${opts.ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
params
|
params
|
||||||
@@ -101,28 +96,24 @@ export const userQueryRouter = router({
|
|||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
switch (apiResponse.status) {
|
switch (apiResponse.status) {
|
||||||
case 400:
|
case 400:
|
||||||
throw badRequestError()
|
throw badRequestError(apiResponse)
|
||||||
case 401:
|
case 401:
|
||||||
throw unauthorizedError()
|
throw unauthorizedError(apiResponse)
|
||||||
case 403:
|
case 403:
|
||||||
throw forbiddenError()
|
throw forbiddenError(apiResponse)
|
||||||
default:
|
default:
|
||||||
throw internalServerError()
|
throw internalServerError(apiResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
if (!apiJson.data) {
|
if (!apiJson.data?.length) {
|
||||||
console.error(`Get Previous Stays - No data found from api call`)
|
throw notFound(apiJson)
|
||||||
throw internalServerError()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifiedData = getStaysSchema.safeParse(apiJson)
|
const verifiedData = getStaysSchema.safeParse(apiJson)
|
||||||
|
|
||||||
if (!verifiedData.success) {
|
if (!verifiedData.success) {
|
||||||
console.info(`Get Previous Stays - Verified Data Error`)
|
throw internalServerError(verifiedData.error)
|
||||||
console.error(verifiedData.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextCursor =
|
const nextCursor =
|
||||||
@@ -135,16 +126,12 @@ export const userQueryRouter = router({
|
|||||||
data: verifiedData.data.data,
|
data: verifiedData.data.data,
|
||||||
nextCursor,
|
nextCursor,
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}),
|
||||||
console.info(`Get Previous Stays Error`)
|
|
||||||
console.error(error)
|
|
||||||
throw internalServerError()
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
upcoming: protectedProcedure.input(staysInput).query(async (opts) => {
|
upcoming: protectedProcedure
|
||||||
try {
|
.input(staysInput)
|
||||||
const { limit, cursor } = opts.input
|
.query(async ({ ctx, input }) => {
|
||||||
|
const { limit, cursor } = input
|
||||||
|
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
params.set("limit", limit.toString())
|
params.set("limit", limit.toString())
|
||||||
@@ -157,7 +144,7 @@ export const userQueryRouter = router({
|
|||||||
api.endpoints.v1.upcomingStays,
|
api.endpoints.v1.upcomingStays,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${opts.ctx.session.token.access_token}`,
|
Authorization: `Bearer ${ctx.session.token.access_token}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
params
|
params
|
||||||
@@ -166,28 +153,24 @@ export const userQueryRouter = router({
|
|||||||
if (!apiResponse.ok) {
|
if (!apiResponse.ok) {
|
||||||
switch (apiResponse.status) {
|
switch (apiResponse.status) {
|
||||||
case 400:
|
case 400:
|
||||||
throw badRequestError()
|
throw badRequestError(apiResponse)
|
||||||
case 401:
|
case 401:
|
||||||
throw unauthorizedError()
|
throw unauthorizedError(apiResponse)
|
||||||
case 403:
|
case 403:
|
||||||
throw forbiddenError()
|
throw forbiddenError(apiResponse)
|
||||||
default:
|
default:
|
||||||
throw internalServerError()
|
throw internalServerError(apiResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiJson = await apiResponse.json()
|
const apiJson = await apiResponse.json()
|
||||||
if (!apiJson.data) {
|
if (!apiJson.data?.length) {
|
||||||
console.error(`Get Upcoming Stays - No data found from api call`)
|
throw notFound(apiJson)
|
||||||
throw internalServerError()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifiedData = getStaysSchema.safeParse(apiJson)
|
const verifiedData = getStaysSchema.safeParse(apiJson)
|
||||||
|
|
||||||
if (!verifiedData.success) {
|
if (!verifiedData.success) {
|
||||||
console.info(`Get Upcoming Stays - Verified Data Error`)
|
throw internalServerError(verifiedData.error)
|
||||||
console.error(verifiedData.error)
|
|
||||||
throw badRequestError()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextCursor =
|
const nextCursor =
|
||||||
@@ -200,11 +183,6 @@ export const userQueryRouter = router({
|
|||||||
data: verifiedData.data.data,
|
data: verifiedData.data.data,
|
||||||
nextCursor,
|
nextCursor,
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}),
|
||||||
console.info(`Get Upcoming Stays Error`)
|
|
||||||
console.error(error)
|
|
||||||
throw internalServerError()
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { initTRPC } from "@trpc/server"
|
|||||||
|
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
import { unauthorizedError } from "./errors/trpc"
|
import { badRequestError, unauthorizedError } from "./errors/trpc"
|
||||||
import { transformer } from "./transformer"
|
import { transformer } from "./transformer"
|
||||||
|
|
||||||
import type { Meta } from "@/types/trpc/meta"
|
import type { Meta } from "@/types/trpc/meta"
|
||||||
@@ -12,6 +12,17 @@ const t = initTRPC.context<Context>().meta<Meta>().create({ transformer })
|
|||||||
|
|
||||||
export const { createCallerFactory, mergeRouters, router } = t
|
export const { createCallerFactory, mergeRouters, router } = t
|
||||||
export const publicProcedure = t.procedure
|
export const publicProcedure = t.procedure
|
||||||
|
export const contentstackProcedure = t.procedure.use(async function (opts) {
|
||||||
|
if (!opts.ctx.uid) {
|
||||||
|
throw badRequestError("Missing UID in tRPC context")
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts.next({
|
||||||
|
ctx: {
|
||||||
|
uid: opts.ctx.uid,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
export const protectedProcedure = t.procedure.use(async function (opts) {
|
export const protectedProcedure = t.procedure.use(async function (opts) {
|
||||||
const authRequired = opts.meta?.authRequired ?? true
|
const authRequired = opts.meta?.authRequired ?? true
|
||||||
const session = await opts.ctx.auth()
|
const session = await opts.ctx.auth()
|
||||||
|
|||||||
6
types/components/header/logo.ts
Normal file
6
types/components/header/logo.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export type LogoProps = {
|
||||||
|
height: number
|
||||||
|
src: string
|
||||||
|
title: string
|
||||||
|
width: number
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import type { LangParams, LayoutArgs } from "@/types/params"
|
|
||||||
|
|
||||||
export interface MyPagesLayoutProps extends LayoutArgs<LangParams> {
|
|
||||||
breadcrumbs: React.ReactNode
|
|
||||||
}
|
|
||||||
@@ -12,8 +12,20 @@ export type LangParams = {
|
|||||||
lang: Lang
|
lang: Lang
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type StatusParams = {
|
||||||
|
status: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ContentTypeParams = {
|
||||||
|
contentType: "loyalty-page" | "content-page"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UIDParams = {
|
||||||
|
uid: string
|
||||||
|
}
|
||||||
|
|
||||||
export type UriParams = {
|
export type UriParams = {
|
||||||
uri?: string
|
uri: string | string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PreviewParams = {
|
export type PreviewParams = {
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import z from "zod"
|
|
||||||
|
|
||||||
export const validateContentTypeUid = z.object({
|
|
||||||
all_content_page: z.object({
|
|
||||||
total: z.number(),
|
|
||||||
}),
|
|
||||||
all_loyalty_page: z.object({
|
|
||||||
total: z.number(),
|
|
||||||
}),
|
|
||||||
all_current_blocks_page: z.object({
|
|
||||||
total: z.number(),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
19
types/requests/entry.ts
Normal file
19
types/requests/entry.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import z from "zod"
|
||||||
|
|
||||||
|
const entryResolveSchema = z.object({
|
||||||
|
items: z.array(
|
||||||
|
z.object({
|
||||||
|
system: z.object({
|
||||||
|
content_type_uid: z.string(),
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
total: z.number(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const validateEntryResolveSchema = z.object({
|
||||||
|
all_content_page: entryResolveSchema,
|
||||||
|
all_loyalty_page: entryResolveSchema,
|
||||||
|
all_current_blocks_page: entryResolveSchema,
|
||||||
|
})
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import type { Image } from "../../image"
|
|
||||||
import type { EdgesWithTotalCount } from "../utils/edges"
|
|
||||||
|
|
||||||
export type LogoQueryData = {
|
|
||||||
all_header: {
|
|
||||||
items: {
|
|
||||||
logoConnection: EdgesWithTotalCount<Image>
|
|
||||||
}[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user