SECURITY - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: ограничение создания проектов
This commit is contained in:
@@ -45,7 +45,7 @@ function AnalyticsPage({ params }: Route.ComponentProps) {
|
||||
|
||||
// permissions
|
||||
const canPerformEmptyStateActions = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ export const ExtendedProjectSidebar = observer(function ExtendedProjectSidebar()
|
||||
|
||||
// auth
|
||||
const isAuthorizedUser = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
|
||||
+20
-14
@@ -9,6 +9,7 @@
|
||||
import { Menu } from "@headlessui/react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { PlusIcon, ProjectIcon } from "@plane/propel/icons";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
@@ -16,6 +17,7 @@ import { cn, copyUrlToClipboard } from "@plane/utils";
|
||||
// hooks
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
// components
|
||||
import { SidebarProjectsListItem } from "@/components/workspace/sidebar/projects-list-item";
|
||||
|
||||
@@ -29,6 +31,8 @@ export const ProjectsToolbarMenu = observer(function ProjectsToolbarMenu({
|
||||
const { workspaceSlug } = useParams();
|
||||
const { joinedProjectIds } = useProject();
|
||||
const { toggleCreateProjectModal } = useCommandPalette();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const canCreateProject = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
|
||||
const handleCopyText = (projectId: string) =>
|
||||
copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/issues`).then(() => {
|
||||
@@ -81,20 +85,22 @@ export const ProjectsToolbarMenu = observer(function ProjectsToolbarMenu({
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-2 border-t border-white/8 px-1 pt-2">
|
||||
<Menu.Item>
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center gap-3 rounded-md px-2 py-2 text-left text-13 font-medium text-secondary transition-colors hover:bg-layer-transparent-hover hover:text-primary"
|
||||
onClick={() => toggleCreateProjectModal(true)}
|
||||
>
|
||||
<span className="grid size-8 flex-shrink-0 place-items-center">
|
||||
<PlusIcon className="size-4" />
|
||||
</span>
|
||||
<span>{t("create_project")}</span>
|
||||
</button>
|
||||
</Menu.Item>
|
||||
</div>
|
||||
{canCreateProject && (
|
||||
<div className="mt-2 border-t border-white/8 px-1 pt-2">
|
||||
<Menu.Item>
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center gap-3 rounded-md px-2 py-2 text-left text-13 font-medium text-secondary transition-colors hover:bg-layer-transparent-hover hover:text-primary"
|
||||
onClick={() => toggleCreateProjectModal(true)}
|
||||
>
|
||||
<span className="grid size-8 flex-shrink-0 place-items-center">
|
||||
<PlusIcon className="size-4" />
|
||||
</span>
|
||||
<span>{t("create_project")}</span>
|
||||
</button>
|
||||
</Menu.Item>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Menu>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useTheme } from "next-themes";
|
||||
// plane imports
|
||||
import { PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EUserPermissions, EUserPermissionsLevel, PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { Button, getButtonStyling } from "@plane/propel/button";
|
||||
import { cn } from "@plane/utils";
|
||||
// assets
|
||||
@@ -16,11 +16,14 @@ import ProjectDarkEmptyState from "@/app/assets/empty-state/project-settings/no-
|
||||
import ProjectLightEmptyState from "@/app/assets/empty-state/project-settings/no-projects-light.png?url";
|
||||
// hooks
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
|
||||
function ProjectSettingsPage() {
|
||||
// store hooks
|
||||
const { resolvedTheme } = useTheme();
|
||||
const { toggleCreateProjectModal } = useCommandPalette();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const canCreateProject = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
// derived values
|
||||
const resolvedPath = resolvedTheme === "dark" ? ProjectDarkEmptyState : ProjectLightEmptyState;
|
||||
return (
|
||||
@@ -35,12 +38,14 @@ function ProjectSettingsPage() {
|
||||
<Link href="https://plane.so/" target="_blank" className={cn(getButtonStyling("secondary", "base"))}>
|
||||
Learn more about projects
|
||||
</Link>
|
||||
<Button
|
||||
onClick={() => toggleCreateProjectModal(true)}
|
||||
data-ph-element={PROJECT_TRACKER_ELEMENTS.EMPTY_STATE_CREATE_PROJECT_BUTTON}
|
||||
>
|
||||
Start your first project
|
||||
</Button>
|
||||
{canCreateProject && (
|
||||
<Button
|
||||
onClick={() => toggleCreateProjectModal(true)}
|
||||
data-ph-element={PROJECT_TRACKER_ELEMENTS.EMPTY_STATE_CREATE_PROJECT_BUTTON}
|
||||
>
|
||||
Start your first project
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -42,7 +42,7 @@ export const NoProjectsEmptyState = observer(function NoProjectsEmptyState() {
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const canCreateProject = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
const isWorkspaceAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@ export const GlobalViewEmptyState = observer(function GlobalViewEmptyState() {
|
||||
const { toggleCreateIssueModal, toggleCreateProjectModal } = useCommandPalette();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// derived values
|
||||
const canCreateProject = allowPermissions([EUserWorkspaceRoles.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
const hasMemberLevelPermission = allowPermissions(
|
||||
[EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
@@ -41,7 +42,7 @@ export const GlobalViewEmptyState = observer(function GlobalViewEmptyState() {
|
||||
onClick: () => {
|
||||
toggleCreateProjectModal(true);
|
||||
},
|
||||
disabled: !hasMemberLevelPermission,
|
||||
disabled: !canCreateProject,
|
||||
variant: "primary",
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -39,10 +39,7 @@ export const WorkspaceDraftIssuesRoot = observer(function WorkspaceDraftIssuesRo
|
||||
const { toggleCreateProjectModal } = useCommandPalette();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// derived values
|
||||
const hasMemberLevelPermission = allowPermissions(
|
||||
[EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
const canCreateProject = allowPermissions([EUserWorkspaceRoles.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
|
||||
//swr hook for fetching issue properties
|
||||
useWorkspaceIssueProperties(workspaceSlug);
|
||||
@@ -77,7 +74,7 @@ export const WorkspaceDraftIssuesRoot = observer(function WorkspaceDraftIssuesRo
|
||||
onClick: () => {
|
||||
toggleCreateProjectModal(true);
|
||||
},
|
||||
disabled: !hasMemberLevelPermission,
|
||||
disabled: !canCreateProject,
|
||||
variant: "primary",
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -48,7 +48,7 @@ export const usePowerKCreationCommandsRecord = (): Record<TPowerKCreationCommand
|
||||
// derived values
|
||||
const canCreateWorkItem = canPerformAnyCreateAction && workspaceProjectIds && workspaceProjectIds.length > 0;
|
||||
const canCreateProject = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
const hasProjectMemberLevelPermissions = (ctx: TPowerKContext) =>
|
||||
|
||||
@@ -48,7 +48,7 @@ export const ProjectCardList = observer(function ProjectCardList(props: TProject
|
||||
|
||||
// permissions
|
||||
const canPerformEmptyStateActions = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
import { getAssetIdFromUrl, checkURLValidity } from "@plane/utils";
|
||||
// plane ui
|
||||
// helpers
|
||||
// hooks
|
||||
import useKeypress from "@/hooks/use-keypress";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
// plane web components
|
||||
import { CreateProjectForm } from "@/plane-web/components/projects/create/root";
|
||||
// plane web types
|
||||
@@ -36,9 +38,11 @@ enum EProjectCreationSteps {
|
||||
|
||||
export function CreateProjectModal(props: Props) {
|
||||
const { isOpen, onClose, setToFavorite = false, workspaceSlug, data, templateId } = props;
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// states
|
||||
const [currentStep, setCurrentStep] = useState<EProjectCreationSteps>(EProjectCreationSteps.CREATE_PROJECT);
|
||||
const [createdProjectId, setCreatedProjectId] = useState<string | null>(null);
|
||||
const canCreateProject = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE, workspaceSlug);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
@@ -47,6 +51,10 @@ export function CreateProjectModal(props: Props) {
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && !canCreateProject) onClose();
|
||||
}, [canCreateProject, isOpen, onClose]);
|
||||
|
||||
const handleNextStep = (projectId: string) => {
|
||||
if (!projectId) return;
|
||||
setCreatedProjectId(projectId);
|
||||
@@ -65,6 +73,8 @@ export function CreateProjectModal(props: Props) {
|
||||
if (isOpen) onClose();
|
||||
});
|
||||
|
||||
if (!canCreateProject) return null;
|
||||
|
||||
return (
|
||||
<ModalCore
|
||||
isOpen={isOpen}
|
||||
|
||||
@@ -33,7 +33,7 @@ export const ProjectsBaseHeader = observer(function ProjectsBaseHeader() {
|
||||
const pathname = usePathname();
|
||||
// auth
|
||||
const isAuthorizedUser = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
const isArchived = pathname.includes("/archives");
|
||||
|
||||
@@ -55,7 +55,7 @@ export const SidebarProjectsList = observer(function SidebarProjectsList() {
|
||||
|
||||
// auth
|
||||
const isAuthorizedUser = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user