diff --git a/web/core/components/issues/issue-layouts/calendar/base-calendar-root.tsx b/web/core/components/issues/issue-layouts/calendar/base-calendar-root.tsx index 1f6384a6fb..0fbcfbfe70 100644 --- a/web/core/components/issues/issue-layouts/calendar/base-calendar-root.tsx +++ b/web/core/components/issues/issue-layouts/calendar/base-calendar-root.tsx @@ -24,6 +24,8 @@ export type CalendarStoreType = | EIssuesStoreType.MODULE | EIssuesStoreType.CYCLE | EIssuesStoreType.PROJECT_VIEW + | EIssuesStoreType.TEAM + | EIssuesStoreType.TEAM_VIEW | EIssuesStoreType.EPIC; interface IBaseCalendarRoot { @@ -32,13 +34,14 @@ interface IBaseCalendarRoot { isCompletedCycle?: boolean; viewId?: string | undefined; isEpic?: boolean; + canEditPropertiesBasedOnProject?: (projectId: string) => boolean; } export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { - const { QuickActions, addIssuesToView, isCompletedCycle = false, viewId, isEpic = false } = props; + const { QuickActions, addIssuesToView, isCompletedCycle = false, viewId, isEpic = false, canEditPropertiesBasedOnProject } = props; // router - const { workspaceSlug, projectId } = useParams(); + const { workspaceSlug } = useParams(); // hooks const storeType = useIssueStoreType() as CalendarStoreType; @@ -63,6 +66,8 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { EUserPermissionsLevel.PROJECT ); + const { enableInlineEditing } = issues?.viewFlags || {}; + const displayFilters = issuesFilter.issueFilters?.displayFilters; const groupedIssueIds = (issues.groupedIssueIds ?? {}) as TGroupedIssues; @@ -71,9 +76,7 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { const { startDate, endDate } = issueCalendarView.getStartAndEndDate(layout) ?? {}; useEffect(() => { - startDate && - endDate && - layout && + if (startDate && endDate && layout) { fetchIssues( "init-loader", { @@ -85,21 +88,23 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { }, viewId ); + } }, [fetchIssues, storeType, startDate, endDate, layout, viewId]); const handleDragAndDrop = async ( issueId: string | undefined, + issueProjectId: string | undefined, sourceDate: string | undefined, destinationDate: string | undefined ) => { - if (!issueId || !destinationDate || !sourceDate) return; + if (!issueId || !destinationDate || !sourceDate || !issueProjectId) return; await handleDragDrop( issueId, sourceDate, destinationDate, workspaceSlug?.toString(), - projectId?.toString(), + issueProjectId, updateIssue ).catch((err) => { setToast({ @@ -127,6 +132,16 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { [issues?.getGroupIssueCount] ); + const canEditProperties = useCallback( + (projectId: string | undefined) => { + const isEditingAllowedBasedOnProject = + canEditPropertiesBasedOnProject && projectId ? canEditPropertiesBasedOnProject(projectId) : isEditingAllowed; + + return enableInlineEditing && isEditingAllowedBasedOnProject; + }, + [canEditPropertiesBasedOnProject, enableInlineEditing, isEditingAllowed] + ); + return ( <>
@@ -147,7 +162,7 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { handleRemoveFromView={async () => removeIssueFromView && removeIssueFromView(issue.project_id, issue.id)} handleArchive={async () => archiveIssue && archiveIssue(issue.project_id, issue.id)} handleRestore={async () => restoreIssue && restoreIssue(issue.project_id, issue.id)} - readOnly={!isEditingAllowed || isCompletedCycle} + readOnly={!canEditProperties(issue.project_id ?? undefined) || isCompletedCycle} placements={placement} /> )} @@ -156,9 +171,10 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { getGroupIssueCount={getGroupIssueCount} addIssuesToView={addIssuesToView} quickAddCallback={quickAddIssue} - readOnly={!isEditingAllowed || isCompletedCycle} + readOnly={isCompletedCycle} updateFilters={updateFilters} handleDragAndDrop={handleDragAndDrop} + canEditProperties={canEditProperties} isEpic={isEpic} />
diff --git a/web/core/components/issues/issue-layouts/calendar/calendar.tsx b/web/core/components/issues/issue-layouts/calendar/calendar.tsx index 52ef59f36a..0377f19c46 100644 --- a/web/core/components/issues/issue-layouts/calendar/calendar.tsx +++ b/web/core/components/issues/issue-layouts/calendar/calendar.tsx @@ -26,10 +26,10 @@ import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType } from "@/constan import { cn } from "@/helpers/common.helper"; import { renderFormattedPayloadDate } from "@/helpers/date-time.helper"; // hooks -import { useIssues, useUserPermissions } from "@/hooks/store"; +import { useIssues } from "@/hooks/store"; import useSize from "@/hooks/use-window-size"; -import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions"; // store +import { IProjectEpicsFilter } from "@/plane-web/store/issue/epic"; import { ICycleIssuesFilter } from "@/store/issue/cycle"; import { ICalendarStore } from "@/store/issue/issue_calendar_view.store"; import { IModuleIssuesFilter } from "@/store/issue/module"; @@ -38,7 +38,6 @@ import { IProjectViewIssuesFilter } from "@/store/issue/project-views"; import { IssueLayoutHOC } from "../issue-layout-HOC"; import { TRenderQuickActions } from "../list/list-view-types"; import type { ICalendarWeek } from "./types"; -import { IProjectEpicsFilter } from "@/plane-web/store/issue/epic"; type Props = { issuesFilterStore: @@ -59,6 +58,7 @@ type Props = { quickActions: TRenderQuickActions; handleDragAndDrop: ( issueId: string | undefined, + issueProjectId: string | undefined, sourceDate: string | undefined, destinationDate: string | undefined ) => Promise; @@ -69,6 +69,7 @@ type Props = { filterType: EIssueFilterType, filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters ) => Promise; + canEditProperties: (projectId: string | undefined) => boolean; isEpic?: boolean; }; @@ -88,6 +89,7 @@ export const CalendarChart: React.FC = observer((props) => { getPaginationData, getGroupIssueCount, updateFilters, + canEditProperties, readOnly = false, isEpic = false, } = props; @@ -99,15 +101,10 @@ export const CalendarChart: React.FC = observer((props) => { const { issues: { viewFlags }, } = useIssues(EIssuesStoreType.PROJECT); - const { allowPermissions } = useUserPermissions(); const [windowWidth] = useSize(); - const { enableIssueCreation } = viewFlags || {}; - const isEditingAllowed = allowPermissions( - [EUserPermissions.ADMIN, EUserPermissions.MEMBER], - EUserPermissionsLevel.PROJECT - ); + const { enableIssueCreation, enableQuickAdd } = viewFlags || {}; const calendarPayload = issueCalendarView.calendarPayload; @@ -171,12 +168,13 @@ export const CalendarChart: React.FC = observer((props) => { loadMoreIssues={loadMoreIssues} getPaginationData={getPaginationData} getGroupIssueCount={getGroupIssueCount} - enableQuickIssueCreate - disableIssueCreation={!enableIssueCreation || !isEditingAllowed} + enableQuickIssueCreate={enableQuickAdd} + disableIssueCreation={!enableIssueCreation} quickActions={quickActions} quickAddCallback={quickAddCallback} addIssuesToView={addIssuesToView} readOnly={readOnly} + canEditProperties={canEditProperties} isEpic={isEpic} /> ))} @@ -194,12 +192,13 @@ export const CalendarChart: React.FC = observer((props) => { loadMoreIssues={loadMoreIssues} getPaginationData={getPaginationData} getGroupIssueCount={getGroupIssueCount} - enableQuickIssueCreate - disableIssueCreation={!enableIssueCreation || !isEditingAllowed} + enableQuickIssueCreate={enableQuickAdd} + disableIssueCreation={!enableIssueCreation} quickActions={quickActions} quickAddCallback={quickAddCallback} addIssuesToView={addIssuesToView} readOnly={readOnly} + canEditProperties={canEditProperties} isEpic={isEpic} /> )} @@ -219,11 +218,12 @@ export const CalendarChart: React.FC = observer((props) => { getPaginationData={getPaginationData} getGroupIssueCount={getGroupIssueCount} quickActions={quickActions} - enableQuickIssueCreate - disableIssueCreation={!enableIssueCreation || !isEditingAllowed} + enableQuickIssueCreate={enableQuickAdd} + disableIssueCreation={!enableIssueCreation} quickAddCallback={quickAddCallback} addIssuesToView={addIssuesToView} readOnly={readOnly} + canEditProperties={canEditProperties} isDragDisabled isMobileView isEpic={isEpic} @@ -246,11 +246,12 @@ export const CalendarChart: React.FC = observer((props) => { loadMoreIssues={loadMoreIssues} getPaginationData={getPaginationData} getGroupIssueCount={getGroupIssueCount} - enableQuickIssueCreate - disableIssueCreation={!enableIssueCreation || !isEditingAllowed} + enableQuickIssueCreate={enableQuickAdd} + disableIssueCreation={!enableIssueCreation} quickAddCallback={quickAddCallback} addIssuesToView={addIssuesToView} readOnly={readOnly} + canEditProperties={canEditProperties} isDragDisabled isMobileView isEpic={isEpic} diff --git a/web/core/components/issues/issue-layouts/calendar/day-tile.tsx b/web/core/components/issues/issue-layouts/calendar/day-tile.tsx index 7708db3ba3..405cbf2ac2 100644 --- a/web/core/components/issues/issue-layouts/calendar/day-tile.tsx +++ b/web/core/components/issues/issue-layouts/calendar/day-tile.tsx @@ -18,12 +18,12 @@ import { MONTHS_LIST } from "@/constants/calendar"; import { cn } from "@/helpers/common.helper"; import { renderFormattedPayloadDate } from "@/helpers/date-time.helper"; // types +import { IProjectEpicsFilter } from "@/plane-web/store/issue/epic"; import { ICycleIssuesFilter } from "@/store/issue/cycle"; import { IModuleIssuesFilter } from "@/store/issue/module"; import { IProjectIssuesFilter } from "@/store/issue/project"; import { IProjectViewIssuesFilter } from "@/store/issue/project-views"; import { TRenderQuickActions } from "../list/list-view-types"; -import { IProjectEpicsFilter } from "@/plane-web/store/issue/epic"; type Props = { issuesFilterStore: @@ -44,6 +44,7 @@ type Props = { quickActions: TRenderQuickActions; handleDragAndDrop: ( issueId: string | undefined, + issueProjectId: string | undefined, sourceDate: string | undefined, destinationDate: string | undefined ) => Promise; @@ -51,6 +52,7 @@ type Props = { readOnly?: boolean; selectedDate: Date; setSelectedDate: (date: Date) => void; + canEditProperties: (projectId: string | undefined) => boolean; isEpic?: boolean; }; @@ -72,6 +74,7 @@ export const CalendarDayTile: React.FC = observer((props) => { selectedDate, handleDragAndDrop, setSelectedDate, + canEditProperties, isEpic = false, } = props; @@ -119,7 +122,12 @@ export const CalendarDayTile: React.FC = observer((props) => { } } - handleDragAndDrop(sourceData?.id, sourceData?.date, destinationData?.date); + handleDragAndDrop( + sourceData?.id, + issueDetails?.project_id ?? undefined, + sourceData?.date, + destinationData?.date + ); highlightIssueOnDrop(source?.element?.id, false); }, }) @@ -184,6 +192,7 @@ export const CalendarDayTile: React.FC = observer((props) => { enableQuickIssueCreate={enableQuickIssueCreate} quickAddCallback={quickAddCallback} readOnly={readOnly} + canEditProperties={canEditProperties} isEpic={isEpic} /> diff --git a/web/core/components/issues/issue-layouts/calendar/issue-block-root.tsx b/web/core/components/issues/issue-layouts/calendar/issue-block-root.tsx index 08154f2e70..2ca6a8f8ea 100644 --- a/web/core/components/issues/issue-layouts/calendar/issue-block-root.tsx +++ b/web/core/components/issues/issue-layouts/calendar/issue-block-root.tsx @@ -16,10 +16,11 @@ type Props = { quickActions: TRenderQuickActions; isDragDisabled: boolean; isEpic?: boolean; + canEditProperties: (projectId: string | undefined) => boolean; }; export const CalendarIssueBlockRoot: React.FC = observer((props) => { - const { issueId, quickActions, isDragDisabled, isEpic = false } = props; + const { issueId, quickActions, isDragDisabled, isEpic = false, canEditProperties } = props; const issueRef = useRef(null); const [isDragging, setIsDragging] = useState(false); @@ -30,6 +31,8 @@ export const CalendarIssueBlockRoot: React.FC = observer((props) => { const issue = getIssueById(issueId); + const canDrag = !isDragDisabled && canEditProperties(issue?.project_id ?? undefined); + useEffect(() => { const element = issueRef.current; @@ -38,7 +41,7 @@ export const CalendarIssueBlockRoot: React.FC = observer((props) => { return combine( draggable({ element, - canDrag: () => !isDragDisabled, + canDrag: () => canDrag, getInitialData: () => ({ id: issue?.id, date: issue?.target_date }), onDragStart: () => { setIsDragging(true); @@ -48,7 +51,7 @@ export const CalendarIssueBlockRoot: React.FC = observer((props) => { }, }) ); - }, [issueRef?.current, issue]); + }, [issueRef?.current, issue, canDrag]); useOutsideClickDetector(issueRef, () => { issueRef?.current?.classList?.remove(HIGHLIGHT_CLASS); diff --git a/web/core/components/issues/issue-layouts/calendar/issue-blocks.tsx b/web/core/components/issues/issue-layouts/calendar/issue-blocks.tsx index 03fc98a363..674819bbf7 100644 --- a/web/core/components/issues/issue-layouts/calendar/issue-blocks.tsx +++ b/web/core/components/issues/issue-layouts/calendar/issue-blocks.tsx @@ -22,6 +22,7 @@ type Props = { addIssuesToView?: (issueIds: string[]) => Promise; readOnly?: boolean; isMobileView?: boolean; + canEditProperties: (projectId: string | undefined) => boolean; isEpic?: boolean; }; @@ -38,6 +39,7 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { addIssuesToView, readOnly, isMobileView = false, + canEditProperties, isEpic = false, } = props; const formattedDatePayload = renderFormattedPayloadDate(date); @@ -65,6 +67,7 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { issueId={issueId} quickActions={quickActions} isDragDisabled={isDragDisabled || isMobileView} + canEditProperties={canEditProperties} isEpic={isEpic} /> diff --git a/web/core/components/issues/issue-layouts/calendar/roots/project-root.tsx b/web/core/components/issues/issue-layouts/calendar/roots/project-root.tsx index cad7336dea..972488e1f2 100644 --- a/web/core/components/issues/issue-layouts/calendar/roots/project-root.tsx +++ b/web/core/components/issues/issue-layouts/calendar/roots/project-root.tsx @@ -1,7 +1,32 @@ import { observer } from "mobx-react"; +import { useParams } from "next/navigation"; // hooks import { ProjectIssueQuickActions } from "@/components/issues"; +// hooks +import { useUserPermissions } from "@/hooks/store"; +// plane web constants +import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions"; // components import { BaseCalendarRoot } from "../base-calendar-root"; -export const CalendarLayout: React.FC = observer(() => ); +export const CalendarLayout: React.FC = observer(() => { + // router + const { workspaceSlug } = useParams(); + // hooks + const { allowPermissions } = useUserPermissions(); + // derived values + const canEditPropertiesBasedOnProject = (projectId: string) => + allowPermissions( + [EUserPermissions.ADMIN, EUserPermissions.MEMBER], + EUserPermissionsLevel.PROJECT, + workspaceSlug?.toString(), + projectId + ); + + return ( + + ); +}); diff --git a/web/core/components/issues/issue-layouts/calendar/week-days.tsx b/web/core/components/issues/issue-layouts/calendar/week-days.tsx index 1a04966f1a..9aaa132294 100644 --- a/web/core/components/issues/issue-layouts/calendar/week-days.tsx +++ b/web/core/components/issues/issue-layouts/calendar/week-days.tsx @@ -5,13 +5,13 @@ import { CalendarDayTile } from "@/components/issues"; // helpers import { renderFormattedPayloadDate } from "@/helpers/date-time.helper"; // types +import { IProjectEpicsFilter } from "@/plane-web/store/issue/epic"; import { ICycleIssuesFilter } from "@/store/issue/cycle"; import { IModuleIssuesFilter } from "@/store/issue/module"; import { IProjectIssuesFilter } from "@/store/issue/project"; import { IProjectViewIssuesFilter } from "@/store/issue/project-views"; import { TRenderQuickActions } from "../list/list-view-types"; import { ICalendarDate, ICalendarWeek } from "./types"; -import { IProjectEpicsFilter } from "@/plane-web/store/issue/epic"; type Props = { issuesFilterStore: @@ -32,6 +32,7 @@ type Props = { quickAddCallback?: (projectId: string | null | undefined, data: TIssue) => Promise; handleDragAndDrop: ( issueId: string | undefined, + issueProjectId: string | undefined, sourceDate: string | undefined, destinationDate: string | undefined ) => Promise; @@ -39,6 +40,7 @@ type Props = { readOnly?: boolean; selectedDate: Date; setSelectedDate: (date: Date) => void; + canEditProperties: (projectId: string | undefined) => boolean; isEpic?: boolean; }; @@ -60,6 +62,7 @@ export const CalendarWeekDays: React.FC = observer((props) => { readOnly = false, selectedDate, setSelectedDate, + canEditProperties, isEpic = false, } = props; @@ -96,6 +99,7 @@ export const CalendarWeekDays: React.FC = observer((props) => { addIssuesToView={addIssuesToView} readOnly={readOnly} handleDragAndDrop={handleDragAndDrop} + canEditProperties={canEditProperties} isEpic={isEpic} /> ); diff --git a/web/core/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx b/web/core/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx index 268358efea..8d6ed598bb 100644 --- a/web/core/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx +++ b/web/core/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx @@ -134,6 +134,13 @@ export const AppliedFiltersList: React.FC = observer((props) => { values={value} /> )} + {filterKey === "team_project" && ( + handleRemoveFilter("team_project", val)} + values={value} + /> + )} {isEditingAllowed && (