[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

@ -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, {

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 { useMember, useUser } from "@/hooks/store";
import { ROLE } from "@/constants/workspace";
import { useMember, useUser, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export interface RowData {
member: IWorkspaceMember;
role: EUserWorkspaceRoles;
role: EUserPermissions;
}
type NameProps = {
@ -24,7 +24,6 @@ type NameProps = {
type AccountTypeProps = {
rowData: RowData;
currentWorkspaceRole: EUserWorkspaceRoles | undefined;
workspaceSlug: string;
};
@ -81,13 +80,15 @@ export const NameColumn: React.FC<NameProps> = (props) => {
};
export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) => {
const { rowData, currentWorkspaceRole, workspaceSlug } = props;
const { rowData, workspaceSlug } = props;
// form info
const {
control,
formState: { errors },
} = useForm();
// store hooks
const { allowPermissions } = useUserPermissions();
const {
workspace: { updateMember },
} = useMember();
@ -95,7 +96,7 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
// derived values
const isCurrentUser = currentUser?.id === rowData.member.id;
const isAdminRole = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
const isAdminRole = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const isRoleNonEditable = isCurrentUser || !isAdminRole;
return (
@ -112,12 +113,12 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
render={({ field: { value } }) => (
<CustomSelect
value={value}
onChange={(value: EUserProjectRoles) => {
onChange={(value: EUserPermissions) => {
console.log({ value, workspaceSlug }, "onChange");
if (!workspaceSlug) return;
updateMember(workspaceSlug.toString(), rowData.member.id, {
role: value as unknown as EUserWorkspaceRoles, // 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;
@ -141,7 +142,7 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
input
>
{Object.keys(ROLE).map((item) => (
<CustomSelect.Option key={item} value={item as unknown as EUserProjectRoles}>
<CustomSelect.Option key={item} value={item as unknown as EUserPermissions}>
{ROLE[item as unknown as keyof typeof ROLE]}
</CustomSelect.Option>
))}

View file

@ -12,7 +12,7 @@ import { ConfirmWorkspaceMemberRemove } from "@/components/workspace";
// constants
import { WORKSPACE_MEMBER_LEAVE } from "@/constants/event-tracker";
// hooks
import { useEventTracker, useMember, useUser } from "@/hooks/store";
import { useEventTracker, useMember, useUser, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { useMemberColumns } from "@/plane-web/components/workspace/settings/useMemberColumns";
@ -26,13 +26,11 @@ export const WorkspaceMembersListItem: FC<Props> = observer((props) => {
// router
const router = useAppRouter();
// store hooks
const {
membership: { leaveWorkspace },
} = useUser();
const { data: currentUser } = useUser();
const {
workspace: { removeMemberFromWorkspace },
} = useMember();
const { leaveWorkspace } = useUserPermissions();
const { captureEvent } = useEventTracker();
// derived values

View file

@ -12,13 +12,14 @@ import { LogoSpinner } from "@/components/common";
import { WorkspaceImageUploadModal } from "@/components/core";
// constants
import { WORKSPACE_UPDATED } from "@/constants/event-tracker";
import { EUserWorkspaceRoles, ORGANIZATION_SIZE } from "@/constants/workspace";
import { ORGANIZATION_SIZE } from "@/constants/workspace";
// helpers
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useEventTracker, useUser, useWorkspace } from "@/hooks/store";
import { useEventTracker, useUserPermissions, useWorkspace } from "@/hooks/store";
// plane web components
import { DeleteWorkspaceSection } from "@/plane-web/components/workspace";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// services
import { FileService } from "@/services/file.service";
@ -39,10 +40,9 @@ export const WorkspaceDetails: FC = observer(() => {
const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false);
// store hooks
const { captureWorkspaceEvent } = useEventTracker();
const {
membership: { currentWorkspaceRole },
} = useUser();
const { currentWorkspace, updateWorkspace } = useWorkspace();
const { allowPermissions } = useUserPermissions();
// form info
const {
handleSubmit,
@ -141,7 +141,7 @@ export const WorkspaceDetails: FC = observer(() => {
if (currentWorkspace) reset({ ...currentWorkspace });
}, [currentWorkspace, reset]);
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
if (!currentWorkspace)
return (

View file

@ -41,15 +41,14 @@ import {
import { Logo } from "@/components/common";
import { LeaveProjectModal, PublishProjectModal } from "@/components/project";
import { SidebarNavItem } from "@/components/sidebar";
// constants
import { EUserProjectRoles } from "@/constants/project";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme, useEventTracker, useProject, useUser } from "@/hooks/store";
import { useAppTheme, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
import { usePlatformOS } from "@/hooks/use-platform-os";
// constants
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { HIGHLIGHT_CLASS, highlightIssueOnDrop } from "../../issues/issue-layouts/utils";
type Props = {
@ -71,37 +70,37 @@ const navigation = (workspaceSlug: string, projectId: string) => [
name: "Issues",
href: `/${workspaceSlug}/projects/${projectId}/issues`,
Icon: LayersIcon,
access: EUserProjectRoles.GUEST,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
},
{
name: "Cycles",
href: `/${workspaceSlug}/projects/${projectId}/cycles`,
Icon: ContrastIcon,
access: EUserProjectRoles.VIEWER,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
},
{
name: "Modules",
href: `/${workspaceSlug}/projects/${projectId}/modules`,
Icon: DiceIcon,
access: EUserProjectRoles.VIEWER,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
},
{
name: "Views",
href: `/${workspaceSlug}/projects/${projectId}/views`,
Icon: Layers,
access: EUserProjectRoles.GUEST,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
},
{
name: "Pages",
href: `/${workspaceSlug}/projects/${projectId}/pages`,
Icon: FileText,
access: EUserProjectRoles.VIEWER,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
},
{
name: "Intake",
href: `/${workspaceSlug}/projects/${projectId}/inbox`,
Icon: Intake,
access: EUserProjectRoles.GUEST,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
},
];
@ -113,9 +112,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
const { setTrackElement } = useEventTracker();
const { addProjectToFavorites, removeProjectFromFavorites, getProjectById } = useProject();
const { isMobile } = usePlatformOS();
const {
membership: { currentWorkspaceAllProjectsRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
// states
const [leaveProjectModalOpen, setLeaveProjectModal] = useState(false);
const [publishModalOpen, setPublishModal] = useState(false);
@ -135,9 +132,18 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
// derived values
const project = getProjectById(projectId);
// auth
const isAdmin = project?.member_role === EUserProjectRoles.ADMIN;
const isViewerOrGuest =
project?.member_role && [EUserProjectRoles.VIEWER, EUserProjectRoles.GUEST].includes(project.member_role);
const isAdmin = allowPermissions(
[EUserPermissions.ADMIN],
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
project?.id
);
const isAuthorized = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
project?.id
);
const handleAddToFavorites = () => {
if (!workspaceSlug || !project) return;
@ -395,7 +401,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
placement="bottom-start"
useCaptureForOutsideClick
>
{!isViewerOrGuest && (
{isAuthorized && (
<CustomMenu.MenuItem
onClick={project.is_favorite ? handleRemoveFromFavorites : handleAddToFavorites}
>
@ -421,7 +427,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
</div>
</CustomMenu.MenuItem>
)}
{!isViewerOrGuest && (
{isAuthorized && (
<CustomMenu.MenuItem>
<Link href={`/${workspaceSlug}/projects/${project?.id}/draft-issues/`}>
<div className="flex items-center justify-start gap-2">
@ -437,7 +443,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
<span>Copy link</span>
</span>
</CustomMenu.MenuItem>
{!isViewerOrGuest && (
{isAuthorized && (
<CustomMenu.MenuItem>
<Link href={`/${workspaceSlug}/projects/${project?.id}/archives/issues`}>
<div className="flex items-center justify-start gap-2">
@ -456,7 +462,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
</Link>
</CustomMenu.MenuItem>
{/* leave project */}
{isViewerOrGuest && (
{!isAuthorized && (
<CustomMenu.MenuItem onClick={handleLeaveProject}>
<div className="flex items-center justify-start gap-2">
<LogOut className="h-3.5 w-3.5 stroke-[1.5]" />
@ -505,12 +511,14 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
(item.name === "Intake" && !project.inbox_view)
)
return;
const currentRole = currentWorkspaceAllProjectsRole
? currentWorkspaceAllProjectsRole[projectId]
: undefined;
return (
<>
{currentRole >= item.access && (
{allowPermissions(
item.access,
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
project.id
) && (
<Tooltip
key={item.name}
isMobile={isMobile}

View file

@ -14,14 +14,13 @@ import { TOAST_TYPE, Tooltip, setToast } from "@plane/ui";
// components
import { CreateProjectModal } from "@/components/project";
import { SidebarProjectsListItem } from "@/components/workspace";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// helpers
import { cn } from "@/helpers/common.helper";
import { orderJoinedProjects } from "@/helpers/project.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
// hooks
import { useAppTheme, useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
import { useAppTheme, useCommandPalette, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const SidebarProjectsList: FC = observer(() => {
// get local storage data for isFavoriteProjectsListOpen and isAllProjectsListOpen
@ -37,16 +36,18 @@ export const SidebarProjectsList: FC = observer(() => {
const { toggleCreateProjectModal } = useCommandPalette();
const { sidebarCollapsed } = useAppTheme();
const { setTrackElement } = useEventTracker();
const {
membership: { currentWorkspaceRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { getProjectById, joinedProjectIds: joinedProjects, updateProjectView } = useProject();
// router params
const { workspaceSlug } = useParams();
const pathname = usePathname();
// auth
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
const isAuthorizedUser = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const handleCopyText = (projectId: string) => {
copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/issues`).then(() => {

View file

@ -11,28 +11,25 @@ import { NotificationAppSidebarOption } from "@/components/workspace-notificatio
// constants
import { SIDEBAR_USER_MENU_ITEMS } from "@/constants/dashboard";
import { SIDEBAR_CLICKED } from "@/constants/event-tracker";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme, useEventTracker, useUser } from "@/hooks/store";
import { useAppTheme, useEventTracker, useUser, useUserPermissions } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const SidebarUserMenu = observer(() => {
// store hooks
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
const { captureEvent } = useEventTracker();
const { isMobile } = usePlatformOS();
const {
membership: { currentWorkspaceRole },
data: currentUser,
} = useUser();
const { data: currentUser } = useUser();
const { allowPermissions } = useUserPermissions();
// router params
const { workspaceSlug } = useParams();
// pathname
const pathname = usePathname();
// computed
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
const getHref = (link: any) =>
`/${workspaceSlug}${link.href}${link.key === "your-work" ? `/${currentUser?.id}` : ""}`;
@ -61,7 +58,7 @@ export const SidebarUserMenu = observer(() => {
>
{SIDEBAR_USER_MENU_ITEMS.map(
(link) =>
workspaceMemberInfo >= link.access && (
allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
<Tooltip
key={link.key}
tooltipContent={link.label}

View file

@ -13,16 +13,16 @@ import { SidebarNavItem } from "@/components/sidebar";
// constants
import { SIDEBAR_WORKSPACE_MENU_ITEMS } from "@/constants/dashboard";
import { SIDEBAR_CLICKED } from "@/constants/event-tracker";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme, useEventTracker, useUser } from "@/hooks/store";
import { useAppTheme, useEventTracker, useUserPermissions } from "@/hooks/store";
import useLocalStorage from "@/hooks/use-local-storage";
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web components
import { UpgradeBadge } from "@/plane-web/components/workspace";
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
export const SidebarWorkspaceMenu = observer(() => {
// state
@ -33,9 +33,7 @@ export const SidebarWorkspaceMenu = observer(() => {
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
const { captureEvent } = useEventTracker();
const { isMobile } = usePlatformOS();
const {
membership: { currentWorkspaceRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
// router params
const { workspaceSlug } = useParams();
// pathname
@ -44,8 +42,6 @@ export const SidebarWorkspaceMenu = observer(() => {
const { setValue: toggleWorkspaceMenu, storedValue } = useLocalStorage<boolean>("is_workspace_menu_open", true);
// derived values
const isWorkspaceMenuOpen = !!storedValue;
// auth
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
const handleLinkClick = (itemKey: string) => {
if (window.innerWidth < 768) {
@ -157,7 +153,7 @@ export const SidebarWorkspaceMenu = observer(() => {
>
{SIDEBAR_WORKSPACE_MENU_ITEMS.map(
(link) =>
workspaceMemberInfo >= link.access && (
allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
<Tooltip
key={link.key}
tooltipContent={link.label}

View file

@ -14,9 +14,10 @@ import {
} from "@/components/workspace";
// constants
import { GLOBAL_VIEW_OPENED } from "@/constants/event-tracker";
import { DEFAULT_GLOBAL_VIEWS_LIST, EUserWorkspaceRoles } from "@/constants/workspace";
import { DEFAULT_GLOBAL_VIEWS_LIST } from "@/constants/workspace";
// store hooks
import { useEventTracker, useGlobalView, useUser } from "@/hooks/store";
import { useEventTracker, useGlobalView, useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const ViewTab = observer((props: { viewId: string }) => {
const { viewId } = props;
@ -77,9 +78,8 @@ export const GlobalViewsHeader: React.FC = observer(() => {
const { globalViewId } = useParams();
// store hooks
const { currentWorkspaceViews } = useGlobalView();
const {
membership: { currentWorkspaceRole },
} = useUser();
const { allowPermissions } = useUserPermissions();
const { captureEvent } = useEventTracker();
// bring the active view to the centre of the header
@ -101,7 +101,10 @@ export const GlobalViewsHeader: React.FC = observer(() => {
}
}, [globalViewId, currentWorkspaceViews, containerRef, captureEvent]);
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
const isAuthorizedUser = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
return (
<Header variant={EHeaderVariant.SECONDARY} className="min-h-[44px]">

View file

@ -12,12 +12,12 @@ import { ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from
import { CreateUpdateWorkspaceViewModal, DeleteGlobalViewModal } from "@/components/workspace";
// constants
import { EViewAccess } from "@/constants/views";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// 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 { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = {
parentRef: React.RefObject<HTMLElement>;
@ -33,13 +33,11 @@ export const WorkspaceViewQuickActions: React.FC<Props> = observer((props) => {
const [updateViewModal, setUpdateViewModal] = useState(false);
const [deleteViewModal, setDeleteViewModal] = useState(false);
// store hooks
const {
membership: { currentWorkspaceRole },
data,
} = useUser();
const { data } = useUser();
const { allowPermissions } = useUserPermissions();
// auth
const isOwner = view?.owned_by === data?.id;
const isAdmin = !!currentWorkspaceRole && currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const viewLink = `${workspaceSlug}/workspace-views/${view.id}`;
const handleCopyText = () =>