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 288a3a2ec51..2068a0921e0 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 @@ -23,20 +23,23 @@ export type CalendarStoreType = | EIssuesStoreType.PROJECT | EIssuesStoreType.MODULE | EIssuesStoreType.CYCLE - | EIssuesStoreType.PROJECT_VIEW; + | EIssuesStoreType.PROJECT_VIEW + | EIssuesStoreType.TEAM + | EIssuesStoreType.TEAM_VIEW; interface IBaseCalendarRoot { QuickActions: FC; addIssuesToView?: (issueIds: string[]) => Promise; isCompletedCycle?: boolean; viewId?: string | undefined; + canEditPropertiesBasedOnProject?: (projectId: string) => boolean; } export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { - const { QuickActions, addIssuesToView, isCompletedCycle = false, viewId } = props; + const { QuickActions, addIssuesToView, isCompletedCycle = false, viewId, canEditPropertiesBasedOnProject } = props; // router - const { workspaceSlug, projectId } = useParams(); + const { workspaceSlug } = useParams(); // hooks const storeType = useIssueStoreType() as CalendarStoreType; @@ -61,6 +64,8 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { EUserPermissionsLevel.PROJECT ); + const { enableInlineEditing } = issues?.viewFlags || {}; + const displayFilters = issuesFilter.issueFilters?.displayFilters; const groupedIssueIds = (issues.groupedIssueIds ?? {}) as TGroupedIssues; @@ -69,9 +74,7 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { const { startDate, endDate } = issueCalendarView.getStartAndEndDate(layout) ?? {}; useEffect(() => { - startDate && - endDate && - layout && + if (startDate && endDate && layout) { fetchIssues( "init-loader", { @@ -83,21 +86,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({ @@ -125,6 +130,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 ( <>
@@ -145,7 +160,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} /> )} @@ -154,9 +169,10 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { getGroupIssueCount={getGroupIssueCount} addIssuesToView={addIssuesToView} quickAddCallback={quickAddIssue} - readOnly={!isEditingAllowed || isCompletedCycle} + readOnly={isCompletedCycle} updateFilters={updateFilters} handleDragAndDrop={handleDragAndDrop} + canEditProperties={canEditProperties} />
diff --git a/web/core/components/issues/issue-layouts/calendar/calendar.tsx b/web/core/components/issues/issue-layouts/calendar/calendar.tsx index 9bac26879e1..87ed664454f 100644 --- a/web/core/components/issues/issue-layouts/calendar/calendar.tsx +++ b/web/core/components/issues/issue-layouts/calendar/calendar.tsx @@ -26,9 +26,8 @@ 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 { ICycleIssuesFilter } from "@/store/issue/cycle"; import { ICalendarStore } from "@/store/issue/issue_calendar_view.store"; @@ -53,6 +52,7 @@ type Props = { quickActions: TRenderQuickActions; handleDragAndDrop: ( issueId: string | undefined, + issueProjectId: string | undefined, sourceDate: string | undefined, destinationDate: string | undefined ) => Promise; @@ -63,6 +63,7 @@ type Props = { filterType: EIssueFilterType, filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters ) => Promise; + canEditProperties: (projectId: string | undefined) => boolean; }; export const CalendarChart: React.FC = observer((props) => { @@ -81,6 +82,7 @@ export const CalendarChart: React.FC = observer((props) => { getPaginationData, getGroupIssueCount, updateFilters, + canEditProperties, readOnly = false, } = props; // states @@ -91,15 +93,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; @@ -163,12 +160,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} /> ))} @@ -185,12 +183,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} /> )} @@ -209,11 +208,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 /> @@ -235,11 +235,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 /> 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 54a638480d4..2392425caca 100644 --- a/web/core/components/issues/issue-layouts/calendar/day-tile.tsx +++ b/web/core/components/issues/issue-layouts/calendar/day-tile.tsx @@ -38,6 +38,7 @@ type Props = { quickActions: TRenderQuickActions; handleDragAndDrop: ( issueId: string | undefined, + issueProjectId: string | undefined, sourceDate: string | undefined, destinationDate: string | undefined ) => Promise; @@ -45,6 +46,7 @@ type Props = { readOnly?: boolean; selectedDate: Date; setSelectedDate: (date: Date) => void; + canEditProperties: (projectId: string | undefined) => boolean; }; export const CalendarDayTile: React.FC = observer((props) => { @@ -65,6 +67,7 @@ export const CalendarDayTile: React.FC = observer((props) => { selectedDate, handleDragAndDrop, setSelectedDate, + canEditProperties, } = props; const [isDraggingOver, setIsDraggingOver] = useState(false); @@ -111,7 +114,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); }, }) @@ -176,6 +184,7 @@ export const CalendarDayTile: React.FC = observer((props) => { enableQuickIssueCreate={enableQuickIssueCreate} quickAddCallback={quickAddCallback} readOnly={readOnly} + canEditProperties={canEditProperties} /> 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 6e63d6d7973..35bea6386e5 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 @@ -15,10 +15,11 @@ type Props = { issueId: string; quickActions: TRenderQuickActions; isDragDisabled: boolean; + canEditProperties: (projectId: string | undefined) => boolean; }; export const CalendarIssueBlockRoot: React.FC = observer((props) => { - const { issueId, quickActions, isDragDisabled } = props; + const { issueId, quickActions, isDragDisabled, canEditProperties } = props; const issueRef = useRef(null); const [isDragging, setIsDragging] = useState(false); @@ -29,6 +30,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; @@ -37,7 +40,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); @@ -47,7 +50,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 8860fcb6adb..a7811a40619 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; }; export const CalendarIssueBlocks: React.FC = observer((props) => { @@ -37,6 +38,7 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { addIssuesToView, readOnly, isMobileView = false, + canEditProperties, } = props; const formattedDatePayload = renderFormattedPayloadDate(date); @@ -63,6 +65,7 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { issueId={issueId} quickActions={quickActions} isDragDisabled={isDragDisabled || isMobileView} + canEditProperties={canEditProperties} /> ))} 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 cad7336dea1..972488e1f2f 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 75f652f948d..1c413db35d8 100644 --- a/web/core/components/issues/issue-layouts/calendar/week-days.tsx +++ b/web/core/components/issues/issue-layouts/calendar/week-days.tsx @@ -26,6 +26,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; @@ -33,6 +34,7 @@ type Props = { readOnly?: boolean; selectedDate: Date; setSelectedDate: (date: Date) => void; + canEditProperties: (projectId: string | undefined) => boolean; }; export const CalendarWeekDays: React.FC = observer((props) => { @@ -53,6 +55,7 @@ export const CalendarWeekDays: React.FC = observer((props) => { readOnly = false, selectedDate, setSelectedDate, + canEditProperties, } = props; const calendarLayout = issuesFilterStore?.issueFilters?.displayFilters?.calendar?.layout ?? "month"; @@ -88,6 +91,7 @@ export const CalendarWeekDays: React.FC = observer((props) => { addIssuesToView={addIssuesToView} readOnly={readOnly} handleDragAndDrop={handleDragAndDrop} + canEditProperties={canEditProperties} /> ); })} 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 268358efea2..8d6ed598bb0 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 && (