[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

@ -14,7 +14,7 @@ import { IssuePeekOverview } from "@/components/issues";
import { EmptyStateType } from "@/constants/empty-state";
import { ENotificationLoader, ENotificationQueryParamType } from "@/constants/notification";
// hooks
import { useIssueDetail, useUser, useWorkspace, useWorkspaceNotifications } from "@/hooks/store";
import { useIssueDetail, useUserPermissions, useWorkspace, useWorkspaceNotifications } from "@/hooks/store";
import { useWorkspaceIssueProperties } from "@/hooks/use-workspace-issue-properties";
const WorkspaceDashboardPage = observer(() => {
@ -28,9 +28,7 @@ const WorkspaceDashboardPage = observer(() => {
notificationIdsByWorkspaceId,
getNotifications,
} = useWorkspaceNotifications();
const {
membership: { fetchUserProjectInfo },
} = useUser();
const { fetchUserProjectInfo } = useUserPermissions();
const { setPeekIssue } = useIssueDetail();
// derived values
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Inbox` : undefined;

View file

@ -2,16 +2,15 @@
import { useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// ui
import { Button } from "@plane/ui";
// components
import { PageHead } from "@/components/core";
import { DownloadActivityButton, WorkspaceActivityListPage } from "@/components/profile";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
// plane-web constants
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const PER_PAGE = 100;
@ -21,13 +20,7 @@ const ProfileActivityPage = observer(() => {
const [totalPages, setTotalPages] = useState(0);
const [resultsCount, setResultsCount] = useState(0);
// router
const { userId } = useParams();
// store hooks
const { data: currentUser } = useUser();
const {
membership: { currentWorkspaceRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const updateTotalPages = (count: number) => setTotalPages(count);
@ -47,8 +40,10 @@ const ProfileActivityPage = observer(() => {
/>
);
const canDownloadActivity =
currentUser?.id === userId && !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
const canDownloadActivity = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
return (
<>

View file

@ -12,9 +12,9 @@ import { BreadcrumbLink } from "@/components/common";
// components
import { ProfileIssuesFilter } from "@/components/profile";
import { PROFILE_ADMINS_TAB, PROFILE_VIEWER_TAB } from "@/constants/profile";
import { EUserWorkspaceRoles } from "@/constants/workspace";
import { cn } from "@/helpers/common.helper";
import { useAppTheme, useUser } from "@/hooks/store";
import { useAppTheme, useUser, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type TUserProfileHeader = {
userProjectsData: IUserProfileProjectSegregation | undefined;
@ -28,14 +28,15 @@ export const UserProfileHeader: FC<TUserProfileHeader> = observer((props) => {
const { workspaceSlug, userId } = useParams();
// store hooks
const { toggleProfileSidebar, profileSidebarCollapsed } = useAppTheme();
const {
membership: { currentWorkspaceRole },
data: currentUser,
} = useUser();
const { data: currentUser } = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
// derived values
const isAuthorized = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.VIEWER;
const isAuthorized = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
if (!currentWorkspaceRole) return null;
if (!workspaceUserInfo) return null;
const tabsList = isAuthorized ? [...PROFILE_VIEWER_TAB, ...PROFILE_ADMINS_TAB] : PROFILE_VIEWER_TAB;

View file

@ -9,10 +9,10 @@ import { ProfileSidebar } from "@/components/profile";
// constants
import { USER_PROFILE_PROJECT_SEGREGATION } from "@/constants/fetch-keys";
import { PROFILE_ADMINS_TAB, PROFILE_VIEWER_TAB } from "@/constants/profile";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
import useSize from "@/hooks/use-window-size";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// local components
import { UserService } from "@/services/user.service";
import { UserProfileHeader } from "./header";
@ -25,17 +25,18 @@ type Props = {
children: React.ReactNode;
};
const AUTHORIZED_ROLES = [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.VIEWER];
const UseProfileLayout: React.FC<Props> = observer((props) => {
const { children } = props;
// router
const { workspaceSlug, userId } = useParams();
const pathname = usePathname();
// store hooks
const {
membership: { currentWorkspaceRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
// derived values
const isAuthorized = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const windowSize = useSize();
const isSmallerScreen = windowSize[0] >= 768;
@ -47,7 +48,6 @@ const UseProfileLayout: React.FC<Props> = observer((props) => {
: null
);
// derived values
const isAuthorized = currentWorkspaceRole && AUTHORIZED_ROLES.includes(currentWorkspaceRole);
const isAuthorizedPath =
pathname.includes("assigned") || pathname.includes("created") || pathname.includes("subscribed");
const isIssuesTab = pathname.includes("assigned") || pathname.includes("created") || pathname.includes("subscribed");

View file

@ -21,7 +21,6 @@ import {
EIssuesStoreType,
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
} from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { cn } from "@/helpers/common.helper";
import { isIssueFilterActive } from "@/helpers/filter.helper";
@ -34,13 +33,14 @@ import {
useMember,
useProject,
useProjectState,
useUser,
useIssues,
useCommandPalette,
useUserPermissions,
} from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import useLocalStorage from "@/hooks/use-local-storage";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const CycleDropdownOption: React.FC<{ cycleId: string }> = ({ cycleId }) => {
// router
@ -81,9 +81,6 @@ export const CycleIssuesHeader: React.FC = observer(() => {
const { currentProjectCycleIds, getCycleById } = useCycle();
const { toggleCreateIssueModal } = useCommandPalette();
const { setTrackElement } = useEventTracker();
const {
membership: { currentProjectRole },
} = useUser();
const { currentProjectDetails, loader } = useProject();
const { projectStates } = useProjectState();
const { projectLabels } = useLabel();
@ -91,6 +88,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
project: { projectMemberIds },
} = useMember();
const { isMobile } = usePlatformOS();
const { allowPermissions } = useUserPermissions();
const activeLayout = issueFilters?.displayFilters?.layout;
@ -149,8 +147,10 @@ export const CycleIssuesHeader: React.FC = observer(() => {
// derived values
const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined;
const isCompletedCycle = cycleDetails?.status?.toLocaleLowerCase() === "completed";
const canUserCreateIssue =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
const canUserCreateIssue = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const issuesCount = getGroupIssueCount(undefined, undefined, false);

View file

@ -8,11 +8,11 @@ import { Breadcrumbs, Button, ContrastIcon, Header } from "@plane/ui";
// components
import { BreadcrumbLink, Logo } from "@/components/common";
import { CyclesViewHeader } from "@/components/cycles";
// constants
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
import { useCommandPalette, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// constants
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const CyclesListHeader: FC = observer(() => {
// router
@ -21,13 +21,13 @@ export const CyclesListHeader: FC = observer(() => {
// store hooks
const { toggleCreateCycleModal } = useCommandPalette();
const { setTrackElement } = useEventTracker();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { currentProjectDetails, loader } = useProject();
const canUserCreateCycle =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
const canUserCreateCycle = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
return (
<Header>

View file

@ -9,10 +9,9 @@ import { Breadcrumbs, Button, Intake, Header } from "@plane/ui";
// components
import { BreadcrumbLink, Logo } from "@/components/common";
import { InboxIssueCreateEditModalRoot } from "@/components/inbox";
// constants
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useProject, useProjectInbox, useUser } from "@/hooks/store";
import { useProject, useProjectInbox, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const ProjectInboxHeader: FC = observer(() => {
// states
@ -20,14 +19,16 @@ export const ProjectInboxHeader: FC = observer(() => {
// router
const { workspaceSlug, projectId } = useParams();
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { currentProjectDetails, loader: currentProjectDetailsLoader } = useProject();
const { loader } = useProjectInbox();
// derived value
const isViewer = currentProjectRole === EUserProjectRoles.VIEWER;
const isAuthorized = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
EUserPermissionsLevel.PROJECT
);
return (
<Header>
@ -66,7 +67,7 @@ export const ProjectInboxHeader: FC = observer(() => {
</div>
</Header.LeftItem>
<Header.RightItem>
{currentProjectDetails?.inbox_view && workspaceSlug && projectId && !isViewer ? (
{currentProjectDetails?.inbox_view && workspaceSlug && projectId && isAuthorized ? (
<div className="flex items-center gap-2">
<InboxIssueCreateEditModalRoot
workspaceSlug={workspaceSlug.toString()}

View file

@ -12,22 +12,19 @@ import { BreadcrumbLink, CountChip, Logo } from "@/components/common";
import HeaderFilters from "@/components/issues/filters";
import { EIssuesStoreType } from "@/constants/issue";
// helpers
import { EUserProjectRoles } from "@/constants/project";
import { SPACE_BASE_PATH, SPACE_BASE_URL } from "@/helpers/common.helper";
// hooks
import { useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
import { useEventTracker, useProject, useCommandPalette, useUserPermissions } from "@/hooks/store";
import { useIssues } from "@/hooks/store/use-issues";
import { useAppRouter } from "@/hooks/use-app-router";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const ProjectIssuesHeader = observer(() => {
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = useParams() as { workspaceSlug: string; projectId: string };
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const {
issues: { getGroupIssueCount },
} = useIssues(EIssuesStoreType.PROJECT);
@ -36,14 +33,17 @@ export const ProjectIssuesHeader = observer(() => {
const { toggleCreateIssueModal } = useCommandPalette();
const { setTrackElement } = useEventTracker();
const { allowPermissions } = useUserPermissions();
const { isMobile } = usePlatformOS();
const SPACE_APP_URL = (SPACE_BASE_URL.trim() === "" ? window.location.origin : SPACE_BASE_URL) + SPACE_BASE_PATH;
const publishedURL = `${SPACE_APP_URL}/issues/${currentProjectDetails?.anchor}`;
const issuesCount = getGroupIssueCount(undefined, undefined, false);
const canUserCreateIssue =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
const canUserCreateIssue = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
return (
<Header>

View file

@ -21,7 +21,6 @@ import {
EIssueLayoutTypes,
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
} from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { cn } from "@/helpers/common.helper";
import { isIssueFilterActive } from "@/helpers/filter.helper";
@ -34,14 +33,15 @@ import {
useModule,
useProject,
useProjectState,
useUser,
useIssues,
useCommandPalette,
useUserPermissions,
} from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { useIssuesActions } from "@/hooks/use-issues-actions";
import useLocalStorage from "@/hooks/use-local-storage";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const ModuleDropdownOption: React.FC<{ moduleId: string }> = ({ moduleId }) => {
// router
@ -83,9 +83,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
const { projectModuleIds, getModuleById } = useModule();
const { toggleCreateIssueModal } = useCommandPalette();
const { setTrackElement } = useEventTracker();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { currentProjectDetails, loader } = useProject();
const { projectLabels } = useLabel();
const { projectStates } = useProjectState();
@ -149,8 +147,10 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
// derived values
const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined;
const canUserCreateIssue =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
const canUserCreateIssue = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const issuesCount = getGroupIssueCount(undefined, undefined, false);

View file

@ -7,11 +7,11 @@ import { Breadcrumbs, Button, DiceIcon, Header } from "@plane/ui";
// components
import { BreadcrumbLink, Logo } from "@/components/common";
import { ModuleViewHeader } from "@/components/modules";
// constants
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
import { useCommandPalette, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// constants
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const ModulesListHeader: React.FC = observer(() => {
// router
@ -20,14 +20,15 @@ export const ModulesListHeader: React.FC = observer(() => {
// store hooks
const { toggleCreateModuleModal } = useCommandPalette();
const { setTrackElement } = useEventTracker();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { currentProjectDetails, loader } = useProject();
// auth
const canUserCreateModule =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
const canUserCreateModule = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
return (
<Header>

View file

@ -9,9 +9,9 @@ import { Breadcrumbs, Button, Header } from "@plane/ui";
import { BreadcrumbLink, Logo } from "@/components/common";
// constants
import { EPageAccess } from "@/constants/page";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
import { useCommandPalette, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const PagesListHeader = observer(() => {
// router
@ -20,14 +20,15 @@ export const PagesListHeader = observer(() => {
const pageType = searchParams.get("type");
// store hooks
const { toggleCreatePageModal } = useCommandPalette();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { currentProjectDetails, loader } = useProject();
const { setTrackElement } = useEventTracker();
const canUserCreatePage =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
const canUserCreatePage = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
return (
<Header>

View file

@ -11,18 +11,19 @@ import { NotAuthorizedView } from "@/components/auth-screens";
import { AutoArchiveAutomation, AutoCloseAutomation } from "@/components/automation";
import { PageHead } from "@/components/core";
// hooks
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const AutomationSettingsPage = observer(() => {
// router
const { workspaceSlug, projectId } = useParams();
// store hooks
const {
canPerformProjectAdminActions,
membership: { currentProjectRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { currentProjectDetails: projectDetails, updateProject } = useProject();
// derived values
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
const handleChange = async (formData: Partial<IProject>) => {
if (!workspaceSlug || !projectId || !projectDetails) return;
@ -38,7 +39,7 @@ const AutomationSettingsPage = observer(() => {
// derived values
const pageTitle = projectDetails?.name ? `${projectDetails?.name} - Automations` : undefined;
if (currentProjectRole && !canPerformProjectAdminActions) {
if (workspaceUserInfo && !canPerformProjectAdminActions) {
return <NotAuthorizedView section="settings" isProjectView />;
}

View file

@ -7,22 +7,22 @@ import { NotAuthorizedView } from "@/components/auth-screens";
import { PageHead } from "@/components/core";
import { EstimateRoot } from "@/components/estimates";
// hooks
import { useUser, useProject } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const EstimatesSettingsPage = observer(() => {
const { workspaceSlug, projectId } = useParams();
const {
canPerformProjectAdminActions,
membership: { currentProjectRole },
} = useUser();
// store
const { currentProjectDetails } = useProject();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
// derived values
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Estimates` : undefined;
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
if (!workspaceSlug || !projectId) return <></>;
if (currentProjectRole && !canPerformProjectAdminActions) {
if (workspaceUserInfo && !canPerformProjectAdminActions) {
return <NotAuthorizedView section="settings" isProjectView />;
}

View file

@ -7,22 +7,22 @@ import { NotAuthorizedView } from "@/components/auth-screens";
import { PageHead } from "@/components/core";
import { ProjectFeaturesList } from "@/components/project";
// hooks
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const FeaturesSettingsPage = observer(() => {
const { workspaceSlug, projectId } = useParams();
// store
const {
canPerformProjectAdminActions,
membership: { currentProjectRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { currentProjectDetails } = useProject();
// derived values
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Features` : undefined;
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
if (!workspaceSlug || !projectId) return null;
if (currentProjectRole && !canPerformProjectAdminActions) {
if (workspaceUserInfo && !canPerformProjectAdminActions) {
return <NotAuthorizedView section="settings" isProjectView />;
}

View file

@ -9,25 +9,21 @@ import { Breadcrumbs, CustomMenu, Header } from "@plane/ui";
// components
import { BreadcrumbLink, Logo } from "@/components/common";
// constants
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// plane web constants
import { PROJECT_SETTINGS_LINKS } from "@/plane-web/constants/project";
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const ProjectSettingHeader: FC = observer(() => {
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = useParams();
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { currentProjectDetails, loader } = useProject();
const projectMemberInfo = currentProjectRole || EUserProjectRoles.GUEST;
return (
<Header>
<Header.LeftItem>
@ -74,7 +70,12 @@ export const ProjectSettingHeader: FC = observer(() => {
>
{PROJECT_SETTINGS_LINKS.map(
(item) =>
projectMemberInfo >= item.access && (
allowPermissions(
item.access,
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
projectId.toString()
) && (
<CustomMenu.MenuItem
key={item.key}
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}${item.href}`)}

View file

@ -9,19 +9,24 @@ import { NotAuthorizedView } from "@/components/auth-screens";
import { PageHead } from "@/components/core";
import { ProjectSettingsLabelList } from "@/components/labels";
// hooks
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const LabelsSettingsPage = observer(() => {
// store hooks
const { currentProjectDetails } = useProject();
const {
canPerformProjectMemberActions,
membership: { currentProjectRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Labels` : undefined;
const scrollableContainerRef = useRef<HTMLDivElement | null>(null);
// derived values
const canPerformProjectMemberActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
// Enable Auto Scroll for Labels list
useEffect(() => {
const element = scrollableContainerRef.current;
@ -35,7 +40,7 @@ const LabelsSettingsPage = observer(() => {
);
}, [scrollableContainerRef?.current]);
if (currentProjectRole && !canPerformProjectMemberActions) {
if (workspaceUserInfo && !canPerformProjectMemberActions) {
return <NotAuthorizedView section="settings" isProjectView />;
}

View file

@ -6,19 +6,21 @@ import { NotAuthorizedView } from "@/components/auth-screens";
import { PageHead } from "@/components/core";
import { ProjectMemberList, ProjectSettingsMemberDefaults } from "@/components/project";
// hooks
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const MembersSettingsPage = observer(() => {
// store
const { currentProjectDetails } = useProject();
const {
canPerformProjectViewerActions,
membership: { currentProjectRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
// derived values
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Members` : undefined;
const canPerformProjectMemberActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
if (currentProjectRole && !canPerformProjectViewerActions) {
if (workspaceUserInfo && !canPerformProjectMemberActions) {
return <NotAuthorizedView section="settings" isProjectView />;
}

View file

@ -15,7 +15,8 @@ import {
ProjectDetailsFormLoader,
} from "@/components/project";
// hooks
import { useProject } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const GeneralSettingsPage = observer(() => {
// states
@ -25,6 +26,8 @@ const GeneralSettingsPage = observer(() => {
const { workspaceSlug, projectId } = useParams();
// store hooks
const { currentProjectDetails, fetchProjectDetails } = useProject();
const { allowPermissions } = useUserPermissions();
// api call to fetch project details
// TODO: removed this API if not necessary
const { isLoading } = useSWR(
@ -32,7 +35,13 @@ const GeneralSettingsPage = observer(() => {
workspaceSlug && projectId ? () => fetchProjectDetails(workspaceSlug.toString(), projectId.toString()) : null
);
// derived values
const isAdmin = currentProjectDetails?.member_role === 20;
const isAdmin = allowPermissions(
[EUserPermissions.ADMIN],
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
projectId.toString()
);
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - General Settings` : undefined;
// const currentNetwork = NETWORK_CHOICES.find((n) => n.key === projectDetails?.network);
// const selectedNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network"));
@ -69,7 +78,7 @@ const GeneralSettingsPage = observer(() => {
<ProjectDetailsFormLoader />
)}
{isAdmin && (
{isAdmin && currentProjectDetails && (
<>
<ArchiveProjectSelection
projectDetails={currentProjectDetails}

View file

@ -8,22 +8,20 @@ import { useParams, usePathname } from "next/navigation";
import { Loader } from "@plane/ui";
// components
import { SidebarNavItem } from "@/components/sidebar";
// constants
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
// plane web constants
import { PROJECT_SETTINGS_LINKS } from "@/plane-web/constants/project";
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const ProjectSettingsSidebar = observer(() => {
const { workspaceSlug, projectId } = useParams();
const pathname = usePathname();
// mobx store
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions, projectUserInfo } = useUserPermissions();
const projectMemberInfo = currentProjectRole || EUserProjectRoles.GUEST;
// derived values
const currentProjectRole = projectUserInfo?.[workspaceSlug?.toString()]?.[projectId?.toString()]?.role;
if (!currentProjectRole) {
return (
@ -47,7 +45,12 @@ export const ProjectSettingsSidebar = observer(() => {
<div className="flex w-full flex-col gap-1">
{PROJECT_SETTINGS_LINKS.map(
(link) =>
projectMemberInfo >= link.access && (
allowPermissions(
link.access,
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
projectId.toString()
) && (
<Link key={link.key} href={`/${workspaceSlug}/projects/${projectId}${link.href}`}>
<SidebarNavItem
key={link.key}

View file

@ -7,20 +7,24 @@ import { NotAuthorizedView } from "@/components/auth-screens";
import { PageHead } from "@/components/core";
import { ProjectStateRoot } from "@/components/project-states";
// hook
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const StatesSettingsPage = observer(() => {
const { workspaceSlug, projectId } = useParams();
// store
const { currentProjectDetails } = useProject();
const {
canPerformProjectMemberActions,
membership: { currentProjectRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
// derived values
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - States` : undefined;
// derived values
const canPerformProjectMemberActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
if (currentProjectRole && !canPerformProjectMemberActions) {
if (workspaceUserInfo && !canPerformProjectMemberActions) {
return <NotAuthorizedView section="settings" isProjectView />;
}

View file

@ -19,7 +19,6 @@ import {
EIssueLayoutTypes,
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
} from "@/constants/issue";
import { EUserProjectRoles } from "@/constants/project";
import { EViewAccess } from "@/constants/views";
// helpers
import { isIssueFilterActive } from "@/helpers/filter.helper";
@ -35,8 +34,9 @@ import {
useProject,
useProjectState,
useProjectView,
useUser,
useUserPermissions,
} from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const ProjectViewIssuesHeader: React.FC = observer(() => {
// router
@ -47,9 +47,8 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
} = useIssues(EIssuesStoreType.PROJECT_VIEW);
const { setTrackElement } = useEventTracker();
const { toggleCreateIssueModal } = useCommandPalette();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { currentProjectDetails, loader } = useProject();
const { projectViewIds, getViewById } = useProjectView();
const { projectStates } = useProjectState();
@ -131,8 +130,10 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
const viewDetails = viewId ? getViewById(viewId.toString()) : null;
const canUserCreateIssue =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
const canUserCreateIssue = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const publishLink = getPublishViewLink(viewDetails?.anchor);
return (

View file

@ -16,7 +16,8 @@ import { APITokenSettingsLoader } from "@/components/ui";
import { EmptyStateType } from "@/constants/empty-state";
import { API_TOKENS_LIST } from "@/constants/fetch-keys";
// store hooks
import { useUser, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// services
import { APITokenService } from "@/services/api_token.service";
@ -28,11 +29,10 @@ const ApiTokensPage = observer(() => {
// router
const { workspaceSlug } = useParams();
// store hooks
const {
canPerformWorkspaceAdminActions,
membership: { currentWorkspaceRole },
} = useUser();
const { currentWorkspace } = useWorkspace();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
// derived values
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const { data: tokens } = useSWR(
workspaceSlug && canPerformWorkspaceAdminActions ? API_TOKENS_LIST(workspaceSlug.toString()) : null,
@ -42,7 +42,7 @@ const ApiTokensPage = observer(() => {
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - API Tokens` : undefined;
if (currentWorkspaceRole && !canPerformWorkspaceAdminActions) {
if (workspaceUserInfo && !canPerformWorkspaceAdminActions) {
return <NotAuthorizedView section="settings" />;
}

View file

@ -5,21 +5,20 @@ import { observer } from "mobx-react";
import { NotAuthorizedView } from "@/components/auth-screens";
import { PageHead } from "@/components/core";
// hooks
import { useUser, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWorkspace } from "@/hooks/store";
// plane web components
import { BillingRoot } from "@/plane-web/components/workspace";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const BillingSettingsPage = observer(() => {
// store hooks
const {
canPerformWorkspaceAdminActions,
membership: { currentWorkspaceRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { currentWorkspace } = useWorkspace();
// derived values
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Billing & Plans` : undefined;
if (currentWorkspaceRole && !canPerformWorkspaceAdminActions) {
if (workspaceUserInfo && !canPerformWorkspaceAdminActions) {
return <NotAuthorizedView section="settings" />;
}

View file

@ -8,22 +8,23 @@ import ExportGuide from "@/components/exporter/guide";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useUser, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const ExportsPage = observer(() => {
// store hooks
const {
canPerformWorkspaceViewerActions,
canPerformWorkspaceMemberActions,
membership: { currentWorkspaceRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { currentWorkspace } = useWorkspace();
// derived values
const canPerformWorkspaceMemberActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Exports` : undefined;
// if user is not authorized to view this page
if (currentWorkspaceRole && !canPerformWorkspaceViewerActions) {
if (workspaceUserInfo && !canPerformWorkspaceMemberActions) {
return <NotAuthorizedView section="settings" />;
}

View file

@ -4,20 +4,17 @@ import { observer } from "mobx-react";
// components
import { PageHead } from "@/components/core";
import IntegrationGuide from "@/components/integration/guide";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const ImportsPage = observer(() => {
// store hooks
const {
membership: { currentWorkspaceRole },
} = useUser();
const { currentWorkspace } = useWorkspace();
const { allowPermissions } = useUserPermissions();
// derived values
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Imports` : undefined;
if (!isAdmin)

View file

@ -8,9 +8,9 @@ import { SingleIntegrationCard } from "@/components/integration";
import { IntegrationAndImportExportBanner, IntegrationsSettingsLoader } from "@/components/ui";
// constants
import { APP_INTEGRATIONS } from "@/constants/fetch-keys";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// services
import { IntegrationService } from "@/services/integrations";
@ -20,13 +20,11 @@ const WorkspaceIntegrationsPage = observer(() => {
// router
const { workspaceSlug } = useParams();
// store hooks
const {
membership: { currentWorkspaceRole },
} = useUser();
const { currentWorkspace } = useWorkspace();
const { allowPermissions } = useUserPermissions();
// derived values
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Integrations` : undefined;
if (!isAdmin)

View file

@ -18,7 +18,8 @@ import { MEMBER_INVITED } from "@/constants/event-tracker";
import { cn } from "@/helpers/common.helper";
import { getUserRole } from "@/helpers/user.helper";
// hooks
import { useEventTracker, useMember, useUser, useWorkspace } from "@/hooks/store";
import { useEventTracker, useMember, useUserPermissions, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const WorkspaceMembersSettingsPage = observer(() => {
// states
@ -27,18 +28,20 @@ const WorkspaceMembersSettingsPage = observer(() => {
// router
const { workspaceSlug } = useParams();
// store hooks
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { captureEvent } = useEventTracker();
const {
canPerformWorkspaceAdminActions,
canPerformWorkspaceViewerActions,
canPerformWorkspaceMemberActions,
membership: { currentWorkspaceRole },
} = useUser();
const {
workspace: { inviteMembersToWorkspace },
} = useMember();
const { currentWorkspace } = useWorkspace();
// derived values
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const canPerformWorkspaceMemberActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const handleWorkspaceInvite = (data: IWorkspaceBulkInviteFormData) => {
if (!workspaceSlug) return;
@ -49,7 +52,7 @@ const WorkspaceMembersSettingsPage = observer(() => {
emails: [
...data.emails.map((email) => ({
email: email.email,
role: getUserRole(email.role),
role: getUserRole(email.role as unknown as EUserPermissions),
})),
],
project_id: undefined,
@ -67,7 +70,7 @@ const WorkspaceMembersSettingsPage = observer(() => {
emails: [
...data.emails.map((email) => ({
email: email.email,
role: getUserRole(email.role),
role: getUserRole(email.role as unknown as EUserPermissions),
})),
],
project_id: undefined,
@ -86,7 +89,7 @@ const WorkspaceMembersSettingsPage = observer(() => {
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Members` : undefined;
// if user is not authorized to view this page
if (currentWorkspaceRole && !canPerformWorkspaceViewerActions) {
if (workspaceUserInfo && !canPerformWorkspaceMemberActions) {
return <NotAuthorizedView section="settings" />;
}

View file

@ -1,11 +1,10 @@
import { observer } from "mobx-react";
import { useParams, usePathname } from "next/navigation";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// plane web constants
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
// plane web helpers
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
@ -15,19 +14,14 @@ export const MobileWorkspaceSettingsTabs = observer(() => {
const { workspaceSlug } = useParams();
const pathname = usePathname();
// mobx store
const {
membership: { currentWorkspaceRole },
} = useUser();
// derived values
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
const { allowPermissions } = useUserPermissions();
return (
<div className="flex-shrink-0 md:hidden sticky inset-0 flex overflow-x-auto bg-custom-background-100 z-10">
{WORKSPACE_SETTINGS_LINKS.map(
(item, index) =>
shouldRenderSettingLink(item.key) &&
workspaceMemberInfo >= item.access && (
allowPermissions(item.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
<div
className={`${
item.highlight(pathname, `/${workspaceSlug}`)

View file

@ -6,11 +6,10 @@ import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
// components
import { SidebarNavItem } from "@/components/sidebar";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
// plane web constants
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
// plane web helpers
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
@ -20,12 +19,7 @@ export const WorkspaceSettingsSidebar = observer(() => {
const { workspaceSlug } = useParams();
const pathname = usePathname();
// mobx store
const {
membership: { currentWorkspaceRole },
} = useUser();
// derived values
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
const { allowPermissions } = useUserPermissions();
return (
<div className="flex w-[280px] flex-col gap-6">
@ -35,7 +29,7 @@ export const WorkspaceSettingsSidebar = observer(() => {
{WORKSPACE_SETTINGS_LINKS.map(
(link) =>
shouldRenderSettingLink(link.key) &&
workspaceMemberInfo >= link.access && (
allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
<Link key={link.key} href={`/${workspaceSlug}${link.href}`}>
<SidebarNavItem
key={link.key}

View file

@ -12,7 +12,8 @@ import { LogoSpinner } from "@/components/common";
import { PageHead } from "@/components/core";
import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "@/components/web-hooks";
// hooks
import { useUser, useWebhook, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWebhook, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const WebhookDetailsPage = observer(() => {
// states
@ -20,18 +21,17 @@ const WebhookDetailsPage = observer(() => {
// router
const { workspaceSlug, webhookId } = useParams();
// mobx store
const {
membership: { currentWorkspaceRole },
} = useUser();
const { currentWebhook, fetchWebhookById, updateWebhook } = useWebhook();
const { currentWorkspace } = useWorkspace();
const { allowPermissions } = useUserPermissions();
// TODO: fix this error
// useEffect(() => {
// if (isCreated !== "true") clearSecretKey();
// }, [clearSecretKey, isCreated]);
const isAdmin = currentWorkspaceRole === 20;
// derived values
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Webhook` : undefined;
useSWR(

View file

@ -15,7 +15,8 @@ import { WebhooksList, CreateWebhookModal } from "@/components/web-hooks";
// constants
import { EmptyStateType } from "@/constants/empty-state";
// hooks
import { useUser, useWebhook, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWebhook, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const WebhooksListPage = observer(() => {
// states
@ -23,13 +24,13 @@ const WebhooksListPage = observer(() => {
// router
const { workspaceSlug } = useParams();
// mobx store
const {
canPerformWorkspaceAdminActions,
membership: { currentWorkspaceRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { fetchWebhooks, webhooks, clearSecretKey, webhookSecretKey, createWebhook } = useWebhook();
const { currentWorkspace } = useWorkspace();
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
useSWR(
workspaceSlug && canPerformWorkspaceAdminActions ? `WEBHOOKS_LIST_${workspaceSlug}` : null,
workspaceSlug && canPerformWorkspaceAdminActions ? () => fetchWebhooks(workspaceSlug.toString()) : null
@ -42,7 +43,7 @@ const WebhooksListPage = observer(() => {
if (!showCreateWebhookModal && webhookSecretKey) clearSecretKey();
}, [showCreateWebhookModal, webhookSecretKey, clearSecretKey]);
if (currentWorkspaceRole && !canPerformWorkspaceAdminActions) {
if (workspaceUserInfo && !canPerformWorkspaceAdminActions) {
return <NotAuthorizedView section="settings" />;
}

View file

@ -13,22 +13,29 @@ import {
import { SidebarFavoritesMenu } from "@/components/workspace/sidebar/favorites/favorites-menu";
import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme, useUser } from "@/hooks/store";
import { useAppTheme, useUserPermissions } from "@/hooks/store";
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
// plane web components
import useSize from "@/hooks/use-window-size";
import { SidebarAppSwitcher } from "@/plane-web/components/sidebar";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export interface IAppSidebar {}
export const AppSidebar: FC<IAppSidebar> = observer(() => {
// store hooks
const { canPerformWorkspaceMemberActions } = useUser();
const { allowPermissions } = useUserPermissions();
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
const windowSize = useSize();
// refs
const ref = useRef<HTMLDivElement>(null);
// derived values
const canPerformWorkspaceMemberActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
useOutsideClickDetector(ref, () => {
if (sidebarCollapsed === false) {
if (window.innerWidth < 768) {

View file

@ -27,6 +27,9 @@ import { useEventTracker, useUser, useUserProfile, useWorkspace } from "@/hooks/
import { useAppRouter } from "@/hooks/use-app-router";
// services
import { AuthenticationWrapper } from "@/lib/wrappers";
// plane web constants
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
// plane web services
import { WorkspaceService } from "@/plane-web/services";
// images
import emptyInvitation from "@/public/empty-state/invitation.svg";
@ -88,7 +91,7 @@ const UserInvitationsPage = observer(() => {
captureEvent(MEMBER_ACCEPTED, {
member_id: invitation?.id,
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
role: getUserRole(invitation?.role!),
role: getUserRole((invitation?.role as unknown as EUserPermissions)!),
project_id: undefined,
accepted_from: "App",
state: "SUCCESS",

View file

@ -1,14 +1,13 @@
import { useState } from "react";
import { useParams } from "next/navigation";
import { IWorkspaceMember } from "@plane/types";
import { EUserProjectRoles } from "@plane/types/src/enums";
import { AccountTypeColumn, NameColumn } from "@/components/project/settings/member-columns";
import { EUserWorkspaceRoles } from "@/constants/workspace";
import { useUser } from "@/hooks/store";
import { useUser, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export interface RowData {
member: IWorkspaceMember;
role: EUserWorkspaceRoles;
role: EUserPermissions;
}
export const useProjectColumns = () => {
@ -17,10 +16,12 @@ export const useProjectColumns = () => {
const { workspaceSlug, projectId } = useParams();
const {
membership: { currentProjectRole },
data: currentUser,
} = useUser();
const { data: currentUser } = useUser();
const { allowPermissions, projectUserInfo } = useUserPermissions();
const currentProjectRole =
(projectUserInfo?.[workspaceSlug.toString()]?.[projectId.toString()]?.role as unknown as EUserPermissions) ??
EUserPermissions.GUEST;
const getFormattedDate = (dateStr: string) => {
const date = new Date(dateStr);
@ -29,7 +30,13 @@ export const useProjectColumns = () => {
return date.toLocaleDateString("en-US", options);
};
// derived values
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
const isAdmin = allowPermissions(
[EUserPermissions.ADMIN],
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
projectId.toString()
);
const columns = [
{
key: "Full Name",

View file

@ -1,8 +1,8 @@
import { useState } from "react";
import { useParams } from "next/navigation";
import { AccountTypeColumn, NameColumn, RowData } from "@/components/workspace/settings/member-columns";
import { EUserWorkspaceRoles } from "@/constants/workspace";
import { useUser } from "@/hooks/store";
import { useUser, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const useMemberColumns = () => {
// states
@ -10,10 +10,8 @@ export const useMemberColumns = () => {
const { workspaceSlug } = useParams();
const {
membership: { currentWorkspaceRole },
data: currentUser,
} = useUser();
const { data: currentUser } = useUser();
const { allowPermissions } = useUserPermissions();
const getFormattedDate = (dateStr: string) => {
const date = new Date(dateStr);
@ -22,7 +20,8 @@ export const useMemberColumns = () => {
return date.toLocaleDateString("en-US", options);
};
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
// derived values
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const columns = [
{
@ -48,13 +47,7 @@ export const useMemberColumns = () => {
{
key: "Account Type",
content: "Account Type",
tdRender: (rowData: RowData) => (
<AccountTypeColumn
rowData={rowData}
currentWorkspaceRole={currentWorkspaceRole}
workspaceSlug={workspaceSlug as string}
/>
),
tdRender: (rowData: RowData) => <AccountTypeColumn rowData={rowData} workspaceSlug={workspaceSlug as string} />,
},
{

View file

@ -3,14 +3,14 @@ import { SettingIcon } from "@/components/icons/attachment";
// types
import { Props } from "@/components/icons/types";
// constants
import { EUserProjectRoles } from "@/constants/project";
import { EUserPermissions } from "../../user-permissions";
export const PROJECT_SETTINGS = {
general: {
key: "general",
label: "General",
href: `/settings`,
access: EUserProjectRoles.GUEST,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
Icon: SettingIcon,
},
@ -18,7 +18,7 @@ export const PROJECT_SETTINGS = {
key: "members",
label: "Members",
href: `/settings/members`,
access: EUserProjectRoles.VIEWER,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
Icon: SettingIcon,
},
@ -26,7 +26,7 @@ export const PROJECT_SETTINGS = {
key: "features",
label: "Features",
href: `/settings/features`,
access: EUserProjectRoles.ADMIN,
access: [EUserPermissions.ADMIN],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/features/`,
Icon: SettingIcon,
},
@ -34,7 +34,7 @@ export const PROJECT_SETTINGS = {
key: "states",
label: "States",
href: `/settings/states`,
access: EUserProjectRoles.MEMBER,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/states/`,
Icon: SettingIcon,
},
@ -42,7 +42,7 @@ export const PROJECT_SETTINGS = {
key: "labels",
label: "Labels",
href: `/settings/labels`,
access: EUserProjectRoles.MEMBER,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/labels/`,
Icon: SettingIcon,
},
@ -50,7 +50,7 @@ export const PROJECT_SETTINGS = {
key: "estimates",
label: "Estimates",
href: `/settings/estimates`,
access: EUserProjectRoles.ADMIN,
access: [EUserPermissions.ADMIN],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/estimates/`,
Icon: SettingIcon,
},
@ -58,7 +58,7 @@ export const PROJECT_SETTINGS = {
key: "automations",
label: "Automations",
href: `/settings/automations`,
access: EUserProjectRoles.ADMIN,
access: [EUserPermissions.ADMIN],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/automations/`,
Icon: SettingIcon,
},
@ -68,7 +68,7 @@ export const PROJECT_SETTINGS_LINKS: {
key: string;
label: string;
href: string;
access: EUserProjectRoles;
access: EUserPermissions[];
highlight: (pathname: string, baseUrl: string) => boolean;
Icon: React.FC<Props>;
}[] = [

View file

@ -0,0 +1,36 @@
export enum EUserPermissionsLevel {
WORKSPACE = "WORKSPACE",
PROJECT = "PROJECT",
}
export type TUserPermissionsLevel = EUserPermissionsLevel;
export enum EUserPermissions {
ADMIN = 20,
MEMBER = 15,
GUEST = 5,
}
export type TUserPermissions = EUserPermissions;
export type TUserAllowedPermissionsObject = {
create: TUserPermissions[];
update: TUserPermissions[];
delete: TUserPermissions[];
read: TUserPermissions[];
};
export type TUserAllowedPermissions = {
workspace: {
[key: string]: Partial<TUserAllowedPermissionsObject>;
};
project: {
[key: string]: Partial<TUserAllowedPermissionsObject>;
};
};
export const USER_ALLOWED_PERMISSIONS: TUserAllowedPermissions = {
workspace: {
dashboard: {
read: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
},
},
project: {},
};

View file

@ -1,15 +1,15 @@
// icons
import { SettingIcon } from "@/components/icons/attachment";
import { Props } from "@/components/icons/types";
import { EUserPermissions } from "./user-permissions";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
export const WORKSPACE_SETTINGS = {
general: {
key: "general",
label: "General",
href: `/settings`,
access: EUserWorkspaceRoles.GUEST,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
Icon: SettingIcon,
},
@ -17,7 +17,7 @@ export const WORKSPACE_SETTINGS = {
key: "members",
label: "Members",
href: `/settings/members`,
access: EUserWorkspaceRoles.VIEWER,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
Icon: SettingIcon,
},
@ -25,7 +25,7 @@ export const WORKSPACE_SETTINGS = {
key: "billing-and-plans",
label: "Billing and plans",
href: `/settings/billing`,
access: EUserWorkspaceRoles.ADMIN,
access: [EUserPermissions.ADMIN],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/billing/`,
Icon: SettingIcon,
},
@ -33,7 +33,7 @@ export const WORKSPACE_SETTINGS = {
key: "export",
label: "Exports",
href: `/settings/exports`,
access: EUserWorkspaceRoles.VIEWER,
access: [EUserPermissions.ADMIN],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/exports/`,
Icon: SettingIcon,
},
@ -41,7 +41,7 @@ export const WORKSPACE_SETTINGS = {
key: "webhooks",
label: "Webhooks",
href: `/settings/webhooks`,
access: EUserWorkspaceRoles.ADMIN,
access: [EUserPermissions.ADMIN],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/webhooks/`,
Icon: SettingIcon,
},
@ -49,7 +49,7 @@ export const WORKSPACE_SETTINGS = {
key: "api-tokens",
label: "API tokens",
href: `/settings/api-tokens`,
access: EUserWorkspaceRoles.ADMIN,
access: [EUserPermissions.ADMIN],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/api-tokens/`,
Icon: SettingIcon,
},
@ -59,7 +59,7 @@ export const WORKSPACE_SETTINGS_LINKS: {
key: string;
label: string;
href: string;
access: EUserWorkspaceRoles;
access: EUserPermissions[];
highlight: (pathname: string, baseUrl: string) => boolean;
Icon: React.FC<Props>;
}[] = [

View file

@ -5,7 +5,7 @@ import { useParams } from "next/navigation";
// hooks
import { ClipboardList } from "lucide-react";
import { Button } from "@plane/ui";
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
// ui
// icons
// images
@ -15,9 +15,7 @@ export const JoinProject: React.FC = () => {
// states
const [isJoiningProject, setIsJoiningProject] = useState(false);
// store hooks
const {
membership: { joinProject },
} = useUser();
const { joinProject } = useUserPermissions();
const { fetchProjects } = useProject();
const { workspaceSlug, projectId } = useParams();
@ -27,7 +25,7 @@ export const JoinProject: React.FC = () => {
setIsJoiningProject(true);
joinProject(workspaceSlug.toString(), [projectId.toString()])
joinProject(workspaceSlug.toString(), projectId.toString())
.then(() => fetchProjects(workspaceSlug.toString()))
.finally(() => setIsJoiningProject(false));
};

View file

@ -10,9 +10,10 @@ import { CustomSelect, Loader, ToggleSwitch } from "@plane/ui";
// component
import { SelectMonthModal } from "@/components/automation";
// constants
import { EUserProjectRoles, PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
import { PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
// hooks
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
handleChange: (formData: Partial<IProject>) => Promise<void>;
@ -25,12 +26,16 @@ export const AutoArchiveAutomation: React.FC<Props> = observer((props) => {
// states
const [monthModal, setmonthModal] = useState(false);
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { currentProjectDetails } = useProject();
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
const isAdmin = allowPermissions(
[EUserPermissions.ADMIN],
EUserPermissionsLevel.PROJECT,
currentProjectDetails?.workspace_detail.slug,
currentProjectDetails?.id
);
return (
<>

View file

@ -11,9 +11,10 @@ import { CustomSelect, CustomSearchSelect, ToggleSwitch, StateGroupIcon, DoubleC
// component
import { SelectMonthModal } from "@/components/automation";
// constants
import { EUserProjectRoles, PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
import { PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
// hooks
import { useProject, useProjectState, useUser } from "@/hooks/store";
import { useProject, useProjectState, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
handleChange: (formData: Partial<IProject>) => Promise<void>;
@ -24,11 +25,9 @@ export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
// states
const [monthModal, setmonthModal] = useState(false);
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { currentProjectDetails } = useProject();
const { projectStates } = useProjectState();
const { allowPermissions } = useUserPermissions();
// const stateGroups = projectStateStore.groupedProjectStates ?? undefined;
@ -57,7 +56,12 @@ export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
default_state: defaultState,
};
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
const isAdmin = allowPermissions(
[EUserPermissions.ADMIN],
EUserPermissionsLevel.PROJECT,
currentProjectDetails?.workspace_detail?.slug,
currentProjectDetails?.id
);
return (
<>

View file

@ -4,12 +4,12 @@ import { Command } from "cmdk";
// hooks
import Link from "next/link";
import { useParams } from "next/navigation";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// plane wev constants
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
// plane web helpers
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
@ -25,11 +25,8 @@ export const CommandPaletteWorkspaceSettingsActions: React.FC<Props> = (props) =
// router params
const { workspaceSlug } = useParams();
// mobx store
const {
membership: { currentWorkspaceRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
// derived values
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
const redirect = (path: string) => {
closePalette();
@ -40,7 +37,7 @@ export const CommandPaletteWorkspaceSettingsActions: React.FC<Props> = (props) =
<>
{WORKSPACE_SETTINGS_LINKS.map(
(setting) =>
workspaceMemberInfo >= setting.access &&
allowPermissions(setting.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) &&
shouldRenderSettingLink(setting.key) && (
<Command.Item
key={setting.key}

View file

@ -20,10 +20,11 @@ import { ISSUE_DETAILS } from "@/constants/fetch-keys";
// helpers
import { copyTextToClipboard } from "@/helpers/string.helper";
// hooks
import { useEventTracker, useUser, useAppTheme, useCommandPalette } from "@/hooks/store";
import { useEventTracker, useUser, useAppTheme, useCommandPalette, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// services
import { IssueService } from "@/services/issue";
@ -43,10 +44,10 @@ export const CommandPalette: FC = observer(() => {
const { platform } = usePlatformOS();
const {
data: currentUser,
canPerformProjectMemberActions,
canPerformWorkspaceMemberActions,
// canPerformProjectMemberActions,
// canPerformWorkspaceMemberActions,
canPerformAnyCreateAction,
canPerformProjectAdminActions,
// canPerformProjectAdminActions,
} = useUser();
const {
issues: { removeIssue },
@ -73,6 +74,7 @@ export const CommandPalette: FC = observer(() => {
toggleDeleteIssueModal,
isAnyModalOpen,
} = useCommandPalette();
const { allowPermissions } = useUserPermissions();
const { data: issueDetails } = useSWR(
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
@ -81,6 +83,17 @@ export const CommandPalette: FC = observer(() => {
: null
);
// derived values
const canPerformWorkspaceMemberActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const canPerformProjectMemberActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
const copyIssueUrlToClipboard = useCallback(() => {
if (!issueId) return;

View file

@ -17,16 +17,16 @@ import { DateRangeDropdown } from "@/components/dropdowns";
// constants
import { CYCLE_STATUS } from "@/constants/cycle";
import { CYCLE_UPDATED } from "@/constants/event-tracker";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// helpers
import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useEventTracker, useCycle, useUser, useMember, useProjectEstimates } from "@/hooks/store";
import { useEventTracker, useCycle, useMember, useProjectEstimates, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// plane web constants
import { EEstimateSystem } from "@/plane-web/constants/estimates";
// services
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { CycleService } from "@/services/cycle.service";
type Props = {
@ -55,9 +55,8 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
// store hooks
const { setTrackElement, captureCycleEvent } = useEventTracker();
const { areEstimateEnabledByProjectId, currentActiveEstimateId, estimateById } = useProjectEstimates();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { getCycleById, updateCycleDetails, restoreCycle } = useCycle();
const { getUserDetails } = useMember();
// derived values
@ -236,7 +235,10 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date);
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
return (
<div className="relative">

View file

@ -4,9 +4,10 @@ import { TCycleFilters } from "@plane/types";
// hooks
import { Tag } from "@plane/ui";
import { AppliedDateFilters, AppliedStatusFilters } from "@/components/cycles";
import { EUserProjectRoles } from "@/constants/project";
import { replaceUnderscoreIfSnakeCase } from "@/helpers/string.helper";
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// components
// helpers
// types
@ -24,15 +25,15 @@ const DATE_FILTERS = ["start_date", "end_date"];
export const CycleAppliedFiltersList: React.FC<Props> = observer((props) => {
const { appliedFilters, handleClearAllFilters, handleRemoveFilter, alwaysAllowEditing } = props;
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
if (!appliedFilters) return null;
if (Object.keys(appliedFilters).length === 0) return null;
const isEditingAllowed = alwaysAllowEditing || (currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER);
const isEditingAllowed =
alwaysAllowEditing ||
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
return (
<div className="flex flex-wrap items-stretch gap-2 bg-custom-background-100">

View file

@ -14,14 +14,14 @@ import { CycleQuickActions } from "@/components/cycles";
// constants
import { CYCLE_STATUS } from "@/constants/cycle";
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED } from "@/constants/event-tracker";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// helpers
import { findHowManyDaysLeft, getDate, renderFormattedDate } from "@/helpers/date-time.helper";
import { generateQueryParams } from "@/helpers/router.helper";
// hooks
import { useEventTracker, useCycle, useUser, useMember } from "@/hooks/store";
import { useEventTracker, useCycle, useMember, 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";
export interface ICyclesBoardCard {
workspaceSlug: string;
@ -39,9 +39,8 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = observer((props) => {
const pathname = usePathname();
// store
const { captureEvent } = useEventTracker();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { addCycleToFavorites, removeCycleFromFavorites, getCycleById } = useCycle();
const { getUserDetails } = useMember();
// computed
@ -57,7 +56,10 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = observer((props) => {
const startDate = getDate(cycleDetails.start_date);
const isDateValid = cycleDetails.start_date || cycleDetails.end_date;
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus);

View file

@ -15,13 +15,14 @@ import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
// constants
import { CYCLE_STATUS } from "@/constants/cycle";
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED } from "@/constants/event-tracker";
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
// hooks
import { useCycle, useEventTracker, useMember, useUser } from "@/hooks/store";
import { useCycle, useEventTracker, useMember, useUserPermissions } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { CycleService } from "@/services/cycle.service";
const cycleService = new CycleService();
type Props = {
@ -44,9 +45,8 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
// store hooks
const { addCycleToFavorites, removeCycleFromFavorites, updateCycleDetails } = useCycle();
const { captureEvent } = useEventTracker();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { getUserDetails } = useMember();
// form
@ -56,7 +56,10 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
// derived values
const cycleStatus = cycleDetails.status ? (cycleDetails.status.toLocaleLowerCase() as TCycleGroups) : "draft";
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const renderIcon = Boolean(cycleDetails.start_date) || Boolean(cycleDetails.end_date);
const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus);
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date) ?? 0;

View file

@ -9,14 +9,13 @@ import { ArchiveRestoreIcon, ExternalLink, LinkIcon, Pencil, Trash2 } from "luci
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
// components
import { ArchiveCycleModal, CycleCreateUpdateModal, CycleDeleteModal } from "@/components/cycles";
// constants
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { cn } from "@/helpers/common.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useCycle, useEventTracker, useUser } from "@/hooks/store";
import { useCycle, useEventTracker, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
parentRef: React.RefObject<HTMLElement>;
@ -35,17 +34,19 @@ export const CycleQuickActions: React.FC<Props> = observer((props) => {
const [deleteModal, setDeleteModal] = useState(false);
// store hooks
const { setTrackElement } = useEventTracker();
const {
membership: { currentWorkspaceAllProjectsRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { getCycleById, restoreCycle } = useCycle();
// derived values
const cycleDetails = getCycleById(cycleId);
const isArchived = !!cycleDetails?.archived_at;
const isCompleted = cycleDetails?.status?.toLowerCase() === "completed";
// auth
const isEditingAllowed =
!!currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId] >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT,
workspaceSlug,
projectId
);
const cycleLink = `${workspaceSlug}/projects/${projectId}/cycles/${cycleId}`;
const handleCopyText = () =>

View file

@ -4,10 +4,9 @@ import { observer } from "mobx-react";
import Image from "next/image";
// ui
import { Button } from "@plane/ui";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// 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 ProjectEmptyStateImage from "@/public/empty-state/onboarding/dashboard-light.webp";
@ -15,11 +14,10 @@ export const DashboardProjectEmptyState = observer(() => {
// store hooks
const { toggleCreateProjectModal } = useCommandPalette();
const { setTrackElement } = useEventTracker();
const {
membership: { currentWorkspaceRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
// derived values
const canCreateProject = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
const canCreateProject = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
return (
<div className="mx-auto flex h-full flex-col justify-center space-y-4 lg:w-3/5">

View file

@ -14,9 +14,9 @@ import { Logo } from "@/components/common";
import { WidgetLoader, WidgetProps } from "@/components/dashboard/widgets";
// constants
import { PROJECT_BACKGROUND_COLORS } from "@/constants/dashboard";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useEventTracker, useDashboard, useProject, useUser, useCommandPalette } from "@/hooks/store";
import { useEventTracker, useDashboard, useProject, useCommandPalette, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const WIDGET_KEY = "recent_projects";
@ -65,13 +65,14 @@ export const RecentProjectsWidget: React.FC<WidgetProps> = observer((props) => {
// store hooks
const { toggleCreateProjectModal } = useCommandPalette();
const { setTrackElement } = useEventTracker();
const {
membership: { currentWorkspaceRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { fetchWidgetStats, getWidgetStats } = useDashboard();
// derived values
const widgetStats = getWidgetStats<TRecentProjectsWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
const canCreateProject = currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
const canCreateProject = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
useEffect(() => {
fetchWidgetStats(workspaceSlug, dashboardId, {

View file

@ -13,7 +13,8 @@ import { Button, TButtonVariant } from "@plane/ui";
import { EMPTY_STATE_DETAILS, EmptyStateType } from "@/constants/empty-state";
// helpers
import { cn } from "@/helpers/common.helper";
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { ComicBoxButton } from "./comic-box-button";
export type EmptyStateProps = {
@ -37,9 +38,7 @@ export const EmptyState: React.FC<EmptyStateProps> = observer((props) => {
secondaryButtonOnClick,
} = props;
// store
const {
membership: { currentWorkspaceRole, currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
// theme
const { resolvedTheme } = useTheme();
@ -53,10 +52,14 @@ export const EmptyState: React.FC<EmptyStateProps> = observer((props) => {
const resolvedEmptyStatePath = `${additionalPath && additionalPath !== "" ? `${path}${additionalPath}` : path}-${
resolvedTheme === "light" ? "light" : "dark"
}.webp`;
// current access type
const currentAccessType = accessType === "workspace" ? currentWorkspaceRole : currentProjectRole;
// permission
const isEditingAllowed = currentAccessType && access && currentAccessType >= access;
const isEditingAllowed =
access &&
accessType &&
allowPermissions(
access,
accessType === "workspace" ? EUserPermissionsLevel.WORKSPACE : EUserPermissionsLevel.PROJECT
);
const anyButton = primaryButton || secondaryButton;
// primary button

View file

@ -17,10 +17,11 @@ import { ImportExportSettingsLoader } from "@/components/ui";
// constants
import { EmptyStateType } from "@/constants/empty-state";
import { EXPORT_SERVICES_LIST } from "@/constants/fetch-keys";
import { EUserWorkspaceRoles, EXPORTERS_LIST } from "@/constants/workspace";
import { EXPORTERS_LIST } from "@/constants/workspace";
// hooks
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUser, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// services
import { IntegrationService } from "@/services/integrations";
@ -37,11 +38,9 @@ const IntegrationGuide = observer(() => {
const searchParams = useSearchParams();
const provider = searchParams.get("provider");
// store hooks
const {
data: currentUser,
canPerformAnyCreateAction,
membership: { currentWorkspaceRole },
} = useUser();
const { data: currentUser, canPerformAnyCreateAction } = useUser();
const { allowPermissions } = useUserPermissions();
const { workspaceProjectIds } = useProject();
const { data: exporterServices } = useSWR(
@ -61,7 +60,7 @@ const IntegrationGuide = observer(() => {
};
const hasProjects = workspaceProjectIds && workspaceProjectIds.length > 0;
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
useEffect(() => {
const interval = setInterval(() => {

View file

@ -27,15 +27,14 @@ import {
SelectDuplicateInboxIssueModal,
} from "@/components/inbox";
import { IssueUpdateStatus } from "@/components/issues";
// constants
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { findHowManyDaysLeft } from "@/helpers/date-time.helper";
import { EInboxIssueStatus } from "@/helpers/inbox.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useUser, useProjectInbox, useProject } from "@/hooks/store";
import { useUser, useProjectInbox, useProject, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// store types
import type { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
@ -70,23 +69,26 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
// store
const { currentTab, deleteInboxIssue, filteredInboxIssueIds } = useProjectInbox();
const { data: currentUser } = useUser();
const {
membership: { currentProjectRoleByProjectId },
} = useUser();
const { allowPermissions } = useUserPermissions();
const router = useAppRouter();
const { getProjectById } = useProject();
const issue = inboxIssue?.issue;
// derived values
const currentProjectRole = currentProjectRoleByProjectId(projectId) || undefined;
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT,
workspaceSlug,
projectId
);
const canMarkAsDuplicate = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
const canMarkAsAccepted = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
const canMarkAsDeclined = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
// can delete only if admin or is creator of the issue
const canDelete =
(!!currentProjectRole && currentProjectRole >= EUserProjectRoles.ADMIN) || issue?.created_by === currentUser?.id;
allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT, workspaceSlug, projectId) ||
issue?.created_by === currentUser?.id;
const isAcceptedOrDeclined = inboxIssue?.status ? [-1, 1, 2].includes(inboxIssue.status) : undefined;
// days left for snooze
const numberOfDaysLeft = findHowManyDaysLeft(inboxIssue?.snoozed_till);

View file

@ -4,11 +4,10 @@ import useSWR from "swr";
// components
import { ContentWrapper } from "@plane/ui";
import { InboxIssueActionsHeader, InboxIssueMainContent } from "@/components/inbox";
// constants
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useProjectInbox, useUser } from "@/hooks/store";
import { useProjectInbox, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type TInboxContentRoot = {
workspaceSlug: string;
@ -37,9 +36,8 @@ export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
// hooks
const { currentTab, fetchInboxIssueById, getIssueInboxByIssueId, getIsIssueAvailable } = useProjectInbox();
const inboxIssue = getIssueInboxByIssueId(inboxIssueId);
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
// derived values
const isIssueAvailable = getIsIssueAvailable(inboxIssueId?.toString() || "");
@ -63,7 +61,7 @@ export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
}
);
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditable = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
if (!inboxIssue) return <></>;

View file

@ -12,9 +12,10 @@ import { Button, Loader, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
// constants
import { WORKSPACE_INTEGRATIONS } from "@/constants/fetch-keys";
// hooks
import { useUser, useInstance } from "@/hooks/store";
import { useInstance, useUserPermissions } from "@/hooks/store";
import useIntegrationPopup from "@/hooks/use-integration-popup";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// services
// icons
import GithubLogo from "@/public/services/github.png";
@ -48,11 +49,9 @@ export const SingleIntegrationCard: React.FC<Props> = observer(({ integration })
const { workspaceSlug } = useParams();
// store hooks
const { config } = useInstance();
const {
membership: { currentWorkspaceRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const isUserAdmin = currentWorkspaceRole === 20;
const isUserAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const { isMobile } = usePlatformOS();
const { startAuth, isConnecting: isInstalling } = useIntegrationPopup({
provider: integration.provider,

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

View file

@ -47,15 +47,15 @@ import {
MODULE_UPDATED,
} from "@/constants/event-tracker";
import { MODULE_STATUS } from "@/constants/module";
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useModule, useUser, useEventTracker, useProjectEstimates } from "@/hooks/store";
import { useModule, useEventTracker, useProjectEstimates, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// plane web constants
import { EEstimateSystem } from "@/plane-web/constants/estimates";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const defaultValues: Partial<IModule> = {
lead_id: "",
@ -84,9 +84,9 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId } = useParams();
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { getModuleById, updateModuleDetails, createModuleLink, updateModuleLink, deleteModuleLink, restoreModule } =
useModule();
const { setTrackElement, captureModuleEvent, captureEvent } = useEventTracker();
@ -264,7 +264,10 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
? "0 Issue"
: `${moduleDetails.completed_estimate_points}/${moduleDetails.total_estimate_points}`;
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
return (
<div className="relative">
@ -550,7 +553,7 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
<Transition show={open}>
<Disclosure.Panel>
<div className="mt-2 flex min-h-72 w-full flex-col space-y-3 overflow-y-auto">
{currentProjectRole && moduleDetails.link_module && moduleDetails.link_module.length > 0 ? (
{isEditingAllowed && moduleDetails.link_module && moduleDetails.link_module.length > 0 ? (
<>
{isEditingAllowed && !isArchived && (
<div className="flex w-full items-center justify-end">
@ -569,13 +572,7 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
moduleId={moduleId}
handleEditLink={handleEditLink}
handleDeleteLink={handleDeleteLink}
userAuth={{
isGuest: currentProjectRole === EUserProjectRoles.GUEST,
isViewer: currentProjectRole === EUserProjectRoles.VIEWER,
isMember: currentProjectRole === EUserProjectRoles.MEMBER,
isOwner: currentProjectRole === EUserProjectRoles.ADMIN,
}}
disabled={isArchived}
disabled={!isEditingAllowed || isArchived}
/>
)}
</>

View file

@ -2,7 +2,7 @@
import { useCallback } from "react";
import { observer } from "mobx-react";
// plane types
import { ILinkDetails, UserAuth } from "@plane/types";
import { ILinkDetails } from "@plane/types";
// components
import { ModulesLinksListItem } from "@/components/modules";
// hooks
@ -13,11 +13,10 @@ type Props = {
handleDeleteLink: (linkId: string) => void;
handleEditLink: (link: ILinkDetails) => void;
moduleId: string;
userAuth: UserAuth;
};
export const ModuleLinksList: React.FC<Props> = observer((props) => {
const { moduleId, handleDeleteLink, handleEditLink, userAuth, disabled } = props;
const { moduleId, handleDeleteLink, handleEditLink, disabled } = props;
// store hooks
const { getModuleById } = useModule();
// derived values
@ -36,7 +35,7 @@ export const ModuleLinksList: React.FC<Props> = observer((props) => {
key={link.id}
handleDeleteLink={() => memoizedDeleteLink(link.id)}
handleEditLink={() => memoizedEditLink(link)}
isEditingAllowed={(userAuth.isMember || userAuth.isOwner) && !disabled}
isEditingAllowed={!disabled}
link={link}
/>
))}

View file

@ -17,16 +17,16 @@ import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdo
import { PROGRESS_STATE_GROUPS_DETAILS } from "@/constants/common";
import { MODULE_FAVORITED, MODULE_UNFAVORITED } from "@/constants/event-tracker";
import { MODULE_STATUS } from "@/constants/module";
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { generateQueryParams } from "@/helpers/router.helper";
// hooks
import { useEventTracker, useMember, useModule, useProjectEstimates, useUser } from "@/hooks/store";
import { useEventTracker, useMember, useModule, useProjectEstimates, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web constants
import { EEstimateSystem } from "@/plane-web/constants/estimates";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
moduleId: string;
@ -42,9 +42,7 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
const searchParams = useSearchParams();
const pathname = usePathname();
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { getModuleById, addModuleToFavorites, removeModuleFromFavorites, updateModuleDetails } = useModule();
const { getUserDetails } = useMember();
const { captureEvent } = useEventTracker();
@ -52,7 +50,10 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
// derived values
const moduleDetails = getModuleById(moduleId);
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const isDisabled = !isEditingAllowed || !!moduleDetails?.archived_at;
const renderIcon = Boolean(moduleDetails?.start_date) || Boolean(moduleDetails?.target_date);

View file

@ -12,14 +12,14 @@ import { FavoriteStar, TOAST_TYPE, Tooltip, setPromiseToast, setToast } from "@p
// components
import { DateRangeDropdown } from "@/components/dropdowns";
import { ModuleQuickActions } from "@/components/modules";
import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown";
import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown";
// constants
import { MODULE_FAVORITED, MODULE_UNFAVORITED } from "@/constants/event-tracker";
import { MODULE_STATUS } from "@/constants/module";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { renderFormattedPayloadDate, getDate } from "@/helpers/date-time.helper";
import { useEventTracker, useMember, useModule, useUser } from "@/hooks/store";
import { useEventTracker, useMember, useModule, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { ButtonAvatars } from "../dropdowns/member/avatar";
type Props = {
@ -33,9 +33,7 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
// router
const { workspaceSlug, projectId } = useParams();
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { addModuleToFavorites, removeModuleFromFavorites, updateModuleDetails } = useModule();
const { getUserDetails } = useMember();
const { captureEvent } = useEventTracker();
@ -43,7 +41,10 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
// derived values
const moduleStatus = MODULE_STATUS.find((status) => status.value === moduleDetails.status);
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const isDisabled = !isEditingAllowed || !!moduleDetails?.archived_at;
const renderIcon = Boolean(moduleDetails.start_date) || Boolean(moduleDetails.target_date);
@ -140,9 +141,9 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
}}
onSelect={(val) => {
handleModuleDetailsChange({
start_date: (val?.from ? renderFormattedPayloadDate(val.from) : null),
target_date: (val?.to ? renderFormattedPayloadDate(val.to) : null)
})
start_date: val?.from ? renderFormattedPayloadDate(val.from) : null,
target_date: val?.to ? renderFormattedPayloadDate(val.to) : null,
});
}}
placeholder={{
from: "Start date",
@ -154,9 +155,9 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
{moduleStatus && (
<ModuleStatusDropdown
isDisabled={isDisabled}
moduleDetails={moduleDetails}
handleModuleDetailsChange={handleModuleDetailsChange}
isDisabled={isDisabled}
moduleDetails={moduleDetails}
handleModuleDetailsChange={handleModuleDetailsChange}
/>
)}
@ -191,4 +192,4 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
)}
</>
);
});
});

View file

@ -9,14 +9,13 @@ import { ArchiveRestoreIcon, ExternalLink, LinkIcon, Pencil, Trash2 } from "luci
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
// components
import { ArchiveModuleModal, CreateUpdateModuleModal, DeleteModuleModal } from "@/components/modules";
// constants
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { cn } from "@/helpers/common.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useModule, useEventTracker, useUser } from "@/hooks/store";
import { useModule, useEventTracker, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
parentRef: React.RefObject<HTMLDivElement>;
@ -35,16 +34,19 @@ export const ModuleQuickActions: React.FC<Props> = observer((props) => {
const [deleteModal, setDeleteModal] = useState(false);
// store hooks
const { setTrackElement } = useEventTracker();
const {
membership: { currentWorkspaceAllProjectsRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { getModuleById, restoreModule } = useModule();
// derived values
const moduleDetails = getModuleById(moduleId);
const isArchived = !!moduleDetails?.archived_at;
// auth
const isEditingAllowed =
!!currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId] >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT,
workspaceSlug,
projectId
);
const moduleState = moduleDetails?.status?.toLocaleLowerCase();
const isInArchivableGroup = !!moduleState && ["completed", "cancelled"].includes(moduleState);

View file

@ -23,12 +23,14 @@ import { IUser, IWorkspace } from "@plane/types";
import { Button, Input, Spinner, TOAST_TYPE, setToast } from "@plane/ui";
// constants
import { MEMBER_INVITED } from "@/constants/event-tracker";
import { EUserWorkspaceRoles, ROLE, ROLE_DETAILS } from "@/constants/workspace";
import { ROLE, ROLE_DETAILS } from "@/constants/workspace";
// helpers
import { getUserRole } from "@/helpers/user.helper";
// hooks
import { useEventTracker } from "@/hooks/store";
import useDynamicDropdownPosition from "@/hooks/use-dynamic-dropdown";
// plane web constants
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
// services
import { WorkspaceService } from "@/plane-web/services";
// assets
@ -47,7 +49,7 @@ type Props = {
type EmailRole = {
email: string;
role: EUserWorkspaceRoles;
role: EUserPermissions;
role_active: boolean;
};

View file

@ -24,7 +24,6 @@ import {
import { Logo } from "@/components/common";
import { ArchiveRestoreProjectModal, DeleteProjectModal, JoinProjectModal } from "@/components/project";
// constants
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { cn } from "@/helpers/common.helper";
import { renderFormattedDate } from "@/helpers/date-time.helper";
@ -33,6 +32,8 @@ import { copyUrlToClipboard } from "@/helpers/string.helper";
import { useProject } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { usePlatformOS } from "@/hooks/use-platform-os";
// plane-web constants
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
type Props = {
project: IProject;
@ -57,8 +58,8 @@ export const ProjectCard: React.FC<Props> = observer((props) => {
// derived values
const projectMembersIds = project.members?.map((member) => member.member_id);
// auth
const isOwner = project.member_role === EUserProjectRoles.ADMIN;
const isMember = project.member_role === EUserProjectRoles.MEMBER;
const isOwner = project.member_role === EUserPermissions.ADMIN;
const isMember = project.member_role === EUserPermissions.MEMBER;
// archive
const isArchived = !!project.archived_at;

View file

@ -8,13 +8,12 @@ import { Search, Briefcase, X } from "lucide-react";
import { Breadcrumbs, Button, Header } from "@plane/ui";
// components
import { BreadcrumbLink } from "@/components/common";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useCommandPalette, useEventTracker, useProjectFilter, useUser } from "@/hooks/store";
import { useCommandPalette, useEventTracker, useProjectFilter, useUserPermissions } from "@/hooks/store";
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import HeaderFilters from "./filters";
export const ProjectsBaseHeader = observer(() => {
@ -25,9 +24,8 @@ export const ProjectsBaseHeader = observer(() => {
// store hooks
const { toggleCreateProjectModal } = useCommandPalette();
const { setTrackElement } = useEventTracker();
const {
membership: { currentWorkspaceRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const pathname = usePathname();
const { searchQuery, updateSearchQuery } = useProjectFilter();
@ -37,7 +35,10 @@ export const ProjectsBaseHeader = observer(() => {
if (isSearchOpen && searchQuery.trim() === "") setIsSearchOpen(false);
});
// auth
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
const isAuthorizedUser = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const isArchived = pathname.includes("/archives");
const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {

View file

@ -8,7 +8,7 @@ import type { IProject } from "@plane/types";
// ui
import { Button } from "@plane/ui";
// hooks
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// type
@ -24,9 +24,7 @@ export const JoinProjectModal: React.FC<TJoinProjectModalProps> = (props) => {
// states
const [isJoiningLoading, setIsJoiningLoading] = useState(false);
// store hooks
const {
membership: { joinProject },
} = useUser();
const { joinProject } = useUserPermissions();
const { fetchProjects } = useProject();
// router
const router = useAppRouter();
@ -34,7 +32,7 @@ export const JoinProjectModal: React.FC<TJoinProjectModalProps> = (props) => {
const handleJoin = () => {
setIsJoiningLoading(true);
joinProject(workspaceSlug, [project.id])
joinProject(workspaceSlug, project.id)
.then(() => {
router.push(`/${workspaceSlug}/projects/${project.id}/issues`);
fetchProjects(workspaceSlug);

View file

@ -14,7 +14,7 @@ import { Button, Input, TOAST_TYPE, setToast } from "@plane/ui";
// constants
import { PROJECT_MEMBER_LEAVE } from "@/constants/event-tracker";
// hooks
import { useEventTracker, useUser } from "@/hooks/store";
import { useEventTracker, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
type FormData = {
@ -40,9 +40,7 @@ export const LeaveProjectModal: FC<ILeaveProjectModal> = observer((props) => {
const { workspaceSlug } = useParams();
// store hooks
const { captureEvent } = useEventTracker();
const {
membership: { leaveProject },
} = useUser();
const { leaveProject } = useUserPermissions();
const {
control,

View file

@ -9,7 +9,7 @@ import { ConfirmProjectMemberRemove } from "@/components/project";
import { PROJECT_MEMBER_LEAVE } from "@/constants/event-tracker";
// hooks
import { useEventTracker, useMember, useProject, useUser } from "@/hooks/store";
import { useEventTracker, useMember, useProject, useUser, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { useProjectColumns } from "@/plane-web/components/projects/settings/useProjectColumns";
import { IProjectMemberDetails } from "@/store/member/project-member.store";
@ -25,9 +25,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
// router
const router = useAppRouter();
// store hooks
const {
membership: { leaveProject },
} = useUser();
const { leaveProject } = useUserPermissions();
const { data: currentUser } = useUser();
const { fetchProjects } = useProject();
const {

View file

@ -9,8 +9,9 @@ import { Button } from "@plane/ui";
import { ProjectMemberListItem, SendProjectInvitationModal } from "@/components/project";
// ui
import { MembersSettingsLoader } from "@/components/ui";
import { EUserProjectRoles } from "@/constants/project";
import { useEventTracker, useMember, useUser } from "@/hooks/store";
import { useEventTracker, useMember, useUserPermissions } from "@/hooks/store";
// plane-web constants
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const ProjectMemberList: React.FC = observer(() => {
// states
@ -21,9 +22,7 @@ export const ProjectMemberList: React.FC = observer(() => {
const {
project: { projectMemberIds, getProjectMemberDetails },
} = useMember();
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const searchedMembers = (projectMemberIds ?? []).filter((userId) => {
const memberDetails = getProjectMemberDetails(userId);
@ -36,6 +35,8 @@ export const ProjectMemberList: React.FC = observer(() => {
});
const memberDetails = searchedMembers?.map((memberId) => getProjectMemberDetails(memberId));
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
return (
<>
<SendProjectInvitationModal isOpen={inviteModal} onClose={() => setInviteModal(false)} />
@ -52,7 +53,7 @@ export const ProjectMemberList: React.FC = observer(() => {
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
{currentProjectRole === EUserProjectRoles.ADMIN && (
{isAdmin && (
<Button
variant="primary"
onClick={() => {

View file

@ -7,14 +7,15 @@ import { Controller, useForm } from "react-hook-form";
import useSWR from "swr";
import { IProject, IUserLite, IWorkspace } from "@plane/types";
// ui
import { Loader, TOAST_TYPE, setToast } from "@plane/ui";
import { Loader, TOAST_TYPE, ToggleSwitch, setToast } from "@plane/ui";
// components
import { MemberSelect } from "@/components/project";
// constants
import { PROJECT_MEMBERS } from "@/constants/fetch-keys";
import { EUserProjectRoles } from "@/constants/project";
// hooks
import { useProject, useUser } from "@/hooks/store";
import { useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// types
const defaultValues: Partial<IProject> = {
@ -26,12 +27,16 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
// router
const { workspaceSlug, projectId } = useParams();
// store hooks
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { currentProjectDetails, fetchProjectDetails, updateProject } = useProject();
// derived values
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
const isAdmin = allowPermissions(
[EUserPermissions.ADMIN],
EUserPermissionsLevel.PROJECT,
currentProjectDetails?.workspace_detail?.slug,
currentProjectDetails?.id
);
// form info
const { reset, control } = useForm<IProject>({ defaultValues });
// fetching user members
@ -83,6 +88,24 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
});
};
const toggleGuestViewAllIssues = async (value: boolean) => {
if (!workspaceSlug || !projectId) return;
updateProject(workspaceSlug.toString(), projectId.toString(), {
guest_view_all_features: value,
})
.then(() => {
setToast({
title: "Success!",
type: TOAST_TYPE.SUCCESS,
message: "Project updated successfully",
});
})
.catch((err) => {
console.error(err);
});
};
return (
<>
<div className="flex items-center border-b border-custom-border-100 pb-3.5">
@ -142,6 +165,24 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
</div>
</div>
</div>
{currentProjectDetails && (
<div className="relative pb-4 flex justify-between items-center gap-3">
<div className="space-y-1">
<h3 className="text-lg font-medium text-custom-text-100">
Grant view access to all issues for guest users:
</h3>
<p className="text-sm text-custom-text-200">
This will allow guests to have view access to all the project issues.
</p>
</div>
<ToggleSwitch
value={currentProjectDetails?.guest_view_all_features}
onChange={() => toggleGuestViewAllIssues(!currentProjectDetails?.guest_view_all_features)}
disabled={!isAdmin}
size="md"
/>
</div>
)}
</>
);
});

View file

@ -6,15 +6,15 @@ import { useParams } from "next/navigation";
import { useForm, Controller, useFieldArray } from "react-hook-form";
import { ChevronDown, Plus, X } from "lucide-react";
import { Dialog, Transition } from "@headlessui/react";
// hooks
// ui
import { Avatar, Button, CustomSelect, CustomSearchSelect, TOAST_TYPE, setToast } from "@plane/ui";
// helpers
import { PROJECT_MEMBER_ADDED } from "@/constants/event-tracker";
import { EUserProjectRoles } from "@/constants/project";
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
import { useEventTracker, useMember, useUser } from "@/hooks/store";
// constants
import { ROLE } from "@/constants/workspace";
// hooks
import { useEventTracker, useMember, useUserPermissions } from "@/hooks/store";
// plane-web constants
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
type Props = {
isOpen: boolean;
@ -23,7 +23,7 @@ type Props = {
};
type member = {
role: EUserProjectRoles;
role: EUserPermissions;
member_id: string;
};
@ -46,9 +46,7 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId } = useParams();
// store hooks
const { captureEvent } = useEventTracker();
const {
membership: { currentProjectRole },
} = useUser();
const { projectUserInfo } = useUserPermissions();
const {
project: { projectMemberIds, bulkAddMembersToProject },
workspace: { workspaceMemberIds, getWorkspaceMemberDetails },
@ -68,6 +66,8 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
name: "members",
});
const currentProjectRole = projectUserInfo?.[workspaceSlug?.toString()]?.[projectId?.toString()]?.role;
const uninvitedPeople = workspaceMemberIds?.filter((userId) => {
const isInvited = projectMemberIds?.find((u) => u === userId);
@ -173,12 +173,10 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
const currentMemberWorkspaceRole = getWorkspaceMemberDetails(value)?.role;
if (!value || !currentMemberWorkspaceRole) return ROLE;
const isGuestOrViewer = [EUserWorkspaceRoles.GUEST, EUserWorkspaceRoles.VIEWER].includes(
currentMemberWorkspaceRole
);
const isGuest = [EUserPermissions.GUEST].includes(currentMemberWorkspaceRole);
return Object.fromEntries(
Object.entries(ROLE).filter(([key]) => !isGuestOrViewer || [5, 10].includes(parseInt(key)))
Object.entries(ROLE).filter(([key]) => !isGuest || [5].includes(parseInt(key)))
);
};
@ -258,7 +256,7 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
const newValue = ROLE[workspaceRole].toUpperCase();
setValue(
`members.${index}.role`,
EUserProjectRoles[newValue as keyof typeof EUserProjectRoles]
EUserPermissions[newValue as keyof typeof EUserPermissions]
);
}}
options={options}
@ -297,7 +295,7 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
{Object.entries(
checkCurrentOptionWorkspaceRole(watch(`members.${index}.member_id`))
).map(([key, label]) => {
if (parseInt(key) > (currentProjectRole ?? EUserProjectRoles.GUEST)) return null;
if (parseInt(key) > (currentProjectRole ?? EUserPermissions.GUEST)) return null;
return (
<CustomSelect.Option key={key} value={key}>

View file

@ -5,13 +5,13 @@ import { Trash2 } from "lucide-react";
import { Disclosure } from "@headlessui/react";
import { IUser, IWorkspaceMember } from "@plane/types";
import { CustomSelect, PopoverMenu, TOAST_TYPE, setToast } from "@plane/ui";
import { EUserProjectRoles } from "@/constants/project";
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
import { ROLE } from "@/constants/workspace";
import { useMember, useUser } from "@/hooks/store";
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
export interface RowData {
member: IWorkspaceMember;
role: EUserWorkspaceRoles;
role: EUserPermissions;
}
type NameProps = {
@ -24,7 +24,7 @@ type NameProps = {
type AccountTypeProps = {
rowData: RowData;
currentProjectRole: EUserProjectRoles | undefined;
currentProjectRole: EUserPermissions | undefined;
workspaceSlug: string;
projectId: string;
};
@ -97,16 +97,14 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
// derived values
const isCurrentUser = currentUser?.id === rowData.member.id;
const isAdminRole = currentProjectRole === EUserProjectRoles.ADMIN;
const isAdminRole = currentProjectRole === EUserPermissions.ADMIN;
const isRoleNonEditable = isCurrentUser || !isAdminRole;
const checkCurrentOptionWorkspaceRole = (value: string) => {
const currentMemberWorkspaceRole = getWorkspaceMemberDetails(value)?.role;
const currentMemberWorkspaceRole = getWorkspaceMemberDetails(value)?.role as EUserPermissions | undefined;
if (!value || !currentMemberWorkspaceRole) return ROLE;
const isGuestOrViewer = [EUserWorkspaceRoles.GUEST, EUserWorkspaceRoles.VIEWER].includes(
currentMemberWorkspaceRole
);
const isGuestOrViewer = [EUserPermissions.GUEST].includes(currentMemberWorkspaceRole);
return Object.fromEntries(
Object.entries(ROLE).filter(([key]) => !isGuestOrViewer || [5, 10].includes(parseInt(key)))
@ -127,11 +125,11 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
render={({ field: { value } }) => (
<CustomSelect
value={value}
onChange={(value: EUserProjectRoles) => {
onChange={(value: EUserPermissions) => {
if (!workspaceSlug) return;
updateMember(workspaceSlug.toString(), projectId.toString(), rowData.member.id, {
role: value as unknown as EUserProjectRoles, // Cast value to unknown first, then to EUserWorkspaceRoles
role: value as unknown as EUserPermissions, // Cast value to unknown first, then to EUserPermissions
}).catch((err) => {
console.log(err, "err");
const error = err.error;
@ -155,7 +153,7 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
input
>
{Object.entries(checkCurrentOptionWorkspaceRole(rowData.member.id)).map(([key, label]) => {
if (parseInt(key) > (currentProjectRole ?? EUserProjectRoles.GUEST)) return null;
if (parseInt(key) > (currentProjectRole ?? EUserPermissions.GUEST)) return null;
return (
<CustomSelect.Option key={key} value={key}>
{label}

View file

@ -9,14 +9,13 @@ import { IProjectView } from "@plane/types";
import { ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
// components
import { CreateUpdateProjectViewModal, DeleteProjectViewModal } from "@/components/views";
// constants
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { cn } from "@/helpers/common.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useUser } from "@/hooks/store";
import { useUser, useUserPermissions } from "@/hooks/store";
import { PublishViewModal, useViewPublish } from "@/plane-web/components/views/publish";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
parentRef: React.RefObject<HTMLElement>;
@ -31,13 +30,11 @@ export const ViewQuickActions: React.FC<Props> = observer((props) => {
const [createUpdateViewModal, setCreateUpdateViewModal] = useState(false);
const [deleteViewModal, setDeleteViewModal] = useState(false);
// store hooks
const {
membership: { currentProjectRole },
data,
} = useUser();
const { data } = useUser();
const { allowPermissions } = useUserPermissions();
// auth
const isOwner = view?.owned_by === data?.id;
const isAdmin = !!currentProjectRole && currentProjectRole == EUserProjectRoles.ADMIN;
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT, workspaceSlug, projectId);
const { isPublishModalOpen, setPublishModalOpen, publishContextMenu } = useViewPublish(
!!view.anchor,

View file

@ -9,12 +9,12 @@ import { Tooltip, FavoriteStar } from "@plane/ui";
// components
import { DeleteProjectViewModal, CreateUpdateProjectViewModal, ViewQuickActions } from "@/components/views";
// constants
import { EUserProjectRoles } from "@/constants/project";
import { EViewAccess } from "@/constants/views";
// helpers
import { calculateTotalFilters } from "@/helpers/filter.helper";
// hooks
import { useMember, useProjectView, useUser } from "@/hooks/store";
import { useMember, useProjectView, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { ButtonAvatars } from "../dropdowns/member/avatar";
type Props = {
@ -30,14 +30,16 @@ export const ViewListItemAction: FC<Props> = observer((props) => {
// router
const { workspaceSlug, projectId } = useParams();
// store
const {
membership: { currentProjectRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { addViewToFavorites, removeViewFromFavorites } = useProjectView();
const { getUserDetails } = useMember();
// derived values
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
const isEditingAllowed = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT
);
const totalFilters = calculateTotalFilters(view.filters ?? {});

View file

@ -2,6 +2,7 @@
import React, { useEffect } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { Controller, useFieldArray, useForm } from "react-hook-form";
import { Plus, X } from "lucide-react";
import { Dialog, Transition } from "@headlessui/react";
@ -9,9 +10,10 @@ import { IWorkspaceBulkInviteFormData } from "@plane/types";
// ui
import { Button, CustomSelect, Input } from "@plane/ui";
// constants
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
import { ROLE } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
// types
type Props = {
@ -22,7 +24,7 @@ type Props = {
type EmailRole = {
email: string;
role: EUserWorkspaceRoles;
role: EUserPermissions;
};
type FormValues = {
@ -40,10 +42,10 @@ const defaultValues: FormValues = {
export const SendWorkspaceInvitationModal: React.FC<Props> = observer((props) => {
const { isOpen, onClose, onSubmit } = props;
// mobx store
const {
membership: { currentWorkspaceRole },
} = useUser();
// store hooks
const { workspaceInfoBySlug } = useUserPermissions();
// router
const { workspaceSlug } = useParams();
// form info
const {
control,
@ -57,6 +59,8 @@ export const SendWorkspaceInvitationModal: React.FC<Props> = observer((props) =>
name: "emails",
});
const currentWorkspaceRole = workspaceInfoBySlug(workspaceSlug.toString())?.role;
const handleClose = () => {
onClose();

View file

@ -9,10 +9,11 @@ import { CustomSelect, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
// components
import { ConfirmWorkspaceMemberRemove } from "@/components/workspace";
// constants
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
import { ROLE } from "@/constants/workspace";
// hooks
import { useMember, useUser } from "@/hooks/store";
import { useMember, useUserPermissions } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
invitationId: string;
@ -25,15 +26,16 @@ export const WorkspaceInvitationsListItem: FC<Props> = observer((props) => {
// router
const { workspaceSlug } = useParams();
// store hooks
const {
membership: { currentWorkspaceMemberInfo, currentWorkspaceRole },
} = useUser();
const { allowPermissions, workspaceInfoBySlug } = useUserPermissions();
const {
workspace: { updateMemberInvitation, deleteMemberInvitation, getWorkspaceInvitationDetails },
} = useMember();
const { isMobile } = usePlatformOS();
// derived values
const invitationDetails = getWorkspaceInvitationDetails(invitationId);
const currentWorkspaceMemberInfo = workspaceInfoBySlug(workspaceSlug.toString());
const currentWorkspaceRole = currentWorkspaceMemberInfo?.role;
const handleRemoveInvitation = async () => {
if (!workspaceSlug || !invitationDetails) return;
@ -58,13 +60,16 @@ export const WorkspaceInvitationsListItem: FC<Props> = observer((props) => {
if (!invitationDetails) return null;
// is the current logged in user admin
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
// role change access-
// 1. user cannot change their own role
// 2. only admin or member can change role
// 3. user cannot change role of higher role
const hasRoleChangeAccess =
currentWorkspaceRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentWorkspaceRole);
const hasRoleChangeAccess = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
if (!currentWorkspaceMemberInfo) return null;
@ -110,7 +115,7 @@ export const WorkspaceInvitationsListItem: FC<Props> = observer((props) => {
</div>
}
value={invitationDetails.role}
onChange={(value: EUserWorkspaceRoles) => {
onChange={(value: EUserPermissions) => {
if (!workspaceSlug || !value) return;
updateMemberInvitation(workspaceSlug.toString(), invitationDetails.id, {

Some files were not shown because too many files have changed in this diff Show more