From 185961c16257b5f415f9b1ede2ce02a1ec37ea04 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sun, 26 Jul 2026 08:45:44 +0330 Subject: [PATCH] permission --- src/helpers/permissions.ts | 59 ++++++++- src/hooks/usePermissions.ts | 28 ++++ src/pages/ads/AdsList.tsx | 38 +++--- src/pages/annoncement/List.tsx | 54 ++++---- src/pages/blog/BlogCategory.tsx | 122 ++++++++++-------- src/pages/blog/BlogList.tsx | 56 ++++---- src/pages/cardBank/List.tsx | 38 +++--- src/pages/criticisms/List.tsx | 6 +- src/pages/customer/List.tsx | 40 +++--- src/pages/discounts/DiscountsList.tsx | 50 ++++--- src/pages/guide/List.tsx | 52 ++++---- src/pages/learning/Category.tsx | 6 +- src/pages/learning/List.tsx | 34 ++--- src/pages/payment/List.tsx | 4 +- src/pages/receipts/List.tsx | 20 ++- src/pages/reseller/List.tsx | 26 ++-- src/pages/service/Category.tsx | 20 ++- src/pages/service/List.tsx | 64 +++++---- src/pages/service/Plans.tsx | 12 +- src/pages/slider/List.tsx | 56 ++++---- src/pages/subscriptions/List.tsx | 12 +- src/pages/support/List.tsx | 38 +++--- .../taskmanager/components/AddNewColumn.tsx | 6 + src/pages/taskmanager/components/Column.tsx | 86 ++++++------ .../taskmanager/components/ColumnMenu.tsx | 46 ++++--- .../task-detail/TaskDetailHeader.tsx | 4 +- .../project/components/ProjectCardMenu.tsx | 34 +++-- .../project/components/ProjectListHeader.tsx | 28 ++-- src/pages/taskmanager/workspace/List.tsx | 50 ++++--- src/pages/ticket/Category.tsx | 4 + src/pages/ticket/masters/List.tsx | 38 +++--- src/pages/users/GroupList.tsx | 32 +++-- src/pages/users/List.tsx | 54 ++++---- src/pages/users/RolesList.tsx | 40 +++--- src/shared/components/SupportSubMenu.tsx | 4 +- 35 files changed, 764 insertions(+), 497 deletions(-) create mode 100644 src/hooks/usePermissions.ts diff --git a/src/helpers/permissions.ts b/src/helpers/permissions.ts index 7986afe..5bf8e15 100644 --- a/src/helpers/permissions.ts +++ b/src/helpers/permissions.ts @@ -145,6 +145,19 @@ export enum PermissionEnum { TASK_PHASE_UPDATE = "task_phase_update", TASK_PHASE_DELETE = "task_phase_delete", + TICKET_CATEGORIES_CREATE = "ticket_categories_create", + TICKET_CATEGORIES_READ = "ticket_categories_read", + TICKET_CATEGORIES_UPDATE = "ticket_categories_update", + TICKET_CATEGORIES_DELETE = "ticket_categories_delete", + + TICKET_MASTERS_CREATE = "ticket_masters_create", + TICKET_MASTERS_READ = "ticket_masters_read", + TICKET_MASTERS_UPDATE = "ticket_masters_update", + TICKET_MASTERS_DELETE = "ticket_masters_delete", + + VIEW_ALL_WORKSPACES = "view_all_workspaces", + VIEW_ALL_PROJECTS = "view_all_projects", + LOGS = "logs", MANAGE_SSO_CLIENTS = "manage_sso_clients", DMENU = "dmenu", @@ -153,12 +166,26 @@ export enum PermissionEnum { DMAIL = "dmail", } -const CRUD_ACTIONS = ["create", "read", "update", "delete"] as const; +export type CrudAction = "create" | "read" | "update" | "delete"; + +const CRUD_ACTIONS: readonly CrudAction[] = ["create", "read", "update", "delete"]; 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 SUPPORT_PERMISSION_RESOURCES = [ + "support_plan", + "tickets", + "ticket_categories", + "ticket_masters", +] as const; +export const TASK_MANAGER_PERMISSION_RESOURCES = [ + "workspace", + "project", + "task", + "task_phase", + "view_all_workspaces", + "view_all_projects", +] as const; export const USERS_SIDEBAR_PERMISSION_RESOURCES = ["admins"] as const; export const PRODUCT_PERMISSION_RESOURCES = ["dmenu", "dkala", "dpage", "dmail"] as const; @@ -180,6 +207,32 @@ export const hasPermission = ( return CRUD_ACTIONS.some((action) => names.includes(`${resourceOrPermission}_${action}`)); }; +export const hasActionPermission = ( + permissions: PermissionEntry[] | undefined, + resource: string, + action: CrudAction, +): boolean => { + const names = getPermissionNames(permissions); + + if (names.includes(resource)) { + return true; + } + + return names.includes(`${resource}_${action}`); +}; + +export const canCreate = (permissions: PermissionEntry[] | undefined, resource: string) => + hasActionPermission(permissions, resource, "create"); + +export const canRead = (permissions: PermissionEntry[] | undefined, resource: string) => + hasActionPermission(permissions, resource, "read"); + +export const canUpdate = (permissions: PermissionEntry[] | undefined, resource: string) => + hasActionPermission(permissions, resource, "update"); + +export const canDelete = (permissions: PermissionEntry[] | undefined, resource: string) => + hasActionPermission(permissions, resource, "delete"); + export const hasAnyPermission = ( permissions: PermissionEntry[] | undefined, resources: readonly string[], diff --git a/src/hooks/usePermissions.ts b/src/hooks/usePermissions.ts new file mode 100644 index 0000000..8f13446 --- /dev/null +++ b/src/hooks/usePermissions.ts @@ -0,0 +1,28 @@ +import { + canCreate, + canDelete, + canRead, + canUpdate, + CrudAction, + hasActionPermission, + hasAnyPermission, + hasPermission, +} from "../helpers/permissions"; +import { useGetAdminPermissions } from "../pages/users/hooks/useUserData"; + +export const usePermissions = () => { + const { data, isPending, isLoading } = useGetAdminPermissions(); + const permissions = data?.data?.permissions; + + return { + permissions, + isPending: isPending || isLoading, + can: (resource: string, action: CrudAction) => hasActionPermission(permissions, resource, action), + canCreate: (resource: string) => canCreate(permissions, resource), + canRead: (resource: string) => canRead(permissions, resource), + canUpdate: (resource: string) => canUpdate(permissions, resource), + canDelete: (resource: string) => canDelete(permissions, resource), + has: (resourceOrPermission: string) => hasPermission(permissions, resourceOrPermission), + hasAny: (resources: readonly string[]) => hasAnyPermission(permissions, resources), + }; +}; diff --git a/src/pages/ads/AdsList.tsx b/src/pages/ads/AdsList.tsx index 58bcdd2..e093b96 100644 --- a/src/pages/ads/AdsList.tsx +++ b/src/pages/ads/AdsList.tsx @@ -14,6 +14,7 @@ import moment from 'moment-jalaali' import ToggleStatusAds from './components/ToggleStatus' import PageLoading from '../../components/PageLoading' import Pagination from '../../components/Pagination' +import { usePermissions } from '../../hooks/usePermissions' const AddList: FC = () => { const { t } = useTranslation('global') @@ -23,6 +24,7 @@ const AddList: FC = () => { const [page, setPage] = useState(1) const getAds = useGetAdsList(page, search, isActive, since) const deleteAds = useDeleteAds() + const { canCreate, canUpdate, canDelete } = usePermissions() const handleDelete = (id: string) => { deleteAds.mutate(id, { @@ -39,18 +41,20 @@ const AddList: FC = () => { {t('ads.ads')} - - - + + + )}
@@ -130,10 +134,14 @@ const AddList: FC = () => {
- - - - handleDelete(item.id)} size={20} color='#8C90A3' /> + {canUpdate('advertisements') && ( + + + + )} + {canDelete('advertisements') && ( + handleDelete(item.id)} size={20} color='#8C90A3' /> + )}
diff --git a/src/pages/annoncement/List.tsx b/src/pages/annoncement/List.tsx index bfbc6f4..a631146 100644 --- a/src/pages/annoncement/List.tsx +++ b/src/pages/annoncement/List.tsx @@ -11,6 +11,7 @@ import moment from 'moment-jalaali' import Pagination from '../../components/Pagination' import { toast } from '../../components/Toast'; import { ErrorType } from '../../helpers/types' +import { usePermissions } from '../../hooks/usePermissions' const AnnouncementtList: FC = () => { @@ -18,6 +19,7 @@ const AnnouncementtList: FC = () => { const [page, setPage] = useState(1) const getAnnoncements = useGetAnnoncements(page, '', '') const deleteAnnoncement = useDeleteAnnoncement() + const { canCreate, canUpdate, canDelete } = usePermissions() const handleDelete = (id: string) => { deleteAnnoncement.mutate(id, { @@ -38,21 +40,23 @@ const AnnouncementtList: FC = () => {
{t('announcement.announcements')}
-
- - - -
+ {canCreate('announcements') && ( +
+ + + +
+ )}
@@ -88,17 +92,21 @@ const AnnouncementtList: FC = () => {
- - + + + )} + {canDelete('announcements') && ( + handleDelete(item.id)} /> - - handleDelete(item.id)} - /> + )}
diff --git a/src/pages/blog/BlogCategory.tsx b/src/pages/blog/BlogCategory.tsx index f851511..7da58f3 100644 --- a/src/pages/blog/BlogCategory.tsx +++ b/src/pages/blog/BlogCategory.tsx @@ -17,6 +17,7 @@ import UploadBoxDraggble from '../../components/UploadBoxDraggble' import { useSingleUpload } from '../service/hooks/useServiceData' import { toast } from '../../components/Toast'; import UpdateCategory from './components/UpdateCategory' +import { usePermissions } from '../../hooks/usePermissions' const BlogCategory: FC = () => { const { t } = useTranslation('global') @@ -25,6 +26,7 @@ const BlogCategory: FC = () => { const singleUpload = useSingleUpload() const createBlogCategory = useCreateBlogCategory() const deleteBlogCategory = useDeleteBlogCategory() + const { canCreate, canUpdate, canDelete } = usePermissions() const formik = useFormik({ initialValues: { title: '', @@ -102,15 +104,19 @@ const BlogCategory: FC = () => {
- - { - deleteBlogCategory.mutateAsync(item.id, { - onSuccess: () => { - toast('دسته بندی با موفقیت حذف شد', 'success') - getBlogCategories.refetch() - } - }) - }} /> + {canUpdate('blogs') && ( + + )} + {canDelete('blogs') && ( + { + deleteBlogCategory.mutateAsync(item.id, { + onSuccess: () => { + toast('دسته بندی با موفقیت حذف شد', 'success') + getBlogCategories.refetch() + } + }) + }} /> + )}
@@ -123,59 +129,61 @@ const BlogCategory: FC = () => {
-
-

{t('blog.add_category')}

-
-

- {t('blog.category_status')} -

-
-
- {t('ads.deactive')} + {canCreate('blogs') && ( +
+

{t('blog.add_category')}

+
+

+ {t('blog.category_status')} +

+
+
+ {t('ads.deactive')} +
+ formik.setFieldValue('isActive', value)} + />
- formik.setFieldValue('isActive', value)} +
+ +
+
-
-
- -
- -
-