From 34bccd7e06f89a27f1944f661ef849955daa8b99 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Date: Thu, 9 Nov 2023 17:57:41 +0530 Subject: [PATCH] refactor: gantt sidebar (#2705) * refactor: gantt sidebar * fix: exception error fix: file placement * refactor: not passing sidebar block as props --- .../cycles/gantt-chart/cycles-list-layout.tsx | 4 +- web/components/gantt-chart/chart/index.tsx | 15 +- web/components/gantt-chart/index.ts | 1 + web/components/gantt-chart/root.tsx | 6 +- .../{ => sidebar}/cycle-sidebar.tsx | 13 +- web/components/gantt-chart/sidebar/index.ts | 4 + .../{ => sidebar}/module-sidebar.tsx | 13 +- .../sidebar/project-view-sidebar.tsx | 160 ++++++++++++++++++ .../gantt-chart/{ => sidebar}/sidebar.tsx | 17 +- .../issues/issue-layouts/gantt/cycle-root.tsx | 11 +- .../issue-layouts/gantt/module-root.tsx | 9 +- .../issue-layouts/gantt/project-view-root.tsx | 11 +- .../issues/issue-layouts/gantt/root.tsx | 11 +- .../gantt-chart/modules-list-layout.tsx | 6 +- 14 files changed, 232 insertions(+), 49 deletions(-) rename web/components/gantt-chart/{ => sidebar}/cycle-sidebar.tsx (93%) create mode 100644 web/components/gantt-chart/sidebar/index.ts rename web/components/gantt-chart/{ => sidebar}/module-sidebar.tsx (93%) create mode 100644 web/components/gantt-chart/sidebar/project-view-sidebar.tsx rename web/components/gantt-chart/{ => sidebar}/sidebar.tsx (90%) diff --git a/web/components/cycles/gantt-chart/cycles-list-layout.tsx b/web/components/cycles/gantt-chart/cycles-list-layout.tsx index 9526de59c..d5bd4e140 100644 --- a/web/components/cycles/gantt-chart/cycles-list-layout.tsx +++ b/web/components/cycles/gantt-chart/cycles-list-layout.tsx @@ -10,7 +10,7 @@ import { CycleService } from "services/cycle.service"; import useUser from "hooks/use-user"; import useProjectDetails from "hooks/use-project-details"; // components -import { GanttChartRoot, IBlockUpdateData } from "components/gantt-chart"; +import { GanttChartRoot, IBlockUpdateData, CycleGanttSidebar } from "components/gantt-chart"; import { CycleGanttBlock, CycleGanttSidebarBlock } from "components/cycles"; // types import { ICycle } from "types"; @@ -85,8 +85,8 @@ export const CyclesListGanttChartView: FC = ({ cycles, mutateCycles }) => loaderTitle="Cycles" blocks={cycles ? blockFormat(cycles) : null} blockUpdateHandler={(block, payload) => handleCycleUpdate(block, payload)} + sidebarToRender={(props) => } blockToRender={(data: ICycle) => } - sidebarBlockToRender={(data: ICycle) => } enableBlockLeftResize={false} enableBlockRightResize={false} enableBlockMove={false} diff --git a/web/components/gantt-chart/chart/index.tsx b/web/components/gantt-chart/chart/index.tsx index 219f11ebb..c6df29452 100644 --- a/web/components/gantt-chart/chart/index.tsx +++ b/web/components/gantt-chart/chart/index.tsx @@ -2,7 +2,7 @@ import { FC, useEffect, useState } from "react"; // icons // components import { GanttChartBlocks } from "components/gantt-chart"; -import { GanttSidebar } from "../sidebar"; +// import { GanttSidebar } from "../sidebar"; // import { HourChartView } from "./hours"; // import { DayChartView } from "./day"; // import { WeekChartView } from "./week"; @@ -40,7 +40,7 @@ type ChartViewRootProps = { blocks: IGanttBlock[] | null; blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void; blockToRender: (data: any) => React.ReactNode; - sidebarBlockToRender: (block: any) => React.ReactNode; + sidebarToRender: (props: any) => React.ReactNode; enableBlockLeftResize: boolean; enableBlockRightResize: boolean; enableBlockMove: boolean; @@ -54,7 +54,7 @@ export const ChartViewRoot: FC = ({ blocks = null, loaderTitle, blockUpdateHandler, - sidebarBlockToRender, + sidebarToRender, blockToRender, enableBlockLeftResize, enableBlockRightResize, @@ -285,13 +285,8 @@ export const ChartViewRoot: FC = ({
{title}
Duration
- + + {sidebarToRender && sidebarToRender({ title, blockUpdateHandler, blocks, enableReorder })}
void; blockToRender: (data: any) => React.ReactNode; - sidebarBlockToRender: (block: any) => React.ReactNode; + sidebarToRender: (props: any) => React.ReactNode; enableBlockLeftResize?: boolean; enableBlockRightResize?: boolean; enableBlockMove?: boolean; @@ -27,7 +27,7 @@ export const GanttChartRoot: FC = ({ blocks, loaderTitle = "blocks", blockUpdateHandler, - sidebarBlockToRender, + sidebarToRender, blockToRender, enableBlockLeftResize = true, enableBlockRightResize = true, @@ -42,7 +42,7 @@ export const GanttChartRoot: FC = ({ blocks={blocks} loaderTitle={loaderTitle} blockUpdateHandler={blockUpdateHandler} - sidebarBlockToRender={sidebarBlockToRender} + sidebarToRender={sidebarToRender} blockToRender={blockToRender} enableBlockLeftResize={enableBlockLeftResize} enableBlockRightResize={enableBlockRightResize} diff --git a/web/components/gantt-chart/cycle-sidebar.tsx b/web/components/gantt-chart/sidebar/cycle-sidebar.tsx similarity index 93% rename from web/components/gantt-chart/cycle-sidebar.tsx rename to web/components/gantt-chart/sidebar/cycle-sidebar.tsx index 5ecc1f3ba..0103c7459 100644 --- a/web/components/gantt-chart/cycle-sidebar.tsx +++ b/web/components/gantt-chart/sidebar/cycle-sidebar.tsx @@ -3,25 +3,26 @@ import { DragDropContext, Draggable, DropResult } from "@hello-pangea/dnd"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { MoreVertical } from "lucide-react"; // hooks -import { useChart } from "./hooks"; +import { useChart } from "components/gantt-chart/hooks"; // ui import { Loader } from "@plane/ui"; +// components +import { CycleGanttSidebarBlock } from "components/cycles"; // helpers import { findTotalDaysInRange } from "helpers/date-time.helper"; // types -import { IBlockUpdateData, IGanttBlock } from "./types"; +import { IBlockUpdateData, IGanttBlock } from "components/gantt-chart/types"; type Props = { title: string; blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void; blocks: IGanttBlock[] | null; - SidebarBlockRender: React.FC; enableReorder: boolean; }; -export const GanttSidebar: React.FC = (props) => { +export const CycleGanttSidebar: React.FC = (props) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { title, blockUpdateHandler, blocks, SidebarBlockRender, enableReorder } = props; + const { title, blockUpdateHandler, blocks, enableReorder } = props; const router = useRouter(); const { cycleId } = router.query; @@ -128,7 +129,7 @@ export const GanttSidebar: React.FC = (props) => { )}
- +
{duration} day{duration > 1 ? "s" : ""} diff --git a/web/components/gantt-chart/sidebar/index.ts b/web/components/gantt-chart/sidebar/index.ts new file mode 100644 index 000000000..8b1d72237 --- /dev/null +++ b/web/components/gantt-chart/sidebar/index.ts @@ -0,0 +1,4 @@ +export * from "./cycle-sidebar"; +export * from "./module-sidebar"; +export * from "./sidebar"; +export * from "./project-view-sidebar"; diff --git a/web/components/gantt-chart/module-sidebar.tsx b/web/components/gantt-chart/sidebar/module-sidebar.tsx similarity index 93% rename from web/components/gantt-chart/module-sidebar.tsx rename to web/components/gantt-chart/sidebar/module-sidebar.tsx index 5ecc1f3ba..8e076d463 100644 --- a/web/components/gantt-chart/module-sidebar.tsx +++ b/web/components/gantt-chart/sidebar/module-sidebar.tsx @@ -3,25 +3,26 @@ import { DragDropContext, Draggable, DropResult } from "@hello-pangea/dnd"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { MoreVertical } from "lucide-react"; // hooks -import { useChart } from "./hooks"; +import { useChart } from "components/gantt-chart/hooks"; // ui import { Loader } from "@plane/ui"; +// components +import { ModuleGanttSidebarBlock } from "components/modules"; // helpers import { findTotalDaysInRange } from "helpers/date-time.helper"; // types -import { IBlockUpdateData, IGanttBlock } from "./types"; +import { IBlockUpdateData, IGanttBlock } from "components/gantt-chart"; type Props = { title: string; blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void; blocks: IGanttBlock[] | null; - SidebarBlockRender: React.FC; enableReorder: boolean; }; -export const GanttSidebar: React.FC = (props) => { +export const ModuleGanttSidebar: React.FC = (props) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { title, blockUpdateHandler, blocks, SidebarBlockRender, enableReorder } = props; + const { title, blockUpdateHandler, blocks, enableReorder } = props; const router = useRouter(); const { cycleId } = router.query; @@ -128,7 +129,7 @@ export const GanttSidebar: React.FC = (props) => { )}
- +
{duration} day{duration > 1 ? "s" : ""} diff --git a/web/components/gantt-chart/sidebar/project-view-sidebar.tsx b/web/components/gantt-chart/sidebar/project-view-sidebar.tsx new file mode 100644 index 000000000..c327e1eac --- /dev/null +++ b/web/components/gantt-chart/sidebar/project-view-sidebar.tsx @@ -0,0 +1,160 @@ +import { useRouter } from "next/router"; +import { DragDropContext, Draggable, DropResult } from "@hello-pangea/dnd"; +import StrictModeDroppable from "components/dnd/StrictModeDroppable"; +import { MoreVertical } from "lucide-react"; +// hooks +import { useChart } from "components/gantt-chart/hooks"; +// ui +import { Loader } from "@plane/ui"; +// components +import { IssueGanttSidebarBlock } from "components/issues"; +// helpers +import { findTotalDaysInRange } from "helpers/date-time.helper"; +// types +import { IBlockUpdateData, IGanttBlock } from "components/gantt-chart/types"; + +type Props = { + title: string; + blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void; + blocks: IGanttBlock[] | null; + enableReorder: boolean; + enableQuickIssueCreate?: boolean; +}; + +export const ProjectViewGanttSidebar: React.FC = (props) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { title, blockUpdateHandler, blocks, enableReorder } = props; + + const router = useRouter(); + const { cycleId } = router.query; + + const { activeBlock, dispatch } = useChart(); + + // update the active block on hover + const updateActiveBlock = (block: IGanttBlock | null) => { + dispatch({ + type: "PARTIAL_UPDATE", + payload: { + activeBlock: block, + }, + }); + }; + + const handleOrderChange = (result: DropResult) => { + if (!blocks) return; + + const { source, destination } = result; + + // return if dropped outside the list + if (!destination) return; + + // return if dropped on the same index + if (source.index === destination.index) return; + + let updatedSortOrder = blocks[source.index].sort_order; + + // update the sort order to the lowest if dropped at the top + if (destination.index === 0) updatedSortOrder = blocks[0].sort_order - 1000; + // update the sort order to the highest if dropped at the bottom + else if (destination.index === blocks.length - 1) updatedSortOrder = blocks[blocks.length - 1].sort_order + 1000; + // update the sort order to the average of the two adjacent blocks if dropped in between + else { + const destinationSortingOrder = blocks[destination.index].sort_order; + const relativeDestinationSortingOrder = + source.index < destination.index + ? blocks[destination.index + 1].sort_order + : blocks[destination.index - 1].sort_order; + + updatedSortOrder = (destinationSortingOrder + relativeDestinationSortingOrder) / 2; + } + + // extract the element from the source index and insert it at the destination index without updating the entire array + const removedElement = blocks.splice(source.index, 1)[0]; + blocks.splice(destination.index, 0, removedElement); + + // call the block update handler with the updated sort order, new and old index + blockUpdateHandler(removedElement.data, { + sort_order: { + destinationIndex: destination.index, + newSortOrder: updatedSortOrder, + sourceIndex: source.index, + }, + }); + }; + + return ( + + + {(droppableProvided) => ( +
+ <> + {blocks ? ( + blocks.map((block, index) => { + const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? "", true); + + return ( + + {(provided, snapshot) => ( +
updateActiveBlock(block)} + onMouseLeave={() => updateActiveBlock(null)} + ref={provided.innerRef} + {...provided.draggableProps} + > +
+ {enableReorder && ( + + )} +
+
+ +
+
+ {duration} day{duration > 1 ? "s" : ""} +
+
+
+
+ )} +
+ ); + }) + ) : ( + + + + + + + )} + {droppableProvided.placeholder} + +
+ )} +
+
+ ); +}; diff --git a/web/components/gantt-chart/sidebar.tsx b/web/components/gantt-chart/sidebar/sidebar.tsx similarity index 90% rename from web/components/gantt-chart/sidebar.tsx rename to web/components/gantt-chart/sidebar/sidebar.tsx index f6c32e099..72fbe1267 100644 --- a/web/components/gantt-chart/sidebar.tsx +++ b/web/components/gantt-chart/sidebar/sidebar.tsx @@ -3,28 +3,27 @@ import { DragDropContext, Draggable, DropResult } from "@hello-pangea/dnd"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { MoreVertical } from "lucide-react"; // hooks -import { useChart } from "./hooks"; +import { useChart } from "components/gantt-chart/hooks"; // ui import { Loader } from "@plane/ui"; // components -import { GanttInlineCreateIssueForm } from "components/issues"; +import { GanttInlineCreateIssueForm, IssueGanttSidebarBlock } from "components/issues"; // helpers import { findTotalDaysInRange } from "helpers/date-time.helper"; // types -import { IBlockUpdateData, IGanttBlock } from "./types"; +import { IGanttBlock, IBlockUpdateData } from "components/gantt-chart/types"; type Props = { title: string; blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void; blocks: IGanttBlock[] | null; - sidebarBlockToRender: (block: any) => React.ReactNode; enableReorder: boolean; enableQuickIssueCreate?: boolean; }; -export const GanttSidebar: React.FC = (props) => { +export const IssueGanttSidebar: React.FC = (props) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { title, blockUpdateHandler, blocks, sidebarBlockToRender, enableReorder, enableQuickIssueCreate } = props; + const { title, blockUpdateHandler, blocks, enableReorder, enableQuickIssueCreate } = props; const router = useRouter(); const { cycleId } = router.query; @@ -130,7 +129,9 @@ export const GanttSidebar: React.FC = (props) => { )}
-
{sidebarBlockToRender(block.data)}
+
+ +
{duration} day{duration > 1 ? "s" : ""}
@@ -151,7 +152,7 @@ export const GanttSidebar: React.FC = (props) => { )} {droppableProvided.placeholder} - + {enableQuickIssueCreate && }
)} diff --git a/web/components/issues/issue-layouts/gantt/cycle-root.tsx b/web/components/issues/issue-layouts/gantt/cycle-root.tsx index 78ba7f2a6..56a17db15 100644 --- a/web/components/issues/issue-layouts/gantt/cycle-root.tsx +++ b/web/components/issues/issue-layouts/gantt/cycle-root.tsx @@ -4,8 +4,13 @@ import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; import useProjectDetails from "hooks/use-project-details"; // components -import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "components/gantt-chart"; -import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues"; +import { IssueGanttBlock } from "components/issues"; +import { + GanttChartRoot, + IBlockUpdateData, + renderIssueBlocksStructure, + IssueGanttSidebar, +} from "components/gantt-chart"; // types import { IIssueUnGroupedStructure } from "store/issue"; import { IIssue } from "types"; @@ -40,7 +45,7 @@ export const CycleGanttLayout: React.FC = observer(() => { blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null} blockUpdateHandler={updateIssue} blockToRender={(data: IIssue) => } - sidebarBlockToRender={(data: IIssue) => } + sidebarToRender={(props) => } enableBlockLeftResize={isAllowed} enableBlockRightResize={isAllowed} enableBlockMove={isAllowed} diff --git a/web/components/issues/issue-layouts/gantt/module-root.tsx b/web/components/issues/issue-layouts/gantt/module-root.tsx index 7584cc7c6..c5a880bbe 100644 --- a/web/components/issues/issue-layouts/gantt/module-root.tsx +++ b/web/components/issues/issue-layouts/gantt/module-root.tsx @@ -4,8 +4,13 @@ import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; import useProjectDetails from "hooks/use-project-details"; // components -import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "components/gantt-chart"; import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues"; +import { + GanttChartRoot, + IBlockUpdateData, + renderIssueBlocksStructure, + IssueGanttSidebar, +} from "components/gantt-chart"; // types import { IIssueUnGroupedStructure } from "store/issue"; import { IIssue } from "types"; @@ -39,8 +44,8 @@ export const ModuleGanttLayout: React.FC = observer(() => { loaderTitle="Issues" blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null} blockUpdateHandler={updateIssue} + sidebarToRender={(data) => } blockToRender={(data: IIssue) => } - sidebarBlockToRender={(data: IIssue) => } enableBlockLeftResize={isAllowed} enableBlockRightResize={isAllowed} enableBlockMove={isAllowed} diff --git a/web/components/issues/issue-layouts/gantt/project-view-root.tsx b/web/components/issues/issue-layouts/gantt/project-view-root.tsx index 1be9ad0c3..c06afd817 100644 --- a/web/components/issues/issue-layouts/gantt/project-view-root.tsx +++ b/web/components/issues/issue-layouts/gantt/project-view-root.tsx @@ -6,8 +6,13 @@ import { useMobxStore } from "lib/mobx/store-provider"; // hooks import useProjectDetails from "hooks/use-project-details"; // components -import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "components/gantt-chart"; -import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues"; +import { IssueGanttBlock } from "components/issues"; +import { + GanttChartRoot, + IBlockUpdateData, + renderIssueBlocksStructure, + ProjectViewGanttSidebar, +} from "components/gantt-chart"; // types import { IIssueUnGroupedStructure } from "store/issue"; import { IIssue } from "types"; @@ -42,7 +47,7 @@ export const ProjectViewGanttLayout: React.FC = observer(() => { blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null} blockUpdateHandler={updateIssue} blockToRender={(data: IIssue) => } - sidebarBlockToRender={(data: IIssue) => } + sidebarToRender={(props) => } enableBlockLeftResize={isAllowed} enableBlockRightResize={isAllowed} enableBlockMove={isAllowed} diff --git a/web/components/issues/issue-layouts/gantt/root.tsx b/web/components/issues/issue-layouts/gantt/root.tsx index 77bbc0710..d7ddd69b2 100644 --- a/web/components/issues/issue-layouts/gantt/root.tsx +++ b/web/components/issues/issue-layouts/gantt/root.tsx @@ -5,8 +5,13 @@ import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; import useProjectDetails from "hooks/use-project-details"; // components -import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "components/gantt-chart"; -import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues"; +import { IssueGanttBlock } from "components/issues"; +import { + GanttChartRoot, + IBlockUpdateData, + renderIssueBlocksStructure, + IssueGanttSidebar, +} from "components/gantt-chart"; // types import { IIssueUnGroupedStructure } from "store/issue"; import { IIssue } from "types"; @@ -41,7 +46,7 @@ export const GanttLayout: React.FC = observer(() => { blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null} blockUpdateHandler={updateIssue} blockToRender={(data: IIssue) => } - sidebarBlockToRender={(data: IIssue) => } + sidebarToRender={(props) => } enableBlockLeftResize={isAllowed} enableBlockRightResize={isAllowed} enableBlockMove={isAllowed} diff --git a/web/components/modules/gantt-chart/modules-list-layout.tsx b/web/components/modules/gantt-chart/modules-list-layout.tsx index 693aec3a5..ee86cae27 100644 --- a/web/components/modules/gantt-chart/modules-list-layout.tsx +++ b/web/components/modules/gantt-chart/modules-list-layout.tsx @@ -3,8 +3,8 @@ import { observer } from "mobx-react-lite"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // components -import { GanttChartRoot, IBlockUpdateData } from "components/gantt-chart"; -import { ModuleGanttBlock, ModuleGanttSidebarBlock } from "components/modules"; +import { GanttChartRoot, IBlockUpdateData, ModuleGanttSidebar } from "components/gantt-chart"; +import { ModuleGanttBlock } from "components/modules"; // types import { IModule } from "types"; @@ -44,8 +44,8 @@ export const ModulesListGanttChartView: React.FC = observer(() => { title="Modules" loaderTitle="Modules" blocks={modules ? blockFormat(modules) : null} + sidebarToRender={(props) => } blockUpdateHandler={(block, payload) => handleModuleUpdate(block, payload)} - sidebarBlockToRender={ModuleGanttSidebarBlock} blockToRender={(data: IModule) => } enableBlockLeftResize={isAllowed} enableBlockRightResize={isAllowed}