forked from github/plane
fix: drag and drop implementation in calendar layout and kanban layout (#2921)
* fix profile issue filters and kanban * chore: calendar drag and drop * chore: kanban drag and drop * dev: remove issue from the kanban layout and resolved build errors --------- Co-authored-by: rahulramesha <rahulramesham@gmail.com>
This commit is contained in:
parent
d5853405ca
commit
3400c119bc
@ -37,10 +37,12 @@ interface IBaseCalendarRoot {
|
||||
[EIssueActions.REMOVE]?: (issue: IIssue) => void;
|
||||
};
|
||||
viewId?: string;
|
||||
handleDragDrop: (source: any, destination: any, issues: any, issueWithIds: any) => void;
|
||||
}
|
||||
|
||||
export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
||||
const { issueStore, issuesFilterStore, calendarViewStore, QuickActions, issueActions, viewId } = props;
|
||||
const { issueStore, issuesFilterStore, calendarViewStore, QuickActions, issueActions, viewId, handleDragDrop } =
|
||||
props;
|
||||
|
||||
const displayFilters = issuesFilterStore.issueFilters?.displayFilters;
|
||||
|
||||
@ -56,7 +58,7 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
||||
// return if dropped on the same date
|
||||
if (result.destination.droppableId === result.source.droppableId) return;
|
||||
|
||||
calendarViewStore?.handleDragDrop(result.source, result.destination);
|
||||
if (handleDragDrop) handleDragDrop(result.source, result.destination, issues, groupedIssueIds);
|
||||
};
|
||||
|
||||
const handleIssues = useCallback(
|
||||
|
@ -14,10 +14,15 @@ export const CycleCalendarLayout: React.FC = observer(() => {
|
||||
cycleIssues: cycleIssueStore,
|
||||
cycleIssuesFilter: cycleIssueFilterStore,
|
||||
cycleIssueCalendarView: cycleIssueCalendarViewStore,
|
||||
calendarHelpers: calendarHelperStore,
|
||||
} = useMobxStore();
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, cycleId } = router.query as { workspaceSlug: string; cycleId: string };
|
||||
const { workspaceSlug, projectId, cycleId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
cycleId: string;
|
||||
};
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||
@ -35,6 +40,20 @@ export const CycleCalendarLayout: React.FC = observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
||||
if (calendarHelperStore.handleDragDrop)
|
||||
calendarHelperStore.handleDragDrop(
|
||||
source,
|
||||
destination,
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
cycleIssueStore,
|
||||
issues,
|
||||
issueWithIds,
|
||||
cycleId
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseCalendarRoot
|
||||
issueStore={cycleIssueStore}
|
||||
@ -43,6 +62,7 @@ export const CycleCalendarLayout: React.FC = observer(() => {
|
||||
QuickActions={CycleIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
viewId={cycleId}
|
||||
handleDragDrop={handleDragDrop}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -14,10 +14,15 @@ export const ModuleCalendarLayout: React.FC = observer(() => {
|
||||
moduleIssues: moduleIssueStore,
|
||||
moduleIssuesFilter: moduleIssueFilterStore,
|
||||
moduleIssueCalendarView: moduleIssueCalendarViewStore,
|
||||
calendarHelpers: calendarHelperStore,
|
||||
} = useMobxStore();
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, moduleId } = router.query as { workspaceSlug: string; moduleId: string };
|
||||
const { workspaceSlug, projectId, moduleId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
moduleId: string;
|
||||
};
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: (issue: IIssue) => {
|
||||
@ -34,6 +39,20 @@ export const ModuleCalendarLayout: React.FC = observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
||||
if (calendarHelperStore.handleDragDrop)
|
||||
calendarHelperStore.handleDragDrop(
|
||||
source,
|
||||
destination,
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
moduleIssueStore,
|
||||
issues,
|
||||
issueWithIds,
|
||||
moduleId
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseCalendarRoot
|
||||
issueStore={moduleIssueStore}
|
||||
@ -42,6 +61,7 @@ export const ModuleCalendarLayout: React.FC = observer(() => {
|
||||
QuickActions={ModuleIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
viewId={moduleId}
|
||||
handleDragDrop={handleDragDrop}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -10,27 +10,41 @@ import { useRouter } from "next/router";
|
||||
|
||||
export const CalendarLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query as { workspaceSlug: string };
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
|
||||
const {
|
||||
projectIssues: issueStore,
|
||||
issueCalendarView: issueCalendarViewStore,
|
||||
projectIssuesFilter: projectIssueFiltersStore,
|
||||
calendarHelpers: calendarHelperStore,
|
||||
} = useMobxStore();
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
issueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
||||
issueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
issueStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||
issueStore.removeIssue(workspaceSlug, issue.project, issue.id);
|
||||
},
|
||||
};
|
||||
|
||||
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
||||
if (calendarHelperStore.handleDragDrop)
|
||||
calendarHelperStore.handleDragDrop(
|
||||
source,
|
||||
destination,
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
issueStore,
|
||||
issues,
|
||||
issueWithIds
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseCalendarRoot
|
||||
issueStore={issueStore}
|
||||
@ -38,6 +52,7 @@ export const CalendarLayout: React.FC = observer(() => {
|
||||
calendarViewStore={issueCalendarViewStore}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
handleDragDrop={handleDragDrop}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -14,10 +14,11 @@ export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
||||
viewIssues: projectViewIssuesStore,
|
||||
viewIssuesFilter: projectIssueViewFiltersStore,
|
||||
projectViewIssueCalendarView: projectViewIssueCalendarViewStore,
|
||||
calendarHelpers: calendarHelperStore,
|
||||
} = useMobxStore();
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query as { workspaceSlug: string };
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||
@ -32,6 +33,19 @@ export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
||||
if (calendarHelperStore.handleDragDrop)
|
||||
calendarHelperStore.handleDragDrop(
|
||||
source,
|
||||
destination,
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
projectViewIssuesStore,
|
||||
issues,
|
||||
issueWithIds
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseCalendarRoot
|
||||
issueStore={projectViewIssuesStore}
|
||||
@ -39,6 +53,7 @@ export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
||||
calendarViewStore={projectViewIssueCalendarViewStore}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
handleDragDrop={handleDragDrop}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { FC, useCallback, useState } from "react";
|
||||
import { DragDropContext } from "@hello-pangea/dnd";
|
||||
import { DragDropContext, Droppable } from "@hello-pangea/dnd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
@ -29,6 +29,7 @@ import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue";
|
||||
import { KanBan } from "./default";
|
||||
import { KanBanSwimLanes } from "./swimlanes";
|
||||
import { EProjectStore } from "store/command-palette.store";
|
||||
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
|
||||
|
||||
export interface IBaseKanBanLayout {
|
||||
issueStore:
|
||||
@ -54,6 +55,14 @@ export interface IBaseKanBanLayout {
|
||||
showLoader?: boolean;
|
||||
viewId?: string;
|
||||
currentStore?: EProjectStore;
|
||||
handleDragDrop?: (
|
||||
source: any,
|
||||
destination: any,
|
||||
subGroupBy: string | null,
|
||||
groupBy: string | null,
|
||||
issues: any,
|
||||
issueWithIds: any
|
||||
) => void;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||
}
|
||||
|
||||
@ -67,6 +76,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
showLoader,
|
||||
viewId,
|
||||
currentStore,
|
||||
handleDragDrop,
|
||||
addIssuesToView,
|
||||
} = props;
|
||||
|
||||
@ -93,9 +103,9 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
|
||||
const currentKanBanView: "swimlanes" | "default" = sub_group_by ? "swimlanes" : "default";
|
||||
|
||||
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
|
||||
|
||||
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issueStore?.viewFlags || {};
|
||||
|
||||
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
|
||||
const onDragStart = () => {
|
||||
setIsDragStarted(true);
|
||||
};
|
||||
@ -115,9 +125,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
)
|
||||
return;
|
||||
|
||||
currentKanBanView === "default"
|
||||
? kanbanViewStore?.handleDragDrop(result.source, result.destination)
|
||||
: kanbanViewStore?.handleSwimlaneDragDrop(result.source, result.destination);
|
||||
if (handleDragDrop) handleDragDrop(result.source, result.destination, sub_group_by, group_by, issues, issueIds);
|
||||
};
|
||||
|
||||
const handleIssues = useCallback(
|
||||
@ -147,6 +155,24 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
|
||||
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
|
||||
<DragDropContext onDragStart={onDragStart} onDragEnd={onDragEnd}>
|
||||
<div className={`fixed left-1/2 -translate-x-1/2 z-40 w-72 top-3 flex items-center justify-center mx-3`}>
|
||||
<Droppable droppableId="issue-trash-box" isDropDisabled={!isDragStarted}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
className={`${
|
||||
isDragStarted ? `opacity-100` : `opacity-0`
|
||||
} w-full flex items-center justify-center rounded border-2 border-red-500/20 bg-custom-background-100 px-3 py-5 text-xs font-medium italic text-red-500 ${
|
||||
snapshot.isDraggingOver ? "bg-red-500 blur-2xl opacity-70" : ""
|
||||
} transition duration-300`}
|
||||
ref={provided.innerRef}
|
||||
{...provided.droppableProps}
|
||||
>
|
||||
Drop here to delete the issue.
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</div>
|
||||
|
||||
{currentKanBanView === "default" ? (
|
||||
<KanBan
|
||||
issues={issues}
|
||||
|
@ -16,13 +16,18 @@ export interface ICycleKanBanLayout {}
|
||||
|
||||
export const CycleKanBanLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, cycleId } = router.query as { workspaceSlug: string; cycleId: string };
|
||||
const { workspaceSlug, projectId, cycleId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
cycleId: string;
|
||||
};
|
||||
|
||||
// store
|
||||
const {
|
||||
cycleIssues: cycleIssueStore,
|
||||
cycleIssuesFilter: cycleIssueFilterStore,
|
||||
cycleIssueKanBanView: cycleIssueKanBanViewStore,
|
||||
kanBanHelpers: kanBanHelperStore,
|
||||
} = useMobxStore();
|
||||
|
||||
const issueActions = {
|
||||
@ -40,6 +45,30 @@ export const CycleKanBanLayout: React.FC = observer(() => {
|
||||
cycleIssueStore.removeIssueFromCycle(workspaceSlug, issue.project, cycleId, issue.id, issue.bridge_id);
|
||||
},
|
||||
};
|
||||
|
||||
const handleDragDrop = (
|
||||
source: any,
|
||||
destination: any,
|
||||
subGroupBy: string | null,
|
||||
groupBy: string | null,
|
||||
issues: IIssue[],
|
||||
issueWithIds: any
|
||||
) => {
|
||||
if (kanBanHelperStore.handleDragDrop)
|
||||
kanBanHelperStore.handleDragDrop(
|
||||
source,
|
||||
destination,
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
cycleIssueStore,
|
||||
subGroupBy,
|
||||
groupBy,
|
||||
issues,
|
||||
issueWithIds,
|
||||
cycleId
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
issueActions={issueActions}
|
||||
@ -50,6 +79,7 @@ export const CycleKanBanLayout: React.FC = observer(() => {
|
||||
QuickActions={CycleIssueQuickActions}
|
||||
viewId={cycleId}
|
||||
currentStore={EProjectStore.CYCLE}
|
||||
handleDragDrop={handleDragDrop}
|
||||
addIssuesToView={(issues: string[]) => cycleIssueStore.addIssueToCycle(workspaceSlug, cycleId, issues)}
|
||||
/>
|
||||
);
|
||||
|
@ -16,13 +16,18 @@ export interface IModuleKanBanLayout {}
|
||||
|
||||
export const ModuleKanBanLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, moduleId } = router.query as { workspaceSlug: string; moduleId: string };
|
||||
const { workspaceSlug, projectId, moduleId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
moduleId: string;
|
||||
};
|
||||
|
||||
// store
|
||||
const {
|
||||
moduleIssues: moduleIssueStore,
|
||||
moduleIssuesFilter: moduleIssueFilterStore,
|
||||
moduleIssueKanBanView: moduleIssueKanBanViewStore,
|
||||
kanBanHelpers: kanBanHelperStore,
|
||||
} = useMobxStore();
|
||||
|
||||
// const handleIssues = useCallback(
|
||||
@ -62,6 +67,29 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
|
||||
moduleIssueStore.removeIssueFromModule(workspaceSlug, issue.project, moduleId, issue.id, issue.bridge_id);
|
||||
},
|
||||
};
|
||||
|
||||
const handleDragDrop = (
|
||||
source: any,
|
||||
destination: any,
|
||||
subGroupBy: string | null,
|
||||
groupBy: string | null,
|
||||
issues: IIssue[],
|
||||
issueWithIds: any
|
||||
) => {
|
||||
if (kanBanHelperStore.handleDragDrop)
|
||||
kanBanHelperStore.handleDragDrop(
|
||||
source,
|
||||
destination,
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
moduleIssueStore,
|
||||
subGroupBy,
|
||||
groupBy,
|
||||
issues,
|
||||
issueWithIds,
|
||||
moduleId
|
||||
);
|
||||
};
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
issueActions={issueActions}
|
||||
@ -72,6 +100,7 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
|
||||
QuickActions={ModuleIssueQuickActions}
|
||||
viewId={moduleId}
|
||||
currentStore={EProjectStore.MODULE}
|
||||
handleDragDrop={handleDragDrop}
|
||||
addIssuesToView={(issues: string[]) => moduleIssueStore.addIssueToModule(workspaceSlug, moduleId, issues)}
|
||||
/>
|
||||
);
|
||||
|
@ -15,12 +15,13 @@ export interface IKanBanLayout {}
|
||||
|
||||
export const KanBanLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query as { workspaceSlug: string };
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
|
||||
const {
|
||||
projectIssues: issueStore,
|
||||
projectIssuesFilter: issuesFilterStore,
|
||||
issueKanBanView: issueKanBanViewStore,
|
||||
kanBanHelpers: kanBanHelperStore,
|
||||
} = useMobxStore();
|
||||
|
||||
const issueActions = {
|
||||
@ -36,6 +37,28 @@ export const KanBanLayout: React.FC = observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const handleDragDrop = (
|
||||
source: any,
|
||||
destination: any,
|
||||
subGroupBy: string | null,
|
||||
groupBy: string | null,
|
||||
issues: IIssue[],
|
||||
issueWithIds: any
|
||||
) => {
|
||||
if (kanBanHelperStore.handleDragDrop)
|
||||
kanBanHelperStore.handleDragDrop(
|
||||
source,
|
||||
destination,
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
issueStore,
|
||||
subGroupBy,
|
||||
groupBy,
|
||||
issues,
|
||||
issueWithIds
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
issueActions={issueActions}
|
||||
@ -45,6 +68,7 @@ export const KanBanLayout: React.FC = observer(() => {
|
||||
showLoader={true}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
currentStore={EProjectStore.PROJECT}
|
||||
handleDragDrop={handleDragDrop}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -15,12 +15,13 @@ export interface IViewKanBanLayout {}
|
||||
|
||||
export const ProjectViewKanBanLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query as { workspaceSlug: string };
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
|
||||
const {
|
||||
viewIssues: projectViewIssuesStore,
|
||||
viewIssuesFilter: projectIssueViewFiltersStore,
|
||||
issueKanBanView: projectViewIssueKanBanViewStore,
|
||||
kanBanHelpers: kanBanHelperStore,
|
||||
} = useMobxStore();
|
||||
|
||||
const issueActions = {
|
||||
@ -36,6 +37,28 @@ export const ProjectViewKanBanLayout: React.FC = observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const handleDragDrop = (
|
||||
source: any,
|
||||
destination: any,
|
||||
subGroupBy: string | null,
|
||||
groupBy: string | null,
|
||||
issues: IIssue[],
|
||||
issueWithIds: any
|
||||
) => {
|
||||
if (kanBanHelperStore.handleDragDrop)
|
||||
kanBanHelperStore.handleDragDrop(
|
||||
source,
|
||||
destination,
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
projectViewIssuesStore,
|
||||
subGroupBy,
|
||||
groupBy,
|
||||
issues,
|
||||
issueWithIds
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
issueActions={issueActions}
|
||||
@ -45,6 +68,7 @@ export const ProjectViewKanBanLayout: React.FC = observer(() => {
|
||||
showLoader={true}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
currentStore={EProjectStore.PROJECT_VIEW}
|
||||
handleDragDrop={handleDragDrop}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -23,9 +23,9 @@
|
||||
"@nivo/line": "0.80.0",
|
||||
"@nivo/pie": "0.80.0",
|
||||
"@nivo/scatterplot": "0.80.0",
|
||||
"@plane/document-editor": "*",
|
||||
"@plane/lite-text-editor": "*",
|
||||
"@plane/rich-text-editor": "*",
|
||||
"@plane/document-editor": "*",
|
||||
"@plane/ui": "*",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/nextjs": "^7.36.0",
|
||||
@ -58,8 +58,8 @@
|
||||
"sharp": "^0.32.1",
|
||||
"swr": "^2.1.3",
|
||||
"tailwind-merge": "^2.0.0",
|
||||
"uuid": "^9.0.0",
|
||||
"use-debounce": "^9.0.4"
|
||||
"use-debounce": "^9.0.4",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
|
53
web/store/issues/base-issue-calendar-helper.store.ts
Normal file
53
web/store/issues/base-issue-calendar-helper.store.ts
Normal file
@ -0,0 +1,53 @@
|
||||
export interface ICalendarHelpers {
|
||||
// actions
|
||||
handleDragDrop: (
|
||||
source: any,
|
||||
destination: any,
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
store: any,
|
||||
issues: any,
|
||||
issueWithIds: any,
|
||||
viewId?: string | null
|
||||
) => void;
|
||||
}
|
||||
|
||||
export class CalendarHelpers implements ICalendarHelpers {
|
||||
constructor() {}
|
||||
|
||||
handleDragDrop = async (
|
||||
source: any,
|
||||
destination: any,
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
store: any,
|
||||
issues: any,
|
||||
issueWithIds: any,
|
||||
viewId: string | null = null // it can be moduleId, cycleId
|
||||
) => {
|
||||
if (issues && issueWithIds) {
|
||||
const sourceColumnId = source?.droppableId || null;
|
||||
const destinationColumnId = destination?.droppableId || null;
|
||||
|
||||
if (!workspaceSlug || !projectId || !sourceColumnId || !destinationColumnId) return;
|
||||
|
||||
if (sourceColumnId === destinationColumnId) return;
|
||||
|
||||
// horizontal
|
||||
if (sourceColumnId != destinationColumnId) {
|
||||
const sourceIssues = issueWithIds[sourceColumnId] || [];
|
||||
|
||||
const [removed] = sourceIssues.splice(source.index, 1);
|
||||
const removedIssueDetail = issues[removed];
|
||||
|
||||
const updateIssue = {
|
||||
id: removedIssueDetail?.id,
|
||||
target_date: destinationColumnId,
|
||||
};
|
||||
|
||||
if (viewId) store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue, viewId);
|
||||
else store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
160
web/store/issues/base-issue-kanban-helper.store.ts
Normal file
160
web/store/issues/base-issue-kanban-helper.store.ts
Normal file
@ -0,0 +1,160 @@
|
||||
export interface IKanBanHelpers {
|
||||
// actions
|
||||
handleDragDrop: (
|
||||
source: any,
|
||||
destination: any,
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
store: any,
|
||||
subGroupBy: string | null,
|
||||
groupBy: string | null,
|
||||
issues: any,
|
||||
issueWithIds: any,
|
||||
viewId?: string | null
|
||||
) => void;
|
||||
}
|
||||
|
||||
export class KanBanHelpers implements IKanBanHelpers {
|
||||
constructor() {}
|
||||
|
||||
handleSortOrder = (destinationIssues: any, destinationIndex: any, issues: any) => {
|
||||
const sortOrderDefaultValue = 65535;
|
||||
let currentIssueState = {};
|
||||
|
||||
if (destinationIssues && destinationIssues.length > 0) {
|
||||
if (destinationIndex === 0) {
|
||||
const destinationIssueId = destinationIssues[destinationIndex];
|
||||
currentIssueState = {
|
||||
...currentIssueState,
|
||||
sort_order: issues[destinationIssueId].sort_order - sortOrderDefaultValue,
|
||||
};
|
||||
} else if (destinationIndex === destinationIssues.length) {
|
||||
const destinationIssueId = destinationIssues[destinationIndex - 1];
|
||||
currentIssueState = {
|
||||
...currentIssueState,
|
||||
sort_order: issues[destinationIssueId].sort_order + sortOrderDefaultValue,
|
||||
};
|
||||
} else {
|
||||
const destinationTopIssueId = destinationIssues[destinationIndex - 1];
|
||||
const destinationBottomIssueId = destinationIssues[destinationIndex];
|
||||
currentIssueState = {
|
||||
...currentIssueState,
|
||||
sort_order: (issues[destinationTopIssueId].sort_order + issues[destinationBottomIssueId].sort_order) / 2,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
currentIssueState = {
|
||||
...currentIssueState,
|
||||
sort_order: sortOrderDefaultValue,
|
||||
};
|
||||
}
|
||||
|
||||
return currentIssueState;
|
||||
};
|
||||
|
||||
handleDragDrop = async (
|
||||
source: any,
|
||||
destination: any,
|
||||
workspaceSlug: string,
|
||||
projectId: string, // projectId for all views or user id in profile issues
|
||||
store: any,
|
||||
subGroupBy: string | null,
|
||||
groupBy: string | null,
|
||||
issues: any,
|
||||
issueWithIds: any,
|
||||
viewId: string | null = null // it can be moduleId, cycleId
|
||||
) => {
|
||||
if (issues && issueWithIds) {
|
||||
let updateIssue: any = {};
|
||||
|
||||
const sourceColumnId = (source?.droppableId && source?.droppableId.split("__")) || null;
|
||||
const destinationColumnId = (destination?.droppableId && destination?.droppableId.split("__")) || null;
|
||||
|
||||
const sourceGroupByColumnId = sourceColumnId[0] || null;
|
||||
const destinationGroupByColumnId = destinationColumnId[0] || null;
|
||||
|
||||
const sourceSubGroupByColumnId = sourceColumnId[1] || null;
|
||||
const destinationSubGroupByColumnId = destinationColumnId[1] || null;
|
||||
|
||||
if (!workspaceSlug || !projectId || !groupBy || !sourceGroupByColumnId || !destinationGroupByColumnId) return;
|
||||
|
||||
if (destinationGroupByColumnId === "issue-trash-box") {
|
||||
const sourceIssues = subGroupBy
|
||||
? issueWithIds[sourceSubGroupByColumnId][sourceGroupByColumnId]
|
||||
: issueWithIds[sourceGroupByColumnId];
|
||||
|
||||
const [removed] = sourceIssues.splice(source.index, 1);
|
||||
|
||||
console.log("removed", removed);
|
||||
|
||||
if (removed) {
|
||||
if (viewId) store?.removeIssue(workspaceSlug, projectId, removed, viewId);
|
||||
else store?.removeIssue(workspaceSlug, projectId, removed);
|
||||
}
|
||||
} else {
|
||||
const sourceIssues = subGroupBy
|
||||
? issueWithIds[sourceSubGroupByColumnId][sourceGroupByColumnId]
|
||||
: issueWithIds[sourceGroupByColumnId];
|
||||
const destinationIssues = subGroupBy
|
||||
? issueWithIds[sourceSubGroupByColumnId][destinationGroupByColumnId]
|
||||
: issueWithIds[destinationGroupByColumnId];
|
||||
|
||||
const [removed] = sourceIssues.splice(source.index, 1);
|
||||
const removedIssueDetail = issues[removed];
|
||||
|
||||
if (subGroupBy && sourceSubGroupByColumnId && destinationSubGroupByColumnId) {
|
||||
updateIssue = {
|
||||
id: removedIssueDetail?.id,
|
||||
};
|
||||
|
||||
// for both horizontal and vertical dnd
|
||||
updateIssue = {
|
||||
...updateIssue,
|
||||
...this.handleSortOrder(destinationIssues, destination.index, issues),
|
||||
};
|
||||
|
||||
if (sourceSubGroupByColumnId === destinationSubGroupByColumnId) {
|
||||
if (sourceGroupByColumnId != destinationGroupByColumnId) {
|
||||
if (groupBy === "state") updateIssue = { ...updateIssue, state: destinationGroupByColumnId };
|
||||
if (groupBy === "priority") updateIssue = { ...updateIssue, priority: destinationGroupByColumnId };
|
||||
}
|
||||
} else {
|
||||
if (subGroupBy === "state")
|
||||
updateIssue = {
|
||||
...updateIssue,
|
||||
state: destinationSubGroupByColumnId,
|
||||
priority: destinationGroupByColumnId,
|
||||
};
|
||||
if (subGroupBy === "priority")
|
||||
updateIssue = {
|
||||
...updateIssue,
|
||||
state: destinationGroupByColumnId,
|
||||
priority: destinationSubGroupByColumnId,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
updateIssue = {
|
||||
id: removedIssueDetail?.id,
|
||||
};
|
||||
|
||||
// for both horizontal and vertical dnd
|
||||
updateIssue = {
|
||||
...updateIssue,
|
||||
...this.handleSortOrder(destinationIssues, destination.index, issues),
|
||||
};
|
||||
|
||||
// for horizontal dnd
|
||||
if (sourceColumnId != destinationColumnId) {
|
||||
if (groupBy === "state") updateIssue = { ...updateIssue, state: destinationGroupByColumnId };
|
||||
if (groupBy === "priority") updateIssue = { ...updateIssue, priority: destinationGroupByColumnId };
|
||||
}
|
||||
}
|
||||
|
||||
if (updateIssue && updateIssue?.id) {
|
||||
if (viewId) store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue, viewId);
|
||||
else store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
/** project issues and issue-filters starts */
|
||||
|
||||
// helpers
|
||||
export * from "./base-issue-calendar-helper.store";
|
||||
export * from "./base-issue-kanban-helper.store";
|
||||
|
||||
// issue and filter helpers
|
||||
export * from "./project-issues/base-issue.store";
|
||||
export * from "./project-issues/base-issue-filter.store";
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
// types
|
||||
import { RootStore } from "store/root";
|
||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueParams } from "types";
|
||||
import { EFilterType } from "store/issues/types";
|
||||
import { handleIssueQueryParamsByLayout } from "helpers/issue.helper";
|
||||
import { IssueFilterBaseStore } from "../project-issues/base-issue-filter.store";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
|
||||
interface IProjectIssuesFiltersOptions {
|
||||
filters: IIssueFilterOptions;
|
||||
@ -199,14 +199,14 @@ export class ProfileIssuesFilterStore extends IssueFilterBaseStore implements IP
|
||||
try {
|
||||
const displayProperties: IIssueDisplayProperties = {
|
||||
assignee: true,
|
||||
start_date: false,
|
||||
due_date: false,
|
||||
start_date: true,
|
||||
due_date: true,
|
||||
labels: true,
|
||||
key: true,
|
||||
priority: true,
|
||||
state: false,
|
||||
sub_issue_count: false,
|
||||
link: false,
|
||||
sub_issue_count: true,
|
||||
link: true,
|
||||
attachment_count: false,
|
||||
estimate: false,
|
||||
created_on: false,
|
||||
|
@ -1,4 +1,8 @@
|
||||
import _ from "lodash";
|
||||
import sortBy from "lodash/sortBy";
|
||||
import get from "lodash/get";
|
||||
import indexOf from "lodash/indexOf";
|
||||
import reverse from "lodash/reverse";
|
||||
import values from "lodash/values";
|
||||
// types
|
||||
import { IIssue, TIssueGroupByOptions, TIssueOrderByOptions } from "types";
|
||||
import { RootStore } from "store/root";
|
||||
@ -51,7 +55,7 @@ export class IssueBaseStore implements IIssueBaseStore {
|
||||
|
||||
for (const issue in projectIssues) {
|
||||
const _issue = projectIssues[issue];
|
||||
const groupArray = this.getGroupArray(_.get(_issue, groupBy as keyof IIssue), isCalendarIssues);
|
||||
const groupArray = this.getGroupArray(get(_issue, groupBy as keyof IIssue), isCalendarIssues);
|
||||
|
||||
for (const group of groupArray) {
|
||||
if (group && _issues[group]) _issues[group].push(_issue.id);
|
||||
@ -82,8 +86,8 @@ export class IssueBaseStore implements IIssueBaseStore {
|
||||
|
||||
for (const issue in projectIssues) {
|
||||
const _issue = projectIssues[issue];
|
||||
const subGroupArray = this.getGroupArray(_.get(_issue, subGroupBy as keyof IIssue));
|
||||
const groupArray = this.getGroupArray(_.get(_issue, groupBy as keyof IIssue));
|
||||
const subGroupArray = this.getGroupArray(get(_issue, subGroupBy as keyof IIssue));
|
||||
const groupArray = this.getGroupArray(get(_issue, groupBy as keyof IIssue));
|
||||
|
||||
for (const subGroup of subGroupArray) {
|
||||
for (const group of groupArray) {
|
||||
@ -121,22 +125,22 @@ export class IssueBaseStore implements IIssueBaseStore {
|
||||
};
|
||||
|
||||
issuesSortWithOrderBy = (issueObject: IIssueResponse, key: Partial<TIssueOrderByOptions>): IIssue[] => {
|
||||
let array = _.values(issueObject);
|
||||
array = _.sortBy(array, "created_at");
|
||||
let array = values(issueObject);
|
||||
array = sortBy(array, "created_at");
|
||||
switch (key) {
|
||||
case "sort_order":
|
||||
return _.sortBy(array, "sort_order");
|
||||
return sortBy(array, "sort_order");
|
||||
case "-created_at":
|
||||
return _.reverse(_.sortBy(array, "created_at"));
|
||||
return reverse(sortBy(array, "created_at"));
|
||||
case "-updated_at":
|
||||
return _.reverse(_.sortBy(array, "updated_at"));
|
||||
return reverse(sortBy(array, "updated_at"));
|
||||
case "start_date":
|
||||
return _.sortBy(array, "start_date");
|
||||
return sortBy(array, "start_date");
|
||||
case "target_date":
|
||||
return _.sortBy(array, "target_date");
|
||||
return sortBy(array, "target_date");
|
||||
case "priority": {
|
||||
const sortArray = ISSUE_PRIORITIES.map((i) => i.key);
|
||||
return _.sortBy(array, (_issue: IIssue) => _.indexOf(sortArray, _issue.priority));
|
||||
return sortBy(array, (_issue: IIssue) => indexOf(sortArray, _issue.priority));
|
||||
}
|
||||
default:
|
||||
return array;
|
||||
|
@ -94,24 +94,15 @@ export class ProjectIssuesStore extends IssueBaseStore implements IProjectIssues
|
||||
let issues: IIssueResponse | IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined = undefined;
|
||||
|
||||
if (layout === "list" && orderBy) {
|
||||
console.log("list");
|
||||
if (groupBy) issues = this.groupedIssues(groupBy, orderBy, this.issues[projectId]);
|
||||
else issues = this.unGroupedIssues(orderBy, this.issues[projectId]);
|
||||
} else if (layout === "kanban" && groupBy && orderBy) {
|
||||
console.log("kanban");
|
||||
if (subGroupBy) issues = this.subGroupedIssues(subGroupBy, groupBy, orderBy, this.issues[projectId]);
|
||||
else issues = this.groupedIssues(groupBy, orderBy, this.issues[projectId]);
|
||||
console.log("issues", issues);
|
||||
} else if (layout === "calendar") {
|
||||
console.log("calendar");
|
||||
} else if (layout === "calendar")
|
||||
issues = this.groupedIssues("target_date" as TIssueGroupByOptions, "target_date", this.issues[projectId], true);
|
||||
} else if (layout === "spreadsheet") {
|
||||
console.log("spreadsheet");
|
||||
issues = this.unGroupedIssues(orderBy ?? "-created_at", this.issues[projectId]);
|
||||
} else if (layout === "gantt_chart") {
|
||||
console.log("gantt_chart");
|
||||
issues = this.unGroupedIssues(orderBy ?? "sort_order", this.issues[projectId]);
|
||||
}
|
||||
else if (layout === "spreadsheet") issues = this.unGroupedIssues(orderBy ?? "-created_at", this.issues[projectId]);
|
||||
else if (layout === "gantt_chart") issues = this.unGroupedIssues(orderBy ?? "sort_order", this.issues[projectId]);
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
@ -164,6 +164,11 @@ import {
|
||||
// global issues filter
|
||||
IGlobalIssuesFilterStore,
|
||||
GlobalIssuesFilterStore,
|
||||
// helpers
|
||||
ICalendarHelpers,
|
||||
CalendarHelpers,
|
||||
IKanBanHelpers,
|
||||
KanBanHelpers,
|
||||
} from "store/issues";
|
||||
|
||||
import { CycleIssueFiltersStore, ICycleIssueFiltersStore } from "store/cycle-issues";
|
||||
@ -274,6 +279,10 @@ export class RootStore {
|
||||
|
||||
workspaceGlobalIssues: IGlobalIssuesStore;
|
||||
workspaceGlobalIssuesFilter: IGlobalIssuesFilterStore;
|
||||
|
||||
calendarHelpers: ICalendarHelpers;
|
||||
|
||||
kanBanHelpers: IKanBanHelpers;
|
||||
// project v3 issue and issue-filters ends
|
||||
|
||||
cycleIssueFilters: ICycleIssueFiltersStore;
|
||||
@ -378,6 +387,10 @@ export class RootStore {
|
||||
|
||||
this.workspaceGlobalIssues = new GlobalIssuesStore(this);
|
||||
this.workspaceGlobalIssuesFilter = new GlobalIssuesFilterStore(this);
|
||||
|
||||
this.calendarHelpers = new CalendarHelpers();
|
||||
|
||||
this.kanBanHelpers = new KanBanHelpers();
|
||||
// project v3 issue and issue-filters ends
|
||||
|
||||
this.cycleIssueFilters = new CycleIssueFiltersStore(this);
|
||||
|
Loading…
Reference in New Issue
Block a user