permission
This commit is contained in:
@@ -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[],
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
};
|
||||
@@ -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<number>(1)
|
||||
const getAds = useGetAdsList(page, search, isActive, since)
|
||||
const deleteAds = useDeleteAds()
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions()
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
deleteAds.mutate(id, {
|
||||
@@ -39,6 +41,7 @@ const AddList: FC = () => {
|
||||
{t('ads.ads')}
|
||||
</div>
|
||||
|
||||
{canCreate('advertisements') && (
|
||||
<Link to={Pages.ads.create}>
|
||||
<Button
|
||||
className='w-[172px]'
|
||||
@@ -51,6 +54,7 @@ const AddList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<div className='flex flex-col xl:flex-row justify-between items-center xl:items-end'>
|
||||
@@ -130,10 +134,14 @@ const AddList: FC = () => {
|
||||
</Td>
|
||||
<Td text={''}>
|
||||
<div className='flex gap-3'>
|
||||
{canUpdate('advertisements') && (
|
||||
<Link to={Pages.ads.update + item.id}>
|
||||
<Eye size={20} color='#8C90A3' />
|
||||
</Link>
|
||||
)}
|
||||
{canDelete('advertisements') && (
|
||||
<Trash onClick={() => handleDelete(item.id)} size={20} color='#8C90A3' />
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
|
||||
@@ -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<number>(1)
|
||||
const getAnnoncements = useGetAnnoncements(page, '', '')
|
||||
const deleteAnnoncement = useDeleteAnnoncement()
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions()
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
deleteAnnoncement.mutate(id, {
|
||||
@@ -38,6 +40,7 @@ const AnnouncementtList: FC = () => {
|
||||
<div>
|
||||
{t('announcement.announcements')}
|
||||
</div>
|
||||
{canCreate('announcements') && (
|
||||
<div>
|
||||
<Link to={Pages.announcement.create}>
|
||||
<Button
|
||||
@@ -53,6 +56,7 @@ const AnnouncementtList: FC = () => {
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -88,17 +92,21 @@ const AnnouncementtList: FC = () => {
|
||||
<Td text={moment(item.publishAt).format('jYYYY-jMM-jDD')} />
|
||||
<Td text={''}>
|
||||
<div className='flex gap-2'>
|
||||
{canUpdate('announcements') && (
|
||||
<Link to={Pages.announcement.detail + item.id}>
|
||||
<Edit
|
||||
className='size-5'
|
||||
color='#888'
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
{canDelete('announcements') && (
|
||||
<Trash
|
||||
className='size-5'
|
||||
color='#888'
|
||||
onClick={() => handleDelete(item.id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
|
||||
@@ -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<CreateBlogCategoryType>({
|
||||
initialValues: {
|
||||
title: '',
|
||||
@@ -102,7 +104,10 @@ const BlogCategory: FC = () => {
|
||||
<Td text={moment(item.createdAt).format('jYYYY/jMM/jDD')} />
|
||||
<Td text={t('')}>
|
||||
<div className='flex gap-2 items-center'>
|
||||
{canUpdate('blogs') && (
|
||||
<UpdateCategory id={item.id} />
|
||||
)}
|
||||
{canDelete('blogs') && (
|
||||
<Trash size={20} color='red' onClick={() => {
|
||||
deleteBlogCategory.mutateAsync(item.id, {
|
||||
onSuccess: () => {
|
||||
@@ -111,6 +116,7 @@ const BlogCategory: FC = () => {
|
||||
}
|
||||
})
|
||||
}} />
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
@@ -123,6 +129,7 @@ const BlogCategory: FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{canCreate('blogs') && (
|
||||
<div className='relative bg-white min-h-[calc(100vh-185px)] w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
||||
<p className='text-md'>{t('blog.add_category')}</p>
|
||||
<div className='text-sm flex items-center justify-between mt-4'>
|
||||
@@ -176,6 +183,7 @@ const BlogCategory: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ import { BlogItemType } from './types/BlogTypes'
|
||||
import moment from 'moment-jalaali'
|
||||
import TrashWithConfrim from '../../components/TrashWithConfrim'
|
||||
import Pagination from '../../components/Pagination'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
const BlogList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
@@ -18,6 +19,7 @@ const BlogList: FC = () => {
|
||||
const [search, setSearch] = useState<string>('')
|
||||
const getBlogs = useGetBlogs(search, page);
|
||||
const deleteBlog = useDeleteBlog()
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions()
|
||||
|
||||
return (
|
||||
<div className='mt-4 min-h-[500px]'>
|
||||
@@ -26,6 +28,7 @@ const BlogList: FC = () => {
|
||||
{t('blog.blog')}
|
||||
</div>
|
||||
|
||||
{canCreate('blogs') && (
|
||||
<Link to={Pages.blog.create}>
|
||||
<Button
|
||||
className='w-[172px]'
|
||||
@@ -38,6 +41,7 @@ const BlogList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -139,9 +143,12 @@ const BlogList: FC = () => {
|
||||
</Td> */}
|
||||
<Td text={''}>
|
||||
<div className='flex gap-2'>
|
||||
{canUpdate('blogs') && (
|
||||
<Link to={Pages.blog.detail + item.id}>
|
||||
<Eye size={20} color='#8C90A3' />
|
||||
</Link>
|
||||
)}
|
||||
{canDelete('blogs') && (
|
||||
<TrashWithConfrim
|
||||
onDelete={() => {
|
||||
deleteBlog.mutate(item.id, {
|
||||
@@ -152,6 +159,7 @@ const BlogList: FC = () => {
|
||||
}}
|
||||
isLoading={deleteBlog.isPending}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
|
||||
@@ -8,10 +8,12 @@ import { Link } from 'react-router-dom'
|
||||
import { Pages } from '../../config/Pages'
|
||||
import Button from '../../components/Button'
|
||||
import { Add, Eye } from 'iconsax-react'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const CardBankList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate, canUpdate } = usePermissions()
|
||||
const getCardBanks = useGetCardBanks()
|
||||
|
||||
return (
|
||||
@@ -23,6 +25,7 @@ const CardBankList: FC = () => {
|
||||
{t('cardBank.manage_banks')}
|
||||
</div>
|
||||
<div>
|
||||
{canCreate('bank_accounts') &&
|
||||
<Link to={Pages.cardBank.create}>
|
||||
<Button
|
||||
className='px-5'
|
||||
@@ -36,6 +39,7 @@ const CardBankList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
@@ -63,9 +67,11 @@ const CardBankList: FC = () => {
|
||||
<Td text={item.cardNumber} />
|
||||
<Td text={item.IBan} />
|
||||
<Td text={''}>
|
||||
{canUpdate('bank_accounts') &&
|
||||
<Link to={Pages.cardBank.detail + item.id}>
|
||||
<Eye size={20} color='#8C90A3' />
|
||||
</Link>
|
||||
}
|
||||
</Td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
@@ -9,9 +9,11 @@ import { CriticismsItemTypes } from './types/CriticismsTypes'
|
||||
import moment from 'moment-jalaali'
|
||||
import ModalConfrim from '../../components/ModalConfrim'
|
||||
import Pagination from '../../components/Pagination'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const CriticismsList: FC = () => {
|
||||
|
||||
const { canDelete } = usePermissions()
|
||||
const [itemId, setItemId] = useState<string>('')
|
||||
const [page, setPage] = useState<number>(1)
|
||||
const [openConfrim, setOpenConfrim] = useState(false)
|
||||
@@ -69,7 +71,9 @@ const CriticismsList: FC = () => {
|
||||
</Link>
|
||||
</Td>
|
||||
<Td text=''>
|
||||
{canDelete('criticisms') &&
|
||||
<Trash onClick={() => handleDelete(item.id)} size={20} color='#8C90A3' className='cursor-pointer' />
|
||||
}
|
||||
</Td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
@@ -8,11 +8,13 @@ import PageLoading from "../../components/PageLoading";
|
||||
import Pagination from "../../components/Pagination";
|
||||
import Td from "../../components/Td";
|
||||
import { Pages } from "../../config/Pages";
|
||||
import { usePermissions } from "../../hooks/usePermissions";
|
||||
import { useGetCustomers } from "./hooks/useCustomerData";
|
||||
import { CustomerItemType } from "./types/CustomerTypes";
|
||||
|
||||
const CustomerList: FC = () => {
|
||||
const { t } = useTranslation("global");
|
||||
const { canCreate, canUpdate } = usePermissions();
|
||||
const [page, setPage] = useState(1);
|
||||
const [limit, setLimit] = useState(10);
|
||||
const [search, setSearch] = useState("");
|
||||
@@ -29,6 +31,7 @@ const CustomerList: FC = () => {
|
||||
<div className="mt-4">
|
||||
<div className="flex w-full justify-between items-center">
|
||||
<div>{t("customer.customer_list")}</div>
|
||||
{canCreate("customers") && (
|
||||
<Link to={Pages.customer.create}>
|
||||
<Button className="px-5">
|
||||
<div className="flex gap-2">
|
||||
@@ -40,6 +43,7 @@ const CustomerList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end items-center mt-12">
|
||||
@@ -92,12 +96,14 @@ const CustomerList: FC = () => {
|
||||
</Link>
|
||||
</Td>
|
||||
<Td text={""}>
|
||||
{canUpdate("customers") && (
|
||||
<Link to={Pages.customer.update + item.id}>
|
||||
<Eye
|
||||
size={20}
|
||||
color="#8C90A3"
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
</Td>
|
||||
<Td text={""} />
|
||||
</tr>
|
||||
|
||||
@@ -14,10 +14,12 @@ import PageLoading from '../../components/PageLoading'
|
||||
import { NumberFormat } from '../../config/func'
|
||||
import { toast } from '../../components/Toast';
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const DiscountList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions()
|
||||
const [search, setSearch] = useState<string>('')
|
||||
const getDiscounts = useGetDiscounts(search)
|
||||
const deleteDiscount = useDeleteDiscount()
|
||||
@@ -40,6 +42,7 @@ const DiscountList: FC = () => {
|
||||
{t('discount.list')}
|
||||
</div>
|
||||
|
||||
{canCreate('discounts') &&
|
||||
<Link to={Pages.discount.create}>
|
||||
<Button
|
||||
className='w-[172px]'
|
||||
@@ -52,6 +55,7 @@ const DiscountList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -114,17 +118,21 @@ const DiscountList: FC = () => {
|
||||
</Td>
|
||||
<Td text={t('')}>
|
||||
<div className='flex gap-2 items-center'>
|
||||
{canUpdate('discounts') &&
|
||||
<Link
|
||||
to={Pages.discount.detail + item.id}
|
||||
>
|
||||
<Edit size={20} color='#888888' />
|
||||
</Link>
|
||||
}
|
||||
|
||||
{canDelete('discounts') &&
|
||||
<Trash
|
||||
size={20}
|
||||
color='#888888'
|
||||
onClick={() => handleDelete(item.id)}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
|
||||
@@ -12,6 +12,7 @@ import Td from '../../components/Td'
|
||||
import StatusWithText from '../../components/StatusWithText'
|
||||
import TrashWithConfrim from '../../components/TrashWithConfrim'
|
||||
import { useDeleteGuide } from './hooks/useGuideData'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
type GuideItem = {
|
||||
id: string;
|
||||
@@ -24,6 +25,7 @@ type GuideItem = {
|
||||
|
||||
const List: FC = () => {
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions()
|
||||
const { id } = useParams()
|
||||
const getGuides = useGetGuides(id || '')
|
||||
const deleteGuide = useDeleteGuide(id || '')
|
||||
@@ -36,6 +38,7 @@ const List: FC = () => {
|
||||
<div>
|
||||
{t('guide.list_guide')}
|
||||
</div>
|
||||
{canCreate('services') && (
|
||||
<div>
|
||||
<Link to={Pages.services.guide + id + '/create'}>
|
||||
<Button
|
||||
@@ -51,6 +54,7 @@ const List: FC = () => {
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{
|
||||
@@ -85,13 +89,17 @@ const List: FC = () => {
|
||||
</Td>
|
||||
<Td text=''>
|
||||
<div className='flex items-center gap-2'>
|
||||
{canUpdate('services') && (
|
||||
<Link to={Pages.services.guide + id + '/' + item.id}>
|
||||
<Eye size={20} color='#8C90A3' />
|
||||
</Link>
|
||||
)}
|
||||
{canDelete('services') && (
|
||||
<TrashWithConfrim
|
||||
onDelete={() => deleteGuide.mutate(item.id)}
|
||||
isLoading={deleteGuide.isPending}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
|
||||
@@ -6,6 +6,7 @@ import Pagination from '../../components/Pagination'
|
||||
import { useGetCategory } from './hooks/useLearningData'
|
||||
import { CategoryLearningType } from './types/LearningTypes'
|
||||
import CreateCategory from './components/CreateCategory'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
|
||||
const LearningCategory: FC = () => {
|
||||
@@ -13,6 +14,7 @@ const LearningCategory: FC = () => {
|
||||
const { t } = useTranslation('global')
|
||||
const [page, setPage] = useState<number>(1)
|
||||
const getCategory = useGetCategory()
|
||||
const { canCreate } = usePermissions()
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
@@ -65,7 +67,9 @@ const LearningCategory: FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{canCreate('learnings') && (
|
||||
<CreateCategory />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -9,11 +9,13 @@ import PageLoading from '../../components/PageLoading'
|
||||
import Td from '../../components/Td'
|
||||
import { LearningItemType } from './types/LearningTypes'
|
||||
import Pagination from '../../components/Pagination'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
const LearningList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const [page, setPage] = useState<number>(1)
|
||||
const getLearning = useGetLearning(page)
|
||||
const { canCreate } = usePermissions()
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
@@ -21,6 +23,7 @@ const LearningList: FC = () => {
|
||||
<div>
|
||||
{t('learning.list_learning')}
|
||||
</div>
|
||||
{canCreate('learnings') && (
|
||||
<div>
|
||||
<Link to={Pages.learning.create}>
|
||||
<Button
|
||||
@@ -36,6 +39,7 @@ const LearningList: FC = () => {
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{
|
||||
|
||||
@@ -10,10 +10,12 @@ import Pagination from '../../components/Pagination'
|
||||
import moment from 'moment-jalaali'
|
||||
import Accept from './components/Accept'
|
||||
import Reject from './components/Reject'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const PaymentList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { canUpdate } = usePermissions()
|
||||
const [page, setPage] = useState<number>(1)
|
||||
const [pageGateWay, setPageGateWay] = useState<number>(1)
|
||||
const [activeTab, setActiveTab] = useState<PaymentStatusType>('GATEWAY')
|
||||
@@ -140,7 +142,7 @@ const PaymentList: FC = () => {
|
||||
<Td text={t(`payment.${item.status}`)} />
|
||||
<Td text={''}>
|
||||
{
|
||||
item.status === 'PENDING' &&
|
||||
item.status === 'PENDING' && canUpdate('payments') &&
|
||||
<Fragment>
|
||||
<div className='flex gap-2'>
|
||||
<Accept
|
||||
|
||||
@@ -17,10 +17,12 @@ import { Pages } from '../../config/Pages'
|
||||
import TrashWithConfrim from '../../components/TrashWithConfrim'
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
import { toast } from '../../components/Toast';
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const ReceiptsList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { canUpdate, canDelete } = usePermissions()
|
||||
const [page, setPage] = useState<number>(1)
|
||||
const [searchParams] = useSearchParams()
|
||||
const [activeTab, setActiveTab] = useState<InvoiceStatus>('PENDING')
|
||||
@@ -177,13 +179,17 @@ const ReceiptsList: FC = () => {
|
||||
</Td>
|
||||
<Td text={''}>
|
||||
<div className='flex gap-2'>
|
||||
{canUpdate('invoices') &&
|
||||
<Link to={Pages.receipts.detail + item.id}>
|
||||
<Edit size={20} color='#888' />
|
||||
</Link>
|
||||
}
|
||||
{canDelete('invoices') &&
|
||||
<TrashWithConfrim
|
||||
onDelete={() => handleDeleteInvoice(item.id)}
|
||||
isLoading={deleteInvoice.isPending}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
|
||||
@@ -8,10 +8,12 @@ import { useGetResellers } from './hooks/useResellerData'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
import Td from '../../components/Td'
|
||||
import { ResellerItemType } from './types/Types'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const ResellerList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate } = usePermissions()
|
||||
const getReseller = useGetResellers()
|
||||
|
||||
return (
|
||||
@@ -21,6 +23,7 @@ const ResellerList: FC = () => {
|
||||
{t('reseller.list')}
|
||||
</div>
|
||||
|
||||
{canCreate('reseller') && (
|
||||
<Link to={Pages.reseller.create}>
|
||||
<Button
|
||||
className='w-[172px]'
|
||||
@@ -33,6 +36,7 @@ const ResellerList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{
|
||||
|
||||
@@ -11,11 +11,13 @@ import StatusCategory from './components/StatusCategory'
|
||||
import Pagination from '../../components/Pagination'
|
||||
import UpdateCategory from './components/UpdateCategory'
|
||||
import { Trash } from 'iconsax-react'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
|
||||
const Category: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions()
|
||||
const [search, setSearch] = useState<string>('')
|
||||
const [page, setPage] = useState<number>(1)
|
||||
const [isActive, setIsActive] = useState<'active' | 'deactive' | ''>('')
|
||||
@@ -102,12 +104,16 @@ const Category: FC = () => {
|
||||
</Td>
|
||||
<Td text={''}>
|
||||
<div className='flex gap-2'>
|
||||
{canUpdate('services') && (
|
||||
<UpdateCategory refetch={() => getCategory.refetch()} id={item.id} />
|
||||
)}
|
||||
{canDelete('services') && (
|
||||
<Trash
|
||||
size={20}
|
||||
color='#888888'
|
||||
onClick={() => handleDelete(item.id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
@@ -130,7 +136,7 @@ const Category: FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CreateCategory />
|
||||
{canCreate('services') && <CreateCategory />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -15,10 +15,12 @@ import Pagination from '../../components/Pagination'
|
||||
import ToggleStatusService from './components/ToggleStatusService'
|
||||
import CheckBoxComponent from '../../components/CheckBoxComponent'
|
||||
import ToggleSliderService from './components/ToggleSliderService'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const ListService: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate, canUpdate } = usePermissions()
|
||||
const [category, setCategory] = useState<string>('')
|
||||
const [status, setstatus] = useState<'ACTIVE' | 'IN_ACTIVE' | ''>('')
|
||||
const [page, setPage] = useState<number>(1)
|
||||
@@ -33,6 +35,7 @@ const ListService: FC = () => {
|
||||
<div>
|
||||
{t('service.list_service')}
|
||||
</div>
|
||||
{canCreate('services') && (
|
||||
<div>
|
||||
<Link to={Pages.services.add}>
|
||||
<Button
|
||||
@@ -48,6 +51,7 @@ const ListService: FC = () => {
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between items-center mt-12'>
|
||||
@@ -134,6 +138,7 @@ const ListService: FC = () => {
|
||||
<Td text=''>
|
||||
{
|
||||
item.subscriptionCount === 0 ?
|
||||
(canCreate('services') &&
|
||||
<Link to={Pages.services.plan + item.id}>
|
||||
<Button
|
||||
className='h-8 bg-transparent border border-black text-black text-xs'
|
||||
@@ -144,6 +149,7 @@ const ListService: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
)
|
||||
:
|
||||
<Link to={Pages.services.plan + item.id} className='flex gap-1 text-sm text-[#0047FF]'>
|
||||
<div>{item.subscriptionCount}</div>
|
||||
@@ -164,9 +170,11 @@ const ListService: FC = () => {
|
||||
/>
|
||||
</Td>
|
||||
<Td text={''}>
|
||||
{canUpdate('services') && (
|
||||
<Link to={Pages.services.update + item.id}>
|
||||
<Eye size={20} color='#8C90A3' />
|
||||
</Link>
|
||||
)}
|
||||
</Td>
|
||||
<Td text={''}>
|
||||
<Link to={Pages.services.guide + item.id}>
|
||||
|
||||
@@ -9,11 +9,13 @@ import { PlanItemType } from './types/ServiceTypes'
|
||||
import ToggleStatusPlan from './components/ToggleStatusPlan'
|
||||
import EditPlan from './components/EditPlan'
|
||||
import { NumberFormat } from '../../config/func'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const Plans: FC = () => {
|
||||
|
||||
const { id } = useParams()
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate, canUpdate } = usePermissions()
|
||||
const getPlans = useGetPlans(id)
|
||||
|
||||
return (
|
||||
@@ -27,7 +29,7 @@ const Plans: FC = () => {
|
||||
<div>
|
||||
{t('plan.plans_list') + ' ' + getPlans.data?.data?.service?.name}
|
||||
</div>
|
||||
<CreatePlan />
|
||||
{canCreate('services') && <CreatePlan />}
|
||||
</div>
|
||||
|
||||
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
|
||||
@@ -54,9 +56,11 @@ const Plans: FC = () => {
|
||||
<ToggleStatusPlan defaultActive={item.isActive} id={item.id} />
|
||||
</Td>
|
||||
<Td text=''>
|
||||
{canUpdate('services') && (
|
||||
<EditPlan
|
||||
id={item.id}
|
||||
/>
|
||||
)}
|
||||
</Td>
|
||||
</tr>
|
||||
))
|
||||
|
||||
@@ -11,11 +11,13 @@ import PageLoading from '../../components/PageLoading'
|
||||
import { toast } from '../../components/Toast';
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
import TrashWithConfrim from '../../components/TrashWithConfrim'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
const SliderList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { data: sliders, isPending, refetch } = useGetSliders()
|
||||
const deleteSlider = useDeleteSlider()
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions()
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
deleteSlider.mutate(id, {
|
||||
@@ -35,6 +37,7 @@ const SliderList: FC = () => {
|
||||
<div>
|
||||
{t('slider.list_slider')}
|
||||
</div>
|
||||
{canCreate('blogs') && (
|
||||
<div>
|
||||
<Link to={Pages.sliders.create}>
|
||||
<Button
|
||||
@@ -50,6 +53,7 @@ const SliderList: FC = () => {
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{
|
||||
@@ -80,16 +84,20 @@ const SliderList: FC = () => {
|
||||
<Td text={item.isActive ? t('slider.active') : t('slider.inactive')} />
|
||||
<Td text={''}>
|
||||
<div className='flex gap-2'>
|
||||
{canUpdate('blogs') && (
|
||||
<Link to={Pages.sliders.detail + item.id}>
|
||||
<Edit
|
||||
className='size-5'
|
||||
color='#888'
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
{canDelete('blogs') && (
|
||||
<TrashWithConfrim
|
||||
onDelete={() => handleDelete(item.id)}
|
||||
isLoading={deleteSlider.isPending}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
|
||||
@@ -13,10 +13,12 @@ import { ServiceItemType } from '../service/types/ServiceTypes'
|
||||
import TrashWithConfrim from '../../components/TrashWithConfrim'
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
import { toast } from '../../components/Toast';
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const SubscriptionsList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { canDelete } = usePermissions()
|
||||
const [page, setPage] = useState<number>(1)
|
||||
const [status, setStatus] = useState<string>('')
|
||||
const [serviceId, setServiceId] = useState<string>('')
|
||||
@@ -199,10 +201,12 @@ const SubscriptionsList: FC = () => {
|
||||
</div>
|
||||
</Td>
|
||||
<Td text=''>
|
||||
{canDelete('customers') && (
|
||||
<TrashWithConfrim
|
||||
onDelete={() => handleDelete(item.id)}
|
||||
isLoading={isPending}
|
||||
/>
|
||||
)}
|
||||
</Td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
@@ -8,9 +8,11 @@ import { Link } from 'react-router-dom'
|
||||
import { Pages } from '../../config/Pages'
|
||||
import Delete from './components/Delete'
|
||||
import Button from '../../components/Button'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
const SupportList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions()
|
||||
const { data, refetch } = useGetPlans()
|
||||
|
||||
return (
|
||||
@@ -20,6 +22,7 @@ const SupportList: FC = () => {
|
||||
{t('support.plans')}
|
||||
</div>
|
||||
|
||||
{canCreate('support_plan') &&
|
||||
<Link to={Pages.support.create}>
|
||||
<Button
|
||||
className='w-fit px-6'
|
||||
@@ -32,6 +35,7 @@ const SupportList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
|
||||
@@ -65,10 +69,14 @@ const SupportList: FC = () => {
|
||||
</Td>
|
||||
<Td text={''}>
|
||||
<div className='flex items-center gap-2'>
|
||||
{canUpdate('support_plan') &&
|
||||
<Link to={Pages.support.detail + item.id}>
|
||||
<Edit size={20} color='#8C90A3' />
|
||||
</Link>
|
||||
}
|
||||
{canDelete('support_plan') &&
|
||||
<Delete id={item.id} refetch={refetch} />
|
||||
}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { clx } from "../../../helpers/utils";
|
||||
import { useCreateTaskPhase } from "../task-phase/hooks/useTaskPhaseData";
|
||||
import type { TaskPhaseItemType } from "../task-phase/types/TaskPhaseTypes";
|
||||
import { LABEL_COLOR_OPTIONS } from "./task-detail/labels/constants";
|
||||
import { usePermissions } from "../../../hooks/usePermissions";
|
||||
|
||||
const DEFAULT_COLUMN_COLOR = "#4F46E5";
|
||||
|
||||
@@ -19,6 +20,7 @@ type Props = {
|
||||
|
||||
const AddNewColumn: FC<Props> = ({ projectId, nextOrder, onColumnCreated }) => {
|
||||
const { t } = useTranslation("global");
|
||||
const { canCreate } = usePermissions();
|
||||
const colorInputRef = useRef<HTMLInputElement>(null);
|
||||
const createTaskPhase = useCreateTaskPhase();
|
||||
|
||||
@@ -57,6 +59,10 @@ const AddNewColumn: FC<Props> = ({ projectId, nextOrder, onColumnCreated }) => {
|
||||
);
|
||||
};
|
||||
|
||||
if (!canCreate("task_phase")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isAdding) {
|
||||
return (
|
||||
<button
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { toast } from '../../../components/Toast';
|
||||
import ModalConfrim from "../../../components/ModalConfrim";
|
||||
import { ErrorType } from "../../../helpers/types";
|
||||
import { usePermissions } from "../../../hooks/usePermissions";
|
||||
import { useCreateTask, useGetTasksByTaskPhase } from "../task/hooks/useTaskData";
|
||||
import type { TaskItemType } from "../task/types/TaskTypes";
|
||||
import { mapTaskItemToTask } from "../task/utils/mapTaskItemToTask";
|
||||
@@ -37,6 +38,7 @@ const Column: FC<Props> = ({
|
||||
onColumnTitleUpdated,
|
||||
}) => {
|
||||
const { t } = useTranslation("global");
|
||||
const { canCreate } = usePermissions();
|
||||
const deleteTaskPhase = useDeleteTaskPhase();
|
||||
const updateTaskPhase = useUpdateTaskPhase();
|
||||
const createTask = useCreateTask();
|
||||
@@ -314,7 +316,8 @@ const Column: FC<Props> = ({
|
||||
)}
|
||||
</Droppable>
|
||||
|
||||
{isAdding ? (
|
||||
{canCreate("task") && (
|
||||
isAdding ? (
|
||||
<div className="mt-3 sm:mt-4 xl:mt-5 shrink-0 flex flex-col gap-3">
|
||||
<input
|
||||
type="text"
|
||||
@@ -356,6 +359,7 @@ const Column: FC<Props> = ({
|
||||
<AddCircle size={18} color="#888888" />
|
||||
<span className="text-description text-[13px] mt-0.5">{t("taskmanager.add_new_task")}</span>
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
|
||||
import { More } from "iconsax-react";
|
||||
import { FC } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { usePermissions } from "../../../hooks/usePermissions";
|
||||
|
||||
type Props = {
|
||||
onEdit: () => void;
|
||||
@@ -10,6 +11,7 @@ type Props = {
|
||||
|
||||
const ColumnMenu: FC<Props> = ({ onEdit, onDelete }) => {
|
||||
const { t } = useTranslation("global");
|
||||
const { canUpdate, canDelete } = usePermissions();
|
||||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
@@ -26,6 +28,7 @@ const ColumnMenu: FC<Props> = ({ onEdit, onDelete }) => {
|
||||
anchor="bottom start"
|
||||
className="z-20 mt-1 min-w-[120px] rounded-xl bg-white shadow-md border border-border py-1 text-sm"
|
||||
>
|
||||
{canUpdate("task_phase") && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@@ -36,6 +39,8 @@ const ColumnMenu: FC<Props> = ({ onEdit, onDelete }) => {
|
||||
>
|
||||
{t("taskmanager.edit_column")}
|
||||
</button>
|
||||
)}
|
||||
{canDelete("task_phase") && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@@ -46,6 +51,7 @@ const ColumnMenu: FC<Props> = ({ onEdit, onDelete }) => {
|
||||
>
|
||||
{t("taskmanager.delete_column")}
|
||||
</button>
|
||||
)}
|
||||
</PopoverPanel>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ArrowDown2, CloseCircle, Paintbucket } from "iconsax-react";
|
||||
import { type FC } from "react";
|
||||
import TrashWithConfrim from "../../../../components/TrashWithConfrim";
|
||||
import { clx } from "../../../../helpers/utils";
|
||||
import { usePermissions } from "../../../../hooks/usePermissions";
|
||||
import type { Column } from "../../types";
|
||||
import TaskDetailCoverColorPopover from "./cover/TaskDetailCoverColorPopover";
|
||||
|
||||
@@ -31,6 +32,7 @@ const TaskDetailHeader: FC<Props> = ({
|
||||
onDelete,
|
||||
isDeleting,
|
||||
}) => {
|
||||
const { canDelete } = usePermissions();
|
||||
const selectedColumn = columns.find((column) => column.id === selectedPhaseId);
|
||||
|
||||
return (
|
||||
@@ -99,7 +101,7 @@ const TaskDetailHeader: FC<Props> = ({
|
||||
)}
|
||||
</Popover>
|
||||
|
||||
{onDelete ? (
|
||||
{onDelete && canDelete("task") ? (
|
||||
<div className="size-8 rounded-full bg-white/60 flex items-center justify-center">
|
||||
<TrashWithConfrim onDelete={onDelete} isLoading={isDeleting} color="#FF0000" />
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
|
||||
import { More } from "iconsax-react";
|
||||
import { FC } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { usePermissions } from "../../../../hooks/usePermissions";
|
||||
|
||||
type Props = {
|
||||
onEdit: () => void;
|
||||
@@ -10,6 +11,7 @@ type Props = {
|
||||
|
||||
const ProjectCardMenu: FC<Props> = ({ onEdit, onDelete }) => {
|
||||
const { t } = useTranslation("global");
|
||||
const { canUpdate, canDelete } = usePermissions();
|
||||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
@@ -21,6 +23,7 @@ const ProjectCardMenu: FC<Props> = ({ onEdit, onDelete }) => {
|
||||
anchor="bottom start"
|
||||
className="z-20 mt-1 min-w-[120px] rounded-xl bg-white shadow-md border border-border py-1 text-sm"
|
||||
>
|
||||
{canUpdate("project") && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onEdit}
|
||||
@@ -28,6 +31,8 @@ const ProjectCardMenu: FC<Props> = ({ onEdit, onDelete }) => {
|
||||
>
|
||||
{t("edit")}
|
||||
</button>
|
||||
)}
|
||||
{canDelete("project") && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onDelete}
|
||||
@@ -35,6 +40,7 @@ const ProjectCardMenu: FC<Props> = ({ onEdit, onDelete }) => {
|
||||
>
|
||||
{t("taskmanager.delete_project")}
|
||||
</button>
|
||||
)}
|
||||
</PopoverPanel>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import Button from "../../../../components/Button";
|
||||
import { Pages } from "../../../../config/Pages";
|
||||
import { usePermissions } from "../../../../hooks/usePermissions";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
@@ -13,6 +14,7 @@ type Props = {
|
||||
|
||||
const ProjectListHeader: FC<Props> = ({ title, workspaceId, onSettingsClick }) => {
|
||||
const { t } = useTranslation("global");
|
||||
const { canCreate } = usePermissions();
|
||||
const createProjectLink = workspaceId ? `${Pages.taskmanager.createProject}?workspaceId=${workspaceId}` : Pages.taskmanager.createProject;
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
@@ -31,6 +33,7 @@ const ProjectListHeader: FC<Props> = ({ title, workspaceId, onSettingsClick }) =
|
||||
<span>برگشت</span>
|
||||
</Button>
|
||||
|
||||
{canCreate("project") && (
|
||||
<Link to={createProjectLink}>
|
||||
<Button
|
||||
onClick={onSettingsClick}
|
||||
@@ -43,6 +46,7 @@ const ProjectListHeader: FC<Props> = ({ title, workspaceId, onSettingsClick }) =
|
||||
<span>{t("taskmanager.new_project")}</span>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -11,9 +11,11 @@ import { Pages } from "../../../config/Pages";
|
||||
import { ErrorType } from "../../../helpers/types";
|
||||
import { useDeleteWorkspace, useGetWorkspaces } from "./hooks/useWorkspaceData";
|
||||
import { WorkspaceItemType } from "./types/WorkspaceTypes";
|
||||
import { usePermissions } from "../../../hooks/usePermissions";
|
||||
|
||||
const WorkspaceList: FC = () => {
|
||||
const { t } = useTranslation("global");
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions();
|
||||
const getWorkspaces = useGetWorkspaces();
|
||||
const deleteWorkspace = useDeleteWorkspace();
|
||||
const [deleteTarget, setDeleteTarget] = useState<WorkspaceItemType | null>(null);
|
||||
@@ -38,6 +40,7 @@ const WorkspaceList: FC = () => {
|
||||
<div className="mt-4">
|
||||
<div className="flex w-full justify-between items-center">
|
||||
<div>{t("taskmanager.workspace_list")}</div>
|
||||
{canCreate("workspace") && (
|
||||
<Link to={Pages.taskmanager.createWorkspace}>
|
||||
<Button className="px-5">
|
||||
<div className="flex gap-2">
|
||||
@@ -49,6 +52,7 @@ const WorkspaceList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{getWorkspaces.isPending ? (
|
||||
@@ -92,18 +96,22 @@ const WorkspaceList: FC = () => {
|
||||
</Td>
|
||||
<Td text="">
|
||||
<div className="flex items-center gap-2">
|
||||
{canUpdate("workspace") && (
|
||||
<Link to={Pages.taskmanager.updateWorkspace + item.id}>
|
||||
<Edit
|
||||
size={20}
|
||||
color="#8C90A3"
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
{canDelete("workspace") && (
|
||||
<Trash
|
||||
onClick={() => setDeleteTarget(item)}
|
||||
color="#888"
|
||||
size={20}
|
||||
className="cursor-pointer"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
|
||||
@@ -14,10 +14,12 @@ import { useCreateCategory, useGetCategories } from './hooks/useTicketData'
|
||||
import Textarea from '../../components/Textarea'
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
import { toast } from '../../components/Toast';
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const TicketCategory: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate } = usePermissions()
|
||||
const getGroups = useGetGroups()
|
||||
const createCategory = useCreateCategory()
|
||||
const getCategories = useGetCategories()
|
||||
@@ -100,6 +102,7 @@ const TicketCategory: FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{canCreate('ticket_categories') &&
|
||||
<div className='bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
||||
<div className='text-sm'>
|
||||
{t('ticket.add_category')}
|
||||
@@ -187,6 +190,7 @@ const TicketCategory: FC = () => {
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -11,9 +11,11 @@ import { Pages } from "../../../config/Pages";
|
||||
import { ErrorType } from "../../../helpers/types";
|
||||
import { useDeleteMaster, useGetMasters } from "../hooks/useTicketData";
|
||||
import { TicketMasterItemType } from "../types/TicketTypes";
|
||||
import { usePermissions } from "../../../hooks/usePermissions";
|
||||
|
||||
const TicketMastersList: FC = () => {
|
||||
const { t } = useTranslation("global");
|
||||
const { canCreate, canDelete } = usePermissions();
|
||||
const getMasters = useGetMasters();
|
||||
const deleteMaster = useDeleteMaster();
|
||||
|
||||
@@ -35,6 +37,7 @@ const TicketMastersList: FC = () => {
|
||||
<div className="flex w-full justify-between items-center">
|
||||
<div>{t("ticket.masters")}</div>
|
||||
<div>
|
||||
{canCreate("ticket_masters") && (
|
||||
<Link to={Pages.ticket.mastersCreate}>
|
||||
<Button className="px-5">
|
||||
<div className="flex gap-2">
|
||||
@@ -46,6 +49,7 @@ const TicketMastersList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -74,11 +78,13 @@ const TicketMastersList: FC = () => {
|
||||
{/* <Link to={Pages.ticket.mastersUpdate + item.id}>
|
||||
<Edit size={20} color="#8C90A3" />
|
||||
</Link> */}
|
||||
{canDelete("ticket_masters") && (
|
||||
<TrashWithConfrim
|
||||
onDelete={() => handleDelete(item.id)}
|
||||
isLoading={deleteMaster.isPending}
|
||||
color="#8C90A3"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
|
||||
@@ -7,11 +7,13 @@ import { Pages } from '../../config/Pages'
|
||||
import { Add } from 'iconsax-react'
|
||||
import Td from '../../components/Td'
|
||||
import { GroupItemType } from './types/UserTypes'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const GroupList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const navigate = useNavigate()
|
||||
const { canCreate } = usePermissions()
|
||||
const getGroups = useGetGroups()
|
||||
|
||||
return (
|
||||
@@ -20,6 +22,7 @@ const GroupList: FC = () => {
|
||||
<div>
|
||||
{t('user.groups')}
|
||||
</div>
|
||||
{canCreate('admins') && (
|
||||
<div>
|
||||
<Button
|
||||
className='px-5'
|
||||
@@ -34,6 +37,7 @@ const GroupList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
|
||||
|
||||
@@ -13,10 +13,12 @@ import { toast } from '../../components/Toast';
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
import Pagination from '../../components/Pagination'
|
||||
import TrashWithConfrim from '../../components/TrashWithConfrim'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
const UsersList: FC = () => {
|
||||
|
||||
const navigate = useNavigate()
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions()
|
||||
const [page, setPage] = useState<number>(1)
|
||||
const [search, setSearch] = useState<string>('')
|
||||
const getUsers = useGetUsers(search, undefined, page)
|
||||
@@ -40,6 +42,7 @@ const UsersList: FC = () => {
|
||||
<div>
|
||||
{t('user.users_list')}
|
||||
</div>
|
||||
{canCreate('admins') && (
|
||||
<div>
|
||||
<Button
|
||||
className='px-5'
|
||||
@@ -54,6 +57,7 @@ const UsersList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between items-center mt-12'>
|
||||
@@ -103,16 +107,20 @@ const UsersList: FC = () => {
|
||||
</Td>
|
||||
<Td text={''}>
|
||||
<div className='flex gap-2'>
|
||||
{canUpdate('admins') && (
|
||||
<Link to={Pages.users.detail + item.id}>
|
||||
<Eye size={20} color='#8C90A3' />
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{canDelete('admins') && (
|
||||
<TrashWithConfrim
|
||||
size={20}
|
||||
color='#8C90A3'
|
||||
onDelete={() => handleDeleteUser(item.id)}
|
||||
isLoading={deleteUser.isPending}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
<Td text={''} />
|
||||
|
||||
@@ -8,11 +8,13 @@ import { Link, useNavigate } from 'react-router-dom'
|
||||
import { Pages } from '../../config/Pages'
|
||||
import { useGetRoles } from './hooks/useUserData'
|
||||
import { RoleItemType } from './types/UserTypes'
|
||||
import { usePermissions } from '../../hooks/usePermissions'
|
||||
|
||||
const RolesList: FC = () => {
|
||||
|
||||
const navigate = useNavigate()
|
||||
const { t } = useTranslation('global')
|
||||
const { canCreate, canUpdate } = usePermissions()
|
||||
const [search, setSearch] = useState<string>('')
|
||||
const getRoles = useGetRoles(search)
|
||||
|
||||
@@ -23,6 +25,7 @@ const RolesList: FC = () => {
|
||||
<div>
|
||||
{t('user.roles_list')}
|
||||
</div>
|
||||
{canCreate('admins') && (
|
||||
<div>
|
||||
<Button
|
||||
className='px-5'
|
||||
@@ -37,6 +40,7 @@ const RolesList: FC = () => {
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='flex justify-end items-center mt-12'>
|
||||
@@ -69,9 +73,11 @@ const RolesList: FC = () => {
|
||||
<Td text={item.userCount + ''} />
|
||||
<Td text={item.isAdmin ? t('yes') : t('no')} />
|
||||
<Td text={''}>
|
||||
{canUpdate('admins') && (
|
||||
<Link to={Pages.users.roleUpdate + item.id}>
|
||||
<Eye size={20} color='#8C90A3' />
|
||||
</Link>
|
||||
)}
|
||||
</Td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
@@ -39,13 +39,13 @@ const SupportSubMenu: FC = () => {
|
||||
title={t("submenu.ticket_category")}
|
||||
isActive={isActive("/tickets/category")}
|
||||
link={Pages.ticket.category}
|
||||
activeName="tickets"
|
||||
activeName="ticket_categories"
|
||||
/>
|
||||
<SubMenuItem
|
||||
title={t("submenu.ticket_masters")}
|
||||
isActive={isActive("/tickets/masters")}
|
||||
link={Pages.ticket.masters}
|
||||
activeName="tickets"
|
||||
activeName="ticket_masters"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user