Skip to content

Commit 9ad1e73

Browse files
authored
dev: gantt chart implementation using MobX (#2302)
* dev: fetch project gantt issues using mobx * chore: handle group by options in the kanban layout
1 parent 479c145 commit 9ad1e73

File tree

18 files changed

+120
-246
lines changed

18 files changed

+120
-246
lines changed
Lines changed: 14 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,24 @@
1-
import React, { useCallback, useState } from "react";
2-
1+
import React from "react";
32
import { useRouter } from "next/router";
4-
3+
import { observer } from "mobx-react-lite";
54
import useSWR from "swr";
65

7-
// react-beautiful-dnd
8-
import { DragDropContext, DropResult } from "react-beautiful-dnd";
9-
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
10-
// services
11-
import stateService from "services/project_state.service";
12-
// hooks
13-
import useUser from "hooks/use-user";
14-
import { useProjectMyMembership } from "contexts/project-member.context";
15-
// components
16-
import { AllLists, AllBoards, CalendarView, SpreadsheetView, GanttChartView } from "components/core";
17-
import { CalendarLayout, KanBanLayout } from "components/issues";
18-
// ui
19-
import { EmptyState, Spinner } from "components/ui";
20-
// icons
21-
import { TrashIcon } from "@heroicons/react/24/outline";
22-
// images
23-
import emptyIssue from "public/empty-state/issue.svg";
24-
import emptyIssueArchive from "public/empty-state/issue-archive.svg";
25-
// helpers
26-
import { getStatesList } from "helpers/state.helper";
27-
// types
28-
import { IIssue, IIssueViewProps } from "types";
29-
// fetch-keys
30-
import { STATES_LIST } from "constants/fetch-keys";
31-
// store
6+
// mobx store
327
import { useMobxStore } from "lib/mobx/store-provider";
33-
import { RootStore } from "store/root";
34-
import { observer } from "mobx-react-lite";
35-
36-
type Props = {
37-
addIssueToDate: (date: string) => void;
38-
addIssueToGroup: (groupTitle: string) => void;
39-
disableUserActions: boolean;
40-
dragDisabled?: boolean;
41-
emptyState: {
42-
title: string;
43-
description?: string;
44-
primaryButton?: {
45-
icon: any;
46-
text: string;
47-
onClick: () => void;
48-
};
49-
secondaryButton?: React.ReactNode;
50-
};
51-
handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void;
52-
handleDraftIssueAction?: (issue: IIssue, action: "edit" | "delete") => void;
53-
handleOnDragEnd: (result: DropResult) => Promise<void>;
54-
openIssuesListModal: (() => void) | null;
55-
removeIssue: ((bridgeId: string, issueId: string) => void) | null;
56-
disableAddIssueOption?: boolean;
57-
trashBox: boolean;
58-
setTrashBox: React.Dispatch<React.SetStateAction<boolean>>;
59-
viewProps: IIssueViewProps;
60-
};
8+
// components
9+
import { CalendarLayout, GanttLayout, KanBanLayout } from "components/issues";
6110

62-
export const AllViews: React.FC<Props> = observer(({ trashBox, setTrashBox }) => {
11+
export const AllViews: React.FC = observer(() => {
6312
const router = useRouter();
64-
const { workspaceSlug, projectId, cycleId, moduleId } = router.query as {
13+
const { workspaceSlug, projectId } = router.query as {
6514
workspaceSlug: string;
6615
projectId: string;
6716
cycleId: string;
6817
moduleId: string;
6918
};
7019

71-
const [myIssueProjectId, setMyIssueProjectId] = useState<string | null>(null);
72-
7320
const { issue: issueStore, project: projectStore, issueFilter: issueFilterStore } = useMobxStore();
7421

75-
const { data: stateGroups } = useSWR(
76-
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
77-
workspaceSlug ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
78-
);
79-
const states = getStatesList(stateGroups);
80-
81-
const handleMyIssueOpen = (issue: IIssue) => {
82-
setMyIssueProjectId(issue.project);
83-
};
84-
85-
const handleTrashBox = useCallback(
86-
(isDragging: boolean) => {
87-
if (isDragging && !trashBox) setTrashBox(true);
88-
},
89-
[trashBox, setTrashBox]
90-
);
91-
9222
useSWR(workspaceSlug && projectId ? `PROJECT_ISSUES` : null, async () => {
9323
if (workspaceSlug && projectId) {
9424
await issueFilterStore.fetchUserProjectFilters(workspaceSlug, projectId);
@@ -105,7 +35,13 @@ export const AllViews: React.FC<Props> = observer(({ trashBox, setTrashBox }) =>
10535

10636
return (
10737
<div className="relative w-full h-full overflow-auto">
108-
{activeLayout === "kanban" ? <KanBanLayout /> : activeLayout === "calendar" ? <CalendarLayout /> : null}
38+
{activeLayout === "kanban" ? (
39+
<KanBanLayout />
40+
) : activeLayout === "calendar" ? (
41+
<CalendarLayout />
42+
) : activeLayout === "gantt_chart" ? (
43+
<GanttLayout />
44+
) : null}
10945
</div>
11046
);
11147
});

web/components/cycles/gantt-chart/cycle-issues-layout.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { useRouter } from "next/router";
44
import useIssuesView from "hooks/use-issues-view";
55
import useUser from "hooks/use-user";
66
import useGanttChartCycleIssues from "hooks/gantt-chart/cycle-issues-view";
7-
import { updateGanttIssue } from "components/gantt-chart/hooks/block-update";
87
import useProjectDetails from "hooks/use-project-details";
98
// components
109
import { GanttChartRoot, renderIssueBlocksStructure } from "components/gantt-chart";
@@ -47,9 +46,7 @@ export const CycleIssuesGanttChartView: React.FC<Props> = ({ disableUserActions
4746
title="Issues"
4847
loaderTitle="Issues"
4948
blocks={ganttIssues ? renderIssueBlocksStructure(ganttIssues as IIssue[]) : null}
50-
blockUpdateHandler={(block, payload) =>
51-
updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString())
52-
}
49+
blockUpdateHandler={(block, payload) => {}}
5350
SidebarBlockRender={IssueGanttSidebarBlock}
5451
BlockRender={IssueGanttBlock}
5552
enableBlockLeftResize={isAllowed}

web/components/gantt-chart/hooks/block-update.tsx

Lines changed: 0 additions & 40 deletions
This file was deleted.

web/components/headers/project-issues.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
1717

1818
const { issueFilter: issueFilterStore } = useMobxStore();
1919

20+
const activeLayout = issueFilterStore.userDisplayFilters.layout;
21+
2022
const handleLayoutChange = useCallback(
2123
(layout: TIssueLayouts) => {
2224
if (!workspaceSlug || !projectId) return;
@@ -72,25 +74,21 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
7274
<LayoutSelection
7375
layouts={["list", "kanban", "calendar", "spreadsheet", "gantt_chart"]}
7476
onChange={(layout) => handleLayoutChange(layout)}
75-
selectedLayout={issueFilterStore.userDisplayFilters.layout ?? "list"}
77+
selectedLayout={activeLayout}
7678
/>
7779
<FiltersDropdown title="Filters">
7880
<FilterSelection
7981
filters={issueFilterStore.userFilters}
8082
handleFiltersUpdate={handleFiltersUpdate}
81-
layoutDisplayFiltersOptions={
82-
ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[issueFilterStore.userDisplayFilters.layout ?? "list"]
83-
}
83+
layoutDisplayFiltersOptions={activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined}
8484
projectId={projectId?.toString() ?? ""}
8585
/>
8686
</FiltersDropdown>
8787
<FiltersDropdown title="View">
8888
<DisplayFiltersSelection
8989
displayFilters={issueFilterStore.userDisplayFilters}
9090
handleDisplayFiltersUpdate={handleDisplayFiltersUpdate}
91-
layoutDisplayFiltersOptions={
92-
ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[issueFilterStore.userDisplayFilters.layout ?? "list"]
93-
}
91+
layoutDisplayFiltersOptions={activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined}
9492
/>
9593
</FiltersDropdown>
9694
</div>

web/components/issues/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ export * from "./activity";
77
export * from "./delete-issue-modal";
88
export * from "./description-form";
99
export * from "./form";
10-
export * from "./gantt-chart";
1110
export * from "./issue-layouts";
1211
export * from "./main-content";
1312
export * from "./modal";
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export * from "./blocks";
2-
export * from "./layout";
2+
export * from "./root";
Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,59 @@
11
import { useRouter } from "next/router";
2+
import { observer } from "mobx-react-lite";
23

4+
// mobx store
5+
import { useMobxStore } from "lib/mobx/store-provider";
36
// hooks
4-
import useIssuesView from "hooks/use-issues-view";
5-
import useUser from "hooks/use-user";
6-
import useGanttChartIssues from "hooks/gantt-chart/issue-view";
7-
import { updateGanttIssue } from "components/gantt-chart/hooks/block-update";
87
import useProjectDetails from "hooks/use-project-details";
98
// components
109
import { GanttChartRoot, renderIssueBlocksStructure } from "components/gantt-chart";
1110
import { IssueGanttBlock, IssueGanttSidebarBlock, IssuePeekOverview } from "components/issues";
1211
// types
13-
import { IIssue } from "types";
12+
import { IIssueUnGroupedStructure } from "store/issue";
1413

15-
type Props = {
16-
disableUserActions: boolean;
17-
};
18-
19-
export const IssueGanttChartView: React.FC<Props> = ({ disableUserActions }) => {
14+
export const GanttLayout: React.FC = observer(() => {
2015
const router = useRouter();
2116
const { workspaceSlug, projectId } = router.query;
2217

23-
const { displayFilters } = useIssuesView();
24-
25-
const { user } = useUser();
2618
const { projectDetails } = useProjectDetails();
2719

28-
const { ganttIssues, mutateGanttIssues } = useGanttChartIssues(
29-
workspaceSlug as string,
30-
projectId as string
31-
);
20+
const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore();
21+
22+
const appliedDisplayFilters = issueFilterStore.userDisplayFilters;
23+
24+
const issues = issueStore.getIssues;
3225

3326
const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15;
3427

28+
console.log("issues", issues);
29+
console.log("appliedFilters", issueFilterStore.appliedFilters);
30+
3531
return (
3632
<>
3733
<IssuePeekOverview
38-
handleMutation={() => mutateGanttIssues()}
3934
projectId={projectId?.toString() ?? ""}
4035
workspaceSlug={workspaceSlug?.toString() ?? ""}
41-
readOnly={disableUserActions}
36+
readOnly={!isAllowed}
4237
/>
4338
<div className="w-full h-full">
4439
<GanttChartRoot
4540
border={false}
4641
title="Issues"
4742
loaderTitle="Issues"
48-
blocks={ganttIssues ? renderIssueBlocksStructure(ganttIssues as IIssue[]) : null}
49-
blockUpdateHandler={(block, payload) =>
50-
updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString())
51-
}
43+
blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null}
44+
blockUpdateHandler={(block, payload) => {
45+
// TODO: update mutation logic
46+
// updateGanttIssue(block, payload, mutateGanttIssues, user, workspaceSlug?.toString())
47+
}}
5248
BlockRender={IssueGanttBlock}
5349
SidebarBlockRender={IssueGanttSidebarBlock}
5450
enableBlockLeftResize={isAllowed}
5551
enableBlockRightResize={isAllowed}
5652
enableBlockMove={isAllowed}
57-
enableReorder={displayFilters.order_by === "sort_order" && isAllowed}
53+
enableReorder={appliedDisplayFilters.order_by === "sort_order" && isAllowed}
5854
bottomSpacing
5955
/>
6056
</div>
6157
</>
6258
);
63-
};
59+
});

web/components/issues/issue-layouts/header/display-filters/display-filters-selection.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ import { ILayoutDisplayFiltersOptions } from "constants/issue";
1717
type Props = {
1818
displayFilters: IIssueDisplayFilterOptions;
1919
handleDisplayFiltersUpdate: (updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => void;
20-
layoutDisplayFiltersOptions: ILayoutDisplayFiltersOptions;
20+
layoutDisplayFiltersOptions: ILayoutDisplayFiltersOptions | undefined;
2121
};
2222

2323
export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
2424
const { displayFilters, handleDisplayFiltersUpdate, layoutDisplayFiltersOptions } = props;
2525

2626
const isDisplayFilterEnabled = (displayFilter: keyof IIssueDisplayFilterOptions) =>
27-
Object.keys(layoutDisplayFiltersOptions.display_filters).includes(displayFilter);
27+
Object.keys(layoutDisplayFiltersOptions?.display_filters ?? {}).includes(displayFilter);
2828

2929
return (
3030
<div className="w-full h-full overflow-hidden overflow-y-auto relative px-2.5 divide-y divide-custom-border-200">
3131
{/* display properties */}
32-
{layoutDisplayFiltersOptions.display_properties && (
32+
{layoutDisplayFiltersOptions?.display_properties && (
3333
<div className="py-2">
3434
<FilterDisplayProperties />
3535
</div>
@@ -41,7 +41,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
4141
<FilterGroupBy
4242
selectedGroupBy={displayFilters.group_by}
4343
selectedSubGroupBy={displayFilters.sub_group_by}
44-
groupByOptions={layoutDisplayFiltersOptions.display_filters.group_by ?? []}
44+
groupByOptions={layoutDisplayFiltersOptions?.display_filters.group_by ?? []}
4545
handleUpdate={(val) =>
4646
handleDisplayFiltersUpdate({
4747
group_by: val,
@@ -62,7 +62,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
6262
sub_group_by: val,
6363
})
6464
}
65-
subGroupByOptions={layoutDisplayFiltersOptions.display_filters.sub_group_by ?? []}
65+
subGroupByOptions={layoutDisplayFiltersOptions?.display_filters.sub_group_by ?? []}
6666
/>
6767
</div>
6868
)}
@@ -96,7 +96,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
9696
)}
9797

9898
{/* Options */}
99-
{layoutDisplayFiltersOptions.extra_options.access && (
99+
{layoutDisplayFiltersOptions?.extra_options.access && (
100100
<div className="py-2">
101101
<FilterExtraOptions
102102
selectedExtraOptions={{
@@ -108,7 +108,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
108108
[key]: val,
109109
})
110110
}
111-
enabledExtraOptions={layoutDisplayFiltersOptions.extra_options.values}
111+
enabledExtraOptions={layoutDisplayFiltersOptions?.extra_options.values}
112112
/>
113113
</div>
114114
)}

web/components/issues/issue-layouts/header/filters/filters-selection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { DATE_FILTER_OPTIONS } from "constants/filters";
2727
type Props = {
2828
filters: IIssueFilterOptions;
2929
handleFiltersUpdate: (key: keyof IIssueFilterOptions, value: string | string[]) => void;
30-
layoutDisplayFiltersOptions: ILayoutDisplayFiltersOptions;
30+
layoutDisplayFiltersOptions: ILayoutDisplayFiltersOptions | undefined;
3131
projectId: string;
3232
};
3333

@@ -125,7 +125,7 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
125125
return filterDetails.currentLength > 5;
126126
};
127127

128-
const isFilterEnabled = (filter: keyof IIssueFilterOptions) => layoutDisplayFiltersOptions.filters.includes(filter);
128+
const isFilterEnabled = (filter: keyof IIssueFilterOptions) => layoutDisplayFiltersOptions?.filters.includes(filter);
129129

130130
return (
131131
<div className="w-full h-full flex flex-col overflow-hidden">

0 commit comments

Comments
 (0)