update permion server

This commit is contained in:
hamid zarghami
2026-07-20 11:45:21 +03:30
parent 6609666279
commit 70e159cefc
6 changed files with 208 additions and 24 deletions
+175
View File
@@ -0,0 +1,175 @@
export enum PermissionEnum {
SERVICES_CREATE = "services_create",
SERVICES_READ = "services_read",
SERVICES_UPDATE = "services_update",
SERVICES_DELETE = "services_delete",
CUSTOMERS_CREATE = "customers_create",
CUSTOMERS_READ = "customers_read",
CUSTOMERS_UPDATE = "customers_update",
CUSTOMERS_DELETE = "customers_delete",
AGENTS_CREATE = "agents_create",
AGENTS_READ = "agents_read",
AGENTS_UPDATE = "agents_update",
AGENTS_DELETE = "agents_delete",
DEVELOPERS_CREATE = "developers_create",
DEVELOPERS_READ = "developers_read",
DEVELOPERS_UPDATE = "developers_update",
DEVELOPERS_DELETE = "developers_delete",
INVOICES_CREATE = "invoices_create",
INVOICES_READ = "invoices_read",
INVOICES_UPDATE = "invoices_update",
INVOICES_DELETE = "invoices_delete",
TRANSACTIONS_CREATE = "transactions_create",
TRANSACTIONS_READ = "transactions_read",
TRANSACTIONS_UPDATE = "transactions_update",
TRANSACTIONS_DELETE = "transactions_delete",
DISCOUNTS_CREATE = "discounts_create",
DISCOUNTS_READ = "discounts_read",
DISCOUNTS_UPDATE = "discounts_update",
DISCOUNTS_DELETE = "discounts_delete",
ADMINS_CREATE = "admins_create",
ADMINS_READ = "admins_read",
ADMINS_UPDATE = "admins_update",
ADMINS_DELETE = "admins_delete",
TICKETS_CREATE = "tickets_create",
TICKETS_READ = "tickets_read",
TICKETS_UPDATE = "tickets_update",
TICKETS_DELETE = "tickets_delete",
CRITICISMS_CREATE = "criticisms_create",
CRITICISMS_READ = "criticisms_read",
CRITICISMS_UPDATE = "criticisms_update",
CRITICISMS_DELETE = "criticisms_delete",
CONTACTS_US_CREATE = "contacts_us_create",
CONTACTS_US_READ = "contacts_us_read",
CONTACTS_US_UPDATE = "contacts_us_update",
CONTACTS_US_DELETE = "contacts_us_delete",
ADVERTISEMENTS_CREATE = "advertisements_create",
ADVERTISEMENTS_READ = "advertisements_read",
ADVERTISEMENTS_UPDATE = "advertisements_update",
ADVERTISEMENTS_DELETE = "advertisements_delete",
ANNOUNCEMENTS_CREATE = "announcements_create",
ANNOUNCEMENTS_READ = "announcements_read",
ANNOUNCEMENTS_UPDATE = "announcements_update",
ANNOUNCEMENTS_DELETE = "announcements_delete",
BLOGS_CREATE = "blogs_create",
BLOGS_READ = "blogs_read",
BLOGS_UPDATE = "blogs_update",
BLOGS_DELETE = "blogs_delete",
LEARNINGS_CREATE = "learnings_create",
LEARNINGS_READ = "learnings_read",
LEARNINGS_UPDATE = "learnings_update",
LEARNINGS_DELETE = "learnings_delete",
SETTINGS_CREATE = "settings_create",
SETTINGS_READ = "settings_read",
SETTINGS_UPDATE = "settings_update",
SETTINGS_DELETE = "settings_delete",
BANK_ACCOUNTS_CREATE = "bank_accounts_create",
BANK_ACCOUNTS_READ = "bank_accounts_read",
BANK_ACCOUNTS_UPDATE = "bank_accounts_update",
BANK_ACCOUNTS_DELETE = "bank_accounts_delete",
PAYMENTS_CREATE = "payments_create",
PAYMENTS_READ = "payments_read",
PAYMENTS_UPDATE = "payments_update",
PAYMENTS_DELETE = "payments_delete",
SUPPORT_PLAN_CREATE = "support_plan_create",
SUPPORT_PLAN_READ = "support_plan_read",
SUPPORT_PLAN_UPDATE = "support_plan_update",
SUPPORT_PLAN_DELETE = "support_plan_delete",
RESELLER_CREATE = "reseller_create",
RESELLER_READ = "reseller_read",
RESELLER_UPDATE = "reseller_update",
RESELLER_DELETE = "reseller_delete",
WORKSPACE_CREATE = "workspace_create",
WORKSPACE_READ = "workspace_read",
WORKSPACE_UPDATE = "workspace_update",
WORKSPACE_DELETE = "workspace_delete",
PROJECT_CREATE = "project_create",
PROJECT_READ = "project_read",
PROJECT_UPDATE = "project_update",
PROJECT_DELETE = "project_delete",
TASK_CREATE = "task_create",
TASK_READ = "task_read",
TASK_UPDATE = "task_update",
TASK_DELETE = "task_delete",
TASK_PHASE_CREATE = "task_phase_create",
TASK_PHASE_READ = "task_phase_read",
TASK_PHASE_UPDATE = "task_phase_update",
TASK_PHASE_DELETE = "task_phase_delete",
LOGS = "logs",
MANAGE_SSO_CLIENTS = "manage_sso_clients",
DMENU = "dmenu",
DKALA = "dkala",
DPAGE = "dpage",
DMAIL = "dmail",
}
const CRUD_ACTIONS = ["create", "read", "update", "delete"] as const;
export const CONTENT_PERMISSION_RESOURCES = ["blogs", "learnings", "advertisements", "announcements"] as const;
export const FINANCIAL_PERMISSION_RESOURCES = ["payments", "invoices", "transactions", "discounts", "bank_accounts"] as const;
export const SUPPORT_PERMISSION_RESOURCES = ["support_plan", "tickets"] as const;
export const TASK_MANAGER_PERMISSION_RESOURCES = ["workspace", "project", "task", "task_phase"] as const;
export const PRODUCT_PERMISSION_RESOURCES = ["dmenu", "dkala", "dpage", "dmail"] as const;
type PermissionEntry = { name: string };
export const getPermissionNames = (permissions: PermissionEntry[] | undefined): string[] =>
permissions?.map((permission) => permission.name) ?? [];
export const hasPermission = (
permissions: PermissionEntry[] | undefined,
resourceOrPermission: string,
): boolean => {
const names = getPermissionNames(permissions);
if (names.includes(resourceOrPermission)) {
return true;
}
return CRUD_ACTIONS.some((action) => names.includes(`${resourceOrPermission}_${action}`));
};
export const hasAnyPermission = (
permissions: PermissionEntry[] | undefined,
resources: readonly string[],
): boolean => resources.some((resource) => hasPermission(permissions, resource));
export const canAccessMenuItem = (
permissions: PermissionEntry[] | undefined,
activeName?: string,
activeNames?: readonly string[],
): boolean => {
if (activeNames && activeNames.length > 0) {
return hasAnyPermission(permissions, activeNames);
}
if (!activeName) {
return true;
}
return hasPermission(permissions, activeName);
};
+20 -12
View File
@@ -19,6 +19,14 @@ import LogoSmall from "../assets/images/logo-small.svg";
import LogoImage from "../assets/images/logo.svg";
import { Pages } from "../config/Pages";
import { getSubMenuNameFromPath } from "../config/SideBarSubMenu";
import {
CONTENT_PERMISSION_RESOURCES,
FINANCIAL_PERMISSION_RESOURCES,
hasAnyPermission,
hasPermission,
PRODUCT_PERMISSION_RESOURCES,
SUPPORT_PERMISSION_RESOURCES,
} from "../helpers/permissions";
import { clx } from "../helpers/utils";
import { useGetProfile } from "../pages/profile/hooks/useProfileData";
import { useGetAdminPermissions } from "../pages/users/hooks/useUserData";
@@ -44,16 +52,16 @@ const SideBar: FC = () => {
const isGroupActive = (name: string) => subMenuName === name || getSubMenuNameFromPath(location.pathname) === name;
const isPathActive = (name: string) => location.pathname.includes(name);
const hasProductPermission = adminPermissions?.data?.permissions?.some((permission: { name: string }) =>
["dmenu", "dkala", "dpage", "dmail"].includes(permission.name),
);
const permissions = adminPermissions?.data?.permissions;
const hasProductPermission = hasAnyPermission(permissions, PRODUCT_PERMISSION_RESOURCES);
const productsDefaultLink = (() => {
const permissions = adminPermissions?.data?.permissions?.map((p: { name: string }) => p.name) ?? [];
if (permissions.includes("dmenu")) return Pages.dmenu.icons.list;
if (permissions.includes("dkala")) return Pages.dkala.icons.list;
if (permissions.includes("dmail")) return Pages.dmail.list;
if (permissions.includes("dpage")) return Pages.dpage.stickers.list;
const permissionNames = permissions?.map((p: { name: string }) => p.name) ?? [];
if (permissionNames.includes("dmenu")) return Pages.dmenu.icons.list;
if (permissionNames.includes("dkala")) return Pages.dkala.icons.list;
if (permissionNames.includes("dmail")) return Pages.dmail.list;
if (permissionNames.includes("dpage")) return Pages.dpage.stickers.list;
return Pages.dashboard;
})();
@@ -148,7 +156,7 @@ const SideBar: FC = () => {
activeName="customers"
/>
{adminPermissions?.data?.permissions?.some((permission: { name: string }) => permission.name === "reseller") && (
{hasPermission(permissions, "reseller") && (
<SideBarItem
icon={
<UserTick
@@ -206,7 +214,7 @@ const SideBar: FC = () => {
isActive={isGroupActive("content")}
link={Pages.blog.list}
name="content"
activeName=""
activeNames={CONTENT_PERMISSION_RESOURCES}
/>
<SideBarItem
@@ -221,7 +229,7 @@ const SideBar: FC = () => {
isActive={isGroupActive("financial")}
link={Pages.payment.list}
name="financial"
activeName=""
activeNames={FINANCIAL_PERMISSION_RESOURCES}
/>
<SideBarItem
@@ -236,7 +244,7 @@ const SideBar: FC = () => {
isActive={isGroupActive("support")}
link={Pages.support.list}
name="support"
activeName=""
activeNames={SUPPORT_PERMISSION_RESOURCES}
/>
{profile?.data?.user?.roles?.some((role: { name: string }) => role.name === "super_admin") && (
+4 -2
View File
@@ -4,6 +4,7 @@ import { clx } from '../helpers/utils'
import { Pages } from '../config/Pages'
import { useSharedStore } from './store/sharedStore'
import { removeToken, removeRefreshToken } from '../config/func'
import { canAccessMenuItem } from '../helpers/permissions'
import { useGetAdminPermissions } from '../pages/users/hooks/useUserData'
type Props = {
@@ -13,7 +14,8 @@ type Props = {
link: string,
isLogout?: boolean,
name?: string,
activeName?: string
activeName?: string,
activeNames?: readonly string[],
}
const SideBarItem: FC<Props> = (props: Props) => {
@@ -32,7 +34,7 @@ const SideBarItem: FC<Props> = (props: Props) => {
setSubtMenu(true)
}
}
if (props.activeName === '' || props.activeName && adminPermissions?.data?.permissions?.some((permission: { name: string }) => permission.name === props.activeName)) {
if (canAccessMenuItem(adminPermissions?.data?.permissions, props.activeName, props.activeNames)) {
return (
<Link onClick={props.isLogout ? handleLogout : handleClick} to={props.link} className={clx(
'flex text-xs gap-9 mt-4',
+4 -5
View File
@@ -1,16 +1,18 @@
import { ArrowDown2 } from "iconsax-react";
import { FC, ReactNode, useEffect, useState } from "react";
import { clx } from "../../helpers/utils";
import { canAccessMenuItem } from "../../helpers/permissions";
import { useGetAdminPermissions } from "../../pages/users/hooks/useUserData";
type Props = {
title: string;
isActive: boolean;
activeName?: string;
activeNames?: readonly string[];
children: ReactNode;
};
const SubMenuAccordion: FC<Props> = ({ title, isActive, activeName = "", children }) => {
const SubMenuAccordion: FC<Props> = ({ title, isActive, activeName, activeNames, children }) => {
const { data: adminPermissions } = useGetAdminPermissions();
const [open, setOpen] = useState(isActive);
@@ -20,10 +22,7 @@ const SubMenuAccordion: FC<Props> = ({ title, isActive, activeName = "", childre
}
}, [isActive]);
if (
activeName !== "" &&
!adminPermissions?.data?.permissions?.some((permission: { name: string }) => permission.name === activeName)
) {
if (!canAccessMenuItem(adminPermissions?.data?.permissions, activeName, activeNames)) {
return null;
}
+3 -5
View File
@@ -1,6 +1,7 @@
import { FC } from "react";
import { Link } from "react-router-dom";
import { clx } from "../../helpers/utils";
import { canAccessMenuItem } from "../../helpers/permissions";
import { useGetAdminPermissions } from "../../pages/users/hooks/useUserData";
type Props = {
@@ -9,16 +10,13 @@ type Props = {
link: string;
isLogout?: boolean;
activeName?: string;
activeNames?: readonly string[];
};
const SubMenuItem: FC<Props> = (props: Props) => {
const { data: adminPermissions } = useGetAdminPermissions();
if (
props.activeName !== undefined &&
props.activeName !== "" &&
!adminPermissions?.data?.permissions?.some((permission: { name: string }) => permission.name === props.activeName)
) {
if (!canAccessMenuItem(adminPermissions?.data?.permissions, props.activeName, props.activeNames)) {
return null;
}
+2
View File
@@ -3,6 +3,7 @@ import { FC } from "react";
import { useTranslation } from "react-i18next";
import { useLocation } from "react-router-dom";
import { Pages } from "../../config/Pages";
import { TASK_MANAGER_PERMISSION_RESOURCES } from "../../helpers/permissions";
import SubMenuItem from "./SubMenuItem";
const UsersSubMenu: FC = () => {
@@ -54,6 +55,7 @@ const UsersSubMenu: FC = () => {
title={t("sidebar.taskmanager")}
isActive={isTaskManagerActive}
link={Pages.taskmanager.workspaceList}
activeNames={TASK_MANAGER_PERMISSION_RESOURCES}
/>
</div>
</div>