diff --git a/src/helpers/permissions.ts b/src/helpers/permissions.ts new file mode 100644 index 0000000..f44a283 --- /dev/null +++ b/src/helpers/permissions.ts @@ -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); +}; diff --git a/src/shared/SideBar.tsx b/src/shared/SideBar.tsx index 9c9beb0..1237910 100644 --- a/src/shared/SideBar.tsx +++ b/src/shared/SideBar.tsx @@ -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") && ( { isActive={isGroupActive("content")} link={Pages.blog.list} name="content" - activeName="" + activeNames={CONTENT_PERMISSION_RESOURCES} /> { isActive={isGroupActive("financial")} link={Pages.payment.list} name="financial" - activeName="" + activeNames={FINANCIAL_PERMISSION_RESOURCES} /> { 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") && ( diff --git a/src/shared/SideBarItem.tsx b/src/shared/SideBarItem.tsx index 936f135..0d8ae50 100644 --- a/src/shared/SideBarItem.tsx +++ b/src/shared/SideBarItem.tsx @@ -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) => { @@ -32,7 +34,7 @@ const SideBarItem: FC = (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 ( = ({ title, isActive, activeName = "", children }) => { +const SubMenuAccordion: FC = ({ title, isActive, activeName, activeNames, children }) => { const { data: adminPermissions } = useGetAdminPermissions(); const [open, setOpen] = useState(isActive); @@ -20,10 +22,7 @@ const SubMenuAccordion: FC = ({ 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; } diff --git a/src/shared/components/SubMenuItem.tsx b/src/shared/components/SubMenuItem.tsx index 0f505a1..26c193e 100644 --- a/src/shared/components/SubMenuItem.tsx +++ b/src/shared/components/SubMenuItem.tsx @@ -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) => { 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; } diff --git a/src/shared/components/UsersSubMenu.tsx b/src/shared/components/UsersSubMenu.tsx index 36307b4..cc2294a 100644 --- a/src/shared/components/UsersSubMenu.tsx +++ b/src/shared/components/UsersSubMenu.tsx @@ -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} />