Enable blocks to have a single date on the block charts

This commit is contained in:
rahulramesha
2024-11-11 20:37:23 +05:30
parent 447af2e05a
commit cb055d566b
12 changed files with 93 additions and 44 deletions

View File

@@ -191,7 +191,7 @@ export class BaseTimeLineStore implements IBaseTimelineStore {
start_date: blockData?.start_date ?? undefined,
target_date: blockData?.target_date ?? undefined,
};
if (this.currentViewData && this.currentViewData?.data?.startDate && this.currentViewData?.data?.dayWidth) {
if (this.currentViewData && (this.currentViewData?.data?.startDate || this.currentViewData?.data?.dayWidth)) {
block.position = getItemPositionWidth(this.currentViewData, block);
}

View File

@@ -55,7 +55,7 @@ type Props = {
export const DateRangeDropdown: React.FC<Props> = (props) => {
const {
applyButtonText = "Apply changes",
bothRequired = true,
bothRequired = false,
buttonClassName,
buttonContainerClassName,
buttonFromDateClassName,

View File

@@ -68,7 +68,7 @@ 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;
const isBlockVisibleOnChart = block.start_date && block.target_date;
const isBlockVisibleOnChart = block.start_date || block.target_date;
const isBlockSelected = selectionHelpers.getIsEntitySelected(block.id);
const isBlockFocused = selectionHelpers.getIsEntityActive(block.id);
const isBlockHoveredOn = isBlockActive(block.id);

View File

@@ -46,10 +46,11 @@ export const GanttChartBlock: React.FC<Props> = observer((props) => {
const { isMoving, handleBlockDrag } = useGanttResizable(block, resizableRef, ganttContainerRef, updateBlockDates);
// hide the block if it doesn't have start and target dates and showAllBlocks is false
if (!block || (!showAllBlocks && !(block.start_date && block.target_date))) return null;
const isBlockVisibleOnChart = block?.start_date || block?.target_date;
const isBlockComplete = block?.start_date && block?.target_date;
const isBlockVisibleOnChart = block.start_date && block.target_date;
// hide the block if it doesn't have start and target dates and showAllBlocks is false
if (!block || (!showAllBlocks && !isBlockVisibleOnChart)) return null;
if (!block.data) return null;
@@ -88,7 +89,7 @@ export const GanttChartBlock: React.FC<Props> = observer((props) => {
handleBlockDrag={handleBlockDrag}
enableBlockLeftResize={enableBlockLeftResize}
enableBlockRightResize={enableBlockRightResize}
enableBlockMove={enableBlockMove}
enableBlockMove={enableBlockMove && !!isBlockComplete}
isMoving={isMoving}
ganttContainerRef={ganttContainerRef}
/>

View File

@@ -28,7 +28,7 @@ import { IssueBulkOperationsRoot } from "@/plane-web/components/issues";
import { useBulkOperationStatus } from "@/plane-web/hooks/use-bulk-operation-status";
//
import { GanttChartRowList } from "../blocks/block-row-list";
import { GANTT_SELECT_GROUP, HEADER_HEIGHT } from "../constants";
import { DEFAULT_BLOCK_WIDTH, GANTT_SELECT_GROUP, HEADER_HEIGHT } from "../constants";
import { getItemPositionWidth } from "../views";
import { TimelineDragHelper } from "./timeline-drag-helper";
@@ -120,7 +120,8 @@ export const GanttChartMainContent: React.FC<Props> = observer((props) => {
const handleScrollToBlock = (block: IGanttBlock) => {
const scrollContainer = ganttContainerRef.current as HTMLDivElement;
const scrollToDate = getDate(block.start_date);
const scrollToEndDate = !block.start_date && block.target_date;
const scrollToDate = block.start_date ? getDate(block.start_date) : getDate(block.target_date);
let chartData;
if (!scrollContainer || !currentViewData || !scrollToDate) return;
@@ -134,7 +135,8 @@ export const GanttChartMainContent: React.FC<Props> = observer((props) => {
const updatedPosition = getItemPositionWidth(chartData ?? currentViewData, block);
setTimeout(() => {
if (updatedPosition) scrollContainer.scrollLeft = updatedPosition.marginLeft - 4;
if (updatedPosition)
scrollContainer.scrollLeft = updatedPosition.marginLeft - 4 - (scrollToEndDate ? DEFAULT_BLOCK_WIDTH : 0);
});
};

View File

@@ -6,4 +6,6 @@ export const GANTT_BREADCRUMBS_HEIGHT = 40;
export const SIDEBAR_WIDTH = 360;
export const DEFAULT_BLOCK_WIDTH = 60;
export const GANTT_SELECT_GROUP = "gantt-issues";

View File

@@ -27,8 +27,8 @@ export const IssuesSidebarBlock = observer((props: Props) => {
const { updateActiveBlockId, isBlockActive, getNumberOfDaysFromPosition } = useTimeLineChartStore();
const { getIsIssuePeeked } = useIssueDetail();
const isBlockVisibleOnChart = !!block?.start_date && !!block?.target_date;
const duration = isBlockVisibleOnChart ? getNumberOfDaysFromPosition(block?.position?.width) : undefined;
const isBlockComplete = !!block?.start_date && !!block?.target_date;
const duration = isBlockComplete ? getNumberOfDaysFromPosition(block?.position?.width) : undefined;
if (!block?.data) return null;

View File

@@ -22,8 +22,8 @@ export const ModulesSidebarBlock: React.FC<Props> = observer((props) => {
if (!block) return <></>;
const isBlockVisibleOnChart = !!block.start_date && !!block.target_date;
const duration = isBlockVisibleOnChart ? getNumberOfDaysFromPosition(block?.position?.width) : undefined;
const isBlockComplete = !!block.start_date && !!block.target_date;
const duration = isBlockComplete ? getNumberOfDaysFromPosition(block?.position?.width) : undefined;
return (
<div

View File

@@ -1,4 +1,5 @@
import { addDaysToDate, findTotalDaysInRange, getDate } from "@/helpers/date-time.helper";
import { DEFAULT_BLOCK_WIDTH } from "../constants";
import { ChartDataType, IGanttBlock } from "../types";
/**
@@ -81,7 +82,7 @@ export const getDateFromPositionOnGantt = (position: number, chartData: ChartDat
*/
export const getItemPositionWidth = (chartData: ChartDataType, itemData: IGanttBlock) => {
let scrollPosition: number = 0;
let scrollWidth: number = 0;
let scrollWidth: number = DEFAULT_BLOCK_WIDTH;
const { startDate: chartStartDate } = chartData.data;
const { start_date, target_date } = itemData;
@@ -89,24 +90,23 @@ export const getItemPositionWidth = (chartData: ChartDataType, itemData: IGanttB
const itemStartDate = getDate(start_date);
const itemTargetDate = getDate(target_date);
if (!itemStartDate || !itemTargetDate) return;
chartStartDate.setHours(0, 0, 0, 0);
itemStartDate.setHours(0, 0, 0, 0);
itemTargetDate.setHours(0, 0, 0, 0);
itemStartDate?.setHours(0, 0, 0, 0);
itemTargetDate?.setHours(0, 0, 0, 0);
// get number of days from chart start date to block's start date
const positionDaysDifference = Math.round(findTotalDaysInRange(chartStartDate, itemStartDate, false) ?? 0);
if (!positionDaysDifference) return;
if (!itemStartDate && !itemTargetDate) return;
// get scroll position from the number of days and width of each day
scrollPosition = positionDaysDifference * chartData.data.dayWidth;
scrollPosition = itemStartDate
? getPositionFromDate(chartData, itemStartDate, 0)
: getPositionFromDate(chartData, itemTargetDate!, -1 * DEFAULT_BLOCK_WIDTH);
// get width of block
const widthTimeDifference: number = itemStartDate.getTime() - itemTargetDate.getTime();
const widthDaysDifference: number = Math.abs(Math.floor(widthTimeDifference / (1000 * 60 * 60 * 24)));
scrollWidth = (widthDaysDifference + 1) * chartData.data.dayWidth;
if (itemStartDate && itemTargetDate) {
// get width of block
const widthTimeDifference: number = itemStartDate.getTime() - itemTargetDate.getTime();
const widthDaysDifference: number = Math.abs(Math.floor(widthTimeDifference / (1000 * 60 * 60 * 24)));
scrollWidth = (widthDaysDifference + 1) * chartData.data.dayWidth;
}
return { marginLeft: scrollPosition, width: scrollWidth };
};
@@ -116,7 +116,7 @@ export const getPositionFromDate = (chartData: ChartDataType, date: string | Dat
const { startDate: chartStartDate } = chartData.data;
if (!currDate || !chartStartDate) return;
if (!currDate || !chartStartDate) return 0;
chartStartDate.setHours(0, 0, 0, 0);
currDate.setHours(0, 0, 0, 0);
@@ -124,7 +124,7 @@ export const getPositionFromDate = (chartData: ChartDataType, date: string | Dat
// get number of days from chart start date to block's start date
const positionDaysDifference = Math.round(findTotalDaysInRange(chartStartDate, currDate, false) ?? 0);
if (!positionDaysDifference) return;
if (!positionDaysDifference) return 0;
// get scroll position from the number of days and width of each day
return positionDaysDifference * chartData.data.dayWidth + offsetWidth;

View File

@@ -15,7 +15,8 @@ import useIssuePeekOverviewRedirection from "@/hooks/use-issue-peek-overview-red
import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web components
import { IssueIdentifier } from "@/plane-web/components/issues";
// local types
//
import { getBlockViewDetails } from "../utils";
import { GanttStoreType } from "./base-gantt-root";
type Props = {
@@ -41,6 +42,8 @@ export const IssueGanttBlock: React.FC<Props> = observer((props) => {
const stateDetails =
issueDetails && getProjectStates(issueDetails?.project_id)?.find((state) => state?.id == issueDetails?.state_id);
const { message, blockStyle } = getBlockViewDetails(issueDetails, stateDetails?.color ?? "");
const handleIssuePeekOverview = () => handleRedirection(workspaceSlug, issueDetails, isMobile);
return (
@@ -49,18 +52,16 @@ export const IssueGanttBlock: React.FC<Props> = observer((props) => {
tooltipContent={
<div className="space-y-1">
<h5>{issueDetails?.name}</h5>
<div>
{renderFormattedDate(issueDetails?.start_date ?? "")} to{" "}
{renderFormattedDate(issueDetails?.target_date ?? "")}
</div>
<div>{message}</div>
</div>
}
position="top-left"
disabled={!message}
>
<div
id={`issue-${issueId}`}
className="relative flex h-full w-full cursor-pointer items-center rounded"
style={{ backgroundColor: stateDetails?.color }}
style={blockStyle}
onClick={handleIssuePeekOverview}
>
<div className="absolute left-0 top-0 h-full w-full bg-custom-background-100/50" />
@@ -95,7 +96,11 @@ export const IssueGanttSidebarBlock: React.FC<Props> = observer((props) => {
// derived values
const issueDetails = getIssueById(issueId);
const handleIssuePeekOverview = () => handleRedirection(workspaceSlug, issueDetails, isMobile);
const handleIssuePeekOverview = (e: any) => {
e.stopPropagation(true);
e.preventDefault();
handleRedirection(workspaceSlug, issueDetails, isMobile);
};
return (
<ControlLink

View File

@@ -1,5 +1,6 @@
"use client";
import { CSSProperties } from "react";
import { extractInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
import clone from "lodash/clone";
import concat from "lodash/concat";
@@ -32,6 +33,7 @@ import { Logo } from "@/components/common";
import { ISSUE_PRIORITIES, EIssuesStoreType } from "@/constants/issue";
import { STATE_GROUPS } from "@/constants/state";
// helpers
import { renderFormattedDate } from "@/helpers/date-time.helper";
import { getFileURL } from "@/helpers/file.helper";
// store
import { ICycleStore } from "@/store/cycle.store";
@@ -672,3 +674,39 @@ export function getApproximateCardHeight(displayProperties: IIssueDisplayPropert
return cardHeight;
}
/**
* This Method is used to get Block view details, that returns block style and tooltip message
* @param block
* @param backgroundColor
* @returns
*/
export const getBlockViewDetails = (
block: { start_date: string | undefined | null; target_date: string | undefined | null } | undefined | null,
backgroundColor: string
) => {
const isBlockVisibleOnChart = block?.start_date || block?.target_date;
const isBlockComplete = block?.start_date && block?.target_date;
let message;
const blockStyle: CSSProperties = {
backgroundColor,
};
if (isBlockVisibleOnChart && !isBlockComplete) {
if (block?.start_date) {
message = `From ${renderFormattedDate(block.start_date)}`;
blockStyle.maskImage = `linear-gradient(to right, ${backgroundColor} 60%, transparent 100%)`;
} else if (block?.target_date) {
message = `Till ${renderFormattedDate(block.target_date)}`;
blockStyle.maskImage = `linear-gradient(to left, ${backgroundColor} 60%, transparent 100%)`;
}
} else if (isBlockComplete) {
message = `${renderFormattedDate(block?.start_date)} to ${renderFormattedDate(block?.target_date)}`;
}
return {
message,
blockStyle,
};
};

View File

@@ -7,10 +7,9 @@ import { useParams } from "next/navigation";
import { Tooltip, ModuleStatusIcon } from "@plane/ui";
// components
import { SIDEBAR_WIDTH } from "@/components/gantt-chart/constants";
import { getBlockViewDetails } from "@/components/issues/issue-layouts/utils";
// constants
import { MODULE_STATUS } from "@/constants/module";
// helpers
import { renderFormattedDate } from "@/helpers/date-time.helper";
// hooks
import { useModule } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
@@ -32,23 +31,25 @@ export const ModuleGanttBlock: React.FC<Props> = observer((props) => {
// hooks
const { isMobile } = usePlatformOS();
const { message, blockStyle } = getBlockViewDetails(
moduleDetails,
MODULE_STATUS.find((s) => s.value === moduleDetails?.status)?.color ?? ""
);
return (
<Tooltip
isMobile={isMobile}
tooltipContent={
<div className="space-y-1">
<h5>{moduleDetails?.name}</h5>
<div>
{renderFormattedDate(moduleDetails?.start_date ?? "")} to{" "}
{renderFormattedDate(moduleDetails?.target_date ?? "")}
</div>
<div>{message}</div>
</div>
}
position="top-left"
>
<div
className="relative flex h-full w-full cursor-pointer items-center rounded"
style={{ backgroundColor: MODULE_STATUS.find((s) => s.value === moduleDetails?.status)?.color }}
style={blockStyle}
onClick={() =>
router.push(
`/${workspaceSlug?.toString()}/projects/${moduleDetails?.project_id}/modules/${moduleDetails?.id}`