diff --git a/components/Current/Header/TopMenu/index.tsx b/components/Current/Header/TopMenu/index.tsx
index 69d23798e..be0e99d97 100644
--- a/components/Current/Header/TopMenu/index.tsx
+++ b/components/Current/Header/TopMenu/index.tsx
@@ -1,7 +1,6 @@
import { logout } from "@/constants/routes/handleAuth"
import { serverClient } from "@/lib/trpc/server"
-import { auth } from "@/auth"
import Link from "@/components/TempDesignSystem/Link"
import { getIntl } from "@/i18n"
@@ -23,9 +22,7 @@ export default async function TopMenu({
lang,
}: TopMenuProps) {
const { formatMessage } = await getIntl()
- const session = await auth()
- const user = session ? await serverClient().user.get() : null
-
+ const user = await serverClient().user.name()
return (
@@ -46,7 +43,7 @@ export default async function TopMenu({
))}
- {session ? (
+ {user ? (
<>
{user ? (
li::marker {
color: var(--Primary-Light-On-Surface-Accent);
}
-.ul:has(.heart),
-.ul:has(.check) {
+.li:has(.heart),
+.li:has(.check) {
list-style: none;
}
+
.li:has(.heart),
.li:has(.check) {
display: flex;
@@ -54,7 +54,7 @@
}
.container {
- display: "grid";
+ display: grid;
gap: var(--Spacing-x3);
max-width: 1197px;
}
diff --git a/components/JsonToHtml/renderOptions.tsx b/components/JsonToHtml/renderOptions.tsx
index f585dc768..b3d0cef9a 100644
--- a/components/JsonToHtml/renderOptions.tsx
+++ b/components/JsonToHtml/renderOptions.tsx
@@ -497,7 +497,10 @@ export const renderOptions: RenderOptions = {
className={styles.ul}
style={
numberOfRows
- ? { gridTemplateRows: `repeat(${numberOfRows}, auto)` }
+ ? {
+ gridTemplateRows: `repeat(${numberOfRows}, auto)`,
+ gridAutoFlow: "column",
+ }
: {}
}
>
diff --git a/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx b/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx
index f174ee491..e1594d97f 100644
--- a/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx
+++ b/components/Loyalty/Blocks/DynamicContent/OverviewTable/index.tsx
@@ -124,13 +124,15 @@ function reducer(state: any, action: OverviewTableReducerAction) {
}
}
-export default function OverviewTable({ user }: OverviewTableProps) {
+export default function OverviewTable({
+ activeMembership,
+}: OverviewTableProps) {
const intl = useIntl()
const lang = Lang.en
const levelsData = levelsTranslations[lang]
const [selectionState, dispatch] = useReducer(
reducer,
- { user, lang },
+ { activeMembership, lang },
getInitialState
)
@@ -150,10 +152,6 @@ export default function OverviewTable({ user }: OverviewTableProps) {
value: level.level,
}))
- const activeMembership = user?.memberships
- ? getMembership(user.memberships)
- : null
-
let activeMembershipLevel: membershipLevels | null = null
if (activeMembership?.membershipLevel) {
activeMembershipLevel = membershipLevels[activeMembership?.membershipLevel]
diff --git a/components/Loyalty/Blocks/DynamicContent/index.tsx b/components/Loyalty/Blocks/DynamicContent/index.tsx
index 3fb13b2af..235fe9ecc 100644
--- a/components/Loyalty/Blocks/DynamicContent/index.tsx
+++ b/components/Loyalty/Blocks/DynamicContent/index.tsx
@@ -21,9 +21,7 @@ import type {
import { LoyaltyComponentEnum } from "@/types/components/loyalty/enums"
async function DynamicComponentBlock({ component }: DynamicComponentProps) {
- const session = await auth()
-
- const user = session ? await serverClient().user.get() : null
+ const membershipLevel = await serverClient().user.membershipLevel()
switch (component) {
case LoyaltyComponentEnum.how_it_works:
@@ -31,7 +29,7 @@ async function DynamicComponentBlock({ component }: DynamicComponentProps) {
case LoyaltyComponentEnum.loyalty_levels:
return
case LoyaltyComponentEnum.overview_table:
- return
+ return
default:
return null
}
diff --git a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx
index e940aab0f..0a49a80e3 100644
--- a/components/Loyalty/Sidebar/JoinLoyalty/index.tsx
+++ b/components/Loyalty/Sidebar/JoinLoyalty/index.tsx
@@ -1,6 +1,5 @@
-import { login } from "@/constants/routes/handleAuth"
+import { serverClient } from "@/lib/trpc/server"
-import { auth } from "@/auth"
import ArrowRight from "@/components/Icons/ArrowRight"
import { ScandicFriends } from "@/components/Levels"
import Button from "@/components/TempDesignSystem/Button"
@@ -21,9 +20,10 @@ export default async function JoinLoyaltyContact({
lang,
}: JoinLoyaltyContactProps & LangParams) {
const { formatMessage } = await getIntl()
- const session = await auth()
+ const user = await serverClient().user.name()
- if (session) {
+ // Check if we have user, that means we are logged in.
+ if (user) {
return null
}
return (
diff --git a/components/Loyalty/Sidebar/MyPagesNavigation/index.tsx b/components/Loyalty/Sidebar/MyPagesNavigation/index.tsx
index 72bc034d3..587074062 100644
--- a/components/Loyalty/Sidebar/MyPagesNavigation/index.tsx
+++ b/components/Loyalty/Sidebar/MyPagesNavigation/index.tsx
@@ -1,12 +1,14 @@
-import { auth } from "@/auth"
+import { serverClient } from "@/lib/trpc/server"
+
import MyPagesSidebar from "@/components/MyPages/Sidebar"
import { LangParams } from "@/types/params"
export default async function MyPagesNavigation({ lang }: LangParams) {
- const session = await auth()
+ const user = await serverClient().user.name()
- if (!session) {
+ // Check if we have user, that means we are logged in.
+ if (!user) {
return null
}
diff --git a/server/context.ts b/server/context.ts
index 537719540..2c4c235ee 100644
--- a/server/context.ts
+++ b/server/context.ts
@@ -10,7 +10,7 @@ import { unauthorizedError } from "./errors/trpc"
typeof auth
type CreateContextOptions = {
- auth: () => Promise
+ auth: () => Promise
lang: Lang
pathname: string
uid?: string | null
@@ -50,7 +50,7 @@ export function createContext() {
const session = await auth()
const webToken = webviewTokenCookie?.value
if (!session?.token && !webToken) {
- throw unauthorizedError()
+ return null
}
return session || ({ token: { access_token: webToken } } as Session)
diff --git a/server/routers/user/query.ts b/server/routers/user/query.ts
index c80a086c5..30a77d9c4 100644
--- a/server/routers/user/query.ts
+++ b/server/routers/user/query.ts
@@ -1,9 +1,13 @@
import * as api from "@/lib/api"
-import { protectedProcedure, router } from "@/server/trpc"
+import {
+ protectedProcedure,
+ router,
+ safeProtectedProcedure,
+} from "@/server/trpc"
import { countries } from "@/components/TempDesignSystem/Form/Country/countries"
import * as maskValue from "@/utils/maskValue"
-import { getMembershipCards } from "@/utils/user"
+import { getMembership, getMembershipCards } from "@/utils/user"
import {
friendTransactionsInput,
@@ -19,6 +23,37 @@ import {
} from "./output"
import { benefits, extendedUser, nextLevelPerks } from "./temp"
+import type { Session } from "next-auth"
+
+async function getVerifiedUser({ session }: { session: Session }) {
+ const apiResponse = await api.get(api.endpoints.v1.profile, {
+ cache: "no-store",
+ headers: {
+ Authorization: `Bearer ${session.token.access_token}`,
+ },
+ })
+
+ if (!apiResponse.ok) {
+ return null
+ }
+
+ const apiJson = await apiResponse.json()
+ if (!apiJson.data?.attributes) {
+ console.error(`User has no data - (user: ${JSON.stringify(session.user)})`)
+ return null
+ }
+
+ const verifiedData = getUserSchema.safeParse(apiJson.data.attributes)
+ if (!verifiedData.success) {
+ console.info(
+ `Failed to validate User - (User: ${JSON.stringify(session.user)})`
+ )
+ console.error(verifiedData.error)
+ return null
+ }
+ return verifiedData
+}
+
function fakingRequest(payload: T): Promise {
return new Promise((resolve) => {
setTimeout(() => {
@@ -31,45 +66,9 @@ export const userQueryRouter = router({
get: protectedProcedure
.input(getUserInputSchema)
.query(async function getUser({ ctx, input }) {
- const apiResponse = await api.get(api.endpoints.v1.profile, {
- cache: "no-store",
- headers: {
- Authorization: `Bearer ${ctx.session.token.access_token}`,
- },
- })
+ const verifiedData = await getVerifiedUser({ session: ctx.session })
- if (!apiResponse.ok) {
- // switch (apiResponse.status) {
- // case 400:
- // throw badRequestError()
- // case 401:
- // throw unauthorizedError()
- // case 403:
- // throw forbiddenError()
- // default:
- // throw internalServerError()
- // }
- console.info(`API Response Failed - Getting User`)
- console.info(`User: (${JSON.stringify(ctx.session.user)})`)
- console.error(apiResponse)
- return null
- }
-
- const apiJson = await apiResponse.json()
- if (!apiJson.data?.attributes) {
- // throw notFound(apiJson)
- console.error(
- `User has no data - (user: ${JSON.stringify(ctx.session.user)})`
- )
- return null
- }
-
- const verifiedData = getUserSchema.safeParse(apiJson.data.attributes)
- if (!verifiedData.success) {
- console.info(
- `Failed to validate User - (User: ${JSON.stringify(ctx.session.user)})`
- )
- console.error(verifiedData.error)
+ if (!verifiedData) {
return null
}
@@ -119,7 +118,33 @@ export const userQueryRouter = router({
return user
}),
+ name: safeProtectedProcedure.query(async function ({ ctx }) {
+ if (!ctx.session) {
+ return null
+ }
+ const verifiedData = await getVerifiedUser({ session: ctx.session })
+ if (!verifiedData) {
+ return null
+ }
+ return {
+ firstName: verifiedData.data.firstName,
+ lastName: verifiedData.data.lastName,
+ }
+ }),
+ membershipLevel: safeProtectedProcedure.query(async function ({ ctx }) {
+ if (!ctx.session) {
+ return null
+ }
+ const verifiedData = await getVerifiedUser({ session: ctx.session })
+
+ if (!verifiedData) {
+ return null
+ }
+
+ const membershipLevel = getMembership(verifiedData.data.memberships)
+ return membershipLevel
+ }),
benefits: router({
current: protectedProcedure.query(async function (opts) {
// TODO: Make request to get user data from Scandic API
diff --git a/server/trpc.ts b/server/trpc.ts
index 3f7cd3c70..1caf32b53 100644
--- a/server/trpc.ts
+++ b/server/trpc.ts
@@ -2,10 +2,16 @@ import { initTRPC } from "@trpc/server"
import { env } from "@/env/server"
-import { badRequestError, sessionExpiredError } from "./errors/trpc"
+import {
+ badRequestError,
+ sessionExpiredError,
+ unauthorizedError,
+} from "./errors/trpc"
import { transformer } from "./transformer"
import { langInput } from "./utils"
+import type { Session } from "next-auth"
+
import type { Meta } from "@/types/trpc/meta"
import type { Context } from "./context"
@@ -57,6 +63,10 @@ export const protectedProcedure = t.procedure.use(async function (opts) {
console.info(`path: ${opts.path} | type: ${opts.type}`)
}
+ if (!session) {
+ throw unauthorizedError()
+ }
+
if (session?.error === "RefreshAccessTokenError") {
throw sessionExpiredError()
}
@@ -67,3 +77,25 @@ export const protectedProcedure = t.procedure.use(async function (opts) {
},
})
})
+
+export const safeProtectedProcedure = t.procedure.use(async function (opts) {
+ const authRequired = opts.meta?.authRequired ?? true
+
+ let session: Session | null = await opts.ctx.auth()
+ 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. ❌❌❌❌`
+ )
+ console.info(`path: ${opts.path} | type: ${opts.type}`)
+ }
+
+ if (!session || session.error === "RefreshAccessTokenError") {
+ session = null
+ }
+
+ return opts.next({
+ ctx: {
+ session,
+ },
+ })
+})
diff --git a/types/components/current/header/mainMenu.ts b/types/components/current/header/mainMenu.ts
index 4251fd529..8e468e8cc 100644
--- a/types/components/current/header/mainMenu.ts
+++ b/types/components/current/header/mainMenu.ts
@@ -16,6 +16,6 @@ export type MainMenuProps = {
languageSwitcher: React.ReactNode | null
myPagesMobileDropdown: React.ReactNode | null
bookingHref: string
- user: User | null
+ user: Pick | null
lang: Lang
}
diff --git a/types/components/loyalty/blocks.ts b/types/components/loyalty/blocks.ts
index f9f337344..c8db57354 100644
--- a/types/components/loyalty/blocks.ts
+++ b/types/components/loyalty/blocks.ts
@@ -7,9 +7,9 @@ import {
RteBlockContent,
} from "@/server/routers/contentstack/loyaltyPage/output"
-import type { IntlFormatters } from "@formatjs/intl"
+import { MembershipLevel } from "@/utils/user"
-import { User } from "@/types/user"
+import type { IntlFormatters } from "@formatjs/intl"
export type BlocksProps = {
blocks: Block[]
@@ -32,7 +32,7 @@ export type Content = { content: RteBlockContent["content"]["content"] }
type Benefit = { title: string }
-export type OverviewTableProps = { user: User | null }
+export type OverviewTableProps = { activeMembership: MembershipLevel | null }
export type Level = {
level: membershipLevels
diff --git a/utils/user.ts b/utils/user.ts
index 052826eaf..57f4e3e16 100644
--- a/utils/user.ts
+++ b/utils/user.ts
@@ -15,6 +15,7 @@ export function getMembership(memberships: User["memberships"]) {
membership.membershipType.toLowerCase() === scandicMemberships.guestpr
)
}
+export type MembershipLevel = ReturnType
export function getMembershipCards(
memberships: z.infer