From b9a6a004704452ecf2aad0bdaacb8269378eefbc Mon Sep 17 00:00:00 2001 From: gurusainath Date: Fri, 8 Sep 2023 12:42:09 +0530 Subject: [PATCH] chore: updated the store for issues and issue filters --- .../issue-layouts/kanban/content.tsx | 17 +- .../issue-layouts/kanban/footer.tsx | 1 - .../issue-layouts/kanban/header.tsx | 23 +- web/components/issue-layouts/kanban/index.tsx | 69 +-- web/package.json | 2 +- web/pages/kanban.tsx | 169 ++++++- web/services/cycles.service.ts | 2 +- web/services/modules.service.ts | 2 +- web/services/user.service.ts | 2 +- web/store/issue-views/Issue_details.ts | 205 ++++++++ web/store/issue-views/Issues.ts | 454 ++++++++++++++++++ web/store/issue-views/filters.ts | 154 ------ .../issue-views/{data.ts => issue_data.ts} | 30 +- web/store/issue-views/issue_filters.ts | 330 +++++++++++++ web/store/issue-views/kanban.ts | 92 ---- web/store/root.ts | 8 +- yarn.lock | 14 +- 17 files changed, 1253 insertions(+), 321 deletions(-) delete mode 100644 web/components/issue-layouts/kanban/footer.tsx create mode 100644 web/store/issue-views/Issue_details.ts create mode 100644 web/store/issue-views/Issues.ts delete mode 100644 web/store/issue-views/filters.ts rename web/store/issue-views/{data.ts => issue_data.ts} (66%) create mode 100644 web/store/issue-views/issue_filters.ts delete mode 100644 web/store/issue-views/kanban.ts diff --git a/web/components/issue-layouts/kanban/content.tsx b/web/components/issue-layouts/kanban/content.tsx index 15e34becd..6f297fcf6 100644 --- a/web/components/issue-layouts/kanban/content.tsx +++ b/web/components/issue-layouts/kanban/content.tsx @@ -22,22 +22,23 @@ export const IssueContent = ({ columnId, issues }: IssueContentProps) => { {(provided: any, snapshot: any) => (
-
+
ONE-{issue.sequence_id}-{issue.sort_order}
-
- {issue.name} {issue.name} {issue.name} {issue.name} {issue.name} {issue.name}{" "} - {issue.name} {issue.name} -
-
Footer
+
{issue.name}
+
Footer
)} diff --git a/web/components/issue-layouts/kanban/footer.tsx b/web/components/issue-layouts/kanban/footer.tsx deleted file mode 100644 index 21b09c11f..000000000 --- a/web/components/issue-layouts/kanban/footer.tsx +++ /dev/null @@ -1 +0,0 @@ -export const IssueFooter = () =>
Footer
; diff --git a/web/components/issue-layouts/kanban/header.tsx b/web/components/issue-layouts/kanban/header.tsx index 4852fabd9..c399b643c 100644 --- a/web/components/issue-layouts/kanban/header.tsx +++ b/web/components/issue-layouts/kanban/header.tsx @@ -1 +1,22 @@ -export const IssueHeader = () =>
Header
; +// lucide icons +import { Plus } from "lucide-react"; + +export const IssueHeader = () => ( +
+ {/* default layout */} +
+ I +
+
Kanban Issue Heading
+
+ 0 +
+ +
+ M +
+
+ +
+
+); diff --git a/web/components/issue-layouts/kanban/index.tsx b/web/components/issue-layouts/kanban/index.tsx index eaa6e4468..27d7fcbc0 100644 --- a/web/components/issue-layouts/kanban/index.tsx +++ b/web/components/issue-layouts/kanban/index.tsx @@ -4,66 +4,75 @@ import { DragDropContext, Droppable } from "react-beautiful-dnd"; // components import { IssueHeader } from "./header"; import { IssueContent } from "./content"; -import { IssueFooter } from "./footer"; // mobx import { observer } from "mobx-react-lite"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; import { RootStore } from "store/root"; -export const IssueRoot = observer(() => { +export const IssueKanBanViewRoot = observer(() => { const store: RootStore = useMobxStore(); - const { kanban: issueViewStore } = store; + const { issueView: issueViewStore } = store; const onDragEnd = (result: any) => { + if (!result) return; + + if ( + result.destination && + result.source && + result.destination.droppableId === result.source.droppableId && + result.destination.index === result.source.index + ) + return; + console.log("result", result); }; return ( -
+
{issueViewStore.loader && issueViewStore?.issues === null ? (
Loading...
) : ( -
+ <> {issueViewStore?.getIssues && Object.keys(issueViewStore?.getIssues).length > 0 ? ( -
- + +
{Object.keys(issueViewStore?.getIssues).map((_issueStateKey: any) => (
-
+
-
- - {(provided: any, snapshot: any) => ( - <> -
- -
- {provided.placeholder} - - )} -
-
- -
- -
+ + {(provided: any, snapshot: any) => ( +
+ {issueViewStore?.getIssues && ( + + )} + {provided.placeholder} +
+ )} +
))} - -
+
+
) : (
No Issues are available
)} -
+ )}
); diff --git a/web/package.json b/web/package.json index 1743e4b6c..1c5997d41 100644 --- a/web/package.json +++ b/web/package.json @@ -58,7 +58,7 @@ "js-cookie": "^3.0.1", "lodash.debounce": "^4.0.8", "lowlight": "^2.9.0", - "lucide-react": "^0.263.1", + "lucide-react": "^0.274.0", "mobx": "^6.10.0", "mobx-react-lite": "^4.0.3", "next": "12.3.2", diff --git a/web/pages/kanban.tsx b/web/pages/kanban.tsx index a25d75824..8e0312dd9 100644 --- a/web/pages/kanban.tsx +++ b/web/pages/kanban.tsx @@ -2,28 +2,175 @@ import React from "react"; // swr import useSWR from "swr"; // components -import { IssueRoot } from "components/issue-layouts/kanban"; +import { IssueKanBanViewRoot } from "components/issue-layouts/kanban"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; import { RootStore } from "store/root"; -const KanbanViewRoot = () => { +const KanBanViewRoot = () => { const workspaceSlug: string = "plane-demo"; - const projectSlug: string = "5b0e3f6e-c9f1-444d-be22-a8c2706bcf54"; + const projectSlug: string = "08d59d96-9dfb-40e5-aa30-ecc66319450f"; + const moduleSlug: string = "05613afc-29ea-4fd8-a025-a3cdfed209d1"; + const cycleSlug: string = "1f66a767-00d1-422c-8f8f-6925282b7249"; + const viewSlug: string = "1f66a767-00d1-422c-8f8f-6925282b7249"; const store: RootStore = useMobxStore(); - const { kanban: issueViewStore } = store; + const { issueView: issueViewStore } = store; - useSWR(`PROJECT_ISSUES_KANBAN_VIEW`, () => { - if (workspaceSlug && projectSlug) - issueViewStore.getIssuesAsync(workspaceSlug, projectSlug, "kanban"); - }); + React.useEffect(() => { + const init = async () => { + // my issues under a workspace + console.log("started--->"); + // await issueViewStore.getMyIssuesAsync(workspaceSlug, "my_issues", "list"); + // await issueViewStore.getMyIssuesAsync(workspaceSlug, "my_issues", "kanban"); + // await issueViewStore.getMyIssuesAsync(workspaceSlug, "my_issues", "calendar"); + // await issueViewStore.getMyIssuesAsync(workspaceSlug, "my_issues", "spreadsheet"); + // await issueViewStore.getMyIssuesAsync(workspaceSlug, "my_issues", "gantt"); + + // project issues under and workspace and project + // await issueViewStore.getProjectIssuesAsync(workspaceSlug, projectSlug, "issues", "list"); + await issueViewStore.getProjectIssuesAsync(workspaceSlug, projectSlug, "issues", "kanban"); + // await issueViewStore.getProjectIssuesAsync(workspaceSlug, projectSlug, "issues", "calendar"); + // await issueViewStore.getProjectIssuesAsync( + // workspaceSlug, + // projectSlug, + // "issues", + // "spreadsheet" + // ); + // await issueViewStore.getProjectIssuesAsync(workspaceSlug, projectSlug, "issues", "gantt"); + + // module issues under and workspace and project + // await issueViewStore.getIssuesForModulesAsync( + // workspaceSlug, + // projectSlug, + // moduleSlug, + // "modules", + // "list" + // ); + // await issueViewStore.getIssuesForModulesAsync( + // workspaceSlug, + // projectSlug, + // moduleSlug, + // "modules", + // "kanban" + // ); + // await issueViewStore.getIssuesForModulesAsync( + // workspaceSlug, + // projectSlug, + // moduleSlug, + // "modules", + // "calendar" + // ); + // await issueViewStore.getIssuesForModulesAsync( + // workspaceSlug, + // projectSlug, + // moduleSlug, + // "modules", + // "spreadsheet" + // ); + // await issueViewStore.getIssuesForModulesAsync( + // workspaceSlug, + // projectSlug, + // moduleSlug, + // "modules", + // "gantt" + // ); + + // cycle issues under and workspace and project + // await issueViewStore.getIssuesForCyclesAsync( + // workspaceSlug, + // projectSlug, + // cycleSlug, + // "cycles", + // "list" + // ); + // await issueViewStore.getIssuesForCyclesAsync( + // workspaceSlug, + // projectSlug, + // cycleSlug, + // "cycles", + // "kanban" + // ); + // await issueViewStore.getIssuesForCyclesAsync( + // workspaceSlug, + // projectSlug, + // cycleSlug, + // "cycles", + // "calendar" + // ); + // await issueViewStore.getIssuesForCyclesAsync( + // workspaceSlug, + // projectSlug, + // cycleSlug, + // "cycles", + // "spreadsheet" + // ); + // await issueViewStore.getIssuesForCyclesAsync( + // workspaceSlug, + // projectSlug, + // cycleSlug, + // "cycles", + // "gantt" + // ); + + // cycle issues under and workspace and project + // await issueViewStore.getIssuesForViewsAsync( + // workspaceSlug, + // projectSlug, + // viewSlug, + // "views", + // "list" + // ); + // await issueViewStore.getIssuesForViewsAsync( + // workspaceSlug, + // projectSlug, + // viewSlug, + // "views", + // "kanban" + // ); + // await issueViewStore.getIssuesForViewsAsync( + // workspaceSlug, + // projectSlug, + // viewSlug, + // "views", + // "calendar" + // ); + // await issueViewStore.getIssuesForViewsAsync( + // workspaceSlug, + // projectSlug, + // viewSlug, + // "views", + // "spreadsheet" + // ); + // await issueViewStore.getIssuesForViewsAsync( + // workspaceSlug, + // projectSlug, + // viewSlug, + // "views", + // "gantt" + // ); + + console.log("ended--->"); + }; + + init(); + }, []); return ( -
- +
+
+
+ Filter Header +
+
+ +
+
); }; -export default KanbanViewRoot; +export default KanBanViewRoot; diff --git a/web/services/cycles.service.ts b/web/services/cycles.service.ts index 89cd50a2f..c41f4188d 100644 --- a/web/services/cycles.service.ts +++ b/web/services/cycles.service.ts @@ -10,7 +10,7 @@ const { NEXT_PUBLIC_API_BASE_URL } = process.env; const trackEvent = process.env.NEXT_PUBLIC_TRACK_EVENTS === "true" || process.env.NEXT_PUBLIC_TRACK_EVENTS === "1"; -class ProjectCycleServices extends APIService { +export class ProjectCycleServices extends APIService { constructor() { super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000"); } diff --git a/web/services/modules.service.ts b/web/services/modules.service.ts index 0e3b5cfe2..d415c1ba4 100644 --- a/web/services/modules.service.ts +++ b/web/services/modules.service.ts @@ -10,7 +10,7 @@ const { NEXT_PUBLIC_API_BASE_URL } = process.env; const trackEvent = process.env.NEXT_PUBLIC_TRACK_EVENTS === "true" || process.env.NEXT_PUBLIC_TRACK_EVENTS === "1"; -class ProjectIssuesServices extends APIService { +export class ProjectIssuesServices extends APIService { constructor() { super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000"); } diff --git a/web/services/user.service.ts b/web/services/user.service.ts index 0e5def647..17836ae3a 100644 --- a/web/services/user.service.ts +++ b/web/services/user.service.ts @@ -17,7 +17,7 @@ const { NEXT_PUBLIC_API_BASE_URL } = process.env; const trackEvent = process.env.NEXT_PUBLIC_TRACK_EVENTS === "true" || process.env.NEXT_PUBLIC_TRACK_EVENTS === "1"; -class UserService extends APIService { +export class UserService extends APIService { constructor() { super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000"); } diff --git a/web/store/issue-views/Issue_details.ts b/web/store/issue-views/Issue_details.ts new file mode 100644 index 000000000..9f0268bbd --- /dev/null +++ b/web/store/issue-views/Issue_details.ts @@ -0,0 +1,205 @@ +import { observable, action, computed, makeObservable, runInAction } from "mobx"; +// types +import { RootStore } from "../root"; +// services +import { ProjectIssuesServices } from "services/issues.service"; +// types +import { TIssueLayouts } from "./issue_filters"; + +export interface IIssueViewStore { + loader: boolean; + error: any | null; + + issues: { [key: string]: { [key: string]: any } } | null; + + getIssuesAsync: ( + workspaceId: string, + projectId: string, + view: TIssueLayouts | null + ) => null | Promise; +} + +class IssueViewStore implements IIssueViewStore { + loader: boolean = false; + error: any | null = null; + + issues: { [key: string]: { [key: string]: any } } | null = null; + + // root store + rootStore; + // service + issueService; + + constructor(_rootStore: RootStore) { + makeObservable(this, { + // observable + loader: observable, + error: observable, + + issues: observable.ref, + // action + getIssuesAsync: action, + // computed + }); + + this.rootStore = _rootStore; + this.issueService = new ProjectIssuesServices(); + } + + // computed + get getIssues() { + if ( + this.rootStore.issueFilters.projectId && + this.issues != null && + this.rootStore.issueFilters.issueView != null + ) + return this.issues[this.rootStore.issueFilters.projectId][ + this.rootStore.issueFilters.issueView + ]; + else return null; + } + + // handling kanBan drag and drop events + + // fetching issues + getIssuesAsync = async (workspaceId: string, projectId: string) => { + try { + this.loader = true; + this.error = null; + + const filteredParams = this.rootStore.issueFilters.getComputedFilters( + workspaceId, + projectId, + "kanBan", + "issues" + ); + const issuesResponse = await this.issueService.getIssuesWithParams( + workspaceId, + projectId, + filteredParams + ); + + if (issuesResponse) { + runInAction(() => { + this.issues = { ...this.issues, [projectId]: { issues: { ...issuesResponse } } }; + this.loader = false; + this.error = null; + }); + } + + return issuesResponse; + } catch (error) { + console.warn("error", error); + this.loader = false; + this.error = null; + return error; + } + }; + + // fetching issues for modules + getIssuesForModulesAsync = async (workspaceId: string, projectId: string, moduleId: string) => { + try { + this.loader = true; + this.error = null; + + const filteredParams = this.rootStore.issueFilters.getComputedFilters( + workspaceId, + projectId, + "kanBan", + "modules" + ); + const issuesResponse = await this.issueService.getIssuesWithParams( + workspaceId, + projectId, + filteredParams + ); + + if (issuesResponse) { + runInAction(() => { + this.issues = { ...this.issues, [projectId]: { views: { ...issuesResponse } } }; + this.loader = false; + this.error = null; + }); + } + + return issuesResponse; + } catch (error) { + console.warn("error", error); + this.loader = false; + this.error = null; + return error; + } + }; + + // fetching issues for cycles + getIssuesForModulesCycles = async (workspaceId: string, projectId: string, moduleId: string) => { + try { + this.loader = true; + this.error = null; + + const filteredParams = this.rootStore.issueFilters.getComputedFilters( + workspaceId, + projectId, + "kanBan", + "cycles" + ); + const issuesResponse = await this.issueService.getIssuesWithParams( + workspaceId, + projectId, + filteredParams + ); + + if (issuesResponse) { + runInAction(() => { + this.issues = { ...this.issues, [projectId]: { views: { ...issuesResponse } } }; + this.loader = false; + this.error = null; + }); + } + + return issuesResponse; + } catch (error) { + console.warn("error", error); + this.loader = false; + this.error = null; + return error; + } + }; + + // fetching issues for views + getIssuesForViews = async (workspaceId: string, projectId: string) => { + try { + this.loader = true; + this.error = null; + + const filteredParams = this.rootStore.issueFilters.getComputedFilters( + workspaceId, + projectId, + "kanBan", + "views" + ); + const issuesResponse = await this.issueService.getIssuesWithParams( + workspaceId, + projectId, + filteredParams + ); + + if (issuesResponse) { + runInAction(() => { + this.issues = { ...this.issues, [projectId]: { views: { ...issuesResponse } } }; + this.loader = false; + this.error = null; + }); + } + + return issuesResponse; + } catch (error) { + console.warn("error", error); + this.loader = false; + this.error = null; + return error; + } + }; +} + +export default IssueViewStore; diff --git a/web/store/issue-views/Issues.ts b/web/store/issue-views/Issues.ts new file mode 100644 index 000000000..817eb7c23 --- /dev/null +++ b/web/store/issue-views/Issues.ts @@ -0,0 +1,454 @@ +import { observable, action, computed, makeObservable, runInAction } from "mobx"; +// types +import { RootStore } from "../root"; +// services +import { UserService } from "services/user.service"; +import { ProjectIssuesServices } from "services/issues.service"; +import { ProjectIssuesServices as ProjectModuleServices } from "services/modules.service"; +import { ProjectCycleServices } from "services/cycles.service"; +// types +import { TIssueLayouts, TIssueViews } from "./issue_filters"; + +export interface IIssues { + [key: string]: any; +} + +export interface IIssuesLayout { + list: IIssues; + kanban: IIssues; + calendar: IIssues; + spreadsheet: IIssues; + gantt: IIssues; +} + +export interface IIssueState { + [key: string]: { + my_issues: IIssuesLayout; + project_issues: { + [key: string]: { + issues: IIssuesLayout; + cycles: { + [key: string]: IIssuesLayout; + }; + modules: { + [key: string]: IIssuesLayout; + }; + views: { + [key: string]: IIssuesLayout; + }; + }; + }; + }; +} + +export interface IIssueViewStore { + loader: boolean; + error: any | null; + issues: IIssueState; + // computed + getIssues: IIssues | null | undefined; + // actions + getMyIssuesAsync: ( + workspaceId: string, + _view: TIssueViews, + _layout: TIssueLayouts + ) => null | Promise; + getProjectIssuesAsync: ( + workspaceId: string, + projectId: string, + _view: TIssueViews, + _layout: TIssueLayouts + ) => null | Promise; + getIssuesForModulesAsync: ( + workspaceId: string, + projectId: string, + moduleId: string, + _view: TIssueViews, + _layout: TIssueLayouts + ) => null | Promise; + getIssuesForCyclesAsync: ( + workspaceId: string, + projectId: string, + cycleId: string, + _view: TIssueViews, + _layout: TIssueLayouts + ) => null | Promise; + getIssuesForViewsAsync: ( + workspaceId: string, + projectId: string, + viewId: string, + _view: TIssueViews, + _layout: TIssueLayouts + ) => null | Promise; +} + +class IssueViewStore implements IIssueViewStore { + loader: boolean = false; + error: any | null = null; + issues: IIssueState = {}; + // root store + rootStore; + // service + issueService; + userService; + modulesService; + cyclesService; + + constructor(_rootStore: RootStore) { + makeObservable(this, { + // observable + loader: observable, + error: observable, + issues: observable.ref, + // action + getMyIssuesAsync: action, + getProjectIssuesAsync: action, + getIssuesForModulesAsync: action, + getIssuesForCyclesAsync: action, + getIssuesForViewsAsync: action, + // computed + getIssues: computed, + }); + + this.rootStore = _rootStore; + this.issueService = new ProjectIssuesServices(); + this.userService = new UserService(); + this.modulesService = new ProjectModuleServices(); + this.cyclesService = new ProjectCycleServices(); + } + + // computed + get getIssues() { + if (this.issues != null) { + const currentView: TIssueViews | null = this.rootStore.issueFilters.issueView; + const currentLayout: TIssueLayouts | null = this.rootStore.issueFilters.issueLayout; + const currentWorkspaceId: string | null = this.rootStore.issueFilters.workspaceId; + const currentProjectId: string | null = this.rootStore.issueFilters.projectId; + const currentModuleId: string | null = this.rootStore.issueFilters.moduleId; + const currentCycleId: string | null = this.rootStore.issueFilters.cycleId; + const currentViewId: string | null = this.rootStore.issueFilters.viewId; + + if (!currentView || !currentLayout || !currentWorkspaceId) return null; + + if (currentView === "my_issues") + return this.issues?.[currentWorkspaceId]?.my_issues?.[currentLayout]; + else if (currentView === "issues" && currentProjectId) + return this.issues?.[currentWorkspaceId]?.project_issues?.[currentProjectId]?.issues?.[ + currentLayout + ]; + else if (currentView === "modules" && currentProjectId && currentModuleId) + return this.issues?.[currentWorkspaceId]?.project_issues?.[currentProjectId]?.modules?.[ + currentModuleId + ]?.[currentLayout]; + else if (currentView === "cycles" && currentProjectId && currentCycleId) + return this.issues?.[currentWorkspaceId]?.project_issues?.[currentProjectId]?.cycles?.[ + currentCycleId + ]?.[currentLayout]; + else if (currentView === "views" && currentProjectId && currentViewId) + return this.issues?.[currentWorkspaceId]?.project_issues?.[currentProjectId]?.views?.[ + currentViewId + ]?.[currentLayout]; + } + } + + // fetching my issues + getMyIssuesAsync = async (workspaceId: string, _view: TIssueViews, _layout: TIssueLayouts) => { + try { + this.loader = true; + this.error = null; + + const filteredParams = this.rootStore.issueFilters.getComputedFilters( + workspaceId, + null, + null, + null, + null, + _view, + _layout + ); + const issuesResponse = await this.userService.userIssues(workspaceId, filteredParams); + + if (issuesResponse) { + const _issueResponse: any = { + ...this.issues, + [workspaceId]: { + ...this?.issues[workspaceId], + my_issues: { + ...this?.issues[workspaceId]?.my_issues, + [_layout as string]: issuesResponse, + }, + }, + }; + + runInAction(() => { + this.issues = _issueResponse; + this.loader = false; + this.error = null; + }); + } + + return issuesResponse; + } catch (error) { + console.warn("error", error); + this.loader = false; + this.error = null; + return error; + } + }; + + // fetching project issues + getProjectIssuesAsync = async ( + workspaceId: string, + projectId: string, + _view: TIssueViews, + _layout: TIssueLayouts + ) => { + try { + this.loader = true; + this.error = null; + + const filteredParams = this.rootStore.issueFilters.getComputedFilters( + workspaceId, + projectId, + null, + null, + null, + _view, + _layout + ); + const issuesResponse = await this.issueService.getIssuesWithParams( + workspaceId, + projectId, + filteredParams + ); + + if (issuesResponse) { + const _issueResponse: any = { + ...this.issues, + [workspaceId]: { + ...this?.issues?.[workspaceId], + project_issues: { + ...this?.issues?.[workspaceId]?.project_issues, + [projectId]: { + ...this?.issues?.[workspaceId]?.project_issues?.[projectId], + issues: { + ...this?.issues[workspaceId]?.project_issues?.[projectId]?.issues, + [_layout as string]: issuesResponse, + }, + }, + }, + }, + }; + + runInAction(() => { + this.issues = _issueResponse; + this.loader = false; + this.error = null; + }); + } + + return issuesResponse; + } catch (error) { + console.warn("error", error); + this.loader = false; + this.error = null; + return error; + } + }; + + // fetching project issues for modules + getIssuesForModulesAsync = async ( + workspaceId: string, + projectId: string, + moduleId: string, + _view: TIssueViews, + _layout: TIssueLayouts + ) => { + try { + this.loader = true; + this.error = null; + + const filteredParams = this.rootStore.issueFilters.getComputedFilters( + workspaceId, + projectId, + moduleId, + null, + null, + _view, + _layout + ); + const issuesResponse = await this.modulesService.getModuleIssuesWithParams( + workspaceId, + projectId, + moduleId, + filteredParams + ); + + if (issuesResponse) { + const _issueResponse: any = { + ...this.issues, + [workspaceId]: { + ...this?.issues?.[workspaceId], + project_issues: { + ...this?.issues?.[workspaceId]?.project_issues, + [projectId]: { + ...this?.issues?.[workspaceId]?.project_issues?.[projectId], + modules: { + ...this?.issues[workspaceId]?.project_issues?.[projectId]?.modules, + [moduleId]: { + ...this?.issues[workspaceId]?.project_issues?.[projectId]?.modules?.[moduleId], + [_layout as string]: issuesResponse, + }, + }, + }, + }, + }, + }; + + runInAction(() => { + this.issues = _issueResponse; + this.loader = false; + this.error = null; + }); + } + + return issuesResponse; + } catch (error) { + console.warn("error", error); + this.loader = false; + this.error = null; + return error; + } + }; + + // fetching project issues for cycles + getIssuesForCyclesAsync = async ( + workspaceId: string, + projectId: string, + cycleId: string, + _view: TIssueViews, + _layout: TIssueLayouts + ) => { + try { + this.loader = true; + this.error = null; + + const filteredParams = this.rootStore.issueFilters.getComputedFilters( + workspaceId, + projectId, + null, + cycleId, + null, + _view, + _layout + ); + const issuesResponse = await this.cyclesService.getCycleIssuesWithParams( + workspaceId, + projectId, + cycleId, + filteredParams + ); + + if (issuesResponse) { + const _issueResponse: any = { + ...this.issues, + [workspaceId]: { + ...this?.issues?.[workspaceId], + project_issues: { + ...this?.issues?.[workspaceId]?.project_issues, + [projectId]: { + ...this?.issues?.[workspaceId]?.project_issues?.[projectId], + cycles: { + ...this?.issues[workspaceId]?.project_issues?.[projectId]?.cycles, + [cycleId]: { + ...this?.issues[workspaceId]?.project_issues?.[projectId]?.cycles?.[cycleId], + [_layout as string]: issuesResponse, + }, + }, + }, + }, + }, + }; + + runInAction(() => { + this.issues = _issueResponse; + this.loader = false; + this.error = null; + }); + } + + return issuesResponse; + } catch (error) { + console.warn("error", error); + this.loader = false; + this.error = null; + return error; + } + }; + + // fetching project issues for views + getIssuesForViewsAsync = async ( + workspaceId: string, + projectId: string, + viewId: string, + _view: TIssueViews, + _layout: TIssueLayouts + ) => { + try { + this.loader = true; + this.error = null; + + const filteredParams = this.rootStore.issueFilters.getComputedFilters( + workspaceId, + projectId, + null, + null, + viewId, + _view, + _layout + ); + const issuesResponse = await this.issueService.getIssuesWithParams( + workspaceId, + projectId, + filteredParams + ); + + if (issuesResponse) { + const _issueResponse: any = { + ...this.issues, + [workspaceId]: { + ...this?.issues?.[workspaceId], + project_issues: { + ...this?.issues?.[workspaceId]?.project_issues, + [projectId]: { + ...this?.issues?.[workspaceId]?.project_issues?.[projectId], + views: { + ...this?.issues[workspaceId]?.project_issues?.[projectId]?.views, + [viewId]: { + ...this?.issues[workspaceId]?.project_issues?.[projectId]?.views?.[viewId], + [_layout as string]: issuesResponse, + }, + }, + }, + }, + }, + }; + + runInAction(() => { + this.issues = _issueResponse; + this.loader = false; + this.error = null; + }); + } + + return issuesResponse; + } catch (error) { + console.warn("error", error); + this.loader = false; + this.error = null; + return error; + } + }; +} + +export default IssueViewStore; diff --git a/web/store/issue-views/filters.ts b/web/store/issue-views/filters.ts deleted file mode 100644 index 2f79b9d3d..000000000 --- a/web/store/issue-views/filters.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { observable, action, computed, makeObservable, runInAction } from "mobx"; -// types -import { RootStore } from "../root"; -// services -import {} from "services/issues.service"; - -export type TIssueViews = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt"; - -export interface IIssueFilterStore { - loader: boolean; - error: any | null; - - // current issue view - workspaceId: string | null; - projectId: string | null; - issueView: string | null; - - // filters - priority?: null; - state?: null; - assignees?: null; - createdBy?: null; - labels?: null; - startDate?: null; - dueDate?: null; - userSelectedParams?: { - assignees: undefined | string; - created_by: undefined | string; - group_by: undefined | string; - labels: undefined | string; - order_by: undefined | string; - priority: undefined | string; - start_date: undefined | string; - state: undefined | string; - sub_issue: boolean; - target_date: undefined | string; - type: undefined | string; - }; - - // display properties - displayProperties?: { - assignee: boolean; - attachment_count: boolean; - created_on: boolean; - due_date: boolean; - estimate: boolean; - key: boolean; - labels: boolean; - link: boolean; - priority: boolean; - start_date: boolean; - state: boolean; - sub_issue_count: boolean; - updated_on: boolean; - }; - - // extra's - showEmptyGroups?: boolean; - - // actions - getProjectIssueFilterProperties: () => any | Promise; - getProjectIssueDisplayProperties: () => any | Promise; - getProjectMembers: () => any | Promise; - getProjectStates: () => any | Promise; - getProjectLabels: () => any | Promise; -} - -class IssueFilterStore implements IIssueFilterStore { - loader: boolean = false; - error: any | null = null; - - workspaceId: string | null = null; - projectId: string | null = null; - issueView: string | null = null; - - // root store - rootStore; - // service - projectPublishService = null; - - constructor(_rootStore: RootStore) { - makeObservable(this, { - // observable - loader: observable, - error: observable, - - workspaceId: observable, - projectId: observable, - - // action - getProjectIssueFilterProperties: action, - getProjectIssueDisplayProperties: action, - getProjectMembers: action, - getProjectStates: action, - getProjectLabels: action, - // computed - }); - - this.rootStore = _rootStore; - this.projectPublishService = null; - } - - // computed functions starts - getComputedFilters = ( - _workspaceId: string, - _projectId: string, - _view: TIssueViews | null = "kanban" - ) => { - this.workspaceId = _workspaceId; - this.projectId = _projectId; - this.issueView = _view; - - let filteredRouteParams = { - assignees: undefined, // ['user_id', 'user_id'] - state: undefined, // ['state_id', 'state_id'] - priority: undefined, // ['low', 'high', 'medium', 'urgent', 'null'] - type: undefined, // 'active' (started, un_started) || 'backlog' || 'null' (all_the_issues) - labels: undefined, // ['label_id', 'label_id'] - created_by: undefined, // ['user_id', 'user_id'] - start_date: undefined, // ['yyyy-mm-dd:after/before', 'yyyy-mm-dd:after/before'] - target_date: undefined, // [yyyy-mm-dd:after, yyyy-mm-dd:before] - order_by: "-created_at", // TIssueOrderByOptions - group_by: "state", // TIssueGroupByOptions - sub_issue: true, // true for all other views except spreadsheet - }; - - if (_view === "list") filteredRouteParams = { ...filteredRouteParams }; - if (_view === "kanban") filteredRouteParams = { ...filteredRouteParams }; - if (_view === "calendar") filteredRouteParams = { ...filteredRouteParams }; - if (_view === "spreadsheet") filteredRouteParams = { ...filteredRouteParams }; - if (_view === "gantt") filteredRouteParams = { ...filteredRouteParams }; - - return filteredRouteParams; - }; - - // computed functions ends - - // fetching current user project issue filter and display settings - getProjectIssueFilterProperties = () => {}; - - // fetching display properties - getProjectIssueDisplayProperties = () => {}; - - // fetching project members - getProjectMembers = () => {}; - - // fetching project state <-> groups - getProjectStates = () => {}; - - // fetching project labels - getProjectLabels = () => {}; -} - -export default IssueFilterStore; diff --git a/web/store/issue-views/data.ts b/web/store/issue-views/issue_data.ts similarity index 66% rename from web/store/issue-views/data.ts rename to web/store/issue-views/issue_data.ts index 559084e08..939967023 100644 --- a/web/store/issue-views/data.ts +++ b/web/store/issue-views/issue_data.ts @@ -1,25 +1,25 @@ export const filtersPriority: { key: string; title: string }[] = [ - { key: "", title: "Urgent" }, - { key: "", title: "High" }, - { key: "", title: "Medium" }, - { key: "", title: "Low" }, - { key: "", title: "None" }, + { key: "urgent", title: "Urgent" }, + { key: "high", title: "High" }, + { key: "medium", title: "Medium" }, + { key: "low", title: "Low" }, + { key: "null", title: "None" }, ]; export const filtersStartDate = [ - { key: "", title: "Last Week" }, - { key: "", title: "2 weeks from now" }, - { key: "", title: "1 month from now" }, - { key: "", title: "2 months from now" }, - { key: "", title: "Custom" }, + { key: "last_week", title: "Last Week" }, + { key: "2_weeks_from_now", title: "2 weeks from now" }, + { key: "1_month_from_now", title: "1 month from now" }, + { key: "2_months_from_now", title: "2 months from now" }, + { key: "custom", title: "Custom" }, ]; export const filtersDueDate = [ - { key: "", title: "Last Week" }, - { key: "", title: "2 weeks from now" }, - { key: "", title: "1 month from now" }, - { key: "", title: "2 months from now" }, - { key: "", title: "Custom" }, + { key: "last_week", title: "Last Week" }, + { key: "2_weeks_from_now", title: "2 weeks from now" }, + { key: "1_month_from_now", title: "1 month from now" }, + { key: "2_months_from_now", title: "2 months from now" }, + { key: "custom", title: "Custom" }, ]; export const displayPropertyGroupBy = [ diff --git a/web/store/issue-views/issue_filters.ts b/web/store/issue-views/issue_filters.ts new file mode 100644 index 000000000..9cb21817e --- /dev/null +++ b/web/store/issue-views/issue_filters.ts @@ -0,0 +1,330 @@ +import { observable, action, computed, makeObservable, runInAction } from "mobx"; +// types +import { RootStore } from "../root"; +// services +import {} from "services/issues.service"; +// default data +import { + filtersPriority, + filtersStartDate, + filtersDueDate, + displayPropertyGroupBy, + displayPropertyOrderBy, + displayPropertyIssueType, + displayProperties, +} from "./issue_data"; + +export type TIssueViews = "my_issues" | "issues" | "modules" | "views" | "cycles"; +export type TIssueLayouts = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt"; + +export interface IIssueFilterStore { + loader: boolean; + error: any | null; + + // current workspace and project id + workspaceId: string | null; + projectId: string | null; + moduleId: string | null; + cycleId: string | null; + viewId: string | null; + // current issue layout:TIssueLayouts and view:TIssueViews + issueView: TIssueViews | null; + issueLayout: TIssueLayouts | null; + + // filters + // static filters data + priority: { key: string; title: string }[]; + startDate: { key: string; title: string }[]; + dueDate: { key: string; title: string }[]; + issueType: { key: string; title: string }[]; + + // static display filters data + groupBy: { key: string; title: string }[]; + orderBy: { key: string; title: string }[]; + + // dynamic filters data + state: { [key: string]: { key: string; title: string }[] } | null; + members: { [key: string]: { key: string; title: string }[] } | null; // members are used for both assignees and crated_by + labels: { [key: string]: { key: string; title: string }[] } | null; + + userSelectedFilters: { + priority: string[] | null; + state: string[] | null; + assignees: string[] | null; + created_by: string[] | null; + labels: string[] | null; + start_date: string[] | null; + target_date: string[] | null; + type: string; + }; + + userSelectedDisplayFilters?: { + group_by: undefined | string; + order_by: undefined | string; + sub_issue: boolean; + showEmptyGroups: boolean; + }; + + // static display properties data + displayProperties?: { key: string; title: string }[] | null; + userSelectedDisplayProperties?: { + assignee: boolean; + attachment_count: boolean; + created_on: boolean; + due_date: boolean; + estimate: boolean; + key: boolean; + labels: boolean; + link: boolean; + priority: boolean; + start_date: boolean; + state: boolean; + sub_issue_count: boolean; + updated_on: boolean; + }; + + // actions + getProjectIssueFilterProperties: () => any | Promise; + getProjectIssueDisplayProperties: () => any | Promise; + getProjectMembers: () => any | Promise; + getProjectStates: () => any | Promise; + getProjectLabels: () => any | Promise; +} + +class IssueFilterStore implements IIssueFilterStore { + loader: boolean = false; + error: any | null = null; + + workspaceId: string | null = null; + projectId: string | null = null; + moduleId: string | null = null; + cycleId: string | null = null; + viewId: string | null = null; + + issueLayout: TIssueLayouts | null = null; + issueView: TIssueViews | null = null; + + priority: { key: string; title: string }[] = filtersPriority; + startDate: { key: string; title: string }[] = filtersStartDate; + dueDate: { key: string; title: string }[] = filtersDueDate; + issueType: { key: string; title: string }[] = displayPropertyIssueType; + + // static display filters data + groupBy: { key: string; title: string }[] = displayPropertyGroupBy; + orderBy: { key: string; title: string }[] = displayPropertyOrderBy; + + state: { [key: string]: { key: string; title: string }[] } | null = null; + members: { [key: string]: { key: string; title: string }[] } | null = null; + labels: { [key: string]: { key: string; title: string }[] } | null = null; + + userSelectedFilters: { + priority: string[] | null; + state: string[] | null; + assignees: string[] | null; + created_by: string[] | null; + labels: string[] | null; + start_date: string[] | null; + target_date: string[] | null; + type: string; + } = { + priority: null, + state: null, + assignees: null, + created_by: null, + labels: null, + start_date: null, + target_date: null, + type: "all", + }; + + // root store + rootStore; + // service + projectPublishService = null; + + constructor(_rootStore: RootStore) { + makeObservable(this, { + // observable + loader: observable, + error: observable, + + workspaceId: observable, + projectId: observable, + moduleId: observable, + cycleId: observable, + viewId: observable, + + issueLayout: observable, + issueView: observable, + + state: observable.ref, + members: observable.ref, + labels: observable.ref, + + userSelectedFilters: observable.ref, + + // action + setWorkspaceId: action, + setProjectId: action, + setModuleId: action, + setCycleId: action, + setViewId: action, + + setIssueLayout: action, + setIssueView: action, + + getProjectIssueFilterProperties: action, + getProjectIssueDisplayProperties: action, + getProjectMembers: action, + getProjectStates: action, + getProjectLabels: action, + // computed + }); + + this.rootStore = _rootStore; + this.projectPublishService = null; + } + + setWorkspaceId = (_workspaceId: string | null) => (this.workspaceId = _workspaceId); + setProjectId = (_projectId: string | null) => (this.projectId = _projectId); + setModuleId = (_moduleId: string | null) => (this.moduleId = _moduleId); + setCycleId = (_cycleId: string | null) => (this.cycleId = _cycleId); + setViewId = (_viewId: string | null) => (this.viewId = _viewId); + setIssueLayout = (_layout: TIssueLayouts | null) => (this.issueLayout = _layout); + setIssueView = (_view: TIssueViews | null) => (this.issueView = _view); + + computedFilter = (filters: any, filteredParams: any) => { + const computedFilters: any = {}; + Object.keys(filters).map((key) => { + if (filters[key] != undefined && filteredParams.includes(key)) + computedFilters[key] = + typeof filters[key] === "string" || typeof filters[key] === "boolean" + ? filters[key] + : filters[key].join(","); + }); + + return computedFilters; + }; + + // computed functions starts + getComputedFilters = ( + _workspaceId: string | null, + _projectId: string | null, + _moduleId: string | null, + _cycleId: string | null, + _viewId: string | null, + _issueView: TIssueViews, + _issueLayout: TIssueLayouts + ) => { + this.setWorkspaceId(_workspaceId); + this.setProjectId(_projectId); + this.setModuleId(_moduleId); + this.setCycleId(_cycleId); + this.setViewId(_viewId); + this.setIssueView(_issueView); + this.setIssueLayout(_issueLayout); + + let filteredRouteParams: any = { + priority: this.userSelectedFilters?.priority || undefined, + state: this.userSelectedFilters?.state || undefined, + assignees: this.userSelectedFilters?.assignees || undefined, // ['user_id', 'user_id'] + created_by: this.userSelectedFilters?.created_by || undefined, // ['user_id', 'user_id'] + labels: this.userSelectedFilters?.labels || undefined, // ['label_id', 'label_id'] + start_date: this.userSelectedFilters?.start_date || undefined, // ['yyyy-mm-dd:after/before', 'yyyy-mm-dd:after/before'] + target_date: this.userSelectedFilters?.target_date || undefined, // [yyyy-mm-dd:after, yyyy-mm-dd:before] + type: this.userSelectedFilters?.type || undefined, // 'active' (started, un_started) || 'backlog' || 'null' (all_the_issues) + group_by: "state", // TIssueGroupByOptions + order_by: "-created_at", // TIssueOrderByOptions + sub_issue: true, // true for all other views except spreadsheet + }; + + let filteredParams: any = {}; + + if (this.issueLayout === "list") + filteredParams = [ + "priority", + "state", + "assignees", + "created_by", + "labels", + "start_date", + "target_date", + "type", + "group_by", + "order_by", + "sub_issue", + ]; + if (this.issueLayout === "kanban") + filteredParams = [ + "priority", + "state", + "assignees", + "created_by", + "labels", + "start_date", + "target_date", + "type", + "group_by", + "order_by", + "sub_issue", + ]; + if (this.issueLayout === "calendar") + filteredParams = [ + "priority", + "state", + "assignees", + "created_by", + "labels", + "start_date", + "target_date", + "type", + ]; + if (this.issueLayout === "spreadsheet") + filteredParams = [ + "priority", + "state", + "assignees", + "created_by", + "labels", + "start_date", + "target_date", + "type", + ]; + if (this.issueLayout === "gantt") + filteredParams = [ + "priority", + "state", + "assignees", + "created_by", + "labels", + "start_date", + "target_date", + "type", + "order_by", + "sub_issue_id", + ]; + + filteredRouteParams = this.computedFilter(filteredRouteParams, filteredParams); + + return filteredRouteParams; + }; + + // computed functions ends + + // fetching current user project issue filter and display settings + getProjectIssueFilterProperties = () => {}; + + // fetching display properties + getProjectIssueDisplayProperties = () => {}; + + // fetching project members + getProjectMembers = () => {}; + + // fetching project state <-> groups + getProjectStates = () => {}; + + // fetching project labels + getProjectLabels = () => {}; +} + +export default IssueFilterStore; diff --git a/web/store/issue-views/kanban.ts b/web/store/issue-views/kanban.ts deleted file mode 100644 index 34a9e5b14..000000000 --- a/web/store/issue-views/kanban.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { observable, action, computed, makeObservable, runInAction } from "mobx"; -// types -import { RootStore } from "../root"; -// services -import { ProjectIssuesServices } from "services/issues.service"; -// types -import { TIssueViews } from "./filters"; - -export interface IKanbanStore { - loader: boolean; - error: any | null; - issues: { [key: string]: any } | null; - - getIssuesAsync: ( - workspaceId: string, - projectId: string, - view: TIssueViews | null - ) => null | Promise; -} - -class KanbanStore implements IKanbanStore { - loader: boolean = false; - error: any | null = null; - - issues: { [key: string]: any } | null = null; - - // root store - rootStore; - // service - issueService; - - constructor(_rootStore: RootStore) { - makeObservable(this, { - // observable - loader: observable, - error: observable, - - issues: observable.ref, - // action - getIssuesAsync: action, - // computed - }); - - this.rootStore = _rootStore; - this.issueService = new ProjectIssuesServices(); - } - - // computed - get getIssues() { - if (this.rootStore.issueFilters.projectId && this.issues != null) - return this.issues[this.rootStore.issueFilters.projectId]; - else return null; - } - - // fetching issues - getIssuesAsync = async (workspaceId: string, projectId: string, view: TIssueViews | null) => { - try { - this.loader = true; - this.error = null; - - const filteredParams = this.rootStore.issueFilters.getComputedFilters( - workspaceId, - projectId, - view - ); - const issuesResponse = await this.issueService.getIssuesWithParams( - workspaceId, - projectId, - filteredParams - ); - - if (issuesResponse) { - runInAction(() => { - this.issues = { ...this.issues, [projectId]: { ...issuesResponse } }; - this.loader = false; - this.error = null; - }); - } - - return issuesResponse; - } catch (error) { - console.warn("error", error); - this.loader = false; - this.error = null; - return error; - } - }; - - // handle issue drag and drop -} - -export default KanbanStore; diff --git a/web/store/root.ts b/web/store/root.ts index eba9aff95..9f4fd1d83 100644 --- a/web/store/root.ts +++ b/web/store/root.ts @@ -7,8 +7,8 @@ import ProjectStore, { IProjectStore } from "./project"; import ProjectPublishStore, { IProjectPublishStore } from "./project-publish"; import IssuesStore from "./issues"; // issues views and filters -import IssueFilterStore from "./issue-views/filters"; -import KanbanStore from "./issue-views/kanban"; +import IssueFilterStore from "./issue-views/issue_filters"; +import IssueViewStore from "./issue-views/Issues"; enableStaticRendering(typeof window === "undefined"); @@ -19,7 +19,7 @@ export class RootStore { projectPublish: IProjectPublishStore; issues: IssuesStore; issueFilters: IssueFilterStore; - kanban: KanbanStore; + issueView: IssueViewStore; constructor() { this.user = new UserStore(this); @@ -28,6 +28,6 @@ export class RootStore { this.projectPublish = new ProjectPublishStore(this); this.issues = new IssuesStore(this); this.issueFilters = new IssueFilterStore(this); - this.kanban = new KanbanStore(this); + this.issueView = new IssueViewStore(this); } } diff --git a/yarn.lock b/yarn.lock index c0b96cdb9..f7ca7be8f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5600,6 +5600,11 @@ lucide-react@^0.263.1: resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.263.1.tgz#a456ee0d171aa373929bd3ee20d6f9fb4429c301" integrity sha512-keqxAx97PlaEN89PXZ6ki1N8nRjGWtDa4021GFYLNj0RgruM5odbpl8GHTExj0hhPq3sF6Up0gnxt6TSHu+ovw== +lucide-react@^0.274.0: + version "0.274.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.274.0.tgz#d3b54dcb972b12f1292061448d61d422ef2e269d" + integrity sha512-qiWcojRXEwDiSimMX1+arnxha+ROJzZjJaVvCC0rsG6a9pUPjZePXSq7em4ZKMp0NDm1hyzPNkM7UaWC3LU2AA== + magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.9" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" @@ -6623,7 +6628,14 @@ prosemirror-menu@^1.2.1: prosemirror-history "^1.0.0" prosemirror-state "^1.0.0" -prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.19.0, prosemirror-model@^1.8.1: +prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.8.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.1.tgz#1d5d6b6de7b983ee67a479dc607165fdef3935bd" + integrity sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw== + dependencies: + orderedmap "^2.0.0" + +prosemirror-model@^1.19.0: version "1.19.3" resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.3.tgz#f0d55285487fefd962d0ac695f716f4ec6705006" integrity sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ==