From 888c33e3b3efab7f2c4dd0f253205d09ad224c90 Mon Sep 17 00:00:00 2001 From: Michael Zetterberg Date: Tue, 14 May 2024 16:32:17 +0200 Subject: [PATCH 01/20] chore(debug): auth --- app/[lang]/(live)/(protected)/bypass/page.tsx | 7 ++ app/[lang]/(live)/(public)/login/route.ts | 64 +++++++++---------- 2 files changed, 39 insertions(+), 32 deletions(-) create mode 100644 app/[lang]/(live)/(protected)/bypass/page.tsx diff --git a/app/[lang]/(live)/(protected)/bypass/page.tsx b/app/[lang]/(live)/(protected)/bypass/page.tsx new file mode 100644 index 000000000..9f37aa2a7 --- /dev/null +++ b/app/[lang]/(live)/(protected)/bypass/page.tsx @@ -0,0 +1,7 @@ +import { auth } from "@/auth" + +export default async function ByPass() { + const session = await auth() + + return
{JSON.stringify(session, null, 2)}
+} diff --git a/app/[lang]/(live)/(public)/login/route.ts b/app/[lang]/(live)/(public)/login/route.ts index 5cbc22d9b..7879d01f5 100644 --- a/app/[lang]/(live)/(public)/login/route.ts +++ b/app/[lang]/(live)/(public)/login/route.ts @@ -41,38 +41,38 @@ export async function GET( "redirectTo=; Expires=Thu, 01 Jan 1970 00:00:00 UTC; Path=/; HttpOnly; SameSite=Lax" ) - try { - // Initiate the seamless login flow - let redirectUrlValue - switch (context.params.lang) { - case Lang.da: - redirectUrlValue = env.SEAMLESS_LOGIN_DA - break - case Lang.de: - redirectUrlValue = env.SEAMLESS_LOGIN_DE - break - case Lang.en: - redirectUrlValue = env.SEAMLESS_LOGIN_EN - break - case Lang.fi: - redirectUrlValue = env.SEAMLESS_LOGIN_FI - break - case Lang.no: - redirectUrlValue = env.SEAMLESS_LOGIN_NO - break - case Lang.sv: - redirectUrlValue = env.SEAMLESS_LOGIN_SV - break - } - const redirectUrl = new URL(redirectUrlValue) - redirectUrl.searchParams.set("returnurl", redirectTo) - redirectTo = redirectUrl.toString() - } catch (e) { - console.error( - "Unable to create URL for seamless login, proceeding without it." - ) - console.error(e) - } + // try { + // // Initiate the seamless login flow + // let redirectUrlValue + // switch (context.params.lang) { + // case Lang.da: + // redirectUrlValue = env.SEAMLESS_LOGIN_DA + // break + // case Lang.de: + // redirectUrlValue = env.SEAMLESS_LOGIN_DE + // break + // case Lang.en: + // redirectUrlValue = env.SEAMLESS_LOGIN_EN + // break + // case Lang.fi: + // redirectUrlValue = env.SEAMLESS_LOGIN_FI + // break + // case Lang.no: + // redirectUrlValue = env.SEAMLESS_LOGIN_NO + // break + // case Lang.sv: + // redirectUrlValue = env.SEAMLESS_LOGIN_SV + // break + // } + // const redirectUrl = new URL(redirectUrlValue) + // redirectUrl.searchParams.set("returnurl", redirectTo) + // redirectTo = redirectUrl.toString() + // } catch (e) { + // console.error( + // "Unable to create URL for seamless login, proceeding without it." + // ) + // console.error(e) + // } } try { From d07826b2a3f4da62f443fb6da1085f15c38104eb Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Thu, 2 May 2024 10:41:37 +0200 Subject: [PATCH 02/20] fix: get access token from headers --- app/[lang]/webview/test/page.tsx | 9 ++++++++- middlewares/webView.ts | 11 ++++++----- server/trpc.ts | 3 +-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/[lang]/webview/test/page.tsx b/app/[lang]/webview/test/page.tsx index e482db7a6..bca6f0dd7 100644 --- a/app/[lang]/webview/test/page.tsx +++ b/app/[lang]/webview/test/page.tsx @@ -1,14 +1,21 @@ +import { cookies, headers } from "next/headers" + +import { serverClient } from "@/lib/trpc/server" + import type { Metadata } from "next" export const metadata: Metadata = { title: "Hello World from Webview", } -export default function WebViewTestPage() { +export default async function WebViewTestPage() { + const data = await serverClient().user.get() + return (

Hello From WebView Test Page!

+

{data.firstName}

) diff --git a/middlewares/webView.ts b/middlewares/webView.ts index 7b6fadee6..7d52e2ae9 100644 --- a/middlewares/webView.ts +++ b/middlewares/webView.ts @@ -37,12 +37,13 @@ export const middleware: NextMiddleware = async (request) => { authorization ) - // Pass the webview token via cookie to the page - return NextResponse.next({ - headers: { - "Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly;`, - }, + const response = NextResponse.next() + response.cookies.set("webviewToken", decryptedData, { + httpOnly: true, + secure: true, }) + + return response } catch (e) { if (e instanceof Error) { console.error(`${e.name}: ${e.message}`) diff --git a/server/trpc.ts b/server/trpc.ts index e09d79215..eb0c5bef6 100644 --- a/server/trpc.ts +++ b/server/trpc.ts @@ -29,8 +29,7 @@ export const contentstackProcedure = t.procedure.use(async function (opts) { }) export const protectedProcedure = t.procedure.use(async function (opts) { const authRequired = opts.meta?.authRequired ?? true - const session = await opts.ctx.auth() - + const session = await (await opts.ctx).session if (!authRequired && env.NODE_ENV === "development") { console.info( `❌❌❌❌ You are opting out of authorization, if its done on purpose maybe you should use the publicProcedure instead. ❌❌❌❌` From 49f760705e7dbaec806c48ab0a62a0a2f300786e Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Mon, 6 May 2024 09:52:43 +0200 Subject: [PATCH 03/20] feat: add webview pages --- .../about-scandic-friends/page.module.css | 6 +++++ .../webview/about-scandic-friends/page.tsx | 26 ++++++++++++++++++ app/[lang]/webview/layout.module.css | 6 +++++ app/[lang]/webview/layout.tsx | 10 ++++++- .../my-pages/benefits/benefits.module.css | 6 +++++ app/[lang]/webview/my-pages/benefits/page.tsx | 26 ++++++++++++++++++ .../my-pages/overview/overview.module.css | 6 +++++ app/[lang]/webview/my-pages/overview/page.tsx | 26 ++++++++++++++++++ app/[lang]/webview/my-pages/points/page.tsx | 27 +++++++++++++++++++ .../webview/my-pages/points/points.module.css | 6 +++++ 10 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 app/[lang]/webview/about-scandic-friends/page.module.css create mode 100644 app/[lang]/webview/about-scandic-friends/page.tsx create mode 100644 app/[lang]/webview/layout.module.css create mode 100644 app/[lang]/webview/my-pages/benefits/benefits.module.css create mode 100644 app/[lang]/webview/my-pages/benefits/page.tsx create mode 100644 app/[lang]/webview/my-pages/overview/overview.module.css create mode 100644 app/[lang]/webview/my-pages/overview/page.tsx create mode 100644 app/[lang]/webview/my-pages/points/page.tsx create mode 100644 app/[lang]/webview/my-pages/points/points.module.css diff --git a/app/[lang]/webview/about-scandic-friends/page.module.css b/app/[lang]/webview/about-scandic-friends/page.module.css new file mode 100644 index 000000000..9ff21ac9f --- /dev/null +++ b/app/[lang]/webview/about-scandic-friends/page.module.css @@ -0,0 +1,6 @@ +.blocks { + display: grid; + gap: 4.2rem; + padding-left: 2rem; + padding-right: 2rem; +} diff --git a/app/[lang]/webview/about-scandic-friends/page.tsx b/app/[lang]/webview/about-scandic-friends/page.tsx new file mode 100644 index 000000000..6f7755401 --- /dev/null +++ b/app/[lang]/webview/about-scandic-friends/page.tsx @@ -0,0 +1,26 @@ +import "@/app/globals.css" +import "@scandic-hotels/design-system/style.css" + +import { serverClient } from "@/lib/trpc/server" + +import MaxWidth from "@/components/MaxWidth" +import Content from "@/components/MyPages/AccountPage/Content" + +import styles from "./page.module.css" + +import type { LangParams, PageArgs, UriParams } from "@/types/params" + +export default async function AboutScandicFriends({ + params, +}: PageArgs) { + const accountPage = await serverClient().contentstack.accountPage.get({ + url: "/my-pages/overview", + lang: params.lang, + }) + + return ( + + + + ) +} diff --git a/app/[lang]/webview/layout.module.css b/app/[lang]/webview/layout.module.css new file mode 100644 index 000000000..4351f00fc --- /dev/null +++ b/app/[lang]/webview/layout.module.css @@ -0,0 +1,6 @@ +.layout { + padding-bottom: 7.7rem; + font-family: var(--ff-fira-sans); + + min-height: 100dvh; +} diff --git a/app/[lang]/webview/layout.tsx b/app/[lang]/webview/layout.tsx index 684d28aa8..b6eef042d 100644 --- a/app/[lang]/webview/layout.tsx +++ b/app/[lang]/webview/layout.tsx @@ -1,3 +1,7 @@ +import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts" + +import styles from "./layout.module.css" + import type { Metadata } from "next" import type { LangParams, LayoutArgs } from "@/types/params" @@ -12,7 +16,11 @@ export default function RootLayout({ }: React.PropsWithChildren>) { return ( - {children} + + {children} + ) } diff --git a/app/[lang]/webview/my-pages/benefits/benefits.module.css b/app/[lang]/webview/my-pages/benefits/benefits.module.css new file mode 100644 index 000000000..9ff21ac9f --- /dev/null +++ b/app/[lang]/webview/my-pages/benefits/benefits.module.css @@ -0,0 +1,6 @@ +.blocks { + display: grid; + gap: 4.2rem; + padding-left: 2rem; + padding-right: 2rem; +} diff --git a/app/[lang]/webview/my-pages/benefits/page.tsx b/app/[lang]/webview/my-pages/benefits/page.tsx new file mode 100644 index 000000000..194f9c7b8 --- /dev/null +++ b/app/[lang]/webview/my-pages/benefits/page.tsx @@ -0,0 +1,26 @@ +import "@/app/globals.css" +import "@scandic-hotels/design-system/style.css" + +import { serverClient } from "@/lib/trpc/server" + +import MaxWidth from "@/components/MaxWidth" +import Content from "@/components/MyPages/AccountPage/Content" + +import styles from "./benefits.module.css" + +import type { LangParams, PageArgs, UriParams } from "@/types/params" + +export default async function MyPages({ + params, +}: PageArgs) { + const accountPage = await serverClient().contentstack.accountPage.get({ + url: "/my-pages/benefits", + lang: params.lang, + }) + + return ( + + + + ) +} diff --git a/app/[lang]/webview/my-pages/overview/overview.module.css b/app/[lang]/webview/my-pages/overview/overview.module.css new file mode 100644 index 000000000..9ff21ac9f --- /dev/null +++ b/app/[lang]/webview/my-pages/overview/overview.module.css @@ -0,0 +1,6 @@ +.blocks { + display: grid; + gap: 4.2rem; + padding-left: 2rem; + padding-right: 2rem; +} diff --git a/app/[lang]/webview/my-pages/overview/page.tsx b/app/[lang]/webview/my-pages/overview/page.tsx new file mode 100644 index 000000000..da4b03cfe --- /dev/null +++ b/app/[lang]/webview/my-pages/overview/page.tsx @@ -0,0 +1,26 @@ +import "@/app/globals.css" +import "@scandic-hotels/design-system/style.css" + +import { serverClient } from "@/lib/trpc/server" + +import MaxWidth from "@/components/MaxWidth" +import Content from "@/components/MyPages/AccountPage/Content" + +import styles from "./overview.module.css" + +import type { LangParams, PageArgs, UriParams } from "@/types/params" + +export default async function MyPages({ + params, +}: PageArgs) { + const accountPage = await serverClient().contentstack.accountPage.get({ + url: "/my-pages/overview", + lang: params.lang, + }) + + return ( + + + + ) +} diff --git a/app/[lang]/webview/my-pages/points/page.tsx b/app/[lang]/webview/my-pages/points/page.tsx new file mode 100644 index 000000000..b1ed1b463 --- /dev/null +++ b/app/[lang]/webview/my-pages/points/page.tsx @@ -0,0 +1,27 @@ +import "@/app/globals.css" +import "@scandic-hotels/design-system/style.css" + +import { serverClient } from "@/lib/trpc/server" + +import MaxWidth from "@/components/MaxWidth" +import Content from "@/components/MyPages/AccountPage/Content" + +import styles from "./points.module.css" + +import type { LangParams, PageArgs, UriParams } from "@/types/params" + +export default async function Points({ + params, +}: PageArgs) { + // const accountPage = await serverClient().contentstack.accountPage.get({ + // url: "/my-pages/points", + // lang: params.lang, + // }) + + return ( + +

POINTS

+ {/* */} +
+ ) +} diff --git a/app/[lang]/webview/my-pages/points/points.module.css b/app/[lang]/webview/my-pages/points/points.module.css new file mode 100644 index 000000000..9ff21ac9f --- /dev/null +++ b/app/[lang]/webview/my-pages/points/points.module.css @@ -0,0 +1,6 @@ +.blocks { + display: grid; + gap: 4.2rem; + padding-left: 2rem; + padding-right: 2rem; +} From b58b5f368defd4b48d8a4f6acd94ea9a0f12d954 Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Mon, 6 May 2024 16:33:39 +0200 Subject: [PATCH 04/20] fix: refactor navigation of webview pages and add redirect to refresh --- .../webview/about-scandic-friends/page.tsx | 26 ------ app/[lang]/webview/layout.module.css | 2 +- app/[lang]/webview/layout.tsx | 3 + .../page.module.css | 0 app/[lang]/webview/loyalty-page/page.tsx | 44 ++++++++++ app/[lang]/webview/my-pages/benefits/page.tsx | 26 ------ .../my-pages/overview/overview.module.css | 6 -- app/[lang]/webview/my-pages/overview/page.tsx | 26 ------ .../benefits.module.css => page.module.css} | 0 app/[lang]/webview/my-pages/page.tsx | 67 +++++++++++++++ app/[lang]/webview/my-pages/points/page.tsx | 27 ------ .../webview/my-pages/points/points.module.css | 6 -- .../MyPages/AccountPage/Webview/Content.tsx | 77 +++++++++++++++++ constants/routes/webviews.ts | 73 ++++++++++++++++ middlewares/webView.ts | 64 +++++++++++--- server/trpc.ts | 3 +- test.js | 84 +++++++++++++++++++ 17 files changed, 403 insertions(+), 131 deletions(-) delete mode 100644 app/[lang]/webview/about-scandic-friends/page.tsx rename app/[lang]/webview/{about-scandic-friends => loyalty-page}/page.module.css (100%) create mode 100644 app/[lang]/webview/loyalty-page/page.tsx delete mode 100644 app/[lang]/webview/my-pages/benefits/page.tsx delete mode 100644 app/[lang]/webview/my-pages/overview/overview.module.css delete mode 100644 app/[lang]/webview/my-pages/overview/page.tsx rename app/[lang]/webview/my-pages/{benefits/benefits.module.css => page.module.css} (100%) create mode 100644 app/[lang]/webview/my-pages/page.tsx delete mode 100644 app/[lang]/webview/my-pages/points/page.tsx delete mode 100644 app/[lang]/webview/my-pages/points/points.module.css create mode 100644 components/MyPages/AccountPage/Webview/Content.tsx create mode 100644 constants/routes/webviews.ts create mode 100644 test.js diff --git a/app/[lang]/webview/about-scandic-friends/page.tsx b/app/[lang]/webview/about-scandic-friends/page.tsx deleted file mode 100644 index 6f7755401..000000000 --- a/app/[lang]/webview/about-scandic-friends/page.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import "@/app/globals.css" -import "@scandic-hotels/design-system/style.css" - -import { serverClient } from "@/lib/trpc/server" - -import MaxWidth from "@/components/MaxWidth" -import Content from "@/components/MyPages/AccountPage/Content" - -import styles from "./page.module.css" - -import type { LangParams, PageArgs, UriParams } from "@/types/params" - -export default async function AboutScandicFriends({ - params, -}: PageArgs) { - const accountPage = await serverClient().contentstack.accountPage.get({ - url: "/my-pages/overview", - lang: params.lang, - }) - - return ( - - - - ) -} diff --git a/app/[lang]/webview/layout.module.css b/app/[lang]/webview/layout.module.css index 4351f00fc..b03073834 100644 --- a/app/[lang]/webview/layout.module.css +++ b/app/[lang]/webview/layout.module.css @@ -1,6 +1,6 @@ .layout { padding-bottom: 7.7rem; font-family: var(--ff-fira-sans); - + background-color: var(--Brand-Coffee-Subtle); min-height: 100dvh; } diff --git a/app/[lang]/webview/layout.tsx b/app/[lang]/webview/layout.tsx index b6eef042d..77b081954 100644 --- a/app/[lang]/webview/layout.tsx +++ b/app/[lang]/webview/layout.tsx @@ -1,3 +1,6 @@ +import "@/app/globals.css" +import "@scandic-hotels/design-system/style.css" + import { firaMono, firaSans } from "@/app/[lang]/(live)/fonts" import styles from "./layout.module.css" diff --git a/app/[lang]/webview/about-scandic-friends/page.module.css b/app/[lang]/webview/loyalty-page/page.module.css similarity index 100% rename from app/[lang]/webview/about-scandic-friends/page.module.css rename to app/[lang]/webview/loyalty-page/page.module.css diff --git a/app/[lang]/webview/loyalty-page/page.tsx b/app/[lang]/webview/loyalty-page/page.tsx new file mode 100644 index 000000000..b173ff4c2 --- /dev/null +++ b/app/[lang]/webview/loyalty-page/page.tsx @@ -0,0 +1,44 @@ +import { notFound, redirect } 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 AboutScandicFriends({ + params, + searchParams, +}: PageArgs) { + if (!searchParams.uri) { + return notFound() + } + + const loyaltyPage = await serverClient({ + onError() { + const returnUrl = new URLSearchParams({ + returnurl: `${params.lang}/webview/${searchParams.uri}`, + }) + + const refreshUrl = `/${params.lang}/webview/refresh?${returnUrl.toString()}` + redirect(refreshUrl) + }, + }).contentstack.loyaltyPage.get({ + href: searchParams.uri, + locale: params.lang, + }) + + return ( +
+ {loyaltyPage.sidebar ? : null} + + + + +
+ ) +} diff --git a/app/[lang]/webview/my-pages/benefits/page.tsx b/app/[lang]/webview/my-pages/benefits/page.tsx deleted file mode 100644 index 194f9c7b8..000000000 --- a/app/[lang]/webview/my-pages/benefits/page.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import "@/app/globals.css" -import "@scandic-hotels/design-system/style.css" - -import { serverClient } from "@/lib/trpc/server" - -import MaxWidth from "@/components/MaxWidth" -import Content from "@/components/MyPages/AccountPage/Content" - -import styles from "./benefits.module.css" - -import type { LangParams, PageArgs, UriParams } from "@/types/params" - -export default async function MyPages({ - params, -}: PageArgs) { - const accountPage = await serverClient().contentstack.accountPage.get({ - url: "/my-pages/benefits", - lang: params.lang, - }) - - return ( - - - - ) -} diff --git a/app/[lang]/webview/my-pages/overview/overview.module.css b/app/[lang]/webview/my-pages/overview/overview.module.css deleted file mode 100644 index 9ff21ac9f..000000000 --- a/app/[lang]/webview/my-pages/overview/overview.module.css +++ /dev/null @@ -1,6 +0,0 @@ -.blocks { - display: grid; - gap: 4.2rem; - padding-left: 2rem; - padding-right: 2rem; -} diff --git a/app/[lang]/webview/my-pages/overview/page.tsx b/app/[lang]/webview/my-pages/overview/page.tsx deleted file mode 100644 index da4b03cfe..000000000 --- a/app/[lang]/webview/my-pages/overview/page.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import "@/app/globals.css" -import "@scandic-hotels/design-system/style.css" - -import { serverClient } from "@/lib/trpc/server" - -import MaxWidth from "@/components/MaxWidth" -import Content from "@/components/MyPages/AccountPage/Content" - -import styles from "./overview.module.css" - -import type { LangParams, PageArgs, UriParams } from "@/types/params" - -export default async function MyPages({ - params, -}: PageArgs) { - const accountPage = await serverClient().contentstack.accountPage.get({ - url: "/my-pages/overview", - lang: params.lang, - }) - - return ( - - - - ) -} diff --git a/app/[lang]/webview/my-pages/benefits/benefits.module.css b/app/[lang]/webview/my-pages/page.module.css similarity index 100% rename from app/[lang]/webview/my-pages/benefits/benefits.module.css rename to app/[lang]/webview/my-pages/page.module.css diff --git a/app/[lang]/webview/my-pages/page.tsx b/app/[lang]/webview/my-pages/page.tsx new file mode 100644 index 000000000..934320bb6 --- /dev/null +++ b/app/[lang]/webview/my-pages/page.tsx @@ -0,0 +1,67 @@ +import "@/app/globals.css" +import "@scandic-hotels/design-system/style.css" + +import { notFound, redirect } from "next/navigation" + +import { Lang } from "@/constants/languages" +import { overview } from "@/constants/routes/webviews" +import { _ } from "@/lib/translation" +import { serverClient } from "@/lib/trpc/server" + +import MaxWidth from "@/components/MaxWidth" +import Content from "@/components/MyPages/AccountPage/Webview/Content" +import Link from "@/components/TempDesignSystem/Link" +import Title from "@/components/Title" + +import styles from "./page.module.css" + +import type { LangParams, PageArgs, UriParams } from "@/types/params" + +function getLink(lang: Lang, uri: string) { + if (uri === overview[lang]) { + return { + title: _("Go to points"), + href: `/${lang}/webview/my-pages/points`, + } + } else { + return { + title: _("Go to membership overview"), + href: `/${lang}/webview/my-pages/overview`, + } + } +} + +export default async function MyPages({ + params, + searchParams, +}: PageArgs) { + if (!searchParams.uri) { + return notFound() + } + + const accountPage = await serverClient({ + onError() { + const returnUrl = new URLSearchParams({ + returnurl: `${params.lang}/webview/${searchParams.uri}`, + }) + + const refreshUrl = `/${params.lang}/webview/refresh?${returnUrl.toString()}` + redirect(refreshUrl) + }, + }).contentstack.accountPage.get({ + url: searchParams.uri, + lang: params.lang, + }) + + const link = getLink(params.lang, searchParams.uri) + + return ( + +
+ {_("Welcome")} + {link.title} +
+ +
+ ) +} diff --git a/app/[lang]/webview/my-pages/points/page.tsx b/app/[lang]/webview/my-pages/points/page.tsx deleted file mode 100644 index b1ed1b463..000000000 --- a/app/[lang]/webview/my-pages/points/page.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import "@/app/globals.css" -import "@scandic-hotels/design-system/style.css" - -import { serverClient } from "@/lib/trpc/server" - -import MaxWidth from "@/components/MaxWidth" -import Content from "@/components/MyPages/AccountPage/Content" - -import styles from "./points.module.css" - -import type { LangParams, PageArgs, UriParams } from "@/types/params" - -export default async function Points({ - params, -}: PageArgs) { - // const accountPage = await serverClient().contentstack.accountPage.get({ - // url: "/my-pages/points", - // lang: params.lang, - // }) - - return ( - -

POINTS

- {/* */} -
- ) -} diff --git a/app/[lang]/webview/my-pages/points/points.module.css b/app/[lang]/webview/my-pages/points/points.module.css deleted file mode 100644 index 9ff21ac9f..000000000 --- a/app/[lang]/webview/my-pages/points/points.module.css +++ /dev/null @@ -1,6 +0,0 @@ -.blocks { - display: grid; - gap: 4.2rem; - padding-left: 2rem; - padding-right: 2rem; -} diff --git a/components/MyPages/AccountPage/Webview/Content.tsx b/components/MyPages/AccountPage/Webview/Content.tsx new file mode 100644 index 000000000..97fd548d9 --- /dev/null +++ b/components/MyPages/AccountPage/Webview/Content.tsx @@ -0,0 +1,77 @@ +import JsonToHtml from "@/components/JsonToHtml" +import Overview from "@/components/MyPages/Blocks/Overview" +import Shortcuts from "@/components/MyPages/Blocks/Shortcuts" + +import { + AccountPageContentProps, + ContentProps, +} from "@/types/components/myPages/myPage/accountPage" +import { + ContentEntries, + DynamicContentComponents, +} from "@/types/components/myPages/myPage/enums" + +function DynamicComponent({ component, props }: AccountPageContentProps) { + switch (component) { + case DynamicContentComponents.membership_overview: + return + default: + return null + } +} + +export default function Content({ lang, content }: ContentProps) { + return ( + <> + {content.map((item) => { + switch (item.__typename) { + case ContentEntries.AccountPageContentDynamicContent: + const link = item.dynamic_content.link.linkConnection.edges.length + ? { + href: + item.dynamic_content.link.linkConnection.edges[0].node + .original_url || + `/${lang}${item.dynamic_content.link.linkConnection.edges[0].node.url}`, + text: item.dynamic_content.link.link_text, + } + : null + + const componentProps = { + lang, + title: item.dynamic_content.title, + // TODO: rename preamble to subtitle in Contentstack? + subtitle: item.dynamic_content.preamble, + ...(link && { link }), + } + return ( + + ) + case ContentEntries.AccountPageContentShortcuts: + return ( + + ) + case ContentEntries.AccountPageContentTextContent: + return ( +
+ +
+ ) + default: + return null + } + })} + + ) +} diff --git a/constants/routes/webviews.ts b/constants/routes/webviews.ts new file mode 100644 index 000000000..f4ec8ef58 --- /dev/null +++ b/constants/routes/webviews.ts @@ -0,0 +1,73 @@ +/** + * @file Due to these records being used in next.config.js, and that is required + * to be a js file, we use jsdoc to type these. + */ + +/** + * These are routes that define code entries for My pages + */ + +/** @type {import('@/types/routes').LangRoute} */ +const myPages = { + da: "/da/webview/mine-sider", + de: "/de/webview/mein-profil", + en: "/en/webview/my-pages", + fi: "/fi/webview/minun-sivujani", + no: "/no/webview/mine-sider", + sv: "/sv/webview/mina-sidor", +} + +/** @type {import('@/types/routes').LangRoute} */ +export const overview = { + da: `${myPages.da}/oversigt`, + de: `${myPages.de}/uberblick`, + en: `${myPages.en}/overview`, + fi: `${myPages.fi}/yleiskatsaus`, + no: `${myPages.no}/oversikt`, + sv: `${myPages.sv}/oversikt`, +} + +/** @type {import('@/types/routes').LangRoute} */ +export const benefits = { + da: `${myPages.da}/fordele`, + de: `${myPages.de}/vorteile`, + en: `${myPages.en}/benefits`, + fi: `${myPages.fi}/etuja`, + no: `${myPages.no}/fordeler`, + sv: `${myPages.sv}/formaner`, +} + +/** @type {import('@/types/routes').LangRoute} */ +export const points = { + da: `${myPages.da}/point`, + de: `${myPages.de}/punkte`, + en: `${myPages.en}/points`, + fi: `${myPages.fi}/pisteitä`, + no: `${myPages.no}/poeng`, + sv: `${myPages.sv}/poang`, +} + +/** @type {import('@/types/routes').LangRoute} */ +export const programOverview = { + da: `/da/webview/ophold`, + de: `/de/webview/aufenthalte`, + en: `/en/webview/stays`, + fi: `/fi/webview/oleskeluni`, + no: `/no/webview/opphold`, + sv: `/sv/webview/vistelser`, +} + +export const webviews = [ + ...Object.values(benefits), + ...Object.values(overview), + ...Object.values(points), + ...Object.values(programOverview), +] + +export const myPagesWebviews = [ + ...Object.values(benefits), + ...Object.values(overview), + ...Object.values(points), +] + +export const loyaltyPagesWebviews = [...Object.values(programOverview)] diff --git a/middlewares/webView.ts b/middlewares/webView.ts index 7d52e2ae9..3db536475 100644 --- a/middlewares/webView.ts +++ b/middlewares/webView.ts @@ -1,19 +1,42 @@ import { type NextMiddleware, NextResponse } from "next/server" import { findLang } from "@/constants/languages" +import { + loyaltyPagesWebviews, + myPagesWebviews, + webviews, +} from "@/constants/routes/webviews" import { env } from "@/env/server" -import { badRequest, internalServerError } from "@/server/errors/next" +import { badRequest } from "@/server/errors/next" import { decryptData } from "@/utils/aes" import type { MiddlewareMatcher } from "@/types/middleware" export const middleware: NextMiddleware = async (request) => { + const { nextUrl } = request + const lang = findLang(nextUrl.pathname) + + const pathNameWithoutLang = nextUrl.pathname.replace(`/${lang}/webview`, "") + const searchParams = new URLSearchParams(request.nextUrl.searchParams) + searchParams.set("uri", pathNameWithoutLang) + const webviewToken = request.cookies.get("webviewToken") if (webviewToken) { // since the token exists, this is a subsequent visit // we're done, allow it - return NextResponse.next() + if (myPagesWebviews.includes(nextUrl.pathname)) { + return NextResponse.rewrite( + new URL(`/${lang}/webview/my-pages?${searchParams.toString()}`, nextUrl) + ) + } else if (loyaltyPagesWebviews.includes(nextUrl.pathname)) { + return NextResponse.rewrite( + new URL( + `/${lang}/webview/loyalty-page?${searchParams.toString()}`, + nextUrl + ) + ) + } } // Authorization header is required for webviews @@ -37,13 +60,31 @@ export const middleware: NextMiddleware = async (request) => { authorization ) - const response = NextResponse.next() - response.cookies.set("webviewToken", decryptedData, { - httpOnly: true, - secure: true, - }) - - return response + if (myPagesWebviews.includes(nextUrl.pathname)) { + return NextResponse.rewrite( + new URL( + `/${lang}/webview/my-pages?${searchParams.toString()}`, + nextUrl + ), + { + headers: { + "Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`, + }, + } + ) + } else if (loyaltyPagesWebviews.includes(nextUrl.pathname)) { + return NextResponse.rewrite( + new URL( + `/${lang}/webview/loyalty-page?${searchParams.toString()}`, + nextUrl + ), + { + headers: { + "Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`, + }, + } + ) + } } catch (e) { if (e instanceof Error) { console.error(`${e.name}: ${e.message}`) @@ -55,7 +96,6 @@ export const middleware: NextMiddleware = async (request) => { export const matcher: MiddlewareMatcher = (request) => { const { nextUrl } = request - const lang = findLang(nextUrl.pathname) - const pathNameWithoutLang = nextUrl.pathname.replace(`/${lang}`, "") - return pathNameWithoutLang.startsWith("/webview/") + + return webviews.includes(nextUrl.pathname) } diff --git a/server/trpc.ts b/server/trpc.ts index eb0c5bef6..8facef2ef 100644 --- a/server/trpc.ts +++ b/server/trpc.ts @@ -29,7 +29,8 @@ export const contentstackProcedure = t.procedure.use(async function (opts) { }) export const protectedProcedure = t.procedure.use(async function (opts) { const authRequired = opts.meta?.authRequired ?? true - const session = await (await opts.ctx).session + const ctx = await opts.ctx + const session = ctx.session if (!authRequired && env.NODE_ENV === "development") { console.info( `❌❌❌❌ You are opting out of authorization, if its done on purpose maybe you should use the publicProcedure instead. ❌❌❌❌` diff --git a/test.js b/test.js new file mode 100644 index 000000000..6bb65f394 --- /dev/null +++ b/test.js @@ -0,0 +1,84 @@ +function base64ToUint8Array(base64String) { + const binaryString = atob(base64String) + const byteArray = new Uint8Array(binaryString.length) + for (let i = 0; i < binaryString.length; i++) { + byteArray[i] = binaryString.charCodeAt(i) + } + return byteArray +} + +function utf8ToUint8Array(utf8String) { + return new TextEncoder().encode(utf8String) +} + +function uint8ArrayToBase64(uint8Array) { + let binaryString = "" + const len = uint8Array.byteLength + for (let i = 0; i < len; i++) { + binaryString += String.fromCharCode(uint8Array[i]) + } + return btoa(binaryString) +} + +function uint8ArrayToUtf8(uint8Array) { + return new TextDecoder().decode(uint8Array) +} + +async function decryptData(keyBase64, ivBase64, encryptedDataBase64) { + const keyBuffer = await crypto.subtle.importKey( + "raw", + base64ToUint8Array(keyBase64), + "AES-CBC", + false, + ["decrypt"] + ) + + const encryptedDataBuffer = base64ToUint8Array(encryptedDataBase64) + const ivBuffer = base64ToUint8Array(ivBase64) + const decryptedDataBuffer = await crypto.subtle.decrypt( + { name: "AES-CBC", iv: ivBuffer }, + keyBuffer, + encryptedDataBuffer + ) + + const decryptedData = uint8ArrayToUtf8(new Uint8Array(decryptedDataBuffer)) + return decryptedData +} +async function encryptData(keyBase64, ivBase64, data) { + const keyBuffer = await crypto.subtle.importKey( + "raw", + base64ToUint8Array(keyBase64), + "AES-CBC", + false, + ["encrypt"] + ) + + const dataBuffer = utf8ToUint8Array(data) + const ivBuffer = base64ToUint8Array(ivBase64) + const encryptedDataBuffer = await crypto.subtle.encrypt( + { name: "AES-CBC", iv: ivBuffer }, + keyBuffer, + dataBuffer + ) + + const encryptedData = uint8ArrayToBase64(new Uint8Array(encryptedDataBuffer)) + return encryptedData +} + +const keyBase64 = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=" +const ivBase64 = btoa("abcdefghijklmnop") +const data = "_0XBPWQQ_60d7f996-04e1-4e2f-b7e9-1d1139b808ea" + +// Encrypt the data +const encryptedData = await encryptData(keyBase64, ivBase64, data) + +// Decrypt the data +const decryptedData = await decryptData(keyBase64, ivBase64, encryptedData) + +// const tegwpjke = await encryptData( +// "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=", +// btoa("abcdefghijklmnop"), +// "_0XBPWQQ_60d7f996-04e1-4e2f-b7e9-1d1139b808ea" +// ) + +// console.log(tegwpjke) From 777fd1e5b6aaf6f4c496a58301a2a1f171fd675b Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Tue, 7 May 2024 12:10:25 +0200 Subject: [PATCH 05/20] fix: set cookie in header on rewrite --- app/[lang]/webview/my-pages/page.tsx | 18 ++++++++++++------ constants/routes/webviews.ts | 12 ++++++------ middlewares/webView.ts | 5 +++++ server/context.ts | 15 +++++++++++++++ 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/app/[lang]/webview/my-pages/page.tsx b/app/[lang]/webview/my-pages/page.tsx index 934320bb6..7374147df 100644 --- a/app/[lang]/webview/my-pages/page.tsx +++ b/app/[lang]/webview/my-pages/page.tsx @@ -10,8 +10,6 @@ import { serverClient } from "@/lib/trpc/server" import MaxWidth from "@/components/MaxWidth" import Content from "@/components/MyPages/AccountPage/Webview/Content" -import Link from "@/components/TempDesignSystem/Link" -import Title from "@/components/Title" import styles from "./page.module.css" @@ -39,6 +37,18 @@ export default async function MyPages({ return notFound() } + // Check if the access token is valid. If not, redirect to the refresh page. + await serverClient({ + onError(opts) { + const returnUrl = new URLSearchParams({ + returnurl: `${params.lang}/webview/${searchParams.uri}`, + }) + + const refreshUrl = `/${params.lang}/webview/refresh?${returnUrl.toString()}` + redirect(refreshUrl) + }, + }).user.get() + const accountPage = await serverClient({ onError() { const returnUrl = new URLSearchParams({ @@ -57,10 +67,6 @@ export default async function MyPages({ return ( -
- {_("Welcome")} - {link.title} -
) diff --git a/constants/routes/webviews.ts b/constants/routes/webviews.ts index f4ec8ef58..ed55a0b71 100644 --- a/constants/routes/webviews.ts +++ b/constants/routes/webviews.ts @@ -49,12 +49,12 @@ export const points = { /** @type {import('@/types/routes').LangRoute} */ export const programOverview = { - da: `/da/webview/ophold`, - de: `/de/webview/aufenthalte`, - en: `/en/webview/stays`, - fi: `/fi/webview/oleskeluni`, - no: `/no/webview/opphold`, - sv: `/sv/webview/vistelser`, + da: `/da/webview/about-scandic-friends`, + de: `/de/webview/about-scandic-friends`, + en: `/en/webview/about-scandic-friends`, + fi: `/fi/webview/about-scandic-friends`, + no: `/no/webview/om-scandic-friends`, + sv: `/sv/webview/om-scandic-friends`, } export const webviews = [ diff --git a/middlewares/webView.ts b/middlewares/webView.ts index 3db536475..5d0e6e135 100644 --- a/middlewares/webView.ts +++ b/middlewares/webView.ts @@ -1,3 +1,4 @@ +import { notFound } from "next/navigation" import { type NextMiddleware, NextResponse } from "next/server" import { findLang } from "@/constants/languages" @@ -36,6 +37,8 @@ export const middleware: NextMiddleware = async (request) => { nextUrl ) ) + } else { + return notFound() } } @@ -69,6 +72,7 @@ export const middleware: NextMiddleware = async (request) => { { headers: { "Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`, + Cookie: `webviewToken=${decryptedData}`, }, } ) @@ -81,6 +85,7 @@ export const middleware: NextMiddleware = async (request) => { { headers: { "Set-Cookie": `webviewToken=${decryptedData}; Secure; HttpOnly; Path=/; SameSite=Strict;`, + Cookie: `webviewToken=${decryptedData}`, }, } ) diff --git a/server/context.ts b/server/context.ts index c81fe1024..aebcd8e3e 100644 --- a/server/context.ts +++ b/server/context.ts @@ -33,6 +33,21 @@ export function createContextInner(opts: CreateContextOptions) { export function createContext() { const h = headers() + // const cookie = cookies() + // const webviewTokenCookie = cookie.get("webviewToken") + + // if (webviewTokenCookie) { + // // since the token exists, this is a subsequent visit + // // we're done, allow it + // return createContextInner({ + // session: { + // token: { access_token: webviewTokenCookie.value }, + // }, + // }) + // } + + // const session = await auth() + return createContextInner({ auth, lang: h.get("x-lang") as Lang, From 9e4f41ee462db1bdc044cd16db8f2c5fa34988ee Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Thu, 16 May 2024 16:57:22 +0200 Subject: [PATCH 06/20] fix: redirect users to /refresh on unauth and mod webview links --- .../{[uid] => }/layout.module.css | 0 .../[contentType]/{[uid] => }/layout.tsx | 0 .../[contentType]/{[uid] => }/page.tsx | 0 app/[lang]/webview/loyalty-page/page.tsx | 20 ++---- app/[lang]/webview/my-pages/page.tsx | 45 +------------ app/[lang]/webview/refresh/page.tsx | 3 + components/Loyalty/Blocks/WebView/index.tsx | 66 +++++++++++++++++++ .../MyPages/AccountPage/Webview/Content.tsx | 11 +++- components/MyPages/Breadcrumbs/index.tsx | 14 +++- .../Header/Hamburger/hamburger.module.css | 16 +++++ components/MyPages/Header/Hamburger/index.tsx | 11 ++++ .../MyPages/Header/LanguageSwitcher/index.tsx | 17 +++++ .../LanguageSwitcher/language.module.css | 15 +++++ components/MyPages/Header/Logo/index.tsx | 34 ++++++++++ .../MyPages/Header/Logo/logo.module.css | 4 ++ components/MyPages/Header/header.module.css | 25 +++++++ components/MyPages/Header/index.tsx | 19 ++++++ constants/routes/webviews.ts | 13 ++++ lib/graphql/Query/Logo.graphql | 16 +++++ lib/trpc/server.ts | 11 ++++ middlewares/cmsContent.ts | 3 +- middlewares/webView.ts | 59 ++++++++++------- server/context.ts | 21 ++---- .../routers/contentstack/breadcrumbs/input.ts | 8 +++ .../routers/contentstack/breadcrumbs/query.ts | 2 +- server/trpc.ts | 5 +- types/params.ts | 2 +- types/requests/myPages/logo.ts | 10 +++ utils/webviews.ts | 13 ++++ 29 files changed, 358 insertions(+), 105 deletions(-) rename app/[lang]/(live)/(public)/[contentType]/{[uid] => }/layout.module.css (100%) rename app/[lang]/(live)/(public)/[contentType]/{[uid] => }/layout.tsx (100%) rename app/[lang]/(live)/(public)/[contentType]/{[uid] => }/page.tsx (100%) create mode 100644 app/[lang]/webview/refresh/page.tsx create mode 100644 components/Loyalty/Blocks/WebView/index.tsx create mode 100644 components/MyPages/Header/Hamburger/hamburger.module.css create mode 100644 components/MyPages/Header/Hamburger/index.tsx create mode 100644 components/MyPages/Header/LanguageSwitcher/index.tsx create mode 100644 components/MyPages/Header/LanguageSwitcher/language.module.css create mode 100644 components/MyPages/Header/Logo/index.tsx create mode 100644 components/MyPages/Header/Logo/logo.module.css create mode 100644 components/MyPages/Header/header.module.css create mode 100644 components/MyPages/Header/index.tsx create mode 100644 lib/graphql/Query/Logo.graphql create mode 100644 server/routers/contentstack/breadcrumbs/input.ts create mode 100644 types/requests/myPages/logo.ts create mode 100644 utils/webviews.ts diff --git a/app/[lang]/(live)/(public)/[contentType]/[uid]/layout.module.css b/app/[lang]/(live)/(public)/[contentType]/layout.module.css similarity index 100% rename from app/[lang]/(live)/(public)/[contentType]/[uid]/layout.module.css rename to app/[lang]/(live)/(public)/[contentType]/layout.module.css diff --git a/app/[lang]/(live)/(public)/[contentType]/[uid]/layout.tsx b/app/[lang]/(live)/(public)/[contentType]/layout.tsx similarity index 100% rename from app/[lang]/(live)/(public)/[contentType]/[uid]/layout.tsx rename to app/[lang]/(live)/(public)/[contentType]/layout.tsx diff --git a/app/[lang]/(live)/(public)/[contentType]/[uid]/page.tsx b/app/[lang]/(live)/(public)/[contentType]/page.tsx similarity index 100% rename from app/[lang]/(live)/(public)/[contentType]/[uid]/page.tsx rename to app/[lang]/(live)/(public)/[contentType]/page.tsx diff --git a/app/[lang]/webview/loyalty-page/page.tsx b/app/[lang]/webview/loyalty-page/page.tsx index b173ff4c2..8d63f02ad 100644 --- a/app/[lang]/webview/loyalty-page/page.tsx +++ b/app/[lang]/webview/loyalty-page/page.tsx @@ -1,14 +1,14 @@ -import { notFound, redirect } from "next/navigation" +import { notFound } from "next/navigation" import { serverClient } from "@/lib/trpc/server" -import { Blocks } from "@/components/Loyalty/Blocks" +import { Blocks } from "@/components/Loyalty/Blocks/WebView" 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" +import { LangParams, PageArgs, UriParams } from "@/types/params" export default async function AboutScandicFriends({ params, @@ -18,26 +18,16 @@ export default async function AboutScandicFriends({ return notFound() } - const loyaltyPage = await serverClient({ - onError() { - const returnUrl = new URLSearchParams({ - returnurl: `${params.lang}/webview/${searchParams.uri}`, - }) - - const refreshUrl = `/${params.lang}/webview/refresh?${returnUrl.toString()}` - redirect(refreshUrl) - }, - }).contentstack.loyaltyPage.get({ + const loyaltyPage = await serverClient().contentstack.loyaltyPage.get({ href: searchParams.uri, locale: params.lang, }) - return (
{loyaltyPage.sidebar ? : null} - +
) diff --git a/app/[lang]/webview/my-pages/page.tsx b/app/[lang]/webview/my-pages/page.tsx index 7374147df..e66326643 100644 --- a/app/[lang]/webview/my-pages/page.tsx +++ b/app/[lang]/webview/my-pages/page.tsx @@ -1,10 +1,8 @@ import "@/app/globals.css" import "@scandic-hotels/design-system/style.css" -import { notFound, redirect } from "next/navigation" +import { notFound } from "next/navigation" -import { Lang } from "@/constants/languages" -import { overview } from "@/constants/routes/webviews" import { _ } from "@/lib/translation" import { serverClient } from "@/lib/trpc/server" @@ -13,21 +11,7 @@ import Content from "@/components/MyPages/AccountPage/Webview/Content" import styles from "./page.module.css" -import type { LangParams, PageArgs, UriParams } from "@/types/params" - -function getLink(lang: Lang, uri: string) { - if (uri === overview[lang]) { - return { - title: _("Go to points"), - href: `/${lang}/webview/my-pages/points`, - } - } else { - return { - title: _("Go to membership overview"), - href: `/${lang}/webview/my-pages/overview`, - } - } -} +import { LangParams, PageArgs, UriParams } from "@/types/params" export default async function MyPages({ params, @@ -37,34 +21,11 @@ export default async function MyPages({ return notFound() } - // Check if the access token is valid. If not, redirect to the refresh page. - await serverClient({ - onError(opts) { - const returnUrl = new URLSearchParams({ - returnurl: `${params.lang}/webview/${searchParams.uri}`, - }) - - const refreshUrl = `/${params.lang}/webview/refresh?${returnUrl.toString()}` - redirect(refreshUrl) - }, - }).user.get() - - const accountPage = await serverClient({ - onError() { - const returnUrl = new URLSearchParams({ - returnurl: `${params.lang}/webview/${searchParams.uri}`, - }) - - const refreshUrl = `/${params.lang}/webview/refresh?${returnUrl.toString()}` - redirect(refreshUrl) - }, - }).contentstack.accountPage.get({ + const accountPage = await serverClient().contentstack.accountPage.get({ url: searchParams.uri, lang: params.lang, }) - const link = getLink(params.lang, searchParams.uri) - return ( diff --git a/app/[lang]/webview/refresh/page.tsx b/app/[lang]/webview/refresh/page.tsx new file mode 100644 index 000000000..1fe6ee753 --- /dev/null +++ b/app/[lang]/webview/refresh/page.tsx @@ -0,0 +1,3 @@ +export default function Refresh() { + return
Hey you've been refreshed
+} diff --git a/components/Loyalty/Blocks/WebView/index.tsx b/components/Loyalty/Blocks/WebView/index.tsx new file mode 100644 index 000000000..f5b5fea0f --- /dev/null +++ b/components/Loyalty/Blocks/WebView/index.tsx @@ -0,0 +1,66 @@ +import JsonToHtml from "@/components/JsonToHtml" +import DynamicContentBlock from "@/components/Loyalty/Blocks/DynamicContent" +import Shortcuts from "@/components/MyPages/Blocks/Shortcuts" +import { modWebviewLink } from "@/utils/webviews" + +import CardGrid from "../CardGrid" + +import type { BlocksProps } from "@/types/components/loyalty/blocks" +import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums" +import { LangParams } from "@/types/params" + +export function Blocks({ lang, blocks }: BlocksProps & LangParams) { + return blocks.map((block) => { + switch (block.__typename) { + case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksCardGrid: + const cardGrid = { + ...block.card_grid, + cards: block.card_grid.cards.map((card) => { + return { + ...card, + link: card.link + ? { ...card.link, href: modWebviewLink(card.link.href, lang) } + : undefined, + } + }), + } + + return + case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent: + return ( +
+ +
+ ) + case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksDynamicContent: + const dynamicContent = { + ...block.dynamic_content, + linK: block.dynamic_content.link + ? { + ...block.dynamic_content.link, + href: modWebviewLink(block.dynamic_content.link.href, lang), + } + : undefined, + } + + return + case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: + const shortcuts = block.shortcuts.shortcuts.map((shortcut) => ({ + ...shortcut, + url: modWebviewLink(shortcut.url, lang), + })) + return ( + + ) + default: + return null + } + }) +} diff --git a/components/MyPages/AccountPage/Webview/Content.tsx b/components/MyPages/AccountPage/Webview/Content.tsx index 97fd548d9..29cee02f2 100644 --- a/components/MyPages/AccountPage/Webview/Content.tsx +++ b/components/MyPages/AccountPage/Webview/Content.tsx @@ -1,6 +1,7 @@ import JsonToHtml from "@/components/JsonToHtml" import Overview from "@/components/MyPages/Blocks/Overview" import Shortcuts from "@/components/MyPages/Blocks/Shortcuts" +import { modWebviewLink } from "@/utils/webviews" import { AccountPageContentProps, @@ -31,7 +32,7 @@ export default function Content({ lang, content }: ContentProps) { href: item.dynamic_content.link.linkConnection.edges[0].node .original_url || - `/${lang}${item.dynamic_content.link.linkConnection.edges[0].node.url}`, + `/${lang}/webview${item.dynamic_content.link.linkConnection.edges[0].node.url}`, text: item.dynamic_content.link.link_text, } : null @@ -50,9 +51,15 @@ export default function Content({ lang, content }: ContentProps) { /> ) case ContentEntries.AccountPageContentShortcuts: + const shortcuts = item.shortcuts.shortcuts.map((shortcut) => { + return { + ...shortcut, + url: modWebviewLink(shortcut.url, lang), + } + }) return ( diff --git a/components/MyPages/Breadcrumbs/index.tsx b/components/MyPages/Breadcrumbs/index.tsx index f0fa8d566..7ae70c5f4 100644 --- a/components/MyPages/Breadcrumbs/index.tsx +++ b/components/MyPages/Breadcrumbs/index.tsx @@ -1,3 +1,4 @@ +import { Lang } from "@/constants/languages" import { _ } from "@/lib/translation" import { serverClient } from "@/lib/trpc/server" @@ -6,8 +7,17 @@ import BreadcrumbsWithLink from "./BreadcrumbWithLink" import styles from "./breadcrumbs.module.css" -export default async function Breadcrumbs() { - const breadcrumbs = await serverClient().contentstack.breadcrumbs.get() +export default async function Breadcrumbs({ + href, + locale, +}: { + href: string + locale: Lang +}) { + const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({ + href, + locale, + }) return (