diff --git a/web/components/issues/issue-layouts/gantt/cycle-root.tsx b/web/components/issues/issue-layouts/gantt/cycle-root.tsx index f411543e8..66b4dc05c 100644 --- a/web/components/issues/issue-layouts/gantt/cycle-root.tsx +++ b/web/components/issues/issue-layouts/gantt/cycle-root.tsx @@ -4,14 +4,14 @@ import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; import useProjectDetails from "hooks/use-project-details"; // components -import { GanttChartRoot, renderIssueBlocksStructure } from "components/gantt-chart"; -import { IssueGanttBlock, IssueGanttSidebarBlock, IssuePeekOverview } from "components/issues"; +import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "components/gantt-chart"; +import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues"; // types import { IIssueUnGroupedStructure } from "store/issue"; export const CycleGanttLayout: React.FC = observer(() => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug, cycleId } = router.query; const { projectDetails } = useProjectDetails(); @@ -21,26 +21,23 @@ export const CycleGanttLayout: React.FC = observer(() => { const issues = cycleIssueStore.getIssues; + const updateIssue = (block: any, payload: IBlockUpdateData) => { + if (!workspaceSlug || !cycleId) return; + + cycleIssueStore.updateGanttIssueStructure(workspaceSlug.toString(), cycleId.toString(), block, payload); + }; + const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; return ( <> -
{ - // TODO: update mutation logic - // updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString()) - }} + blockUpdateHandler={updateIssue} BlockRender={IssueGanttBlock} SidebarBlockRender={IssueGanttSidebarBlock} enableBlockLeftResize={isAllowed} diff --git a/web/components/issues/issue-layouts/gantt/module-root.tsx b/web/components/issues/issue-layouts/gantt/module-root.tsx index 4158b663b..9c9d840b1 100644 --- a/web/components/issues/issue-layouts/gantt/module-root.tsx +++ b/web/components/issues/issue-layouts/gantt/module-root.tsx @@ -4,14 +4,14 @@ import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; import useProjectDetails from "hooks/use-project-details"; // components -import { GanttChartRoot, renderIssueBlocksStructure } from "components/gantt-chart"; -import { IssueGanttBlock, IssueGanttSidebarBlock, IssuePeekOverview } from "components/issues"; +import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "components/gantt-chart"; +import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues"; // types import { IIssueUnGroupedStructure } from "store/issue"; export const ModuleGanttLayout: React.FC = observer(() => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug, moduleId } = router.query; const { projectDetails } = useProjectDetails(); @@ -21,26 +21,23 @@ export const ModuleGanttLayout: React.FC = observer(() => { const issues = moduleIssueStore.getIssues; + const updateIssue = (block: any, payload: IBlockUpdateData) => { + if (!workspaceSlug || !moduleId) return; + + moduleIssueStore.updateGanttIssueStructure(workspaceSlug.toString(), moduleId.toString(), block, payload); + }; + const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; return ( <> -
{ - // TODO: update mutation logic - // updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString()) - }} + blockUpdateHandler={updateIssue} BlockRender={IssueGanttBlock} SidebarBlockRender={IssueGanttSidebarBlock} enableBlockLeftResize={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 095137996..ce6ebe0f0 100644 --- a/web/components/issues/issue-layouts/gantt/project-view-root.tsx +++ b/web/components/issues/issue-layouts/gantt/project-view-root.tsx @@ -6,14 +6,14 @@ import { useMobxStore } from "lib/mobx/store-provider"; // hooks import useProjectDetails from "hooks/use-project-details"; // components -import { GanttChartRoot, renderIssueBlocksStructure } from "components/gantt-chart"; -import { IssueGanttBlock, IssueGanttSidebarBlock, IssuePeekOverview } from "components/issues"; +import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "components/gantt-chart"; +import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues"; // types import { IIssueUnGroupedStructure } from "store/issue"; export const ProjectViewGanttLayout: React.FC = observer(() => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug, viewId } = router.query; const { projectDetails } = useProjectDetails(); @@ -23,26 +23,23 @@ export const ProjectViewGanttLayout: React.FC = observer(() => { const issues = projectViewIssuesStore.getIssues; + const updateIssue = (block: any, payload: IBlockUpdateData) => { + if (!workspaceSlug || !viewId) return; + + projectViewIssuesStore.updateGanttIssueStructure(workspaceSlug.toString(), viewId.toString(), block, payload); + }; + const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; return ( <> -
{ - // TODO: update mutation logic - // updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString()) - }} + blockUpdateHandler={updateIssue} BlockRender={IssueGanttBlock} SidebarBlockRender={IssueGanttSidebarBlock} enableBlockLeftResize={isAllowed} diff --git a/web/components/issues/issue-layouts/gantt/root.tsx b/web/components/issues/issue-layouts/gantt/root.tsx index 8277aa52a..bf52e29f5 100644 --- a/web/components/issues/issue-layouts/gantt/root.tsx +++ b/web/components/issues/issue-layouts/gantt/root.tsx @@ -4,14 +4,14 @@ import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; import useProjectDetails from "hooks/use-project-details"; // components -import { GanttChartRoot, renderIssueBlocksStructure } from "components/gantt-chart"; -import { IssueGanttBlock, IssueGanttSidebarBlock, IssuePeekOverview } from "components/issues"; +import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "components/gantt-chart"; +import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues"; // types import { IIssueUnGroupedStructure } from "store/issue"; export const GanttLayout: React.FC = observer(() => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug } = router.query; const { projectDetails } = useProjectDetails(); @@ -21,26 +21,23 @@ export const GanttLayout: React.FC = observer(() => { const issues = issueStore.getIssues; + const updateIssue = (block: any, payload: IBlockUpdateData) => { + if (!workspaceSlug) return; + + issueStore.updateGanttIssueStructure(workspaceSlug.toString(), block, payload); + }; + const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; return ( <> -
{ - // TODO: update mutation logic - // updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString()) - }} + blockUpdateHandler={updateIssue} BlockRender={IssueGanttBlock} SidebarBlockRender={IssueGanttSidebarBlock} enableBlockLeftResize={isAllowed} diff --git a/web/store/cycle/cycle_issue.store.ts b/web/store/cycle/cycle_issue.store.ts index ce484c0bb..e343a733d 100644 --- a/web/store/cycle/cycle_issue.store.ts +++ b/web/store/cycle/cycle_issue.store.ts @@ -1,13 +1,14 @@ import { observable, action, computed, makeObservable, runInAction, autorun } from "mobx"; // store import { RootStore } from "../root"; -// types -import { IIssue } from "types"; // services import { CycleService } from "services/cycle.service"; import { IssueService } from "services/issue"; // constants import { sortArrayByDate, sortArrayByPriority } from "constants/kanban-helpers"; +// types +import { IIssue } from "types"; +import { IBlockUpdateData } from "components/gantt-chart"; export type IIssueType = "grouped" | "groupWithSubGroups" | "ungrouped"; export type IIssueGroupedStructure = { [group_id: string]: IIssue[] }; @@ -35,6 +36,7 @@ export interface ICycleIssueStore { // action fetchIssues: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; updateIssueStructure: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; + updateGanttIssueStructure: (workspaceSlug: string, cycleId: string, issue: IIssue, payload: IBlockUpdateData) => void; deleteIssue: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => void; removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, bridgeId: string) => void; @@ -74,6 +76,7 @@ export class CycleIssueStore implements ICycleIssueStore { // actions fetchIssues: action, updateIssueStructure: action, + updateGanttIssueStructure: action, deleteIssue: action, addIssueToCycle: action, removeIssueFromCycle: action, @@ -169,6 +172,50 @@ export class CycleIssueStore implements ICycleIssueStore { }); }; + updateGanttIssueStructure = async ( + workspaceSlug: string, + cycleId: string, + issue: IIssue, + payload: IBlockUpdateData + ) => { + if (!issue || !workspaceSlug) return; + + const issues = this.getIssues as IIssueUnGroupedStructure; + + const newIssues = issues.map((i) => ({ + ...i, + ...(i.id === issue.id + ? { + sort_order: payload.sort_order?.newSortOrder ?? i.sort_order, + start_date: payload.start_date, + target_date: payload.target_date, + } + : {}), + })); + + if (payload.sort_order) { + const removedElement = newIssues.splice(payload.sort_order.sourceIndex, 1)[0]; + removedElement.sort_order = payload.sort_order.newSortOrder; + newIssues.splice(payload.sort_order.destinationIndex, 0, removedElement); + } + + runInAction(() => { + this.issues = { + ...this.issues, + [cycleId]: { + ...this.issues[cycleId], + ungrouped: newIssues, + }, + }; + }); + + const newPayload: any = { ...payload }; + + if (newPayload.sort_order && payload.sort_order) newPayload.sort_order = payload.sort_order.newSortOrder; + + this.rootStore.issueDetail.updateIssue(workspaceSlug, issue.project, issue.id, newPayload); + }; + deleteIssue = async (group_id: string | null, sub_group_id: string | null, issue: IIssue) => { const cycleId: string | null = this.rootStore.cycle.cycleId; const issueType = this.getIssueType; diff --git a/web/store/issue/issue.store.ts b/web/store/issue/issue.store.ts index 5b29fe9e2..ec337abcb 100644 --- a/web/store/issue/issue.store.ts +++ b/web/store/issue/issue.store.ts @@ -6,6 +6,7 @@ import { IIssue } from "types"; // services import { IssueService } from "services/issue"; import { sortArrayByDate, sortArrayByPriority } from "constants/kanban-helpers"; +import { IBlockUpdateData } from "components/gantt-chart"; export type IIssueType = "grouped" | "groupWithSubGroups" | "ungrouped"; export type IIssueGroupedStructure = { [group_id: string]: IIssue[] }; @@ -34,6 +35,7 @@ export interface IIssueStore { fetchIssues: (workspaceSlug: string, projectId: string) => Promise; updateIssueStructure: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; deleteIssue: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; + updateGanttIssueStructure: (workspaceSlug: string, issue: IIssue, payload: IBlockUpdateData) => void; } export class IssueStore implements IIssueStore { @@ -69,6 +71,7 @@ export class IssueStore implements IIssueStore { fetchIssues: action, updateIssueStructure: action, deleteIssue: action, + updateGanttIssueStructure: action, }); this.rootStore = _rootStore; @@ -165,6 +168,45 @@ export class IssueStore implements IIssueStore { }); }; + updateGanttIssueStructure = async (workspaceSlug: string, issue: IIssue, payload: IBlockUpdateData) => { + if (!issue || !workspaceSlug) return; + + const issues = this.getIssues as IIssueUnGroupedStructure; + + const newIssues = issues.map((i) => ({ + ...i, + ...(i.id === issue.id + ? { + sort_order: payload.sort_order?.newSortOrder ?? i.sort_order, + start_date: payload.start_date, + target_date: payload.target_date, + } + : {}), + })); + + if (payload.sort_order) { + const removedElement = newIssues.splice(payload.sort_order.sourceIndex, 1)[0]; + removedElement.sort_order = payload.sort_order.newSortOrder; + newIssues.splice(payload.sort_order.destinationIndex, 0, removedElement); + } + + runInAction(() => { + this.issues = { + ...this.issues, + [issue.project]: { + ...this.issues[issue.project], + ungrouped: newIssues, + }, + }; + }); + + const newPayload: any = { ...payload }; + + if (newPayload.sort_order && payload.sort_order) newPayload.sort_order = payload.sort_order.newSortOrder; + + this.rootStore.issueDetail.updateIssue(workspaceSlug, issue.project, issue.id, newPayload); + }; + deleteIssue = async (group_id: string | null, sub_group_id: string | null, issue: IIssue) => { const projectId: string | null = issue?.project; const issueType = this.getIssueType; diff --git a/web/store/module/module_issue.store.ts b/web/store/module/module_issue.store.ts index 5cd794861..9751ef708 100644 --- a/web/store/module/module_issue.store.ts +++ b/web/store/module/module_issue.store.ts @@ -1,12 +1,13 @@ import { observable, action, computed, makeObservable, runInAction, autorun } from "mobx"; // store import { RootStore } from "../root"; -// types -import { IIssue } from "types"; // services import { ModuleService } from "services/module.service"; // helpers import { sortArrayByDate, sortArrayByPriority } from "constants/kanban-helpers"; +// types +import { IIssue } from "types"; +import { IBlockUpdateData } from "components/gantt-chart"; export type IIssueType = "grouped" | "groupWithSubGroups" | "ungrouped"; export type IIssueGroupedStructure = { [group_id: string]: IIssue[] }; @@ -34,6 +35,12 @@ export interface IModuleIssueStore { // action fetchIssues: (workspaceSlug: string, projectId: string, moduleId: string) => Promise; updateIssueStructure: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; + updateGanttIssueStructure: ( + workspaceSlug: string, + moduleId: string, + issue: IIssue, + payload: IBlockUpdateData + ) => void; deleteIssue: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; addIssueToModule: (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => Promise; removeIssueFromModule: (workspaceSlug: string, projectId: string, moduleId: string, bridgeId: string) => Promise; @@ -72,6 +79,7 @@ export class ModuleIssueStore implements IModuleIssueStore { // actions fetchIssues: action, updateIssueStructure: action, + updateGanttIssueStructure: action, deleteIssue: action, addIssueToModule: action, removeIssueFromModule: action, @@ -174,6 +182,50 @@ export class ModuleIssueStore implements IModuleIssueStore { }); }; + updateGanttIssueStructure = async ( + workspaceSlug: string, + moduleId: string, + issue: IIssue, + payload: IBlockUpdateData + ) => { + if (!issue || !workspaceSlug) return; + + const issues = this.getIssues as IIssueUnGroupedStructure; + + const newIssues = issues.map((i) => ({ + ...i, + ...(i.id === issue.id + ? { + sort_order: payload.sort_order?.newSortOrder ?? i.sort_order, + start_date: payload.start_date, + target_date: payload.target_date, + } + : {}), + })); + + if (payload.sort_order) { + const removedElement = newIssues.splice(payload.sort_order.sourceIndex, 1)[0]; + removedElement.sort_order = payload.sort_order.newSortOrder; + newIssues.splice(payload.sort_order.destinationIndex, 0, removedElement); + } + + runInAction(() => { + this.issues = { + ...this.issues, + [moduleId]: { + ...this.issues[moduleId], + ungrouped: newIssues, + }, + }; + }); + + const newPayload: any = { ...payload }; + + if (newPayload.sort_order && payload.sort_order) newPayload.sort_order = payload.sort_order.newSortOrder; + + this.rootStore.issueDetail.updateIssue(workspaceSlug, issue.project, issue.id, newPayload); + }; + deleteIssue = async (group_id: string | null, sub_group_id: string | null, issue: IIssue) => { const moduleId: string | null = this.rootStore.module.moduleId; const issueType = this.getIssueType; diff --git a/web/store/project-view/project_view_issues.store.ts b/web/store/project-view/project_view_issues.store.ts index 8022204cf..9588237fe 100644 --- a/web/store/project-view/project_view_issues.store.ts +++ b/web/store/project-view/project_view_issues.store.ts @@ -3,16 +3,12 @@ import { observable, action, makeObservable, runInAction, computed, autorun } fr import { IssueService } from "services/issue"; // helpers import { handleIssueQueryParamsByLayout } from "helpers/issue.helper"; +import { sortArrayByDate, sortArrayByPriority } from "constants/kanban-helpers"; // types import { RootStore } from "../root"; +import { IIssueGroupWithSubGroupsStructure, IIssueGroupedStructure, IIssueUnGroupedStructure } from "store/issue"; import { IIssue, IIssueFilterOptions } from "types"; -import { - IIssueGroupWithSubGroupsStructure, - IIssueGroupedStructure, - IIssueUnGroupedStructure, -} from "../module/module_issue.store"; -// helpers -import { sortArrayByDate, sortArrayByPriority } from "constants/kanban-helpers"; +import { IBlockUpdateData } from "components/gantt-chart"; export interface IProjectViewIssuesStore { // states @@ -30,6 +26,7 @@ export interface IProjectViewIssuesStore { // actions updateIssueStructure: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; + updateGanttIssueStructure: (workspaceSlug: string, viewId: string, issue: IIssue, payload: IBlockUpdateData) => void; deleteIssue: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; fetchViewIssues: ( workspaceSlug: string, @@ -73,6 +70,7 @@ export class ProjectViewIssuesStore implements IProjectViewIssuesStore { // actions updateIssueStructure: action, + updateGanttIssueStructure: action, deleteIssue: action, fetchViewIssues: action, @@ -169,6 +167,50 @@ export class ProjectViewIssuesStore implements IProjectViewIssuesStore { }); }; + updateGanttIssueStructure = async ( + workspaceSlug: string, + viewId: string, + issue: IIssue, + payload: IBlockUpdateData + ) => { + if (!issue || !workspaceSlug) return; + + const issues = this.getIssues as IIssueUnGroupedStructure; + + const newIssues = issues.map((i) => ({ + ...i, + ...(i.id === issue.id + ? { + sort_order: payload.sort_order?.newSortOrder ?? i.sort_order, + start_date: payload.start_date, + target_date: payload.target_date, + } + : {}), + })); + + if (payload.sort_order) { + const removedElement = newIssues.splice(payload.sort_order.sourceIndex, 1)[0]; + removedElement.sort_order = payload.sort_order.newSortOrder; + newIssues.splice(payload.sort_order.destinationIndex, 0, removedElement); + } + + runInAction(() => { + this.viewIssues = { + ...this.viewIssues, + [viewId]: { + ...this.viewIssues[viewId], + ungrouped: newIssues, + }, + }; + }); + + const newPayload: any = { ...payload }; + + if (newPayload.sort_order && payload.sort_order) newPayload.sort_order = payload.sort_order.newSortOrder; + + this.rootStore.issueDetail.updateIssue(workspaceSlug, issue.project, issue.id, newPayload); + }; + deleteIssue = async (group_id: string | null, sub_group_id: string | null, issue: IIssue) => { const viewId: string | null = this.rootStore.projectViews.viewId; const issueType = this.rootStore.issue.getIssueType;