From 785a6e887146d3b4ee9dc90b5d6c67315c0a5308 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Fri, 11 Aug 2023 15:59:13 +0530 Subject: [PATCH] chore: gantt chart resizable blocks, y-axis drag and drop (#1810) * chore: gantt chart resizable blocks * chore: right scroll added * chore: left scroll added * fix: build errors * chore: remove unnecessary console logs * chore: add block type and remove info toggle * feat: gantt chart blocks y-axis drag and drop * chore: disable drag flag * fix: y-axis drag mutation * fix: scroll container undefined error * fix: negative scroll * fix: negative scroll * style: blocks tooltip consistency --- .../core/filters/issues-view-filter.tsx | 229 +++++++-------- .../cycles/cycles-list-gantt-chart.tsx | 103 ++++--- .../cycles/cycles-list/all-cycles-list.tsx | 4 +- .../cycles-list/completed-cycles-list.tsx | 4 +- .../cycles/cycles-list/draft-cycles-list.tsx | 4 +- .../cycles-list/upcoming-cycles-list.tsx | 4 +- apps/app/components/cycles/cycles-view.tsx | 7 +- apps/app/components/cycles/gantt-chart.tsx | 98 ++----- .../components/gantt-chart/blocks/block.tsx | 103 +++++++ .../gantt-chart/blocks/blocks-display.tsx | 178 ++++++++++++ .../components/gantt-chart/blocks/index.ts | 2 + .../components/gantt-chart/blocks/index.tsx | 82 ------ .../components/gantt-chart/chart/bi-week.tsx | 2 +- apps/app/components/gantt-chart/chart/day.tsx | 2 +- .../components/gantt-chart/chart/hours.tsx | 2 +- .../components/gantt-chart/chart/index.tsx | 57 ++-- .../components/gantt-chart/chart/month.tsx | 43 +-- .../components/gantt-chart/chart/quarter.tsx | 2 +- .../app/components/gantt-chart/chart/week.tsx | 2 +- .../app/components/gantt-chart/chart/year.tsx | 2 +- .../gantt-chart/helpers/block-structure.tsx | 14 + .../gantt-chart/helpers/draggable.tsx | 261 ++++++++++-------- .../components/gantt-chart/helpers/index.ts | 1 + .../gantt-chart/hooks/block-update.tsx | 43 +++ apps/app/components/gantt-chart/index.ts | 4 + apps/app/components/gantt-chart/root.tsx | 15 +- .../app/components/gantt-chart/types/index.ts | 24 +- .../gantt-chart/views/month-view.ts | 40 ++- .../components/gantt-chart/views/year-view.ts | 2 - apps/app/components/issues/gantt-chart.tsx | 96 ++----- apps/app/components/issues/modal.tsx | 6 +- apps/app/components/modules/gantt-chart.tsx | 94 ++----- .../modules/modules-list-gantt-chart.tsx | 99 ++++--- apps/app/components/project/sidebar-list.tsx | 4 +- apps/app/components/views/gantt-chart.tsx | 90 ++---- apps/app/constants/fetch-keys.ts | 14 +- .../hooks/gantt-chart/cycle-issues-view.tsx | 18 +- apps/app/hooks/gantt-chart/issue-view.tsx | 16 +- .../hooks/gantt-chart/module-issues-view.tsx | 18 +- .../hooks/gantt-chart/view-issues-view.tsx | 13 +- apps/app/layouts/app-layout/app-sidebar.tsx | 1 + .../projects/[projectId]/modules/index.tsx | 6 +- apps/app/services/modules.service.ts | 4 +- apps/app/types/cycles.d.ts | 1 + apps/app/types/modules.d.ts | 1 + yarn.lock | 142 +++++----- 46 files changed, 1109 insertions(+), 848 deletions(-) create mode 100644 apps/app/components/gantt-chart/blocks/block.tsx create mode 100644 apps/app/components/gantt-chart/blocks/blocks-display.tsx create mode 100644 apps/app/components/gantt-chart/blocks/index.ts delete mode 100644 apps/app/components/gantt-chart/blocks/index.tsx create mode 100644 apps/app/components/gantt-chart/helpers/block-structure.tsx create mode 100644 apps/app/components/gantt-chart/helpers/index.ts create mode 100644 apps/app/components/gantt-chart/hooks/block-update.tsx diff --git a/apps/app/components/core/filters/issues-view-filter.tsx b/apps/app/components/core/filters/issues-view-filter.tsx index 0bdd30563..2fa80c975 100644 --- a/apps/app/components/core/filters/issues-view-filter.tsx +++ b/apps/app/components/core/filters/issues-view-filter.tsx @@ -113,44 +113,46 @@ export const IssuesFilterView: React.FC = () => { ))} )} - { - const key = option.key as keyof typeof filters; + {issueView !== "gantt_chart" && ( + { + const key = option.key as keyof typeof filters; - if (key === "target_date") { - const valueExists = checkIfArraysHaveSameElements( - filters.target_date ?? [], - option.value - ); - - setFilters({ - target_date: valueExists ? null : option.value, - }); - } else { - const valueExists = filters[key]?.includes(option.value); - - if (valueExists) - setFilters( - { - [option.key]: ((filters[key] ?? []) as any[])?.filter( - (val) => val !== option.value - ), - }, - !Boolean(viewId) + if (key === "target_date") { + const valueExists = checkIfArraysHaveSameElements( + filters.target_date ?? [], + option.value ); - else - setFilters( - { - [option.key]: [...((filters[key] ?? []) as any[]), option.value], - }, - !Boolean(viewId) - ); - } - }} - direction="left" - height="rg" - /> + + setFilters({ + target_date: valueExists ? null : option.value, + }); + } else { + const valueExists = filters[key]?.includes(option.value); + + if (valueExists) + setFilters( + { + [option.key]: ((filters[key] ?? []) as any[])?.filter( + (val) => val !== option.value + ), + }, + !Boolean(viewId) + ); + else + setFilters( + { + [option.key]: [...((filters[key] ?? []) as any[]), option.value], + }, + !Boolean(viewId) + ); + } + }} + direction="left" + height="rg" + /> + )} {({ open }) => ( <> @@ -177,8 +179,9 @@ export const IssuesFilterView: React.FC = () => {
- {issueView !== "calendar" && issueView !== "spreadsheet" && ( - <> + {issueView !== "calendar" && + issueView !== "spreadsheet" && + issueView !== "gantt_chart" && (

Group by

@@ -206,34 +209,34 @@ export const IssuesFilterView: React.FC = () => {
-
-

Order by

-
- option.key === orderBy)?.name ?? - "Select" - } - className="!w-full" - buttonClassName="w-full" - > - {ORDER_BY_OPTIONS.map((option) => - groupByProperty === "priority" && - option.key === "priority" ? null : ( - { - setOrderBy(option.key); - }} - > - {option.name} - - ) - )} - -
+ )} + {issueView !== "calendar" && issueView !== "spreadsheet" && ( +
+

Order by

+
+ option.key === orderBy)?.name ?? + "Select" + } + className="!w-full" + buttonClassName="w-full" + > + {ORDER_BY_OPTIONS.map((option) => + groupByProperty === "priority" && option.key === "priority" ? null : ( + { + setOrderBy(option.key); + }} + > + {option.name} + + ) + )} +
- +
)}

Issue type

@@ -263,16 +266,19 @@ export const IssuesFilterView: React.FC = () => {
{issueView !== "calendar" && issueView !== "spreadsheet" && ( - <> -
-

Show sub-issues

-
- setShowSubIssues(!showSubIssues)} - /> -
+
+

Show sub-issues

+
+ setShowSubIssues(!showSubIssues)} + />
+
+ )} + {issueView !== "calendar" && + issueView !== "spreadsheet" && + issueView !== "gantt_chart" && (

Show empty states

@@ -282,6 +288,10 @@ export const IssuesFilterView: React.FC = () => { />
+ )} + {issueView !== "calendar" && + issueView !== "spreadsheet" && + issueView !== "gantt_chart" && (
- - )} + )}
-
-

Display Properties

-
- {Object.keys(properties).map((key) => { - if (key === "estimate" && !isEstimateActive) return null; + {issueView !== "gantt_chart" && ( +
+

Display Properties

+
+ {Object.keys(properties).map((key) => { + if (key === "estimate" && !isEstimateActive) return null; - if ( - issueView === "spreadsheet" && - (key === "attachment_count" || - key === "link" || - key === "sub_issue_count") - ) - return null; + if ( + issueView === "spreadsheet" && + (key === "attachment_count" || + key === "link" || + key === "sub_issue_count") + ) + return null; - if ( - issueView !== "spreadsheet" && - (key === "created_on" || key === "updated_on") - ) - return null; + if ( + issueView !== "spreadsheet" && + (key === "created_on" || key === "updated_on") + ) + return null; - return ( - - ); - })} + return ( + + ); + })} +
-
+ )}
diff --git a/apps/app/components/cycles/cycles-list-gantt-chart.tsx b/apps/app/components/cycles/cycles-list-gantt-chart.tsx index 938fd9a39..ea66f0929 100644 --- a/apps/app/components/cycles/cycles-list-gantt-chart.tsx +++ b/apps/app/components/cycles/cycles-list-gantt-chart.tsx @@ -1,21 +1,28 @@ import { FC } from "react"; -// next imports -import Link from "next/link"; + import { useRouter } from "next/router"; + +import { KeyedMutator } from "swr"; + +// services +import cyclesService from "services/cycles.service"; +// hooks +import useUser from "hooks/use-user"; // components -import { GanttChartRoot } from "components/gantt-chart"; -// ui -import { Tooltip } from "components/ui"; +import { CycleGanttBlock, GanttChartRoot, IBlockUpdateData } from "components/gantt-chart"; // types import { ICycle } from "types"; type Props = { cycles: ICycle[]; + mutateCycles: KeyedMutator; }; -export const CyclesListGanttChartView: FC = ({ cycles }) => { +export const CyclesListGanttChartView: FC = ({ cycles, mutateCycles }) => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug } = router.query; + + const { user } = useUser(); // rendering issues on gantt sidebar const GanttSidebarBlockView = ({ data }: any) => ( @@ -28,53 +35,65 @@ export const CyclesListGanttChartView: FC = ({ cycles }) => {
); - // rendering issues on gantt card - const GanttBlockView = ({ data }: { data: ICycle }) => ( - - -
- -
- {data?.name} -
-
-
- - ); + const handleCycleUpdate = (cycle: ICycle, payload: IBlockUpdateData) => { + if (!workspaceSlug || !user) return; - // handle gantt issue start date and target date - const handleUpdateDates = async (data: any) => { - const payload = { - id: data?.id, - start_date: data?.start_date, - target_date: data?.target_date, - }; + mutateCycles((prevData) => { + if (!prevData) return prevData; + + const newList = prevData.map((p) => ({ + ...p, + ...(p.id === cycle.id + ? { + start_date: payload.start_date ? payload.start_date : p.start_date, + target_date: payload.target_date ? payload.target_date : p.end_date, + sort_order: payload.sort_order ? payload.sort_order.newSortOrder : p.sort_order, + } + : {}), + })); + + if (payload.sort_order) { + const removedElement = newList.splice(payload.sort_order.sourceIndex, 1)[0]; + newList.splice(payload.sort_order.destinationIndex, 0, removedElement); + } + + return newList; + }, false); + + const newPayload: any = { ...payload }; + + if (newPayload.sort_order && payload.sort_order) + newPayload.sort_order = payload.sort_order.newSortOrder; + + cyclesService + .patchCycle(workspaceSlug.toString(), cycle.project, cycle.id, newPayload, user) + .finally(() => mutateCycles()); }; - const blockFormat = (blocks: any) => + const blockFormat = (blocks: ICycle[]) => blocks && blocks.length > 0 - ? blocks.map((_block: any) => { - if (_block?.start_date && _block.target_date) console.log("_block", _block); - return { - start_date: new Date(_block.created_at), - target_date: new Date(_block.updated_at), - data: _block, - }; - }) + ? blocks + .filter((b) => b.start_date && b.end_date) + .map((block) => ({ + data: block, + id: block.id, + sort_order: block.sort_order, + start_date: new Date(block.start_date ?? ""), + target_date: new Date(block.end_date ?? ""), + })) : []; return (
handleCycleUpdate(block, payload)} sidebarBlockRender={(data: any) => } - blockRender={(data: any) => } + blockRender={(data: any) => } + enableLeftDrag={false} + enableRightDrag={false} />
); diff --git a/apps/app/components/cycles/cycles-list/all-cycles-list.tsx b/apps/app/components/cycles/cycles-list/all-cycles-list.tsx index 7ebd92a50..26bf0799c 100644 --- a/apps/app/components/cycles/cycles-list/all-cycles-list.tsx +++ b/apps/app/components/cycles/cycles-list/all-cycles-list.tsx @@ -17,7 +17,7 @@ export const AllCyclesList: React.FC = ({ viewType }) => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; - const { data: allCyclesList } = useSWR( + const { data: allCyclesList, mutate } = useSWR( workspaceSlug && projectId ? CYCLES_LIST(projectId.toString()) : null, workspaceSlug && projectId ? () => @@ -25,5 +25,5 @@ export const AllCyclesList: React.FC = ({ viewType }) => { : null ); - return ; + return ; }; diff --git a/apps/app/components/cycles/cycles-list/completed-cycles-list.tsx b/apps/app/components/cycles/cycles-list/completed-cycles-list.tsx index 79a427d95..552596d93 100644 --- a/apps/app/components/cycles/cycles-list/completed-cycles-list.tsx +++ b/apps/app/components/cycles/cycles-list/completed-cycles-list.tsx @@ -17,7 +17,7 @@ export const CompletedCyclesList: React.FC = ({ viewType }) => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; - const { data: completedCyclesList } = useSWR( + const { data: completedCyclesList, mutate } = useSWR( workspaceSlug && projectId ? COMPLETED_CYCLES_LIST(projectId.toString()) : null, workspaceSlug && projectId ? () => @@ -29,5 +29,5 @@ export const CompletedCyclesList: React.FC = ({ viewType }) => { : null ); - return ; + return ; }; diff --git a/apps/app/components/cycles/cycles-list/draft-cycles-list.tsx b/apps/app/components/cycles/cycles-list/draft-cycles-list.tsx index fd2dccc93..05815dc9c 100644 --- a/apps/app/components/cycles/cycles-list/draft-cycles-list.tsx +++ b/apps/app/components/cycles/cycles-list/draft-cycles-list.tsx @@ -17,7 +17,7 @@ export const DraftCyclesList: React.FC = ({ viewType }) => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; - const { data: draftCyclesList } = useSWR( + const { data: draftCyclesList, mutate } = useSWR( workspaceSlug && projectId ? DRAFT_CYCLES_LIST(projectId.toString()) : null, workspaceSlug && projectId ? () => @@ -25,5 +25,5 @@ export const DraftCyclesList: React.FC = ({ viewType }) => { : null ); - return ; + return ; }; diff --git a/apps/app/components/cycles/cycles-list/upcoming-cycles-list.tsx b/apps/app/components/cycles/cycles-list/upcoming-cycles-list.tsx index 140727cb8..eba212af2 100644 --- a/apps/app/components/cycles/cycles-list/upcoming-cycles-list.tsx +++ b/apps/app/components/cycles/cycles-list/upcoming-cycles-list.tsx @@ -17,7 +17,7 @@ export const UpcomingCyclesList: React.FC = ({ viewType }) => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; - const { data: upcomingCyclesList } = useSWR( + const { data: upcomingCyclesList, mutate } = useSWR( workspaceSlug && projectId ? UPCOMING_CYCLES_LIST(projectId.toString()) : null, workspaceSlug && projectId ? () => @@ -29,5 +29,5 @@ export const UpcomingCyclesList: React.FC = ({ viewType }) => { : null ); - return ; + return ; }; diff --git a/apps/app/components/cycles/cycles-view.tsx b/apps/app/components/cycles/cycles-view.tsx index 6f3fa336a..ede13b65e 100644 --- a/apps/app/components/cycles/cycles-view.tsx +++ b/apps/app/components/cycles/cycles-view.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { useRouter } from "next/router"; -import { mutate } from "swr"; +import { KeyedMutator, mutate } from "swr"; // services import cyclesService from "services/cycles.service"; @@ -35,10 +35,11 @@ import { type Props = { cycles: ICycle[] | undefined; + mutateCycles: KeyedMutator; viewType: string | null; }; -export const CyclesView: React.FC = ({ cycles, viewType }) => { +export const CyclesView: React.FC = ({ cycles, mutateCycles, viewType }) => { const [createUpdateCycleModal, setCreateUpdateCycleModal] = useState(false); const [selectedCycleToUpdate, setSelectedCycleToUpdate] = useState(null); @@ -202,7 +203,7 @@ export const CyclesView: React.FC = ({ cycles, viewType }) => { ))}
) : ( - + ) ) : (
diff --git a/apps/app/components/cycles/gantt-chart.tsx b/apps/app/components/cycles/gantt-chart.tsx index 0deb10079..fe276b50d 100644 --- a/apps/app/components/cycles/gantt-chart.tsx +++ b/apps/app/components/cycles/gantt-chart.tsx @@ -1,20 +1,27 @@ -import { FC } from "react"; -// next imports -import Link from "next/link"; import { useRouter } from "next/router"; -// components -import { GanttChartRoot } from "components/gantt-chart"; -// ui -import { Tooltip } from "components/ui"; + // hooks +import useIssuesView from "hooks/use-issues-view"; +import useUser from "hooks/use-user"; import useGanttChartCycleIssues from "hooks/gantt-chart/cycle-issues-view"; +import { updateGanttIssue } from "components/gantt-chart/hooks/block-update"; +// components +import { + GanttChartRoot, + IssueGanttBlock, + renderIssueBlocksStructure, +} from "components/gantt-chart"; +// types +import { IIssue } from "types"; -type Props = {}; - -export const CycleIssuesGanttChartView: FC = ({}) => { +export const CycleIssuesGanttChartView = () => { const router = useRouter(); const { workspaceSlug, projectId, cycleId } = router.query; + const { orderBy } = useIssuesView(); + + const { user } = useUser(); + const { ganttIssues, mutateGanttIssues } = useGanttChartCycleIssues( workspaceSlug as string, projectId as string, @@ -32,77 +39,18 @@ export const CycleIssuesGanttChartView: FC = ({}) => {
); - // rendering issues on gantt card - const GanttBlockView = ({ data }: any) => ( - - -
- -
- {data?.name} -
-
- {data.infoToggle && ( - -
- - info - -
-
- )} -
- - ); - - // handle gantt issue start date and target date - const handleUpdateDates = async (data: any) => { - const payload = { - id: data?.id, - start_date: data?.start_date, - target_date: data?.target_date, - }; - - console.log("payload", payload); - }; - - const blockFormat = (blocks: any) => - blocks && blocks.length > 0 - ? blocks.map((_block: any) => { - let startDate = new Date(_block.created_at); - let targetDate = new Date(_block.updated_at); - let infoToggle = true; - - if (_block?.start_date && _block.target_date) { - startDate = _block?.start_date; - targetDate = _block.target_date; - infoToggle = false; - } - - return { - start_date: new Date(startDate), - target_date: new Date(targetDate), - infoToggle: infoToggle, - data: _block, - }; - }) - : []; - return (
+ updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString()) + } sidebarBlockRender={(data: any) => } - blockRender={(data: any) => } + blockRender={(data: any) => } + enableReorder={orderBy === "sort_order"} />
); diff --git a/apps/app/components/gantt-chart/blocks/block.tsx b/apps/app/components/gantt-chart/blocks/block.tsx new file mode 100644 index 000000000..52fd1fe52 --- /dev/null +++ b/apps/app/components/gantt-chart/blocks/block.tsx @@ -0,0 +1,103 @@ +import Link from "next/link"; +import { useRouter } from "next/router"; + +// ui +import { Tooltip } from "components/ui"; +// helpers +import { renderShortDate } from "helpers/date-time.helper"; +// types +import { ICycle, IIssue, IModule } from "types"; +// constants +import { MODULE_STATUS } from "constants/module"; + +export const IssueGanttBlock = ({ issue }: { issue: IIssue }) => { + const router = useRouter(); + const { workspaceSlug } = router.query; + + return ( + + +
+ +
{issue.name}
+
+ {renderShortDate(issue.start_date ?? "")} to{" "} + {renderShortDate(issue.target_date ?? "")} +
+
+ } + position="top-left" + > +
+ {issue.name} +
+ +
+ + ); +}; + +export const CycleGanttBlock = ({ cycle }: { cycle: ICycle }) => { + const router = useRouter(); + const { workspaceSlug } = router.query; + + return ( + + +
+ +
{cycle.name}
+
+ {renderShortDate(cycle.start_date ?? "")} to {renderShortDate(cycle.end_date ?? "")} +
+
+ } + position="top-left" + > +
+ {cycle.name} +
+ +
+ + ); +}; + +export const ModuleGanttBlock = ({ module }: { module: IModule }) => { + const router = useRouter(); + const { workspaceSlug } = router.query; + + return ( + + +
s.value === module.status)?.color }} + /> + +
{module.name}
+
+ {renderShortDate(module.start_date ?? "")} to{" "} + {renderShortDate(module.target_date ?? "")} +
+
+ } + position="top-left" + > +
+ {module.name} +
+ +
+ + ); +}; diff --git a/apps/app/components/gantt-chart/blocks/blocks-display.tsx b/apps/app/components/gantt-chart/blocks/blocks-display.tsx new file mode 100644 index 000000000..fd43c733e --- /dev/null +++ b/apps/app/components/gantt-chart/blocks/blocks-display.tsx @@ -0,0 +1,178 @@ +import { FC } from "react"; + +// react-beautiful-dnd +import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd"; +import StrictModeDroppable from "components/dnd/StrictModeDroppable"; +// helpers +import { ChartDraggable } from "../helpers/draggable"; +import { renderDateFormat } from "helpers/date-time.helper"; +// types +import { IBlockUpdateData, IGanttBlock } from "../types"; + +export const GanttChartBlocks: FC<{ + itemsContainerWidth: number; + blocks: IGanttBlock[] | null; + sidebarBlockRender: FC; + blockRender: FC; + blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void; + enableLeftDrag: boolean; + enableRightDrag: boolean; + enableReorder: boolean; +}> = ({ + itemsContainerWidth, + blocks, + sidebarBlockRender, + blockRender, + blockUpdateHandler, + enableLeftDrag, + enableRightDrag, + enableReorder, +}) => { + const handleChartBlockPosition = ( + block: IGanttBlock, + totalBlockShifts: number, + dragDirection: "left" | "right" + ) => { + let updatedDate = new Date(); + + if (dragDirection === "left") { + const originalDate = new Date(block.start_date); + + const currentDay = originalDate.getDate(); + updatedDate = new Date(originalDate); + + updatedDate.setDate(currentDay - totalBlockShifts); + } else { + const originalDate = new Date(block.target_date); + + const currentDay = originalDate.getDate(); + updatedDate = new Date(originalDate); + + updatedDate.setDate(currentDay + totalBlockShifts); + } + + blockUpdateHandler(block.data, { + [dragDirection === "left" ? "start_date" : "target_date"]: renderDateFormat(updatedDate), + }); + }; + + const handleOrderChange = (result: DropResult) => { + if (!blocks) return; + + const { source, destination, draggableId } = result; + + if (!destination) return; + + if (source.index === destination.index && document) { + // const draggedBlock = document.querySelector(`#${draggableId}`) as HTMLElement; + // const blockStyles = window.getComputedStyle(draggedBlock); + + // console.log(blockStyles.marginLeft); + + return; + } + + let updatedSortOrder = blocks[source.index].sort_order; + + if (destination.index === 0) updatedSortOrder = blocks[0].sort_order - 1000; + else if (destination.index === blocks.length - 1) + updatedSortOrder = blocks[blocks.length - 1].sort_order + 1000; + 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; + } + + const removedElement = blocks.splice(source.index, 1)[0]; + blocks.splice(destination.index, 0, removedElement); + + blockUpdateHandler(removedElement.data, { + sort_order: { + destinationIndex: destination.index, + newSortOrder: updatedSortOrder, + sourceIndex: source.index, + }, + }); + }; + + return ( +
+ + + {(droppableProvided, droppableSnapshot) => ( +
+ <> + {blocks && + blocks.length > 0 && + blocks.map( + (block, index: number) => + block.start_date && + block.target_date && ( + + {(provided) => ( +
+ handleChartBlockPosition(block, ...args)} + enableLeftDrag={enableLeftDrag} + enableRightDrag={enableRightDrag} + provided={provided} + > +
+ {blockRender({ + ...block.data, + })} +
+
+
+ )} +
+ ) + )} + {droppableProvided.placeholder} + +
+ )} +
+
+ + {/* sidebar */} + {/*
+ {blocks && + blocks.length > 0 && + blocks.map((block: any, _idx: number) => ( +
+ {sidebarBlockRender(block?.data)} +
+ ))} +
*/} +
+ ); +}; diff --git a/apps/app/components/gantt-chart/blocks/index.ts b/apps/app/components/gantt-chart/blocks/index.ts new file mode 100644 index 000000000..8773b2797 --- /dev/null +++ b/apps/app/components/gantt-chart/blocks/index.ts @@ -0,0 +1,2 @@ +export * from "./block"; +export * from "./blocks-display"; diff --git a/apps/app/components/gantt-chart/blocks/index.tsx b/apps/app/components/gantt-chart/blocks/index.tsx deleted file mode 100644 index dcc3a2910..000000000 --- a/apps/app/components/gantt-chart/blocks/index.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { FC, useEffect, useState } from "react"; -// helpers -import { ChartDraggable } from "../helpers/draggable"; -// data -import { datePreview } from "../data"; - -export const GanttChartBlocks: FC<{ - itemsContainerWidth: number; - blocks: null | any[]; - sidebarBlockRender: FC; - blockRender: FC; -}> = ({ itemsContainerWidth, blocks, sidebarBlockRender, blockRender }) => { - const handleChartBlockPosition = (block: any) => { - // setChartBlocks((prevData: any) => - // prevData.map((_block: any) => (_block?.data?.id == block?.data?.id ? block : _block)) - // ); - }; - - return ( -
-
- {blocks && - blocks.length > 0 && - blocks.map((block: any, _idx: number) => ( - <> - {block.start_date && block.target_date && ( - -
-
-
- {block?.start_date ? datePreview(block?.start_date) : "-"} -
-
- -
- {blockRender({ - ...block?.data, - infoToggle: block?.infoToggle ? true : false, - })} -
- -
-
- {block?.target_date ? datePreview(block?.target_date) : "-"} -
-
-
-
- )} - - ))} -
- - {/* sidebar */} - {/*
- {blocks && - blocks.length > 0 && - blocks.map((block: any, _idx: number) => ( -
- {sidebarBlockRender(block?.data)} -
- ))} -
*/} -
- ); -}; diff --git a/apps/app/components/gantt-chart/chart/bi-week.tsx b/apps/app/components/gantt-chart/chart/bi-week.tsx index 1e1173ad4..3637f88ea 100644 --- a/apps/app/components/gantt-chart/chart/bi-week.tsx +++ b/apps/app/components/gantt-chart/chart/bi-week.tsx @@ -25,7 +25,7 @@ export const BiWeekChartView: FC = () => {
= () => {
= () => {
void; + blocks: IGanttBlock[] | null; + blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void; sidebarBlockRender: FC; blockRender: FC; + enableLeftDrag: boolean; + enableRightDrag: boolean; + enableReorder: boolean; }; export const ChartViewRoot: FC = ({ @@ -54,6 +52,9 @@ export const ChartViewRoot: FC = ({ blockUpdateHandler, sidebarBlockRender, blockRender, + enableLeftDrag, + enableRightDrag, + enableReorder, }) => { const { currentView, currentViewData, renderView, dispatch, allViews } = useChart(); @@ -62,13 +63,13 @@ export const ChartViewRoot: FC = ({ const [blocksSidebarView, setBlocksSidebarView] = useState(false); // blocks state management starts - const [chartBlocks, setChartBlocks] = useState(null); + const [chartBlocks, setChartBlocks] = useState(null); - const renderBlockStructure = (view: any, blocks: any) => + const renderBlockStructure = (view: any, blocks: IGanttBlock[]) => blocks && blocks.length > 0 - ? blocks.map((_block: any) => ({ - ..._block, - position: getMonthChartItemPositionWidthInMonth(view, _block), + ? blocks.map((block: any) => ({ + ...block, + position: getMonthChartItemPositionWidthInMonth(view, block), })) : []; @@ -154,13 +155,14 @@ export const ChartViewRoot: FC = ({ const updatingCurrentLeftScrollPosition = (width: number) => { const scrollContainer = document.getElementById("scroll-container") as HTMLElement; - scrollContainer.scrollLeft = width + scrollContainer.scrollLeft; - setItemsContainerWidth(width + scrollContainer.scrollLeft); + scrollContainer.scrollLeft = width + scrollContainer?.scrollLeft; + setItemsContainerWidth(width + scrollContainer?.scrollLeft); }; const handleScrollToCurrentSelectedDate = (currentState: ChartDataType, date: Date) => { const scrollContainer = document.getElementById("scroll-container") as HTMLElement; - const clientVisibleWidth: number = scrollContainer.clientWidth; + + const clientVisibleWidth: number = scrollContainer?.clientWidth; let scrollWidth: number = 0; let daysDifference: number = 0; @@ -189,9 +191,9 @@ export const ChartViewRoot: FC = ({ const onScroll = () => { const scrollContainer = document.getElementById("scroll-container") as HTMLElement; - const scrollWidth: number = scrollContainer.scrollWidth; - const clientVisibleWidth: number = scrollContainer.clientWidth; - const currentScrollPosition: number = scrollContainer.scrollLeft; + const scrollWidth: number = scrollContainer?.scrollWidth; + const clientVisibleWidth: number = scrollContainer?.clientWidth; + const currentScrollPosition: number = scrollContainer?.scrollLeft; const approxRangeLeft: number = scrollWidth >= clientVisibleWidth + 1000 ? 1000 : scrollWidth - clientVisibleWidth; @@ -207,6 +209,7 @@ export const ChartViewRoot: FC = ({ const scrollContainer = document.getElementById("scroll-container") as HTMLElement; scrollContainer.addEventListener("scroll", onScroll); + return () => { scrollContainer.removeEventListener("scroll", onScroll); }; @@ -242,7 +245,7 @@ export const ChartViewRoot: FC = ({
*/} {/* chart header */} -
+
{/*
setBlocksSidebarView(() => !blocksSidebarView)} @@ -301,8 +304,8 @@ export const ChartViewRoot: FC = ({
setFullScreenMode(() => !fullScreenMode)} + className="transition-all border border-custom-border-200 p-1 flex justify-center items-center cursor-pointer rounded-sm hover:bg-custom-background-80" + onClick={() => setFullScreenMode((prevData) => !prevData)} > {fullScreenMode ? ( @@ -325,6 +328,10 @@ export const ChartViewRoot: FC = ({ blocks={chartBlocks} sidebarBlockRender={sidebarBlockRender} blockRender={blockRender} + blockUpdateHandler={blockUpdateHandler} + enableLeftDrag={enableLeftDrag} + enableRightDrag={enableRightDrag} + enableReorder={enableReorder} /> )} diff --git a/apps/app/components/gantt-chart/chart/month.tsx b/apps/app/components/gantt-chart/chart/month.tsx index 68e517960..b6c68b5d1 100644 --- a/apps/app/components/gantt-chart/chart/month.tsx +++ b/apps/app/components/gantt-chart/chart/month.tsx @@ -1,48 +1,55 @@ import { FC } from "react"; -// context + +// hooks import { useChart } from "../hooks"; +// types +import { IMonthBlock } from "../views"; export const MonthChartView: FC = () => { - const { currentView, currentViewData, renderView, dispatch, allViews } = useChart(); + const { currentViewData, renderView } = useChart(); + + const monthBlocks: IMonthBlock[] = renderView; return ( <> -
- {renderView && - renderView.length > 0 && - renderView.map((_itemRoot: any, _idxRoot: any) => ( -
+
+ {monthBlocks && + monthBlocks.length > 0 && + monthBlocks.map((block, _idxRoot) => ( +
- {_itemRoot?.title} + {block?.title}
-
- {_itemRoot.children && - _itemRoot.children.length > 0 && - _itemRoot.children.map((_item: any, _idx: any) => ( +
+ {block?.children && + block?.children.length > 0 && + block?.children.map((monthDay, _idx) => (
-
{_item.title}
+
{monthDay?.title}
- {_item?.today && ( -
+ {monthDay?.today && ( +
)}
diff --git a/apps/app/components/gantt-chart/chart/quarter.tsx b/apps/app/components/gantt-chart/chart/quarter.tsx index 67605b6b5..abe56c83e 100644 --- a/apps/app/components/gantt-chart/chart/quarter.tsx +++ b/apps/app/components/gantt-chart/chart/quarter.tsx @@ -25,7 +25,7 @@ export const QuarterChartView: FC = () => {
= () => {
= () => {
+ blocks && blocks.length > 0 + ? blocks.map((block) => ({ + data: block, + id: block.id, + sort_order: block.sort_order, + start_date: new Date(block.start_date ?? ""), + target_date: new Date(block.target_date ?? ""), + })) + : []; diff --git a/apps/app/components/gantt-chart/helpers/draggable.tsx b/apps/app/components/gantt-chart/helpers/draggable.tsx index a5404cc8e..8a85a0dd3 100644 --- a/apps/app/components/gantt-chart/helpers/draggable.tsx +++ b/apps/app/components/gantt-chart/helpers/draggable.tsx @@ -1,138 +1,155 @@ -import { useState, useRef } from "react"; +import React, { useRef, useState } from "react"; -export const ChartDraggable = ({ children, block, handleBlock, className }: any) => { - const [dragging, setDragging] = useState(false); +// react-beautiful-dnd +import { DraggableProvided } from "react-beautiful-dnd"; +import { useChart } from "../hooks"; +// types +import { IGanttBlock } from "../types"; - const [chartBlockPositionLeft, setChartBlockPositionLeft] = useState(0); - const [blockPositionLeft, setBlockPositionLeft] = useState(0); - const [dragBlockOffsetX, setDragBlockOffsetX] = useState(0); +type Props = { + children: any; + block: IGanttBlock; + handleBlock: (totalBlockShifts: number, dragDirection: "left" | "right") => void; + enableLeftDrag: boolean; + enableRightDrag: boolean; + provided: DraggableProvided; +}; - const handleMouseDown = (event: any) => { - const chartBlockPositionLeft: number = block.position.marginLeft; - const blockPositionLeft: number = event.target.getBoundingClientRect().left; - const dragBlockOffsetX: number = event.clientX - event.target.getBoundingClientRect().left; +export const ChartDraggable: React.FC = ({ + children, + block, + handleBlock, + enableLeftDrag = true, + enableRightDrag = true, + provided, +}) => { + const [isLeftResizing, setIsLeftResizing] = useState(false); + const [isRightResizing, setIsRightResizing] = useState(false); - console.log("--------------------"); - console.log("chartBlockPositionLeft", chartBlockPositionLeft); - console.log("blockPositionLeft", blockPositionLeft); - console.log("dragBlockOffsetX", dragBlockOffsetX); - console.log("-->"); + const parentDivRef = useRef(null); + const resizableRef = useRef(null); - setDragging(true); - setChartBlockPositionLeft(chartBlockPositionLeft); - setBlockPositionLeft(blockPositionLeft); - setDragBlockOffsetX(dragBlockOffsetX); - }; + const { currentViewData } = useChart(); - const handleMouseMove = (event: any) => { - if (!dragging) return; + const handleDrag = (dragDirection: "left" | "right") => { + if (!currentViewData || !resizableRef.current || !parentDivRef.current || !block.position) + return; - const currentBlockPosition = event.clientX - dragBlockOffsetX; - console.log("currentBlockPosition", currentBlockPosition); - if (currentBlockPosition <= blockPositionLeft) { - const updatedPosition = chartBlockPositionLeft - (blockPositionLeft - currentBlockPosition); - console.log("updatedPosition", updatedPosition); - handleBlock({ ...block, position: { ...block.position, marginLeft: updatedPosition } }); - } else { - const updatedPosition = chartBlockPositionLeft + (blockPositionLeft - currentBlockPosition); - console.log("updatedPosition", updatedPosition); - handleBlock({ ...block, position: { ...block.position, marginLeft: updatedPosition } }); - } - console.log("--------------------"); - }; + const resizableDiv = resizableRef.current; + const parentDiv = parentDivRef.current; - const handleMouseUp = () => { - setDragging(false); - setChartBlockPositionLeft(0); - setBlockPositionLeft(0); - setDragBlockOffsetX(0); + const columnWidth = currentViewData.data.width; + + const blockInitialWidth = + resizableDiv.clientWidth ?? parseInt(block.position.width.toString(), 10); + + let initialWidth = resizableDiv.clientWidth ?? parseInt(block.position.width.toString(), 10); + let initialMarginLeft = block?.position?.marginLeft; + + const handleMouseMove = (e: MouseEvent) => { + if (!window) return; + + let delWidth = 0; + + const posFromLeft = e.clientX; + const posFromRight = window.innerWidth - e.clientX; + + const scrollContainer = document.querySelector("#scroll-container") as HTMLElement; + const appSidebar = document.querySelector("#app-sidebar") as HTMLElement; + + // manually scroll to left if reached the left end while dragging + if (posFromLeft - appSidebar.clientWidth <= 70) { + if (e.movementX > 0) return; + + delWidth = dragDirection === "left" ? -5 : 5; + + scrollContainer.scrollBy(-1 * Math.abs(delWidth), 0); + } else delWidth = dragDirection === "left" ? -1 * e.movementX : e.movementX; + + // manually scroll to right if reached the right end while dragging + if (posFromRight <= 70) { + if (e.movementX < 0) return; + + delWidth = dragDirection === "left" ? -5 : 5; + + scrollContainer.scrollBy(Math.abs(delWidth), 0); + } else delWidth = dragDirection === "left" ? -1 * e.movementX : e.movementX; + + // calculate new width and update the initialMarginLeft using += + const newWidth = Math.round((initialWidth += delWidth) / columnWidth) * columnWidth; + + // block needs to be at least 1 column wide + if (newWidth < columnWidth) return; + + resizableDiv.style.width = `${newWidth}px`; + if (block.position) block.position.width = newWidth; + + // update the margin left of the block if dragging from the left end + if (dragDirection === "left") { + // calculate new marginLeft and update the initial marginLeft using -= + const newMarginLeft = + Math.round((initialMarginLeft -= delWidth) / columnWidth) * columnWidth; + + parentDiv.style.marginLeft = `${newMarginLeft}px`; + if (block.position) block.position.marginLeft = newMarginLeft; + } + }; + + const handleMouseUp = () => { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + + const totalBlockShifts = Math.ceil( + (resizableDiv.clientWidth - blockInitialWidth) / columnWidth + ); + + handleBlock(totalBlockShifts, dragDirection); + }; + + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); }; return (
- {children} + {enableLeftDrag && ( + <> +
handleDrag("left")} + onMouseEnter={() => setIsLeftResizing(true)} + onMouseLeave={() => setIsLeftResizing(false)} + className="absolute top-1/2 -left-2.5 -translate-y-1/2 z-[1] w-6 h-10 bg-brand-backdrop rounded-md cursor-col-resize" + /> +
+ + )} + {React.cloneElement(children, { ref: resizableRef, ...provided.dragHandleProps })} + {enableRightDrag && ( + <> +
handleDrag("right")} + onMouseEnter={() => setIsRightResizing(true)} + onMouseLeave={() => setIsRightResizing(false)} + className="absolute top-1/2 -right-2.5 -translate-y-1/2 z-[1] w-6 h-6 bg-brand-backdrop rounded-md cursor-col-resize" + /> +
+ + )}
); }; - -// import { useState } from "react"; - -// export const ChartDraggable = ({ children, id, className = "", style }: any) => { -// const [dragging, setDragging] = useState(false); - -// const [chartBlockPositionLeft, setChartBlockPositionLeft] = useState(0); -// const [blockPositionLeft, setBlockPositionLeft] = useState(0); -// const [dragBlockOffsetX, setDragBlockOffsetX] = useState(0); - -// const handleDragStart = (event: any) => { -// // event.dataTransfer.setData("text/plain", event.target.id); - -// const chartBlockPositionLeft: number = parseInt(event.target.style.left.slice(0, -2)); -// const blockPositionLeft: number = event.target.getBoundingClientRect().left; -// const dragBlockOffsetX: number = event.clientX - event.target.getBoundingClientRect().left; - -// console.log("chartBlockPositionLeft", chartBlockPositionLeft); -// console.log("blockPositionLeft", blockPositionLeft); -// console.log("dragBlockOffsetX", dragBlockOffsetX); -// console.log("--------------------"); - -// setDragging(true); -// setChartBlockPositionLeft(chartBlockPositionLeft); -// setBlockPositionLeft(blockPositionLeft); -// setDragBlockOffsetX(dragBlockOffsetX); -// }; - -// const handleDragEnd = () => { -// setDragging(false); -// setChartBlockPositionLeft(0); -// setBlockPositionLeft(0); -// setDragBlockOffsetX(0); -// }; - -// const handleDragOver = (event: any) => { -// event.preventDefault(); -// if (dragging) { -// const scrollContainer = document.getElementById(`block-parent-${id}`) as HTMLElement; -// const currentBlockPosition = event.clientX - dragBlockOffsetX; -// console.log('currentBlockPosition') -// if (currentBlockPosition <= blockPositionLeft) { -// const updatedPosition = chartBlockPositionLeft - (blockPositionLeft - currentBlockPosition); -// console.log("updatedPosition", updatedPosition); -// if (scrollContainer) scrollContainer.style.left = `${updatedPosition}px`; -// } else { -// const updatedPosition = chartBlockPositionLeft + (blockPositionLeft - currentBlockPosition); -// console.log("updatedPosition", updatedPosition); -// if (scrollContainer) scrollContainer.style.left = `${updatedPosition}px`; -// } -// console.log("--------------------"); -// } -// }; - -// const handleDrop = (event: any) => { -// event.preventDefault(); -// setDragging(false); -// setChartBlockPositionLeft(0); -// setBlockPositionLeft(0); -// setDragBlockOffsetX(0); -// }; - -// return ( -//
-// {children} -//
-// ); -// }; diff --git a/apps/app/components/gantt-chart/helpers/index.ts b/apps/app/components/gantt-chart/helpers/index.ts new file mode 100644 index 000000000..c4c919ec0 --- /dev/null +++ b/apps/app/components/gantt-chart/helpers/index.ts @@ -0,0 +1 @@ +export * from "./block-structure"; diff --git a/apps/app/components/gantt-chart/hooks/block-update.tsx b/apps/app/components/gantt-chart/hooks/block-update.tsx new file mode 100644 index 000000000..d9d808b38 --- /dev/null +++ b/apps/app/components/gantt-chart/hooks/block-update.tsx @@ -0,0 +1,43 @@ +import { KeyedMutator } from "swr"; + +// services +import issuesService from "services/issues.service"; +// types +import { ICurrentUserResponse, IIssue } from "types"; +import { IBlockUpdateData } from "../types"; + +export const updateGanttIssue = ( + issue: IIssue, + payload: IBlockUpdateData, + mutate: KeyedMutator, + user: ICurrentUserResponse | undefined, + workspaceSlug: string | undefined +) => { + if (!issue || !workspaceSlug || !user) return; + + mutate((prevData: IIssue[]) => { + if (!prevData) return prevData; + + const newList = prevData.map((p) => ({ + ...p, + ...(p.id === issue.id ? payload : {}), + })); + + if (payload.sort_order) { + const removedElement = newList.splice(payload.sort_order.sourceIndex, 1)[0]; + removedElement.sort_order = payload.sort_order.newSortOrder; + newList.splice(payload.sort_order.destinationIndex, 0, removedElement); + } + + return newList; + }, false); + + const newPayload: any = { ...payload }; + + if (newPayload.sort_order && payload.sort_order) + newPayload.sort_order = payload.sort_order.newSortOrder; + + issuesService + .patchIssue(workspaceSlug, issue.project, issue.id, newPayload, user) + .finally(() => mutate()); +}; diff --git a/apps/app/components/gantt-chart/index.ts b/apps/app/components/gantt-chart/index.ts index 1efe34c51..4520ee194 100644 --- a/apps/app/components/gantt-chart/index.ts +++ b/apps/app/components/gantt-chart/index.ts @@ -1 +1,5 @@ +export * from "./blocks"; +export * from "./helpers"; +export * from "./hooks"; export * from "./root"; +export * from "./types"; diff --git a/apps/app/components/gantt-chart/root.tsx b/apps/app/components/gantt-chart/root.tsx index c52bc55b0..077e8a896 100644 --- a/apps/app/components/gantt-chart/root.tsx +++ b/apps/app/components/gantt-chart/root.tsx @@ -3,15 +3,20 @@ import { FC } from "react"; import { ChartViewRoot } from "./chart"; // context import { ChartContextProvider } from "./contexts"; +// types +import { IBlockUpdateData, IGanttBlock } from "./types"; type GanttChartRootProps = { border?: boolean; title: null | string; loaderTitle: string; - blocks: any; - blockUpdateHandler: (data: any) => void; + blocks: IGanttBlock[] | null; + blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void; sidebarBlockRender: FC; blockRender: FC; + enableLeftDrag?: boolean; + enableRightDrag?: boolean; + enableReorder?: boolean; }; export const GanttChartRoot: FC = ({ @@ -22,6 +27,9 @@ export const GanttChartRoot: FC = ({ blockUpdateHandler, sidebarBlockRender, blockRender, + enableLeftDrag = true, + enableRightDrag = true, + enableReorder = true, }) => ( = ({ blockUpdateHandler={blockUpdateHandler} sidebarBlockRender={sidebarBlockRender} blockRender={blockRender} + enableLeftDrag={enableLeftDrag} + enableRightDrag={enableRightDrag} + enableReorder={enableReorder} /> ); diff --git a/apps/app/components/gantt-chart/types/index.ts b/apps/app/components/gantt-chart/types/index.ts index aa6ebe9da..645fd9c87 100644 --- a/apps/app/components/gantt-chart/types/index.ts +++ b/apps/app/components/gantt-chart/types/index.ts @@ -5,10 +5,32 @@ export type allViewsType = { data: Object | null; }; +export interface IGanttBlock { + data: any; + id: string; + position?: { + marginLeft: number; + width: number; + }; + sort_order: number; + start_date: Date; + target_date: Date; +} + +export interface IBlockUpdateData { + sort_order?: { + destinationIndex: number; + newSortOrder: number; + sourceIndex: number; + }; + start_date?: string; + target_date?: string; +} + export interface ChartContextData { allViews: allViewsType[]; currentView: "hours" | "day" | "week" | "bi_week" | "month" | "quarter" | "year"; - currentViewData: any; + currentViewData: ChartDataType | undefined; renderView: any; } diff --git a/apps/app/components/gantt-chart/views/month-view.ts b/apps/app/components/gantt-chart/views/month-view.ts index 7211a45eb..db21e372b 100644 --- a/apps/app/components/gantt-chart/views/month-view.ts +++ b/apps/app/components/gantt-chart/views/month-view.ts @@ -1,5 +1,5 @@ // types -import { ChartDataType } from "../types"; +import { ChartDataType, IGanttBlock } from "../types"; // data import { weeks, months } from "../data"; // helpers @@ -19,7 +19,35 @@ type GetAllDaysInMonthInMonthViewType = { active: boolean; today: boolean; }; -const getAllDaysInMonthInMonthView = (month: number, year: number) => { + +interface IMonthChild { + active: boolean; + date: Date; + day: number; + dayData: { + key: number; + shortTitle: string; + title: string; + }; + title: string; + today: boolean; + weekNumber: number; +} + +export interface IMonthBlock { + children: IMonthChild[]; + month: number; + monthData: { + key: number; + shortTitle: string; + title: string; + }; + title: string; + year: number; +} +[]; + +const getAllDaysInMonthInMonthView = (month: number, year: number): IMonthChild[] => { const day: GetAllDaysInMonthInMonthViewType[] = []; const numberOfDaysInMonth = getNumberOfDaysInMonth(month, year); const currentDate = new Date(); @@ -45,7 +73,7 @@ const getAllDaysInMonthInMonthView = (month: number, year: number) => { return day; }; -const generateMonthDataByMonthAndYearInMonthView = (month: number, year: number) => { +const generateMonthDataByMonthAndYearInMonthView = (month: number, year: number): IMonthBlock => { const currentMonth: number = month; const currentYear: number = year; @@ -162,7 +190,11 @@ export const getNumberOfDaysBetweenTwoDatesInMonth = (startDate: Date, endDate: return daysDifference; }; -export const getMonthChartItemPositionWidthInMonth = (chartData: ChartDataType, itemData: any) => { +// calc item scroll position and width +export const getMonthChartItemPositionWidthInMonth = ( + chartData: ChartDataType, + itemData: IGanttBlock +) => { let scrollPosition: number = 0; let scrollWidth: number = 0; diff --git a/apps/app/components/gantt-chart/views/year-view.ts b/apps/app/components/gantt-chart/views/year-view.ts index 76edb0d57..82d397e97 100644 --- a/apps/app/components/gantt-chart/views/year-view.ts +++ b/apps/app/components/gantt-chart/views/year-view.ts @@ -100,8 +100,6 @@ export const generateYearChart = (yearPayload: ChartDataType, side: null | "left .map((monthData: any) => monthData.children.length) .reduce((partialSum: number, a: number) => partialSum + a, 0) * yearPayload.data.width; - console.log("scrollWidth", scrollWidth); - return { state: renderState, payload: renderPayload, scrollWidth: scrollWidth }; }; diff --git a/apps/app/components/issues/gantt-chart.tsx b/apps/app/components/issues/gantt-chart.tsx index d8a54619f..4912183a8 100644 --- a/apps/app/components/issues/gantt-chart.tsx +++ b/apps/app/components/issues/gantt-chart.tsx @@ -1,20 +1,27 @@ -import { FC } from "react"; -// next imports -import Link from "next/link"; import { useRouter } from "next/router"; -// components -import { GanttChartRoot } from "components/gantt-chart"; -// ui -import { Tooltip } from "components/ui"; + // hooks +import useIssuesView from "hooks/use-issues-view"; +import useUser from "hooks/use-user"; import useGanttChartIssues from "hooks/gantt-chart/issue-view"; +import { updateGanttIssue } from "components/gantt-chart/hooks/block-update"; +// components +import { + GanttChartRoot, + IssueGanttBlock, + renderIssueBlocksStructure, +} from "components/gantt-chart"; +// types +import { IIssue } from "types"; -type Props = {}; - -export const IssueGanttChartView: FC = ({}) => { +export const IssueGanttChartView = () => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; + const { orderBy } = useIssuesView(); + + const { user } = useUser(); + const { ganttIssues, mutateGanttIssues } = useGanttChartIssues( workspaceSlug as string, projectId as string @@ -31,76 +38,19 @@ export const IssueGanttChartView: FC = ({}) => {
); - // rendering issues on gantt card - const GanttBlockView = ({ data }: any) => ( - - -
- -
- {data?.name} -
-
- {data.infoToggle && ( - -
- - info - -
-
- )} -
- - ); - - // handle gantt issue start date and target date - const handleUpdateDates = async (data: any) => { - const payload = { - id: data?.id, - start_date: data?.start_date, - target_date: data?.target_date, - }; - }; - - const blockFormat = (blocks: any) => - blocks && blocks.length > 0 - ? blocks.map((_block: any) => { - let startDate = new Date(_block.created_at); - let targetDate = new Date(_block.updated_at); - let infoToggle = true; - - if (_block?.start_date && _block.target_date) { - startDate = _block?.start_date; - targetDate = _block.target_date; - infoToggle = false; - } - - return { - start_date: new Date(startDate), - target_date: new Date(targetDate), - infoToggle: infoToggle, - data: _block, - }; - }) - : []; - return (
+ updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString()) + } sidebarBlockRender={(data: any) => } - blockRender={(data: any) => } + blockRender={(data: any) => } + enableReorder={orderBy === "sort_order"} />
); diff --git a/apps/app/components/issues/modal.tsx b/apps/app/components/issues/modal.tsx index 5a66c98b8..8f13a8047 100644 --- a/apps/app/components/issues/modal.tsx +++ b/apps/app/components/issues/modal.tsx @@ -248,7 +248,11 @@ export const CreateUpdateIssueModal: React.FC = ({ await addIssueToModule(res.id, payload.module); if (issueView === "calendar") mutate(calendarFetchKey); - if (issueView === "gantt_chart") mutate(ganttFetchKey); + if (issueView === "gantt_chart") + mutate(ganttFetchKey, { + start_target_date: true, + order_by: "sort_order", + }); if (issueView === "spreadsheet") mutate(spreadsheetFetchKey); if (groupedIssues) mutateMyIssues(); diff --git a/apps/app/components/modules/gantt-chart.tsx b/apps/app/components/modules/gantt-chart.tsx index 3ef46deb4..8ab8b6024 100644 --- a/apps/app/components/modules/gantt-chart.tsx +++ b/apps/app/components/modules/gantt-chart.tsx @@ -1,13 +1,20 @@ import { FC } from "react"; -// next imports -import Link from "next/link"; + import { useRouter } from "next/router"; -// components -import { GanttChartRoot } from "components/gantt-chart"; -// ui -import { Tooltip } from "components/ui"; + // hooks +import useIssuesView from "hooks/use-issues-view"; +import useUser from "hooks/use-user"; import useGanttChartModuleIssues from "hooks/gantt-chart/module-issues-view"; +import { updateGanttIssue } from "components/gantt-chart/hooks/block-update"; +// components +import { + GanttChartRoot, + IssueGanttBlock, + renderIssueBlocksStructure, +} from "components/gantt-chart"; +// types +import { IIssue } from "types"; type Props = {}; @@ -15,6 +22,10 @@ export const ModuleIssuesGanttChartView: FC = ({}) => { const router = useRouter(); const { workspaceSlug, projectId, moduleId } = router.query; + const { orderBy } = useIssuesView(); + + const { user } = useUser(); + const { ganttIssues, mutateGanttIssues } = useGanttChartModuleIssues( workspaceSlug as string, projectId as string, @@ -32,77 +43,18 @@ export const ModuleIssuesGanttChartView: FC = ({}) => {
); - // rendering issues on gantt card - const GanttBlockView = ({ data }: any) => ( - - -
- -
- {data?.name} -
-
- {data.infoToggle && ( - -
- - info - -
-
- )} -
- - ); - - // handle gantt issue start date and target date - const handleUpdateDates = async (data: any) => { - const payload = { - id: data?.id, - start_date: data?.start_date, - target_date: data?.target_date, - }; - - console.log("payload", payload); - }; - - const blockFormat = (blocks: any) => - blocks && blocks.length > 0 - ? blocks.map((_block: any) => { - let startDate = new Date(_block.created_at); - let targetDate = new Date(_block.updated_at); - let infoToggle = true; - - if (_block?.start_date && _block.target_date) { - startDate = _block?.start_date; - targetDate = _block.target_date; - infoToggle = false; - } - - return { - start_date: new Date(startDate), - target_date: new Date(targetDate), - infoToggle: infoToggle, - data: _block, - }; - }) - : []; - return (
+ updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString()) + } sidebarBlockRender={(data: any) => } - blockRender={(data: any) => } + blockRender={(data: any) => } + enableReorder={orderBy === "sort_order"} />
); diff --git a/apps/app/components/modules/modules-list-gantt-chart.tsx b/apps/app/components/modules/modules-list-gantt-chart.tsx index b739f0b1e..2dd482d8b 100644 --- a/apps/app/components/modules/modules-list-gantt-chart.tsx +++ b/apps/app/components/modules/modules-list-gantt-chart.tsx @@ -1,11 +1,15 @@ import { FC } from "react"; -// next imports -import Link from "next/link"; + import { useRouter } from "next/router"; + +import { KeyedMutator } from "swr"; + +// services +import modulesService from "services/modules.service"; +// hooks +import useUser from "hooks/use-user"; // components -import { GanttChartRoot } from "components/gantt-chart"; -// ui -import { Tooltip } from "components/ui"; +import { GanttChartRoot, IBlockUpdateData, ModuleGanttBlock } from "components/gantt-chart"; // types import { IModule } from "types"; // constants @@ -13,11 +17,14 @@ import { MODULE_STATUS } from "constants/module"; type Props = { modules: IModule[]; + mutateModules: KeyedMutator; }; -export const ModulesListGanttChartView: FC = ({ modules }) => { +export const ModulesListGanttChartView: FC = ({ modules, mutateModules }) => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug } = router.query; + + const { user } = useUser(); // rendering issues on gantt sidebar const GanttSidebarBlockView = ({ data }: any) => ( @@ -32,42 +39,52 @@ export const ModulesListGanttChartView: FC = ({ modules }) => {
); - // rendering issues on gantt card - const GanttBlockView = ({ data }: { data: IModule }) => ( - - -
s.value === data.status)?.color }} - /> - -
- {data?.name} -
-
-
- - ); + const handleModuleUpdate = (module: IModule, payload: IBlockUpdateData) => { + if (!workspaceSlug || !user) return; - // handle gantt issue start date and target date - const handleUpdateDates = async (data: any) => { - const payload = { - id: data?.id, - start_date: data?.start_date, - target_date: data?.target_date, - }; + mutateModules((prevData) => { + if (!prevData) return prevData; + + const newList = prevData.map((p) => ({ + ...p, + ...(p.id === module.id + ? { + start_date: payload.start_date ? payload.start_date : p.start_date, + target_date: payload.target_date ? payload.target_date : p.target_date, + sort_order: payload.sort_order ? payload.sort_order.newSortOrder : p.sort_order, + } + : {}), + })); + + if (payload.sort_order) { + const removedElement = newList.splice(payload.sort_order.sourceIndex, 1)[0]; + newList.splice(payload.sort_order.destinationIndex, 0, removedElement); + } + + return newList; + }, false); + + const newPayload: any = { ...payload }; + + if (newPayload.sort_order && payload.sort_order) + newPayload.sort_order = payload.sort_order.newSortOrder; + + modulesService + .patchModule(workspaceSlug.toString(), module.project, module.id, newPayload, user) + .finally(() => mutateModules()); }; - const blockFormat = (blocks: any) => + const blockFormat = (blocks: IModule[]) => blocks && blocks.length > 0 - ? blocks.map((_block: any) => { - if (_block?.start_date && _block.target_date) console.log("_block", _block); - return { - start_date: new Date(_block.created_at), - target_date: new Date(_block.updated_at), - data: _block, - }; - }) + ? blocks + .filter((b) => b.start_date && b.target_date) + .map((block) => ({ + data: block, + id: block.id, + sort_order: block.sort_order, + start_date: new Date(block.start_date ?? ""), + target_date: new Date(block.target_date ?? ""), + })) : []; return ( @@ -76,9 +93,9 @@ export const ModulesListGanttChartView: FC = ({ modules }) => { title="Modules" loaderTitle="Modules" blocks={modules ? blockFormat(modules) : null} - blockUpdateHandler={handleUpdateDates} + blockUpdateHandler={(block, payload) => handleModuleUpdate(block, payload)} sidebarBlockRender={(data: any) => } - blockRender={(data: any) => } + blockRender={(data: any) => } />
); diff --git a/apps/app/components/project/sidebar-list.tsx b/apps/app/components/project/sidebar-list.tsx index 63a5e0f80..29dafd6cf 100644 --- a/apps/app/components/project/sidebar-list.tsx +++ b/apps/app/components/project/sidebar-list.tsx @@ -103,9 +103,7 @@ export const ProjectSidebarList: FC = () => { ? (projectsList[destination.index + 1].sort_order as number) : (projectsList[destination.index - 1].sort_order as number); - updatedSortOrder = Math.round( - (destinationSortingOrder + relativeDestinationSortingOrder) / 2 - ); + updatedSortOrder = (destinationSortingOrder + relativeDestinationSortingOrder) / 2; } mutate( diff --git a/apps/app/components/views/gantt-chart.tsx b/apps/app/components/views/gantt-chart.tsx index 6687cb93f..630ffaca0 100644 --- a/apps/app/components/views/gantt-chart.tsx +++ b/apps/app/components/views/gantt-chart.tsx @@ -1,13 +1,19 @@ import { FC } from "react"; -// next imports -import Link from "next/link"; + import { useRouter } from "next/router"; -// components -import { GanttChartRoot } from "components/gantt-chart"; -// ui -import { Tooltip } from "components/ui"; + // hooks import useGanttChartViewIssues from "hooks/gantt-chart/view-issues-view"; +import useUser from "hooks/use-user"; +import { updateGanttIssue } from "components/gantt-chart/hooks/block-update"; +// components +import { + GanttChartRoot, + IssueGanttBlock, + renderIssueBlocksStructure, +} from "components/gantt-chart"; +// types +import { IIssue } from "types"; type Props = {}; @@ -15,6 +21,8 @@ export const ViewIssuesGanttChartView: FC = ({}) => { const router = useRouter(); const { workspaceSlug, projectId, viewId } = router.query; + const { user } = useUser(); + const { ganttIssues, mutateGanttIssues } = useGanttChartViewIssues( workspaceSlug as string, projectId as string, @@ -32,77 +40,17 @@ export const ViewIssuesGanttChartView: FC = ({}) => {
); - // rendering issues on gantt card - const GanttBlockView = ({ data }: any) => ( - - -
- -
- {data?.name} -
-
- {data.infoToggle && ( - -
- - info - -
-
- )} -
- - ); - - // handle gantt issue start date and target date - const handleUpdateDates = async (data: any) => { - const payload = { - id: data?.id, - start_date: data?.start_date, - target_date: data?.target_date, - }; - - console.log("payload", payload); - }; - - const blockFormat = (blocks: any) => - blocks && blocks.length > 0 - ? blocks.map((_block: any) => { - let startDate = new Date(_block.created_at); - let targetDate = new Date(_block.updated_at); - let infoToggle = true; - - if (_block?.start_date && _block.target_date) { - startDate = _block?.start_date; - targetDate = _block.target_date; - infoToggle = false; - } - - return { - start_date: new Date(startDate), - target_date: new Date(targetDate), - infoToggle: infoToggle, - data: _block, - }; - }) - : []; - return (
+ updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString()) + } sidebarBlockRender={(data: any) => } - blockRender={(data: any) => } + blockRender={(data: any) => } />
); diff --git a/apps/app/constants/fetch-keys.ts b/apps/app/constants/fetch-keys.ts index b6d164068..295d55f77 100644 --- a/apps/app/constants/fetch-keys.ts +++ b/apps/app/constants/fetch-keys.ts @@ -2,13 +2,23 @@ import { objToQueryParams } from "helpers/string.helper"; import { IAnalyticsParams, IJiraMetadata, INotificationParams } from "types"; const paramsToKey = (params: any) => { - const { state, priority, assignees, created_by, labels, target_date, sub_issue } = params; + const { + state, + priority, + assignees, + created_by, + labels, + target_date, + sub_issue, + start_target_date, + } = params; let stateKey = state ? state.split(",") : []; let priorityKey = priority ? priority.split(",") : []; let assigneesKey = assignees ? assignees.split(",") : []; let createdByKey = created_by ? created_by.split(",") : []; let labelsKey = labels ? labels.split(",") : []; + const startTargetDate = start_target_date ? `${start_target_date}`.toUpperCase() : "FALSE"; const targetDateKey = target_date ?? ""; const type = params.type ? params.type.toUpperCase() : "NULL"; const groupBy = params.group_by ? params.group_by.toUpperCase() : "NULL"; @@ -21,7 +31,7 @@ const paramsToKey = (params: any) => { createdByKey = createdByKey.sort().join("_"); labelsKey = labelsKey.sort().join("_"); - return `${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${targetDateKey}_${sub_issue}`; + return `${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${targetDateKey}_${sub_issue}_${startTargetDate}`; }; const inboxParamsToKey = (params: any) => { diff --git a/apps/app/hooks/gantt-chart/cycle-issues-view.tsx b/apps/app/hooks/gantt-chart/cycle-issues-view.tsx index 1782ca339..25baf0d3e 100644 --- a/apps/app/hooks/gantt-chart/cycle-issues-view.tsx +++ b/apps/app/hooks/gantt-chart/cycle-issues-view.tsx @@ -2,6 +2,8 @@ import useSWR from "swr"; // services import cyclesService from "services/cycles.service"; +// hooks +import useIssuesView from "hooks/use-issues-view"; // fetch-keys import { CYCLE_ISSUES_WITH_PARAMS } from "constants/fetch-keys"; @@ -10,15 +12,27 @@ const useGanttChartCycleIssues = ( projectId: string | undefined, cycleId: string | undefined ) => { + const { orderBy, filters, showSubIssues } = useIssuesView(); + + const params: any = { + order_by: orderBy, + type: filters?.type ? filters?.type : undefined, + sub_issue: showSubIssues, + start_target_date: true, + }; + // all issues under the workspace and project const { data: ganttIssues, mutate: mutateGanttIssues } = useSWR( - workspaceSlug && projectId && cycleId ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString()) : null, + workspaceSlug && projectId && cycleId + ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params) + : null, workspaceSlug && projectId && cycleId ? () => cyclesService.getCycleIssuesWithParams( workspaceSlug.toString(), projectId.toString(), - cycleId.toString() + cycleId.toString(), + params ) : null ); diff --git a/apps/app/hooks/gantt-chart/issue-view.tsx b/apps/app/hooks/gantt-chart/issue-view.tsx index bcdb0fca4..c7ffa0ffe 100644 --- a/apps/app/hooks/gantt-chart/issue-view.tsx +++ b/apps/app/hooks/gantt-chart/issue-view.tsx @@ -2,15 +2,27 @@ import useSWR from "swr"; // services import issuesService from "services/issues.service"; +// hooks +import useIssuesView from "hooks/use-issues-view"; // fetch-keys import { PROJECT_ISSUES_LIST_WITH_PARAMS } from "constants/fetch-keys"; const useGanttChartIssues = (workspaceSlug: string | undefined, projectId: string | undefined) => { + const { orderBy, filters, showSubIssues } = useIssuesView(); + + const params: any = { + order_by: orderBy, + type: filters?.type ? filters?.type : undefined, + sub_issue: showSubIssues, + start_target_date: true, + }; + // all issues under the workspace and project const { data: ganttIssues, mutate: mutateGanttIssues } = useSWR( - workspaceSlug && projectId ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId) : null, + workspaceSlug && projectId ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId, params) : null, workspaceSlug && projectId - ? () => issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString()) + ? () => + issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params) : null ); diff --git a/apps/app/hooks/gantt-chart/module-issues-view.tsx b/apps/app/hooks/gantt-chart/module-issues-view.tsx index baf995944..ca686f4e0 100644 --- a/apps/app/hooks/gantt-chart/module-issues-view.tsx +++ b/apps/app/hooks/gantt-chart/module-issues-view.tsx @@ -2,6 +2,8 @@ import useSWR from "swr"; // services import modulesService from "services/modules.service"; +// hooks +import useIssuesView from "hooks/use-issues-view"; // fetch-keys import { MODULE_ISSUES_WITH_PARAMS } from "constants/fetch-keys"; @@ -10,15 +12,27 @@ const useGanttChartModuleIssues = ( projectId: string | undefined, moduleId: string | undefined ) => { + const { orderBy, filters, showSubIssues } = useIssuesView(); + + const params: any = { + order_by: orderBy, + type: filters?.type ? filters?.type : undefined, + sub_issue: showSubIssues, + start_target_date: true, + }; + // all issues under the workspace and project const { data: ganttIssues, mutate: mutateGanttIssues } = useSWR( - workspaceSlug && projectId && moduleId ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString()) : null, + workspaceSlug && projectId && moduleId + ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params) + : null, workspaceSlug && projectId && moduleId ? () => modulesService.getModuleIssuesWithParams( workspaceSlug.toString(), projectId.toString(), - moduleId.toString() + moduleId.toString(), + params ) : null ); diff --git a/apps/app/hooks/gantt-chart/view-issues-view.tsx b/apps/app/hooks/gantt-chart/view-issues-view.tsx index 7fc138570..b66b35128 100644 --- a/apps/app/hooks/gantt-chart/view-issues-view.tsx +++ b/apps/app/hooks/gantt-chart/view-issues-view.tsx @@ -17,14 +17,15 @@ const useGanttChartViewIssues = ( // all issues under the view const { data: ganttIssues, mutate: mutateGanttIssues } = useSWR( - workspaceSlug && projectId && viewId ? VIEW_ISSUES(viewId.toString(), viewGanttParams) : null, + workspaceSlug && projectId && viewId + ? VIEW_ISSUES(viewId.toString(), { ...viewGanttParams, start_target_date: true }) + : null, workspaceSlug && projectId && viewId ? () => - issuesService.getIssuesWithParams( - workspaceSlug.toString(), - projectId.toString(), - viewGanttParams - ) + issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), { + ...viewGanttParams, + start_target_date: true, + }) : null ); diff --git a/apps/app/layouts/app-layout/app-sidebar.tsx b/apps/app/layouts/app-layout/app-sidebar.tsx index 7143d9cad..04cc8393a 100644 --- a/apps/app/layouts/app-layout/app-sidebar.tsx +++ b/apps/app/layouts/app-layout/app-sidebar.tsx @@ -25,6 +25,7 @@ const Sidebar: React.FC = observer(({ toggleSidebar, setToggleSide return (
{ : null ); - const { data: modules } = useSWR( + const { data: modules, mutate: mutateModules } = useSWR( workspaceSlug && projectId ? MODULE_LIST(projectId as string) : null, workspaceSlug && projectId ? () => modulesService.getModules(workspaceSlug as string, projectId as string) @@ -139,7 +139,9 @@ const ProjectModules: NextPage = () => {
)} - {modulesView === "gantt_chart" && } + {modulesView === "gantt_chart" && ( + + )}
) : ( , user: ICurrentUserResponse | undefined ): Promise { return this.patch( @@ -127,7 +127,7 @@ class ProjectIssuesServices extends APIService { workspaceSlug: string, projectId: string, moduleId: string, - queries?: Partial + queries?: any ): Promise< | IIssue[] | { diff --git a/apps/app/types/cycles.d.ts b/apps/app/types/cycles.d.ts index df358b7a9..955e82222 100644 --- a/apps/app/types/cycles.d.ts +++ b/apps/app/types/cycles.d.ts @@ -29,6 +29,7 @@ export interface ICycle { owned_by: IUser; project: string; project_detail: IProjectLite; + sort_order: number; start_date: string | null; started_issues: number; total_issues: number; diff --git a/apps/app/types/modules.d.ts b/apps/app/types/modules.d.ts index 96afcff48..eefd42788 100644 --- a/apps/app/types/modules.d.ts +++ b/apps/app/types/modules.d.ts @@ -43,6 +43,7 @@ export interface IModule { name: string; project: string; project_detail: IProjectLite; + sort_order: number; start_date: string | null; started_issues: number; status: "backlog" | "planned" | "in-progress" | "paused" | "completed" | "cancelled" | null; diff --git a/yarn.lock b/yarn.lock index 9f1999d04..01e9d2a84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2993,26 +2993,26 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.3.3.tgz#16ab6c727d8c2020a5b6e4a176a243ecd88d8d69" integrity sha512-0xd7qez0AQ+MbHatZTlI1gu5vkG8r7MYRUJAHPAHJBmGLs16zpkrpAVLvjQKQOqaXPDUBwOiJzNc00znHSCVBw== -"@sentry-internal/tracing@7.61.1": - version "7.61.1" - resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.61.1.tgz#8055b7dfbf89b7089a591b27e05484d5f6773948" - integrity sha512-E8J6ZMXHGdWdmgKBK/ounuUppDK65c4Hphin6iVckDGMEATn0auYAKngeyRUMLof1167DssD8wxcIA4aBvmScA== +"@sentry-internal/tracing@7.62.0": + version "7.62.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.62.0.tgz#f14400f20a32844f2895a8a333080d52fa32cd1d" + integrity sha512-LHT8i2c93JhQ1uBU1cqb5AIhmHPWlyovE4ZQjqEizk6Fk7jXc9L8kKhaIWELVPn8Xg6YtfGWhRBZk3ssj4JpfQ== dependencies: - "@sentry/core" "7.61.1" - "@sentry/types" "7.61.1" - "@sentry/utils" "7.61.1" + "@sentry/core" "7.62.0" + "@sentry/types" "7.62.0" + "@sentry/utils" "7.62.0" tslib "^2.4.1 || ^1.9.3" -"@sentry/browser@7.61.1": - version "7.61.1" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.61.1.tgz#ce5005ea76d4c2e91c09a43b218c25cc5e9c1340" - integrity sha512-v6Wv0O/PF+sqji+WWpJmxAlQafsiKmsXQLzKAIntVjl3HbYO5oVS3ubCyqfxSlLxIhM5JuHcEOLn6Zi3DPtpcw== +"@sentry/browser@7.62.0": + version "7.62.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.62.0.tgz#0b00a0ed8e4cd4873f7ec413b094ec6b170bb085" + integrity sha512-e52EPiRtPTZv+9iFIZT3n8qNozc8ymqT0ra7QwkwbVuF9fWSCOc1gzkTa9VKd/xwcGzOfglozl2O+Zz4GtoGUg== dependencies: - "@sentry-internal/tracing" "7.61.1" - "@sentry/core" "7.61.1" - "@sentry/replay" "7.61.1" - "@sentry/types" "7.61.1" - "@sentry/utils" "7.61.1" + "@sentry-internal/tracing" "7.62.0" + "@sentry/core" "7.62.0" + "@sentry/replay" "7.62.0" + "@sentry/types" "7.62.0" + "@sentry/utils" "7.62.0" tslib "^2.4.1 || ^1.9.3" "@sentry/cli@^1.74.6": @@ -3027,88 +3027,88 @@ proxy-from-env "^1.1.0" which "^2.0.2" -"@sentry/core@7.61.1": - version "7.61.1" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.61.1.tgz#8043c7cecf5ca0601f6c61979fb2880ceac37287" - integrity sha512-WTRt0J33KhUbYuDQZ5G58kdsNeQ5JYrpi6o+Qz+1xTv60DQq/tBGRJ7d86SkmdnGIiTs6W1hsxAtyiLS0y9d2A== +"@sentry/core@7.62.0": + version "7.62.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.62.0.tgz#3d9571741b052b1f2fa8fb8ae0088de8e79b4f4e" + integrity sha512-l6n+c3mSlWa+FhT/KBrAU1BtbaLYCljf5MuGlH6NKRpnBcrZCbzk8ZuFcSND+gr2SqxycQkhEWX1zxVHPDdZxw== dependencies: - "@sentry/types" "7.61.1" - "@sentry/utils" "7.61.1" + "@sentry/types" "7.62.0" + "@sentry/utils" "7.62.0" tslib "^2.4.1 || ^1.9.3" -"@sentry/integrations@7.61.1": - version "7.61.1" - resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.61.1.tgz#ca9bf2fc59c852f5e73543bb7e69b181a4ef2d45" - integrity sha512-mdmWzUQmW1viOiW0/Gi6AQ5LXukqhuefjzLdn5o6HMxiAgskIpNX+0+BOQ/6162/o7mHWSTNEHqEzMNTK2ppLw== +"@sentry/integrations@7.62.0": + version "7.62.0" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.62.0.tgz#fad35d8de97890b35269d132636218ae157dab22" + integrity sha512-BNlW4xczhbL+zmmc8kFZunjKBrVYZsAltQ/gMuaHw5iiEr+chVMgQDQ2A9EVB7WEtuTJQ0XmeqofH2nAk2qYHg== dependencies: - "@sentry/types" "7.61.1" - "@sentry/utils" "7.61.1" + "@sentry/types" "7.62.0" + "@sentry/utils" "7.62.0" localforage "^1.8.1" tslib "^2.4.1 || ^1.9.3" "@sentry/nextjs@^7.36.0": - version "7.61.1" - resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-7.61.1.tgz#556bd48740dd67694ee54aaed042a22c255290ed" - integrity sha512-ssq0AX+QaDzLSeA45lQLt3OVkzUNiNsI5loMU9gq+Bsts3KOHnykturFvdrb5T3WuIucE6PsswNjZIWqP+lrMg== + version "7.62.0" + resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-7.62.0.tgz#6a5362dc03c768e8ef855ea7c26f94dddc40d7eb" + integrity sha512-Hg5D8dAgGkn+ZoTh2SSOx35hcVJUf9QO4D2FKFmPwFpnrpP/thcusE7m2k6jsUlK6jBvZhtC0rcZk26K3WsioA== dependencies: "@rollup/plugin-commonjs" "24.0.0" - "@sentry/core" "7.61.1" - "@sentry/integrations" "7.61.1" - "@sentry/node" "7.61.1" - "@sentry/react" "7.61.1" - "@sentry/types" "7.61.1" - "@sentry/utils" "7.61.1" + "@sentry/core" "7.62.0" + "@sentry/integrations" "7.62.0" + "@sentry/node" "7.62.0" + "@sentry/react" "7.62.0" + "@sentry/types" "7.62.0" + "@sentry/utils" "7.62.0" "@sentry/webpack-plugin" "1.20.0" chalk "3.0.0" rollup "2.78.0" stacktrace-parser "^0.1.10" tslib "^2.4.1 || ^1.9.3" -"@sentry/node@7.61.1": - version "7.61.1" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.61.1.tgz#bc49d321d0a511936f8bdd0bbd3ddc5e01b8d98c" - integrity sha512-+crVAeymXdWZcDuwU9xySf4sVv2fHOFlr13XqeXl73q4zqKJM1IX4VUO9On3+jTyGfB5SCAuBBYpzA3ehBfeYw== +"@sentry/node@7.62.0": + version "7.62.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.62.0.tgz#8ccac64974748705103fccd3cf40f76003bad94a" + integrity sha512-2z1JmYV97eJ8zwshJA15hppjRdUeMhbaL8LSsbdtx7vTMmjuaIGfPR4EnI4Fhuw+J1Nnf5sE/CRKpZCCa74vXw== dependencies: - "@sentry-internal/tracing" "7.61.1" - "@sentry/core" "7.61.1" - "@sentry/types" "7.61.1" - "@sentry/utils" "7.61.1" + "@sentry-internal/tracing" "7.62.0" + "@sentry/core" "7.62.0" + "@sentry/types" "7.62.0" + "@sentry/utils" "7.62.0" cookie "^0.4.1" https-proxy-agent "^5.0.0" lru_map "^0.3.3" tslib "^2.4.1 || ^1.9.3" -"@sentry/react@7.61.1": - version "7.61.1" - resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.61.1.tgz#88a62fe9a847ffb0feeff935c49737abd7904007" - integrity sha512-n8xNT05gdERpETvq3GJZ2lP6HZYLRQQoUDc13egDzKf840MzCjle0LiLmsVhRv8AL1GnWaIPwnvTGvS4BuNlvw== +"@sentry/react@7.62.0": + version "7.62.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.62.0.tgz#8fa7246ba61f57c007893d76dcd5784b4e12d34e" + integrity sha512-jCQEs6lYGQdqj6XXWdR+i5IzJMgrSzTFI/TSMSeTdAeldmppg7uuRuJlBJGaWsxoiwed539Vn3kitRswn1ugeA== dependencies: - "@sentry/browser" "7.61.1" - "@sentry/types" "7.61.1" - "@sentry/utils" "7.61.1" + "@sentry/browser" "7.62.0" + "@sentry/types" "7.62.0" + "@sentry/utils" "7.62.0" hoist-non-react-statics "^3.3.2" tslib "^2.4.1 || ^1.9.3" -"@sentry/replay@7.61.1": - version "7.61.1" - resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.61.1.tgz#20cdb5f31b5ce25a7afe11bcaaf67b1f875d2833" - integrity sha512-Nsnnzx8c+DRjnfQ0Md11KGdY21XOPa50T2B3eBEyFAhibvYEc/68PuyVWkMBQ7w9zo/JV+q6HpIXKD0THUtqZA== +"@sentry/replay@7.62.0": + version "7.62.0" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.62.0.tgz#9131c24ae2e797ae47983834ba88b3b5c7f6e566" + integrity sha512-mSbqtV6waQAvWTG07uR211jft63HduRXdHq+1xuaKulDcZ9chOkYqOCMpL0HjRIANEiZRTDDKlIo4s+3jkY5Ug== dependencies: - "@sentry/core" "7.61.1" - "@sentry/types" "7.61.1" - "@sentry/utils" "7.61.1" + "@sentry/core" "7.62.0" + "@sentry/types" "7.62.0" + "@sentry/utils" "7.62.0" -"@sentry/types@7.61.1": - version "7.61.1" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.61.1.tgz#225912689459c92e62f0b6e3ff145f6dbf72ff0e" - integrity sha512-CpPKL+OfwYOduRX9AT3p+Ie1fftgcCPd5WofTVVq7xeWRuerOOf2iJd0v+8yHQ25omgres1YOttDkCcvQRn4Jw== +"@sentry/types@7.62.0": + version "7.62.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.62.0.tgz#f15729f656459ffa3a5998fafe9d17ee7fb1c9ff" + integrity sha512-oPy/fIT3o2VQWLTq01R2W/jt13APYMqZCVa0IT3lF9lgxzgfTbeZl3nX2FgCcc8ntDZC0dVw03dL+wLvjPqQpQ== -"@sentry/utils@7.61.1": - version "7.61.1" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.61.1.tgz#1545db778b7309d122a7f04eb0e803173c80c581" - integrity sha512-pUPXoiuYrTEPcBHjRizFB6eZEGm/6cTBwdWSHUjkGKvt19zuZ1ixFJQV6LrIL/AMeiQbmfQ+kTd/8SR7E9rcTQ== +"@sentry/utils@7.62.0": + version "7.62.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.62.0.tgz#915501c6056d704a9625239a1f584a7b2e4492ea" + integrity sha512-12w+Lpvn2iaocgjf6AxhtBz7XG8iFE5aMyt9BTuQp1/7sOjtEVNHlDlGrHbtPqxNCmL2SEcmNHka1panLqWHDw== dependencies: - "@sentry/types" "7.61.1" + "@sentry/types" "7.62.0" tslib "^2.4.1 || ^1.9.3" "@sentry/webpack-plugin@1.20.0": @@ -6439,9 +6439,9 @@ levn@^0.4.1: type-check "~0.4.0" lib0@^0.2.42, lib0@^0.2.74: - version "0.2.79" - resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.79.tgz#b82ee41bfab31a4358bbc0c8ad0645394149a4a9" - integrity sha512-fIdPbxzMVq10wt3ou1lp3/f9n5ciHZ6t+P1vyGy3XXr018AntTYM4eg24sNFcNq8SYDQwmhhoGdS58IlYBzfBw== + version "0.2.80" + resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.80.tgz#97f560c1240b947b825f9923fdfa45c1b4bd7cb8" + integrity sha512-1yVb13p19DrgbL7M/zQmRe/5tQrm37QlCHOssk+G8Q9qnZBh6Azfk876zhaxmKqyMnFGbQqBjH+CV0zkpr+TTw== dependencies: isomorphic.js "^0.2.4"