[WEB-2357] fix: update and redefine user roles across the platform (#5466)

* chore: removed viewer role

* chore: indentation

* chore: remove viewer role

* chore: handled user permissions in store

* chore: updated the migration file

* chore: updated user permissions store

* chore: removed the owner key

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* fix: build error

* chore: updated user permissions store and handled the permissions fetch in workspace and project wrappers

* chore: package user enum updated

* chore: user permission updated

* chore: user permission updated

* chore: resolved build errors

* chore: resolved build error

* chore: resolved build errors

* chore: computedFn deep map issue resolved

* chore: added back migration

* chore: added new field in project table

* chore: removed member store in users

* chore: private project for admins

* chore: workspace notification access validation updated

* fix: workspace member edit option

* fix: project intake permission validation updated

* chore: workspace export settings permission updated

* chore: guest_view_all_issues added

* chore: guest_view_all_issues added

* chore: key changed for guest access

* chore: added validation for individual issues

* chore: changed the dashboard issues count

* chore: added new yarn file

* chore: modified yarn file

* chore: project page permission updated

* chore: project page permission updated

* chore: member setting ux updated

* chore: build error

* fix: yarn lock

* fix: build error

---------

Co-authored-by: gurusainath <gurusainath007@gmail.com>
Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
This commit is contained in:
Bavisetti Narayan 2024-09-11 17:10:15 +05:30 committed by GitHub
parent 7013a36629
commit fdcd9a376c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
172 changed files with 2057 additions and 1627 deletions

View file

@ -8,8 +8,8 @@ import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui";
// constants
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
// hooks
import { useIssues, useProject, useUser } from "@/hooks/store";
import { useIssues, useProject, useUser, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
isOpen: boolean;
handleClose: () => void;
@ -26,7 +26,12 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
// store hooks
const { issueMap } = useIssues();
const { getProjectById } = useProject();
const { data: currentUser, canPerformProjectAdminActions } = useUser();
const { allowPermissions } = useUserPermissions();
const { data: currentUser } = useUser();
// derived values
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
useEffect(() => {
setIsDeleting(false);

View file

@ -10,15 +10,22 @@ import { ArchiveIssueModal, DeleteIssueModal, IssueSubscription } from "@/compon
// constants
import { ISSUE_ARCHIVED, ISSUE_DELETED } from "@/constants/event-tracker";
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
import { ARCHIVABLE_STATE_GROUPS } from "@/constants/state";
// helpers
import { cn } from "@/helpers/common.helper";
import { copyTextToClipboard } from "@/helpers/string.helper";
// hooks
import { useEventTracker, useIssueDetail, useIssues, useProjectState, useUser } from "@/hooks/store";
import {
useEventTracker,
useIssueDetail,
useIssues,
useProjectState,
useUser,
useUserPermissions,
} from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
workspaceSlug: string;
@ -38,10 +45,8 @@ export const IssueDetailQuickActions: FC<Props> = observer((props) => {
const router = useAppRouter();
// hooks
const {
data: currentUser,
membership: { currentProjectRole },
} = useUser();
const { data: currentUser } = useUser();
const { allowPermissions } = useUserPermissions();
const { isMobile } = usePlatformOS();
const { getStateById } = useProjectState();
const {
@ -149,8 +154,11 @@ export const IssueDetailQuickActions: FC<Props> = observer((props) => {
};
// auth
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const canRestoreIssue = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditable = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
const canRestoreIssue = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const isArchivingAllowed = !issue?.archived_at && isEditable;
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);

View file

@ -13,10 +13,10 @@ import { IssuePeekOverview } from "@/components/issues";
// constants
import { ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED } from "@/constants/event-tracker";
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useAppTheme, useEventTracker, useIssueDetail, useIssues, useUser } from "@/hooks/store";
import { useAppTheme, useEventTracker, useIssueDetail, useIssues, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// images
import emptyIssue from "@/public/empty-state/issue.svg";
// local components
@ -77,9 +77,7 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
issues: { removeIssue: removeArchivedIssue },
} = useIssues(EIssuesStoreType.ARCHIVED);
const { captureIssueEvent } = useEventTracker();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { issueDetailSidebarCollapsed } = useAppTheme();
const issueOperations: TIssueOperations = useMemo(
@ -332,7 +330,7 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
// issue details
const issue = getIssueById(issueId);
// checking if issue is editable, based on user role
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditable = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
return (
<>

View file

@ -10,11 +10,11 @@ import { TOAST_TYPE, setToast } from "@plane/ui";
import { CalendarChart } from "@/components/issues";
//constants
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useIssues, useUser, useCalendarView } from "@/hooks/store";
import { useIssues, useCalendarView, useUserPermissions } from "@/hooks/store";
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
import { useIssuesActions } from "@/hooks/use-issues-actions";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// types
import { IQuickActionProps } from "../list/list-view-types";
import { handleDragDrop } from "./utils";
@ -40,9 +40,7 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
// hooks
const storeType = useIssueStoreType() as CalendarStoreType;
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { issues, issuesFilter, issueMap } = useIssues(storeType);
const {
fetchIssues,
@ -58,7 +56,10 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
const issueCalendarView = useCalendarView();
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const displayFilters = issuesFilter.issueFilters?.displayFilters;

View file

@ -22,13 +22,13 @@ import { CalendarHeader, CalendarIssueBlocks, CalendarWeekDays, CalendarWeekHead
// constants
import { MONTHS_LIST } from "@/constants/calendar";
import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { cn } from "@/helpers/common.helper";
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
// hooks
import { useIssues, useUser } from "@/hooks/store";
import { useIssues, useUserPermissions } from "@/hooks/store";
import useSize from "@/hooks/use-window-size";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// store
import { ICycleIssuesFilter } from "@/store/issue/cycle";
import { ICalendarStore } from "@/store/issue/issue_calendar_view.store";
@ -91,14 +91,15 @@ export const CalendarChart: React.FC<Props> = observer((props) => {
const {
issues: { viewFlags },
} = useIssues(EIssuesStoreType.PROJECT);
const { allowPermissions } = useUserPermissions();
const {
membership: { currentProjectRole },
} = useUser();
const [windowWidth] = useSize();
const { enableIssueCreation } = viewFlags || {};
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const calendarPayload = issueCalendarView.calendarPayload;

View file

@ -4,9 +4,9 @@ import { PlusIcon } from "lucide-react";
import { EmptyState } from "@/components/common";
// constants
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useCommandPalette, useEventTracker, useUser } from "@/hooks/store";
import { useCommandPalette, useEventTracker, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// assets
import emptyIssue from "@/public/empty-state/issue.svg";
@ -14,11 +14,13 @@ export const ProjectViewEmptyState: React.FC = observer(() => {
// store hooks
const { toggleCreateIssueModal } = useCommandPalette();
const { setTrackElement } = useEventTracker();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
// auth
const isCreatingIssueAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isCreatingIssueAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
return (
<div className="grid h-full w-full place-items-center">

View file

@ -16,13 +16,13 @@ import {
AppliedStateGroupFilters,
} from "@/components/issues";
// constants
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { replaceUnderscoreIfSnakeCase } from "@/helpers/string.helper";
// hooks
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
// plane web components
import { AppliedIssueTypeFilters } from "@/plane-web/components/issues";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
appliedFilters: IIssueFilterOptions;
@ -48,16 +48,16 @@ export const AppliedFiltersList: React.FC<Props> = observer((props) => {
disableEditing = false,
} = props;
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
if (!appliedFilters) return null;
if (Object.keys(appliedFilters).length === 0) return null;
const isEditingAllowed =
!disableEditing && (alwaysAllowEditing || (currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER));
!disableEditing &&
(alwaysAllowEditing ||
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT));
return (
<div className="flex flex-wrap items-stretch gap-2 bg-custom-background-100 truncate my-auto">

View file

@ -17,9 +17,10 @@ import { CreateUpdateWorkspaceViewModal } from "@/components/workspace";
import { GLOBAL_VIEW_UPDATED } from "@/constants/event-tracker";
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
import { EViewAccess } from "@/constants/views";
import { DEFAULT_GLOBAL_VIEWS_LIST, EUserWorkspaceRoles } from "@/constants/workspace";
import { DEFAULT_GLOBAL_VIEWS_LIST } from "@/constants/workspace";
// hooks
import { useEventTracker, useGlobalView, useIssues, useLabel, useUser } from "@/hooks/store";
import { useEventTracker, useGlobalView, useIssues, useLabel, useUser, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { getAreFiltersEqual } from "../../../utils";
type Props = {
@ -37,10 +38,8 @@ export const GlobalViewsAppliedFiltersRoot = observer((props: Props) => {
const { workspaceLabels } = useLabel();
const { globalViewMap, updateGlobalView } = useGlobalView();
const { captureEvent } = useEventTracker();
const {
data,
membership: { currentWorkspaceRole },
} = useUser();
const { data } = useUser();
const { allowPermissions } = useUserPermissions();
const [isModalOpen, setIsModalOpen] = useState(false);
@ -120,7 +119,10 @@ export const GlobalViewsAppliedFiltersRoot = observer((props: Props) => {
// add a placeholder object instead of appliedFilters if it is undefined
const areFiltersEqual = getAreFiltersEqual(appliedFilters ?? {}, issueFilters, viewDetails);
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
const isAuthorizedUser = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const isDefaultView = DEFAULT_GLOBAL_VIEWS_LIST.map((view) => view.key).includes(globalViewId as TStaticViewTypes);

View file

@ -7,9 +7,10 @@ import { Header, EHeaderVariant } from "@plane/ui";
import { AppliedFiltersList, SaveFilterView } from "@/components/issues";
// constants
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
import { useLabel, useProjectState, useUser } from "@/hooks/store";
import { useLabel, useProjectState, useUserPermissions } from "@/hooks/store";
import { useIssues } from "@/hooks/store/use-issues";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// types
export const ProjectAppliedFiltersRoot: React.FC = observer(() => {
@ -23,12 +24,14 @@ export const ProjectAppliedFiltersRoot: React.FC = observer(() => {
const {
issuesFilter: { issueFilters, updateFilters },
} = useIssues(EIssuesStoreType.PROJECT);
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { projectStates } = useProjectState();
// derived values
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const userFilters = issueFilters?.filters;
// filters whose value not null or empty array
const appliedFilters: IIssueFilterOptions = {};

View file

@ -15,9 +15,9 @@ import { UpdateViewComponent } from "@/components/views/update-view-component";
// constants
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
import { EViewAccess } from "@/constants/views";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useIssues, useLabel, useProjectState, useProjectView, useUser } from "@/hooks/store";
import { useIssues, useLabel, useProjectState, useProjectView, useUser, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { getAreFiltersEqual } from "../../../utils";
export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
@ -30,10 +30,8 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
const { projectLabels } = useLabel();
const { projectStates } = useProjectState();
const { viewMap, updateView } = useProjectView();
const {
data,
membership: { currentWorkspaceRole },
} = useUser();
const { data } = useUser();
const { allowPermissions } = useUserPermissions();
const [isModalOpen, setIsModalOpen] = useState(false);
// derived values
@ -108,7 +106,10 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
updateView(workspaceSlug.toString(), projectId.toString(), viewId.toString(), viewFilters);
};
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
const isAuthorizedUser = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const isLocked = !!viewDetails?.is_locked;
const isOwner = viewDetails?.owned_by === data?.id;

View file

@ -10,15 +10,15 @@ import { getMonthChartItemPositionWidthInMonth } from "@/components/gantt-chart/
import { QuickAddIssueRoot, IssueGanttBlock, GanttQuickAddIssueButton } from "@/components/issues";
//constants
import { EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { getIssueBlocksStructure } from "@/helpers/issue.helper";
//hooks
import { useIssues, useUser } from "@/hooks/store";
import { useIssues, useUserPermissions } from "@/hooks/store";
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
import { useIssuesActions } from "@/hooks/use-issues-actions";
// plane web hooks
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { useBulkOperationStatus } from "@/plane-web/hooks/use-bulk-operation-status";
import { IssueLayoutHOC } from "../issue-layout-HOC";
@ -43,9 +43,8 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
const { issues, issuesFilter, issueMap } = useIssues(storeType);
const { fetchIssues, fetchNextIssues, updateIssue, quickAddIssue } = useIssuesActions(storeType);
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const appliedDisplayFilters = issuesFilter.issueFilters?.displayFilters;
// plane web hooks
const isBulkOperationsEnabled = useBulkOperationStatus();
@ -90,7 +89,7 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
updateIssue && (await updateIssue(issue.project_id, issue.id, payload));
};
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isAllowed = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
const quickAdd =
enableIssueCreation && isAllowed && !isCompletedCycle ? (

View file

@ -11,12 +11,12 @@ import { DeleteIssueModal } from "@/components/issues";
//constants
import { ISSUE_DELETED } from "@/constants/event-tracker";
import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
//hooks
import { useEventTracker, useIssueDetail, useIssues, useKanbanView, useUser } from "@/hooks/store";
import { useEventTracker, useIssueDetail, useIssues, useKanbanView, useUserPermissions } from "@/hooks/store";
import { useGroupIssuesDragNDrop } from "@/hooks/use-group-dragndrop";
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
import { useIssuesActions } from "@/hooks/use-issues-actions";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// store
// ui
// types
@ -49,9 +49,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
const pathname = usePathname();
// store hooks
const storeType = useIssueStoreType() as KanbanStoreType;
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { captureIssueEvent } = useEventTracker();
const { issueMap, issuesFilter, issues } = useIssues(storeType);
const {
@ -115,7 +113,10 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
const [draggedIssueId, setDraggedIssueId] = useState<string | undefined>(undefined);
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const handleOnDrop = useGroupIssuesDragNDrop(storeType, orderBy, group_by, sub_group_by);

View file

@ -5,9 +5,9 @@ import { useParams } from "next/navigation";
import { CycleIssueQuickActions } from "@/components/issues";
// constants
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useCycle, useIssues, useUser } from "@/hooks/store";
import { useCycle, useIssues, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// components
import { BaseKanBanRoot } from "../base-kanban-root";
@ -19,13 +19,14 @@ export const CycleKanBanLayout: React.FC = observer(() => {
// store
const { issues } = useIssues(EIssuesStoreType.CYCLE);
const { currentProjectCompletedCycleIds } = useCycle();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const isCompletedCycle =
cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false;
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const canEditIssueProperties = useCallback(
() => !isCompletedCycle && isEditingAllowed,

View file

@ -2,8 +2,9 @@ import { observer } from "mobx-react";
// hooks
import { useParams } from "next/navigation";
import { ProjectIssueQuickActions } from "@/components/issues";
import { EUserProjectRoles } from "@/constants/project";
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// components
// types
// constants
@ -11,16 +12,16 @@ import { BaseKanBanRoot } from "../base-kanban-root";
export const ProfileIssuesKanBanLayout: React.FC = observer(() => {
// router
const { profileViewId } = useParams();
const {
membership: { currentWorkspaceAllProjectsRole },
} = useUser();
const { workspaceSlug, profileViewId } = useParams();
const { allowPermissions } = useUserPermissions();
const canEditPropertiesBasedOnProject = (projectId: string) => {
const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId];
return !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
};
const canEditPropertiesBasedOnProject = (projectId: string) =>
allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
projectId
);
return (
<BaseKanBanRoot

View file

@ -4,13 +4,13 @@ import { observer } from "mobx-react";
import { GroupByColumnTypes, TGroupedIssues } from "@plane/types";
// constants
import { EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useIssues, useUser } from "@/hooks/store";
import { useIssues, useUserPermissions } from "@/hooks/store";
// hooks
import { useGroupIssuesDragNDrop } from "@/hooks/use-group-dragndrop";
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
import { useIssuesActions } from "@/hooks/use-issues-actions";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// components
import { IssueLayoutHOC } from "../issue-layout-HOC";
import { List } from "./default";
@ -49,9 +49,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
restoreIssue,
} = useIssuesActions(storeType);
// mobx store
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { issueMap } = useIssues();
const displayFilters = issuesFilter?.issueFilters?.displayFilters;
@ -67,7 +65,10 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
const groupedIssueIds = issues?.groupedIssueIds as TGroupedIssues | undefined;
// auth
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issues?.viewFlags || {};
const canEditProperties = useCallback(

View file

@ -5,9 +5,9 @@ import { useParams } from "next/navigation";
import { CycleIssueQuickActions } from "@/components/issues";
// constants
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useCycle, useIssues, useUser } from "@/hooks/store";
import { useCycle, useIssues, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// types
import { BaseListRoot } from "../base-list-root";
@ -18,13 +18,14 @@ export const CycleListLayout: React.FC = observer(() => {
// store
const { issues } = useIssues(EIssuesStoreType.CYCLE);
const { currentProjectCompletedCycleIds } = useCycle(); // mobx store
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const isCompletedCycle =
cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false;
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const canEditIssueProperties = useCallback(
() => !isCompletedCycle && isEditingAllowed,

View file

@ -3,8 +3,9 @@ import { observer } from "mobx-react";
// hooks
import { useParams } from "next/navigation";
import { ProjectIssueQuickActions } from "@/components/issues";
import { EUserProjectRoles } from "@/constants/project";
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// components
// types
// constants
@ -12,17 +13,17 @@ import { BaseListRoot } from "../base-list-root";
export const ProfileIssuesListLayout: FC = observer(() => {
// router
const { profileViewId } = useParams();
const { workspaceSlug, profileViewId } = useParams();
// store
const {
membership: { currentWorkspaceAllProjectsRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const canEditPropertiesBasedOnProject = (projectId: string) => {
const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId];
return !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
};
const canEditPropertiesBasedOnProject = (projectId: string) =>
allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
projectId
);
return (
<BaseListRoot

View file

@ -11,12 +11,12 @@ import { ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from
import { DeleteIssueModal } from "@/components/issues";
// constants
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { cn } from "@/helpers/common.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useEventTracker, useIssues, useUser } from "@/hooks/store";
import { useEventTracker, useIssues, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// types
import { IQuickActionProps } from "../list/list-view-types";
@ -36,15 +36,15 @@ export const ArchivedIssueQuickActions: React.FC<IQuickActionProps> = observer((
// router
const { workspaceSlug } = useParams();
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { setTrackElement } = useEventTracker();
const { issuesFilter } = useIssues(EIssuesStoreType.ARCHIVED);
// derived values
const activeLayout = `${issuesFilter.issueFilters?.displayFilters?.layout} layout`;
// auth
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER && !readOnly;
const isEditingAllowed =
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT) && !readOnly;
const isRestoringAllowed = handleRestore && isEditingAllowed;
const issueLink = `${workspaceSlug}/projects/${issue.project_id}/archives/issues/${issue.id}`;

View file

@ -13,13 +13,13 @@ import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, set
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
// constants
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
import { ARCHIVABLE_STATE_GROUPS } from "@/constants/state";
// helpers
import { cn } from "@/helpers/common.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useEventTracker, useIssues, useProjectState, useUser } from "@/hooks/store";
import { useEventTracker, useIssues, useProjectState, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// types
import { IQuickActionProps } from "../list/list-view-types";
@ -46,14 +46,13 @@ export const CycleIssueQuickActions: React.FC<IQuickActionProps> = observer((pro
// store hooks
const { setTrackElement } = useEventTracker();
const { issuesFilter } = useIssues(EIssuesStoreType.CYCLE);
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { getStateById } = useProjectState();
// derived values
const stateDetails = getStateById(issue.state_id);
// auth
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER && !readOnly;
const isEditingAllowed =
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT) && !readOnly;
const isArchivingAllowed = handleArchive && isEditingAllowed;
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
const isDeletingAllowed = isEditingAllowed;

View file

@ -13,11 +13,11 @@ import { ContextMenu, CustomMenu, TContextMenuItem } from "@plane/ui";
import { CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
// constant
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useEventTracker, useIssues, useUser } from "@/hooks/store";
import { useEventTracker, useIssues, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// types
import { IQuickActionProps } from "../list/list-view-types";
@ -37,15 +37,14 @@ export const DraftIssueQuickActions: React.FC<IQuickActionProps> = observer((pro
const [issueToEdit, setIssueToEdit] = useState<TIssue | undefined>(undefined);
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { setTrackElement } = useEventTracker();
const { issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
// derived values
const activeLayout = `${issuesFilter.issueFilters?.displayFilters?.layout} layout`;
// auth
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER && !readOnly;
const isEditingAllowed =
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT) && !readOnly;
const isDeletingAllowed = isEditingAllowed;
const duplicateIssuePayload = omit(

View file

@ -13,13 +13,13 @@ import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, set
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
// constants
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
import { ARCHIVABLE_STATE_GROUPS } from "@/constants/state";
// helpers
import { cn } from "@/helpers/common.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useIssues, useEventTracker, useUser, useProjectState } from "@/hooks/store";
import { useIssues, useEventTracker, useProjectState, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// types
import { IQuickActionProps } from "../list/list-view-types";
@ -46,14 +46,13 @@ export const ModuleIssueQuickActions: React.FC<IQuickActionProps> = observer((pr
// store hooks
const { setTrackElement } = useEventTracker();
const { issuesFilter } = useIssues(EIssuesStoreType.MODULE);
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { getStateById } = useProjectState();
// derived values
const stateDetails = getStateById(issue.state_id);
// auth
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER && !readOnly;
const isEditingAllowed =
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT) && !readOnly;
const isArchivingAllowed = handleArchive && isEditingAllowed;
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
const isDeletingAllowed = isEditingAllowed;

View file

@ -13,13 +13,13 @@ import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, set
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
// constants
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
import { ARCHIVABLE_STATE_GROUPS } from "@/constants/state";
// helpers
import { cn } from "@/helpers/common.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useEventTracker, useIssues, useProjectState, useUser } from "@/hooks/store";
import { useEventTracker, useIssues, useProjectState, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// types
import { IQuickActionProps } from "../list/list-view-types";
@ -44,9 +44,7 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = observer((p
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
const [archiveIssueModal, setArchiveIssueModal] = useState(false);
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { setTrackElement } = useEventTracker();
const { issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
const { getStateById } = useProjectState();
@ -54,7 +52,8 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = observer((p
const activeLayout = `${issuesFilter.issueFilters?.displayFilters?.layout} layout`;
const stateDetails = getStateById(issue.state_id);
// auth
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER && !readOnly;
const isEditingAllowed =
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT) && !readOnly;
const isArchivingAllowed = handleArchive && isEditingAllowed;
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
const isDeletingAllowed = isEditingAllowed;

View file

@ -19,13 +19,13 @@ import {
EIssuesStoreType,
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
} from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useGlobalView, useIssues, useUser } from "@/hooks/store";
import { useGlobalView, useIssues, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { IssuesStoreContext } from "@/hooks/use-issue-layout-store";
import { useIssuesActions } from "@/hooks/use-issues-actions";
import { useWorkspaceIssueProperties } from "@/hooks/use-workspace-issue-properties";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// store
import emptyView from "@/public/empty-state/view.svg";
import { IssuePeekOverview } from "../../peek-overview";
@ -57,9 +57,8 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props: Props) => {
} = useIssues(EIssuesStoreType.GLOBAL);
const { updateIssue, removeIssue, archiveIssue } = useIssuesActions(EIssuesStoreType.GLOBAL);
const {
membership: { currentWorkspaceAllProjectsRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { fetchAllGlobalViews, getViewDetailsById } = useGlobalView();
const viewDetails = getViewDetailsById(globalViewId?.toString());
@ -132,12 +131,14 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props: Props) => {
const canEditProperties = useCallback(
(projectId: string | undefined) => {
if (!projectId) return false;
const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId];
return !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
return allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
projectId
);
},
[currentWorkspaceAllProjectsRole]
[workspaceSlug]
);
const issueFilters = globalViewId ? filters?.[globalViewId.toString()] : undefined;

View file

@ -6,11 +6,11 @@ import { ALL_ISSUES } from "@plane/constants";
import { IIssueDisplayFilterOptions } from "@plane/types";
// hooks
import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useIssues, useUser } from "@/hooks/store";
import { useIssues, useUserPermissions } from "@/hooks/store";
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
import { useIssuesActions } from "@/hooks/use-issues-actions";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// views
// stores
// components
@ -38,9 +38,7 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
const { projectId } = useParams();
// store hooks
const storeType = useIssueStoreType() as SpreadsheetStoreType;
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { issues, issuesFilter } = useIssues(storeType);
const {
fetchIssues,
@ -56,7 +54,10 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
// derived values
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issues?.viewFlags || {};
// user role validation
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
useEffect(() => {
fetchIssues("init-loader", { canGroup: false, perPageCount: 100 }, viewId);

View file

@ -1,10 +1,9 @@
import React, { useCallback } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// constants
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useCycle, useUser } from "@/hooks/store";
import { useCycle, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// components
import { CycleIssueQuickActions } from "../../quick-action-dropdowns";
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
@ -14,13 +13,14 @@ export const CycleSpreadsheetLayout: React.FC = observer(() => {
const { cycleId } = useParams();
// store hooks
const { currentProjectCompletedCycleIds } = useCycle();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
// auth
const isCompletedCycle =
cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false;
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const canEditIssueProperties = useCallback(
() => !isCompletedCycle && isEditingAllowed,

View file

@ -10,10 +10,10 @@ import { IssueView, TIssueOperations } from "@/components/issues";
// constants
import { ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED, ISSUE_RESTORED } from "@/constants/event-tracker";
import { EIssuesStoreType } from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useEventTracker, useIssueDetail, useIssues, useUser } from "@/hooks/store";
import { useEventTracker, useIssueDetail, useIssues, useUserPermissions } from "@/hooks/store";
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
interface IIssuePeekOverview {
embedIssue?: boolean;
@ -26,9 +26,9 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
const { embedIssue = false, embedRemoveCurrentNotification, is_archived = false, is_draft = false } = props;
// router
const pathname = usePathname();
const {
membership: { currentWorkspaceAllProjectsRole },
} = useUser();
// store hook
const { allowPermissions } = useUserPermissions();
const {
issues: { restoreIssue },
} = useIssues(EIssuesStoreType.ARCHIVED);
@ -335,9 +335,13 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
if (!peekIssue?.workspaceSlug || !peekIssue?.projectId || !peekIssue?.issueId) return <></>;
const currentProjectRole = currentWorkspaceAllProjectsRole?.[peekIssue?.projectId];
// Check if issue is editable, based on user role
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditable = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT,
peekIssue?.workspaceSlug,
peekIssue?.projectId
);
return (
<IssueView