fix: Permission levels for project settings (#2978)

* fix add subgroup issue FED-1101

* fix subgroup by None assignee FED-1100

* fix grouping by asignee or labels FED-1096

* fix create view popup FED-1093

* fix subgroup exception in swimlanes

* fix show sub issue filter FED-1102

* use Enums instead of numbers

* fix Estimates setting permission for admin

* disable access to project settings for viewers and guests

* fix project unautorized flicker

* add observer to estimates

* add permissions to member list
This commit is contained in:
rahulramesha 2023-12-04 20:03:23 +05:30 committed by GitHub
parent b527ec582f
commit 8a8eea38f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 115 additions and 61 deletions

View File

@ -11,6 +11,7 @@ import { ArchiveRestore } from "lucide-react";
import { PROJECT_AUTOMATION_MONTHS } from "constants/project";
// types
import { IProject } from "types";
import { EUserWorkspaceRoles } from "constants/workspace";
type Props = {
handleChange: (formData: Partial<IProject>) => Promise<void>;
@ -28,6 +29,8 @@ export const AutoArchiveAutomation: React.FC<Props> = observer((props) => {
const projectDetails = projectStore.currentProjectDetails;
const userRole = userStore.currentProjectRole;
const isAdmin = userRole === EUserWorkspaceRoles.ADMIN;
return (
<>
<SelectMonthModal
@ -56,7 +59,7 @@ export const AutoArchiveAutomation: React.FC<Props> = observer((props) => {
projectDetails?.archive_in === 0 ? handleChange({ archive_in: 1 }) : handleChange({ archive_in: 0 })
}
size="sm"
disabled={userRole !== 20}
disabled={!isAdmin}
/>
</div>
@ -74,7 +77,7 @@ export const AutoArchiveAutomation: React.FC<Props> = observer((props) => {
}}
input
width="w-full"
disabled={userRole !== 20}
disabled={!isAdmin}
>
<>
{PROJECT_AUTOMATION_MONTHS.map((month) => (

View File

@ -11,6 +11,7 @@ import { ArchiveX } from "lucide-react";
import { IProject } from "types";
// fetch keys
import { PROJECT_AUTOMATION_MONTHS } from "constants/project";
import { EUserWorkspaceRoles } from "constants/workspace";
type Props = {
handleChange: (formData: Partial<IProject>) => Promise<void>;
@ -53,6 +54,8 @@ export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
default_state: defaultState,
};
const isAdmin = userRole === EUserWorkspaceRoles.ADMIN;
return (
<>
<SelectMonthModal
@ -83,7 +86,7 @@ export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
: handleChange({ close_in: 0, default_state: null })
}
size="sm"
disabled={userRole !== 20}
disabled={!isAdmin}
/>
</div>
@ -102,7 +105,7 @@ export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
}}
input
width="w-full"
disabled={userRole !== 20}
disabled={!isAdmin}
>
<>
{PROJECT_AUTOMATION_MONTHS.map((month) => (

View File

@ -22,6 +22,7 @@ import { Button } from "@plane/ui";
import { CheckCircle2, ChevronDown, ChevronUp, Clock, FileStack, Inbox, Trash2, XCircle } from "lucide-react";
// types
import type { TInboxStatus } from "types";
import { EUserWorkspaceRoles } from "constants/workspace";
export const InboxActionsHeader = observer(() => {
const [date, setDate] = useState(new Date());
@ -71,7 +72,7 @@ export const InboxActionsHeader = observer(() => {
}, [issue]);
const issueStatus = issue?.issue_inbox[0].status;
const isAllowed = userRole === 15 || userRole === 20;
const isAllowed = !!userRole && userRole >= EUserWorkspaceRoles.MEMBER;
const today = new Date();
const tomorrow = new Date(today);

View File

@ -16,6 +16,7 @@ import { Loader } from "@plane/ui";
import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
// types
import { IInboxIssue, IIssue } from "types";
import { EUserWorkspaceRoles } from "constants/workspace";
const defaultValues: Partial<IInboxIssue> = {
name: "",
@ -144,6 +145,8 @@ export const InboxMainContent: React.FC = observer(() => {
</div>
);
const isAllowed = !!userRole && userRole >= EUserWorkspaceRoles.MEMBER;
return (
<>
{issueDetails ? (
@ -222,7 +225,7 @@ export const InboxMainContent: React.FC = observer(() => {
description_html: issueDetails.description_html,
}}
handleFormSubmit={submitChanges}
isAllowed={userRole === 15 || userRole === 20 || user?.id === issueDetails.created_by}
isAllowed={isAllowed || user?.id === issueDetails.created_by}
/>
</div>

View File

@ -25,6 +25,7 @@ import {
IViewIssuesStore,
} from "store/issues";
import { TUnGroupedIssues } from "store/issues/types";
import { EUserWorkspaceRoles } from "constants/workspace";
interface IBaseGanttRoot {
issueFiltersStore:
@ -69,7 +70,7 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
);
};
const isAllowed = currentProjectRole && currentProjectRole >= 15;
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
return (
<>

View File

@ -31,6 +31,7 @@ import { KanBan } from "./default";
import { KanBanSwimLanes } from "./swimlanes";
import { EProjectStore } from "store/command-palette.store";
import { IssuePeekOverview } from "components/issues";
import { EUserWorkspaceRoles } from "constants/workspace";
export interface IBaseKanBanLayout {
issueStore:
@ -93,7 +94,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
} = useMobxStore();
const { currentProjectRole } = userStore;
const isEditingAllowed = [15, 20].includes(currentProjectRole || 0);
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const issues = issueStore?.getIssues || {};
const issueIds = issueStore?.getIssuesIds || [];

View File

@ -25,6 +25,7 @@ import { IIssueResponse } from "store/issues/types";
import { EProjectStore } from "store/command-palette.store";
import { IssuePeekOverview } from "components/issues";
import { useRouter } from "next/router";
import { EUserWorkspaceRoles } from "constants/workspace";
enum EIssueActions {
UPDATE = "update",
@ -83,7 +84,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
} = useMobxStore();
const { currentProjectRole } = userStore;
const isEditingAllowed = [15, 20].includes(currentProjectRole || 0);
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const issueIds = issueStore?.getIssuesIds || [];
const issues = issueStore?.getIssues;

View File

@ -18,6 +18,7 @@ import { observer } from "mobx-react-lite";
import { EFilterType, TUnGroupedIssues } from "store/issues/types";
import { EIssueActions } from "../types";
import { IQuickActionProps } from "../list/list-view-types";
import { EUserWorkspaceRoles } from "constants/workspace";
interface IBaseSpreadsheetRoot {
issueFiltersStore:
@ -49,7 +50,7 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
} = useMobxStore();
const { currentProjectRole } = userStore;
const isEditingAllowed = [15, 20].includes(currentProjectRole || 0);
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const issuesResponse = issueStore.getIssues;
const issueIds = (issueStore.getIssuesIds ?? []) as TUnGroupedIssues;

View File

@ -14,6 +14,7 @@ import { IIssue } from "types";
// services
import { FileService } from "services/file.service";
import { useMobxStore } from "lib/mobx/store-provider";
import { EUserWorkspaceRoles } from "constants/workspace";
const fileService = new FileService();
@ -32,7 +33,7 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
// store
const { user: userStore } = useMobxStore();
const { currentProjectRole } = userStore;
const isAllowed = [15, 20].includes(currentProjectRole || 0);
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
// states
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
const [characterLimit, setCharacterLimit] = useState(false);

View File

@ -12,6 +12,7 @@ import { IssueView } from "./view";
import { copyUrlToClipboard } from "helpers/string.helper";
// types
import { IIssue } from "types";
import { EUserWorkspaceRoles } from "constants/workspace";
interface IIssuePeekOverview {
workspaceSlug: string;
@ -118,7 +119,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
}
};
const userRole = userStore.currentProjectRole ?? 5;
const userRole = userStore.currentProjectRole ?? EUserWorkspaceRoles.GUEST;
return (
<Fragment>

View File

@ -26,6 +26,7 @@ import { MinusCircle } from "lucide-react";
import { IIssue, IIssueComment } from "types";
// fetch-keys
import { PROJECT_ISSUES_ACTIVITY, SUB_ISSUES } from "constants/fetch-keys";
import { EUserWorkspaceRoles } from "constants/workspace";
type Props = {
issueDetails: IIssue;
@ -100,6 +101,8 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
);
};
const isAllowed = !!userRole && userRole >= EUserWorkspaceRoles.MEMBER;
return (
<>
<div className="rounded-lg">
@ -166,7 +169,7 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
workspaceSlug={workspaceSlug as string}
issue={issueDetails}
handleFormSubmit={submitChanges}
isAllowed={userRole === 20 || userRole === 15 || !uneditable}
isAllowed={isAllowed || !uneditable}
/>
<IssueReaction workspaceSlug={workspaceSlug} issueId={issueId} projectId={projectId} />

View File

@ -40,6 +40,7 @@ import { copyTextToClipboard } from "helpers/string.helper";
import type { IIssue, IIssueLink, linkDetails } from "types";
// fetch-keys
import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
import { EUserWorkspaceRoles } from "constants/workspace";
type Props = {
control: any;
@ -245,7 +246,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
setLinkModal(true);
};
const isNotAllowed = userRole === 5 || userRole === 10;
const isAllowed = !!userRole && userRole >= EUserWorkspaceRoles.MEMBER;
return (
<>
@ -295,7 +296,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
<LinkIcon className="h-3.5 w-3.5" />
</button>
)}
{!isNotAllowed && (fieldsToShow.includes("all") || fieldsToShow.includes("delete")) && (
{isAllowed && (fieldsToShow.includes("all") || fieldsToShow.includes("delete")) && (
<button
type="button"
className="rounded-md border border-red-500 p-2 text-red-500 shadow-sm duration-300 hover:bg-red-500/20 focus:outline-none"
@ -325,7 +326,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
<SidebarStateSelect
value={value}
onChange={(val: string) => submitChanges({ state: val })}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
/>
@ -346,7 +347,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
<SidebarAssigneeSelect
value={value}
onChange={(val: string[]) => submitChanges({ assignees: val })}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
/>
@ -367,7 +368,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
<SidebarPrioritySelect
value={value}
onChange={(val) => submitChanges({ priority: val })}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
/>
@ -388,7 +389,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
<SidebarEstimateSelect
value={value}
onChange={(val: number | null) => submitChanges({ estimate_point: val })}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
/>
@ -416,7 +417,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
onChange(val);
}}
issueDetails={issueDetail}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
/>
@ -441,7 +442,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
}}
watch={watchIssue}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
{(fieldsToShow.includes("all") || fieldsToShow.includes("blocked")) && (
@ -462,7 +463,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
}}
watch={watchIssue}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
{(fieldsToShow.includes("all") || fieldsToShow.includes("duplicate")) && (
@ -480,7 +481,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
}}
watch={watchIssue}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
{(fieldsToShow.includes("all") || fieldsToShow.includes("relates_to")) && (
@ -498,7 +499,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
}}
watch={watchIssue}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
{(fieldsToShow.includes("all") || fieldsToShow.includes("startDate")) && (
@ -522,7 +523,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
}
className="bg-custom-background-80 border-none"
maxDate={maxDate ?? undefined}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
/>
@ -550,7 +551,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
}
className="bg-custom-background-80 border-none"
minDate={minDate ?? undefined}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
)}
/>
@ -571,7 +572,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
<SidebarCycleSelect
issueDetail={issueDetail}
handleCycleChange={handleCycleChange}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
</div>
</div>
@ -586,7 +587,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
<SidebarModuleSelect
issueDetail={issueDetail}
handleModuleChange={handleModuleChange}
disabled={isNotAllowed || uneditable}
disabled={!isAllowed || uneditable}
/>
</div>
</div>
@ -605,7 +606,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
issueDetails={issueDetail}
labelList={issueDetail?.labels ?? []}
submitChanges={submitChanges}
isNotAllowed={isNotAllowed}
isNotAllowed={!isAllowed}
uneditable={uneditable ?? false}
/>
</div>
@ -615,7 +616,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
<div className={`min-h-[116px] py-1 text-xs ${uneditable ? "opacity-60" : ""}`}>
<div className="flex items-center justify-between gap-2">
<h4>Links</h4>
{!isNotAllowed && (
{isAllowed && (
<button
type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-custom-background-90 ${

View File

@ -22,6 +22,7 @@ import { IUser, IIssue, ISearchIssueResponse } from "types";
import { IssueService } from "services/issue";
// fetch keys
import { SUB_ISSUES } from "constants/fetch-keys";
import { EUserWorkspaceRoles } from "constants/workspace";
export interface ISubIssuesRoot {
parentIssue: IIssue;
@ -176,7 +177,7 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
[updateIssueStructure, projectId, updateIssue, user, workspaceSlug]
);
const isEditable = userRole === 5 || userRole === 10 ? false : true;
const isEditable = !!userRole && userRole >= EUserWorkspaceRoles.MEMBER;
const mutateSubIssues = (parentIssueId: string | null) => {
if (parentIssueId) mutate(SUB_ISSUES(parentIssueId));

View File

@ -33,6 +33,7 @@ import { linkDetails, IModule, ModuleLink } from "types";
import { MODULE_DETAILS } from "constants/fetch-keys";
// constant
import { MODULE_STATUS } from "constants/module";
import { EUserWorkspaceRoles } from "constants/workspace";
const defaultValues: Partial<IModule> = {
lead: "",
@ -588,10 +589,10 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
handleEditLink={handleEditLink}
handleDeleteLink={handleDeleteLink}
userAuth={{
isGuest: userRole === 5,
isViewer: userRole === 10,
isMember: userRole === 15,
isOwner: userRole === 20,
isGuest: userRole === EUserWorkspaceRoles.GUEST,
isViewer: userRole === EUserWorkspaceRoles.VIEWER,
isMember: userRole === EUserWorkspaceRoles.MEMBER,
isOwner: userRole === EUserWorkspaceRoles.ADMIN,
}}
/>
</>

View File

@ -26,6 +26,7 @@ import { CustomMenu, Tooltip } from "@plane/ui";
import { CreateUpdatePageModal, DeletePageModal } from "components/pages";
// types
import { IPage } from "types";
import { EUserWorkspaceRoles } from "constants/workspace";
export interface IPagesListItem {
workspaceSlug: string;
@ -144,7 +145,7 @@ export const PagesListItem: FC<IPagesListItem> = observer((props) => {
setCreateUpdatePageModal(true);
};
const userCanEdit = currentProjectRole === 15 || currentProjectRole === 20;
const userCanEdit = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
return (
<>

View File

@ -13,7 +13,7 @@ import { CustomSelect, Tooltip } from "@plane/ui";
// icons
import { ChevronDown, Dot, XCircle } from "lucide-react";
// constants
import { ROLE } from "constants/workspace";
import { EUserWorkspaceRoles, ROLE } from "constants/workspace";
// types
import { IProjectMember, TUserProjectRole } from "types";
@ -38,7 +38,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
const { setToastAlert } = useToast();
// derived values
const isAdmin = currentProjectRole === 20;
const isAdmin = currentProjectRole === EUserWorkspaceRoles.ADMIN;
const memberDetails = member.member;
const handleRemove = async () => {
@ -148,12 +148,13 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
disabled={
memberDetails.id === currentUser?.id ||
!member.member ||
(currentProjectRole && currentProjectRole !== 20 && currentProjectRole < member.role)
!currentProjectRole ||
currentProjectRole < member.role
}
placement="bottom-end"
>
{Object.keys(ROLE).map((key) => {
if (currentProjectRole && currentProjectRole !== 20 && currentProjectRole < parseInt(key)) return null;
if (currentProjectRole && !isAdmin && currentProjectRole < parseInt(key)) return null;
return (
<CustomSelect.Option key={key} value={parseInt(key, 10)}>

View File

@ -15,6 +15,7 @@ import { Loader } from "@plane/ui";
import { IProject, IUserLite, IWorkspace } from "types";
// fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
import { EUserWorkspaceRoles } from "constants/workspace";
const defaultValues: Partial<IProject> = {
project_lead: null,
@ -29,7 +30,7 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
const { user: userStore, project: projectStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
const { currentProjectRole } = userStore;
const isAdmin = currentProjectRole === 20;
const isAdmin = currentProjectRole === EUserWorkspaceRoles.ADMIN;
// hooks
const { setToastAlert } = useToast();
// form info

View File

@ -15,7 +15,7 @@ import useToast from "hooks/use-toast";
// types
import { IProjectMember, TUserProjectRole } from "types";
// constants
import { ROLE } from "constants/workspace";
import { EUserWorkspaceRoles, ROLE } from "constants/workspace";
type Props = {
isOpen: boolean;
@ -246,7 +246,8 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
width="w-full"
>
{Object.entries(ROLE).map(([key, label]) => {
if (parseInt(key) > (currentProjectRole ?? 5)) return null;
if (parseInt(key) > (currentProjectRole ?? EUserWorkspaceRoles.GUEST))
return null;
return (
<CustomSelect.Option key={key} value={key}>

View File

@ -9,6 +9,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
// types
import { IProject } from "types";
import { EUserWorkspaceRoles } from "constants/workspace";
type Props = {};
@ -56,7 +57,7 @@ export const ProjectFeaturesList: FC<Props> = observer(() => {
user: { currentUser, currentProjectRole },
trackEvent: { setTrackElement, postHogEventTracker },
} = useMobxStore();
const isAdmin = currentProjectRole === 20;
const isAdmin = currentProjectRole === EUserWorkspaceRoles.ADMIN;
// hooks
const { setToastAlert } = useToast();
@ -97,7 +98,7 @@ export const ProjectFeaturesList: FC<Props> = observer(() => {
project_id: currentProjectDetails?.id,
project_name: currentProjectDetails?.name,
project_identifier: currentProjectDetails?.identifier,
enabled: !currentProjectDetails?.[feature.property as keyof IProject]
enabled: !currentProjectDetails?.[feature.property as keyof IProject],
});
handleSubmit({
[feature.property]: !currentProjectDetails?.[feature.property as keyof IProject],

View File

@ -1,12 +1,14 @@
export const getUserRole = (role: number) => {
import { EUserWorkspaceRoles } from "constants/workspace";
export const getUserRole = (role: EUserWorkspaceRoles) => {
switch (role) {
case 5:
case EUserWorkspaceRoles.GUEST:
return "GUEST";
case 10:
case EUserWorkspaceRoles.VIEWER:
return "VIEWER";
case 15:
case EUserWorkspaceRoles.MEMBER:
return "MEMBER";
case 20:
case EUserWorkspaceRoles.ADMIN:
return "ADMIN";
}
};

View File

@ -1,15 +1,27 @@
import { FC, ReactNode } from "react";
// components
import { ProjectSettingsSidebar } from "./sidebar";
import { useMobxStore } from "lib/mobx/store-provider";
import { EUserWorkspaceRoles } from "constants/workspace";
import { NotAuthorizedView } from "components/auth-screens";
import { observer } from "mobx-react-lite";
export interface IProjectSettingLayout {
children: ReactNode;
}
export const ProjectSettingLayout: FC<IProjectSettingLayout> = (props) => {
export const ProjectSettingLayout: FC<IProjectSettingLayout> = observer((props) => {
const { children } = props;
return (
const {
user: { currentProjectRole },
} = useMobxStore();
const restrictViewSettings = currentProjectRole && currentProjectRole <= EUserWorkspaceRoles.VIEWER;
return restrictViewSettings ? (
<NotAuthorizedView type="project" />
) : (
<div className="flex gap-2 h-full w-full overflow-x-hidden overflow-y-scroll">
<div className="w-80 pt-8 overflow-y-hidden flex-shrink-0">
<ProjectSettingsSidebar />
@ -17,4 +29,4 @@ export const ProjectSettingLayout: FC<IProjectSettingLayout> = (props) => {
{children}
</div>
);
};
});

View File

@ -14,6 +14,7 @@ import { ProjectSettingHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { IProject } from "types";
import { EUserWorkspaceRoles } from "constants/workspace";
const AutomationSettingsPage: NextPageWithLayout = observer(() => {
const router = useRouter();
@ -39,7 +40,7 @@ const AutomationSettingsPage: NextPageWithLayout = observer(() => {
});
};
const isAdmin = currentProjectRole === 20;
const isAdmin = currentProjectRole === EUserWorkspaceRoles.ADMIN;
return (
<section className={`pr-9 py-8 w-full overflow-y-auto ${isAdmin ? "" : "opacity-60"}`}>

View File

@ -7,12 +7,23 @@ import { ProjectSettingHeader } from "components/headers";
import { EstimatesList } from "components/estimates";
// types
import { NextPageWithLayout } from "types/app";
import { useMobxStore } from "lib/mobx/store-provider";
import { EUserWorkspaceRoles } from "constants/workspace";
import { observer } from "mobx-react-lite";
const EstimatesSettingsPage: NextPageWithLayout = () => (
<div className="pr-9 py-8 w-full overflow-y-auto">
<EstimatesList />
</div>
);
const EstimatesSettingsPage: NextPageWithLayout = observer(() => {
const {
user: { currentProjectRole },
} = useMobxStore();
const isAdmin = currentProjectRole === EUserWorkspaceRoles.ADMIN;
return (
<div className={`pr-9 py-8 w-full overflow-y-auto ${isAdmin ? "" : "opacity-60 pointer-events-none"}`}>
<EstimatesList />
</div>
);
});
EstimatesSettingsPage.getLayout = function getLayout(page: ReactElement) {
return (

View File

@ -5,6 +5,7 @@ import { RootStore } from "../root";
import { InboxService } from "services/inbox.service";
// types
import { IInbox, IInboxFilterOptions, IInboxQueryParams } from "types";
import { EUserWorkspaceRoles } from "constants/workspace";
export interface IInboxFiltersStore {
// states
@ -132,8 +133,8 @@ export class InboxFiltersStore implements IInboxFiltersStore {
};
});
const userRole = this.rootStore.user?.projectMemberInfo?.[projectId]?.role || 0;
if (userRole > 10) {
const userRole = this.rootStore.user?.currentProjectRole || EUserWorkspaceRoles.GUEST;
if (userRole > EUserWorkspaceRoles.VIEWER) {
await this.inboxService.patchInbox(workspaceSlug, projectId, inboxId, { view_props: newViewProps });
}
} catch (error) {