Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apiserver/plane/app/views/issue/relation.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ def create(self, request, slug, project_id, issue_id):
origin=request.META.get("HTTP_ORIGIN"),
)

if relation_type == "blocking":
if relation_type in ["blocking", "start_after", "finish_after"]:
return Response(
RelatedIssueSerializer(issue_relation, many=True).data,
status=status.HTTP_201_CREATED,
Expand All @@ -333,7 +333,7 @@ def remove_relation(self, request, slug, project_id, issue_id):
relation_type = request.data.get("relation_type", None)
related_issue = request.data.get("related_issue", None)

if relation_type == "blocking":
if relation_type in ["blocking", "start_after", "finish_after"]:
issue_relation = IssueRelation.objects.get(
workspace__slug=slug,
project_id=project_id,
Expand Down
20 changes: 20 additions & 0 deletions web/ce/components/relations/activity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { TIssueActivity } from "@plane/types";

export const getRelationActivityContent = (activity: TIssueActivity | undefined): string | undefined => {
if (!activity) return;

switch (activity.field) {
case "blocking":
return activity.old_value === "" ? `marked this issue is blocking issue ` : `removed the blocking issue `;
case "blocked_by":
return activity.old_value === ""
? `marked this issue is being blocked by `
: `removed this issue being blocked by issue `;
case "duplicate":
return activity.old_value === "" ? `marked this issue as duplicate of ` : `removed this issue as a duplicate of `;
case "relates_to":
activity.old_value === "" ? `marked that this issue relates to ` : `removed the relation from `;
}
Comment on lines +6 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix critical bug in relates_to case and improve message consistency.

There are several issues in the switch statement:

  1. The relates_to case is missing a return statement, making it always return undefined
  2. Message formatting could be more consistent across cases

Apply these fixes:

   switch (activity.field) {
     case "blocking":
-      return activity.old_value === "" ? `marked this issue is blocking issue ` : `removed the blocking issue `;
+      return activity.old_value === "" ? `marked this issue as blocking ` : `removed blocking relation from `;
     case "blocked_by":
       return activity.old_value === ""
-        ? `marked this issue is being blocked by `
-        : `removed this issue being blocked by issue `;
+        ? `marked this issue as blocked by `
+        : `removed blocked by relation from `;
     case "duplicate":
       return activity.old_value === "" ? `marked this issue as duplicate of ` : `removed this issue as a duplicate of `;
     case "relates_to":
-      activity.old_value === "" ? `marked that this issue relates to ` : `removed the relation from `;
+      return activity.old_value === "" ? `marked this issue as related to ` : `removed relation from `;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
switch (activity.field) {
case "blocking":
return activity.old_value === "" ? `marked this issue is blocking issue ` : `removed the blocking issue `;
case "blocked_by":
return activity.old_value === ""
? `marked this issue is being blocked by `
: `removed this issue being blocked by issue `;
case "duplicate":
return activity.old_value === "" ? `marked this issue as duplicate of ` : `removed this issue as a duplicate of `;
case "relates_to":
activity.old_value === "" ? `marked that this issue relates to ` : `removed the relation from `;
}
switch (activity.field) {
case "blocking":
return activity.old_value === "" ? `marked this issue as blocking ` : `removed blocking relation from `;
case "blocked_by":
return activity.old_value === ""
? `marked this issue as blocked by `
: `removed blocked by relation from `;
case "duplicate":
return activity.old_value === "" ? `marked this issue as duplicate of ` : `removed this issue as a duplicate of `;
case "relates_to":
return activity.old_value === "" ? `marked this issue as related to ` : `removed relation from `;
}


return;
};
4 changes: 4 additions & 0 deletions web/ce/components/relations/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { RelatedIcon } from "@plane/ui";
import { TRelationObject } from "@/components/issues";
import { TIssueRelationTypes } from "../../types";

export * from "./activity";

export const ISSUE_RELATION_OPTIONS: Record<TIssueRelationTypes, TRelationObject> = {
relates_to: {
key: "relates_to",
Expand Down Expand Up @@ -33,3 +35,5 @@ export const ISSUE_RELATION_OPTIONS: Record<TIssueRelationTypes, TRelationObject
placeholder: "None",
},
};

export const useTimeLineRelationOptions = () => ISSUE_RELATION_OPTIONS;
14 changes: 12 additions & 2 deletions web/core/components/gantt-chart/blocks/block-row-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,29 @@ import RenderIfVisible from "@/components/core/render-if-visible-HOC";
import { TSelectionHelper } from "@/hooks/use-multiple-select";
// types
import { BLOCK_HEIGHT } from "../constants";
import { IBlockUpdateData } from "../types";
import { IBlockUpdateData, IGanttBlock } from "../types";
import { BlockRow } from "./block-row";

export type GanttChartBlocksProps = {
blockIds: string[];
blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void;
handleScrollToBlock: (block: IGanttBlock) => void;
enableAddBlock: boolean | ((blockId: string) => boolean);
showAllBlocks: boolean;
selectionHelpers: TSelectionHelper;
ganttContainerRef: React.RefObject<HTMLDivElement>;
};

export const GanttChartRowList: FC<GanttChartBlocksProps> = (props) => {
const { blockIds, blockUpdateHandler, enableAddBlock, showAllBlocks, selectionHelpers, ganttContainerRef } = props;
const {
blockIds,
blockUpdateHandler,
handleScrollToBlock,
enableAddBlock,
showAllBlocks,
selectionHelpers,
ganttContainerRef,
} = props;

return (
<div className="absolute top-0 left-0 min-w-full w-max">
Expand All @@ -37,6 +46,7 @@ export const GanttChartRowList: FC<GanttChartBlocksProps> = (props) => {
blockId={blockId}
showAllBlocks={showAllBlocks}
blockUpdateHandler={blockUpdateHandler}
handleScrollToBlock={handleScrollToBlock}
enableAddBlock={typeof enableAddBlock === "function" ? enableAddBlock(blockId) : enableAddBlock}
selectionHelpers={selectionHelpers}
ganttContainerRef={ganttContainerRef}
Expand Down
15 changes: 4 additions & 11 deletions web/core/components/gantt-chart/blocks/block-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ import { useTimeLineChartStore } from "@/hooks/use-timeline-chart";
//
import { BLOCK_HEIGHT, SIDEBAR_WIDTH } from "../constants";
import { ChartAddBlock } from "../helpers";
import { IBlockUpdateData } from "../types";
import { IBlockUpdateData, IGanttBlock } from "../types";

type Props = {
blockId: string;
showAllBlocks: boolean;
blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void;
handleScrollToBlock: (block: IGanttBlock) => void;
enableAddBlock: boolean;
selectionHelpers: TSelectionHelper;
ganttContainerRef: React.RefObject<HTMLDivElement>;
};

export const BlockRow: React.FC<Props> = observer((props) => {
const { blockId, showAllBlocks, blockUpdateHandler, enableAddBlock, selectionHelpers } = props;
const { blockId, showAllBlocks, blockUpdateHandler, handleScrollToBlock, enableAddBlock, selectionHelpers } = props;
// states
const [isHidden, setIsHidden] = useState(false);
const [isBlockHiddenOnLeft, setIsBlockHiddenOnLeft] = useState(false);
Expand Down Expand Up @@ -67,14 +68,6 @@ export const BlockRow: React.FC<Props> = observer((props) => {
// hide the block if it doesn't have start and target dates and showAllBlocks is false
if (!block || !block.data || (!showAllBlocks && !(block.start_date && block.target_date))) return null;

// scroll to a hidden block
const handleScrollToBlock = () => {
const scrollContainer = document.querySelector("#gantt-container") as HTMLDivElement;
if (!scrollContainer || !block.position) return;
// update container's scroll position to the block's position
scrollContainer.scrollLeft = block.position.marginLeft - 4;
};

const isBlockVisibleOnChart = block.start_date && block.target_date;
const isBlockSelected = selectionHelpers.getIsEntitySelected(block.id);
const isBlockFocused = selectionHelpers.getIsEntityActive(block.id);
Expand Down Expand Up @@ -106,7 +99,7 @@ export const BlockRow: React.FC<Props> = observer((props) => {
style={{
left: `${SIDEBAR_WIDTH + 4}px`,
}}
onClick={handleScrollToBlock}
onClick={() => handleScrollToBlock(block)}
>
<ArrowRight
className={cn("h-3.5 w-3.5", {
Expand Down
31 changes: 30 additions & 1 deletion web/core/components/gantt-chart/chart/main-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ import { observer } from "mobx-react";
// components
import { MultipleSelectGroup } from "@/components/core";
import {
ChartDataType,
GanttChartBlocksList,
GanttChartSidebar,
IBlockUpdateData,
IBlockUpdateDependencyData,
IGanttBlock,
MonthChartView,
QuarterChartView,
TGanttViews,
WeekChartView,
} from "@/components/gantt-chart";
// helpers
import { cn } from "@/helpers/common.helper";
import { getDate } from "@/helpers/date-time.helper";
// hooks
import { useTimeLineChartStore } from "@/hooks/use-timeline-chart";
// plane web components
Expand All @@ -26,6 +29,7 @@ import { useBulkOperationStatus } from "@/plane-web/hooks/use-bulk-operation-sta
//
import { GanttChartRowList } from "../blocks/block-row-list";
import { GANTT_SELECT_GROUP, HEADER_HEIGHT } from "../constants";
import { getItemPositionWidth } from "../views";
import { TimelineDragHelper } from "./timeline-drag-helper";

type Props = {
Expand All @@ -46,7 +50,11 @@ type Props = {
showAllBlocks: boolean;
sidebarToRender: (props: any) => React.ReactNode;
title: string;
updateCurrentViewRenderPayload: (direction: "left" | "right", currentView: TGanttViews) => void;
updateCurrentViewRenderPayload: (
direction: "left" | "right",
currentView: TGanttViews,
targetDate?: Date
) => ChartDataType | undefined;
quickAdd?: React.JSX.Element | undefined;
};

Expand Down Expand Up @@ -105,6 +113,26 @@ export const GanttChartMainContent: React.FC<Props> = observer((props) => {
if (approxRangeLeft < clientWidth) updateCurrentViewRenderPayload("left", currentView);
};

const handleScrollToBlock = (block: IGanttBlock) => {
const scrollContainer = ganttContainerRef.current as HTMLDivElement;
const scrollToDate = getDate(block.start_date);
let chartData;

if (!scrollContainer || !currentViewData || !scrollToDate) return;

if (scrollToDate.getTime() < currentViewData.data.startDate.getTime()) {
chartData = updateCurrentViewRenderPayload("left", currentView, scrollToDate);
} else if (scrollToDate.getTime() > currentViewData.data.endDate.getTime()) {
chartData = updateCurrentViewRenderPayload("right", currentView, scrollToDate);
}
// update container's scroll position to the block's position
const updatedPosition = getItemPositionWidth(chartData ?? currentViewData, block);

setTimeout(() => {
if (updatedPosition) scrollContainer.scrollLeft = updatedPosition.marginLeft - 4;
});
};

const CHART_VIEW_COMPONENTS: {
[key in TGanttViews]: React.FC;
} = {
Expand Down Expand Up @@ -166,6 +194,7 @@ export const GanttChartMainContent: React.FC<Props> = observer((props) => {
<GanttChartRowList
blockIds={blockIds}
blockUpdateHandler={blockUpdateHandler}
handleScrollToBlock={handleScrollToBlock}
enableAddBlock={enableAddBlock}
showAllBlocks={showAllBlocks}
selectionHelpers={helpers}
Expand Down
18 changes: 13 additions & 5 deletions web/core/components/gantt-chart/chart/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = observer((props) => {
updateAllBlocksOnChartChangeWhileDragging,
} = useTimeLineChartStore();

const updateCurrentViewRenderPayload = (side: null | "left" | "right", view: TGanttViews) => {
const updateCurrentViewRenderPayload = (side: null | "left" | "right", view: TGanttViews, targetDate?: Date) => {
const selectedCurrentView: TGanttViews = view;
const selectedCurrentViewData: ChartDataType | undefined =
selectedCurrentView && selectedCurrentView === currentViewData?.key
Expand All @@ -96,7 +96,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = observer((props) => {
if (selectedCurrentViewData === undefined) return;

const currentViewHelpers = timelineViewHelpers[selectedCurrentView];
const currentRender = currentViewHelpers.generateChart(selectedCurrentViewData, side);
const currentRender = currentViewHelpers.generateChart(selectedCurrentViewData, side, targetDate);
const mergeRenderPayloads = currentViewHelpers.mergeRenderPayloads as (
a: IWeekBlock[] | IMonthView | IMonthBlock[],
b: IWeekBlock[] | IMonthView | IMonthBlock[]
Expand All @@ -109,7 +109,8 @@ export const ChartViewRoot: FC<ChartViewRootProps> = observer((props) => {
if (side === "left") {
updateCurrentView(selectedCurrentView);
updateRenderView(mergeRenderPayloads(currentRender.payload, renderView));
updatingCurrentLeftScrollPosition(currentRender.scrollWidth);
updateItemsContainerWidth(currentRender.scrollWidth);
if (!targetDate) updateCurrentLeftScrollPosition(currentRender.scrollWidth);
updateAllBlocksOnChartChangeWhileDragging(currentRender.scrollWidth);
setItemsContainerWidth(itemsContainerWidth + currentRender.scrollWidth);
} else if (side === "right") {
Expand All @@ -125,6 +126,8 @@ export const ChartViewRoot: FC<ChartViewRootProps> = observer((props) => {
}, 50);
}
}

return currentRender.state;
};

const handleToday = () => updateCurrentViewRenderPayload(null, currentView);
Expand All @@ -135,12 +138,17 @@ export const ChartViewRoot: FC<ChartViewRootProps> = observer((props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const updatingCurrentLeftScrollPosition = (width: number) => {
const updateItemsContainerWidth = (width: number) => {
const scrollContainer = document.querySelector("#gantt-container") as HTMLDivElement;
if (!scrollContainer) return;
setItemsContainerWidth(width + scrollContainer?.scrollLeft);
};

const updateCurrentLeftScrollPosition = (width: number) => {
const scrollContainer = document.querySelector("#gantt-container") as HTMLDivElement;
if (!scrollContainer) return;

scrollContainer.scrollLeft = width + scrollContainer?.scrollLeft;
setItemsContainerWidth(width + scrollContainer?.scrollLeft);
};

const handleScrollToCurrentSelectedDate = (currentState: ChartDataType, date: Date) => {
Expand Down
4 changes: 2 additions & 2 deletions web/core/components/gantt-chart/chart/views/month.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const MonthChartView: FC<any> = observer(() => {
className={cn(
"flex flex-shrink-0 py-1 px-2 text-center capitalize justify-between outline-[0.25px] outline outline-custom-border-200",
{
"bg-custom-primary-10": weekBlock.today,
"bg-custom-primary-100/20": weekBlock.today,
}
)}
style={{ width: `${currentViewData?.data.dayWidth * 7}px` }}
Expand All @@ -92,7 +92,7 @@ export const MonthChartView: FC<any> = observer(() => {
<div
key={`column-${weekBlock.startDate}-${weekBlock.endDate}`}
className={cn("h-full overflow-hidden outline-[0.25px] outline outline-custom-border-100", {
"bg-custom-primary-10": weekBlock.today,
"bg-custom-primary-100/20": weekBlock.today,
})}
style={{ width: `${currentViewData?.data.dayWidth * 7}px` }}
/>
Expand Down
4 changes: 2 additions & 2 deletions web/core/components/gantt-chart/chart/views/quarter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const QuarterChartView: FC<any> = observer(() => {
className={cn(
"flex flex-shrink-0 text-center capitalize justify-center outline-[0.25px] outline outline-custom-border-200",
{
"bg-custom-primary-10": monthBlock.today,
"bg-custom-primary-100/20": monthBlock.today,
}
)}
style={{ width: `${currentViewData?.data.dayWidth * monthBlock.days}px` }}
Expand All @@ -80,7 +80,7 @@ export const QuarterChartView: FC<any> = observer(() => {
<div
key={`column-${rootIndex}-${index}`}
className={cn("h-full overflow-hidden outline-[0.25px] outline outline-custom-border-100", {
"bg-custom-primary-10": monthBlock.today,
"bg-custom-primary-100/20": monthBlock.today,
})}
style={{ width: `${currentViewData?.data.dayWidth * monthBlock.days}px` }}
/>
Expand Down
6 changes: 3 additions & 3 deletions web/core/components/gantt-chart/chart/views/week.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const WeekChartView: FC<any> = observer(() => {
className={cn(
"flex flex-shrink-0 p-1 text-center capitalize justify-between outline-[0.25px] outline outline-custom-border-200",
{
"bg-custom-primary-10": weekDay.today,
"bg-custom-primary-100/20": weekDay.today,
}
)}
style={{ width: `${currentViewData?.data.dayWidth}px` }}
Expand All @@ -71,12 +71,12 @@ export const WeekChartView: FC<any> = observer(() => {
</div>
</div>
{/** Day Columns */}
<div className="h-full w-full flex-grow flex">
<div className="h-full w-full flex-grow flex bg-custom-background-100">
{block?.children?.map((weekDay, index) => (
<div
key={`column-${rootIndex}-${index}`}
className={cn("h-full overflow-hidden outline-[0.25px] outline outline-custom-border-100", {
"bg-custom-primary-10": weekDay.today,
"bg-custom-primary-100/20": weekDay.today,
})}
style={{ width: `${currentViewData?.data.dayWidth}px` }}
>
Expand Down
20 changes: 11 additions & 9 deletions web/core/components/gantt-chart/views/month-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface IMonthView {
* @param side
* @returns
*/
const generateMonthChart = (monthPayload: ChartDataType, side: null | "left" | "right") => {
const generateMonthChart = (monthPayload: ChartDataType, side: null | "left" | "right", targetDate?: Date) => {
let renderState = cloneDeep(monthPayload);

const range: number = renderState.data.approxFilterRange || 6;
Expand Down Expand Up @@ -63,30 +63,32 @@ const generateMonthChart = (monthPayload: ChartDataType, side: null | "left" | "
}
// When side is left, generate more months on the left side of the start date
else if (side === "left") {
const currentDate = renderState.data.startDate;
const chartStartDate = renderState.data.startDate;
const currentDate = targetDate ? targetDate : chartStartDate;

minusDate = new Date(currentDate.getFullYear(), currentDate.getMonth() - range, currentDate.getDate());
plusDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() - 1);
minusDate = new Date(currentDate.getFullYear(), currentDate.getMonth() - range, 1);
plusDate = new Date(chartStartDate.getFullYear(), chartStartDate.getMonth(), chartStartDate.getDate() - 1);

if (minusDate && plusDate) filteredDates = getMonthsViewBetweenTwoDates(minusDate, plusDate);

startDate = filteredDates.weeks[0]?.startDate;
endDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() - 1);
endDate = new Date(chartStartDate.getFullYear(), chartStartDate.getMonth(), chartStartDate.getDate() - 1);
renderState = {
...renderState,
data: { ...renderState.data, startDate },
};
}
// When side is right, generate more months on the right side of the end date
else if (side === "right") {
const currentDate = renderState.data.endDate;
const chartEndDate = renderState.data.endDate;
const currentDate = targetDate ? targetDate : chartEndDate;

minusDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() + 1);
plusDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + range, currentDate.getDate());
minusDate = new Date(chartEndDate.getFullYear(), chartEndDate.getMonth(), chartEndDate.getDate() + 1);
plusDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + range, 1);

if (minusDate && plusDate) filteredDates = getMonthsViewBetweenTwoDates(minusDate, plusDate);

startDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() + 1);
startDate = new Date(chartEndDate.getFullYear(), chartEndDate.getMonth(), chartEndDate.getDate() + 1);
endDate = filteredDates.weeks[filteredDates.weeks.length - 1]?.endDate;
renderState = {
...renderState,
Expand Down
Loading