[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:
parent
7013a36629
commit
fdcd9a376c
172 changed files with 2057 additions and 1627 deletions
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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, {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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]">
|
||||
|
|
|
|||
|
|
@ -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 = () =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue