From 0e3d5cc4ebff6cb9d5ead4dffc0a878186c7e136 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 29 Apr 2024 00:50:15 +0530 Subject: [PATCH] fix: incomplete cycle issues auth (#4299) --- web/components/cycles/sidebar.tsx | 2 +- .../empty-states/project-view.tsx | 35 ++++++++++++------- .../issue-layouts/kanban/roots/cycle-root.tsx | 18 +++++++--- .../issue-layouts/list/roots/cycle-root.tsx | 22 ++++++++---- .../spreadsheet/roots/cycle-root.tsx | 19 +++++++--- web/components/modules/sidebar.tsx | 4 +-- .../pages/editor/header/options-dropdown.tsx | 20 +++++++++-- 7 files changed, 87 insertions(+), 33 deletions(-) diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index 9ada6cf63..261dc72f7 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -416,7 +416,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { to: "End date", }} required={cycleDetails.status !== "draft"} - disabled={isArchived} + disabled={!isEditingAllowed || isArchived} /> )} /> diff --git a/web/components/issues/issue-layouts/empty-states/project-view.tsx b/web/components/issues/issue-layouts/empty-states/project-view.tsx index fc4bc6ea9..2939c524f 100644 --- a/web/components/issues/issue-layouts/empty-states/project-view.tsx +++ b/web/components/issues/issue-layouts/empty-states/project-view.tsx @@ -1,10 +1,12 @@ import { observer } from "mobx-react-lite"; import { PlusIcon } from "lucide-react"; -// hooks -import { EmptyState } from "@/components/common"; -import { EIssuesStoreType } from "@/constants/issue"; -import { useApplication, useEventTracker } from "@/hooks/store"; // components +import { EmptyState } from "@/components/common"; +// constants +import { EIssuesStoreType } from "@/constants/issue"; +import { EUserProjectRoles } from "@/constants/project"; +// hooks +import { useApplication, useEventTracker, useUser } from "@/hooks/store"; // assets import emptyIssue from "public/empty-state/issue.svg"; @@ -12,6 +14,11 @@ export const ProjectViewEmptyState: React.FC = observer(() => { // store hooks const { commandPalette: commandPaletteStore } = useApplication(); const { setTrackElement } = useEventTracker(); + const { + membership: { currentProjectRole }, + } = useUser(); + // auth + const isCreatingIssueAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; return (
@@ -19,14 +26,18 @@ export const ProjectViewEmptyState: React.FC = observer(() => { title="View issues will appear here" description="Issues help you track individual pieces of work. With Issues, keep track of what's going on, who is working on it, and what's done." image={emptyIssue} - primaryButton={{ - text: "New issue", - icon: , - onClick: () => { - setTrackElement("View issue empty state"); - commandPaletteStore.toggleCreateIssueModal(true, EIssuesStoreType.PROJECT_VIEW); - }, - }} + primaryButton={ + isCreatingIssueAllowed + ? { + text: "New issue", + icon: , + onClick: () => { + setTrackElement("View issue empty state"); + commandPaletteStore.toggleCreateIssueModal(true, EIssuesStoreType.PROJECT_VIEW); + }, + } + : undefined + } />
); diff --git a/web/components/issues/issue-layouts/kanban/roots/cycle-root.tsx b/web/components/issues/issue-layouts/kanban/roots/cycle-root.tsx index be61dbbda..956e0d1d1 100644 --- a/web/components/issues/issue-layouts/kanban/roots/cycle-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/cycle-root.tsx @@ -1,12 +1,13 @@ import React, { useCallback } from "react"; import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; -// hooks +// components import { CycleIssueQuickActions } from "@/components/issues"; +// constants import { EIssuesStoreType } from "@/constants/issue"; -import { useCycle, useIssues } from "@/hooks/store"; -// ui -// types +import { EUserProjectRoles } from "@/constants/project"; +// hooks +import { useCycle, useIssues, useUser } from "@/hooks/store"; // components import { BaseKanBanRoot } from "../base-kanban-root"; @@ -19,11 +20,18 @@ export const CycleKanBanLayout: React.FC = observer(() => { // store const { issues } = useIssues(EIssuesStoreType.CYCLE); const { currentProjectCompletedCycleIds } = useCycle(); + const { + membership: { currentProjectRole }, + } = useUser(); const isCompletedCycle = cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false; + const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; - const canEditIssueProperties = useCallback(() => !isCompletedCycle, [isCompletedCycle]); + const canEditIssueProperties = useCallback( + () => !isCompletedCycle && isEditingAllowed, + [isCompletedCycle, isEditingAllowed] + ); const addIssuesToView = useCallback( (issueIds: string[]) => { diff --git a/web/components/issues/issue-layouts/list/roots/cycle-root.tsx b/web/components/issues/issue-layouts/list/roots/cycle-root.tsx index 863921e7d..54ebd8c4b 100644 --- a/web/components/issues/issue-layouts/list/roots/cycle-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/cycle-root.tsx @@ -1,13 +1,14 @@ import React, { useCallback } from "react"; import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; -// hooks -import { CycleIssueQuickActions } from "@/components/issues"; -import { EIssuesStoreType } from "@/constants/issue"; -import { useCycle, useIssues } from "@/hooks/store"; // components -// types +import { CycleIssueQuickActions } from "@/components/issues"; // constants +import { EIssuesStoreType } from "@/constants/issue"; +import { EUserProjectRoles } from "@/constants/project"; +// hooks +import { useCycle, useIssues, useUser } from "@/hooks/store"; +// types import { BaseListRoot } from "../base-list-root"; export interface ICycleListLayout {} @@ -17,12 +18,19 @@ export const CycleListLayout: React.FC = observer(() => { const { workspaceSlug, projectId, cycleId } = router.query; // store const { issues } = useIssues(EIssuesStoreType.CYCLE); - const { currentProjectCompletedCycleIds } = useCycle(); + const { currentProjectCompletedCycleIds } = useCycle(); // mobx store + const { + membership: { currentProjectRole }, + } = useUser(); const isCompletedCycle = cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false; + const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; - const canEditIssueProperties = useCallback(() => !isCompletedCycle, [isCompletedCycle]); + const canEditIssueProperties = useCallback( + () => !isCompletedCycle && isEditingAllowed, + [isCompletedCycle, isEditingAllowed] + ); const addIssuesToView = useCallback( (issueIds: string[]) => { diff --git a/web/components/issues/issue-layouts/spreadsheet/roots/cycle-root.tsx b/web/components/issues/issue-layouts/spreadsheet/roots/cycle-root.tsx index c00c0d919..f73a3cb3f 100644 --- a/web/components/issues/issue-layouts/spreadsheet/roots/cycle-root.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/roots/cycle-root.tsx @@ -1,22 +1,33 @@ import React, { useCallback } from "react"; import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; -// mobx store +// constants import { EIssuesStoreType } from "@/constants/issue"; -import { useCycle } from "@/hooks/store"; +import { EUserProjectRoles } from "@/constants/project"; +// hooks +import { useCycle, useUser } from "@/hooks/store"; // components import { CycleIssueQuickActions } from "../../quick-action-dropdowns"; import { BaseSpreadsheetRoot } from "../base-spreadsheet-root"; export const CycleSpreadsheetLayout: React.FC = observer(() => { + // router const router = useRouter(); const { cycleId } = router.query; + // store hooks const { currentProjectCompletedCycleIds } = useCycle(); - + const { + membership: { currentProjectRole }, + } = useUser(); + // auth const isCompletedCycle = cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false; + const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; - const canEditIssueProperties = useCallback(() => !isCompletedCycle, [isCompletedCycle]); + const canEditIssueProperties = useCallback( + () => !isCompletedCycle && isEditingAllowed, + [isCompletedCycle, isEditingAllowed] + ); if (!cycleId) return null; diff --git a/web/components/modules/sidebar.tsx b/web/components/modules/sidebar.tsx index 91d69c2e1..a4585956e 100644 --- a/web/components/modules/sidebar.tsx +++ b/web/components/modules/sidebar.tsx @@ -480,7 +480,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { from: "Start date", to: "Target date", }} - disabled={isArchived} + disabled={!isEditingAllowed || isArchived} /> ); }} @@ -510,7 +510,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { multiple={false} buttonVariant="background-with-text" placeholder="Lead" - disabled={isArchived} + disabled={!isEditingAllowed || isArchived} /> )} diff --git a/web/components/pages/editor/header/options-dropdown.tsx b/web/components/pages/editor/header/options-dropdown.tsx index d2691175a..8158438d1 100644 --- a/web/components/pages/editor/header/options-dropdown.tsx +++ b/web/components/pages/editor/header/options-dropdown.tsx @@ -4,10 +4,13 @@ import { ArchiveRestoreIcon, Clipboard, Copy, Link, Lock, LockOpen } from "lucid import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/document-editor"; // ui import { ArchiveIcon, CustomMenu, TOAST_TYPE, ToggleSwitch, setToast } from "@plane/ui"; +// constants +import { EUserProjectRoles } from "@/constants/project"; // helpers +import { cn } from "@/helpers/common.helper"; import { copyTextToClipboard, copyUrlToClipboard } from "@/helpers/string.helper"; // hooks -import { useApplication } from "@/hooks/store"; +import { useApplication, useUser } from "@/hooks/store"; // store import { IPageStore } from "@/store/pages/page.store"; @@ -38,6 +41,11 @@ export const PageOptionsDropdown: React.FC = observer((props) => { const { router: { workspaceSlug, projectId }, } = useApplication(); + const { + membership: { currentProjectRole }, + } = useUser(); + // auth + const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; const handleArchivePage = async () => await archive().catch(() => @@ -146,9 +154,17 @@ export const PageOptionsDropdown: React.FC = observer((props) => { full_width: !view_props?.full_width, }) } + disabled={!isEditingAllowed} > Full width - {}} /> + {}} + className={cn({ + "opacity-40": !isEditingAllowed, + })} + disabled={!isEditingAllowed} + /> {MENU_ITEMS.map((item) => { if (!item.shouldRender) return null;