diff --git a/web/ce/store/timeline/base-timeline.store.ts b/web/ce/store/timeline/base-timeline.store.ts index d0287e6bdb..71169ddada 100644 --- a/web/ce/store/timeline/base-timeline.store.ts +++ b/web/ce/store/timeline/base-timeline.store.ts @@ -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); } diff --git a/web/core/components/dropdowns/date-range.tsx b/web/core/components/dropdowns/date-range.tsx index 453215e994..7c7126d042 100644 --- a/web/core/components/dropdowns/date-range.tsx +++ b/web/core/components/dropdowns/date-range.tsx @@ -55,7 +55,7 @@ type Props = { export const DateRangeDropdown: React.FC = (props) => { const { applyButtonText = "Apply changes", - bothRequired = true, + bothRequired = false, buttonClassName, buttonContainerClassName, buttonFromDateClassName, diff --git a/web/core/components/gantt-chart/blocks/block-row.tsx b/web/core/components/gantt-chart/blocks/block-row.tsx index 1bcec7f389..b215aee4b8 100644 --- a/web/core/components/gantt-chart/blocks/block-row.tsx +++ b/web/core/components/gantt-chart/blocks/block-row.tsx @@ -68,7 +68,7 @@ export const BlockRow: React.FC = 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); diff --git a/web/core/components/gantt-chart/blocks/block.tsx b/web/core/components/gantt-chart/blocks/block.tsx index c82d2e8a1a..8671993c7c 100644 --- a/web/core/components/gantt-chart/blocks/block.tsx +++ b/web/core/components/gantt-chart/blocks/block.tsx @@ -46,10 +46,11 @@ export const GanttChartBlock: React.FC = 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 = observer((props) => { handleBlockDrag={handleBlockDrag} enableBlockLeftResize={enableBlockLeftResize} enableBlockRightResize={enableBlockRightResize} - enableBlockMove={enableBlockMove} + enableBlockMove={enableBlockMove && !!isBlockComplete} isMoving={isMoving} ganttContainerRef={ganttContainerRef} /> diff --git a/web/core/components/gantt-chart/chart/main-content.tsx b/web/core/components/gantt-chart/chart/main-content.tsx index ee3ec6e8e1..b18d03b47b 100644 --- a/web/core/components/gantt-chart/chart/main-content.tsx +++ b/web/core/components/gantt-chart/chart/main-content.tsx @@ -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 = 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 = 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); }); }; diff --git a/web/core/components/gantt-chart/constants.ts b/web/core/components/gantt-chart/constants.ts index 0f11791ea2..e875fb8eae 100644 --- a/web/core/components/gantt-chart/constants.ts +++ b/web/core/components/gantt-chart/constants.ts @@ -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"; diff --git a/web/core/components/gantt-chart/sidebar/issues/block.tsx b/web/core/components/gantt-chart/sidebar/issues/block.tsx index 9b0dc270b2..bb286c2800 100644 --- a/web/core/components/gantt-chart/sidebar/issues/block.tsx +++ b/web/core/components/gantt-chart/sidebar/issues/block.tsx @@ -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; diff --git a/web/core/components/gantt-chart/sidebar/modules/block.tsx b/web/core/components/gantt-chart/sidebar/modules/block.tsx index 2dc85ac20a..fb8eaf4818 100644 --- a/web/core/components/gantt-chart/sidebar/modules/block.tsx +++ b/web/core/components/gantt-chart/sidebar/modules/block.tsx @@ -22,8 +22,8 @@ export const ModulesSidebarBlock: React.FC = 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 (
{ 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; diff --git a/web/core/components/issues/issue-layouts/gantt/blocks.tsx b/web/core/components/issues/issue-layouts/gantt/blocks.tsx index ad01d53bde..efb2b6bd14 100644 --- a/web/core/components/issues/issue-layouts/gantt/blocks.tsx +++ b/web/core/components/issues/issue-layouts/gantt/blocks.tsx @@ -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 = 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 = observer((props) => { tooltipContent={
{issueDetails?.name}
-
- {renderFormattedDate(issueDetails?.start_date ?? "")} to{" "} - {renderFormattedDate(issueDetails?.target_date ?? "")} -
+
{message}
} position="top-left" + disabled={!message} >
@@ -95,7 +96,11 @@ export const IssueGanttSidebarBlock: React.FC = 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 ( { + 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, + }; +}; diff --git a/web/core/components/modules/gantt-chart/blocks.tsx b/web/core/components/modules/gantt-chart/blocks.tsx index 8282179a68..8c1567011d 100644 --- a/web/core/components/modules/gantt-chart/blocks.tsx +++ b/web/core/components/modules/gantt-chart/blocks.tsx @@ -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 = observer((props) => { // hooks const { isMobile } = usePlatformOS(); + const { message, blockStyle } = getBlockViewDetails( + moduleDetails, + MODULE_STATUS.find((s) => s.value === moduleDetails?.status)?.color ?? "" + ); + return (
{moduleDetails?.name}
-
- {renderFormattedDate(moduleDetails?.start_date ?? "")} to{" "} - {renderFormattedDate(moduleDetails?.target_date ?? "")} -
+
{message}
} position="top-left" >
s.value === moduleDetails?.status)?.color }} + style={blockStyle} onClick={() => router.push( `/${workspaceSlug?.toString()}/projects/${moduleDetails?.project_id}/modules/${moduleDetails?.id}`