diff --git a/web/components/issues/issue-layouts/calendar/base-calendar-root.tsx b/web/components/issues/issue-layouts/calendar/base-calendar-root.tsx index d2b93910c..f8c0de167 100644 --- a/web/components/issues/issue-layouts/calendar/base-calendar-root.tsx +++ b/web/components/issues/issue-layouts/calendar/base-calendar-root.tsx @@ -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( diff --git a/web/components/issues/issue-layouts/calendar/roots/cycle-root.tsx b/web/components/issues/issue-layouts/calendar/roots/cycle-root.tsx index 60339acf8..fde701b51 100644 --- a/web/components/issues/issue-layouts/calendar/roots/cycle-root.tsx +++ b/web/components/issues/issue-layouts/calendar/roots/cycle-root.tsx @@ -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 ( { QuickActions={CycleIssueQuickActions} issueActions={issueActions} viewId={cycleId} + handleDragDrop={handleDragDrop} /> ); }); diff --git a/web/components/issues/issue-layouts/calendar/roots/module-root.tsx b/web/components/issues/issue-layouts/calendar/roots/module-root.tsx index b6f6079aa..e8d66e04b 100644 --- a/web/components/issues/issue-layouts/calendar/roots/module-root.tsx +++ b/web/components/issues/issue-layouts/calendar/roots/module-root.tsx @@ -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 ( { QuickActions={ModuleIssueQuickActions} issueActions={issueActions} viewId={moduleId} + handleDragDrop={handleDragDrop} /> ); }); diff --git a/web/components/issues/issue-layouts/calendar/roots/project-root.tsx b/web/components/issues/issue-layouts/calendar/roots/project-root.tsx index e8b4e3282..254dd714c 100644 --- a/web/components/issues/issue-layouts/calendar/roots/project-root.tsx +++ b/web/components/issues/issue-layouts/calendar/roots/project-root.tsx @@ -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 ( { calendarViewStore={issueCalendarViewStore} QuickActions={ProjectIssueQuickActions} issueActions={issueActions} + handleDragDrop={handleDragDrop} /> ); }); diff --git a/web/components/issues/issue-layouts/calendar/roots/project-view-root.tsx b/web/components/issues/issue-layouts/calendar/roots/project-view-root.tsx index dc100c5bc..0cf116749 100644 --- a/web/components/issues/issue-layouts/calendar/roots/project-view-root.tsx +++ b/web/components/issues/issue-layouts/calendar/roots/project-view-root.tsx @@ -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 ( { calendarViewStore={projectViewIssueCalendarViewStore} QuickActions={ProjectIssueQuickActions} issueActions={issueActions} + handleDragDrop={handleDragDrop} /> ); }); diff --git a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx index 5f58705de..14c2cdbeb 100644 --- a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx +++ b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx @@ -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; } @@ -67,6 +76,7 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas showLoader, viewId, currentStore, + handleDragDrop, addIssuesToView, } = props; @@ -93,9 +103,9 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas const currentKanBanView: "swimlanes" | "default" = sub_group_by ? "swimlanes" : "default"; - const [isDragStarted, setIsDragStarted] = useState(false); - const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issueStore?.viewFlags || {}; + + const [isDragStarted, setIsDragStarted] = useState(false); const onDragStart = () => { setIsDragStarted(true); }; @@ -115,9 +125,7 @@ export const BaseKanBanRoot: React.FC = 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 = observer((props: IBas
+
+ + {(provided, snapshot) => ( +
+ Drop here to delete the issue. +
+ )} +
+
+ {currentKanBanView === "default" ? ( { 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 ( { QuickActions={CycleIssueQuickActions} viewId={cycleId} currentStore={EProjectStore.CYCLE} + handleDragDrop={handleDragDrop} addIssuesToView={(issues: string[]) => cycleIssueStore.addIssueToCycle(workspaceSlug, cycleId, issues)} /> ); diff --git a/web/components/issues/issue-layouts/kanban/roots/module-root.tsx b/web/components/issues/issue-layouts/kanban/roots/module-root.tsx index bbab98159..257378e78 100644 --- a/web/components/issues/issue-layouts/kanban/roots/module-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/module-root.tsx @@ -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 ( { QuickActions={ModuleIssueQuickActions} viewId={moduleId} currentStore={EProjectStore.MODULE} + handleDragDrop={handleDragDrop} addIssuesToView={(issues: string[]) => moduleIssueStore.addIssueToModule(workspaceSlug, moduleId, issues)} /> ); diff --git a/web/components/issues/issue-layouts/kanban/roots/project-root.tsx b/web/components/issues/issue-layouts/kanban/roots/project-root.tsx index bd50d6824..bc085b37c 100644 --- a/web/components/issues/issue-layouts/kanban/roots/project-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/project-root.tsx @@ -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 ( { showLoader={true} QuickActions={ProjectIssueQuickActions} currentStore={EProjectStore.PROJECT} + handleDragDrop={handleDragDrop} /> ); }); diff --git a/web/components/issues/issue-layouts/kanban/roots/project-view-root.tsx b/web/components/issues/issue-layouts/kanban/roots/project-view-root.tsx index 625d2720f..3ad8b65cf 100644 --- a/web/components/issues/issue-layouts/kanban/roots/project-view-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/project-view-root.tsx @@ -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 ( { showLoader={true} QuickActions={ProjectIssueQuickActions} currentStore={EProjectStore.PROJECT_VIEW} + handleDragDrop={handleDragDrop} /> ); }); diff --git a/web/package.json b/web/package.json index 135a913a4..4044be2ae 100644 --- a/web/package.json +++ b/web/package.json @@ -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", diff --git a/web/store/issues/base-issue-calendar-helper.store.ts b/web/store/issues/base-issue-calendar-helper.store.ts new file mode 100644 index 000000000..4eeee8418 --- /dev/null +++ b/web/store/issues/base-issue-calendar-helper.store.ts @@ -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); + } + } + }; +} diff --git a/web/store/issues/base-issue-kanban-helper.store.ts b/web/store/issues/base-issue-kanban-helper.store.ts new file mode 100644 index 000000000..6ab32bbc5 --- /dev/null +++ b/web/store/issues/base-issue-kanban-helper.store.ts @@ -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); + } + } + } + }; +} diff --git a/web/store/issues/index.ts b/web/store/issues/index.ts index eeb192030..53a05cdbd 100644 --- a/web/store/issues/index.ts +++ b/web/store/issues/index.ts @@ -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"; diff --git a/web/store/issues/profile/filter.store.ts b/web/store/issues/profile/filter.store.ts index 771368a78..e8567b042 100644 --- a/web/store/issues/profile/filter.store.ts +++ b/web/store/issues/profile/filter.store.ts @@ -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, diff --git a/web/store/issues/project-issues/base-issue.store.ts b/web/store/issues/project-issues/base-issue.store.ts index d10dba31e..2e3e744ec 100644 --- a/web/store/issues/project-issues/base-issue.store.ts +++ b/web/store/issues/project-issues/base-issue.store.ts @@ -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): 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; diff --git a/web/store/issues/project-issues/project/issue.store.ts b/web/store/issues/project-issues/project/issue.store.ts index 127c6e328..5e89ae712 100644 --- a/web/store/issues/project-issues/project/issue.store.ts +++ b/web/store/issues/project-issues/project/issue.store.ts @@ -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; } diff --git a/web/store/root.ts b/web/store/root.ts index c15890298..a4652889f 100644 --- a/web/store/root.ts +++ b/web/store/root.ts @@ -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);