Revamp auth system
This commit is contained in:
parent
667e06b198
commit
b9214dfe88
|
|
@ -1,25 +1,36 @@
|
||||||
import { MantineProvider } from "@mantine/core";
|
import { MantineProvider } from "@mantine/core";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import DashboardLayout from "@/modules/dashboard/components/DashboardLayout";
|
import DashboardLayout from "@/modules/dashboard/components/DashboardLayout";
|
||||||
import getUser from "@/modules/auth/actions/getMyDetailAction";
|
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
import { Notifications } from "@mantine/notifications";
|
import { Notifications } from "@mantine/notifications";
|
||||||
|
import getCurrentUser from "@/modules/auth/services/getCurrentUser";
|
||||||
|
import { AuthContextProvider } from "@/modules/auth/contexts/AuthContext";
|
||||||
|
import getSidebarMenus from "@/modules/dashboard/services/getSidebarMenus";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Layout(props: Props) {
|
export default async function Layout(props: Props) {
|
||||||
const user = await getUser();
|
const user = (await getCurrentUser());
|
||||||
|
|
||||||
if (!user) {
|
// if (!user) {
|
||||||
redirect("/login");
|
// redirect("/dashboard/login");
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
const userData = user ? {
|
||||||
|
id: user.id,
|
||||||
|
name: user.name ?? "",
|
||||||
|
email: user.email ?? "",
|
||||||
|
photoProfile: user.photoProfile,
|
||||||
|
sidebarMenus: await getSidebarMenus()
|
||||||
|
} : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MantineProvider>
|
<MantineProvider>
|
||||||
<Notifications />
|
<Notifications />
|
||||||
<DashboardLayout>{props.children}</DashboardLayout>
|
<AuthContextProvider userData={userData}>
|
||||||
|
<DashboardLayout isLoggedIn={!!user}>{props.children}</DashboardLayout>
|
||||||
|
</AuthContextProvider>
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import "@mantine/core/styles.css";
|
||||||
import '@mantine/notifications/styles.css';
|
import '@mantine/notifications/styles.css';
|
||||||
|
|
||||||
import { ColorSchemeScript } from "@mantine/core";
|
import { ColorSchemeScript } from "@mantine/core";
|
||||||
import { AuthContextProvider } from "@/modules/auth/contexts/AuthContext";
|
|
||||||
|
|
||||||
const inter = Inter({ subsets: ["latin"] });
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
|
|
@ -26,7 +25,7 @@ export default function RootLayout({
|
||||||
<ColorSchemeScript />
|
<ColorSchemeScript />
|
||||||
</head>
|
</head>
|
||||||
<body className={inter.className}>
|
<body className={inter.className}>
|
||||||
<AuthContextProvider>{children}</AuthContextProvider>
|
{children}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { cookies } from "next/headers";
|
||||||
* This function uses a JWT token obtained from cookies to authenticate the user and fetch their details.
|
* This function uses a JWT token obtained from cookies to authenticate the user and fetch their details.
|
||||||
* If the authentication fails due to an invalid JWT token, or if any other error occurs, the function handles these errors gracefully.
|
* If the authentication fails due to an invalid JWT token, or if any other error occurs, the function handles these errors gracefully.
|
||||||
*
|
*
|
||||||
|
* @deprecated
|
||||||
* @returns A promise that resolves to a `ServerResponseAction` object. This object includes a `success` flag indicating the operation's outcome, the user's details in the `data` field if successful, or an error object in the `error` field if an error occurs.
|
* @returns A promise that resolves to a `ServerResponseAction` object. This object includes a `success` flag indicating the operation's outcome, the user's details in the `data` field if successful, or an error object in the `error` field if an error occurs.
|
||||||
* @throws an unhandled error if an unexpected error occurs during the function execution.
|
* @throws an unhandled error if an unexpected error occurs during the function execution.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import getMyDetail from "../services/getMyDetail";
|
import getCurrentUser from "../services/getCurrentUser";
|
||||||
import { cookies } from "next/headers";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enforces a guest-only access policy by redirecting authenticated users to the dashboard.
|
* Enforces a guest-only access policy by redirecting authenticated users to the dashboard.
|
||||||
|
|
@ -12,11 +11,7 @@ import { cookies } from "next/headers";
|
||||||
* @returns A promise that resolves when the operation completes. The function itself does not return a value.
|
* @returns A promise that resolves when the operation completes. The function itself does not return a value.
|
||||||
*/
|
*/
|
||||||
export default async function guestOnly(): Promise<void> {
|
export default async function guestOnly(): Promise<void> {
|
||||||
const token = cookies().get("token");
|
const user = await getCurrentUser();
|
||||||
|
|
||||||
if (!token) return;
|
|
||||||
|
|
||||||
const user = await getMyDetail(token.value);
|
|
||||||
|
|
||||||
// If an authenticated user is detected, redirect them to the dashboard.
|
// If an authenticated user is detected, redirect them to the dashboard.
|
||||||
if (user) {
|
if (user) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
|
import { revalidatePath } from "next/cache";
|
||||||
import { cookies } from "next/headers";
|
import { cookies } from "next/headers";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import "server-only";
|
import "server-only";
|
||||||
|
|
@ -12,5 +13,6 @@ import "server-only";
|
||||||
*/
|
*/
|
||||||
export default async function logout() {
|
export default async function logout() {
|
||||||
cookies().delete("token");
|
cookies().delete("token");
|
||||||
|
revalidatePath("/dashboard/login");
|
||||||
redirect("/dashboard/login");
|
redirect("/dashboard/login");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,29 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
// Importing React functionalities and required components.
|
// Importing React functionalities and required components.
|
||||||
import React, { ReactElement, ReactNode, createContext, useCallback, useContext, useEffect, useState } from "react";
|
import React, { ReactElement, ReactNode, createContext, useContext } from "react";
|
||||||
import { notifications } from "@mantine/notifications";
|
import SidebarMenu from "@/modules/dashboard/types/SidebarMenu";
|
||||||
import getMyDetailAction from "../actions/getMyDetailAction";
|
|
||||||
import withServerAction from "@/modules/dashboard/utils/withServerAction";
|
|
||||||
import ClientError from "@/core/error/ClientError";
|
|
||||||
|
|
||||||
// Defining the structure for user data within the authentication context.
|
// Defining the structure for user data within the authentication context.
|
||||||
interface UserData {
|
interface UserData {
|
||||||
name: string;
|
id: string,
|
||||||
email: string;
|
name: string,
|
||||||
photoUrl: string | null;
|
email: string,
|
||||||
// Additional user fields can be added here.
|
photoProfile: string | null,
|
||||||
|
sidebarMenus: SidebarMenu[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// State structure for the authentication context.
|
// State structure for the authentication context.
|
||||||
interface AuthContextState {
|
interface AuthContextState {
|
||||||
user: UserData | null;
|
user: UserData | null;
|
||||||
fetchUserData: () => void;
|
// fetchUserData: () => void;
|
||||||
logout: () => void;
|
// logout: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Props type definition for the AuthContextProvider component.
|
// Props type definition for the AuthContextProvider component.
|
||||||
interface Props {
|
interface Props {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
userData: UserData | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creating the authentication context with an undefined initial value.
|
// Creating the authentication context with an undefined initial value.
|
||||||
|
|
@ -39,41 +38,11 @@ const AuthContext = createContext<AuthContextState | undefined>(undefined);
|
||||||
* @param {Props} props - Component props containing children to be rendered within the provider.
|
* @param {Props} props - Component props containing children to be rendered within the provider.
|
||||||
* @returns {ReactElement} A provider component wrapping children with access to authentication context.
|
* @returns {ReactElement} A provider component wrapping children with access to authentication context.
|
||||||
*/
|
*/
|
||||||
export const AuthContextProvider = ({ children }: Props): ReactElement => {
|
export const AuthContextProvider = ({ children, userData }: Props): ReactElement => {
|
||||||
const [user, setUser] = useState<UserData | null>(null);
|
|
||||||
|
|
||||||
// Function to fetch user data and update state accordingly.
|
|
||||||
const fetchUserData = useCallback(() => {
|
|
||||||
withServerAction(getMyDetailAction)
|
|
||||||
.then((response) => {
|
|
||||||
setUser(response.data);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
if (error instanceof ClientError){
|
|
||||||
if (error.errorCode === "UNAUTHENTICATED") return;
|
|
||||||
}
|
|
||||||
notifications.show({
|
|
||||||
title: 'Error',
|
|
||||||
message: 'Error while retrieving user data',
|
|
||||||
color: 'red',
|
|
||||||
});
|
|
||||||
console.error("Error while retrieving user data", error);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Fetch user data on component mount.
|
|
||||||
useEffect(() => {
|
|
||||||
fetchUserData();
|
|
||||||
}, [fetchUserData]);
|
|
||||||
|
|
||||||
// Function to clear user data, effectively logging the user out.
|
|
||||||
const logout = () => {
|
|
||||||
setUser(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Providing authentication state and functions to the context consumers.
|
// Providing authentication state and functions to the context consumers.
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{ user, fetchUserData, logout }}>
|
<AuthContext.Provider value={{ user: userData }}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
13
src/modules/auth/services/getCurrentUser.ts
Normal file
13
src/modules/auth/services/getCurrentUser.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { cookies } from "next/headers";
|
||||||
|
import "server-only";
|
||||||
|
import getUserFromToken from "../utils/getUserFromToken";
|
||||||
|
|
||||||
|
export default async function getCurrentUser() {
|
||||||
|
const token = cookies().get("token")?.value;
|
||||||
|
|
||||||
|
if (!token) return null;
|
||||||
|
|
||||||
|
const userData = await getUserFromToken(token);
|
||||||
|
|
||||||
|
return userData;
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,9 @@ import AuthError from "../error/AuthError";
|
||||||
* If the token is not present or the user cannot be found, it returns null.
|
* If the token is not present or the user cannot be found, it returns null.
|
||||||
* Otherwise, it returns the user's name, email, and photo URL.
|
* Otherwise, it returns the user's name, email, and photo URL.
|
||||||
*
|
*
|
||||||
|
* Deprecated. use getCurrentUser() instead (see getCurrentUser.ts)
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
* @returns An object containing the user's name, email, and photo URL, or null if the user cannot be authenticated.
|
* @returns An object containing the user's name, email, and photo URL, or null if the user cannot be authenticated.
|
||||||
*/
|
*/
|
||||||
export default async function getMyDetail(token?: string) {
|
export default async function getMyDetail(token?: string) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { cache } from "react";
|
import { cache } from "react";
|
||||||
import decodeJwtToken from "./decodeJwtToken";
|
import decodeJwtToken from "./decodeJwtToken";
|
||||||
import prisma from "@/core/db";
|
import prisma from "@/core/db";
|
||||||
|
import "server-only";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves user data from the database based on the provided JWT token.
|
* Retrieves user data from the database based on the provided JWT token.
|
||||||
|
|
@ -12,7 +13,7 @@ import prisma from "@/core/db";
|
||||||
* @returns The user's data if the user exists, or null if no user is found.
|
* @returns The user's data if the user exists, or null if no user is found.
|
||||||
* Throws an error if the token is invalid or the database query fails.
|
* Throws an error if the token is invalid or the database query fails.
|
||||||
*/
|
*/
|
||||||
const getUserFromToken = async (token: string) => {
|
const getUserFromToken = cache(async (token: string) => {
|
||||||
// Decode the JWT token to extract the user ID
|
// Decode the JWT token to extract the user ID
|
||||||
const decodedToken = decodeJwtToken(token) as { id: string; iat: number };
|
const decodedToken = decodeJwtToken(token) as { id: string; iat: number };
|
||||||
|
|
||||||
|
|
@ -32,6 +33,6 @@ const getUserFromToken = async (token: string) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
};
|
});
|
||||||
|
|
||||||
export default getUserFromToken;
|
export default getUserFromToken;
|
||||||
|
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
"use server";
|
|
||||||
import SidebarMenu from "../types/SidebarMenu";
|
|
||||||
import "server-only";
|
|
||||||
import ServerResponseAction from "../types/ServerResponseAction";
|
|
||||||
import handleCatch from "../utils/handleCatch";
|
|
||||||
import getUserRoles from "@/modules/auth/utils/getUserRoles";
|
|
||||||
import getUserPermissions from "@/modules/auth/utils/getUserPermissions";
|
|
||||||
import sidebarMenus from "../data/sidebarMenus";
|
|
||||||
|
|
||||||
export default async function getSidebarMenus(): Promise<
|
|
||||||
ServerResponseAction<SidebarMenu[]>
|
|
||||||
> {
|
|
||||||
try {
|
|
||||||
const filteredMenus: SidebarMenu[] = [];
|
|
||||||
|
|
||||||
const roles = await getUserRoles();
|
|
||||||
const permissions = await getUserPermissions();
|
|
||||||
|
|
||||||
for (let menu of sidebarMenus) {
|
|
||||||
//if has children
|
|
||||||
if (menu.children) {
|
|
||||||
const currentMenuChildren: SidebarMenu["children"] = [];
|
|
||||||
for (let menuChild of menu.children) {
|
|
||||||
if (
|
|
||||||
menuChild.allowedPermissions?.some((perm) =>
|
|
||||||
permissions?.includes(perm)
|
|
||||||
) ||
|
|
||||||
menuChild.allowedRoles?.some((role) =>
|
|
||||||
roles?.includes(role)
|
|
||||||
) ||
|
|
||||||
menuChild.allowedPermissions?.includes("*") ||
|
|
||||||
menuChild.allowedRoles?.includes("*")
|
|
||||||
|| roles.includes("super-admin")
|
|
||||||
)
|
|
||||||
currentMenuChildren.push(menuChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentMenuChildren.length > 0) {
|
|
||||||
filteredMenus.push({
|
|
||||||
...menu,
|
|
||||||
children: currentMenuChildren,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//if does not have any children
|
|
||||||
else {
|
|
||||||
if (
|
|
||||||
menu.allowedPermissions?.some((perm) =>
|
|
||||||
permissions?.includes(perm)
|
|
||||||
) ||
|
|
||||||
menu.allowedRoles?.some((role) => roles?.includes(role)) ||
|
|
||||||
menu.allowedPermissions?.includes("*") ||
|
|
||||||
menu.allowedRoles?.includes("*")
|
|
||||||
) {
|
|
||||||
filteredMenus.push(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
data: filteredMenus,
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
return handleCatch(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
22
src/modules/dashboard/actions/getSidebarMenusAction.ts
Normal file
22
src/modules/dashboard/actions/getSidebarMenusAction.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
"use server";
|
||||||
|
import "server-only";
|
||||||
|
import SidebarMenu from "../types/SidebarMenu";
|
||||||
|
import ServerResponseAction from "../types/ServerResponseAction";
|
||||||
|
import handleCatch from "../utils/handleCatch";
|
||||||
|
import getSidebarMenus from "../services/getSidebarMenus";
|
||||||
|
|
||||||
|
export default async function getSidebarMenusAction(): Promise<
|
||||||
|
ServerResponseAction<SidebarMenu[]>
|
||||||
|
> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const filteredMenus = await getSidebarMenus();
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: filteredMenus,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return handleCatch(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ export default function AppHeader(props: Props) {
|
||||||
>
|
>
|
||||||
<Group gap={7}>
|
<Group gap={7}>
|
||||||
<Avatar
|
<Avatar
|
||||||
src={user?.photoUrl}
|
src={user?.photoProfile}
|
||||||
alt={user?.name}
|
alt={user?.name}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
size={20}
|
size={20}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React from "react";
|
||||||
import { AppShell, ScrollArea, Skeleton, Stack } from "@mantine/core";
|
import { AppShell, ScrollArea } from "@mantine/core";
|
||||||
|
|
||||||
import MenuItem from "./SidebarMenuItem";
|
import MenuItem from "./SidebarMenuItem";
|
||||||
import getSidebarMenus from "../actions/getSidebarMenus";
|
import { useAuth } from "@/modules/auth/contexts/AuthContext";
|
||||||
import withServerAction from "../utils/withServerAction";
|
|
||||||
import SidebarMenu from "../types/SidebarMenu";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `AppNavbar` is a React functional component that renders the application's navigation bar.
|
* `AppNavbar` is a React functional component that renders the application's navigation bar.
|
||||||
|
|
@ -13,35 +11,16 @@ import SidebarMenu from "../types/SidebarMenu";
|
||||||
* @returns A React element representing the application's navigation bar.
|
* @returns A React element representing the application's navigation bar.
|
||||||
*/
|
*/
|
||||||
export default function AppNavbar() {
|
export default function AppNavbar() {
|
||||||
const [isFetching, setFetching] = useState(true);
|
|
||||||
const [sidebarMenus, setSidebarMenus] = useState<SidebarMenu[]>([]);
|
|
||||||
|
|
||||||
// Mapping all menu items to MenuItem components
|
const {user} = useAuth();
|
||||||
// const menus = getSidebarMenus().map((menu, i) => <MenuItem menu={menu} key={i} />);
|
|
||||||
useEffect(() => {
|
|
||||||
setFetching(true);
|
|
||||||
withServerAction(getSidebarMenus)
|
|
||||||
.then((response) => {
|
|
||||||
setSidebarMenus(response.data);
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(e);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setFetching(false);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppShell.Navbar p="md">
|
<AppShell.Navbar p="md">
|
||||||
<ScrollArea style={{ flex: "1" }}>
|
<ScrollArea style={{ flex: "1" }}>
|
||||||
{
|
{
|
||||||
isFetching ? <Stack gap="md">
|
user?.sidebarMenus.map((menu, i) => (
|
||||||
{[...new Array(10)].map((_,i) => <Skeleton key={i} visible={true} height={40} width={"100%"} />)}
|
|
||||||
</Stack> :
|
|
||||||
sidebarMenus.map((menu, i) => (
|
|
||||||
<MenuItem menu={menu} key={i} />
|
<MenuItem menu={menu} key={i} />
|
||||||
))}
|
)) ?? null}
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</AppShell.Navbar>
|
</AppShell.Navbar>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,15 @@
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
|
||||||
"use client";
|
"use client";
|
||||||
import React, { useEffect, useState } from "react";
|
import React from "react";
|
||||||
import { AppShell } from "@mantine/core";
|
import { AppShell } from "@mantine/core";
|
||||||
import { useDisclosure } from "@mantine/hooks";
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
|
|
||||||
import AppHeader from "./AppHeader";
|
import AppHeader from "./AppHeader";
|
||||||
import AppNavbar from "./AppNavbar";
|
import AppNavbar from "./AppNavbar";
|
||||||
import { useAuth } from "@/modules/auth/contexts/AuthContext";
|
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import dashboardConfig from "../dashboard.config";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
isLoggedIn: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -24,22 +22,14 @@ interface Props {
|
||||||
*/
|
*/
|
||||||
export default function DashboardLayout(props: Props) {
|
export default function DashboardLayout(props: Props) {
|
||||||
|
|
||||||
|
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
console.log(pathname)
|
||||||
// State and toggle function for handling the disclosure of the navigation bar
|
// State and toggle function for handling the disclosure of the navigation bar
|
||||||
const [openNavbar, { toggle }] = useDisclosure(false);
|
const [openNavbar, { toggle }] = useDisclosure(false);
|
||||||
|
|
||||||
const {fetchUserData} = useAuth();
|
const withAppShell = props.isLoggedIn;
|
||||||
|
|
||||||
const [withAppShell, setWithAppShell] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchUserData()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setWithAppShell(!dashboardConfig.routesWithoutAppShell.some(v => `${dashboardConfig.baseRoute}${v}` === pathname))
|
|
||||||
}, [pathname])
|
|
||||||
|
|
||||||
return withAppShell ? (
|
return withAppShell ? (
|
||||||
<AppShell
|
<AppShell
|
||||||
|
|
|
||||||
55
src/modules/dashboard/services/getSidebarMenus.ts
Normal file
55
src/modules/dashboard/services/getSidebarMenus.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import "server-only";
|
||||||
|
import SidebarMenu from "../types/SidebarMenu";
|
||||||
|
import getUserRoles from "@/modules/auth/utils/getUserRoles";
|
||||||
|
import getUserPermissions from "@/modules/auth/utils/getUserPermissions";
|
||||||
|
import sidebarMenus from "../data/sidebarMenus";
|
||||||
|
|
||||||
|
export default async function getSidebarMenus() {
|
||||||
|
const filteredMenus: SidebarMenu[] = [];
|
||||||
|
|
||||||
|
const roles = await getUserRoles();
|
||||||
|
const permissions = await getUserPermissions();
|
||||||
|
|
||||||
|
for (let menu of sidebarMenus) {
|
||||||
|
//if has children
|
||||||
|
if (menu.children) {
|
||||||
|
const currentMenuChildren: SidebarMenu["children"] = [];
|
||||||
|
for (let menuChild of menu.children) {
|
||||||
|
if (
|
||||||
|
menuChild.allowedPermissions?.some((perm) =>
|
||||||
|
permissions?.includes(perm)
|
||||||
|
) ||
|
||||||
|
menuChild.allowedRoles?.some((role) =>
|
||||||
|
roles?.includes(role)
|
||||||
|
) ||
|
||||||
|
menuChild.allowedPermissions?.includes("*") ||
|
||||||
|
menuChild.allowedRoles?.includes("*") ||
|
||||||
|
roles.includes("super-admin")
|
||||||
|
)
|
||||||
|
currentMenuChildren.push(menuChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMenuChildren.length > 0) {
|
||||||
|
filteredMenus.push({
|
||||||
|
...menu,
|
||||||
|
children: currentMenuChildren,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if does not have any children
|
||||||
|
else {
|
||||||
|
if (
|
||||||
|
menu.allowedPermissions?.some((perm) =>
|
||||||
|
permissions?.includes(perm)
|
||||||
|
) ||
|
||||||
|
menu.allowedRoles?.some((role) => roles?.includes(role)) ||
|
||||||
|
menu.allowedPermissions?.includes("*") ||
|
||||||
|
menu.allowedRoles?.includes("*")
|
||||||
|
) {
|
||||||
|
filteredMenus.push(menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredMenus;
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
|
"noErrorTruncation": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user