From 7833ca7bea859343ac71933d26fa094a3178baca Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Wed, 17 Jan 2024 18:37:46 +0530 Subject: [PATCH] fix: project views bugs related to store refactor. (#3391) * chore: remove debounce logic to fix create/ update view modal bugs. * fix: bug in delete views not mutating the store. * chore: replace `Project Empty State` with `Project Views Empty State`. * chore: add issue peek overview. * refactor: issue update, delete actions for project views layout. fix: issue update and delete action throwing error bug. fix: issue quick add throwing error bug. --- .../calendar/roots/project-view-root.tsx | 35 +++++------ .../issue-layouts/gantt/project-view-root.tsx | 38 +++++++----- .../kanban/roots/project-view-root.tsx | 37 +++++------- .../list/roots/project-view-root.tsx | 37 +++++------- .../roots/project-view-layout-root.tsx | 59 +++++++++++++------ .../spreadsheet/roots/project-view-root.tsx | 37 ++++++------ web/components/views/form.tsx | 2 +- web/components/views/modal.tsx | 7 +-- web/store/project-view.store.ts | 4 +- 9 files changed, 131 insertions(+), 125 deletions(-) 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 43e59dc76..573a9cf20 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 @@ -4,33 +4,28 @@ import { observer } from "mobx-react-lite"; import { useIssues } from "hooks/store"; // components import { ProjectIssueQuickActions } from "components/issues"; +import { BaseCalendarRoot } from "../base-calendar-root"; // types import { TIssue } from "@plane/types"; import { EIssueActions } from "../../types"; -import { BaseCalendarRoot } from "../base-calendar-root"; +// constants import { EIssuesStoreType } from "constants/issue"; -import { useMemo } from "react"; -export const ProjectViewCalendarLayout: React.FC = observer(() => { +export interface IViewCalendarLayout { + issueActions: { + [EIssueActions.DELETE]: (issue: TIssue) => Promise; + [EIssueActions.UPDATE]?: (issue: TIssue) => Promise; + [EIssueActions.REMOVE]?: (issue: TIssue) => Promise; + }; +} + +export const ProjectViewCalendarLayout: React.FC = observer((props) => { + const { issueActions } = props; + // store const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW); + // router const router = useRouter(); - const { workspaceSlug, projectId, viewId } = router.query; - - const issueActions = useMemo( - () => ({ - [EIssueActions.UPDATE]: async (issue: TIssue) => { - if (!workspaceSlug || !projectId) return; - - await issues.updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, issue); - }, - [EIssueActions.DELETE]: async (issue: TIssue) => { - if (!workspaceSlug || !projectId) return; - - await issues.removeIssue(workspaceSlug.toString(), projectId.toString(), issue.id); - }, - }), - [issues, workspaceSlug, projectId] - ); + const { viewId } = router.query; return ( { +export interface IViewGanttLayout { + issueActions: { + [EIssueActions.DELETE]: (issue: TIssue) => Promise; + [EIssueActions.UPDATE]?: (issue: TIssue) => Promise; + [EIssueActions.REMOVE]?: (issue: TIssue) => Promise; + }; +} + +export const ProjectViewGanttLayout: React.FC = observer((props) => { + const { issueActions } = props; + // store const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW); // router const router = useRouter(); - const { workspaceSlug } = router.query; + const { viewId } = router.query; - const issueActions = { - [EIssueActions.UPDATE]: async (issue: TIssue) => { - if (!workspaceSlug) return; - - await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue); - }, - [EIssueActions.DELETE]: async (issue: TIssue) => { - if (!workspaceSlug) return; - - await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id); - }, - }; - - return ; + return ( + + ); }); 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 86f6a281b..0458b6f98 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 @@ -1,38 +1,32 @@ -import React, { useMemo } from "react"; +import React from "react"; import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; // hooks import { useIssues } from "hooks/store"; // constant +import { EIssuesStoreType } from "constants/issue"; +// types import { TIssue } from "@plane/types"; import { EIssueActions } from "../../types"; -import { ProjectIssueQuickActions } from "../../quick-action-dropdowns"; // components import { BaseKanBanRoot } from "../base-kanban-root"; -import { EIssuesStoreType } from "constants/issue"; +import { ProjectIssueQuickActions } from "../../quick-action-dropdowns"; -export interface IViewKanBanLayout {} +export interface IViewKanBanLayout { + issueActions: { + [EIssueActions.DELETE]: (issue: TIssue) => Promise; + [EIssueActions.UPDATE]?: (issue: TIssue) => Promise; + [EIssueActions.REMOVE]?: (issue: TIssue) => Promise; + }; +} -export const ProjectViewKanBanLayout: React.FC = observer(() => { +export const ProjectViewKanBanLayout: React.FC = observer((props) => { + const { issueActions } = props; + // router const router = useRouter(); - const { workspaceSlug } = router.query as { workspaceSlug: string; projectId: string }; + const { viewId } = router.query; const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW); - const issueActions = useMemo( - () => ({ - [EIssueActions.UPDATE]: async (issue: TIssue) => { - if (!workspaceSlug) return; - - await issues.updateIssue(workspaceSlug, issue.project_id, issue.id, issue); - }, - [EIssueActions.DELETE]: async (issue: TIssue) => { - if (!workspaceSlug) return; - - await issues.removeIssue(workspaceSlug, issue.project_id, issue.id); - }, - }), - [issues, workspaceSlug] - ); return ( { showLoader={true} QuickActions={ProjectIssueQuickActions} currentStore={EIssuesStoreType.PROJECT_VIEW} + viewId={viewId?.toString()} /> ); }); diff --git a/web/components/issues/issue-layouts/list/roots/project-view-root.tsx b/web/components/issues/issue-layouts/list/roots/project-view-root.tsx index 8139307e6..92b8a9f2c 100644 --- a/web/components/issues/issue-layouts/list/roots/project-view-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/project-view-root.tsx @@ -1,43 +1,35 @@ -import React, { useMemo } from "react"; +import React from "react"; import { observer } from "mobx-react-lite"; +import { useRouter } from "next/router"; // store import { useIssues } from "hooks/store"; // constants -import { useRouter } from "next/router"; +import { EIssuesStoreType } from "constants/issue"; +// types import { EIssueActions } from "../../types"; import { TIssue } from "@plane/types"; // components import { BaseListRoot } from "../base-list-root"; import { ProjectIssueQuickActions } from "../../quick-action-dropdowns"; -import { EIssuesStoreType } from "constants/issue"; -export interface IViewListLayout {} +export interface IViewListLayout { + issueActions: { + [EIssueActions.DELETE]: (issue: TIssue) => Promise; + [EIssueActions.UPDATE]?: (issue: TIssue) => Promise; + [EIssueActions.REMOVE]?: (issue: TIssue) => Promise; + }; +} -export const ProjectViewListLayout: React.FC = observer(() => { +export const ProjectViewListLayout: React.FC = observer((props) => { + const { issueActions } = props; // store const { issuesFilter, issues } = useIssues(EIssuesStoreType.PROJECT_VIEW); const router = useRouter(); - const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; + const { workspaceSlug, projectId, viewId } = router.query; if (!workspaceSlug || !projectId) return null; - const issueActions = useMemo( - () => ({ - [EIssueActions.UPDATE]: async (issue: TIssue) => { - if (!workspaceSlug || !projectId) return; - - await issues.updateIssue(workspaceSlug, projectId, issue.id, issue); - }, - [EIssueActions.DELETE]: async (issue: TIssue) => { - if (!workspaceSlug || !projectId) return; - - await issues.removeIssue(workspaceSlug, projectId, issue.id); - }, - }), - [issues, workspaceSlug, projectId] - ); - return ( { QuickActions={ProjectIssueQuickActions} issueActions={issueActions} currentStore={EIssuesStoreType.PROJECT_VIEW} + viewId={viewId?.toString()} /> ); }); diff --git a/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx b/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx index b11d18a59..11564ca84 100644 --- a/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useMemo } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import useSWR from "swr"; @@ -6,16 +6,21 @@ import useSWR from "swr"; import { useIssues } from "hooks/store"; // components import { - ProjectEmptyState, + IssuePeekOverview, ProjectViewAppliedFiltersRoot, ProjectViewCalendarLayout, + ProjectViewEmptyState, ProjectViewGanttLayout, ProjectViewKanBanLayout, ProjectViewListLayout, ProjectViewSpreadsheetLayout, } from "components/issues"; import { Spinner } from "@plane/ui"; +// constants import { EIssuesStoreType } from "constants/issue"; +// types +import { TIssue } from "@plane/types"; +import { EIssueActions } from "../types"; export const ProjectViewLayoutRoot: React.FC = observer(() => { // router @@ -39,6 +44,22 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => { } ); + const issueActions = useMemo( + () => ({ + [EIssueActions.UPDATE]: async (issue: TIssue) => { + if (!workspaceSlug || !projectId) return; + + await issues.updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, issue, viewId?.toString()); + }, + [EIssueActions.DELETE]: async (issue: TIssue) => { + if (!workspaceSlug || !projectId) return; + + await issues.removeIssue(workspaceSlug.toString(), projectId.toString(), issue.id, viewId?.toString()); + }, + }), + [issues, workspaceSlug, projectId, viewId] + ); + const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout; if (!workspaceSlug || !projectId || !viewId) return <>; @@ -53,22 +74,26 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => { ) : ( <> {!issues?.groupedIssueIds ? ( - // TODO: Replace this with project view empty state - + ) : ( -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : activeLayout === "calendar" ? ( - - ) : activeLayout === "gantt_chart" ? ( - - ) : activeLayout === "spreadsheet" ? ( - - ) : null} -
+ <> +
+ {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : activeLayout === "calendar" ? ( + + ) : activeLayout === "gantt_chart" ? ( + + ) : activeLayout === "spreadsheet" ? ( + + ) : null} +
+ + {/* peek overview */} + + )} )} diff --git a/web/components/issues/issue-layouts/spreadsheet/roots/project-view-root.tsx b/web/components/issues/issue-layouts/spreadsheet/roots/project-view-root.tsx index 7420d1e48..28b766cd1 100644 --- a/web/components/issues/issue-layouts/spreadsheet/roots/project-view-root.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/roots/project-view-root.tsx @@ -1,43 +1,40 @@ -import React, { useMemo } from "react"; +import React from "react"; import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; // mobx store import { useIssues } from "hooks/store"; // components import { BaseSpreadsheetRoot } from "../base-spreadsheet-root"; +import { ProjectIssueQuickActions } from "../../quick-action-dropdowns"; +// types import { EIssueActions } from "../../types"; import { TIssue } from "@plane/types"; -import { ProjectIssueQuickActions } from "../../quick-action-dropdowns"; +// constants import { EIssuesStoreType } from "constants/issue"; -export const ProjectViewSpreadsheetLayout: React.FC = observer(() => { +export interface IViewSpreadsheetLayout { + issueActions: { + [EIssueActions.DELETE]: (issue: TIssue) => Promise; + [EIssueActions.UPDATE]?: (issue: TIssue) => Promise; + [EIssueActions.REMOVE]?: (issue: TIssue) => Promise; + }; +} + +export const ProjectViewSpreadsheetLayout: React.FC = observer((props) => { + const { issueActions } = props; + // router const router = useRouter(); - const { workspaceSlug } = router.query as { workspaceSlug: string }; + const { viewId } = router.query; const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW); - const issueActions = useMemo( - () => ({ - [EIssueActions.UPDATE]: async (issue: TIssue) => { - if (!workspaceSlug) return; - - await issues.updateIssue(workspaceSlug, issue.project_id, issue.id, issue); - }, - [EIssueActions.DELETE]: async (issue: TIssue) => { - if (!workspaceSlug) return; - - await issues.removeIssue(workspaceSlug, issue.project_id, issue.id); - }, - }), - [issues, workspaceSlug] - ); - return ( ); }); diff --git a/web/components/views/form.tsx b/web/components/views/form.tsx index c20354b3d..04ed5a0e2 100644 --- a/web/components/views/form.tsx +++ b/web/components/views/form.tsx @@ -208,7 +208,7 @@ export const ProjectViewForm: React.FC = observer((props) => { -