From 0c13d05e278e0558e79732596f0d29ea2be51b18 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Fri, 13 Oct 2023 13:19:15 +0530 Subject: [PATCH] refactor: spreadsheet layout --- .../issue-column/issue-column.tsx | 4 +- .../issue-column/spreadsheet-issue-column.tsx | 4 - .../views/spreadsheet-view/single-issue.tsx | 10 +- .../spreadsheet-view/spreadsheet-view.tsx | 30 ++-- .../issues/issue-layouts/gantt/index.ts | 2 +- .../gantt/{root.tsx => project-root.tsx} | 2 +- .../issues/issue-layouts/kanban/default.tsx | 4 +- .../roots/project-layout-root.tsx | 9 +- .../issues/issue-layouts/spreadsheet/index.ts | 2 +- .../spreadsheet/project-root.tsx | 62 +++++++ .../issues/issue-layouts/spreadsheet/root.tsx | 161 ------------------ web/store/issue.ts | 4 +- 12 files changed, 95 insertions(+), 199 deletions(-) rename web/components/issues/issue-layouts/gantt/{root.tsx => project-root.tsx} (96%) create mode 100644 web/components/issues/issue-layouts/spreadsheet/project-root.tsx delete mode 100644 web/components/issues/issue-layouts/spreadsheet/root.tsx diff --git a/web/components/core/views/spreadsheet-view/issue-column/issue-column.tsx b/web/components/core/views/spreadsheet-view/issue-column/issue-column.tsx index 7d93f8098..7fb73c583 100644 --- a/web/components/core/views/spreadsheet-view/issue-column/issue-column.tsx +++ b/web/components/core/views/spreadsheet-view/issue-column/issue-column.tsx @@ -22,7 +22,6 @@ type Props = { properties: Properties; handleEditIssue: (issue: IIssue) => void; handleDeleteIssue: (issue: IIssue) => void; - setCurrentProjectId: React.Dispatch>; disableUserActions: boolean; nestingLevel: number; }; @@ -35,7 +34,6 @@ export const IssueColumn: React.FC = ({ properties, handleEditIssue, handleDeleteIssue, - setCurrentProjectId, disableUserActions, nestingLevel, }) => { @@ -49,7 +47,7 @@ export const IssueColumn: React.FC = ({ const openPeekOverview = () => { const { query } = router; - setCurrentProjectId(issue.project_detail.id); + router.push({ pathname: router.pathname, query: { ...query, peekIssue: issue.id }, diff --git a/web/components/core/views/spreadsheet-view/issue-column/spreadsheet-issue-column.tsx b/web/components/core/views/spreadsheet-view/issue-column/spreadsheet-issue-column.tsx index 2ba00d903..f5ddd0c9b 100644 --- a/web/components/core/views/spreadsheet-view/issue-column/spreadsheet-issue-column.tsx +++ b/web/components/core/views/spreadsheet-view/issue-column/spreadsheet-issue-column.tsx @@ -14,7 +14,6 @@ type Props = { setExpandedIssues: React.Dispatch>; properties: Properties; handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void; - setCurrentProjectId: React.Dispatch>; disableUserActions: boolean; nestingLevel?: number; }; @@ -26,7 +25,6 @@ export const SpreadsheetIssuesColumn: React.FC = ({ setExpandedIssues, properties, handleIssueAction, - setCurrentProjectId, disableUserActions, nestingLevel = 0, }) => { @@ -57,7 +55,6 @@ export const SpreadsheetIssuesColumn: React.FC = ({ properties={properties} handleEditIssue={() => handleIssueAction(issue, "edit")} handleDeleteIssue={() => handleIssueAction(issue, "delete")} - setCurrentProjectId={setCurrentProjectId} disableUserActions={disableUserActions} nestingLevel={nestingLevel} /> @@ -75,7 +72,6 @@ export const SpreadsheetIssuesColumn: React.FC = ({ setExpandedIssues={setExpandedIssues} properties={properties} handleIssueAction={handleIssueAction} - setCurrentProjectId={setCurrentProjectId} disableUserActions={disableUserActions} nestingLevel={nestingLevel + 1} /> diff --git a/web/components/core/views/spreadsheet-view/single-issue.tsx b/web/components/core/views/spreadsheet-view/single-issue.tsx index 73ba62994..f421692b9 100644 --- a/web/components/core/views/spreadsheet-view/single-issue.tsx +++ b/web/components/core/views/spreadsheet-view/single-issue.tsx @@ -19,7 +19,7 @@ import { StateSelect } from "components/states"; import { copyTextToClipboard } from "helpers/string.helper"; import { renderLongDetailDateFormat } from "helpers/date-time.helper"; // types -import { ICurrentUserResponse, IIssue, IState, ISubIssueResponse, Properties, TIssuePriorities, UserAuth } from "types"; +import { IUser, IIssue, IState, ISubIssueResponse, Properties, TIssuePriorities, UserAuth } from "types"; // constant import { CYCLE_DETAILS, @@ -42,7 +42,7 @@ type Props = { handleDeleteIssue: (issue: IIssue) => void; gridTemplateColumns: string; disableUserActions: boolean; - user: ICurrentUserResponse | undefined; + user: IUser | undefined; userAuth: UserAuth; nestingLevel: number; }; @@ -159,6 +159,8 @@ export const SingleSpreadsheetIssue: React.FC = ({ }; const handleStateChange = (data: string, states: IState[] | undefined) => { + if (!user) return; + const oldState = states?.find((s) => s.id === issue.state); const newState = states?.find((s) => s.id === data); @@ -197,6 +199,8 @@ export const SingleSpreadsheetIssue: React.FC = ({ }; const handlePriorityChange = (data: TIssuePriorities) => { + if (!user) return; + partialUpdateIssue({ priority: data }, issue); trackEventServices.trackIssuePartialPropertyUpdateEvent( { @@ -213,6 +217,8 @@ export const SingleSpreadsheetIssue: React.FC = ({ }; const handleAssigneeChange = (data: any) => { + if (!user) return; + const newData = issue.assignees ?? []; if (newData.includes(data)) newData.splice(newData.indexOf(data), 1); diff --git a/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx b/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx index 258026a4b..a22376d45 100644 --- a/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx +++ b/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx @@ -6,7 +6,7 @@ import { observer } from "mobx-react-lite"; import useLocalStorage from "hooks/use-local-storage"; // components import { - ListInlineCreateIssueForm, + // ListInlineCreateIssueForm, SpreadsheetAssigneeColumn, SpreadsheetCreatedOnColumn, SpreadsheetDueDateColumn, @@ -19,7 +19,6 @@ import { SpreadsheetUpdatedOnColumn, } from "components/core"; import { CustomMenu, Icon } from "components/ui"; -import { IssuePeekOverview } from "components/issues"; import { Spinner } from "@plane/ui"; // types import { IIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties, TIssueOrderByOptions } from "types"; @@ -32,7 +31,7 @@ type Props = { handleDisplayFilterUpdate: (data: Partial) => void; issues: IIssue[] | undefined; handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void; - handleUpdateIssue: (issueId: string, data: Partial) => void; + handleUpdateIssue: (issue: IIssue, data: Partial) => void; openIssuesListModal?: (() => void) | null; disableUserActions: boolean; }; @@ -50,7 +49,6 @@ export const SpreadsheetView: React.FC = observer((props) => { } = props; const [expandedIssues, setExpandedIssues] = useState([]); - const [currentProjectId, setCurrentProjectId] = useState(null); const [isInlineCreateIssueFormOpen, setIsInlineCreateIssueFormOpen] = useState(false); @@ -59,7 +57,7 @@ export const SpreadsheetView: React.FC = observer((props) => { const containerRef = useRef(null); const router = useRouter(); - const { workspaceSlug, cycleId, moduleId } = router.query; + const { cycleId, moduleId } = router.query; const type = cycleId ? "cycle" : moduleId ? "module" : "issue"; @@ -266,10 +264,10 @@ export const SpreadsheetView: React.FC = observer((props) => { ); const handleScroll = () => { - if (containerRef.current) { - const scrollLeft = containerRef.current.scrollLeft; - setIsScrolled(scrollLeft > 0); - } + if (!containerRef.current) return; + + const scrollLeft = containerRef.current.scrollLeft; + setIsScrolled(scrollLeft > 0); }; useEffect(() => { @@ -288,11 +286,6 @@ export const SpreadsheetView: React.FC = observer((props) => { return ( <> -
@@ -302,7 +295,7 @@ export const SpreadsheetView: React.FC = observer((props) => {
@@ -312,14 +305,13 @@ export const SpreadsheetView: React.FC = observer((props) => { Issue
- {issues.map((issue: IIssue, index) => ( + {issues.map((issue, index) => ( = observer((props) => {
-
+ {/*
setIsInlineCreateIssueFormOpen(false)} @@ -370,7 +362,7 @@ export const SpreadsheetView: React.FC = observer((props) => { ...(moduleId && { module: moduleId.toString() }), }} /> -
+
*/} {type === "issue" ? !disableUserActions && diff --git a/web/components/issues/issue-layouts/gantt/index.ts b/web/components/issues/issue-layouts/gantt/index.ts index d8cfadd48..a16a9c920 100644 --- a/web/components/issues/issue-layouts/gantt/index.ts +++ b/web/components/issues/issue-layouts/gantt/index.ts @@ -1,5 +1,5 @@ export * from "./blocks"; export * from "./cycle-root"; export * from "./module-root"; +export * from "./project-root"; export * from "./project-view-root"; -export * from "./root"; diff --git a/web/components/issues/issue-layouts/gantt/root.tsx b/web/components/issues/issue-layouts/gantt/project-root.tsx similarity index 96% rename from web/components/issues/issue-layouts/gantt/root.tsx rename to web/components/issues/issue-layouts/gantt/project-root.tsx index 732134b46..1d590aced 100644 --- a/web/components/issues/issue-layouts/gantt/root.tsx +++ b/web/components/issues/issue-layouts/gantt/project-root.tsx @@ -11,7 +11,7 @@ import { IssueGanttBlock, IssueGanttSidebarBlock, IssuePeekOverview } from "comp // types import { IIssueUnGroupedStructure } from "store/issue"; -export const GanttLayout: React.FC = observer(() => { +export const ProjectGanttLayout: React.FC = observer(() => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; diff --git a/web/components/issues/issue-layouts/kanban/default.tsx b/web/components/issues/issue-layouts/kanban/default.tsx index e1400ab7e..86d0092c9 100644 --- a/web/components/issues/issue-layouts/kanban/default.tsx +++ b/web/components/issues/issue-layouts/kanban/default.tsx @@ -11,6 +11,8 @@ import { observer } from "mobx-react-lite"; // mobx import { useMobxStore } from "lib/mobx/store-provider"; import { RootStore } from "store/root"; +// types +import { IIssue } from "types"; export interface IGroupByKanBan { issues: any; @@ -20,7 +22,7 @@ export interface IGroupByKanBan { list: any; listKey: string; isDragDisabled: boolean; - handleIssues?: (sub_group_by: string | null, group_by: string | null, issue: any) => void; + handleIssues?: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => void; display_properties: any; kanBanToggle: any; handleKanBanToggle: any; diff --git a/web/components/issues/issue-layouts/roots/project-layout-root.tsx b/web/components/issues/issue-layouts/roots/project-layout-root.tsx index 576c6f9f2..5d2d90e9d 100644 --- a/web/components/issues/issue-layouts/roots/project-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/project-layout-root.tsx @@ -9,10 +9,10 @@ import { useMobxStore } from "lib/mobx/store-provider"; import { ListLayout, CalendarLayout, - GanttLayout, + ProjectGanttLayout, KanBanLayout, ProjectAppliedFiltersRoot, - SpreadsheetLayout, + ProjectSpreadsheetLayout, } from "components/issues"; export const ProjectLayoutRoot: React.FC = observer(() => { @@ -26,6 +26,7 @@ export const ProjectLayoutRoot: React.FC = observer(() => { const { issue: issueStore, project: projectStore, issueFilter: issueFilterStore } = useMobxStore(); + // TODO: remove fetch logic from here useSWR( workspaceSlug && projectId ? `PROJECT_ISSUES` : null, async () => { @@ -56,9 +57,9 @@ export const ProjectLayoutRoot: React.FC = observer(() => { ) : activeLayout === "calendar" ? ( ) : activeLayout === "gantt_chart" ? ( - + ) : activeLayout === "spreadsheet" ? ( - + ) : null}
diff --git a/web/components/issues/issue-layouts/spreadsheet/index.ts b/web/components/issues/issue-layouts/spreadsheet/index.ts index 89cf064a4..10037416c 100644 --- a/web/components/issues/issue-layouts/spreadsheet/index.ts +++ b/web/components/issues/issue-layouts/spreadsheet/index.ts @@ -1,4 +1,4 @@ export * from "./cycle-root"; export * from "./module-root"; +export * from "./project-root"; export * from "./project-view-root"; -export * from "./root"; diff --git a/web/components/issues/issue-layouts/spreadsheet/project-root.tsx b/web/components/issues/issue-layouts/spreadsheet/project-root.tsx new file mode 100644 index 000000000..12e85ec3b --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/project-root.tsx @@ -0,0 +1,62 @@ +import React, { useCallback } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; + +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import useProjectDetails from "hooks/use-project-details"; +// components +import { SpreadsheetView } from "components/core"; +import { IssuePeekOverview } from "components/issues"; +// types +import { IIssue, IIssueDisplayFilterOptions } from "types"; + +export const ProjectSpreadsheetLayout: React.FC = observer(() => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { projectDetails } = useProjectDetails(); + + const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore(); + + const issues = issueStore.getIssues; + + const handleDisplayFiltersUpdate = useCallback( + (updatedDisplayFilter: Partial) => { + if (!workspaceSlug || !projectId) return; + + issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + display_filters: { + ...updatedDisplayFilter, + }, + }); + }, + [issueFilterStore, projectId, workspaceSlug] + ); + + const updateIssue = (group_by: string | null, sub_group_by: string | null, issue: IIssue) => { + issueStore.updateIssueStructure(group_by, sub_group_by, issue); + }; + + const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; + + return ( + <> + + {}} + handleUpdateIssue={() => {}} + disableUserActions={false} + /> + + ); +}); diff --git a/web/components/issues/issue-layouts/spreadsheet/root.tsx b/web/components/issues/issue-layouts/spreadsheet/root.tsx deleted file mode 100644 index 0c2373dd8..000000000 --- a/web/components/issues/issue-layouts/spreadsheet/root.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import React, { useCallback, useState } from "react"; -import { useRouter } from "next/router"; -import { observer } from "mobx-react-lite"; - -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// hooks -import useUser from "hooks/use-user"; -import useProjectDetails from "hooks/use-project-details"; -// components -import { SpreadsheetColumns, SpreadsheetIssues } from "components/core"; -import { IssuePeekOverview } from "components/issues"; -// ui -import { CustomMenu } from "components/ui"; -import { Spinner } from "@plane/ui"; -// icon -import { PlusIcon } from "@heroicons/react/24/outline"; -// types -import { IIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties } from "types"; -import { IIssueUnGroupedStructure } from "store/issue"; -// constants -import { SPREADSHEET_COLUMN } from "constants/spreadsheet"; - -export const SpreadsheetLayout: React.FC = observer(() => { - const [expandedIssues, setExpandedIssues] = useState([]); - - const router = useRouter(); - const { workspaceSlug, projectId, cycleId, moduleId } = router.query; - - const { user } = useUser(); - const { projectDetails } = useProjectDetails(); - - const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore(); - - const issues = issueStore.getIssues; - const issueDisplayProperties = issueFilterStore.userDisplayProperties; - - const handleDisplayFiltersUpdate = useCallback( - (updatedDisplayFilter: Partial) => { - if (!workspaceSlug || !projectId) return; - - issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { - display_filters: { - ...updatedDisplayFilter, - }, - }); - }, - [issueFilterStore, projectId, workspaceSlug] - ); - - const type = cycleId ? "cycle" : moduleId ? "module" : "issue"; - - const columnData = SPREADSHEET_COLUMN.map((column) => ({ - ...column, - isActive: issueDisplayProperties - ? column.propertyName === "labels" - ? issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] - : column.propertyName === "title" - ? true - : issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] - : false, - })); - - const gridTemplateColumns = columnData - .filter((column) => column.isActive) - .map((column) => column.colSize) - .join(" "); - - const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; - - return ( - <> - -
-
- -
- {issues ? ( -
- {(issues as IIssueUnGroupedStructure).map((issue: IIssue, index) => ( - {}} - disableUserActions={!isAllowed} - user={user} - userAuth={{ - isViewer: projectDetails?.member_role === 5, - isGuest: projectDetails?.member_role === 10, - isMember: projectDetails?.member_role === 15, - isOwner: projectDetails?.member_role === 20, - }} - /> - ))} -
- {type === "issue" ? ( - - ) : ( - isAllowed && ( - - - Add Issue - - } - position="left" - optionsClassName="left-5 !w-36" - noBorder - > - { - const e = new KeyboardEvent("keydown", { key: "c" }); - document.dispatchEvent(e); - }} - > - Create new - - {true && {}}>Add an existing issue} - - ) - )} -
-
- ) : ( - - )} -
- - ); -}); diff --git a/web/store/issue.ts b/web/store/issue.ts index 06a4dd16a..feada1cad 100644 --- a/web/store/issue.ts +++ b/web/store/issue.ts @@ -99,13 +99,13 @@ class IssueStore implements IIssueStore { return this.issues?.[projectId]?.[issueType] || null; } + // TODO: params order is different from what is present in components updateIssueStructure = async (group_id: string | null, sub_group_id: string | null, issue: IIssue) => { const projectId: string | null = issue?.project; const issueType = this.getIssueType; if (!projectId || !issueType) return null; - let issues: IIssueGroupedStructure | IIssueGroupWithSubGroupsStructure | IIssueUnGroupedStructure | null = - this.getIssues; + let issues = this.getIssues; if (!issues) return null; if (issueType === "grouped" && group_id) {